Skip to content
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

Replace TypeSpecifier by simpler DataType #53

Merged
merged 4 commits into from
Sep 1, 2021
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
18 changes: 9 additions & 9 deletions test/test_era5.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@

import unittest

import xcube
import xcube.core
from jsonschema import ValidationError
from xcube.core.store import TYPE_SPECIFIER_CUBE
from xcube.core.store import VariableDescriptor

import xcube
import xcube.core
from test.mocks import CDSClientMock
from xcube.core.store import DATASET_TYPE
from xcube.core.store import VariableDescriptor
from xcube_cds.store import CDSDataOpener
from xcube_cds.store import CDSDataStore

Expand Down Expand Up @@ -179,8 +179,8 @@ def test_open_data_null_variables_list(self):
store = CDSDataStore(client_class=CDSClientMock,
endpoint_url=_CDS_API_URL,
cds_api_key=_CDS_API_KEY)
data_id = 'reanalysis-era5-single-levels-monthly-means:'\
'monthly_averaged_reanalysis'
data_id = 'reanalysis-era5-single-levels-monthly-means:' \
'monthly_averaged_reanalysis'
schema = store.get_open_data_params_schema(data_id)
n_vars = len(schema.properties['variable_names'].items.enum)
dataset = store.open_data(
Expand Down Expand Up @@ -214,11 +214,11 @@ def test_era5_describe_data(self):
self.assertTupleEqual(('time', 'latitude', 'longitude'),
vd.dims)

def test_get_type_specifiers_for_data(self):
def test_get_data_types_for_data(self):
store = CDSDataStore()
self.assertEqual(
(TYPE_SPECIFIER_CUBE, ),
store.get_type_specifiers_for_data('reanalysis-era5-land')
(DATASET_TYPE.alias,),
store.get_data_types_for_data('reanalysis-era5-land')
)

def test_has_data_true(self):
Expand Down
53 changes: 25 additions & 28 deletions test/test_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,9 @@

import xcube
import xcube.core
from xcube.core.store import DataDescriptor
from xcube.core.store import DataStoreError
from xcube.core.store import TYPE_SPECIFIER_CUBE
from xcube.core.store import TYPE_SPECIFIER_DATASET

from test.mocks import CDSClientMock
from xcube.core.store import DATASET_TYPE
from xcube.core.store import DataDescriptor
from xcube_cds.constants import CDS_DATA_OPENER_ID
from xcube_cds.datasets.reanalysis_era5 import ERA5DatasetHandler
from xcube_cds.store import CDSDataOpener
Expand Down Expand Up @@ -111,13 +108,13 @@ def test_get_open_params_schema_without_data_id(self):
actual['properties'].keys()
)

def test_search_data_invalid_id(self):
def test_search_data_invalid_data_type(self):
store = CDSDataStore(endpoint_url=_CDS_API_URL,
cds_api_key=_CDS_API_KEY)
with self.assertRaises(DataStoreError):
store.search_data('This is an invalid ID.')
with self.assertRaises(ValueError):
store.search_data(data_type='This is an invalid data type.')

def test_search_data_valid_id(self):
def test_search_data_valid_data_type(self):
store = CDSDataStore(endpoint_url=_CDS_API_URL,
cds_api_key=_CDS_API_KEY)
result = list(store.search_data('dataset'))
Expand All @@ -133,27 +130,31 @@ def test_get_data_store_params_schema(self):
'additionalProperties': False
}, CDSDataStore.get_data_store_params_schema().to_dict())

def test_get_type_specifiers(self):
type_specifiers = CDSDataStore.get_type_specifiers()
self.assertEqual(1, len(type_specifiers))
self.assertIsInstance(type_specifiers[0], str)
self.assertTupleEqual(('dataset[cube]',), type_specifiers)
def test_get_data_types(self):
data_types = CDSDataStore.get_data_types()
self.assertEqual(1, len(data_types))
self.assertIsInstance(data_types[0], str)
self.assertTupleEqual((DATASET_TYPE.alias,), data_types)

def test_has_data_false(self):
self.assertFalse(CDSDataStore().has_data('nonexistent data ID'))

def test_get_data_opener_ids_invalid_type_id(self):
with self.assertRaises(DataStoreError):
CDSDataStore().get_data_opener_ids(CDS_DATA_OPENER_ID,
'this is an invalid ID')
def test_get_data_opener_ids_invalid_data_type(self):
with self.assertRaises(ValueError):
CDSDataStore().get_data_opener_ids(
data_id=CDS_DATA_OPENER_ID,
data_type='this is an invalid data type'
)

def test_get_data_opener_ids_invalid_opener_id(self):
def test_get_data_opener_ids_invalid_data_id(self):
with self.assertRaises(ValueError):
CDSDataStore().get_data_opener_ids('this is an invalid ID',
TYPE_SPECIFIER_DATASET)
CDSDataStore().get_data_opener_ids(
data_id='this is an invalid data ID',
data_type=DATASET_TYPE
)

def test_get_data_opener_ids_with_default_arguments(self):
self.assertTupleEqual((CDS_DATA_OPENER_ID, ),
self.assertTupleEqual((CDS_DATA_OPENER_ID,),
CDSDataStore().get_data_opener_ids())

def test_get_store_open_params_schema_without_data_id(self):
Expand All @@ -166,9 +167,8 @@ def test_get_data_ids(self):
store = CDSDataStore(client_class=CDSClientMock,
endpoint_url=_CDS_API_URL,
cds_api_key=_CDS_API_KEY)
self.assertEqual([], list(store.get_data_ids('unsupported_type_spec')))
self.assertEqual([],
list(store.get_data_ids('dataset[unsupported_flag]')))
with self.assertRaises(ValueError):
list(store.get_data_ids(data_type='unsupported_data_type'))

# The number of available datasets is expected to increase over time,
# so to avoid overfitting the test we just check that more than a few
Expand All @@ -177,8 +177,6 @@ def test_get_data_ids(self):
minimum_expected_datasets = 5
self.assertGreater(len(list(store.get_data_ids('dataset'))),
minimum_expected_datasets)
self.assertGreater(len(list(store.get_data_ids('dataset[cube]'))),
minimum_expected_datasets)

def test_era5_transform_params_empty_variable_list(self):
handler = ERA5DatasetHandler()
Expand All @@ -190,7 +188,6 @@ def test_era5_transform_params_empty_variable_list(self):


class ClientUrlTest(unittest.TestCase):

"""Tests connected with passing CDS API URL and key to opener or store."""

def setUp(self):
Expand Down
56 changes: 28 additions & 28 deletions xcube_cds/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@
import xarray as xr

import xcube.core.normalize
from xcube.core.store import DATASET_TYPE
from xcube.core.store import DataDescriptor
from xcube.core.store import DataOpener
from xcube.core.store import DataStore
from xcube.core.store import DataStoreError
from xcube.core.store import DataTypeLike
from xcube.core.store import DatasetDescriptor
from xcube.core.store import DefaultSearchMixin
from xcube.core.store import TYPE_SPECIFIER_CUBE
from xcube.core.store import TypeSpecifier
from xcube.util.jsonschema import JsonArraySchema
from xcube.util.jsonschema import JsonBooleanSchema
from xcube.util.jsonschema import JsonDateSchema
Expand Down Expand Up @@ -753,18 +753,18 @@ def get_data_store_params_schema(cls) -> JsonObjectSchema:
)

@classmethod
def get_type_specifiers(cls) -> Tuple[str, ...]:
return str(TYPE_SPECIFIER_CUBE),
def get_data_types(cls) -> Tuple[str, ...]:
return DATASET_TYPE.alias,

def get_type_specifiers_for_data(self, data_id: str) -> Tuple[str, ...]:
def get_data_types_for_data(self, data_id: str) -> Tuple[str, ...]:
self._validate_data_id(data_id)
return str(TYPE_SPECIFIER_CUBE),
return DATASET_TYPE.alias,

def get_data_ids(self, type_specifier: Optional[str] = None,
def get_data_ids(self,
data_type: DataTypeLike = None,
include_attrs: Container[str] = None) -> \
Union[Iterator[str], Iterator[Tuple[str, Dict[str, Any]]]]:

if self._is_type_specifier_satisfied(type_specifier):
if self._is_data_type_satisfied(data_type):
# Only if the type specifier isn't compatible
return_tuples = include_attrs is not None
# TODO: respect names other than "title" in include_attrs
Expand All @@ -773,38 +773,38 @@ def get_data_ids(self, type_specifier: Optional[str] = None,
for data_id, handler in self._handler_registry.items():
if return_tuples:
if include_titles:
yield data_id,\
yield data_id, \
{'title':
handler.get_human_readable_data_id(data_id)}
handler.get_human_readable_data_id(data_id)}
else:
yield data_id, {}
else:
yield data_id

def has_data(self, data_id: str, type_specifier: Optional[str] = None) \
def has_data(self, data_id: str, data_type: Optional[str] = None) \
-> bool:
return self._is_type_specifier_satisfied(type_specifier) and \
return self._is_data_type_satisfied(data_type) and \
data_id in self._handler_registry

def describe_data(self, data_id: str,
type_specifier: Optional[str] = None) \
data_type: Optional[str] = None) \
-> DatasetDescriptor:
self._validate_data_id(data_id)
self._validate_type_specifier(type_specifier)
self._validate_data_type(data_type)
return self._handler_registry[data_id].describe_data(data_id)

# noinspection PyTypeChecker
def search_data(self, type_specifier: Optional[str] = None,
def search_data(self, data_type: Optional[DataTypeLike] = None,
**search_params) \
-> Iterator[DataDescriptor]:
self._validate_type_specifier(type_specifier)
return super().search_data(type_specifier=type_specifier,
self._validate_data_type(data_type)
return super().search_data(data_type=data_type,
**search_params)

def get_data_opener_ids(self, data_id: Optional[str] = None,
type_specifier: Optional[str] = None) \
data_type: Optional[str] = None) \
-> Tuple[str, ...]:
self._validate_type_specifier(type_specifier)
self._validate_data_type(data_type)
self._validate_data_id(data_id, allow_none=True)
return CDS_DATA_OPENER_ID,

Expand All @@ -827,23 +827,23 @@ def open_data(self, data_id: str, opener_id: Optional[str] = None,
# Implementation helpers

@staticmethod
def _validate_type_specifier(type_specifier: Union[str, TypeSpecifier]):
if not CDSDataStore._is_type_specifier_satisfied(type_specifier):
def _validate_data_type(data_type: DataTypeLike):
if not CDSDataStore._is_data_type_satisfied(data_type):
raise DataStoreError(
f'Supplied type specifier "{type_specifier}" is not compatible '
f'with "{TYPE_SPECIFIER_CUBE}."'
f'Supplied data type {data_type!r} is not compatible'
f' with "{DATASET_TYPE!r}."'
)

@staticmethod
def _is_type_specifier_satisfied(
type_specifier: Union[str, TypeSpecifier]) -> bool:
def _is_data_type_satisfied(
data_type: DataTypeLike) -> bool:
# At present, all datasets are available as cubes, so we simply check
# against TYPE_SPECIFIER_CUBE. If more (non-cube) datasets are added,
# the logic will have to be delegated to CDSDatasetHandler
# implementations.
if type_specifier is None:
if data_type is None:
return True
return TYPE_SPECIFIER_CUBE.satisfies(type_specifier)
return DATASET_TYPE.is_super_type_of(data_type)

@staticmethod
def _assert_valid_opener_id(opener_id):
Expand Down
2 changes: 1 addition & 1 deletion xcube_cds/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

version = '0.8.2.dev0'
version = '0.9.0.dev0'