Skip to content

Commit

Permalink
Better error message when no backend engine is found. (#5300)
Browse files Browse the repository at this point in the history
* Better error message when no backend engine is found.

I consider this progress towards fixing GH5291 but not a complete fix
yet.

* Better error message for tutorial datasets

* flake8
  • Loading branch information
shoyer authored May 18, 2021
1 parent 49aa235 commit 24c6152
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 4 deletions.
19 changes: 18 additions & 1 deletion xarray/backends/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,24 @@ def guess_engine(store_spec):
except Exception:
warnings.warn(f"{engine!r} fails while guessing", RuntimeWarning)

raise ValueError("cannot guess the engine, try passing one explicitly")
installed = [k for k in engines if k != "store"]
if installed:
raise ValueError(
"did not find a match in any of xarray's currently installed IO "
f"backends {installed}. Consider explicitly selecting one of the "
"installed backends via the ``engine`` parameter to "
"xarray.open_dataset(), or installing additional IO dependencies:\n"
"http://xarray.pydata.org/en/stable/getting-started-guide/installing.html\n"
"http://xarray.pydata.org/en/stable/user-guide/io.html"
)
else:
raise ValueError(
"xarray is unable to open this file because it has no currently "
"installed IO backends. Xarray's read/write support requires "
"installing optional dependencies:\n"
"http://xarray.pydata.org/en/stable/getting-started-guide/installing.html\n"
"http://xarray.pydata.org/en/stable/user-guide/io.html"
)


def get_backend(engine):
Expand Down
9 changes: 7 additions & 2 deletions xarray/tests/test_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -2771,7 +2771,9 @@ def test_open_badbytes(self):
with pytest.raises(ValueError, match=r"HDF5 as bytes"):
with open_dataset(b"\211HDF\r\n\032\n", engine="h5netcdf"):
pass
with pytest.raises(ValueError, match=r"cannot guess the engine"):
with pytest.raises(
ValueError, match=r"match in any of xarray's currently installed IO"
):
with open_dataset(b"garbage"):
pass
with pytest.raises(ValueError, match=r"can only read bytes"):
Expand Down Expand Up @@ -2823,7 +2825,10 @@ def test_open_fileobj(self):
# `raises_regex`?). Ref https://github.com/pydata/xarray/pull/5191
with open(tmp_file, "rb") as f:
f.seek(8)
with pytest.raises(ValueError, match="cannot guess the engine"):
with pytest.raises(
ValueError,
match="match in any of xarray's currently installed IO",
):
with pytest.warns(
RuntimeWarning,
match=re.escape("'h5netcdf' fails while guessing"),
Expand Down
20 changes: 20 additions & 0 deletions xarray/tests/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,23 @@ def test_build_engines_sorted():

assert set(indices) < {0, -1}
assert list(backend_entrypoints) == sorted(backend_entrypoints)


@mock.patch(
"xarray.backends.plugins.list_engines",
mock.MagicMock(return_value={"dummy": DummyBackendEntrypointArgs()}),
)
def test_no_matching_engine_found():
with pytest.raises(
ValueError, match="match in any of xarray's currently installed IO"
):
plugins.guess_engine("not-valid")


@mock.patch(
"xarray.backends.plugins.list_engines",
mock.MagicMock(return_value={}),
)
def test_no_engines_installed():
with pytest.raises(ValueError, match="no currently installed IO backends."):
plugins.guess_engine("not-valid")
42 changes: 41 additions & 1 deletion xarray/tutorial.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,48 @@ def _construct_cache_dir(path):
"RGB.byte": "https://github.com/mapbox/rasterio/raw/1.2.1/tests/data/RGB.byte.tif",
"shade": "https://github.com/mapbox/rasterio/raw/1.2.1/tests/data/shade.tif",
}
file_formats = {
"air_temperature": 3,
"rasm": 3,
"ROMS_example": 4,
"tiny": 3,
"eraint_uvz": 3,
}


def _check_netcdf_engine_installed(name):
version = file_formats.get(name)
if version == 3:
try:
import scipy # noqa
except ImportError:
try:
import netCDF4 # noqa
except ImportError:
raise ImportError(
f"opening tutorial dataset {name} requires either scipy or "
"netCDF4 to be installed."
)
if version == 4:
try:
import h5netcdf # noqa
except ImportError:
try:
import netCDF4 # noqa
except ImportError:
raise ImportError(
f"opening tutorial dataset {name} requires either h5netcdf "
"or netCDF4 to be installed."
)


# idea borrowed from Seaborn
def open_dataset(
name,
cache=True,
cache_dir=None,
*,
engine=None,
**kws,
):
"""
Expand Down Expand Up @@ -94,13 +129,18 @@ def open_dataset(
if not path.suffix:
# process the name
default_extension = ".nc"
if engine is None:
_check_netcdf_engine_installed(name)
path = path.with_suffix(default_extension)
elif path.suffix == ".grib":
if engine is None:
engine = "cfgrib"

url = f"{base_url}/raw/{version}/{path.name}"

# retrieve the file
filepath = pooch.retrieve(url=url, known_hash=None, path=cache_dir)
ds = _open_dataset(filepath, **kws)
ds = _open_dataset(filepath, engine=engine, **kws)
if not cache:
ds = ds.load()
pathlib.Path(filepath).unlink()
Expand Down

0 comments on commit 24c6152

Please sign in to comment.