Skip to content
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ The format is (loosely) based on [Keep a Changelog](http://keepachangelog.com/)

### Changed

- Made `stac-pydantic` an optional dependency ([#129](https://github.com/stac-utils/stac-check/pull/129))
- `stac-validator` is now installed without the `[pydantic]` extra by default
- Added `stac-check[pydantic]` extra for users who need pydantic validation
- Added graceful fallback to JSONSchema validation when pydantic is not available
- Updated tests to handle both scenarios (with and without pydantic installed)
- Added helpful warning messages when pydantic is requested but not installed

- Migrated documentation from Read the Docs to GitHub Pages ([#128](https://github.com/stac-utils/stac-check/pull/128))
- Updated documentation build system to use Sphinx with sphinx_rtd_theme
- Added support for Markdown content in documentation using myst-parser
Expand Down
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"requests>=2.32.3",
"jsonschema>=4.23.0",
"click>=8.1.8",
"stac-validator[pydantic]>=3.7.0",
"stac-validator>=3.7.0",
"PyYAML",
"python-dotenv",
],
Expand All @@ -29,13 +29,15 @@
"pytest",
"requests-mock",
"types-setuptools",
"stac-validator[pydantic]",
],
"docs": [
"sphinx>=4.0.0",
"sphinx_rtd_theme>=1.0.0",
"myst-parser>=0.18.0",
"sphinx-autodoc-typehints>=1.18.0",
],
"pydantic": ["stac-validator[pydantic]"],
},
entry_points={"console_scripts": ["stac-check=stac_check.cli:main"]},
author="Jonathan Healy",
Expand Down
14 changes: 13 additions & 1 deletion stac_check/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,24 @@ def cli_message(linter: Linter) -> None:
@click.option(
"--pydantic",
is_flag=True,
help="Use stac-pydantic for enhanced validation with Pydantic models.",
help="Use pydantic validation (requires stac-pydantic to be installed).",
)
@click.command()
@click.argument("file")
@click.version_option(version=importlib.metadata.distribution("stac-check").version)
def main(file, recursive, max_depth, assets, links, no_assets_urls, header, pydantic):
# Check if pydantic validation is requested but not installed
if pydantic:
try:
importlib.import_module("stac_pydantic")
except ImportError:
click.secho(
"Warning: stac-pydantic is not installed. Pydantic validation will be disabled.\n"
"To enable pydantic validation, install it with: pip install stac-check[pydantic]",
fg="yellow",
)
pydantic = False

linter = Linter(
file,
assets=assets,
Expand Down
15 changes: 15 additions & 0 deletions stac_check/lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,21 @@ def check_summaries(self) -> bool:
pydantic: bool = False

def __post_init__(self):
# Check if pydantic validation is requested but not installed
if self.pydantic:
try:
importlib.import_module("stac_pydantic")
except ImportError:
import warnings

warnings.warn(
"stac-pydantic is not installed. Pydantic validation will be disabled. "
"Install it with: pip install stac-check[pydantic]",
UserWarning,
stacklevel=2,
)
self.pydantic = False

self.data = self.load_data(self.item)
self.message = self.validate_file(self.item)
self.config = self.parse_config(self.config_file)
Expand Down
50 changes: 38 additions & 12 deletions tests/test_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -923,34 +923,60 @@ def test_bbox_antimeridian():
assert linter.check_bbox_antimeridian() == True


# Check if stac-pydantic is available
try:
import importlib

importlib.import_module("stac_pydantic")
PYDANTIC_AVAILABLE = True
except ImportError:
PYDANTIC_AVAILABLE = False

# Test decorator for pydantic tests
pytest.mark.pydantic = pytest.mark.skipif(
not PYDANTIC_AVAILABLE,
reason="stac-pydantic is not installed. Run 'pip install -e .[dev]' to install test dependencies.",
)


@pytest.mark.pydantic
def test_lint_pydantic_validation_valid():
"""Test pydantic validation with a valid STAC item."""
file = "sample_files/1.0.0/core-item.json"
linter = Linter(file, pydantic=True)

assert linter.valid_stac == True
assert linter.valid_stac is True
assert linter.asset_type == "ITEM"
assert "stac-pydantic Item model" in linter.message["schema"]
assert any("stac-pydantic" in schema for schema in linter.message["schema"])
assert linter.message["validation_method"] == "pydantic"


@pytest.mark.pydantic
def test_lint_pydantic_validation_invalid():
"""Test pydantic validation with an invalid STAC item (missing required fields)."""
file = "sample_files/1.0.0/bad-item.json"
linter = Linter(file, pydantic=True)

assert linter.valid_stac == False
assert linter.valid_stac is False
assert "PydanticValidationError" in linter.message["error_type"]
assert "id: Field required" in linter.message["error_message"]
assert linter.message["validation_method"] == "pydantic"


def test_lint_pydantic_validation_recursive():
"""Test pydantic validation with recursive option."""
file = "sample_files/1.0.0/collection.json"
linter = Linter(file, recursive=True, max_depth=1, pydantic=True)
def test_pydantic_fallback_without_import(monkeypatch):
"""Test that pydantic validation falls back to JSONSchema when stac-pydantic is not available."""
# Skip this test if stac-pydantic is actually installed
if PYDANTIC_AVAILABLE:
pytest.skip("stac-pydantic is installed, skipping fallback test")

assert linter.valid_stac == True
assert linter.asset_type == "COLLECTION"
assert "stac-pydantic Collection model" in linter.message["schema"]
assert linter.message["validation_method"] == "pydantic"
# Test that pydantic=False works without stac-pydantic
file = "sample_files/1.0.0/core-item.json"
linter = Linter(file, pydantic=False)
assert linter.valid_stac is True
assert linter.asset_type == "ITEM"
assert linter.message["validation_method"] == "default"

# Test that pydantic=True falls back to JSONSchema when stac-pydantic is not available
linter = Linter(file, pydantic=True)
assert linter.valid_stac is True
assert linter.asset_type == "ITEM"
assert linter.message["validation_method"] == "default"