diff --git a/README.md b/README.md index d96bb4b..c0f56df 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,9 @@ implicitly or explicitly: #### Implicit The `item_model_factory` function creates an appropriate Pydantic model based on the structure of the item by looking -at the extensions defined by the `stac_extensions` member. +at the extensions defined by the `stac_extensions` member. The model can be created once and reused for the life of +the interpreter. + ```python from stac_pydantic import item_model_factory @@ -62,6 +64,14 @@ item = model(**stac_item) field required (eo) (type=value_error.missing) ``` +The `stac_pydantic.validate_item` function provides a convenience wrapper over `item_model_factory` for one-off validation: + +```python +from stac_pydantic import validate_item + +assert validate_item(stac_item) +``` + #### Explicit Subclass any of the models provided by the library to declare a customized validator: diff --git a/stac_pydantic/__init__.py b/stac_pydantic/__init__.py index 7d71e3c..da61fc1 100644 --- a/stac_pydantic/__init__.py +++ b/stac_pydantic/__init__.py @@ -1,4 +1,10 @@ from .catalog import Catalog from .collection import Collection from .extensions import Extensions -from .item import Item, ItemCollection, ItemProperties, item_model_factory +from .item import ( + Item, + ItemCollection, + ItemProperties, + item_model_factory, + validate_item, +) diff --git a/stac_pydantic/item.py b/stac_pydantic/item.py index 862ed02..1711656 100644 --- a/stac_pydantic/item.py +++ b/stac_pydantic/item.py @@ -108,3 +108,16 @@ def item_model_factory( ) return create_model("CustomStacItem", **item_fields, __base__=base_class) + + +def validate_item(item: Dict, reraise_exception: bool = False, **kwargs) -> bool: + """ + Wrapper around ``item_model_factory`` for stac item validation + """ + try: + item_model_factory(item, **kwargs)(**item) + except Exception: + if reraise_exception: + raise + return False + return True diff --git a/stac_pydantic/scripts/cli.py b/stac_pydantic/scripts/cli.py index 81faa88..347fb36 100644 --- a/stac_pydantic/scripts/cli.py +++ b/stac_pydantic/scripts/cli.py @@ -2,7 +2,7 @@ import requests from pydantic import ValidationError -from stac_pydantic import item_model_factory +from stac_pydantic import validate_item as validate @click.group(short_help="Validate STAC") @@ -19,7 +19,7 @@ def validate_item(infile): r.raise_for_status() stac_item = r.json() try: - item_model_factory(stac_item, skip_remote_refs=True)(**stac_item) + validate(stac_item, skip_remote_refs=True, reraise_exception=True) except ValidationError as e: click.echo(str(e)) return diff --git a/tests/test_models.py b/tests/test_models.py index baa672e..5ea7e73 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -13,7 +13,7 @@ from stac_pydantic.api.search import Search from stac_pydantic.extensions import Extensions from stac_pydantic.extensions.single_file_stac import SingleFileStac -from stac_pydantic.item import item_model_factory +from stac_pydantic.item import item_model_factory, validate_item from stac_pydantic.shared import DATETIME_RFC339, Link from .conftest import dict_match, request @@ -626,3 +626,16 @@ def test_skip_remote_extension(): # This should work item_model_factory(test_item, skip_remote_refs=True)(**test_item) + + +def test_validate_item(): + test_item = request(EO_EXTENSION) + assert validate_item(test_item) + + +def test_validate_item_reraise_exception(): + test_item = request(EO_EXTENSION) + del test_item["properties"]["eo:bands"] + + with pytest.raises(ValidationError): + validate_item(test_item, reraise_exception=True)