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

add request and jsonschema optional dependencies #156

Merged
merged 3 commits into from
Jul 9, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Unreleased

- Cache remote JSON schemas for extensions (#155, @avbentem)
- add `requests` and `jsonschema` in a **validation** optional dependencies (#156, @vincentsarago)

## 3.1.0 (2024-05-21)

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ For more comprehensive schema validation and robust extension support, use [pyst

```shell
python -m pip install stac-pydantic

# or

python -m pip install stac-pydantic["validation"]
```

For local development:
Expand Down
8 changes: 7 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ keywords=["stac", "pydantic", "validation"]
authors=[{ name = "Arturo Engineering", email = "[email protected]"}]
license= { text = "MIT" }
requires-python=">=3.8"
dependencies = ["click>=8.1.7", "pydantic>=2.4.1", "geojson-pydantic>=1.0.0"]
dependencies = [
"click>=8.1.7",
"pydantic>=2.4.1",
"geojson-pydantic>=1.0.0",
]
dynamic = ["version", "readme"]

[project.scripts]
Expand All @@ -31,6 +35,8 @@ homepage = "https://github.com/stac-utils/stac-pydantic"
repository ="https://github.com/stac-utils/stac-pydantic.git"

[project.optional-dependencies]
validation = ["jsonschema>=4.19.1", "requests>=2.31.0"]

dev = [
"pytest>=7.4.2",
"pytest-cov>=4.1.0",
Expand Down
21 changes: 14 additions & 7 deletions stac_pydantic/extensions.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import json
from functools import lru_cache
from typing import Any, Dict, Union

import jsonschema
import requests

from stac_pydantic.catalog import Catalog
from stac_pydantic.collection import Collection
from stac_pydantic.item import Item

try:
import requests
except ImportError: # pragma: nocover
requests = None # type: ignore

try:
import jsonschema
except ImportError: # pragma: nocover
jsonschema = None # type: ignore


@lru_cache(maxsize=128)
def _fetch_and_cache_schema(url: str) -> dict:
Expand All @@ -25,12 +31,13 @@ def validate_extensions(
Fetch the remote JSON schema, if not already cached, and validate the STAC
object against that schema.
"""
assert requests is not None, "requests must be installed to validate extensions"
assert jsonschema is not None, "jsonschema must be installed to validate extensions"

if isinstance(stac_obj, dict):
stac_dict = stac_obj
else:
# can't use `stac_obj.model_dump()` here
# b/c jsonschema expects pure string representations, not python types
stac_dict = json.loads(stac_obj.model_dump_json())
stac_dict = stac_obj.model_dump(mode="json")

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use mode="json" instead of model_dump_json + json.loads

try:
if stac_dict["stac_extensions"]:
Expand Down
17 changes: 16 additions & 1 deletion stac_pydantic/scripts/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@
from stac_pydantic.extensions import validate_extensions
from stac_pydantic.item import Item

try:
import requests
except ImportError: # pragma: nocover
requests = None # type: ignore

try:
import jsonschema
except ImportError: # pragma: nocover
jsonschema = None # type: ignore


@click.group(short_help="Validate STAC")
def app() -> None:
Expand All @@ -16,14 +26,19 @@ def app() -> None:
@click.argument("infile")
def validate_item(infile: str) -> None:
"""Validate stac item"""
assert requests is not None, "requests must be installed to validate items"
assert jsonschema is not None, "jsonschema must be installed to validate items"

r = requests.get(infile)
r.raise_for_status()

stac_item = r.json()
try:
item = Item(**stac_item)
item = Item.model_validate(stac_item)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

model_validate is the recommended way of parsing/validating an object

validate_extensions(item, reraise_exception=True)
except ValidationError as e:
click.echo(str(e))
return

click.echo(f"{infile} is valid")
return
Loading