From 7bee65af82276816a7a065cef2591ea53b8f8f6d Mon Sep 17 00:00:00 2001 From: Stephan Hoyer Date: Wed, 19 May 2021 15:04:26 -0700 Subject: [PATCH] Add xarray.backends.NoMatchingEngineError Fixes GH5329 --- doc/api.rst | 1 + doc/whats-new.rst | 5 +++++ xarray/backends/__init__.py | 3 ++- xarray/backends/api.py | 7 +++++++ xarray/backends/plugins.py | 11 ++++++++--- xarray/tests/test_backends.py | 5 +++-- xarray/tests/test_plugins.py | 9 ++++++--- 7 files changed, 32 insertions(+), 9 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index fbd0a75bb41..36916c4d396 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -887,6 +887,7 @@ Exceptions MergeError SerializationWarning + backends.NoMatchingEngineError Advanced API ============ diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 8e4e40546b1..efa0c792f10 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -26,6 +26,11 @@ New Features Breaking changes ~~~~~~~~~~~~~~~~ +- :py:func:`open_dataset` now raises ``xarray.backends.NoMatchingEngineError`` + when the ``engine`` keyword is not provided and it is unable to detect the + correct engine to use when opening a file (:issue:`5329`). + Previously (in xarray 0.18.0 and 0.18.1), this was ``ValueError``. + By `Stephan Hoyer `_ Deprecations ~~~~~~~~~~~~ diff --git a/xarray/backends/__init__.py b/xarray/backends/__init__.py index 2ebf7a4244b..ec0743fef82 100644 --- a/xarray/backends/__init__.py +++ b/xarray/backends/__init__.py @@ -9,7 +9,7 @@ from .h5netcdf_ import H5NetCDFStore from .memory import InMemoryDataStore from .netCDF4_ import NetCDF4DataStore -from .plugins import list_engines +from .plugins import NoMatchingEngineError, list_engines from .pseudonetcdf_ import PseudoNetCDFDataStore from .pydap_ import PydapDataStore from .pynio_ import NioDataStore @@ -33,4 +33,5 @@ "ZarrStore", "PseudoNetCDFDataStore", "list_engines", + "NoMatchingEngineError", ] diff --git a/xarray/backends/api.py b/xarray/backends/api.py index e950baed5e0..326801e9b5d 100644 --- a/xarray/backends/api.py +++ b/xarray/backends/api.py @@ -453,6 +453,13 @@ def open_dataset( dataset : Dataset The newly created dataset. + Raises + ------ + xarray.backends.NoMatchingEngineError + If ``engine`` is not provided and automatic engine detection fails, + either because the file-type cannot be inferred or because the necessary + backend dependencies are not installed. + Notes ----- ``open_dataset`` opens the file with read-only access. When you modify diff --git a/xarray/backends/plugins.py b/xarray/backends/plugins.py index 633459239c2..4f82071d7cc 100644 --- a/xarray/backends/plugins.py +++ b/xarray/backends/plugins.py @@ -96,6 +96,10 @@ def list_engines(): return build_engines(pkg_entrypoints) +class NoMatchingEngineError(Exception): + pass + + def guess_engine(store_spec): engines = list_engines() @@ -108,16 +112,17 @@ def guess_engine(store_spec): installed = [k for k in engines if k != "store"] if installed: - raise ValueError( + raise NoMatchingEngineError( "did not find a match in any of xarray's currently installed IO " - f"backends {installed}. Consider explicitly selecting one of the " + f"backends {installed}. The provided file may not exist: " + f"{store_spec}\n\nConsider 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( + raise NoMatchingEngineError( "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" diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index 908d2f4940d..e97dded596e 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -2772,7 +2772,8 @@ def test_open_badbytes(self): with open_dataset(b"\211HDF\r\n\032\n", engine="h5netcdf"): pass with pytest.raises( - ValueError, match=r"match in any of xarray's currently installed IO" + backends.NoMatchingEngineError, + match=r"match in any of xarray's currently installed IO", ): with open_dataset(b"garbage"): pass @@ -2826,7 +2827,7 @@ def test_open_fileobj(self): with open(tmp_file, "rb") as f: f.seek(8) with pytest.raises( - ValueError, + backends.NoMatchingEngineError, match="match in any of xarray's currently installed IO", ): with pytest.warns( diff --git a/xarray/tests/test_plugins.py b/xarray/tests/test_plugins.py index b35971e185b..6ef37d6c4ac 100644 --- a/xarray/tests/test_plugins.py +++ b/xarray/tests/test_plugins.py @@ -3,7 +3,7 @@ import pkg_resources import pytest -from xarray.backends import common, plugins +from xarray.backends import NoMatchingEngineError, common, plugins class DummyBackendEntrypointArgs(common.BackendEntrypoint): @@ -165,7 +165,7 @@ def test_build_engines_sorted(): ) def test_no_matching_engine_found(): with pytest.raises( - ValueError, match="match in any of xarray's currently installed IO" + NoMatchingEngineError, match="match in any of xarray's currently installed IO" ): plugins.guess_engine("not-valid") @@ -175,5 +175,8 @@ def test_no_matching_engine_found(): mock.MagicMock(return_value={}), ) def test_no_engines_installed(): - with pytest.raises(ValueError, match="no currently installed IO backends."): + with pytest.raises( + NoMatchingEngineError, + match="no currently installed IO backends.", + ): plugins.guess_engine("not-valid")