Skip to content

Commit 53418d7

Browse files
authored
Do not share query_types between subclasses of BaseEDRProvider (#2049)
* Add test for shared edr query_type inheritance * Fix test_load_and_prepare_item_missing_geojson_parts * Auto-register subclasses EDR query types - Automatically register EDR query types - Ensure subclasses of `BaseEDRProvider` does not share query_types between instances of its subclass
1 parent b9c145d commit 53418d7

5 files changed

Lines changed: 44 additions & 24 deletions

File tree

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ jobs:
143143
run: |
144144
pytest tests/api
145145
pytest tests/test_api_ogr_provider.py
146+
pytest tests/test_base_provider.py
146147
pytest tests/test_config.py
147148
pytest tests/test_csv__formatter.py
148149
pytest tests/test_csv__provider.py

pygeoapi/provider/base_edr.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,28 @@ def __init__(self, provider_def):
5656

5757
# self.instances = []
5858

59-
@classmethod
60-
def register(cls):
61-
def inner(fn):
62-
if fn.__name__ not in EDR_QUERY_TYPES:
63-
msg = 'Invalid EDR Query type'
64-
LOGGER.error(msg)
65-
raise ProviderInvalidDataError(msg)
66-
67-
cls.query_types.append(fn.__name__)
68-
return fn
69-
return inner
59+
def __init_subclass__(cls, **kwargs):
60+
super().__init_subclass__(**kwargs)
61+
62+
cls.query_types = [
63+
name for name, function in cls.__dict__.items()
64+
if name in EDR_QUERY_TYPES and callable(function)
65+
]
66+
67+
if not cls.query_types:
68+
msg = f"{cls.__name__} does not implement any query types"
69+
LOGGER.error(msg)
70+
raise ProviderInvalidDataError(msg)
71+
72+
LOGGER.debug(
73+
f'{cls.__name__} registered query types: {cls.query_types}'
74+
)
75+
76+
if 'items' in cls.query_types:
77+
LOGGER.warning(
78+
f'items query is registered in {cls.__name__}, '
79+
'but requests will be routed to a feature provider'
80+
)
7081

7182
def get_instance(self, instance):
7283
"""

pygeoapi/provider/sensorthings_edr.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ def get_fields(self):
100100

101101
return self._fields
102102

103-
@BaseEDRProvider.register()
104103
def items(self, **kwargs):
105104
"""
106105
Retrieve a collection of items.
@@ -113,7 +112,6 @@ def items(self, **kwargs):
113112
# We implement this method inside of the feature provider
114113
pass
115114

116-
@BaseEDRProvider.register()
117115
def locations(
118116
self,
119117
select_properties: list = [],
@@ -188,7 +186,6 @@ def locations(
188186

189187
return fc
190188

191-
@BaseEDRProvider.register()
192189
def cube(
193190
self,
194191
select_properties: list = [],
@@ -240,7 +237,6 @@ def cube(
240237

241238
return self._make_coverage_collection(response)
242239

243-
@BaseEDRProvider.register()
244240
def area(
245241
self,
246242
wkt: str,

pygeoapi/provider/xarray_edr.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ def __init__(self, provider_def):
5757
BaseEDRProvider.__init__(self, provider_def)
5858
XarrayProvider.__init__(self, provider_def)
5959

60-
@BaseEDRProvider.register()
6160
def position(self, **kwargs):
6261
"""
6362
Extract data from collection collection
@@ -168,7 +167,6 @@ def position(self, **kwargs):
168167

169168
return self.gen_covjson(out_meta, data, self.fields)
170169

171-
@BaseEDRProvider.register()
172170
def cube(self, **kwargs):
173171
"""
174172
Extract data from collection

tests/test_base_provider.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -318,28 +318,28 @@ def test_load_and_prepare_item_accept_missing_identifier(
318318
assert data["type"] == "Feature"
319319

320320

321-
@pytest.mark.parametrize("missing_part,item_data", [
322-
("geometry", {
321+
@pytest.mark.parametrize("item_data", [
322+
{
323323
"type": "Feature",
324324
"id": "test_id",
325325
"properties": {"name": "Test Feature"}
326-
}),
327-
("properties", {
326+
},
327+
{
328328
"type": "Feature",
329329
"id": "test_id",
330330
"geometry": {"type": "Point", "coordinates": [0, 0]}
331-
})
331+
}
332332
])
333333
def test_load_and_prepare_item_missing_geojson_parts(
334-
mock_provider_with_get, missing_part, item_data, remove_stdout
334+
mock_provider_with_get, item_data, remove_stdout
335335
):
336336
"""Test loading item without required GeoJSON parts."""
337337
item_json = json.dumps(item_data)
338338

339339
with remove_stdout():
340340
with pytest.raises(
341341
ProviderInvalidDataError,
342-
match=f"Missing core GeoJSON {missing_part}"
342+
match="Missing core GeoJSON geometry or properties"
343343
):
344344
mock_provider_with_get._load_and_prepare_item(item_json)
345345

@@ -400,3 +400,17 @@ def test_provider_request_entity_too_large_error_with_message():
400400
def test_schema_type_values(schema_type, expected_value):
401401
"""Test SchemaType enum values."""
402402
assert schema_type.value == expected_value
403+
404+
405+
def test_unique_subclass_query_types():
406+
from pygeoapi.provider.base_edr import BaseEDRProvider
407+
assert BaseEDRProvider.query_types == []
408+
409+
from pygeoapi.provider.xarray_edr import XarrayEDRProvider
410+
assert BaseEDRProvider.query_types != XarrayEDRProvider.query_types
411+
assert XarrayEDRProvider.query_types == ['position', 'cube']
412+
413+
from pygeoapi.provider.sensorthings_edr import SensorThingsEDRProvider
414+
assert BaseEDRProvider.query_types != SensorThingsEDRProvider.query_types
415+
assert SensorThingsEDRProvider.query_types == \
416+
['items', 'locations', 'cube', 'area']

0 commit comments

Comments
 (0)