Skip to content

Commit f28081a

Browse files
Pydantic 2 and STAC API 1.0 (#127)
* Migrate to Pydantic v2 * PEP 621 - use pyproject.toml * Fix pydantic 2 issues, single out item collection into its own module * fix cli module * move api specific link logic into API subpackage * move catalog, collection, and item specific API logic into API subpackage * single out API tests, add more tests * fix context and mark as deprecated * add more text data * lint * add model serializer to Item to address developmentseed/geojson-pydantic#147 * update CI and readme * Fix type hints for py3.8 * use literals for Catalog type and Link Relations and Methods * add inline comment * untangle item_collection and api.item_collectoin tests --------- Co-authored-by: David Huard <[email protected]>
1 parent 86f7303 commit f28081a

File tree

74 files changed

+3360
-774
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+3360
-774
lines changed

.github/workflows/cicd.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,21 @@ jobs:
1414
runs-on: ubuntu-latest
1515
strategy:
1616
matrix:
17-
python-version: ["3.7", "3.8", "3.9", "3.10"]
17+
python-version: ["3.8", "3.9", "3.10", "3.11"]
1818

1919
steps:
20-
- uses: actions/checkout@v2
20+
- uses: actions/checkout@v4
2121

2222
- name: Set up Python ${{ matrix.python-version }}
23-
uses: actions/setup-python@v2
23+
uses: actions/setup-python@v4
2424
with:
2525
python-version: ${{ matrix.python-version }}
2626

2727
- name: Install dependencies
2828
run: |
2929
python -m pip install --upgrade pip
30-
python -m pip install tox codecov pre-commit
30+
python -m pip install tox pre-commit
31+
pre-commit install
3132
3233
# Run tox using the version of Python in `PATH`
3334
- name: Run Tox

.github/workflows/release.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,22 @@ jobs:
1111
runs-on: ubuntu-latest
1212
if: ${{ github.repository }} == 'stac-utils/stac-pydantic'
1313
steps:
14-
- uses: actions/checkout@v2
14+
- uses: actions/checkout@v4
1515

1616
- name: Set up Python 3.x
17-
uses: actions/setup-python@v2
17+
uses: actions/setup-python@v4
1818
with:
1919
python-version: "3.x"
2020

2121
- name: Install release dependencies
2222
run: |
2323
python -m pip install --upgrade pip
24-
pip install setuptools wheel twine
24+
pip install build
2525
2626
- name: Build and publish package
2727
env:
2828
TWINE_USERNAME: ${{ secrets.PYPI_STACUTILS_USERNAME }}
2929
TWINE_PASSWORD: ${{ secrets.PYPI_STACUTILS_PASSWORD }}
3030
run: |
31-
python setup.py sdist bdist_wheel
31+
python -m build
3232
twine upload dist/*

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ celerybeat-schedule
106106
# dotenv
107107
.env
108108

109+
# venv
110+
.venv
111+
109112
# Spyder project settings
110113
.spyderproject
111114
.spyproject
@@ -117,4 +120,4 @@ celerybeat-schedule
117120
/site
118121

119122
# skaffold temporary build/deploy files
120-
build.out
123+
build.out

.pre-commit-config.yaml

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,37 @@
1+
# See https://pre-commit.com for more information
2+
# See https://pre-commit.com/hooks.html for more hooks
13
repos:
2-
- repo: https://github.com/psf/black
3-
rev: 22.3.0
4+
- repo: https://github.com/pre-commit/pre-commit-hooks
5+
rev: v4.0.1
46
hooks:
5-
- id: black
6-
language_version: python3
7-
args: ["--safe"]
7+
- id: end-of-file-fixer
8+
- id: trailing-whitespace
9+
- id: check-yaml
10+
- id: check-added-large-files
11+
args: ["--maxkb=40960"]
812

9-
- repo: https://github.com/PyCQA/isort
10-
rev: 5.10.1
13+
- repo: local
1114
hooks:
15+
- id: black
16+
name: Black Formatting
17+
language: system
18+
types: [python]
19+
entry: black
20+
1221
- id: isort
13-
language_version: python3
22+
name: iSort Import Sorting
23+
language: system
24+
types: [python]
25+
entry: isort
1426

15-
- repo: https://github.com/PyCQA/flake8
16-
rev: 4.0.1
17-
hooks:
1827
- id: flake8
19-
language_version: python3
28+
name: Flake8 Formatting
29+
language: system
30+
types: [python]
31+
entry: flake8 --config=./pyproject.toml
2032

21-
- repo: https://github.com/pre-commit/mirrors-mypy
22-
rev: v0.931
23-
hooks:
2433
- id: mypy
25-
exclude: ^tests/
26-
args: [--strict]
27-
additional_dependencies: ["pydantic", "types-jsonschema", "types-setuptools", "types-requests"]
34+
name: MyPy Typecheck
35+
language: system
36+
types: [python]
37+
entry: mypy --config-file=./pyproject.toml

CHANGELOG.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
Unreleased
22
------------------
3+
- Support pydantic>2.0 (@huard)
34

45
2.0.3 (2022-5-3)
56
------------------
@@ -105,4 +106,4 @@ Unreleased
105106
- Allow extra asset-level fields (#1)
106107
- Fix population by field name model config, allowing model creation without extension namespaces (#2)
107108
- Add enum of commonly used asset media types (#3)
108-
- Move geojson models to `geojson-pydantic` library (#4)
109+
- Move geojson models to `geojson-pydantic` library (#4)

README.md

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pip install stac-pydantic
88

99
For local development:
1010
```
11-
pip install -e .["dev"]
11+
pip install -e ".[all]"
1212
```
1313

1414
| stac-pydantic | STAC Version |
@@ -31,11 +31,11 @@ pre-commit install
3131
Ensure you have all Python versions installed that the tests will be run against. If using pyenv, run:
3232

3333
```shell
34-
pyenv install 3.7.12
35-
pyenv install 3.8.12
36-
pyenv install 3.9.10
37-
pyenv install 3.10.2
38-
pyenv local 3.7.12 3.8.12 3.9.10 3.10.2
34+
pyenv install 3.8.18
35+
pyenv install 3.9.18
36+
pyenv install 3.10.13
37+
pyenv install 3.11.5
38+
pyenv local 3.8.18 3.9.18 3.10.13 3.11.5
3939
```
4040

4141
Run the entire test suite:
@@ -78,7 +78,7 @@ assert catalog.links[0].href == "item.json"
7878
```
7979

8080
### Extensions
81-
STAC defines many extensions which let the user customize the data in their catalog. `stac-pydantic.extensions.validate_extensions` will validate a `dict`, `Item`, `Collection` or `Catalog` against the schema urls provided in the `stac_extensions` property:
81+
STAC defines many extensions which let the user customize the data in their catalog. `stac-pydantic.extensions.validate_extensions` will validate a `dict`, `Item`, `Collection` or `Catalog` against the schema urls provided in the `stac_extensions` property:
8282

8383
```python
8484
from stac_pydantic import Item
@@ -88,36 +88,87 @@ stac_item = {
8888
"id": "12345",
8989
"type": "Feature",
9090
"stac_extensions": [
91-
"https://stac-extensions.github.io/eo/v1.0.0/schema.json"
91+
"https://stac-extensions.github.io/eo/v1.0.0/schema.json"
9292
],
9393
"geometry": { "type": "Point", "coordinates": [0, 0] },
94+
"bbox": [0.0, 0.0, 0.0, 0.0],
9495
"properties": {
9596
"datetime": "2020-03-09T14:53:23.262208+00:00",
9697
"eo:cloud_cover": 25,
9798
},
9899
"links": [],
99-
"assets": [],
100+
"assets": {},
100101
}
101102

102-
model = Item(**stac_item)
103+
model = Item(**stac_item)
103104
validate_extensions(model, reraise_exception=True)
104-
assert getattr(model.properties, "eo:cloud_cover") == 25
105+
assert getattr(model.properties, "eo:cloud_cover") == 25
105106
```
106107

107108
The complete list of current STAC Extensions can be found [here](https://stac-extensions.github.io/).
108109

109110
#### Vendor Extensions
110111
The same procedure described above works for any STAC Extension schema as long as it can be loaded from a public url.
111112

113+
### STAC API
114+
The [STAC API Specs](https://github.com/radiantearth/stac-api-spec) extent the core STAC specification for implementing dynamic catalogs. STAC Objects used in an API context should always import models from the `api` subpackage. This package extends
115+
Catalog, Collection, and Item models with additional fields and validation rules and introduces Collections and ItemCollections models and Pagination/ Search Links.
116+
It also implements models for defining ItemSeach queries.
117+
118+
```python
119+
from stac_pydantic.api import Item, ItemCollection
120+
121+
stac_item = Item(**{
122+
"id": "12345",
123+
"type": "Feature",
124+
"stac_extensions": [],
125+
"geometry": { "type": "Point", "coordinates": [0, 0] },
126+
"bbox": [0.0, 0.0, 0.0, 0.0],
127+
"properties": {
128+
"datetime": "2020-03-09T14:53:23.262208+00:00",
129+
},
130+
"collection": "CS3",
131+
"links": [
132+
{
133+
"rel": "self",
134+
"href": "http://stac.example.com/catalog/collections/CS3-20160503_132130_04/items/CS3-20160503_132130_04.json"
135+
},
136+
{
137+
"rel": "collection",
138+
"href": "http://stac.example.com/catalog/CS3-20160503_132130_04/catalog.json"
139+
},
140+
{
141+
"rel": "root",
142+
"href": "http://stac.example.com/catalog"
143+
}],
144+
"assets": {},
145+
})
146+
147+
stac_item_collection = ItemCollection(**{
148+
"type": "FeatureCollection",
149+
"features": [stac_item],
150+
"links": [
151+
{
152+
"rel": "self",
153+
"href": "http://stac.example.com/catalog/search?collection=CS3",
154+
"type": "application/geo+json"
155+
},
156+
{
157+
"rel": "root",
158+
"href": "http://stac.example.com/catalog",
159+
"type": "application/json"
160+
}],
161+
})
162+
```
163+
112164
### Exporting Models
113165
Most STAC extensions are namespaced with a colon (ex `eo:gsd`) to keep them distinct from other extensions. Because
114166
Python doesn't support the use of colons in variable names, we use [Pydantic aliasing](https://pydantic-docs.helpmanual.io/usage/model_config/#alias-generator)
115167
to add the namespace upon model export. This requires [exporting](https://pydantic-docs.helpmanual.io/usage/exporting_models/)
116-
the model with the `by_alias = True` parameter. A convenience method (``to_dict()``) is provided to export models with
117-
extension namespaces:
168+
the model with the `by_alias = True` parameter. Export methods (`model_dump()` and `model_dump_json()`) for models in this library have `by_alias` and `exclude_unset` st to `True` by default:
118169

119170
```python
120-
item_dict = item.to_dict()
171+
item_dict = item.model_dump()
121172
assert item_dict['properties']['landsat:row'] == item.properties.row == 250
122173
```
123174

pyproject.toml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
[build-system]
2+
requires = ["setuptools>=61.0"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name="stac-pydantic"
7+
description="Pydantic data models for the STAC spec"
8+
classifiers=[
9+
"Intended Audience :: Developers",
10+
"Intended Audience :: Information Technology",
11+
"Intended Audience :: Science/Research",
12+
"Programming Language :: Python :: 3.8",
13+
"Programming Language :: Python :: 3.9",
14+
"Programming Language :: Python :: 3.10",
15+
"Programming Language :: Python :: 3.11",
16+
"License :: OSI Approved :: MIT License",
17+
]
18+
keywords=["stac", "pydantic", "validation"]
19+
authors=[{ name = "Arturo Engineering", email = "[email protected]"}]
20+
license= { text = "MIT" }
21+
requires-python=">=3.8"
22+
dependencies = ["click>=8.1.7", "pydantic>=2.4.1", "geojson-pydantic>=1.0.0"]
23+
dynamic = ["version", "readme"]
24+
25+
[project.scripts]
26+
stac-pydantic = "stac_pydantic.scripts.cli:app"
27+
28+
[project.urls]
29+
homepage = "https://github.com/stac-utils/stac-pydantic"
30+
repository ="https://github.com/stac-utils/stac-pydantic.git"
31+
32+
[project.optional-dependencies]
33+
dev = ["arrow>=1.2.3",
34+
"pytest>=7.4.2",
35+
"pytest-cov>=4.1.0",
36+
"pytest-icdiff>=0.8",
37+
"requests>=2.31.0",
38+
"shapely>=2.0.1",
39+
"dictdiffer>=0.9.0",
40+
"jsonschema>=4.19.1",
41+
"pyyaml>=6.0.1"]
42+
lint = ["types-requests>=2.31.0.5",
43+
"types-jsonschema>=4.19.0.3",
44+
"types-PyYAML>=6.0.12.12",
45+
"black>=23.9.1",
46+
"isort>=5.12.0",
47+
"flake8>=6.1.0",
48+
"Flake8-pyproject>=1.2.3",
49+
"mypy>=1.5.1",
50+
"pre-commit>=3.4.0",
51+
"tox>=4.11.3"]
52+
53+
[tool.setuptools.dynamic]
54+
version = { attr = "stac_pydantic.version.__version__" }
55+
readme = {file = ["README.md"], content-type = "text/markdown"}
56+
57+
[tool.setuptools.package-data]
58+
stac_pydantic= ["*.typed"]
59+
60+
[tool.setuptools.packages.find]
61+
include = ["stac_pydantic*"]
62+
exclude = ["tests*"]
63+
64+
[tool.pytest.ini_options]
65+
addopts = "-sv --cov stac_pydantic --cov-report xml --cov-report term-missing --cov-fail-under 95"
66+
67+
[tool.black]
68+
line-length = 88
69+
target-version = ["py311"]
70+
71+
[tool.isort]
72+
profile = "black"
73+
known_first_party = "stac_pydantic"
74+
known_third_party = ["pydantic", "geojson-pydantic", "click"]
75+
default_section = "THIRDPARTY"
76+
77+
[tool.mypy]
78+
plugins = "pydantic.mypy"
79+
ignore_missing_imports = true
80+
exclude = ["tests", ".venv"]
81+
82+
[tool.flake8]
83+
ignore = ["E203", "E501"]
84+
select = ["C","E","F","W","B","B950"]
85+
exclude = ["tests", ".venv"]
86+
max-line-length = 88

setup.cfg

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)