Skip to content

Remove entrypoints in setup for internal backends #4724

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,6 @@ setup_requires =
setuptools >= 38.4
setuptools_scm

[options.entry_points]
xarray.backends =
zarr = xarray.backends.zarr:zarr_backend
h5netcdf = xarray.backends.h5netcdf_:h5netcdf_backend
cfgrib = xarray.backends.cfgrib_:cfgrib_backend
scipy = xarray.backends.scipy_:scipy_backend
pynio = xarray.backends.pynio_:pynio_backend
pseudonetcdf = xarray.backends.pseudonetcdf_:pseudonetcdf_backend
netcdf4 = xarray.backends.netCDF4_:netcdf4_backend
store = xarray.backends.store:store_backend


[options.extras_require]
io =
Expand Down
3 changes: 1 addition & 2 deletions xarray/backends/cfgrib_.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
from ..core import indexing
from ..core.utils import Frozen, FrozenDict, close_on_error
from ..core.variable import Variable
from .common import AbstractDataStore, BackendArray
from .common import AbstractDataStore, BackendArray, BackendEntrypoint
from .locks import SerializableLock, ensure_lock
from .plugins import BackendEntrypoint
from .store import open_backend_dataset_store

# FIXME: Add a dedicated lock, even if ecCodes is supposed to be thread-safe
Expand Down
9 changes: 9 additions & 0 deletions xarray/backends/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,12 @@ def encode(self, variables, attributes):
variables = {k: self.encode_variable(v) for k, v in variables.items()}
attributes = {k: self.encode_attribute(v) for k, v in attributes.items()}
return variables, attributes


class BackendEntrypoint:
__slots__ = ("guess_can_open", "open_dataset", "open_dataset_parameters")

def __init__(self, open_dataset, open_dataset_parameters=None, guess_can_open=None):
self.open_dataset = open_dataset
self.open_dataset_parameters = open_dataset_parameters
self.guess_can_open = guess_can_open
3 changes: 1 addition & 2 deletions xarray/backends/h5netcdf_.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from ..core import indexing
from ..core.utils import FrozenDict, is_remote_uri, read_magic_number
from ..core.variable import Variable
from .common import WritableCFDataStore, find_root_and_group
from .common import BackendEntrypoint, WritableCFDataStore, find_root_and_group
from .file_manager import CachingFileManager, DummyFileManager
from .locks import HDF5_LOCK, combine_locks, ensure_lock, get_write_lock
from .netCDF4_ import (
Expand All @@ -18,7 +18,6 @@
_get_datatype,
_nc4_require_group,
)
from .plugins import BackendEntrypoint
from .store import open_backend_dataset_store


Expand Down
2 changes: 1 addition & 1 deletion xarray/backends/netCDF4_.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
from ..core.variable import Variable
from .common import (
BackendArray,
BackendEntrypoint,
WritableCFDataStore,
find_root_and_group,
robust_getitem,
)
from .file_manager import CachingFileManager, DummyFileManager
from .locks import HDF5_LOCK, NETCDFC_LOCK, combine_locks, ensure_lock, get_write_lock
from .netcdf3 import encode_nc3_attr_value, encode_nc3_variable
from .plugins import BackendEntrypoint
from .store import open_backend_dataset_store

# This lookup table maps from dtype.byteorder to a readable endian
Expand Down
49 changes: 35 additions & 14 deletions xarray/backends/plugins.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
import functools
import inspect
import itertools
import logging
import typing as T
import warnings
from functools import lru_cache

import pkg_resources


class BackendEntrypoint:
__slots__ = ("guess_can_open", "open_dataset", "open_dataset_parameters")

def __init__(self, open_dataset, open_dataset_parameters=None, guess_can_open=None):
self.open_dataset = open_dataset
self.open_dataset_parameters = open_dataset_parameters
self.guess_can_open = guess_can_open
from .cfgrib_ import cfgrib_backend
from .common import BackendEntrypoint
from .h5netcdf_ import h5netcdf_backend
from .netCDF4_ import netcdf4_backend
from .pseudonetcdf_ import pseudonetcdf_backend
from .pydap_ import pydap_backend
from .pynio_ import pynio_backend
from .scipy_ import scipy_backend
from .store import store_backend
from .zarr import zarr_backend

BACKEND_ENTRYPOINTS: T.Dict[str, BackendEntrypoint] = {
"store": store_backend,
"netcdf4": netcdf4_backend,
"h5netcdf": h5netcdf_backend,
"scipy": scipy_backend,
"pseudonetcdf": pseudonetcdf_backend,
"zarr": zarr_backend,
"cfgrib": cfgrib_backend,
"pydap": pydap_backend,
"pynio": pynio_backend,
}


def remove_duplicates(backend_entrypoints):
Expand Down Expand Up @@ -71,13 +86,19 @@ def set_missing_parameters(engines):
backend.open_dataset_parameters = detect_parameters(open_dataset)


@lru_cache(maxsize=1)
def build_engines(entrypoints):
backend_entrypoints = BACKEND_ENTRYPOINTS.copy()
pkg_entrypoints = remove_duplicates(entrypoints)
external_backend_entrypoints = create_engines_dict(pkg_entrypoints)
backend_entrypoints.update(external_backend_entrypoints)
set_missing_parameters(backend_entrypoints)
return backend_entrypoints


@functools.lru_cache(maxsize=1)
def list_engines():
entrypoints = pkg_resources.iter_entry_points("xarray.backends")
backend_entrypoints = remove_duplicates(entrypoints)
engines = create_engines_dict(backend_entrypoints)
set_missing_parameters(engines)
return engines
return build_engines(entrypoints)


def guess_engine(store_spec):
Expand Down
3 changes: 1 addition & 2 deletions xarray/backends/pseudonetcdf_.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
from ..core import indexing
from ..core.utils import Frozen, FrozenDict, close_on_error
from ..core.variable import Variable
from .common import AbstractDataStore, BackendArray
from .common import AbstractDataStore, BackendArray, BackendEntrypoint
from .file_manager import CachingFileManager
from .locks import HDF5_LOCK, NETCDFC_LOCK, combine_locks, ensure_lock
from .plugins import BackendEntrypoint
from .store import open_backend_dataset_store

# psuedonetcdf can invoke netCDF libraries internally
Expand Down
3 changes: 1 addition & 2 deletions xarray/backends/pydap_.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
from ..core.pycompat import integer_types
from ..core.utils import Frozen, FrozenDict, close_on_error, is_dict_like, is_remote_uri
from ..core.variable import Variable
from .common import AbstractDataStore, BackendArray, robust_getitem
from .plugins import BackendEntrypoint
from .common import AbstractDataStore, BackendArray, BackendEntrypoint, robust_getitem
from .store import open_backend_dataset_store


Expand Down
3 changes: 1 addition & 2 deletions xarray/backends/pynio_.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
from ..core import indexing
from ..core.utils import Frozen, FrozenDict, close_on_error
from ..core.variable import Variable
from .common import AbstractDataStore, BackendArray
from .common import AbstractDataStore, BackendArray, BackendEntrypoint
from .file_manager import CachingFileManager
from .locks import HDF5_LOCK, NETCDFC_LOCK, SerializableLock, combine_locks, ensure_lock
from .plugins import BackendEntrypoint
from .store import open_backend_dataset_store

# PyNIO can invoke netCDF libraries internally
Expand Down
3 changes: 1 addition & 2 deletions xarray/backends/scipy_.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
from ..core.indexing import NumpyIndexingAdapter
from ..core.utils import Frozen, FrozenDict, close_on_error, read_magic_number
from ..core.variable import Variable
from .common import BackendArray, WritableCFDataStore
from .common import BackendArray, BackendEntrypoint, WritableCFDataStore
from .file_manager import CachingFileManager, DummyFileManager
from .locks import ensure_lock, get_write_lock
from .netcdf3 import encode_nc3_attr_value, encode_nc3_variable, is_valid_nc3_name
from .plugins import BackendEntrypoint
from .store import open_backend_dataset_store


Expand Down
3 changes: 1 addition & 2 deletions xarray/backends/store.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from .. import conventions
from ..core.dataset import Dataset
from .common import AbstractDataStore
from .plugins import BackendEntrypoint
from .common import AbstractDataStore, BackendEntrypoint


def guess_can_open_store(store_spec):
Expand Down
8 changes: 6 additions & 2 deletions xarray/backends/zarr.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
from ..core.pycompat import integer_types
from ..core.utils import FrozenDict, HiddenKeyDict, close_on_error
from ..core.variable import Variable
from .common import AbstractWritableDataStore, BackendArray, _encode_variable_name
from .plugins import BackendEntrypoint
from .common import (
AbstractWritableDataStore,
BackendArray,
BackendEntrypoint,
_encode_variable_name,
)
from .store import open_backend_dataset_store

# need some special secret attributes to tell us the dimensions
Expand Down
31 changes: 24 additions & 7 deletions xarray/tests/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pkg_resources
import pytest

from xarray.backends import plugins
from xarray.backends import common, plugins


def dummy_open_dataset_args(filename_or_obj, *args):
Expand All @@ -18,6 +18,9 @@ def dummy_open_dataset(filename_or_obj, *, decoder):
pass


dummy_cfgrib = common.BackendEntrypoint(dummy_open_dataset)


@pytest.fixture
def dummy_duplicated_entrypoints():
specs = [
Expand All @@ -31,7 +34,8 @@ def dummy_duplicated_entrypoints():


def test_remove_duplicates(dummy_duplicated_entrypoints):
entrypoints = plugins.remove_duplicates(dummy_duplicated_entrypoints)
with pytest.warns(RuntimeWarning):
entrypoints = plugins.remove_duplicates(dummy_duplicated_entrypoints)
assert len(entrypoints) == 2


Expand Down Expand Up @@ -60,8 +64,8 @@ def test_create_engines_dict():


def test_set_missing_parameters():
backend_1 = plugins.BackendEntrypoint(dummy_open_dataset)
backend_2 = plugins.BackendEntrypoint(dummy_open_dataset, ("filename_or_obj",))
backend_1 = common.BackendEntrypoint(dummy_open_dataset)
backend_2 = common.BackendEntrypoint(dummy_open_dataset, ("filename_or_obj",))
engines = {"engine_1": backend_1, "engine_2": backend_2}
plugins.set_missing_parameters(engines)

Expand All @@ -74,20 +78,33 @@ def test_set_missing_parameters():

def test_set_missing_parameters_raise_error():

backend = plugins.BackendEntrypoint(dummy_open_dataset_args)
backend = common.BackendEntrypoint(dummy_open_dataset_args)
with pytest.raises(TypeError):
plugins.set_missing_parameters({"engine": backend})

backend = plugins.BackendEntrypoint(
backend = common.BackendEntrypoint(
dummy_open_dataset_args, ("filename_or_obj", "decoder")
)
plugins.set_missing_parameters({"engine": backend})

backend = plugins.BackendEntrypoint(dummy_open_dataset_kwargs)
backend = common.BackendEntrypoint(dummy_open_dataset_kwargs)
with pytest.raises(TypeError):
plugins.set_missing_parameters({"engine": backend})

backend = plugins.BackendEntrypoint(
dummy_open_dataset_kwargs, ("filename_or_obj", "decoder")
)
plugins.set_missing_parameters({"engine": backend})


@mock.patch("pkg_resources.EntryPoint.load", mock.MagicMock(return_value=dummy_cfgrib))
def test_build_engines():
dummy_cfgrib_pkg_entrypoint = pkg_resources.EntryPoint.parse(
"cfgrib = xarray.tests.test_plugins:backend_1"
)
backend_entrypoints = plugins.build_engines([dummy_cfgrib_pkg_entrypoint])
assert backend_entrypoints["cfgrib"] is dummy_cfgrib
assert backend_entrypoints["cfgrib"].open_dataset_parameters == (
"filename_or_obj",
"decoder",
)