From 73edc4702035b7ceed38acba93b7ceb1f6c79990 Mon Sep 17 00:00:00 2001 From: Paul van Schayck Date: Fri, 5 Jul 2024 16:28:06 +0200 Subject: [PATCH 1/3] Add CoverageJSON TypeAdapter to support generic loading of CoverageJSON --- README.md | 32 ++++++++++++++++++++++++++++++++ example.py | 24 ++++++++++++++++++++++++ src/covjson_pydantic/coverage.py | 17 ++++++++++++++--- tests/test_coverage.py | 10 ++++++++++ 4 files changed, 80 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0d95722..e34f058 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ pip install git+https://github.com/KNMI/covjson-pydantic.git ## Usage +### Generating CovJSON + ```python from datetime import datetime, timezone from pydantic import AwareDatetime @@ -104,6 +106,36 @@ Will print } ``` +### Validating CovJSON + +There is a helper `TypeAdapter` called `CoverageJSON` that will validate any (supported) CoverageJSON input, and +return the corresponding model. These can be of the type `CoverageCollection`, `Coverage`, `Domain`, `TiledNdArray` +or `NdArray`. + +```python +from covjson_pydantic.coverage import CoverageJSON +covjson = CoverageJSON.validate_json(""" +{ + "type": "NdArray", + "dataType": "float", + "axisNames": [ + "t", + "y", + "x" + ], + "shape": [ + 1, + 1, + 1 + ], + "values": [ + 27.1 + ] +} +""") +print(type(covjson)) +``` + ## Contributing Make an editable installation from within the repository root diff --git a/example.py b/example.py index a494f56..989ea94 100644 --- a/example.py +++ b/example.py @@ -2,6 +2,7 @@ from datetime import timezone from covjson_pydantic.coverage import Coverage +from covjson_pydantic.coverage import CoverageJSON from covjson_pydantic.domain import Axes from covjson_pydantic.domain import Domain from covjson_pydantic.domain import DomainType @@ -22,3 +23,26 @@ ) print(c.model_dump_json(exclude_none=True, indent=4)) + +covjson = CoverageJSON.validate_json( + """ +{ + "type": "NdArray", + "dataType": "float", + "axisNames": [ + "t", + "y", + "x" + ], + "shape": [ + 1, + 1, + 1 + ], + "values": [ + 27.1 + ] +} +""" +) +print(type(covjson)) diff --git a/src/covjson_pydantic/coverage.py b/src/covjson_pydantic/coverage.py index 279240b..4be1db6 100644 --- a/src/covjson_pydantic/coverage.py +++ b/src/covjson_pydantic/coverage.py @@ -1,16 +1,19 @@ +from typing import Annotated from typing import Dict from typing import List from typing import Literal from typing import Optional from typing import Union +from covjson_pydantic.domain import Domain +from covjson_pydantic.ndarray import NdArray +from covjson_pydantic.ndarray import TiledNdArray from pydantic import AnyUrl +from pydantic import Field +from pydantic import TypeAdapter from .base_models import CovJsonBaseModel -from .domain import Domain from .domain import DomainType -from .ndarray import NdArray -from .ndarray import TiledNdArray from .parameter import Parameter from .parameter import ParameterGroup from .reference_system import ReferenceSystemConnectionObject @@ -32,3 +35,11 @@ class CoverageCollection(CovJsonBaseModel, extra="allow"): parameters: Optional[Dict[str, Parameter]] = None parameterGroups: Optional[List[ParameterGroup]] = None # noqa: N815 referencing: Optional[List[ReferenceSystemConnectionObject]] = None + + +CoverageJSON = TypeAdapter( + Annotated[ + Union[CoverageCollection, Coverage, Domain, TiledNdArray, NdArray], + Field(discriminator="type"), + ] +) diff --git a/tests/test_coverage.py b/tests/test_coverage.py index 4c678e6..352ae22 100644 --- a/tests/test_coverage.py +++ b/tests/test_coverage.py @@ -4,6 +4,7 @@ import pytest from covjson_pydantic.coverage import Coverage from covjson_pydantic.coverage import CoverageCollection +from covjson_pydantic.coverage import CoverageJSON from covjson_pydantic.domain import Axes from covjson_pydantic.domain import Domain from covjson_pydantic.ndarray import NdArray @@ -76,3 +77,12 @@ def test_error_cases(file_name, object_type, error_message): with pytest.raises(ValidationError, match=error_message): object_type.model_validate_json(json_string) + + +def test_covjson_type_adapter(): + file = Path(__file__).parent.resolve() / "test_data" / "coverage-json.json" + # Put JSON in default unindented format + with open(file, "r") as f: + o = CoverageJSON.validate_json(f.read()) + + assert isinstance(o, Coverage) From 508da9ca3c4c3b534031b3f7312c8a0d32b8f2a1 Mon Sep 17 00:00:00 2001 From: Paul van Schayck Date: Fri, 5 Jul 2024 16:38:23 +0200 Subject: [PATCH 2/3] Disable Python 3.8 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5db4608..49d1eec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.9', '3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v4 From b385b89782948485733656308451bd446d4352e6 Mon Sep 17 00:00:00 2001 From: Paul van Schayck Date: Fri, 5 Jul 2024 16:42:37 +0200 Subject: [PATCH 3/3] Add mypy type hint --- src/covjson_pydantic/coverage.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/covjson_pydantic/coverage.py b/src/covjson_pydantic/coverage.py index 4be1db6..5ef6646 100644 --- a/src/covjson_pydantic/coverage.py +++ b/src/covjson_pydantic/coverage.py @@ -37,6 +37,7 @@ class CoverageCollection(CovJsonBaseModel, extra="allow"): referencing: Optional[List[ReferenceSystemConnectionObject]] = None +# type: Union[CoverageCollection, Coverage, Domain, TiledNdArray, NdArray] CoverageJSON = TypeAdapter( Annotated[ Union[CoverageCollection, Coverage, Domain, TiledNdArray, NdArray],