diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 89e9fbef56f..4b7bf8b2afd 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -35,6 +35,13 @@ Internal Changes ~~~~~~~~~~~~~~~~ +Performance +~~~~~~~~~~~ + +- Add a fastpath to the backend plugin system for standard engines (:issue:`10178`, :pull:`10937`). + By `Sam Levang `_. + + .. _whats-new.2025.12.0: v2025.12.0 (Dec 5, 2025) diff --git a/xarray/backends/plugins.py b/xarray/backends/plugins.py index 97455d8acc0..6e1784af5b6 100644 --- a/xarray/backends/plugins.py +++ b/xarray/backends/plugins.py @@ -215,15 +215,21 @@ def guess_engine( def get_backend(engine: str | type[BackendEntrypoint]) -> BackendEntrypoint: """Select open_dataset method based on current engine.""" if isinstance(engine, str): - engines = list_engines() - if engine not in engines: - raise ValueError( - f"unrecognized engine '{engine}' must be one of your download engines: {list(engines)}. " - "To install additional dependencies, see:\n" - "https://docs.xarray.dev/en/stable/user-guide/io.html \n" - "https://docs.xarray.dev/en/stable/getting-started-guide/installing.html" - ) - backend = engines[engine] + if engine in BACKEND_ENTRYPOINTS: + # fast path for built-in engines + backend_cls = BACKEND_ENTRYPOINTS[engine][1] + set_missing_parameters({engine: backend_cls}) + backend = backend_cls() + else: + engines = list_engines() + if engine not in engines: + raise ValueError( + f"unrecognized engine '{engine}' must be one of your download engines: {list(engines)}. " + "To install additional dependencies, see:\n" + "https://docs.xarray.dev/en/stable/user-guide/io.html \n" + "https://docs.xarray.dev/en/stable/getting-started-guide/installing.html" + ) + backend = engines[engine] elif issubclass(engine, BackendEntrypoint): backend = engine() else: diff --git a/xarray/tests/test_plugins.py b/xarray/tests/test_plugins.py index a961be74b82..e20f665c9ff 100644 --- a/xarray/tests/test_plugins.py +++ b/xarray/tests/test_plugins.py @@ -208,6 +208,15 @@ def test_engines_not_installed() -> None: plugins.guess_engine("foo.nc") +@pytest.mark.parametrize("engine", common.BACKEND_ENTRYPOINTS.keys()) +def test_get_backend_fastpath_skips_list_engines(engine: str) -> None: + """Test that built-in engines skip list_engines (fastpath).""" + plugins.list_engines.cache_clear() + initial_misses = plugins.list_engines.cache_info().misses + plugins.get_backend(engine) + assert plugins.list_engines.cache_info().misses == initial_misses + + def test_lazy_import() -> None: """Test that some modules are imported in a lazy manner.