Repository: ibis-project/ibis

Issue:
bug: interactive repr silenty fails for unsupported datatypes
### What happened?

We currently don't have handling in PyArrow for interval columns (which we should fix, separate issue incoming), but when there is a missing handler for a given datatype, the repr simply fails to appear and doesn't raise an error or a warning.  See the below example for this behavior with an interval column:

```python
[ins] In [1]: from ibis.interactive import *

[ins] In [2]: con = ibis.duckdb.connect()
t
[ins] In [3]: t = con.read_parquet(
         ...:     "ci/ibis-testing-data/parquet/functional_alltypes.parquet",
         ...:     table_name="alltypes",
         ...: )

[ins] In [4]: t = t.mutate(date_col=t.timestamp_col.cast("date")).mutate(
         ...:     date_diff=_.date_col - _.date_col
         ...: )

[ins] In [5]: t
Out[5]: 
```

That's where the output stops.  Just with a blank `Out[5]`.  
`to_pandas` will show the expected output of the column.
An explicit call to `to_pyarrow` will raise the error that is getting swallowed somewhere in the interactive repr code.

```python
[ins] In [6]: t.to_pandas()
Out[6]: 
        id  bool_col  tinyint_col  smallint_col  int_col  bigint_col  float_col  double_col date_string_col string_col           timestamp_col  year  month    date_col date_diff
0     6690      True            0             0        0           0        0.0         0.0        11/01/10          0 2010-11-01 00:00:00.000  2010     11  2010-11-01    0 days
1     6691     False            1             1        1          10        1.1        10.1        11/01/10          1 2010-11-01 00:01:00.000  2010     11  2010-11-01    0 days
2     6692      True            2             2        2          20        2.2        20.2        11/01/10          2 2010-11-01 00:02:00.100  2010     11  2010-11-01    0 days
3     6693     False            3             3        3          30        3.3        30.3        11/01/10          3 2010-11-01 00:03:00.300  2010     11  2010-11-01    0 days
4     6694      True            4             4        4          40        4.4        40.4        11/01/10          4 2010-11-01 00:04:00.600  2010     11  2010-11-01    0 days
...    ...       ...          ...           ...      ...         ...        ...         ...             ...        ...                     ...   ...    ...         ...       ...
7295  3955     False            5             5        5          50        5.5        50.5        01/31/10          5 2010-01-31 05:05:13.600  2010      1  2010-01-31    0 days
7296  3956      True            6             6        6          60        6.6        60.6        01/31/10          6 2010-01-31 05:06:13.650  2010      1  2010-01-31    0 days
7297  3957     False            7             7        7          70        7.7        70.7        01/31/10          7 2010-01-31 05:07:13.710  2010      1  2010-01-31    0 days
7298  3958      True            8             8        8          80        8.8        80.8        01/31/10          8 2010-01-31 05:08:13.780  2010      1  2010-01-31    0 days
7299  3959     False            9             9        9          90        9.9        90.9        01/31/10          9 2010-01-31 05:09:13.860  2010      1  2010-01-31    0 days

[7300 rows x 15 columns]

[ins] In [7]: t.to_pyarrow()
---------------------------------------------------------------------------
ArrowNotImplementedError                  Traceback (most recent call last)
Cell In[7], line 1
----> 1 t.to_pyarrow()

File ~/github.com/ibis-project/ibis/ibis/expr/types/core.py:421, in Expr.to_pyarrow(self, params, limit, **kwargs)
    393 @experimental
    394 def to_pyarrow(
    395     self,
   (...)
    399     **kwargs: Any,
    400 ) -> pa.Table:
    401     """Execute expression and return results in as a pyarrow table.
    402 
    403     This method is eager and will execute the associated expression
   (...)
    419         A pyarrow table holding the results of the executed expression.
    420     """
--> 421     return self._find_backend(use_default=True).to_pyarrow(
    422         self, params=params, limit=limit, **kwargs
    423     )

File ~/github.com/ibis-project/ibis/ibis/backends/duckdb/__init__.py:1048, in Backend.to_pyarrow(self, expr, params, limit, **_)
   1045 with self.begin() as con:
   1046     table = con.connection.sql(sql).to_arrow_table()
-> 1048 return expr.__pyarrow_result__(table)

File ~/github.com/ibis-project/ibis/ibis/expr/types/relations.py:168, in Table.__pyarrow_result__(self, table, data_mapper)
    165 if data_mapper is None:
    166     from ibis.formats.pyarrow import PyArrowData as data_mapper
--> 168 return data_mapper.convert_table(table, self.schema())

File ~/github.com/ibis-project/ibis/ibis/formats/pyarrow.py:292, in PyArrowData.convert_table(cls, table, schema)
    289     table = table.rename_columns(schema.names)
    291 if pa_schema != desired_schema:
--> 292     return table.cast(desired_schema, safe=False)
    293 else:
    294     return table

File /nix/store/0lc3sih4525bhlxf0qws9lnhxn7n1n29-python3-3.10.13-env/lib/python3.10/site-packages/pyarrow/table.pxi:3806, in pyarrow.lib.Table.cast()

File /nix/store/0lc3sih4525bhlxf0qws9lnhxn7n1n29-python3-3.10.13-env/lib/python3.10/site-packages/pyarrow/table.pxi:566, in pyarrow.lib.ChunkedArray.cast()

File /nix/store/0lc3sih4525bhlxf0qws9lnhxn7n1n29-python3-3.10.13-env/lib/python3.10/site-packages/pyarrow/compute.py:404, in cast(arr, target_type, safe, options, memory_pool)
    402     else:
    403         options = CastOptions.safe(target_type)
--> 404 return call_function("cast", [arr], options, memory_pool)

File /nix/store/0lc3sih4525bhlxf0qws9lnhxn7n1n29-python3-3.10.13-env/lib/python3.10/site-packages/pyarrow/_compute.pyx:590, in pyarrow._compute.call_function()

File /nix/store/0lc3sih4525bhlxf0qws9lnhxn7n1n29-python3-3.10.13-env/lib/python3.10/site-packages/pyarrow/_compute.pyx:385, in pyarrow._compute.Function.call()

File /nix/store/0lc3sih4525bhlxf0qws9lnhxn7n1n29-python3-3.10.13-env/lib/python3.10/site-packages/pyarrow/error.pxi:154, in pyarrow.lib.pyarrow_internal_check_status()

File /nix/store/0lc3sih4525bhlxf0qws9lnhxn7n1n29-python3-3.10.13-env/lib/python3.10/site-packages/pyarrow/error.pxi:91, in pyarrow.lib.check_status()

ArrowNotImplementedError: Unsupported cast from int64 to month_day_nano_interval using function cast_month_day_nano_interval

```


### What version of ibis are you using?

main

### What backend(s) are you using, if any?

DuckDB

### Relevant log output

_No response_

### Code of Conduct

- [X] I agree to follow this project's Code of Conduct


Discussion:
This should raise an error.

Also, we have a utility in the hypothesis tests to generate sample data from an ibis schema that would be good for testing this.

Resolving patch:
diff --git a/ibis/expr/types/relations.py b/ibis/expr/types/relations.py
--- a/ibis/expr/types/relations.py
+++ b/ibis/expr/types/relations.py
@@ -365,7 +365,25 @@ def __interactive_rich_console__(self, console, options):
         else:
             width = options.max_width
 
-        table = to_rich_table(self, width)
+        try:
+            table = to_rich_table(self, width)
+        except Exception as e:
+            # In IPython exceptions inside of _repr_mimebundle_ are swallowed to
+            # allow calling several display functions and choosing to display
+            # the "best" result based on some priority.
+            # This behavior, though, means that exceptions that bubble up inside of the interactive repr
+            # are silently caught.
+            #
+            # We can't stop the exception from being swallowed, but we can force
+            # the display of that exception as we do here.
+            #
+            # A _very_ annoying caveat is that this exception is _not_ being
+            # ` raise`d, it is only being printed to the console.  This means
+            # that you cannot "catch" it.
+            #
+            # This restriction is only present in IPython, not in other REPLs.
+            console.print_exception()
+            raise e
         return console.render(table, options=options)
 
     def __getitem__(self, what):


Test patch: