Repository: streamlit/streamlit

Issue:
df.dtypes fails with Arrow
### Summary

Using `df.dtypes` to show the datatypes for a dataframe fails when you're using the Arrow codepath.

### Steps to reproduce

Code snippet:

```
import streamlit as st
import pandas as pd

df = pd.read_json("https://cdn.jsdelivr.net/npm/vega-datasets@2/data/penguins.json")

st.write(df.dtypes)
```

**Expected behavior:**

The app should show a table with datatypes for the given dataframe

**Actual behavior:**

The app shows this exception:

![image](https://user-images.githubusercontent.com/690814/129639941-0cb162f2-5d85-4e0a-ab90-7ba20c083b8e.png)

Exception text
```
ArrowInvalid: ("Could not convert dtype('O') with type numpy.dtype[object_]: did not recognize Python value type when inferring an Arrow data type", 'Conversion failed for column 0 with type object')
Traceback:
File "/home/tvst/.local/share/virtualenvs/streamlit-vega-lite-demo-Se5mEOd4/lib/python3.9/site-packages/streamlit/script_runner.py", line 350, in _run_script
    exec(code, module.__dict__)
File "/media/storage/Projects/streamlit/streamlit-vega-lite-demo/demo.py", line 15, in <module>
    df.dtypes
File "/home/tvst/.local/share/virtualenvs/streamlit-vega-lite-demo-Se5mEOd4/lib/python3.9/site-packages/streamlit/__init__.py", line 478, in _transparent_write
    write(*args)
File "/home/tvst/.local/share/virtualenvs/streamlit-vega-lite-demo-Se5mEOd4/lib/python3.9/site-packages/streamlit/elements/write.py", line 182, in write
    self.dg.dataframe(arg)
File "/home/tvst/.local/share/virtualenvs/streamlit-vega-lite-demo-Se5mEOd4/lib/python3.9/site-packages/streamlit/elements/dataframe_selector.py", line 85, in dataframe
    return self.dg._arrow_dataframe(data, width, height)
File "/home/tvst/.local/share/virtualenvs/streamlit-vega-lite-demo-Se5mEOd4/lib/python3.9/site-packages/streamlit/elements/arrow.py", line 82, in _arrow_dataframe
    marshall(proto, data, default_uuid)
File "/home/tvst/.local/share/virtualenvs/streamlit-vega-lite-demo-Se5mEOd4/lib/python3.9/site-packages/streamlit/elements/arrow.py", line 160, in marshall
    proto.data = type_util.data_frame_to_bytes(df)
File "/home/tvst/.local/share/virtualenvs/streamlit-vega-lite-demo-Se5mEOd4/lib/python3.9/site-packages/streamlit/type_util.py", line 371, in data_frame_to_bytes
    table = pa.Table.from_pandas(df)
File "pyarrow/table.pxi", line 1561, in pyarrow.lib.Table.from_pandas
File "/home/tvst/.local/share/virtualenvs/streamlit-vega-lite-demo-Se5mEOd4/lib64/python3.9/site-packages/pyarrow/pandas_compat.py", line 594, in dataframe_to_arrays
    arrays = [convert_column(c, f)
File "/home/tvst/.local/share/virtualenvs/streamlit-vega-lite-demo-Se5mEOd4/lib64/python3.9/site-packages/pyarrow/pandas_compat.py", line 594, in <listcomp>
    arrays = [convert_column(c, f)
File "/home/tvst/.local/share/virtualenvs/streamlit-vega-lite-demo-Se5mEOd4/lib64/python3.9/site-packages/pyarrow/pandas_compat.py", line 581, in convert_column
    raise e
File "/home/tvst/.local/share/virtualenvs/streamlit-vega-lite-demo-Se5mEOd4/lib64/python3.9/site-packages/pyarrow/pandas_compat.py", line 575, in convert_column
    result = pa.array(col, type=type_, from_pandas=True, safe=safe)
File "pyarrow/array.pxi", line 302, in pyarrow.lib.array
File "pyarrow/array.pxi", line 83, in pyarrow.lib._ndarray_to_array
File "pyarrow/error.pxi", line 99, in pyarrow.lib.check_status
```

### Is this a regression?

yes (arrow vs legacy)

### Debug info

- Streamlit version: 0.86.0


Discussion:
Similar issue. Anyone know anything about this?

Resolving patch:
diff --git a/lib/streamlit/type_util.py b/lib/streamlit/type_util.py
--- a/lib/streamlit/type_util.py
+++ b/lib/streamlit/type_util.py
@@ -368,9 +368,21 @@ def data_frame_to_bytes(df: DataFrame) -> bytes:
         A dataframe to convert.
 
     """
-
-    table = pa.Table.from_pandas(df)
-    return pyarrow_table_to_bytes(table)
+    try:
+        table = pa.Table.from_pandas(df)
+        return pyarrow_table_to_bytes(table)
+    except Exception as e:
+        _NUMPY_DTYPE_ERROR_MESSAGE = "Could not convert dtype"
+        if _NUMPY_DTYPE_ERROR_MESSAGE in str(e):
+            raise errors.StreamlitAPIException(
+                """
+Unable to convert `numpy.dtype` to `pyarrow.DataType`.  
+This is likely due to a bug in Arrow (see https://issues.apache.org/jira/browse/ARROW-14087).  
+As a temporary workaround, you can convert the DataFrame cells to strings with `df.astype(str)`.
+"""
+            )
+        else:
+            raise errors.StreamlitAPIException(e)
 
 
 def bytes_to_data_frame(source: bytes) -> DataFrame:


Test patch:
diff --git a/lib/tests/streamlit/type_util_test.py b/lib/tests/streamlit/type_util_test.py
--- a/lib/tests/streamlit/type_util_test.py
+++ b/lib/tests/streamlit/type_util_test.py
@@ -16,10 +16,12 @@
 from collections import namedtuple
 from unittest.mock import patch
 
+import pandas as pd
 import plotly.graph_objs as go
 
 from streamlit import type_util
-from streamlit.type_util import is_bytes_like, to_bytes
+from streamlit.type_util import data_frame_to_bytes, is_bytes_like, to_bytes
+from streamlit.errors import StreamlitAPIException
 
 
 class TypeUtilTest(unittest.TestCase):
@@ -88,3 +90,10 @@ def test_to_bytes(self):
         self.assertFalse(is_bytes_like(string_obj))
         with self.assertRaises(RuntimeError):
             to_bytes(string_obj)
+
+    def test_data_frame_to_bytes_numpy_dtype_exception(self):
+        df1 = pd.DataFrame(["foo", "bar"])
+        df2 = pd.DataFrame(df1.dtypes)
+
+        with self.assertRaises(StreamlitAPIException):
+            data_frame_to_bytes(df2)