diff --git a/xarray/namedarray/parallelcompat.py b/xarray/namedarray/parallelcompat.py index c6263bff4ff..c923e55db8b 100644 --- a/xarray/namedarray/parallelcompat.py +++ b/xarray/namedarray/parallelcompat.py @@ -121,11 +121,13 @@ def guess_chunkmanager( ) -def get_chunked_array_type(*args: Any) -> ChunkManagerEntrypoint[Any]: +def get_chunked_array_type(*args: Any) -> ChunkManagerEntrypoint[Any] | None: """ Detects which parallel backend should be used for given set of arrays. Also checks that all arrays are of same chunking type (i.e. not a mix of cubed and dask). + + Returns None if no matching ChunkManager is found. """ # TODO this list is probably redundant with something inside xarray.apply_ufunc @@ -155,9 +157,7 @@ def get_chunked_array_type(*args: Any) -> ChunkManagerEntrypoint[Any]: if chunkmanager.is_chunked_array(chunked_arr) ] if not selected: - raise TypeError( - f"Could not find a Chunk Manager which recognises type {type(chunked_arr)}" - ) + return None elif len(selected) >= 2: raise TypeError(f"Multiple ChunkManagers recognise type {type(chunked_arr)}") else: diff --git a/xarray/namedarray/pycompat.py b/xarray/namedarray/pycompat.py index 3ce33d4d8ea..84854ffb33a 100644 --- a/xarray/namedarray/pycompat.py +++ b/xarray/namedarray/pycompat.py @@ -108,7 +108,8 @@ def to_numpy( # TODO first attempt to call .to_numpy() once some libraries implement it if is_chunked_array(data): chunkmanager = get_chunked_array_type(data) - data, *_ = chunkmanager.compute(data, **kwargs) + if chunkmanager is not None: + data, *_ = chunkmanager.compute(data, **kwargs) if isinstance(data, array_type("cupy")): data = data.get() # pint has to be imported dynamically as pint imports xarray @@ -127,8 +128,9 @@ def to_duck_array(data: Any, **kwargs: dict[str, Any]) -> duckarray[_ShapeType, if is_chunked_array(data): chunkmanager = get_chunked_array_type(data) - loaded_data, *_ = chunkmanager.compute(data, **kwargs) # type: ignore[var-annotated] - return loaded_data + if chunkmanager is not None: + loaded_data, *_ = chunkmanager.compute(data, **kwargs) # type: ignore[var-annotated] + return loaded_data if isinstance(data, ExplicitlyIndexed): return data.get_duck_array() # type: ignore[no-untyped-call, no-any-return] diff --git a/xarray/tests/test_parallelcompat.py b/xarray/tests/test_parallelcompat.py index d4a4e273bc0..a96ed31c04b 100644 --- a/xarray/tests/test_parallelcompat.py +++ b/xarray/tests/test_parallelcompat.py @@ -195,10 +195,7 @@ def test_raise_if_no_arrays_chunked(self, register_dummy_chunkmanager) -> None: def test_raise_if_no_matching_chunkmanagers(self) -> None: dummy_arr = DummyChunkedArray([1, 2, 3]) - with pytest.raises( - TypeError, match="Could not find a Chunk Manager which recognises" - ): - get_chunked_array_type(dummy_arr) + assert get_chunked_array_type(dummy_arr) is None @requires_dask def test_detect_dask_if_installed(self) -> None: diff --git a/xarray/tests/test_variable.py b/xarray/tests/test_variable.py index 6575f2e1740..12a170ad75e 100644 --- a/xarray/tests/test_variable.py +++ b/xarray/tests/test_variable.py @@ -29,7 +29,7 @@ from xarray.core.types import T_DuckArray from xarray.core.utils import NDArrayMixin from xarray.core.variable import as_compatible_data, as_variable -from xarray.namedarray.pycompat import array_type +from xarray.namedarray.pycompat import array_type, is_chunked_array from xarray.tests import ( assert_allclose, assert_array_equal, @@ -2722,6 +2722,27 @@ def __init__(self, array): orig = Variable(dims=(), data=array) assert isinstance(orig._data.item(), CustomWithValuesAttr) + def test_duck_array_with_chunks(self): + # Non indexable type + class CustomArray(NDArrayMixin): + def __init__(self, array): + self.array = array + + @property + def chunks(self): + return self.shape + + def __array_function__(self, *args, **kwargs): + return NotImplemented + + def __array_ufunc__(self, *args, **kwargs): + return NotImplemented + + array = CustomArray(np.arange(3)) + assert is_chunked_array(array) + var = Variable(dims=("x"), data=array) + var.load() + def test_raise_no_warning_for_nan_in_binary_ops(): with assert_no_warnings():