Skip to content

Commit 56d8c36

Browse files
committed
Merge branch 'main' into coarsen_reshape
* main: Improve error message for guess engine (pydata#5455) Refactor dataset groupby tests (pydata#5506) DOC: zarr note on encoding (pydata#5427) Allow plotting categorical data (pydata#5464)
2 parents 4b69c9f + eea7673 commit 56d8c36

19 files changed

+349
-257
lines changed

doc/whats-new.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ New Features
4646
By `Thomas Hirtz <https://github.com/thomashirtz>`_.
4747
- allow passing a function to ``combine_attrs`` (:pull:`4896`).
4848
By `Justus Magin <https://github.com/keewis>`_.
49+
- Allow plotting categorical data (:pull:`5464`).
50+
By `Jimmy Westling <https://github.com/illviljan>`_.
4951

5052
Breaking changes
5153
~~~~~~~~~~~~~~~~

xarray/backends/cfgrib_.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ def get_encoding(self):
9494

9595

9696
class CfgribfBackendEntrypoint(BackendEntrypoint):
97+
available = has_cfgrib
98+
9799
def guess_can_open(self, filename_or_obj):
98100
try:
99101
_, ext = os.path.splitext(filename_or_obj)
@@ -147,5 +149,4 @@ def open_dataset(
147149
return ds
148150

149151

150-
if has_cfgrib:
151-
BACKEND_ENTRYPOINTS["cfgrib"] = CfgribfBackendEntrypoint
152+
BACKEND_ENTRYPOINTS["cfgrib"] = CfgribfBackendEntrypoint

xarray/backends/h5netcdf_.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,8 @@ def close(self, **kwargs):
337337

338338

339339
class H5netcdfBackendEntrypoint(BackendEntrypoint):
340+
available = has_h5netcdf
341+
340342
def guess_can_open(self, filename_or_obj):
341343
magic_number = try_read_magic_number_from_file_or_path(filename_or_obj)
342344
if magic_number is not None:
@@ -394,5 +396,4 @@ def open_dataset(
394396
return ds
395397

396398

397-
if has_h5netcdf:
398-
BACKEND_ENTRYPOINTS["h5netcdf"] = H5netcdfBackendEntrypoint
399+
BACKEND_ENTRYPOINTS["h5netcdf"] = H5netcdfBackendEntrypoint

xarray/backends/netCDF4_.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,8 @@ def close(self, **kwargs):
512512

513513

514514
class NetCDF4BackendEntrypoint(BackendEntrypoint):
515+
available = has_netcdf4
516+
515517
def guess_can_open(self, filename_or_obj):
516518
if isinstance(filename_or_obj, str) and is_remote_uri(filename_or_obj):
517519
return True
@@ -573,5 +575,4 @@ def open_dataset(
573575
return ds
574576

575577

576-
if has_netcdf4:
577-
BACKEND_ENTRYPOINTS["netcdf4"] = NetCDF4BackendEntrypoint
578+
BACKEND_ENTRYPOINTS["netcdf4"] = NetCDF4BackendEntrypoint

xarray/backends/plugins.py

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ def sort_backends(backend_entrypoints):
8181

8282

8383
def build_engines(pkg_entrypoints):
84-
backend_entrypoints = BACKEND_ENTRYPOINTS.copy()
84+
backend_entrypoints = {}
85+
for backend_name, backend in BACKEND_ENTRYPOINTS.items():
86+
if backend.available:
87+
backend_entrypoints[backend_name] = backend
8588
pkg_entrypoints = remove_duplicates(pkg_entrypoints)
8689
external_backend_entrypoints = backends_dict_from_pkg(pkg_entrypoints)
8790
backend_entrypoints.update(external_backend_entrypoints)
@@ -101,30 +104,49 @@ def guess_engine(store_spec):
101104

102105
for engine, backend in engines.items():
103106
try:
104-
if backend.guess_can_open and backend.guess_can_open(store_spec):
107+
if backend.guess_can_open(store_spec):
105108
return engine
106109
except Exception:
107110
warnings.warn(f"{engine!r} fails while guessing", RuntimeWarning)
108111

109-
installed = [k for k in engines if k != "store"]
110-
if installed:
111-
raise ValueError(
112-
"did not find a match in any of xarray's currently installed IO "
113-
f"backends {installed}. Consider explicitly selecting one of the "
114-
"installed backends via the ``engine`` parameter to "
115-
"xarray.open_dataset(), or installing additional IO dependencies:\n"
116-
"http://xarray.pydata.org/en/stable/getting-started-guide/installing.html\n"
117-
"http://xarray.pydata.org/en/stable/user-guide/io.html"
118-
)
112+
compatible_engines = []
113+
for engine, backend_cls in BACKEND_ENTRYPOINTS.items():
114+
try:
115+
backend = backend_cls()
116+
if backend.guess_can_open(store_spec):
117+
compatible_engines.append(engine)
118+
except Exception:
119+
warnings.warn(f"{engine!r} fails while guessing", RuntimeWarning)
120+
121+
installed_engines = [k for k in engines if k != "store"]
122+
if not compatible_engines:
123+
if installed_engines:
124+
error_msg = (
125+
"did not find a match in any of xarray's currently installed IO "
126+
f"backends {installed_engines}. Consider explicitly selecting one of the "
127+
"installed engines via the ``engine`` parameter, or installing "
128+
"additional IO dependencies, see:\n"
129+
"http://xarray.pydata.org/en/stable/getting-started-guide/installing.html\n"
130+
"http://xarray.pydata.org/en/stable/user-guide/io.html"
131+
)
132+
else:
133+
error_msg = (
134+
"xarray is unable to open this file because it has no currently "
135+
"installed IO backends. Xarray's read/write support requires "
136+
"installing optional IO dependencies, see:\n"
137+
"http://xarray.pydata.org/en/stable/getting-started-guide/installing.html\n"
138+
"http://xarray.pydata.org/en/stable/user-guide/io"
139+
)
119140
else:
120-
raise ValueError(
121-
"xarray is unable to open this file because it has no currently "
122-
"installed IO backends. Xarray's read/write support requires "
123-
"installing optional dependencies:\n"
124-
"http://xarray.pydata.org/en/stable/getting-started-guide/installing.html\n"
125-
"http://xarray.pydata.org/en/stable/user-guide/io.html"
141+
error_msg = (
142+
"found the following matches with the input file in xarray's IO "
143+
f"backends: {compatible_engines}. But their dependencies may not be installed, see:\n"
144+
"http://xarray.pydata.org/en/stable/user-guide/io.html \n"
145+
"http://xarray.pydata.org/en/stable/getting-started-guide/installing.html"
126146
)
127147

148+
raise ValueError(error_msg)
149+
128150

129151
def get_backend(engine):
130152
"""Select open_dataset method based on current engine."""

xarray/backends/pseudonetcdf_.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def close(self):
102102

103103

104104
class PseudoNetCDFBackendEntrypoint(BackendEntrypoint):
105+
available = has_pseudonetcdf
105106

106107
# *args and **kwargs are not allowed in open_backend_dataset_ kwargs,
107108
# unless the open_dataset_parameters are explicity defined like this:
@@ -153,5 +154,4 @@ def open_dataset(
153154
return ds
154155

155156

156-
if has_pseudonetcdf:
157-
BACKEND_ENTRYPOINTS["pseudonetcdf"] = PseudoNetCDFBackendEntrypoint
157+
BACKEND_ENTRYPOINTS["pseudonetcdf"] = PseudoNetCDFBackendEntrypoint

xarray/backends/pydap_.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ def get_dimensions(self):
110110

111111

112112
class PydapBackendEntrypoint(BackendEntrypoint):
113+
available = has_pydap
114+
113115
def guess_can_open(self, filename_or_obj):
114116
return isinstance(filename_or_obj, str) and is_remote_uri(filename_or_obj)
115117

@@ -154,5 +156,4 @@ def open_dataset(
154156
return ds
155157

156158

157-
if has_pydap:
158-
BACKEND_ENTRYPOINTS["pydap"] = PydapBackendEntrypoint
159+
BACKEND_ENTRYPOINTS["pydap"] = PydapBackendEntrypoint

xarray/backends/pynio_.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ def close(self):
9999

100100

101101
class PynioBackendEntrypoint(BackendEntrypoint):
102+
available = has_pynio
103+
102104
def open_dataset(
103105
self,
104106
filename_or_obj,
@@ -112,13 +114,13 @@ def open_dataset(
112114
mode="r",
113115
lock=None,
114116
):
117+
filename_or_obj = _normalize_path(filename_or_obj)
115118
store = NioDataStore(
116119
filename_or_obj,
117120
mode=mode,
118121
lock=lock,
119122
)
120123

121-
filename_or_obj = _normalize_path(filename_or_obj)
122124
store_entrypoint = StoreBackendEntrypoint()
123125
with close_on_error(store):
124126
ds = store_entrypoint.open_dataset(
@@ -134,5 +136,4 @@ def open_dataset(
134136
return ds
135137

136138

137-
if has_pynio:
138-
BACKEND_ENTRYPOINTS["pynio"] = PynioBackendEntrypoint
139+
BACKEND_ENTRYPOINTS["pynio"] = PynioBackendEntrypoint

xarray/backends/scipy_.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ def close(self):
238238

239239

240240
class ScipyBackendEntrypoint(BackendEntrypoint):
241+
available = has_scipy
242+
241243
def guess_can_open(self, filename_or_obj):
242244

243245
magic_number = try_read_magic_number_from_file_or_path(filename_or_obj)
@@ -290,5 +292,4 @@ def open_dataset(
290292
return ds
291293

292294

293-
if has_scipy:
294-
BACKEND_ENTRYPOINTS["scipy"] = ScipyBackendEntrypoint
295+
BACKEND_ENTRYPOINTS["scipy"] = ScipyBackendEntrypoint

xarray/backends/store.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55

66
class StoreBackendEntrypoint(BackendEntrypoint):
7+
available = True
8+
79
def guess_can_open(self, filename_or_obj):
810
return isinstance(filename_or_obj, AbstractDataStore)
911

0 commit comments

Comments
 (0)