Repository: nv-legate/cunumeric

Issue:
cannot access `__legate_data_interface` on arrays of a complex type
This program:
```
import cunumeric as np
x = np.zeros(5, dtype=np.complex128)
x.__legate_data_interface__
```

raises the following exception:
```
[0 - 700008d3b000]    1.279344 {6}{python}: python exception occurred within task:
Traceback (most recent call last):
  File "/Users/rohany/Documents/nvidia/install/lib/python3.9/site-packages/legion_top.py", line 440, in legion_python_main
    run_path(args[start], run_name='__main__')
  File "/Users/rohany/Documents/nvidia/install/lib/python3.9/site-packages/legion_top.py", line 255, in run_path
    exec(code, module.__dict__, module.__dict__)
  File "repro.py", line 22, in <module>
    x.__legate_data_interface__
  File "/Users/rohany/Documents/nvidia/install/lib/python3.9/site-packages/cunumeric/array.py", line 276, in __legate_data_interface__
    raise e
  File "/Users/rohany/Documents/nvidia/install/lib/python3.9/site-packages/cunumeric/array.py", line 273, in __legate_data_interface__
    arrow_type = pyarrow.from_numpy_dtype(self.dtype)
  File "pyarrow/types.pxi", line 3245, in pyarrow.lib.from_numpy_dtype
  File "pyarrow/error.pxi", line 121, in pyarrow.lib.check_status
pyarrow.lib.ArrowNotImplementedError: Unsupported numpy type 15
```


Discussion:

Resolving patch:
diff --git a/cunumeric/array.py b/cunumeric/array.py
--- a/cunumeric/array.py
+++ b/cunumeric/array.py
@@ -31,6 +31,7 @@
     cast,
 )
 
+import legate.core.types as ty
 import numpy as np
 import pyarrow  # type: ignore
 from legate.core import Array
@@ -187,6 +188,21 @@ def _convert_all_to_numpy(obj: Any) -> Any:
         return obj
 
 
+# FIXME: we can't give an accurate return type as mypy thinks
+# the pyarrow import can be ignored, and can't override the check
+# either, because no-any-unimported needs Python >= 3.10. We can
+# fix it once we bump up the Python version
+def convert_numpy_dtype_to_pyarrow(dtype: np.dtype[Any]) -> Any:
+    if dtype.kind != "c":
+        return pyarrow.from_numpy_dtype(dtype)
+    elif dtype == np.complex64:
+        return ty.complex64
+    elif dtype == np.complex128:
+        return ty.complex128
+    else:
+        raise ValueError(f"Unsupported NumPy dtype: {dtype}")
+
+
 @clone_np_ndarray
 class ndarray:
     def __init__(
@@ -269,7 +285,7 @@ def __legate_data_interface__(self) -> dict[str, Any]:
             # All of our thunks implement the Legate Store interface
             # so we just need to convert our type and stick it in
             # a Legate Array
-            arrow_type = pyarrow.from_numpy_dtype(self.dtype)
+            arrow_type = convert_numpy_dtype_to_pyarrow(self.dtype)
             # If the thunk is an eager array, we need to convert it to a
             # deferred array so we can extract a legate store
             deferred_thunk = runtime.to_deferred_array(self._thunk)


Test patch:
diff --git a/tests/integration/test_data_interface.py b/tests/integration/test_data_interface.py
--- a/tests/integration/test_data_interface.py
+++ b/tests/integration/test_data_interface.py
@@ -16,6 +16,9 @@
 import pytest
 
 import cunumeric as num
+from cunumeric.runtime import _supported_dtypes
+
+DTYPES = _supported_dtypes.keys()
 
 
 # A simple wrapper with a legate data interface implementation for testing
@@ -28,8 +31,9 @@ def __legate_data_interface__(self):
         return self.wrapped
 
 
-def test_roundtrip():
-    arr1 = num.array([1, 2, 3, 4], dtype=num.float64)
+@pytest.mark.parametrize("dtype", DTYPES)
+def test_roundtrip(dtype):
+    arr1 = num.array([1, 2, 3, 4], dtype=dtype)
     data = Wrapper(arr1.__legate_data_interface__)
     arr2 = num.asarray(data)
     assert num.array_equal(arr1, arr2)