Skip to content

Commit 4272d86

Browse files
Merge pull request #155 from avbentem/cache-remote-schemas
Cache remote JSON schemas
2 parents b9fa617 + 03f0873 commit 4272d86

File tree

4 files changed

+35
-4
lines changed

4 files changed

+35
-4
lines changed

Diff for: CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## Unreleased
2+
3+
- Cache remote JSON schemas for extensions (#155, @avbentem)
4+
15
## 3.1.0 (2024-05-21)
26

37
- Allow extra fields in Links (#144, @jonhealy1)

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ assert catalog.links[0].href == "item.json"
9191

9292
### Extensions
9393

94-
STAC defines many extensions which let the user customize the data in their catalog. `stac-pydantic.extensions.validate_extensions` will validate a `dict`, `Item`, `Collection` or `Catalog` against the schema urls provided in the `stac_extensions` property:
94+
STAC defines many extensions which let the user customize the data in their catalog. `stac-pydantic.extensions.validate_extensions` gets the JSON schemas from the URLs provided in the `stac_extensions` property (caching the last fetched ones), and will validate a `dict`, `Item`, `Collection` or `Catalog` against those fetched schemas:
9595

9696
```python
9797
from stac_pydantic import Item

Diff for: stac_pydantic/extensions.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
from functools import lru_cache
23
from typing import Any, Dict, Union
34

45
import jsonschema
@@ -9,10 +10,21 @@
910
from stac_pydantic.item import Item
1011

1112

13+
@lru_cache(maxsize=128)
14+
def _fetch_and_cache_schema(url: str) -> dict:
15+
"""Fetch the remote JSON schema, if not already cached."""
16+
req = requests.get(url)
17+
return req.json()
18+
19+
1220
def validate_extensions(
1321
stac_obj: Union[Item, Collection, Catalog, Dict[str, Any]],
1422
reraise_exception: bool = False,
1523
) -> bool:
24+
"""
25+
Fetch the remote JSON schema, if not already cached, and validate the STAC
26+
object against that schema.
27+
"""
1628
if isinstance(stac_obj, dict):
1729
stac_dict = stac_obj
1830
else:
@@ -23,8 +35,7 @@ def validate_extensions(
2335
try:
2436
if stac_dict["stac_extensions"]:
2537
for ext in stac_dict["stac_extensions"]:
26-
req = requests.get(ext)
27-
schema = req.json()
38+
schema = _fetch_and_cache_schema(ext)
2839
jsonschema.validate(instance=stac_dict, schema=schema)
2940
except Exception:
3041
if reraise_exception:

Diff for: tests/test_models.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from shapely.geometry import shape
77

88
from stac_pydantic import Collection, Item, ItemProperties
9-
from stac_pydantic.extensions import validate_extensions
9+
from stac_pydantic.extensions import _fetch_and_cache_schema, validate_extensions
1010
from stac_pydantic.links import Link, Links
1111
from stac_pydantic.shared import MimeTypes, StacCommonMetadata
1212

@@ -116,6 +116,22 @@ def test_explicit_extension_validation() -> None:
116116
validate_extensions(test_item)
117117

118118

119+
def test_extension_validation_schema_cache() -> None:
120+
# Defines 3 extensions, but one is a non-existing URL
121+
test_item = request(EO_EXTENSION)
122+
123+
_fetch_and_cache_schema.cache_clear()
124+
125+
assert not validate_extensions(test_item)
126+
assert _fetch_and_cache_schema.cache_info().hits == 0
127+
assert _fetch_and_cache_schema.cache_info().misses == 3
128+
129+
assert not validate_extensions(test_item)
130+
assert _fetch_and_cache_schema.cache_info().hits == 2
131+
# The non-existing URL will have failed, hence retried
132+
assert _fetch_and_cache_schema.cache_info().misses == 4
133+
134+
119135
@pytest.mark.parametrize(
120136
"infile,model",
121137
[(EO_EXTENSION, Item), (COLLECTION, Collection)],

0 commit comments

Comments
 (0)