Skip to content

Commit 8cc34cb

Browse files
authored
WIP: backend interface, now it uses subclassing (#4836)
* draft * working version * fix: instantiate BackendEtrypoints * rename AbstractBackendEntrypoint in BackendEntrypoint * fix plugins tests * style * style * raise NotImplemetedError if BackendEntrypoint.open_dataset is not implemented
1 parent 9fea799 commit 8cc34cb

File tree

12 files changed

+446
-455
lines changed

12 files changed

+446
-455
lines changed

xarray/backends/cfgrib_.py

Lines changed: 51 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
BackendEntrypoint,
1313
)
1414
from .locks import SerializableLock, ensure_lock
15-
from .store import open_backend_dataset_store
15+
from .store import StoreBackendEntrypoint
1616

1717
try:
1818
import cfgrib
@@ -86,62 +86,58 @@ def get_encoding(self):
8686
return encoding
8787

8888

89-
def guess_can_open_cfgrib(store_spec):
90-
try:
91-
_, ext = os.path.splitext(store_spec)
92-
except TypeError:
93-
return False
94-
return ext in {".grib", ".grib2", ".grb", ".grb2"}
95-
96-
97-
def open_backend_dataset_cfgrib(
98-
filename_or_obj,
99-
*,
100-
mask_and_scale=True,
101-
decode_times=None,
102-
concat_characters=None,
103-
decode_coords=None,
104-
drop_variables=None,
105-
use_cftime=None,
106-
decode_timedelta=None,
107-
lock=None,
108-
indexpath="{path}.{short_hash}.idx",
109-
filter_by_keys={},
110-
read_keys=[],
111-
encode_cf=("parameter", "time", "geography", "vertical"),
112-
squeeze=True,
113-
time_dims=("time", "step"),
114-
):
115-
116-
store = CfGribDataStore(
89+
class CfgribfBackendEntrypoint(BackendEntrypoint):
90+
def guess_can_open(self, store_spec):
91+
try:
92+
_, ext = os.path.splitext(store_spec)
93+
except TypeError:
94+
return False
95+
return ext in {".grib", ".grib2", ".grb", ".grb2"}
96+
97+
def open_dataset(
98+
self,
11799
filename_or_obj,
118-
indexpath=indexpath,
119-
filter_by_keys=filter_by_keys,
120-
read_keys=read_keys,
121-
encode_cf=encode_cf,
122-
squeeze=squeeze,
123-
time_dims=time_dims,
124-
lock=lock,
125-
)
126-
127-
with close_on_error(store):
128-
ds = open_backend_dataset_store(
129-
store,
130-
mask_and_scale=mask_and_scale,
131-
decode_times=decode_times,
132-
concat_characters=concat_characters,
133-
decode_coords=decode_coords,
134-
drop_variables=drop_variables,
135-
use_cftime=use_cftime,
136-
decode_timedelta=decode_timedelta,
100+
*,
101+
mask_and_scale=True,
102+
decode_times=None,
103+
concat_characters=None,
104+
decode_coords=None,
105+
drop_variables=None,
106+
use_cftime=None,
107+
decode_timedelta=None,
108+
lock=None,
109+
indexpath="{path}.{short_hash}.idx",
110+
filter_by_keys={},
111+
read_keys=[],
112+
encode_cf=("parameter", "time", "geography", "vertical"),
113+
squeeze=True,
114+
time_dims=("time", "step"),
115+
):
116+
117+
store = CfGribDataStore(
118+
filename_or_obj,
119+
indexpath=indexpath,
120+
filter_by_keys=filter_by_keys,
121+
read_keys=read_keys,
122+
encode_cf=encode_cf,
123+
squeeze=squeeze,
124+
time_dims=time_dims,
125+
lock=lock,
137126
)
138-
return ds
139-
140-
141-
cfgrib_backend = BackendEntrypoint(
142-
open_dataset=open_backend_dataset_cfgrib, guess_can_open=guess_can_open_cfgrib
143-
)
127+
store_entrypoint = StoreBackendEntrypoint()
128+
with close_on_error(store):
129+
ds = store_entrypoint.open_dataset(
130+
store,
131+
mask_and_scale=mask_and_scale,
132+
decode_times=decode_times,
133+
concat_characters=concat_characters,
134+
decode_coords=decode_coords,
135+
drop_variables=drop_variables,
136+
use_cftime=use_cftime,
137+
decode_timedelta=decode_timedelta,
138+
)
139+
return ds
144140

145141

146142
if has_cfgrib:
147-
BACKEND_ENTRYPOINTS["cfgrib"] = cfgrib_backend
143+
BACKEND_ENTRYPOINTS["cfgrib"] = CfgribfBackendEntrypoint

xarray/backends/common.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22
import time
33
import traceback
4-
from typing import Dict
4+
from typing import Dict, Tuple, Type, Union
55

66
import numpy as np
77

@@ -344,12 +344,13 @@ def encode(self, variables, attributes):
344344

345345

346346
class BackendEntrypoint:
347-
__slots__ = ("guess_can_open", "open_dataset", "open_dataset_parameters")
347+
open_dataset_parameters: Union[Tuple, None] = None
348348

349-
def __init__(self, open_dataset, open_dataset_parameters=None, guess_can_open=None):
350-
self.open_dataset = open_dataset
351-
self.open_dataset_parameters = open_dataset_parameters
352-
self.guess_can_open = guess_can_open
349+
def open_dataset(self):
350+
raise NotImplementedError
353351

352+
def guess_can_open(self, store_spec):
353+
return False
354354

355-
BACKEND_ENTRYPOINTS: Dict[str, BackendEntrypoint] = {}
355+
356+
BACKEND_ENTRYPOINTS: Dict[str, Type[BackendEntrypoint]] = {}

xarray/backends/h5netcdf_.py

Lines changed: 53 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
_get_datatype,
2424
_nc4_require_group,
2525
)
26-
from .store import open_backend_dataset_store
26+
from .store import StoreBackendEntrypoint
2727

2828
try:
2929
import h5netcdf
@@ -328,62 +328,61 @@ def close(self, **kwargs):
328328
self._manager.close(**kwargs)
329329

330330

331-
def guess_can_open_h5netcdf(store_spec):
332-
try:
333-
return read_magic_number(store_spec).startswith(b"\211HDF\r\n\032\n")
334-
except TypeError:
335-
pass
336-
337-
try:
338-
_, ext = os.path.splitext(store_spec)
339-
except TypeError:
340-
return False
341-
342-
return ext in {".nc", ".nc4", ".cdf"}
343-
344-
345-
def open_backend_dataset_h5netcdf(
346-
filename_or_obj,
347-
*,
348-
mask_and_scale=True,
349-
decode_times=None,
350-
concat_characters=None,
351-
decode_coords=None,
352-
drop_variables=None,
353-
use_cftime=None,
354-
decode_timedelta=None,
355-
format=None,
356-
group=None,
357-
lock=None,
358-
invalid_netcdf=None,
359-
phony_dims=None,
360-
):
361-
362-
store = H5NetCDFStore.open(
331+
class H5netcdfBackendEntrypoint(BackendEntrypoint):
332+
def guess_can_open(self, store_spec):
333+
try:
334+
return read_magic_number(store_spec).startswith(b"\211HDF\r\n\032\n")
335+
except TypeError:
336+
pass
337+
338+
try:
339+
_, ext = os.path.splitext(store_spec)
340+
except TypeError:
341+
return False
342+
343+
return ext in {".nc", ".nc4", ".cdf"}
344+
345+
def open_dataset(
346+
self,
363347
filename_or_obj,
364-
format=format,
365-
group=group,
366-
lock=lock,
367-
invalid_netcdf=invalid_netcdf,
368-
phony_dims=phony_dims,
369-
)
348+
*,
349+
mask_and_scale=True,
350+
decode_times=None,
351+
concat_characters=None,
352+
decode_coords=None,
353+
drop_variables=None,
354+
use_cftime=None,
355+
decode_timedelta=None,
356+
format=None,
357+
group=None,
358+
lock=None,
359+
invalid_netcdf=None,
360+
phony_dims=None,
361+
):
370362

371-
ds = open_backend_dataset_store(
372-
store,
373-
mask_and_scale=mask_and_scale,
374-
decode_times=decode_times,
375-
concat_characters=concat_characters,
376-
decode_coords=decode_coords,
377-
drop_variables=drop_variables,
378-
use_cftime=use_cftime,
379-
decode_timedelta=decode_timedelta,
380-
)
381-
return ds
363+
store = H5NetCDFStore.open(
364+
filename_or_obj,
365+
format=format,
366+
group=group,
367+
lock=lock,
368+
invalid_netcdf=invalid_netcdf,
369+
phony_dims=phony_dims,
370+
)
382371

372+
store_entrypoint = StoreBackendEntrypoint()
373+
374+
ds = store_entrypoint.open_dataset(
375+
store,
376+
mask_and_scale=mask_and_scale,
377+
decode_times=decode_times,
378+
concat_characters=concat_characters,
379+
decode_coords=decode_coords,
380+
drop_variables=drop_variables,
381+
use_cftime=use_cftime,
382+
decode_timedelta=decode_timedelta,
383+
)
384+
return ds
383385

384-
h5netcdf_backend = BackendEntrypoint(
385-
open_dataset=open_backend_dataset_h5netcdf, guess_can_open=guess_can_open_h5netcdf
386-
)
387386

388387
if has_h5netcdf:
389-
BACKEND_ENTRYPOINTS["h5netcdf"] = h5netcdf_backend
388+
BACKEND_ENTRYPOINTS["h5netcdf"] = H5netcdfBackendEntrypoint

0 commit comments

Comments
 (0)