Skip to content

Commit dead70f

Browse files
keewispre-commit-ci[bot]TomNicholasdcherian
authored
friendlier error messages for missing chunk managers (#9676)
* raise an error message while guessing if there's no chunkmanager available * don't skip the no chunkmanager test if dask is not installed * whats-new * ensure at least one chunk manager is available * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove additional blank line from a bad merge * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * improve the wording Co-authored-by: Tom Nicholas <tom@cworthy.org> * switch to ImportError * raise a helpful `ImportError` for known chunk managers * make sure the new `ImportError` is actually raised * check that the more specific error message is preferred * prefer the more specific error * also use `ImportError` as indicator for `chunks=None` * move and improve the whats-new entry * captialize global variable KNOWN_CHUNKMANAGERS * chunkmanagers -> available_chunkmanagers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * also use the string repr for printing `manager` * reword * more repr * reflow * adapt the test to the new error message --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tom Nicholas <tom@cworthy.org> Co-authored-by: Deepak Cherian <dcherian@users.noreply.github.com>
1 parent 6875c64 commit dead70f

File tree

4 files changed

+54
-14
lines changed

4 files changed

+54
-14
lines changed

doc/whats-new.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ v.2024.12.0 (unreleased)
2121

2222
New Features
2323
~~~~~~~~~~~~
24+
- Improve the error message raised when using chunked-array methods if no chunk manager is available or if the requested chunk manager is missing (:pull:`9676`)
25+
By `Justus Magin <https://github.com/keewis>`_. (:pull:`9676`)
2426
- Better support wrapping additional array types (e.g. ``cupy`` or ``jax``) by calling generalized
2527
duck array operations throughout more xarray methods. (:issue:`7848`, :pull:`9798`).
2628
By `Sam Levang <https://github.com/slevang>`_.

xarray/backends/zarr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1471,7 +1471,7 @@ def open_zarr(
14711471
) # attempt to import that parallel backend
14721472

14731473
chunks = {}
1474-
except ValueError:
1474+
except (ValueError, ImportError):
14751475
chunks = None
14761476

14771477
if kwargs:

xarray/namedarray/parallelcompat.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ def compute(
4646

4747
T_ChunkedArray = TypeVar("T_ChunkedArray", bound=ChunkedArrayMixinProtocol)
4848

49+
KNOWN_CHUNKMANAGERS = {
50+
"dask": "dask",
51+
"cubed": "cubed-xarray",
52+
"arkouda": "arkouda-xarray",
53+
}
54+
4955

5056
@functools.lru_cache(maxsize=1)
5157
def list_chunkmanagers() -> dict[str, ChunkManagerEntrypoint[Any]]:
@@ -95,29 +101,42 @@ def guess_chunkmanager(
95101
Else use whatever is installed, defaulting to dask if there are multiple options.
96102
"""
97103

98-
chunkmanagers = list_chunkmanagers()
104+
available_chunkmanagers = list_chunkmanagers()
99105

100106
if manager is None:
101-
if len(chunkmanagers) == 1:
107+
if len(available_chunkmanagers) == 1:
102108
# use the only option available
103-
manager = next(iter(chunkmanagers.keys()))
109+
manager = next(iter(available_chunkmanagers.keys()))
104110
else:
105111
# use the one in options (default dask)
106112
manager = OPTIONS["chunk_manager"]
107113

108114
if isinstance(manager, str):
109-
if manager not in chunkmanagers:
115+
if manager not in available_chunkmanagers and manager in KNOWN_CHUNKMANAGERS:
116+
raise ImportError(
117+
f"chunk manager {manager!r} is not available."
118+
f" Please make sure {KNOWN_CHUNKMANAGERS[manager]!r} is installed"
119+
" and importable."
120+
)
121+
elif len(available_chunkmanagers) == 0:
122+
raise ImportError(
123+
"no chunk managers available. Try installing `dask` or another package"
124+
" that provides a chunk manager."
125+
)
126+
elif manager not in available_chunkmanagers:
110127
raise ValueError(
111-
f"unrecognized chunk manager {manager} - must be one of: {list(chunkmanagers)}"
128+
f"unrecognized chunk manager {manager!r} - must be one of the installed"
129+
f" chunk managers: {list(available_chunkmanagers)}"
112130
)
113131

114-
return chunkmanagers[manager]
132+
return available_chunkmanagers[manager]
115133
elif isinstance(manager, ChunkManagerEntrypoint):
116134
# already a valid ChunkManager so just pass through
117135
return manager
118136
else:
119137
raise TypeError(
120-
f"manager must be a string or instance of ChunkManagerEntrypoint, but received type {type(manager)}"
138+
"manager must be a string or instance of ChunkManagerEntrypoint,"
139+
f" but received type {type(manager)}"
121140
)
122141

123142

xarray/tests/test_parallelcompat.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@
1111
from xarray.namedarray._typing import _Chunks
1212
from xarray.namedarray.daskmanager import DaskManager
1313
from xarray.namedarray.parallelcompat import (
14+
KNOWN_CHUNKMANAGERS,
1415
ChunkManagerEntrypoint,
1516
get_chunked_array_type,
1617
guess_chunkmanager,
1718
list_chunkmanagers,
1819
load_chunkmanagers,
1920
)
20-
from xarray.tests import has_dask, requires_dask
21+
from xarray.tests import requires_dask
2122

2223

2324
class DummyChunkedArray(np.ndarray):
@@ -158,18 +159,36 @@ def test_get_chunkmanger_via_set_options(self, register_dummy_chunkmanager) -> N
158159
chunkmanager = guess_chunkmanager(None)
159160
assert isinstance(chunkmanager, DummyChunkManager)
160161

161-
def test_fail_on_nonexistent_chunkmanager(self) -> None:
162-
with pytest.raises(ValueError, match="unrecognized chunk manager foo"):
162+
def test_fail_on_known_but_missing_chunkmanager(
163+
self, register_dummy_chunkmanager, monkeypatch
164+
) -> None:
165+
monkeypatch.setitem(KNOWN_CHUNKMANAGERS, "test", "test-package")
166+
with pytest.raises(
167+
ImportError, match="chunk manager 'test' is not available.+test-package"
168+
):
169+
guess_chunkmanager("test")
170+
171+
def test_fail_on_nonexistent_chunkmanager(
172+
self, register_dummy_chunkmanager
173+
) -> None:
174+
with pytest.raises(ValueError, match="unrecognized chunk manager 'foo'"):
163175
guess_chunkmanager("foo")
164176

165177
@requires_dask
166178
def test_get_dask_if_installed(self) -> None:
167179
chunkmanager = guess_chunkmanager(None)
168180
assert isinstance(chunkmanager, DaskManager)
169181

170-
@pytest.mark.skipif(has_dask, reason="requires dask not to be installed")
171-
def test_dont_get_dask_if_not_installed(self) -> None:
172-
with pytest.raises(ValueError, match="unrecognized chunk manager dask"):
182+
def test_no_chunk_manager_available(self, monkeypatch) -> None:
183+
monkeypatch.setattr("xarray.namedarray.parallelcompat.list_chunkmanagers", dict)
184+
with pytest.raises(ImportError, match="no chunk managers available"):
185+
guess_chunkmanager("foo")
186+
187+
def test_no_chunk_manager_available_but_known_manager_requested(
188+
self, monkeypatch
189+
) -> None:
190+
monkeypatch.setattr("xarray.namedarray.parallelcompat.list_chunkmanagers", dict)
191+
with pytest.raises(ImportError, match="chunk manager 'dask' is not available"):
173192
guess_chunkmanager("dask")
174193

175194
@requires_dask

0 commit comments

Comments
 (0)