[Git][debian-gis-team/stac-pydantic][master] 8 commits: New upstream version 3.5.0
Antonio Valentino (@antonio.valentino)
gitlab at salsa.debian.org
Sun Feb 8 17:27:57 GMT 2026
Antonio Valentino pushed to branch master at Debian GIS Project / stac-pydantic
Commits:
eeab05bb by Antonio Valentino at 2026-02-08T17:09:29+00:00
New upstream version 3.5.0
- - - - -
20b543b2 by Antonio Valentino at 2026-02-08T17:09:30+00:00
Update upstream source from tag 'upstream/3.5.0'
Update to upstream version '3.5.0'
with Debian dir f5c0a3a752b8352e272a7937195a1b158d0abe1b
- - - - -
762cdf41 by Antonio Valentino at 2026-02-08T17:10:00+00:00
New upstream release
- - - - -
aa4dd068 by Antonio Valentino at 2026-02-08T17:13:00+00:00
Refresh all patches
- - - - -
4b36a48f by Antonio Valentino at 2026-02-08T17:14:54+00:00
Refresh patches
- - - - -
cedb7b8e by Antonio Valentino at 2026-02-08T17:15:40+00:00
Reorder Files paragraphs in debian/copyright by directory depth.
Changes-By: lintian-brush
Fixes: lintian: globbing-patterns-out-of-order
See-also: https://lintian.debian.org/tags/globbing-patterns-out-of-order.html
Fixes: lintian: globbing-patterns-out-of-order
See-also: https://lintian.debian.org/tags/globbing-patterns-out-of-order.html
- - - - -
d29fcb8f by Antonio Valentino at 2026-02-08T17:22:45+00:00
Add build-dependency on pythom3-hatchling
- - - - -
d7fb978c by Antonio Valentino at 2026-02-08T17:25:11+00:00
Update dates in d/copyright
- - - - -
22 changed files:
- .github/workflows/cicd.yml
- .github/workflows/release.yml
- .pre-commit-config.yaml
- CHANGELOG.md
- CONTRIBUTING.md
- README.md
- debian/changelog
- debian/control
- debian/copyright
- − debian/patches/0001-Network-mark.patch
- debian/patches/0002-No-coverage.patch → debian/patches/0001-No-coverage.patch
- debian/patches/series
- pyproject.toml
- stac_pydantic/item.py
- stac_pydantic/shared.py
- stac_pydantic/version.py
- tests/api/test_item_collection.py
- tests/api/test_landing_page.py
- tests/test_cli.py
- tests/test_models.py
- − tox.ini
- + uv.lock
Changes:
=====================================
.github/workflows/cicd.yml
=====================================
@@ -9,38 +9,48 @@ on:
- '*'
pull_request:
env:
- LATEST_PY_VERSION: '3.13'
+ LATEST_PY_VERSION: '3.14'
jobs:
tests:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
+ python-version:
+ - '3.8'
+ - '3.9'
+ - '3.10'
+ - '3.11'
+ - '3.12'
+ - '3.13'
+ - '3.14'
steps:
- - uses: actions/checkout at v4
+ - uses: actions/checkout at v5
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python at v5
+ - name: Install uv
+ uses: astral-sh/setup-uv at v7
with:
+ version: "0.9.*"
+ enable-cache: true
python-version: ${{ matrix.python-version }}
- name: Install dependencies
+ run: uv sync
+
+ - name: Run pre-commit
+ if: ${{ matrix.python-version == env.LATEST_PY_VERSION }}
run: |
- python -m pip install --upgrade pip
- python -m pip install tox pre-commit
- pre-commit install
+ uv run pre-commit run --all-files
- # Run tox using the version of Python in `PATH`
- - name: Run Tox
- run: tox -e py
+ - name: Run tests
+ run: uv run pytest --cov stac_pydantic --cov-report term-missing --cov-report xml
- name: Upload Results
if: ${{ matrix.python-version == env.LATEST_PY_VERSION }}
- uses: codecov/codecov-action at v4
+ uses: codecov/codecov-action at v5
with:
- file: ./coverage.xml
+ files: ./coverage.xml
flags: unittests
- name: ${{ matrix.python-version }}
fail_ci_if_error: false
+ token: ${{ secrets.CODECOV_TOKEN }}
=====================================
.github/workflows/release.yml
=====================================
@@ -12,22 +12,35 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.repository }} == 'stac-utils/stac-pydantic'
steps:
- - uses: actions/checkout at v4
+ - uses: actions/checkout at v5
- - name: Set up Python 3.x
- uses: actions/setup-python at v5
+ - name: Install uv
+ uses: astral-sh/setup-uv at v7
with:
- python-version: "3.x"
+ version: "0.9.*"
+ enable-cache: true
+ python-version: '3.14'
- - name: Install release dependencies
+ - name: Install dependencies
run: |
- python -m pip install --upgrade pip
- python -m pip install build twine
+ uv sync --group deploy
- - name: Build and publish package
+ - name: Set tag version
+ id: tag
+ run: |
+ echo "version=${GITHUB_REF#refs/*/}"
+ echo "version=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
+
+ - name: Set module version
+ id: module
+ run: |
+ echo "version=$(uv run hatch --quiet version)" >> $GITHUB_OUTPUT
+
+ - name: Build and publish
+ if: ${{ steps.tag.outputs.version }} == ${{ steps.module.outputs.version}}
env:
- TWINE_USERNAME: ${{ secrets.PYPI_STACUTILS_USERNAME }}
- TWINE_PASSWORD: ${{ secrets.PYPI_STACUTILS_PASSWORD }}
+ HATCH_INDEX_USER: ${{ secrets.PYPI_STACUTILS_USERNAME }}
+ HATCH_INDEX_AUTH: ${{ secrets.PYPI_STACUTILS_PASSWORD }}
run: |
- python -m build
- twine upload dist/*
+ uv run hatch build
+ uv run hatch publish
=====================================
.pre-commit-config.yaml
=====================================
@@ -1,8 +1,6 @@
-# See https://pre-commit.com for more information
-# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/abravalheri/validate-pyproject
- rev: v0.16
+ rev: v0.24
hooks:
- id: validate-pyproject
@@ -29,11 +27,12 @@ repos:
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: v1.15.0
+ rev: v1.11.2
hooks:
- id: mypy
language_version: python
- # No reason to run if only tests have changed. They intentionally break typing.
- exclude: tests/.*
additional_dependencies:
+ - types-attrs
- types-requests
+ - types-PyYAML
+ - pydantic~=2.0
=====================================
CHANGELOG.md
=====================================
@@ -1,6 +1,57 @@
## Unreleased
+## 3.5.0 (2026-01-29)
+
+- add python 3.14 support
+- use `uv` for project managment
+- fix: make sure to return `properties.datetime: null` and `geometry: null` when serializing the Item model
+
+ ```python
+ from stac_pydantic.api import Item
+
+ stac_item = Item.model_validate(
+ {
+ "id": "12345",
+ "type": "Feature",
+ "stac_extensions": [],
+ "geometry": None,
+ "properties": {
+ "datetime": None,
+ "start_datetime": "2024-01-01T00:00:00Z",
+ "end_datetime": "2024-01-02T00:00:00Z",
+ },
+ "collection": "collection",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://stac.example.com/catalog/collections/CS3-20160503_132130_04/items/CS3-20160503_132130_04.json"
+ },
+ {
+ "rel": "collection",
+ "href": "http://stac.example.com/catalog/CS3-20160503_132130_04/catalog.json"
+ },
+ {
+ "rel": "root",
+ "href": "http://stac.example.com/catalog"
+ }
+ ],
+ "assets": {},
+ }
+ )
+
+ out = stac_item.model_dump(exclude_none=True)
+ # `geometry` is required
+ assert out["geometry"] is None
+ # `datetime` is a required property
+ assert out["properties"]["datetime"] is None
+
+ # force exclusion of required keys
+ out = stac_item.model_dump(exclude_none=True, exclude={"properties": {"datetime"}, "geometry": True})
+ assert "geometry" not in out
+ assert "datetime" not in out["properties"]
+ ```
+
## 3.4.0 (2025-07-17)
- Remove 'label:assets' (extension) from Link attribute (#184, @fmigneault)
=====================================
CONTRIBUTING.md
=====================================
@@ -2,25 +2,33 @@
Issues and pull requests are more than welcome.
-**dev install**
+We recommand using [`uv`](https://docs.astral.sh/uv) as project manager for development.
+
+See https://docs.astral.sh/uv/getting-started/installation/ for installation
```bash
git clone https://github.com/stac-utils/stac-pydantic.git
cd stac-pydantic
-python -m pip install -e ".[dev]"
+uv sync
```
You can then run the tests with the following command:
```sh
-python -m pytest --cov stac_pydantic --cov-report term-missing
+uv run pytest --cov stac_pydantic --cov-report term-missing
```
+To run only tests that do not require access to the internet,
+the following command can be used:
+
+```sh
+uv run pytest -m "not network"
+```
**pre-commit**
This repo is set to use `pre-commit` to run *ruff*, *pydocstring* and mypy when committing new code.
```bash
-pre-commit install
+uv run pre-commit install
```
=====================================
README.md
=====================================
@@ -18,12 +18,6 @@ python -m pip install stac-pydantic
python -m pip install stac-pydantic["validation"]
```
-For local development:
-
-```shell
-python -m pip install -e '.[dev,lint]'
-```
-
| stac-pydantic | STAC Version | STAC API Version | Pydantic Version |
|--------------|---------------|------------------|-----------------|
| 1.2.x | 1.0.0-beta.1 | <1* | ^1.6 |
@@ -34,38 +28,6 @@ python -m pip install -e '.[dev,lint]'
\* various beta releases, specs not fully implemented
-## Development
-
-Install the [pre-commit](https://pre-commit.com/) hooks:
-
-```shell
-pre-commit install
-```
-
-## Testing
-
-Ensure you have all Python versions installed that the tests will be run against. If using pyenv, run:
-
-```shell
-pyenv install 3.8.18
-pyenv install 3.9.18
-pyenv install 3.10.13
-pyenv install 3.11.5
-pyenv local 3.8.18 3.9.18 3.10.13 3.11.5
-```
-
-Run the entire test suite:
-
-```shell
-tox
-```
-
-Run a single test case using the standard pytest convention:
-
-```shell
-python -m pytest -v tests/test_models.py::test_item_extensions
-```
-
## Usage
### Loading Models
@@ -137,31 +99,34 @@ It also implements models for defining ItemSeach queries.
```python
from stac_pydantic.api import Item, ItemCollection
-stac_item = Item(**{
- "id": "12345",
- "type": "Feature",
- "stac_extensions": [],
- "geometry": { "type": "Point", "coordinates": [0, 0] },
- "bbox": [0.0, 0.0, 0.0, 0.0],
- "properties": {
- "datetime": "2020-03-09T14:53:23.262208+00:00",
- },
- "collection": "CS3",
- "links": [
- {
- "rel": "self",
- "href": "http://stac.example.com/catalog/collections/CS3-20160503_132130_04/items/CS3-20160503_132130_04.json"
- },
- {
- "rel": "collection",
- "href": "http://stac.example.com/catalog/CS3-20160503_132130_04/catalog.json"
- },
- {
- "rel": "root",
- "href": "http://stac.example.com/catalog"
- }],
- "assets": {},
- })
+stac_item = Item.model_validate(
+ {
+ "id": "12345",
+ "type": "Feature",
+ "stac_extensions": [],
+ "geometry": { "type": "Point", "coordinates": [0, 0] },
+ "bbox": [0.0, 0.0, 0.0, 0.0],
+ "properties": {
+ "datetime": "2020-03-09T14:53:23.262208+00:00",
+ },
+ "collection": "CS3",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://stac.example.com/catalog/collections/CS3-20160503_132130_04/items/CS3-20160503_132130_04.json"
+ },
+ {
+ "rel": "collection",
+ "href": "http://stac.example.com/catalog/CS3-20160503_132130_04/catalog.json"
+ },
+ {
+ "rel": "root",
+ "href": "http://stac.example.com/catalog"
+ }
+ ],
+ "assets": {},
+ }
+)
stac_item_collection = ItemCollection(**{
"type": "FeatureCollection",
@@ -192,6 +157,55 @@ item_dict = item.model_dump()
assert item_dict['properties']['landsat:row'] == item.properties.row == 250
```
+#### Required keys
+
+STAC specification requires some keys to be present even if their value is `null`. When exporting a model to dict or json, stac-pydantic will make sure to keep the keys even if `exclude_none` is set to `True`. Users can overwrite this by using `exclude={'key'}`.
+
+```python
+from stac_pydantic.api import Item
+
+stac_item = Item.model_validate(
+ {
+ "id": "12345",
+ "type": "Feature",
+ "stac_extensions": [],
+ "geometry": None,
+ "properties": {
+ "datetime": None,
+ "start_datetime": "2024-01-01T00:00:00Z",
+ "end_datetime": "2024-01-02T00:00:00Z",
+ },
+ "collection": "collection",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://stac.example.com/catalog/collections/CS3-20160503_132130_04/items/CS3-20160503_132130_04.json"
+ },
+ {
+ "rel": "collection",
+ "href": "http://stac.example.com/catalog/CS3-20160503_132130_04/catalog.json"
+ },
+ {
+ "rel": "root",
+ "href": "http://stac.example.com/catalog"
+ }
+ ],
+ "assets": {},
+ }
+)
+
+out = stac_item.model_dump(exclude_none=True)
+# `geometry` is required
+assert out["geometry"] is None
+# `datetime` is a required property
+assert out["properties"]["datetime"] is None
+
+# force exclusion of required keys
+out = stac_item.model_dump(exclude_none=True, exclude={"properties": {"datetime"}, "geometry": True})
+assert "geometry" not in out
+assert "datetime" not in out["properties"]
+```
+
### CLI
```text
@@ -205,3 +219,7 @@ Options:
Commands:
validate-item Validate STAC Item
```
+
+## Contribution & Development
+
+See [CONTRIBUTING.md](CONTRIBUTING.md)
=====================================
debian/changelog
=====================================
@@ -1,3 +1,15 @@
+stac-pydantic (3.5.0-1) UNRELEASED; urgency=medium
+
+ * New upstream release.
+ * debian/patches:
+ - Drop 0001-Network-mark.patch, applied upstream.
+ - Refresh and renumber remainig patches.
+ * Reorder Files paragraphs in d/copyright by directory depth.
+ * debian/control:
+ - Add build-dependency on python3-hatchling.
+
+ -- Antonio Valentino <antonio.valentino at tiscali.it> Sun, 08 Feb 2026 17:09:32 +0000
+
stac-pydantic (3.4.0-1) unstable; urgency=low
* Initial release (Closes: #1123818).
=====================================
debian/control
=====================================
@@ -9,6 +9,7 @@ Build-Depends: debhelper-compat (= 13),
python3-click,
python3-dictdiffer <!nocheck>,
python3-geojson-pydantic,
+ python3-hatchling,
python3-jsonschema,
python3-pydantic,
python3-pytest <!nocheck>,
=====================================
debian/copyright
=====================================
@@ -7,6 +7,14 @@ Files: *
Copyright: 2020, Arturo AI
License: Expat
+Files: debian/*
+Copyright: 2025-2026, Antonio Valentino <antonio.valentino at tiscali.it>
+License: Expat
+
+Files: tests/example_stac/example-collection_version-extension.json
+Copyright: NONE
+License: CC0-1.0
+
Files: tests/api/examples/v1.0.0/itemcollection-sample-full.json
tests/example_stac/example-search.json
tests/example_stac/itemcollection-sample-full.json
@@ -14,14 +22,6 @@ Files: tests/api/examples/v1.0.0/itemcollection-sample-full.json
Copyright: NONE
License: PDDL-1.0
-Files: tests/example_stac/example-collection_version-extension.json
-Copyright: NONE
-License: CC0-1.0
-
-Files: debian/*
-Copyright: 2025, Antonio Valentino <antonio.valentino at tiscali.it>
-License: Expat
-
License: CC0-1.0
To the extent possible under law, the author(s) have dedicated all copyright
and related and neighboring rights to this software to the public domain
=====================================
debian/patches/0001-Network-mark.patch deleted
=====================================
@@ -1,100 +0,0 @@
-From: Antonio Valentino <antonio.valentino at tiscali.it>
-Date: Mon, 22 Dec 2025 10:20:04 +0000
-Subject: Network-mark
-
-Makt tests requiring access to the internet.
-
-Forwarded: https://github.com/stac-utils/stac-pydantic/pull/187
----
- pyproject.toml | 1 +
- tests/api/test_item_collection.py | 1 +
- tests/api/test_landing_page.py | 1 +
- tests/test_cli.py | 2 ++
- tests/test_models.py | 4 ++++
- 5 files changed, 9 insertions(+)
-
-diff --git a/pyproject.toml b/pyproject.toml
-index d01281d..a8f0cfc 100644
---- a/pyproject.toml
-+++ b/pyproject.toml
-@@ -75,6 +75,7 @@ exclude = ["tests*"]
-
- [tool.pytest.ini_options]
- addopts = "-sv --cov stac_pydantic --cov-report xml --cov-report term-missing --cov-fail-under 95"
-+markers = ["network"]
-
- [tool.isort]
- profile = "black"
-diff --git a/tests/api/test_item_collection.py b/tests/api/test_item_collection.py
-index d714750..b1ea8fe 100644
---- a/tests/api/test_item_collection.py
-+++ b/tests/api/test_item_collection.py
-@@ -12,6 +12,7 @@ ITEM_COLLECTION = "itemcollection-sample-full.json"
- PATH = ["tests", "api", "examples", f"v{STAC_API_VERSION}"]
-
-
-+ at pytest.mark.network
- @pytest.mark.parametrize(
- "example_url",
- [
-diff --git a/tests/api/test_landing_page.py b/tests/api/test_landing_page.py
-index 08fb765..280ef01 100644
---- a/tests/api/test_landing_page.py
-+++ b/tests/api/test_landing_page.py
-@@ -57,6 +57,7 @@ def test_landing_page_invalid_features(example_url):
- LandingPage(**example)
-
-
-+ at pytest.mark.network
- @pytest.mark.parametrize("example_url,schema_url", unique_combinations)
- def test_schema(example_url, schema_url):
- rsp_yaml = requests.get(schema_url).text
-diff --git a/tests/test_cli.py b/tests/test_cli.py
-index 4293cec..42e2162 100644
---- a/tests/test_cli.py
-+++ b/tests/test_cli.py
-@@ -1,6 +1,8 @@
- from stac_pydantic.scripts.cli import app
-+import pytest
-
-
-+ at pytest.mark.network
- def test_valid_stac_item(cli_runner):
- result = cli_runner.invoke(
- app,
-diff --git a/tests/test_models.py b/tests/test_models.py
-index a292134..ea0cb91 100644
---- a/tests/test_models.py
-+++ b/tests/test_models.py
-@@ -92,6 +92,7 @@ def test_item_assets_extension() -> None:
- dict_match(test_coll, valid_coll)
-
-
-+ at pytest.mark.network
- def test_label_extension() -> None:
- test_item = request(LABEL_EXTENSION)
-
-@@ -119,6 +120,7 @@ def test_explicit_extension_validation() -> None:
- validate_extensions(test_item)
-
-
-+ at pytest.mark.network
- def test_extension_validation_schema_cache() -> None:
- # Defines 3 extensions, but one is a non-existing URL
- test_item = request(EO_EXTENSION)
-@@ -296,6 +298,7 @@ def test_excludes() -> None:
- assert "eo:bands" not in valid_item["properties"]
-
-
-+ at pytest.mark.network
- def test_validate_extensions() -> None:
- test_item = request(SAR_EXTENSION)
- assert validate_extensions(test_item)
-@@ -310,6 +313,7 @@ def test_validate_extensions_reraise_exception() -> None:
- validate_extensions(test_item, reraise_exception=True)
-
-
-+ at pytest.mark.network
- def test_validate_extensions_rfc3339_with_partial_seconds() -> None:
- test_item = request(SAR_EXTENSION)
- test_item["properties"]["updated"] = "2018-10-01T01:08:32.033Z"
=====================================
debian/patches/0002-No-coverage.patch → debian/patches/0001-No-coverage.patch
=====================================
@@ -10,11 +10,11 @@ Forwarded: not-needed
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
-index a8f0cfc..70dcdb4 100644
+index 48f0032..313e161 100644
--- a/pyproject.toml
+++ b/pyproject.toml
-@@ -74,7 +74,7 @@ include = ["stac_pydantic*"]
- exclude = ["tests*"]
+@@ -76,7 +76,7 @@ only-include = ["stac_pydantic/"]
+ only-include = ["stac_pydantic/"]
[tool.pytest.ini_options]
-addopts = "-sv --cov stac_pydantic --cov-report xml --cov-report term-missing --cov-fail-under 95"
=====================================
debian/patches/series
=====================================
@@ -1,2 +1 @@
-0001-Network-mark.patch
-0002-No-coverage.patch
+0001-No-coverage.patch
=====================================
pyproject.toml
=====================================
@@ -1,10 +1,7 @@
-[build-system]
-requires = ["setuptools>=61.0"]
-build-backend = "setuptools.build_meta"
-
[project]
name="stac-pydantic"
description="Pydantic data models for the STAC spec"
+readme = "README.md"
classifiers=[
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
@@ -15,18 +12,25 @@ classifiers=[
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
"License :: OSI Approved :: MIT License",
]
keywords=["stac", "pydantic", "validation"]
-authors=[{ name = "Arturo Engineering", email = "engineering at arturo.ai"}]
-license= { text = "MIT" }
+authors=[
+ { name = "Arturo Engineering", email = "engineering at arturo.ai"},
+]
+maintainers = [
+ { name = "Vincent Sarago", email = "vincent at developmentseed.org" },
+ { name = "Pete Gadomski", email = "pete.gadomski at gmail.com" },
+]
+license = {file = "LICENSE"}
requires-python=">=3.8"
+dynamic = ["version"]
dependencies = [
"click>=8.1.7",
"pydantic>=2.4.1",
"geojson-pydantic>=1.0.0",
]
-dynamic = ["version", "readme"]
[project.scripts]
stac-pydantic = "stac_pydantic.scripts.cli:app"
@@ -36,8 +40,12 @@ 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"]
+validation = [
+ "jsonschema>=4.19.1",
+ "requests>=2.31.0",
+]
+[dependency-groups]
dev = [
"pytest>=7.4.2",
"pytest-cov>=4.1.0",
@@ -46,35 +54,30 @@ dev = [
"shapely>=2.0.1",
"dictdiffer>=0.9.0",
"jsonschema>=4.19.1",
- "pyyaml>=6.0.1"
+ "pyyaml>=6.0.1",
+ "pre-commit",
]
-lint = [
- "types-requests>=2.31.0.5",
- "types-jsonschema>=4.19.0.3",
- "types-PyYAML>=6.0.12.12",
- "black>=23.9.1",
- "isort>=5.12.0",
- "flake8>=6.1.0",
- "Flake8-pyproject>=1.2.3",
- "mypy>=1.5.1",
- "pre-commit>=3.4.0",
- "tox>=4.11.3"
+deploy = [
+ "hatch",
]
-[tool.setuptools.dynamic]
-version = { attr = "stac_pydantic.version.__version__" }
-readme = {file = ["README.md"], content-type = "text/markdown"}
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[tool.hatch.version]
+path = "stac_pydantic/version.py"
-[tool.setuptools.package-data]
-stac_pydantic= ["*.typed"]
+[tool.hatch.build.targets.sdist]
+only-include = ["stac_pydantic/"]
-[tool.setuptools.packages.find]
-include = ["stac_pydantic*"]
-exclude = ["tests*"]
+[tool.hatch.build.targets.wheel]
+only-include = ["stac_pydantic/"]
[tool.pytest.ini_options]
addopts = "-sv --cov stac_pydantic --cov-report xml --cov-report term-missing --cov-fail-under 95"
+markers = ["network"]
[tool.isort]
profile = "black"
=====================================
stac_pydantic/item.py
=====================================
@@ -1,7 +1,15 @@
from typing import Any, Dict, List, Optional
from geojson_pydantic import Feature
-from pydantic import AnyUrl, ConfigDict, Field, model_serializer, model_validator
+from pydantic import (
+ AnyUrl,
+ ConfigDict,
+ Field,
+ SerializationInfo,
+ SerializerFunctionWrapHandler,
+ model_serializer,
+ model_validator,
+)
from stac_pydantic.links import Links
from stac_pydantic.shared import SEMVER_REGEX, Asset, StacBaseModel, StacCommonMetadata
@@ -38,10 +46,19 @@ class Item(Feature, StacBaseModel):
return values
# https://github.com/developmentseed/geojson-pydantic/issues/147
- @model_serializer(mode="wrap")
- def _serialize(self, handler):
- data = handler(self)
+ @model_serializer(when_used="always", mode="wrap")
+ def _serialize(
+ self,
+ serializer: SerializerFunctionWrapHandler,
+ info: SerializationInfo,
+ ):
+ data = serializer(self)
for field in self.__geojson_exclude_if_none__:
if field in data and data[field] is None:
del data[field]
+
+ if "geometry" not in data:
+ if info.exclude_none and "geometry" not in (info.exclude or {}):
+ data["geometry"] = None
+
return data
=====================================
stac_pydantic/shared.py
=====================================
@@ -10,7 +10,10 @@ from pydantic import (
BaseModel,
ConfigDict,
Field,
+ SerializationInfo,
+ SerializerFunctionWrapHandler,
TypeAdapter,
+ model_serializer,
model_validator,
)
from typing_extensions import Annotated, Self
@@ -34,7 +37,7 @@ UtcDatetime = Annotated[
AfterValidator(lambda d: d.astimezone(timezone.utc)),
]
-SearchDatetime = TypeAdapter(Optional[UtcDatetime])
+SearchDatetime: TypeAdapter = TypeAdapter(Optional[UtcDatetime])
class MimeTypes(str, Enum):
@@ -112,14 +115,14 @@ class StacBaseModel(BaseModel):
by_alias=by_alias, exclude_unset=exclude_unset, **kwargs
)
- def model_dump(
+ def model_dump( # type: ignore[override]
self, *, by_alias: bool = True, exclude_unset: bool = True, **kwargs: Any
) -> Dict[str, Any]:
return super().model_dump(
by_alias=by_alias, exclude_unset=exclude_unset, **kwargs
)
- def model_dump_json(
+ def model_dump_json( # type: ignore[override]
self, *, by_alias: bool = True, exclude_unset: bool = True, **kwargs: Any
) -> str:
return super().model_dump_json(
@@ -185,6 +188,22 @@ class StacCommonMetadata(StacBaseModel):
)
return self
+ @model_serializer(when_used="always", mode="wrap")
+ def include_datetime_null(
+ self,
+ serializer: SerializerFunctionWrapHandler,
+ info: SerializationInfo,
+ ):
+ """Custom Model serializer make sure to allways keep datetime."""
+ data = serializer(self)
+ start = data.get("start_datetime")
+ end = data.get("end_datetime")
+ if not data.get("datetime") and (start and end):
+ if info.exclude_none and "datetime" not in (info.exclude or {}):
+ data["datetime"] = None
+
+ return data
+
class Asset(StacBaseModel):
"""
=====================================
stac_pydantic/version.py
=====================================
@@ -1,5 +1,5 @@
"""stac-pydantic and STAC spec versions."""
-__version__ = "3.4.0"
+__version__ = "3.5.0"
STAC_VERSION = "1.0.0"
=====================================
tests/api/test_item_collection.py
=====================================
@@ -12,6 +12,7 @@ ITEM_COLLECTION = "itemcollection-sample-full.json"
PATH = ["tests", "api", "examples", f"v{STAC_API_VERSION}"]
+ at pytest.mark.network
@pytest.mark.parametrize(
"example_url",
[
=====================================
tests/api/test_landing_page.py
=====================================
@@ -57,6 +57,7 @@ def test_landing_page_invalid_features(example_url):
LandingPage(**example)
+ at pytest.mark.network
@pytest.mark.parametrize("example_url,schema_url", unique_combinations)
def test_schema(example_url, schema_url):
rsp_yaml = requests.get(schema_url).text
=====================================
tests/test_cli.py
=====================================
@@ -1,6 +1,9 @@
+import pytest
+
from stac_pydantic.scripts.cli import app
+ at pytest.mark.network
def test_valid_stac_item(cli_runner):
result = cli_runner.invoke(
app,
=====================================
tests/test_models.py
=====================================
@@ -92,6 +92,7 @@ def test_item_assets_extension() -> None:
dict_match(test_coll, valid_coll)
+ at pytest.mark.network
def test_label_extension() -> None:
test_item = request(LABEL_EXTENSION)
@@ -119,6 +120,7 @@ def test_explicit_extension_validation() -> None:
validate_extensions(test_item)
+ at pytest.mark.network
def test_extension_validation_schema_cache() -> None:
# Defines 3 extensions, but one is a non-existing URL
test_item = request(EO_EXTENSION)
@@ -198,13 +200,43 @@ def test_geo_interface() -> None:
],
)
def test_stac_common_dates(args) -> None:
- StacCommonMetadata(**args)
+ metadata = StacCommonMetadata(**args)
+ assert "datetime" in metadata.model_dump(mode="json")
+ assert "datetime" in metadata.model_dump(mode="json", exclude_unset=True)
+ assert "datetime" in metadata.model_dump(exclude_none=True)
+ assert "datetime" in metadata.model_dump(mode="json", exclude_none=True)
+ assert "datetime" not in metadata.model_dump(
+ exclude_none=True, exclude={"datetime"}
+ )
+
+
+def test_item_datetime_null() -> None:
+ """Check datetime custom serialization works for sub-model."""
+ test_item = request(SAR_EXTENSION)
+ test_item["properties"]["datetime"] = None
+ itm = Item.model_validate(test_item)
+ assert not itm.properties.datetime
+
+ itm_dict = itm.model_dump(exclude_none=True)
+ assert itm_dict["properties"]["datetime"] is None
+ assert Item.model_validate(itm_dict)
+
+ itm_dict = itm.model_dump(mode="json", exclude_none=True)
+ assert itm_dict["properties"]["datetime"] is None
+ assert Item.model_validate(itm_dict)
+
+ itm_json = itm.model_dump_json(exclude_none=True)
+ assert '"datetime":null' in itm_json
+ assert Item.model_validate_json(itm_json)
+
+ itm_dict = itm.model_dump(exclude_none=True, exclude={"properties": {"datetime"}})
+ assert "datetime" not in itm_dict
def test_stac_null_datetime_required() -> None:
with pytest.raises(ValidationError):
- StacCommonMetadata(
- **{
+ StacCommonMetadata.model_validate(
+ {
"start_datetime": "2024-01-01T00:00:00Z",
"end_datetime": "2024-01-02T00:00:00Z",
}
@@ -296,6 +328,7 @@ def test_excludes() -> None:
assert "eo:bands" not in valid_item["properties"]
+ at pytest.mark.network
def test_validate_extensions() -> None:
test_item = request(SAR_EXTENSION)
assert validate_extensions(test_item)
@@ -310,6 +343,7 @@ def test_validate_extensions_reraise_exception() -> None:
validate_extensions(test_item, reraise_exception=True)
+ at pytest.mark.network
def test_validate_extensions_rfc3339_with_partial_seconds() -> None:
test_item = request(SAR_EXTENSION)
test_item["properties"]["updated"] = "2018-10-01T01:08:32.033Z"
@@ -341,8 +375,19 @@ def test_resolve_links() -> None:
def test_geometry_null_item() -> None:
test_item = request(ITEM_GEOMETRY_NULL)
- valid_item = Item(**test_item).model_dump()
- dict_match(test_item, valid_item)
+ valid_item = Item.model_validate(test_item)
+ dict_match(test_item, valid_item.model_dump())
+
+ assert not valid_item.geometry
+ item_dict = valid_item.model_dump(exclude_none=True)
+ assert "geometry" in item_dict
+
+ itm_json = valid_item.model_dump_json(exclude_none=True)
+ assert '"geometry":null' in itm_json
+ assert Item.model_validate_json(itm_json)
+
+ itm_dict = valid_item.model_dump(exclude_none=True, exclude={"geometry"})
+ assert "geometry" not in itm_dict
def test_item_bbox_validation() -> None:
=====================================
tox.ini deleted
=====================================
@@ -1,35 +0,0 @@
-[tox]
-envlist = py38,py39,py310,py311,py312
-
-[testenv]
-extras = dev
-commands = python -m pytest
-
-[testenv:lint]
-extras = lint
-description = run linters
-commands = SKIP=mypy pre-commit run --all-files
-
-[testenv:type]
-extras = lint
-description = run type checks
-commands = pre-commit run mypy --all-files
-
-[testenv:build]
-basepython = python3
-skip_install = true
-deps = build
-commands = python -m build
-
-[testenv:release]
-setenv =
- TWINE_USERNAME = {env:TWINE_USERNAME}
- TWINE_PASSWORD = {env:TWINE_PASSWORD}
-basepython = python3
-skip_install = true
-deps =
- {[testenv:build]deps}
- twine >= 1.5.0
-commands =
- {[testenv:build]commands}
- twine upload --skip-existing dist/*
=====================================
uv.lock
=====================================
The diff for this file was not included because it is too large.
View it on GitLab: https://salsa.debian.org/debian-gis-team/stac-pydantic/-/compare/553c3e13da9573085c65894b0062b40418230191...d7fb978cbbfb847c66fa69584b7f0090e4b6256c
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/stac-pydantic/-/compare/553c3e13da9573085c65894b0062b40418230191...d7fb978cbbfb847c66fa69584b7f0090e4b6256c
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20260208/168a5500/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list