[Git][debian-gis-team/pystac][upstream] New upstream version 1.12.2
Antonio Valentino (@antonio.valentino)
gitlab at salsa.debian.org
Sat Feb 22 17:21:31 GMT 2025
Antonio Valentino pushed to branch upstream at Debian GIS Project / pystac
Commits:
768dc38c by Antonio Valentino at 2025-02-22T16:35:09+00:00
New upstream version 1.12.2
- - - - -
25 changed files:
- .github/pull_request_template.md
- .github/workflows/continuous-integration.yml
- .gitignore
- + .python-version
- .readthedocs.yaml
- CHANGELOG.md
- README.md
- docs/contributing.rst
- − docs/pyproject.toml
- pyproject.toml
- pystac/serialization/identify.py
- pystac/version.py
- − scripts/bench
- scripts/pull-static
- − scripts/test
- tests/cassettes/test_item/ItemTest.test_null_geometry.yaml → tests/cassettes/test_item/test_null_geometry.yaml
- tests/conftest.py
- tests/extensions/test_eo.py
- tests/serialization/test_identify.py
- tests/test_collection.py
- tests/test_item.py
- tests/test_item_assets.py
- tests/test_item_collection.py
- tests/test_version.py
- uv.lock
Changes:
=====================================
.github/pull_request_template.md
=====================================
@@ -6,7 +6,8 @@
**PR Checklist:**
-- [ ] Pre-commit hooks and tests pass (run `scripts/test`)
+- [ ] Pre-commit hooks pass (run `pre-commit run --all-files`)
+- [ ] Tests pass (run `pytest`)
- [ ] Documentation has been updated to reflect changes, if applicable
- [ ] This PR maintains or improves overall codebase code coverage.
- [ ] Changes are added to the [CHANGELOG](https://github.com/stac-utils/pystac/blob/main/CHANGELOG.md). See [the docs](https://pystac.readthedocs.io/en/latest/contributing.html#changelog) for information about adding to the changelog.
=====================================
.github/workflows/continuous-integration.yml
=====================================
@@ -13,7 +13,6 @@ on:
merge_group:
concurrency:
- # Cancel running job if another commit is pushed to the branch
group: ${{ github.ref }}
cancel-in-progress: true
@@ -34,14 +33,14 @@ jobs:
- macos-latest
steps:
- uses: actions/checkout at v4
- - uses: actions/setup-python at v5
- with:
- python-version: ${{ matrix.python-version }}
- uses: astral-sh/setup-uv at v5
with:
- enable-cache: true
+ python-version: ${{ matrix.python-version }}
- name: Sync
run: uv sync --all-extras
+ - name: Lint
+ if: runner.os != 'Windows'
+ run: uv run pre-commit run --all-files
- name: Test on windows
if: runner.os == 'Windows'
shell: bash
@@ -50,19 +49,14 @@ jobs:
run: uv run pytest tests
- name: Test
if: runner.os != 'Windows'
- run: uv run scripts/test
+ run: uv run pytest tests --block-network --record-mode=none
coverage:
name: coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout at v4
- - uses: actions/setup-python at v5
- with:
- python-version: "3.10"
- uses: astral-sh/setup-uv at v5
- with:
- enable-cache: true
- name: Install with dependencies
run: uv sync --all-extras
- name: Run coverage with orjson
@@ -80,45 +74,18 @@ jobs:
if: ${{ env.GITHUB_REPOSITORY }} == 'stac-utils/pystac'
with:
token: ${{ secrets.CODECOV_TOKEN }}
- file: ./coverage.xml
+ files: ./coverage.xml
fail_ci_if_error: false
- name: Check for coverage drop
# This will use the configured fail-under, causing this job to fail if the
# coverage drops.
run: uv run coverage report
- lint:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- python-version:
- - "3.10"
- - "3.11"
- - "3.12"
- - "3.13"
- steps:
- - uses: actions/checkout at v4
- - uses: actions/setup-python at v5
- with:
- python-version: ${{ matrix.python-version }}
- - uses: astral-sh/setup-uv at v5
- with:
- enable-cache: true
- - name: Sync
- run: uv sync
- - name: Execute linters & type checkers
- run: uv run pre-commit run --all-files
-
without-orjson:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout at v4
- - uses: actions/setup-python at v5
- with:
- python-version: "3.10"
- uses: astral-sh/setup-uv at v5
- with:
- enable-cache: true
- name: Sync
run: uv sync
- name: Uninstall orjson
@@ -150,15 +117,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout at v4
- - uses: actions/setup-python at v5
- with:
- python-version: "3.10"
- uses: astral-sh/setup-uv at v5
- with:
- enable-cache: true
- name: Install pandoc
run: sudo apt-get install pandoc
- - name: Install pystac
- run: uv sync --no-dev && uv sync --package pystac-docs --inexact
+ - name: Sync
+ run: uv sync --group docs
- name: Check docs
run: uv run make -C docs html SPHINXOPTS="-W --keep-going -n"
=====================================
.gitignore
=====================================
@@ -107,7 +107,7 @@ ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
-.python-version
+# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
=====================================
.python-version
=====================================
@@ -0,0 +1 @@
+3.10
\ No newline at end of file
=====================================
.readthedocs.yaml
=====================================
@@ -14,8 +14,7 @@ build:
- asdf plugin add uv
- asdf install uv latest
- asdf global uv latest
- - uv sync --all-extras --no-dev
- - uv sync --package pystac-docs --inexact # we need inexact to keep the top-level sync packages
+ - uv sync --group docs
- uv run sphinx-build -T -b html -d docs/_build/doctrees -D language=en docs $READTHEDOCS_OUTPUT/html
formats:
=====================================
CHANGELOG.md
=====================================
@@ -2,6 +2,12 @@
## [Unreleased]
+## [v1.12.2]
+
+### Fixed
+
+- Make sure that `VersionRange` has `VersionID`s rather than strings ([#1512](https://github.com/stac-utils/pystac/pull/1512))
+
## [v1.12.1]
### Changed
@@ -904,7 +910,8 @@ use `Band.create`
Initial release.
-[Unreleased]: <https://github.com/stac-utils/pystac/compare/v1.12.1..main>
+[Unreleased]: <https://github.com/stac-utils/pystac/compare/v1.12.2..main>
+[v1.12.2]: <https://github.com/stac-utils/pystac/compare/v1.12.1..v1.12.2>
[v1.12.1]: <https://github.com/stac-utils/pystac/compare/v1.12.0..v1.12.1>
[v1.12.0]: <https://github.com/stac-utils/pystac/compare/v1.11.0..v1.12.0>
[v1.11.0]: <https://github.com/stac-utils/pystac/compare/v1.10.1..v1.11.0>
=====================================
README.md
=====================================
@@ -1,6 +1,6 @@
# PySTAC
-[](https://github.com/stac-utils/pystac/actions/workflows/continuous-integration.yml)
+[](https://github.com/stac-utils/pystac/actions/workflows/continuous-integration.yml)
[](https://badge.fury.io/py/pystac)
[](https://anaconda.org/conda-forge/pystac)
[](https://pystac.readthedocs.io/en/latest/)
=====================================
docs/contributing.rst
=====================================
@@ -33,7 +33,7 @@ To run the tests and generate the coverage report:
.. code-block:: bash
- $ pytest -v -s --block-network --cov pystac --cov-report term-missing
+ $ pytest -v -s --cov pystac --cov-report term-missing
To view the coverage report, you can run
`coverage report` (to view the report in the terminal) or `coverage html` (to generate
@@ -100,7 +100,7 @@ and report any improvements or regressions.
.. code-block:: bash
- scripts/bench
+ asv continuous --split -e --interleave-rounds --factor 1.25 main HEAD
The benchmark suite takes a while to run, and will report any significant
changes to standard output. For example, here's a benchmark comparison between
=====================================
docs/pyproject.toml deleted
=====================================
@@ -1,19 +0,0 @@
-[project]
-name = "pystac-docs"
-version = "0.0.0"
-description = "This package is only for uv, so it can share a lockfile with the top-level project. It should never be published"
-requires-python = ">=3.10"
-dependencies = [
- "boto3>=1.35.39",
- "ipython>=8.28.0",
- "jinja2>=3.1.4",
- "jupyter>=1.1.1",
- "nbsphinx>=0.9.5",
- "pydata-sphinx-theme>=0.15.4",
- "rasterio>=1.4.1",
- "shapely>=2.0.6",
- "sphinx>=8.1.1",
- "sphinx-autobuild>=2024.10.3",
- "sphinx-design>=0.6.1",
- "sphinxcontrib-fulltoc>=1.2.0",
-]
=====================================
pyproject.toml
=====================================
@@ -30,6 +30,47 @@ orjson = ["orjson>=3.5"]
urllib3 = ["urllib3>=1.26"]
validation = ["jsonschema~=4.18"]
+[dependency-groups]
+dev = [
+ "asv>=0.6.4",
+ "codespell<2.3",
+ "coverage>=7.6.2",
+ "doc8>=1.1.2",
+ "html5lib>=1.1",
+ "jinja2>=3.1.4",
+ "jsonschema>=4.23.0",
+ "mypy>=1.11.2",
+ "orjson>=3.10.7",
+ "packaging>=24.1",
+ "pre-commit>=4.0.1",
+ "pytest>=8.3.3",
+ "pytest-cov>=5.0.0",
+ "pytest-mock>=3.14.0",
+ "pytest-recording>=0.13.2",
+ "requests-mock>=1.12.1",
+ "ruff>=0.6.9",
+ "types-html5lib>=1.1.11.20240806",
+ "types-jsonschema>=4.23.0.20240813",
+ "types-orjson>=3.6.2",
+ "types-python-dateutil>=2.9.0.20241003",
+ "types-urllib3>=1.26.25.14",
+ "virtualenv>=20.26.6",
+]
+docs = [
+ "boto3>=1.35.39",
+ "ipython>=8.28.0",
+ "jinja2>=3.1.4",
+ "jupyter>=1.1.1",
+ "nbsphinx>=0.9.5",
+ "pydata-sphinx-theme>=0.15.4",
+ "rasterio>=1.4.1",
+ "shapely>=2.0.6",
+ "sphinx>=8.1.1",
+ "sphinx-autobuild>=2024.10.3",
+ "sphinx-design>=0.6.1",
+ "sphinxcontrib-fulltoc>=1.2.0",
+]
+
[project.urls]
Documentation = "https://pystac.readthedocs.io"
Repository = "https://github.com/stac-utils/pystac"
@@ -64,6 +105,7 @@ lint.select = ["E", "F", "I"]
[tool.pytest.ini_options]
filterwarnings = ["error"]
+addopts = "--block-network --record-mode=none"
[tool.setuptools.packages.find]
include = ["pystac*"]
@@ -72,36 +114,6 @@ exclude = ["tests*", "benchmarks*"]
[tool.setuptools.dynamic]
version = { attr = "pystac.version.__version__" }
-[tool.uv]
-dev-dependencies = [
- "asv>=0.6.4",
- "codespell<2.3",
- "coverage>=7.6.2",
- "doc8>=1.1.2",
- "html5lib>=1.1",
- "jinja2>=3.1.4",
- "jsonschema>=4.23.0",
- "mypy>=1.11.2",
- "orjson>=3.10.7",
- "packaging>=24.1",
- "pre-commit>=4.0.1",
- "pytest>=8.3.3",
- "pytest-cov>=5.0.0",
- "pytest-mock>=3.14.0",
- "pytest-recording>=0.13.2",
- "requests-mock>=1.12.1",
- "ruff>=0.6.9",
- "types-html5lib>=1.1.11.20240806",
- "types-jsonschema>=4.23.0.20240813",
- "types-orjson>=3.6.2",
- "types-python-dateutil>=2.9.0.20241003",
- "types-urllib3>=1.26.25.14",
- "virtualenv>=20.26.6",
-]
-
-[tool.uv.workspace]
-members = ["docs"]
-
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
=====================================
pystac/serialization/identify.py
=====================================
@@ -82,8 +82,8 @@ class STACVersionID:
class STACVersionRange:
"""Defines a range of STAC versions."""
- min_version: STACVersionID
- max_version: STACVersionID
+ _min_version: STACVersionID
+ _max_version: STACVersionID
def __init__(
self,
@@ -103,21 +103,45 @@ class STACVersionRange:
else:
self.max_version = max_version
- def set_min(self, v: STACVersionID) -> None:
+ @property
+ def min_version(self) -> STACVersionID:
+ return self._min_version
+
+ @min_version.setter
+ def min_version(self, v: str | STACVersionID) -> None:
+ if isinstance(v, str):
+ v = STACVersionID(v)
+ self._min_version = v
+
+ @property
+ def max_version(self) -> STACVersionID:
+ return self._max_version
+
+ @max_version.setter
+ def max_version(self, v: str | STACVersionID) -> None:
+ if isinstance(v, str):
+ v = STACVersionID(v)
+ self._max_version = v
+
+ def set_min(self, v: str | STACVersionID) -> None:
+ if isinstance(v, str):
+ v = STACVersionID(v)
if self.min_version < v:
if v < self.max_version:
self.min_version = v
else:
self.min_version = self.max_version
- def set_max(self, v: STACVersionID) -> None:
+ def set_max(self, v: str | STACVersionID) -> None:
+ if isinstance(v, str):
+ v = STACVersionID(v)
if v < self.max_version:
if self.min_version < v:
self.max_version = v
else:
self.max_version = self.min_version
- def set_to_single(self, v: STACVersionID) -> None:
+ def set_to_single(self, v: str | STACVersionID) -> None:
self.set_min(v)
self.set_max(v)
@@ -263,12 +287,12 @@ def identify_stac_object(json_dict: dict[str, Any]) -> STACJSONDescription:
stac_extensions = json_dict.get("stac_extensions", None)
if stac_version is None:
- version_range.set_min(STACVersionID("0.8.0"))
+ version_range.set_min("0.8.0")
else:
version_range.set_to_single(stac_version)
if stac_extensions is not None:
- version_range.set_min(STACVersionID("0.8.0"))
+ version_range.set_min("0.8.0")
if stac_extensions is None:
stac_extensions = []
=====================================
pystac/version.py
=====================================
@@ -1,6 +1,6 @@
import os
-__version__ = "1.12.1"
+__version__ = "1.12.2"
"""Library version"""
=====================================
scripts/bench deleted
=====================================
@@ -1,11 +0,0 @@
-#!/bin/bash
-
-set -e
-
-if [[ -z $ASV_FACTOR ]]; then
- ASV_FACTOR=1.25;
-fi
-
-asv continuous --split -e --interleave-rounds \
- --factor ${ASV_FACTOR} \
- main HEAD;
=====================================
scripts/pull-static
=====================================
@@ -2,7 +2,7 @@
set -e
-VERSION="1.5.0" #v1.5.0-beta.2 should work or no??
+VERSION="1.5.0"
SRC="https://cdn.jsdelivr.net/npm/@radiantearth/stac-fields@$VERSION/fields-normalized.json"
HERE=$(dirname "$0")
DEST=$(dirname "$HERE")/pystac/static/fields-normalized.json
=====================================
scripts/test deleted
=====================================
@@ -1,18 +0,0 @@
-#!/bin/bash
-
-set -e
-
-if [[ -z ${CI} ]]; then
- pre-commit run --all-files
-fi
-echo
-
-if [[ -z ${CI} || -n ${CHECK_COVERAGE} ]]; then
- echo " -- RUNNING UNIT TESTS (WITH COVERAGE) --"
- pytest tests --block-network --record-mode=none --cov
-else
- echo " -- RUNNING UNIT TESTS (WITHOUT COVERAGE) --"
- pytest tests --block-network --record-mode=none
-fi
-
-echo
=====================================
tests/cassettes/test_item/ItemTest.test_null_geometry.yaml → tests/cassettes/test_item/test_null_geometry.yaml
=====================================
@@ -7,7 +7,7 @@ interactions:
Host:
- schemas.stacspec.org
User-Agent:
- - Python-urllib/3.12
+ - Python-urllib/3.13
method: GET
uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/item.json
response:
@@ -99,7 +99,7 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- - Thu, 23 Jan 2025 15:04:21 GMT
+ - Sun, 02 Feb 2025 00:00:39 GMT
ETag:
- '"66e1651c-147c"'
Last-Modified:
@@ -111,19 +111,19 @@ interactions:
Via:
- 1.1 varnish
X-Cache:
- - MISS
+ - HIT
X-Cache-Hits:
- '0'
X-Fastly-Request-ID:
- - eee882ae55a7f40729d77f3d500a84bed4c637d0
+ - 9a54b2c2e8d8dfda667d69d0dd28acd3c223b4c7
X-GitHub-Request-Id:
- - 7F28:34A10D:FF6968:11D3377:67925A75
+ - 6B5F:16F8:3EC0E8:470F4B:679E5948
X-Served-By:
- - cache-den-kden1300051-DEN
+ - cache-bos4633-BOS
X-Timer:
- - S1737644661.145511,VS0,VE59
+ - S1738454440.623801,VS0,VE20
expires:
- - Thu, 23 Jan 2025 15:14:21 GMT
+ - Sat, 01 Feb 2025 17:36:33 GMT
x-proxy-cache:
- MISS
status:
@@ -137,7 +137,7 @@ interactions:
Host:
- schemas.stacspec.org
User-Agent:
- - Python-urllib/3.12
+ - Python-urllib/3.13
method: GET
uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/basics.json
response:
@@ -166,7 +166,7 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- - Thu, 23 Jan 2025 15:04:21 GMT
+ - Sun, 02 Feb 2025 00:00:39 GMT
ETag:
- '"66e1651c-21c"'
Last-Modified:
@@ -178,19 +178,19 @@ interactions:
Via:
- 1.1 varnish
X-Cache:
- - MISS
+ - HIT
X-Cache-Hits:
- '0'
X-Fastly-Request-ID:
- - 24282bdf85a8fde571d18d0a3007d5cf6bf30fcb
+ - 8f6c3b198c4c026542ea5cc5b46485cfce14313c
X-GitHub-Request-Id:
- - 5ED4:12DF28:F44C6C:1121586:67925A75
+ - 7D47:4AAAC:67C965:716773:679E5948
X-Served-By:
- - cache-den-kden1300073-DEN
+ - cache-bos4643-BOS
X-Timer:
- - S1737644661.228637,VS0,VE68
+ - S1738454440.712740,VS0,VE30
expires:
- - Thu, 23 Jan 2025 15:14:21 GMT
+ - Sat, 01 Feb 2025 17:36:33 GMT
x-origin-cache:
- HIT
x-proxy-cache:
@@ -206,7 +206,7 @@ interactions:
Host:
- schemas.stacspec.org
User-Agent:
- - Python-urllib/3.12
+ - Python-urllib/3.13
method: GET
uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/datetime.json
response:
@@ -265,7 +265,7 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- - Thu, 23 Jan 2025 15:04:21 GMT
+ - Sun, 02 Feb 2025 00:00:39 GMT
ETag:
- '"66e1651c-a82"'
Last-Modified:
@@ -277,19 +277,19 @@ interactions:
Via:
- 1.1 varnish
X-Cache:
- - MISS
+ - HIT
X-Cache-Hits:
- '0'
X-Fastly-Request-ID:
- - a29e86e905de4ad9406e730b8f75d8d9fc0e363d
+ - 530a4436422927e3d78506b58f78887510928f7f
X-GitHub-Request-Id:
- - 5488:230B9F:B893E:CFA99:67925A75
+ - 97C0:A1EF3:5E972D:68288C:679E5949
X-Served-By:
- - cache-den-kden1300042-DEN
+ - cache-bos4655-BOS
X-Timer:
- - S1737644661.327324,VS0,VE57
+ - S1738454440.811790,VS0,VE30
expires:
- - Thu, 23 Jan 2025 15:14:21 GMT
+ - Sat, 01 Feb 2025 17:36:33 GMT
x-proxy-cache:
- MISS
status:
@@ -303,7 +303,7 @@ interactions:
Host:
- schemas.stacspec.org
User-Agent:
- - Python-urllib/3.12
+ - Python-urllib/3.13
method: GET
uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/instrument.json
response:
@@ -334,7 +334,7 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- - Thu, 23 Jan 2025 15:04:21 GMT
+ - Sun, 02 Feb 2025 00:00:39 GMT
ETag:
- '"66e1651c-2a2"'
Last-Modified:
@@ -346,21 +346,19 @@ interactions:
Via:
- 1.1 varnish
X-Cache:
- - MISS
+ - HIT
X-Cache-Hits:
- '0'
X-Fastly-Request-ID:
- - dd7f2955fbdd7a5b0b24cac94bac85eadaea1608
+ - efcbb9e72539f153ef7cf2b7377c273d12c96c9e
X-GitHub-Request-Id:
- - 802D:338B06:10BB8A9:1298392:67925A75
+ - FA0A:15A6D:5C46DD:65DCD4:679E5947
X-Served-By:
- - cache-den-kden1300065-DEN
+ - cache-bos4692-BOS
X-Timer:
- - S1737644661.410596,VS0,VE58
+ - S1738454440.905646,VS0,VE41
expires:
- - Thu, 23 Jan 2025 15:14:21 GMT
- x-origin-cache:
- - HIT
+ - Sat, 01 Feb 2025 17:36:33 GMT
x-proxy-cache:
- MISS
status:
@@ -374,7 +372,7 @@ interactions:
Host:
- schemas.stacspec.org
User-Agent:
- - Python-urllib/3.12
+ - Python-urllib/3.13
method: GET
uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/licensing.json
response:
@@ -400,7 +398,7 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- - Thu, 23 Jan 2025 15:04:21 GMT
+ - Sun, 02 Feb 2025 00:00:40 GMT
ETag:
- '"66e1651c-135"'
Last-Modified:
@@ -412,19 +410,19 @@ interactions:
Via:
- 1.1 varnish
X-Cache:
- - MISS
+ - HIT
X-Cache-Hits:
- '0'
X-Fastly-Request-ID:
- - 2b8fd8778031ec89e594e7017d5111a507d66dfc
+ - 3d703898d330148032f27df7d9d46c65c819285e
X-GitHub-Request-Id:
- - D377:3405BB:10C72BA:12A3CD2:67925A75
+ - 085E:1C811A:56FD1C:609622:679E5949
X-Served-By:
- - cache-den-kden1300032-DEN
+ - cache-bos4624-BOS
X-Timer:
- - S1737644661.495593,VS0,VE54
+ - S1738454440.007807,VS0,VE27
expires:
- - Thu, 23 Jan 2025 15:14:21 GMT
+ - Sat, 01 Feb 2025 17:36:33 GMT
x-origin-cache:
- HIT
x-proxy-cache:
@@ -440,7 +438,7 @@ interactions:
Host:
- schemas.stacspec.org
User-Agent:
- - Python-urllib/3.12
+ - Python-urllib/3.13
method: GET
uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/provider.json
response:
@@ -476,7 +474,7 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- - Thu, 23 Jan 2025 15:04:21 GMT
+ - Sun, 02 Feb 2025 00:00:40 GMT
ETag:
- '"66e1651c-40e"'
Last-Modified:
@@ -488,19 +486,19 @@ interactions:
Via:
- 1.1 varnish
X-Cache:
- - MISS
+ - HIT
X-Cache-Hits:
- '0'
X-Fastly-Request-ID:
- - 84d8905f4abd1401e3d76535680a6bda88fa0568
+ - 01a1b60753cc5e73cb47bd5d87093ead8c9e73d8
X-GitHub-Request-Id:
- - 538F:1011A9:1037FFE:1214BBF:67925A75
+ - 6370:26E82C:5B4B43:64E477:679E5947
X-Served-By:
- - cache-den-kden1300062-DEN
+ - cache-bos4647-BOS
X-Timer:
- - S1737644662.570305,VS0,VE54
+ - S1738454440.107510,VS0,VE29
expires:
- - Thu, 23 Jan 2025 15:14:21 GMT
+ - Sat, 01 Feb 2025 17:36:33 GMT
x-origin-cache:
- HIT
x-proxy-cache:
=====================================
tests/conftest.py
=====================================
@@ -1,9 +1,11 @@
# TODO move all test case code to this file
+import json
import shutil
import uuid
from datetime import datetime
from pathlib import Path
+from typing import Any
import pytest
@@ -61,6 +63,14 @@ def get_data_file(rel_path: str) -> str:
return str(here / "data-files" / rel_path)
+ at pytest.fixture
+def sample_item_dict() -> dict[str, Any]:
+ m = TestCases.get_path("data-files/item/sample-item.json")
+ with open(m) as f:
+ item_dict: dict[str, Any] = json.load(f)
+ return item_dict
+
+
@pytest.fixture
def sample_item() -> Item:
return Item.from_file(TestCases.get_path("data-files/item/sample-item.json"))
=====================================
tests/extensions/test_eo.py
=====================================
@@ -516,3 +516,18 @@ def test_required_property_missing(ext_item: pystac.Item) -> None:
assert bands is not None
with pytest.raises(RequiredPropertyMissing):
bands[0].name
+
+
+def test_unnecessary_migrations_not_performed(ext_item: Item) -> None:
+ item_as_dict = ext_item.to_dict(include_self_link=False, transform_hrefs=False)
+ item_as_dict["stac_version"] = "1.0.0"
+ item_as_dict["properties"]["eo:bands"] = [{"name": "B1", "common_name": "coastal"}]
+
+ item = Item.from_dict(item_as_dict)
+
+ migrated_item = pystac.Item.from_dict(item_as_dict, migrate=True)
+
+ assert item.properties == migrated_item.properties
+ assert len(item.assets) == len(migrated_item.assets)
+ for key, value in item.assets.items():
+ assert value.to_dict() == migrated_item.assets[key].to_dict()
=====================================
tests/serialization/test_identify.py
=====================================
@@ -118,3 +118,17 @@ class VersionTest(unittest.TestCase):
version_range = STACVersionRange(min_version="0.6.0-rc1", max_version="0.9.0")
self.assertTrue(version_range.contains("0.9.0"))
+
+ def test_version_range_set_to_single(self) -> None:
+ version_range = STACVersionRange()
+ version_range.set_min("1.0.0-beta.1")
+ version_range.set_to_single("1.0.0")
+
+ self.assertTrue(version_range.contains("1.0.0"))
+
+ def test_version_range_set_min_and_max_directly(self) -> None:
+ version_range = STACVersionRange()
+ version_range.min_version = "1.0.0-beta.1" # type:ignore
+ version_range.max_version = "1.1.0" # type:ignore
+
+ self.assertTrue(version_range.contains("1.0.0"))
=====================================
tests/test_collection.py
=====================================
@@ -3,7 +3,6 @@ from __future__ import annotations
import json
import os
import tempfile
-import unittest
from collections.abc import Iterator
from copy import deepcopy
from datetime import datetime
@@ -32,499 +31,512 @@ from tests.utils import ARBITRARY_BBOX, ARBITRARY_GEOM, TestCases
TEST_DATETIME = datetime(2020, 3, 14, 16, 32)
-class ProviderTest(unittest.TestCase):
- def test_to_from_dict(self) -> None:
- provider_dict = {
- "name": "Remote Data, Inc",
- "description": "Producers of awesome spatiotemporal assets",
- "roles": ["producer", "processor"],
- "url": "http://remotedata.io",
- "extension:field": "some value",
- }
- expected_extra_fields = {"extension:field": provider_dict["extension:field"]}
-
- provider = Provider.from_dict(provider_dict)
-
- self.assertEqual(provider_dict["name"], provider.name)
- self.assertEqual(provider_dict["description"], provider.description)
- self.assertEqual(provider_dict["roles"], provider.roles)
- self.assertEqual(provider_dict["url"], provider.url)
- self.assertDictEqual(expected_extra_fields, provider.extra_fields)
-
- self.assertDictEqual(provider_dict, provider.to_dict())
-
-
-class CollectionTest(unittest.TestCase):
- def test_spatial_extent_from_coordinates(self) -> None:
- extent = SpatialExtent.from_coordinates(ARBITRARY_GEOM["coordinates"])
-
- self.assertEqual(len(extent.bboxes), 1)
- bbox = extent.bboxes[0]
- self.assertEqual(len(bbox), 4)
- for x in bbox:
- self.assertTrue(isinstance(x, float))
-
- def test_read_eo_items_are_heritable(self) -> None:
- cat = TestCases.case_5()
- item = next(cat.get_items(recursive=True))
-
- self.assertTrue(EOExtension.has_extension(item))
-
- def test_save_uses_previous_catalog_type(self) -> None:
- collection = TestCases.case_8()
- assert collection.STAC_OBJECT_TYPE == pystac.STACObjectType.COLLECTION
- self.assertEqual(collection.catalog_type, CatalogType.SELF_CONTAINED)
- with tempfile.TemporaryDirectory() as tmp_dir:
- collection.normalize_hrefs(tmp_dir)
- href = collection.self_href
- collection.save()
-
- collection2 = pystac.Collection.from_file(href)
- self.assertEqual(collection2.catalog_type, CatalogType.SELF_CONTAINED)
-
- def test_clone_uses_previous_catalog_type(self) -> None:
- catalog = TestCases.case_8()
- assert catalog.catalog_type == CatalogType.SELF_CONTAINED
- clone = catalog.clone()
- self.assertEqual(clone.catalog_type, CatalogType.SELF_CONTAINED)
-
- def test_clone_cant_mutate_original(self) -> None:
- collection = TestCases.case_8()
- assert collection.keywords is not None
- self.assertListEqual(collection.keywords, ["disaster", "open"])
- clone = collection.clone()
- clone.extra_fields["test"] = "extra"
- self.assertNotIn("test", collection.extra_fields)
- assert clone.keywords is not None
- clone.keywords.append("clone")
- self.assertListEqual(clone.keywords, ["disaster", "open", "clone"])
- self.assertListEqual(collection.keywords, ["disaster", "open"])
- self.assertNotEqual(id(collection.summaries), id(clone.summaries))
-
- def test_multiple_extents(self) -> None:
- cat1 = TestCases.case_1()
- country = cat1.get_child("country-1")
- assert country is not None
- col1 = country.get_child("area-1-1")
- assert col1 is not None
- col1.validate()
- self.assertIsInstance(col1, Collection)
- validate_dict(col1.to_dict(), pystac.STACObjectType.COLLECTION)
-
- multi_ext_uri = TestCases.get_path("data-files/collections/multi-extent.json")
- with open(multi_ext_uri) as f:
- multi_ext_dict = json.load(f)
- validate_dict(multi_ext_dict, pystac.STACObjectType.COLLECTION)
- self.assertIsInstance(Collection.from_dict(multi_ext_dict), Collection)
-
- multi_ext_col = Collection.from_file(multi_ext_uri)
- multi_ext_col.validate()
- ext = multi_ext_col.extent
- extent_dict = multi_ext_dict["extent"]
- self.assertIsInstance(ext, Extent)
- self.assertIsInstance(ext.spatial.bboxes[0], list)
- self.assertEqual(len(ext.spatial.bboxes), 3)
- self.assertDictEqual(ext.to_dict(), extent_dict)
-
- cloned_ext = ext.clone()
- self.assertDictEqual(cloned_ext.to_dict(), multi_ext_dict["extent"])
-
- def test_extra_fields(self) -> None:
- catalog = TestCases.case_2()
- collection = catalog.get_child("1a8c1632-fa91-4a62-b33e-3a87c2ebdf16")
- assert collection is not None
-
- collection.extra_fields["test"] = "extra"
-
- with tempfile.TemporaryDirectory() as tmp_dir:
- p = os.path.join(tmp_dir, "collection.json")
- collection.save_object(include_self_link=False, dest_href=p)
- with open(p) as f:
- col_json = json.load(f)
- self.assertTrue("test" in col_json)
- self.assertEqual(col_json["test"], "extra")
-
- read_col = pystac.Collection.from_file(p)
- self.assertTrue("test" in read_col.extra_fields)
- self.assertEqual(read_col.extra_fields["test"], "extra")
-
- def test_update_extents(self) -> None:
- catalog = TestCases.case_2()
- base_collection = catalog.get_child("1a8c1632-fa91-4a62-b33e-3a87c2ebdf16")
- assert isinstance(base_collection, Collection)
- base_extent = base_collection.extent
- collection = base_collection.clone()
-
- item1 = Item(
- id="test-item-1",
- geometry=ARBITRARY_GEOM,
- bbox=[-180, -90, 180, 90],
- datetime=TEST_DATETIME,
- properties={"key": "one"},
- stac_extensions=["eo", "commons"],
- )
-
- item2 = Item(
- id="test-item-1",
- geometry=ARBITRARY_GEOM,
- bbox=[-180, -90, 180, 90],
- datetime=None,
- properties={
- "start_datetime": datetime_to_str(datetime(2000, 1, 1, 12, 0, 0, 0)),
- "end_datetime": datetime_to_str(datetime(2000, 2, 1, 12, 0, 0, 0)),
- },
- stac_extensions=["eo", "commons"],
- )
-
- collection.add_item(item1)
-
- collection.update_extent_from_items()
- self.assertEqual([[-180, -90, 180, 90]], collection.extent.spatial.bboxes)
- self.assertEqual(
- len(base_extent.spatial.bboxes[0]), len(collection.extent.spatial.bboxes[0])
- )
-
- self.assertNotEqual(
- base_extent.temporal.intervals, collection.extent.temporal.intervals
- )
- collection.remove_item("test-item-1")
- collection.update_extent_from_items()
- self.assertNotEqual([[-180, -90, 180, 90]], collection.extent.spatial.bboxes)
- collection.add_item(item2)
-
- collection.update_extent_from_items()
-
- self.assertEqual(
- [
- [
- item2.common_metadata.start_datetime,
- base_extent.temporal.intervals[0][1],
- ]
- ],
- collection.extent.temporal.intervals,
- )
-
- def test_supplying_href_in_init_does_not_fail(self) -> None:
- test_href = "http://example.com/collection.json"
- spatial_extent = SpatialExtent(bboxes=[ARBITRARY_BBOX])
- temporal_extent = TemporalExtent(intervals=[[TEST_DATETIME, None]])
-
- collection_extent = Extent(spatial=spatial_extent, temporal=temporal_extent)
- collection = Collection(
- id="test", description="test desc", extent=collection_extent, href=test_href
- )
-
- self.assertEqual(collection.get_self_href(), test_href)
-
- def test_collection_with_href_caches_by_href(self) -> None:
- collection = pystac.Collection.from_file(
- TestCases.get_path("data-files/examples/hand-0.8.1/collection.json")
- )
- cache = collection._resolved_objects
-
- # Since all of our STAC objects have HREFs, everything should be
- # cached only by HREF
- self.assertEqual(len(cache.id_keys_to_objects), 0)
-
- @pytest.mark.block_network
- def test_assets(self) -> None:
- path = TestCases.get_path("data-files/collections/with-assets.json")
- with open(path) as f:
- data = json.load(f)
- collection = pystac.Collection.from_dict(data)
- collection.validate()
-
- def test_get_assets(self) -> None:
- collection = pystac.Collection.from_file(
- TestCases.get_path("data-files/collections/with-assets.json")
- )
-
- media_type_filter = collection.get_assets(media_type=pystac.MediaType.PNG)
- self.assertCountEqual(media_type_filter.keys(), ["thumbnail"])
- role_filter = collection.get_assets(role="thumbnail")
- self.assertCountEqual(role_filter.keys(), ["thumbnail"])
- multi_filter = collection.get_assets(
- media_type=pystac.MediaType.PNG, role="thumbnail"
- )
- self.assertCountEqual(multi_filter.keys(), ["thumbnail"])
-
- no_filter = collection.get_assets()
- self.assertIsNot(no_filter, collection.assets)
- self.assertCountEqual(no_filter.keys(), ["thumbnail"])
- no_filter["thumbnail"].description = "foo"
- assert collection.assets["thumbnail"].description != "foo"
-
- no_assets = collection.get_assets(media_type=pystac.MediaType.HDF)
- self.assertEqual(no_assets, {})
-
- def test_removing_optional_attributes(self) -> None:
- path = TestCases.get_path("data-files/collections/with-assets.json")
- with open(path) as file:
- data = json.load(file)
- data["title"] = "dummy title"
- data["stac_extensions"] = ["dummy extension"]
- data["keywords"] = ["key", "word"]
- data["providers"] = [{"name": "pystac"}]
- collection = pystac.Collection.from_dict(data)
-
- # Assert we have everything set
- assert collection.title
- assert collection.stac_extensions
- assert collection.keywords
- assert collection.providers
- assert collection.summaries
- assert collection.assets
-
- # Remove all of the optional stuff
- collection.title = None
- collection.stac_extensions = []
- collection.keywords = []
- collection.providers = []
- collection.summaries = pystac.Summaries({})
- collection.assets = {}
-
- collection_as_dict = collection.to_dict()
- for key in (
- "title",
- "stac_extensions",
- "keywords",
- "providers",
- "summaries",
- "assets",
- ):
- assert key not in collection_as_dict
-
- def test_from_dict_preserves_dict(self) -> None:
- path = TestCases.get_path("data-files/collections/with-assets.json")
- with open(path) as f:
- collection_dict = json.load(f)
- param_dict = deepcopy(collection_dict)
-
- # test that the parameter is preserved
- _ = Collection.from_dict(param_dict)
- self.assertEqual(param_dict, collection_dict)
-
- # assert that the parameter is not preserved with
- # non-default parameter
- _ = Collection.from_dict(param_dict, preserve_dict=False, migrate=False)
- self.assertNotEqual(param_dict, collection_dict)
-
- def test_from_dict_set_root(self) -> None:
- path = TestCases.get_path("data-files/examples/hand-0.8.1/collection.json")
- with open(path) as f:
- collection_dict = json.load(f)
- catalog = pystac.Catalog(id="test", description="test desc")
- collection = Collection.from_dict(collection_dict, root=catalog)
- self.assertIs(collection.get_root(), catalog)
-
- def test_schema_summary(self) -> None:
- collection = pystac.Collection.from_file(
- TestCases.get_path(
- "data-files/examples/1.0.0/collection-only/collection-with-schemas.json"
- )
- )
- instruments_schema = get_required(
- collection.summaries.get_schema("instruments"),
- collection.summaries,
- "instruments",
- )
-
- self.assertIsInstance(instruments_schema, dict)
-
- def test_from_invalid_dict_raises_exception(self) -> None:
- stac_io = pystac.StacIO.default()
- catalog_dict = stac_io.read_json(
- TestCases.get_path("data-files/catalogs/test-case-1/catalog.json")
- )
- with self.assertRaises(pystac.STACTypeError):
- _ = pystac.Collection.from_dict(catalog_dict)
-
- def test_clone_preserves_assets(self) -> None:
- path = TestCases.get_path("data-files/collections/with-assets.json")
- original_collection = Collection.from_file(path)
- assert len(original_collection.assets) > 0
- assert all(
- asset.owner is original_collection
- for asset in original_collection.assets.values()
- )
-
- cloned_collection = original_collection.clone()
-
- for key in original_collection.assets:
- with self.subTest(f"Preserves {key} asset"):
- self.assertIn(key, cloned_collection.assets)
- cloned_asset = cloned_collection.assets.get(key)
- if cloned_asset is not None:
- with self.subTest(f"Sets owner for {key}"):
- self.assertIs(cloned_asset.owner, cloned_collection)
-
- def test_to_dict_no_self_href(self) -> None:
- temporal_extent = TemporalExtent(intervals=[[TEST_DATETIME, None]])
- spatial_extent = SpatialExtent(bboxes=ARBITRARY_BBOX)
- extent = Extent(spatial=spatial_extent, temporal=temporal_extent)
- collection = Collection(
- id="an-id", description="A test Collection", extent=extent
+def test_provider_to_from_dict() -> None:
+ provider_dict = {
+ "name": "Remote Data, Inc",
+ "description": "Producers of awesome spatiotemporal assets",
+ "roles": ["producer", "processor"],
+ "url": "http://remotedata.io",
+ "extension:field": "some value",
+ }
+ expected_extra_fields = {"extension:field": provider_dict["extension:field"]}
+
+ provider = Provider.from_dict(provider_dict)
+
+ assert (
+ provider_dict["name"],
+ provider_dict["description"],
+ provider_dict["roles"],
+ provider_dict["url"],
+ expected_extra_fields,
+ provider_dict,
+ ) == (
+ provider.name,
+ provider.description,
+ provider.roles,
+ provider.url,
+ provider.extra_fields,
+ provider.to_dict(),
+ )
+
+
+def test_spatial_extent_from_coordinates() -> None:
+ extent = SpatialExtent.from_coordinates(ARBITRARY_GEOM["coordinates"])
+
+ assert len(extent.bboxes) == 1
+ bbox = extent.bboxes[0]
+ assert len(bbox) == 4
+ for x in bbox:
+ assert isinstance(x, float)
+
+
+def test_read_eo_items_are_heritable() -> None:
+ cat = TestCases.case_5()
+ item = next(cat.get_items(recursive=True))
+
+ assert EOExtension.has_extension(item)
+
+
+def test_save_uses_previous_catalog_type() -> None:
+ collection = TestCases.case_8()
+ assert collection.STAC_OBJECT_TYPE == pystac.STACObjectType.COLLECTION
+ assert collection.catalog_type == CatalogType.SELF_CONTAINED
+ with tempfile.TemporaryDirectory() as tmp_dir:
+ collection.normalize_hrefs(tmp_dir)
+ href = collection.self_href
+ collection.save()
+
+ collection2 = pystac.Collection.from_file(href)
+ assert collection2.catalog_type == CatalogType.SELF_CONTAINED
+
+
+def test_clone_uses_previous_catalog_type() -> None:
+ catalog = TestCases.case_8()
+ assert catalog.catalog_type == CatalogType.SELF_CONTAINED
+ clone = catalog.clone()
+ assert clone.catalog_type == CatalogType.SELF_CONTAINED
+
+
+def test_clone_cant_mutate_original() -> None:
+ collection = TestCases.case_8()
+ assert collection.keywords == ["disaster", "open"]
+ clone = collection.clone()
+ clone.extra_fields["test"] = "extra"
+ assert "test" not in collection.extra_fields
+ assert clone.keywords is not None
+ clone.keywords.append("clone")
+ assert clone.keywords == ["disaster", "open", "clone"]
+ assert collection.keywords == ["disaster", "open"]
+ assert id(collection.summaries) != id(clone.summaries)
+
+
+def test_multiple_extents() -> None:
+ cat1 = TestCases.case_1()
+ country = cat1.get_child("country-1")
+ assert country is not None
+ col1 = country.get_child("area-1-1")
+ assert col1 is not None
+ col1.validate()
+ assert isinstance(col1, Collection)
+ validate_dict(col1.to_dict(), pystac.STACObjectType.COLLECTION)
+
+ multi_ext_uri = TestCases.get_path("data-files/collections/multi-extent.json")
+ with open(multi_ext_uri) as f:
+ multi_ext_dict = json.load(f)
+ validate_dict(multi_ext_dict, pystac.STACObjectType.COLLECTION)
+ assert isinstance(Collection.from_dict(multi_ext_dict), Collection)
+
+ multi_ext_col = Collection.from_file(multi_ext_uri)
+ multi_ext_col.validate()
+ ext = multi_ext_col.extent
+ extent_dict = multi_ext_dict["extent"]
+ assert isinstance(ext, Extent)
+ assert isinstance(ext.spatial.bboxes[0], list)
+ assert len(ext.spatial.bboxes) == 3
+ assert ext.to_dict() == extent_dict
+
+ cloned_ext = ext.clone()
+ assert cloned_ext.to_dict() == multi_ext_dict["extent"]
+
+
+def test_extra_fields() -> None:
+ catalog = TestCases.case_2()
+ collection = catalog.get_child("1a8c1632-fa91-4a62-b33e-3a87c2ebdf16")
+ assert collection is not None
+
+ collection.extra_fields["test"] = "extra"
+
+ with tempfile.TemporaryDirectory() as tmp_dir:
+ p = os.path.join(tmp_dir, "collection.json")
+ collection.save_object(include_self_link=False, dest_href=p)
+ with open(p) as f:
+ col_json = json.load(f)
+ assert "test" in col_json
+ assert col_json["test"] == "extra"
+
+ read_col = pystac.Collection.from_file(p)
+ assert "test" in read_col.extra_fields
+ assert read_col.extra_fields["test"] == "extra"
+
+
+def test_update_extents() -> None:
+ catalog = TestCases.case_2()
+ base_collection = catalog.get_child("1a8c1632-fa91-4a62-b33e-3a87c2ebdf16")
+ assert isinstance(base_collection, Collection)
+ base_extent = base_collection.extent
+ collection = base_collection.clone()
+
+ item1 = Item(
+ id="test-item-1",
+ geometry=ARBITRARY_GEOM,
+ bbox=[-180, -90, 180, 90],
+ datetime=TEST_DATETIME,
+ properties={"key": "one"},
+ stac_extensions=["eo", "commons"],
+ )
+
+ item2 = Item(
+ id="test-item-1",
+ geometry=ARBITRARY_GEOM,
+ bbox=[-180, -90, 180, 90],
+ datetime=None,
+ properties={
+ "start_datetime": datetime_to_str(datetime(2000, 1, 1, 12, 0, 0, 0)),
+ "end_datetime": datetime_to_str(datetime(2000, 2, 1, 12, 0, 0, 0)),
+ },
+ stac_extensions=["eo", "commons"],
+ )
+
+ collection.add_item(item1)
+
+ collection.update_extent_from_items()
+ assert [[-180, -90, 180, 90]] == collection.extent.spatial.bboxes
+ assert len(base_extent.spatial.bboxes[0]) == len(
+ collection.extent.spatial.bboxes[0]
+ )
+ assert base_extent.temporal.intervals != collection.extent.temporal.intervals
+
+ collection.remove_item("test-item-1")
+ collection.update_extent_from_items()
+ assert [[-180, -90, 180, 90]] != collection.extent.spatial.bboxes
+ collection.add_item(item2)
+
+ collection.update_extent_from_items()
+
+ assert [
+ [
+ item2.common_metadata.start_datetime,
+ base_extent.temporal.intervals[0][1],
+ ]
+ ] == collection.extent.temporal.intervals
+
+
+def test_supplying_href_in_init_does_not_fail() -> None:
+ test_href = "http://example.com/collection.json"
+ spatial_extent = SpatialExtent(bboxes=[ARBITRARY_BBOX])
+ temporal_extent = TemporalExtent(intervals=[[TEST_DATETIME, None]])
+
+ collection_extent = Extent(spatial=spatial_extent, temporal=temporal_extent)
+ collection = Collection(
+ id="test", description="test desc", extent=collection_extent, href=test_href
+ )
+
+ assert collection.get_self_href() == test_href
+
+
+def test_collection_with_href_caches_by_href() -> None:
+ collection = pystac.Collection.from_file(
+ TestCases.get_path("data-files/examples/hand-0.8.1/collection.json")
+ )
+ cache = collection._resolved_objects
+
+ # Since all of our STAC objects have HREFs, everything should be
+ # cached only by HREF
+ assert len(cache.id_keys_to_objects) == 0
+
+
+ at pytest.mark.block_network
+def test_assets() -> None:
+ path = TestCases.get_path("data-files/collections/with-assets.json")
+ with open(path) as f:
+ data = json.load(f)
+ collection = pystac.Collection.from_dict(data)
+ collection.validate()
+
+
+def test_get_assets() -> None:
+ collection = pystac.Collection.from_file(
+ TestCases.get_path("data-files/collections/with-assets.json")
+ )
+
+ media_type_filter = collection.get_assets(media_type=pystac.MediaType.PNG)
+ assert list(media_type_filter.keys()) == ["thumbnail"]
+ role_filter = collection.get_assets(role="thumbnail")
+ assert list(role_filter.keys()) == ["thumbnail"]
+ multi_filter = collection.get_assets(
+ media_type=pystac.MediaType.PNG, role="thumbnail"
+ )
+ assert list(multi_filter.keys()) == ["thumbnail"]
+
+ no_filter = collection.get_assets()
+ assert no_filter is not collection.assets
+ assert list(no_filter.keys()) == ["thumbnail"]
+ no_filter["thumbnail"].description = "foo"
+ assert collection.assets["thumbnail"].description != "foo"
+
+ no_assets = collection.get_assets(media_type=pystac.MediaType.HDF)
+ assert no_assets == {}
+
+
+def test_removing_optional_attributes() -> None:
+ path = TestCases.get_path("data-files/collections/with-assets.json")
+ with open(path) as file:
+ data = json.load(file)
+ data["title"] = "dummy title"
+ data["stac_extensions"] = ["dummy extension"]
+ data["keywords"] = ["key", "word"]
+ data["providers"] = [{"name": "pystac"}]
+ collection = pystac.Collection.from_dict(data)
+
+ # Assert we have everything set
+ assert collection.title
+ assert collection.stac_extensions
+ assert collection.keywords
+ assert collection.providers
+ assert collection.summaries
+ assert collection.assets
+
+ # Remove all of the optional stuff
+ collection.title = None
+ collection.stac_extensions = []
+ collection.keywords = []
+ collection.providers = []
+ collection.summaries = pystac.Summaries({})
+ collection.assets = {}
+
+ collection_as_dict = collection.to_dict()
+ for key in (
+ "title",
+ "stac_extensions",
+ "keywords",
+ "providers",
+ "summaries",
+ "assets",
+ ):
+ assert key not in collection_as_dict
+
+
+def test_from_dict_preserves_dict() -> None:
+ path = TestCases.get_path("data-files/collections/with-assets.json")
+ with open(path) as f:
+ collection_dict = json.load(f)
+ param_dict = deepcopy(collection_dict)
+
+ # test that the parameter is preserved
+ _ = Collection.from_dict(param_dict)
+ assert param_dict == collection_dict
+
+ # assert that the parameter is not preserved with
+ # non-default parameter
+ _ = Collection.from_dict(param_dict, preserve_dict=False, migrate=False)
+ assert param_dict != collection_dict
+
+
+def test_from_dict_set_root() -> None:
+ path = TestCases.get_path("data-files/examples/hand-0.8.1/collection.json")
+ with open(path) as f:
+ collection_dict = json.load(f)
+ catalog = pystac.Catalog(id="test", description="test desc")
+ collection = Collection.from_dict(collection_dict, root=catalog)
+ assert collection.get_root() is catalog
+
+
+def test_schema_summary() -> None:
+ collection = pystac.Collection.from_file(
+ TestCases.get_path(
+ "data-files/examples/1.0.0/collection-only/collection-with-schemas.json"
)
- d = collection.to_dict(include_self_link=False)
- Collection.from_dict(d)
-
-
-class ExtentTest(unittest.TestCase):
- def setUp(self) -> None:
- self.maxDiff = None
-
- def test_temporal_extent_init_typing(self) -> None:
- # This test exists purely to test the typing of the intervals argument to
- # TemporalExtent
- start_datetime = str_to_datetime("2022-01-01T00:00:00Z")
- end_datetime = str_to_datetime("2022-01-31T23:59:59Z")
-
- _ = TemporalExtent([[start_datetime, end_datetime]])
-
- @pytest.mark.block_network()
- def test_temporal_extent_allows_single_interval(self) -> None:
- start_datetime = str_to_datetime("2022-01-01T00:00:00Z")
- end_datetime = str_to_datetime("2022-01-31T23:59:59Z")
-
- interval = [start_datetime, end_datetime]
- temporal_extent = TemporalExtent(intervals=interval) # type: ignore
-
- self.assertEqual(temporal_extent.intervals, [interval])
-
- @pytest.mark.block_network()
- def test_temporal_extent_allows_single_interval_open_start(self) -> None:
- end_datetime = str_to_datetime("2022-01-31T23:59:59Z")
-
- interval = [None, end_datetime]
- temporal_extent = TemporalExtent(intervals=interval)
-
- self.assertEqual(temporal_extent.intervals, [interval])
-
- @pytest.mark.block_network()
- def test_temporal_extent_non_list_intervals_fails(self) -> None:
- with pytest.raises(TypeError):
- # Pass in non-list intervals
- _ = TemporalExtent(intervals=1) # type: ignore
-
- @pytest.mark.block_network()
- def test_spatial_allows_single_bbox(self) -> None:
- temporal_extent = TemporalExtent(intervals=[[TEST_DATETIME, None]])
-
- # Pass in a single BBOX
- spatial_extent = SpatialExtent(bboxes=ARBITRARY_BBOX)
+ )
+ instruments_schema = get_required(
+ collection.summaries.get_schema("instruments"),
+ collection.summaries,
+ "instruments",
+ )
- collection_extent = Extent(spatial=spatial_extent, temporal=temporal_extent)
+ assert isinstance(instruments_schema, dict)
- collection = Collection(
- id="test", description="test desc", extent=collection_extent
- )
- # HREF required by validation
- collection.set_self_href("https://example.com/collection.json")
+def test_from_invalid_dict_raises_exception() -> None:
+ stac_io = pystac.StacIO.default()
+ catalog_dict = stac_io.read_json(
+ TestCases.get_path("data-files/catalogs/test-case-1/catalog.json")
+ )
+ with pytest.raises(pystac.STACTypeError):
+ _ = pystac.Collection.from_dict(catalog_dict)
- collection.validate()
- @pytest.mark.block_network()
- def test_spatial_extent_non_list_bboxes_fails(self) -> None:
- with pytest.raises(TypeError):
- # Pass in non-list bboxes
- _ = SpatialExtent(bboxes=1) # type: ignore
+def test_clone_preserves_assets() -> None:
+ path = TestCases.get_path("data-files/collections/with-assets.json")
+ original_collection = Collection.from_file(path)
+ assert len(original_collection.assets) > 0
+ assert all(
+ asset.owner is original_collection
+ for asset in original_collection.assets.values()
+ )
- def test_from_items(self) -> None:
- item1 = Item(
- id="test-item-1",
- geometry=ARBITRARY_GEOM,
- bbox=[-10, -20, 0, -10],
- datetime=datetime(2000, 2, 1, 12, 0, 0, 0, tzinfo=tz.UTC),
- properties={},
- )
+ cloned_collection = original_collection.clone()
- item2 = Item(
- id="test-item-2",
- geometry=ARBITRARY_GEOM,
- bbox=[0, -9, 10, 1],
- datetime=None,
- properties={
- "start_datetime": datetime_to_str(
- datetime(2000, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC)
- ),
- "end_datetime": datetime_to_str(
- datetime(2000, 7, 1, 12, 0, 0, 0, tzinfo=tz.UTC)
- ),
- },
- )
+ for key in original_collection.assets:
+ assert key in cloned_collection.assets, f"Failed to Preserve {key} asset"
+ cloned_asset = cloned_collection.assets.get(key)
+ if cloned_asset is not None:
+ assert (
+ cloned_asset.owner is cloned_collection
+ ), f"Failed to set owner for {key}"
- item3 = Item(
- id="test-item-2",
- geometry=ARBITRARY_GEOM,
- bbox=[-5, -20, 5, 0],
- datetime=None,
- properties={
- "start_datetime": datetime_to_str(
- datetime(2000, 12, 1, 12, 0, 0, 0, tzinfo=tz.UTC)
- ),
- "end_datetime": datetime_to_str(
- datetime(2001, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC),
- ),
- },
- )
- extent = Extent.from_items([item1, item2, item3])
-
- self.assertEqual(len(extent.spatial.bboxes), 1)
- self.assertEqual(extent.spatial.bboxes[0], [-10, -20, 10, 1])
-
- self.assertEqual(len(extent.temporal.intervals), 1)
- interval = extent.temporal.intervals[0]
-
- self.assertEqual(interval[0], datetime(2000, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC))
- self.assertEqual(interval[1], datetime(2001, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC))
-
- def test_to_from_dict(self) -> None:
- spatial_dict = {
- "bbox": [
- [
- 172.91173669923782,
- 1.3438851951615003,
- 172.95469614953714,
- 1.3690476620161975,
- ]
- ],
- "extension:field": "spatial value",
- }
- temporal_dict = {
- "interval": [
- ["2020-12-11T22:38:32.125000Z", "2020-12-14T18:02:31.437000Z"]
- ],
- "extension:field": "temporal value",
- }
- extent_dict = {
- "spatial": spatial_dict,
- "temporal": temporal_dict,
- "extension:field": "extent value",
- }
- expected_extent_extra_fields = {
- "extension:field": extent_dict["extension:field"],
- }
- expected_spatial_extra_fields = {
- "extension:field": spatial_dict["extension:field"],
- }
- expected_temporal_extra_fields = {
- "extension:field": temporal_dict["extension:field"],
- }
-
- extent = Extent.from_dict(extent_dict)
-
- self.assertDictEqual(expected_extent_extra_fields, extent.extra_fields)
- self.assertDictEqual(expected_spatial_extra_fields, extent.spatial.extra_fields)
- self.assertDictEqual(
- expected_temporal_extra_fields, extent.temporal.extra_fields
- )
+def test_to_dict_no_self_href() -> None:
+ temporal_extent = TemporalExtent(intervals=[[TEST_DATETIME, None]])
+ spatial_extent = SpatialExtent(bboxes=ARBITRARY_BBOX)
+ extent = Extent(spatial=spatial_extent, temporal=temporal_extent)
+ collection = Collection(id="an-id", description="A test Collection", extent=extent)
+ d = collection.to_dict(include_self_link=False)
+ Collection.from_dict(d)
+
+
+def test_temporal_extent_init_typing() -> None:
+ # This test exists purely to test the typing of the intervals argument to
+ # TemporalExtent
+ start_datetime = str_to_datetime("2022-01-01T00:00:00Z")
+ end_datetime = str_to_datetime("2022-01-31T23:59:59Z")
+
+ _ = TemporalExtent([[start_datetime, end_datetime]])
+
+
+ at pytest.mark.block_network()
+def test_temporal_extent_allows_single_interval() -> None:
+ start_datetime = str_to_datetime("2022-01-01T00:00:00Z")
+ end_datetime = str_to_datetime("2022-01-31T23:59:59Z")
+
+ interval = [start_datetime, end_datetime]
+ temporal_extent = TemporalExtent(intervals=interval) # type: ignore
+
+ assert temporal_extent.intervals == [interval]
+
+
+ at pytest.mark.block_network()
+def test_temporal_extent_allows_single_interval_open_start() -> None:
+ end_datetime = str_to_datetime("2022-01-31T23:59:59Z")
+
+ interval = [None, end_datetime]
+ temporal_extent = TemporalExtent(intervals=interval)
+
+ assert temporal_extent.intervals == [interval]
+
+
+ at pytest.mark.block_network()
+def test_temporal_extent_non_list_intervals_fails() -> None:
+ with pytest.raises(TypeError):
+ # Pass in non-list intervals
+ _ = TemporalExtent(intervals=1) # type: ignore
+
+
+ at pytest.mark.block_network()
+def test_spatial_allows_single_bbox() -> None:
+ temporal_extent = TemporalExtent(intervals=[[TEST_DATETIME, None]])
+
+ # Pass in a single BBOX
+ spatial_extent = SpatialExtent(bboxes=ARBITRARY_BBOX)
+
+ collection_extent = Extent(spatial=spatial_extent, temporal=temporal_extent)
+
+ collection = Collection(
+ id="test", description="test desc", extent=collection_extent
+ )
+
+ # HREF required by validation
+ collection.set_self_href("https://example.com/collection.json")
+
+ collection.validate()
+
+
+ at pytest.mark.block_network()
+def test_spatial_extent_non_list_bboxes_fails() -> None:
+ with pytest.raises(TypeError):
+ # Pass in non-list bboxes
+ _ = SpatialExtent(bboxes=1) # type: ignore
+
+
+def test_extent_from_items() -> None:
+ item1 = Item(
+ id="test-item-1",
+ geometry=ARBITRARY_GEOM,
+ bbox=[-10, -20, 0, -10],
+ datetime=datetime(2000, 2, 1, 12, 0, 0, 0, tzinfo=tz.UTC),
+ properties={},
+ )
+
+ item2 = Item(
+ id="test-item-2",
+ geometry=ARBITRARY_GEOM,
+ bbox=[0, -9, 10, 1],
+ datetime=None,
+ properties={
+ "start_datetime": datetime_to_str(
+ datetime(2000, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC)
+ ),
+ "end_datetime": datetime_to_str(
+ datetime(2000, 7, 1, 12, 0, 0, 0, tzinfo=tz.UTC)
+ ),
+ },
+ )
- self.assertDictEqual(extent_dict, extent.to_dict())
+ item3 = Item(
+ id="test-item-2",
+ geometry=ARBITRARY_GEOM,
+ bbox=[-5, -20, 5, 0],
+ datetime=None,
+ properties={
+ "start_datetime": datetime_to_str(
+ datetime(2000, 12, 1, 12, 0, 0, 0, tzinfo=tz.UTC)
+ ),
+ "end_datetime": datetime_to_str(
+ datetime(2001, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC),
+ ),
+ },
+ )
+ extent = Extent.from_items([item1, item2, item3])
+ assert len(extent.spatial.bboxes) == 1
+ assert extent.spatial.bboxes[0] == [-10, -20, 10, 1]
+ assert len(extent.temporal.intervals) == 1
-class CollectionSubClassTest(unittest.TestCase):
+ interval = extent.temporal.intervals[0]
+ assert interval[0] == datetime(2000, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC)
+ assert interval[1] == datetime(2001, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC)
+
+
+def test_extent_to_from_dict() -> None:
+ spatial_dict = {
+ "bbox": [
+ [
+ 172.91173669923782,
+ 1.3438851951615003,
+ 172.95469614953714,
+ 1.3690476620161975,
+ ]
+ ],
+ "extension:field": "spatial value",
+ }
+ temporal_dict = {
+ "interval": [["2020-12-11T22:38:32.125000Z", "2020-12-14T18:02:31.437000Z"]],
+ "extension:field": "temporal value",
+ }
+ extent_dict = {
+ "spatial": spatial_dict,
+ "temporal": temporal_dict,
+ "extension:field": "extent value",
+ }
+ expected_extent_extra_fields = {
+ "extension:field": extent_dict["extension:field"],
+ }
+ expected_spatial_extra_fields = {
+ "extension:field": spatial_dict["extension:field"],
+ }
+ expected_temporal_extra_fields = {
+ "extension:field": temporal_dict["extension:field"],
+ }
+
+ extent = Extent.from_dict(extent_dict)
+
+ assert expected_extent_extra_fields == extent.extra_fields
+ assert expected_spatial_extra_fields == extent.spatial.extra_fields
+ assert expected_temporal_extra_fields == extent.temporal.extra_fields
+
+ assert extent_dict == extent.to_dict()
+
+
+class TestCollectionSubClass:
"""This tests cases related to creating classes inheriting from pystac.Catalog to
ensure that inheritance, class methods, etc. function as expected."""
@@ -537,25 +549,23 @@ class CollectionSubClassTest(unittest.TestCase):
# backwards compatibility of inherited classes
return super().get_items()
- def setUp(self) -> None:
- self.stac_io = pystac.StacIO.default()
-
def test_from_dict_returns_subclass(self) -> None:
- collection_dict = self.stac_io.read_json(self.MULTI_EXTENT)
+ stac_io = pystac.StacIO.default()
+ collection_dict = stac_io.read_json(self.MULTI_EXTENT)
custom_collection = self.BasicCustomCollection.from_dict(collection_dict)
- self.assertIsInstance(custom_collection, self.BasicCustomCollection)
+ assert isinstance(custom_collection, self.BasicCustomCollection)
def test_from_file_returns_subclass(self) -> None:
custom_collection = self.BasicCustomCollection.from_file(self.MULTI_EXTENT)
- self.assertIsInstance(custom_collection, self.BasicCustomCollection)
+ assert isinstance(custom_collection, self.BasicCustomCollection)
def test_clone(self) -> None:
custom_collection = self.BasicCustomCollection.from_file(self.MULTI_EXTENT)
cloned_collection = custom_collection.clone()
- self.assertIsInstance(cloned_collection, self.BasicCustomCollection)
+ assert isinstance(cloned_collection, self.BasicCustomCollection)
def test_collection_get_item_works(self) -> None:
path = TestCases.get_path(
@@ -567,7 +577,7 @@ class CollectionSubClassTest(unittest.TestCase):
collection.get_item("area-1-1-imagery")
-class CollectionPartialSubClassTest(unittest.TestCase):
+def test_collection_get_item_raises_type_error() -> None:
class BasicCustomCollection(pystac.Collection):
def get_items( # type: ignore
self, *, recursive: bool = False
@@ -575,14 +585,13 @@ class CollectionPartialSubClassTest(unittest.TestCase):
# This get_items does not allow ids as args.
return super().get_items(recursive=recursive)
- def test_collection_get_item_raises_type_error(self) -> None:
- path = TestCases.get_path(
- "data-files/catalogs/test-case-1/country-1/area-1-1/collection.json"
- )
- custom_collection = self.BasicCustomCollection.from_file(path)
- collection = custom_collection.clone()
- with pytest.raises(TypeError, match="takes 1 positional argument"):
- collection.get_item("area-1-1-imagery")
+ path = TestCases.get_path(
+ "data-files/catalogs/test-case-1/country-1/area-1-1/collection.json"
+ )
+ custom_collection = BasicCustomCollection.from_file(path)
+ collection = custom_collection.clone()
+ with pytest.raises(TypeError, match="takes 1 positional argument"):
+ collection.get_item("area-1-1-imagery")
def test_custom_collection_from_dict(collection: Collection) -> None:
=====================================
tests/test_item.py
=====================================
@@ -5,10 +5,9 @@ import json
import os
import pickle
import tempfile
-import unittest
from copy import deepcopy
from pathlib import Path
-from typing import Any
+from typing import Any, cast
import dateutil.relativedelta
import pytest
@@ -27,343 +26,321 @@ from pystac.validation import validate_dict
from tests.utils import TestCases, assert_to_from_dict
-class ItemTest(unittest.TestCase):
- def get_example_item_dict(self) -> dict[str, Any]:
- m = TestCases.get_path("data-files/item/sample-item.json")
- with open(m) as f:
- item_dict: dict[str, Any] = json.load(f)
- return item_dict
+def test_to_from_dict(sample_item_dict: dict[str, Any]) -> None:
+ param_dict = deepcopy(sample_item_dict)
- def test_to_from_dict(self) -> None:
- self.maxDiff = None
+ assert_to_from_dict(Item, param_dict)
+ item = Item.from_dict(param_dict)
+ assert item.id == "CS3-20160503_132131_05"
- item_dict = self.get_example_item_dict()
- param_dict = deepcopy(item_dict)
+ # test asset creation additional field(s)
+ assert (
+ item.assets["analytic"].extra_fields["product"]
+ == "http://cool-sat.com/catalog/products/analytic.json"
+ )
+ assert len(item.assets["thumbnail"].extra_fields) == 0
+
+ # test that the parameter is preserved
+ assert param_dict == sample_item_dict
+
+ # assert that the parameter is preserved regardless of preserve_dict
+ Item.from_dict(param_dict, preserve_dict=False)
+ assert param_dict == sample_item_dict
+
+
+def test_from_dict_set_root(sample_item_dict: dict[str, Any]) -> None:
+ catalog = pystac.Catalog(id="test", description="test desc")
+ item = Item.from_dict(sample_item_dict, root=catalog)
+ assert item.get_root() is catalog
+
+
+def test_set_self_href_does_not_break_asset_hrefs() -> None:
+ cat = TestCases.case_2()
+ for item in cat.get_items(recursive=True):
+ for asset in item.assets.values():
+ if is_absolute_href(asset.href):
+ asset.href = f"./{os.path.basename(asset.href)}"
+ item.set_self_href("http://example.com/item.json")
+ for asset in item.assets.values():
+ assert is_absolute_href(asset.href)
+
+
+def test_set_self_href_none_ignores_relative_asset_hrefs() -> None:
+ cat = TestCases.case_2()
+ for item in cat.get_items(recursive=True):
+ for asset in item.assets.values():
+ if is_absolute_href(asset.href):
+ asset.href = f"./{os.path.basename(asset.href)}"
+ item.set_self_href(None)
+ for asset in item.assets.values():
+ assert not is_absolute_href(asset.href)
+
+
+def test_asset_absolute_href(sample_item: Item) -> None:
+ item_path = TestCases.get_path("data-files/item/sample-item.json")
+ sample_item.set_self_href(item_path)
+ rel_asset = Asset("./data.geojson")
+ rel_asset.set_owner(sample_item)
+ expected_href = make_posix_style(
+ os.path.abspath(os.path.join(os.path.dirname(item_path), "./data.geojson"))
+ )
+ actual_href = rel_asset.get_absolute_href()
+ assert expected_href == actual_href
- assert_to_from_dict(Item, param_dict)
- item = Item.from_dict(param_dict)
- self.assertEqual(item.id, "CS3-20160503_132131_05")
- # test asset creation additional field(s)
- self.assertEqual(
- item.assets["analytic"].extra_fields["product"],
- "http://cool-sat.com/catalog/products/analytic.json",
- )
- self.assertEqual(len(item.assets["thumbnail"].extra_fields), 0)
-
- # test that the parameter is preserved
- self.assertEqual(param_dict, item_dict)
-
- # assert that the parameter is preserved regardless of
- # preserve_dict
- _ = Item.from_dict(param_dict, preserve_dict=False)
- self.assertEqual(param_dict, item_dict)
-
- def test_from_dict_set_root(self) -> None:
- item_dict = self.get_example_item_dict()
- catalog = pystac.Catalog(id="test", description="test desc")
- item = Item.from_dict(item_dict, root=catalog)
- self.assertIs(item.get_root(), catalog)
-
- def test_set_self_href_does_not_break_asset_hrefs(self) -> None:
- cat = TestCases.case_2()
- for item in cat.get_items(recursive=True):
- for asset in item.assets.values():
- if is_absolute_href(asset.href):
- asset.href = f"./{os.path.basename(asset.href)}"
- item.set_self_href("http://example.com/item.json")
- for asset in item.assets.values():
- self.assertTrue(is_absolute_href(asset.href))
-
- def test_set_self_href_none_ignores_relative_asset_hrefs(self) -> None:
- cat = TestCases.case_2()
- for item in cat.get_items(recursive=True):
- for asset in item.assets.values():
- if is_absolute_href(asset.href):
- asset.href = f"./{os.path.basename(asset.href)}"
- item.set_self_href(None)
- for asset in item.assets.values():
- self.assertFalse(is_absolute_href(asset.href))
-
- def test_asset_absolute_href(self) -> None:
- item_path = TestCases.get_path("data-files/item/sample-item.json")
- item_dict = self.get_example_item_dict()
- item = Item.from_dict(item_dict)
- item.set_self_href(item_path)
- rel_asset = Asset("./data.geojson")
- rel_asset.set_owner(item)
- expected_href = make_posix_style(
- os.path.abspath(os.path.join(os.path.dirname(item_path), "./data.geojson"))
- )
- actual_href = rel_asset.get_absolute_href()
- self.assertEqual(expected_href, actual_href)
-
- def test_asset_absolute_href_no_item_self(self) -> None:
- item_dict = self.get_example_item_dict()
- item = Item.from_dict(item_dict)
- assert item.get_self_href() is None
-
- rel_asset = Asset("./data.geojson")
- rel_asset.set_owner(item)
- actual_href = rel_asset.get_absolute_href()
- self.assertEqual(None, actual_href)
-
- def test_item_field_order(self) -> None:
- item = pystac.Item.from_file(
- TestCases.get_path("data-files/item/sample-item.json")
- )
- item_dict = item.to_dict(include_self_link=False)
- expected_order = [
- "type",
- "stac_version",
- "stac_extensions",
- "id",
- "geometry",
- "bbox",
- "properties",
- "links",
- "assets",
- "collection",
- ]
- actual_order = list(item_dict.keys())
- self.assertEqual(
- actual_order,
- expected_order,
- f"Order was {actual_order}, expected {expected_order}",
- )
+def test_asset_absolute_href_no_item_self(sample_item_dict: dict[str, Any]) -> None:
+ item = Item.from_dict(sample_item_dict)
+ assert item.get_self_href() is None
- def test_extra_fields(self) -> None:
- item = pystac.Item.from_file(
- TestCases.get_path("data-files/item/sample-item.json")
- )
+ rel_asset = Asset("./data.geojson")
+ rel_asset.set_owner(item)
+ actual_href = rel_asset.get_absolute_href()
+ assert actual_href is None
- item.extra_fields["test"] = "extra"
-
- with tempfile.TemporaryDirectory() as tmp_dir:
- p = os.path.join(tmp_dir, "item.json")
- item.save_object(include_self_link=False, dest_href=p)
- with open(p) as f:
- item_json = json.load(f)
- self.assertTrue("test" in item_json)
- self.assertEqual(item_json["test"], "extra")
-
- read_item = pystac.Item.from_file(p)
- self.assertTrue("test" in read_item.extra_fields)
- self.assertEqual(read_item.extra_fields["test"], "extra")
-
- def test_clearing_collection(self) -> None:
- collection = TestCases.case_4().get_child("acc")
- assert isinstance(collection, pystac.Collection)
- item = next(collection.get_items(recursive=True))
- self.assertEqual(item.collection_id, collection.id)
- item.set_collection(None)
- self.assertIsNone(item.collection_id)
- self.assertIsNone(item.get_collection())
- item.set_collection(collection)
- self.assertEqual(item.collection_id, collection.id)
- self.assertIs(item.get_collection(), collection)
-
- def test_datetime_ISO8601_format(self) -> None:
- item_dict = self.get_example_item_dict()
-
- item = Item.from_dict(item_dict)
-
- formatted_time = item.to_dict()["properties"]["datetime"]
-
- self.assertEqual("2016-05-03T13:22:30.040000Z", formatted_time)
-
- @pytest.mark.vcr()
- def test_null_datetime(self) -> None:
- item = pystac.Item.from_file(
- TestCases.get_path("data-files/item/sample-item.json")
- )
- with self.assertRaises(pystac.STACError):
- Item(
- "test",
- geometry=item.geometry,
- bbox=item.bbox,
- datetime=None,
- properties={},
- )
+def test_item_field_order() -> None:
+ item = pystac.Item.from_file(TestCases.get_path("data-files/item/sample-item.json"))
+ item_dict = item.to_dict(include_self_link=False)
+ expected_order = [
+ "type",
+ "stac_version",
+ "stac_extensions",
+ "id",
+ "geometry",
+ "bbox",
+ "properties",
+ "links",
+ "assets",
+ "collection",
+ ]
+ actual_order = list(item_dict.keys())
+ assert actual_order == expected_order
+
- null_dt_item = Item(
+def test_extra_fields() -> None:
+ item = pystac.Item.from_file(TestCases.get_path("data-files/item/sample-item.json"))
+
+ item.extra_fields["test"] = "extra"
+
+ with tempfile.TemporaryDirectory() as tmp_dir:
+ p = os.path.join(tmp_dir, "item.json")
+ item.save_object(include_self_link=False, dest_href=p)
+ with open(p) as f:
+ item_json = json.load(f)
+ assert "test" in item_json
+ assert item_json["test"] == "extra"
+
+ read_item = pystac.Item.from_file(p)
+ assert "test" in read_item.extra_fields
+ assert read_item.extra_fields["test"] == "extra"
+
+
+def test_clearing_collection() -> None:
+ collection = TestCases.case_4().get_child("acc")
+ assert isinstance(collection, pystac.Collection)
+ item = next(collection.get_items(recursive=True))
+ assert item.collection_id == collection.id
+ item.set_collection(None)
+ assert item.collection_id is None
+ assert item.get_collection() is None
+ item.set_collection(collection)
+ assert item.collection_id == collection.id
+ assert item.get_collection() is collection
+
+
+def test_datetime_ISO8601_format(sample_item: Item) -> None:
+ formatted_time = sample_item.to_dict()["properties"]["datetime"]
+ assert "2016-05-03T13:22:30.040000Z" == formatted_time
+
+
+ at pytest.mark.vcr()
+def test_null_datetime() -> None:
+ item = pystac.Item.from_file(TestCases.get_path("data-files/item/sample-item.json"))
+
+ with pytest.raises(pystac.STACError):
+ Item(
"test",
geometry=item.geometry,
bbox=item.bbox,
datetime=None,
- properties={
- "start_datetime": datetime_to_str(get_opt(item.datetime)),
- "end_datetime": datetime_to_str(get_opt(item.datetime)),
- },
+ properties={},
)
- null_dt_item.validate()
+ null_dt_item = Item(
+ "test",
+ geometry=item.geometry,
+ bbox=item.bbox,
+ datetime=None,
+ properties={
+ "start_datetime": datetime_to_str(get_opt(item.datetime)),
+ "end_datetime": datetime_to_str(get_opt(item.datetime)),
+ },
+ )
+
+ null_dt_item.validate()
- def test_get_assets(self) -> None:
- item = pystac.Item.from_file(
- TestCases.get_path("data-files/item/sample-item.json")
- )
- media_type_filter = item.get_assets(media_type=pystac.MediaType.COG)
- self.assertCountEqual(media_type_filter.keys(), ["analytic"])
- role_filter = item.get_assets(role="data")
- self.assertCountEqual(role_filter.keys(), ["analytic"])
- multi_filter = item.get_assets(
- media_type=pystac.MediaType.PNG, role="thumbnail"
- )
- self.assertCountEqual(multi_filter.keys(), ["thumbnail"])
- multi_filter["thumbnail"].description = "foo"
- assert item.assets["thumbnail"].description != "foo"
-
- no_filter = item.get_assets()
- self.assertCountEqual(no_filter.keys(), ["analytic", "thumbnail"])
- no_assets = item.get_assets(media_type=pystac.MediaType.HDF)
- self.assertEqual(no_assets, {})
-
- @pytest.mark.vcr()
- def test_null_datetime_constructor(self) -> None:
- item = pystac.Item.from_file(
- TestCases.get_path("data-files/item/sample-item.json")
+def test_get_assets() -> None:
+ item = pystac.Item.from_file(TestCases.get_path("data-files/item/sample-item.json"))
+
+ media_type_filter = item.get_assets(media_type=pystac.MediaType.COG)
+ assert set(media_type_filter.keys()) == {"analytic"}
+ role_filter = item.get_assets(role="data")
+ assert set(role_filter.keys()) == {"analytic"}
+ multi_filter = item.get_assets(media_type=pystac.MediaType.PNG, role="thumbnail")
+ assert set(multi_filter.keys()) == {"thumbnail"}
+ multi_filter["thumbnail"].description = "foo"
+ assert item.assets["thumbnail"].description != "foo"
+
+ no_filter = item.get_assets()
+ assert set(no_filter.keys()) == {"analytic", "thumbnail"}
+ no_assets = item.get_assets(media_type=pystac.MediaType.HDF)
+ assert no_assets == {}
+
+
+ at pytest.mark.vcr()
+def test_null_datetime_constructor() -> None:
+ item = pystac.Item.from_file(TestCases.get_path("data-files/item/sample-item.json"))
+ with pytest.raises(pystac.STACError):
+ Item(
+ "test",
+ geometry=item.geometry,
+ bbox=item.bbox,
+ datetime=None,
+ end_datetime=item.datetime,
+ properties={},
)
- with self.assertRaises(pystac.STACError):
- Item(
- "test",
- geometry=item.geometry,
- bbox=item.bbox,
- datetime=None,
- end_datetime=item.datetime,
- properties={},
- )
- with self.assertRaises(pystac.STACError):
- Item(
- "test",
- geometry=item.geometry,
- bbox=item.bbox,
- datetime=None,
- start_datetime=item.datetime,
- properties={},
- )
- assert item.datetime
- null_dt_item = Item(
+ with pytest.raises(pystac.STACError):
+ Item(
"test",
geometry=item.geometry,
bbox=item.bbox,
datetime=None,
start_datetime=item.datetime,
- end_datetime=item.datetime + dateutil.relativedelta.relativedelta(days=1),
properties={},
)
- null_dt_item.validate()
+ assert item.datetime
+ null_dt_item = Item(
+ "test",
+ geometry=item.geometry,
+ bbox=item.bbox,
+ datetime=None,
+ start_datetime=item.datetime,
+ end_datetime=item.datetime + dateutil.relativedelta.relativedelta(days=1),
+ properties={},
+ )
+ null_dt_item.validate()
- def test_get_set_asset_datetime(self) -> None:
- item = pystac.Item.from_file(
- TestCases.get_path("data-files/item/sample-item-asset-properties.json")
- )
- item_datetime = item.datetime
- # No property on asset
- self.assertEqual(item.get_datetime(item.assets["thumbnail"]), item.datetime)
+def test_get_set_asset_datetime() -> None:
+ item = pystac.Item.from_file(
+ TestCases.get_path("data-files/item/sample-item-asset-properties.json")
+ )
+ item_datetime = item.datetime
- # Property on asset
- self.assertNotEqual(item.get_datetime(item.assets["analytic"]), item.datetime)
- self.assertEqual(
- item.get_datetime(item.assets["analytic"]),
- str_to_datetime("2017-05-03T13:22:30.040Z"),
- )
+ # No property on asset
+ assert item.get_datetime(item.assets["thumbnail"]) == item.datetime
- item.set_datetime(
- str_to_datetime("2018-05-03T13:22:30.040Z"), item.assets["thumbnail"]
- )
- self.assertEqual(item.get_datetime(), item_datetime)
- self.assertEqual(
- item.get_datetime(item.assets["thumbnail"]),
- str_to_datetime("2018-05-03T13:22:30.040Z"),
- )
+ # Property on asset
+ assert item.get_datetime(item.assets["analytic"]) != item.datetime
+ assert item.get_datetime(item.assets["analytic"]) == str_to_datetime(
+ "2017-05-03T13:22:30.040Z"
+ )
- def test_read_eo_item_owns_asset(self) -> None:
- item = next(TestCases.case_1().get_items(recursive=True))
- assert len(item.assets) > 0
- for asset_key in item.assets:
- self.assertEqual(item.assets[asset_key].owner, item)
+ item.set_datetime(
+ str_to_datetime("2018-05-03T13:22:30.040Z"), item.assets["thumbnail"]
+ )
+ assert item.get_datetime() == item_datetime
+ assert item.get_datetime(item.assets["thumbnail"]) == str_to_datetime(
+ "2018-05-03T13:22:30.040Z"
+ )
- @pytest.mark.vcr()
- def test_null_geometry(self) -> None:
- m = TestCases.get_path(
- "data-files/examples/1.0.0-beta.2/item-spec/examples/null-geom-item.json"
- )
- with open(m) as f:
- item_dict = json.load(f)
- validate_dict(item_dict, pystac.STACObjectType.ITEM)
+def test_read_eo_item_owns_asset() -> None:
+ item = next(TestCases.case_1().get_items(recursive=True))
+ assert len(item.assets) > 0
+ for asset_key in item.assets:
+ assert item.assets[asset_key].owner == item
- item = Item.from_dict(item_dict)
- self.assertIsInstance(item, Item)
- item.validate()
- item_dict = item.to_dict()
- self.assertIsNone(item_dict["geometry"])
- self.assertNotIn("bbox", item_dict)
+ at pytest.mark.vcr()
+def test_null_geometry() -> None:
+ m = TestCases.get_path(
+ "data-files/examples/1.0.0-beta.2/item-spec/examples/null-geom-item.json"
+ )
+ with open(m) as f:
+ item_dict = json.load(f)
- def test_0_9_item_with_no_extensions_does_not_read_collection_data(self) -> None:
- item_json = pystac.StacIO.default().read_json(
- TestCases.get_path("data-files/examples/hand-0.9.0/010100/010100.json")
- )
- assert item_json.get("stac_extensions") is None
- assert item_json.get("stac_version") == "0.9.0"
+ validate_dict(item_dict, pystac.STACObjectType.ITEM)
- did_merge = pystac.serialization.common_properties.merge_common_properties(
- item_json
- )
- self.assertFalse(did_merge)
-
- def test_clone_preserves_assets(self) -> None:
- cat = TestCases.case_2()
- original_item = next(cat.get_items(recursive=True))
- assert len(original_item.assets) > 0
- assert all(
- asset.owner is original_item for asset in original_item.assets.values()
- )
+ item = Item.from_dict(item_dict)
+ assert isinstance(item, Item)
+ item.validate()
- cloned_item = original_item.clone()
+ item_dict = item.to_dict()
+ assert item_dict["geometry"] is None
+ assert "bbox" not in item_dict
- for key in original_item.assets:
- with self.subTest(f"Preserves {key} asset"):
- self.assertIn(key, cloned_item.assets)
- cloned_asset = cloned_item.assets.get(key)
- if cloned_asset is not None:
- with self.subTest(f"Sets owner for {key}"):
- self.assertIs(cloned_asset.owner, cloned_item)
- def test_make_asset_href_relative_is_noop_on_relative_hrefs(self) -> None:
- cat = TestCases.case_2()
- item = next(cat.get_items(recursive=True))
- asset = list(item.assets.values())[0]
- assert not is_absolute_href(asset.href)
- original_href = asset.get_absolute_href()
+def test_0_9_item_with_no_extensions_does_not_read_collection_data() -> None:
+ item_json = pystac.StacIO.default().read_json(
+ TestCases.get_path("data-files/examples/hand-0.9.0/010100/010100.json")
+ )
+ assert item_json.get("stac_extensions") is None
+ assert item_json.get("stac_version") == "0.9.0"
- item.make_asset_hrefs_relative()
- self.assertEqual(asset.get_absolute_href(), original_href)
+ did_merge = pystac.serialization.common_properties.merge_common_properties(
+ item_json
+ )
+ assert not did_merge
- def test_from_invalid_dict_raises_exception(self) -> None:
- stac_io = pystac.StacIO.default()
- catalog_dict = stac_io.read_json(
- TestCases.get_path("data-files/catalogs/test-case-1/catalog.json")
- )
- with self.assertRaises(pystac.STACTypeError):
- _ = pystac.Item.from_dict(catalog_dict)
-
- @pytest.mark.vcr()
- def test_relative_extension_path(self) -> None:
- item = pystac.Item.from_file(
- TestCases.get_path(
- "data-files/item/sample-item-with-relative-extension-path.json"
- )
+
+def test_clone_preserves_assets() -> None:
+ cat = TestCases.case_2()
+ original_item = next(cat.get_items(recursive=True))
+ assert len(original_item.assets) > 0
+ assert all(asset.owner is original_item for asset in original_item.assets.values())
+
+ cloned_item = original_item.clone()
+
+ for key in original_item.assets:
+ assert key in cloned_item.assets, f"Failed to preserve asset {key}"
+ cloned_asset = cloned_item.assets.get(key)
+ if cloned_asset is not None:
+ assert cloned_asset.owner is cloned_item, f"Failed set owner for {key}"
+
+
+def test_make_asset_href_relative_is_noop_on_relative_hrefs() -> None:
+ cat = TestCases.case_2()
+ item = next(cat.get_items(recursive=True))
+ asset = list(item.assets.values())[0]
+ assert not is_absolute_href(asset.href)
+ original_href = asset.get_absolute_href()
+
+ item.make_asset_hrefs_relative()
+ assert asset.get_absolute_href() == original_href
+
+
+def test_from_invalid_dict_raises_exception() -> None:
+ stac_io = pystac.StacIO.default()
+ catalog_dict = stac_io.read_json(
+ TestCases.get_path("data-files/catalogs/test-case-1/catalog.json")
+ )
+ with pytest.raises(pystac.STACTypeError):
+ _ = pystac.Item.from_dict(catalog_dict)
+
+
+ at pytest.mark.vcr()
+def test_relative_extension_path() -> None:
+ item = pystac.Item.from_file(
+ TestCases.get_path(
+ "data-files/item/sample-item-with-relative-extension-path.json"
)
- item.validate()
+ )
+ item.validate()
-class ItemSubClassTest(unittest.TestCase):
+class TestItemSubClass:
"""This tests cases related to creating classes inheriting from pystac.Catalog to
ensure that inheritance, class methods, etc. function as expected."""
@@ -372,87 +349,66 @@ class ItemSubClassTest(unittest.TestCase):
class BasicCustomItem(pystac.Item):
pass
- def setUp(self) -> None:
- self.stac_io = pystac.StacIO.default()
-
def test_from_dict_returns_subclass(self) -> None:
- item_dict = self.stac_io.read_json(self.SAMPLE_ITEM)
+ stac_io = pystac.StacIO.default()
+ item_dict = stac_io.read_json(self.SAMPLE_ITEM)
custom_item = self.BasicCustomItem.from_dict(item_dict)
- self.assertIsInstance(custom_item, self.BasicCustomItem)
+ assert isinstance(custom_item, self.BasicCustomItem)
def test_from_file_returns_subclass(self) -> None:
custom_item = self.BasicCustomItem.from_file(self.SAMPLE_ITEM)
- self.assertIsInstance(custom_item, self.BasicCustomItem)
+ assert isinstance(custom_item, self.BasicCustomItem)
def test_clone(self) -> None:
custom_item = self.BasicCustomItem.from_file(self.SAMPLE_ITEM)
cloned_item = custom_item.clone()
- self.assertIsInstance(cloned_item, self.BasicCustomItem)
-
+ assert isinstance(cloned_item, self.BasicCustomItem)
-class AssetTest(unittest.TestCase):
- def setUp(self) -> None:
- self.maxDiff = None
- with open(TestCases.get_path("data-files/item/sample-item.json")) as src:
- item_dict = json.load(src)
- self.asset_dict = item_dict["assets"]["analytic"]
+def test_asset_clone() -> None:
+ with open(TestCases.get_path("data-files/item/sample-item.json")) as src:
+ item_dict = json.load(src)
+ asset_dict = item_dict["assets"]["analytic"]
+ original_asset = Asset.from_dict(asset_dict)
- def example_asset(self) -> Asset:
- return Asset.from_dict(self.asset_dict)
+ cloned_asset = original_asset.clone()
- def test_clone(self) -> None:
- original_asset = self.example_asset()
- cloned_asset = original_asset.clone()
+ assert original_asset.to_dict() == asset_dict
+ assert cloned_asset.to_dict() == asset_dict
- self.assertDictEqual(original_asset.to_dict(), self.asset_dict)
- self.assertDictEqual(cloned_asset.to_dict(), self.asset_dict)
+ # Changes to original asset should not affect cloned Asset
+ original_asset.description = "Some new description"
+ original_asset.href = "/path/to/new/href"
+ original_asset.title = "New Title"
+ original_asset.roles = ["new role"]
+ original_asset.roles.append("new role")
+ original_asset.extra_fields["new_field"] = "new_value"
+ assert cloned_asset.to_dict() == asset_dict
- # Changes to original asset should not affect cloned Asset
- original_asset.description = "Some new description"
- self.assertDictEqual(cloned_asset.to_dict(), self.asset_dict)
- original_asset.href = "/path/to/new/href"
- self.assertDictEqual(cloned_asset.to_dict(), self.asset_dict)
-
- original_asset.title = "New Title"
- self.assertDictEqual(cloned_asset.to_dict(), self.asset_dict)
-
- original_asset.roles = ["new role"]
- self.assertDictEqual(cloned_asset.to_dict(), self.asset_dict)
-
- original_asset.roles.append("new role")
- self.assertDictEqual(cloned_asset.to_dict(), self.asset_dict)
-
- original_asset.extra_fields["new_field"] = "new_value"
- self.assertDictEqual(cloned_asset.to_dict(), self.asset_dict)
-
-
-class AssetSubClassTest(unittest.TestCase):
+class TestAssetSubClass:
class CustomAsset(Asset):
pass
- def setUp(self) -> None:
- self.maxDiff = None
+ AssetDict = dict[str, str | list[str]]
+
+ @pytest.fixture
+ def asset_dict(self) -> AssetDict:
with open(TestCases.get_path("data-files/item/sample-item.json")) as src:
item_dict = json.load(src)
+ return cast(TestAssetSubClass.AssetDict, item_dict["assets"]["analytic"])
- self.asset_dict = item_dict["assets"]["analytic"]
-
- def test_from_dict(self) -> None:
- asset = self.CustomAsset.from_dict(self.asset_dict)
-
- self.assertIsInstance(asset, self.CustomAsset)
+ def test_from_dict(self, asset_dict: AssetDict) -> None:
+ asset = self.CustomAsset.from_dict(asset_dict)
+ assert isinstance(asset, self.CustomAsset)
- def test_clone(self) -> None:
- asset = self.CustomAsset.from_dict(self.asset_dict)
+ def test_clone(self, asset_dict: AssetDict) -> None:
+ asset = self.CustomAsset.from_dict(asset_dict)
cloned_asset = asset.clone()
-
- self.assertIsInstance(cloned_asset, self.CustomAsset)
- self.assertIsInstance(cloned_asset, self.CustomAsset)
+ assert isinstance(cloned_asset, self.CustomAsset)
def test_custom_item_from_dict(item: Item) -> None:
=====================================
tests/test_item_assets.py
=====================================
@@ -1,5 +1,3 @@
-import unittest
-
import pytest
from pystac import Collection
@@ -13,43 +11,18 @@ CLASSIFICATION_COLLECTION_RASTER_URI = TestCases.get_path(
)
-class TestItemAssets(unittest.TestCase):
- def setUp(self) -> None:
- self.maxDiff = None
- self.collection = Collection.from_file(
- TestCases.get_path("data-files/item-assets/example-landsat8.json")
- )
-
- def test_example(self) -> None:
- collection = self.collection.clone()
-
- self.assertEqual(len(collection.item_assets), 13)
-
- self.assertEqual(
- collection.item_assets["B1"],
- ItemAssetDefinition(
- {
- "type": "image/tiff; application=geotiff",
- "eo:bands": [
- {
- "name": "B1",
- "common_name": "coastal",
- "center_wavelength": 0.44,
- "full_width_half_max": 0.02,
- }
- ],
- "title": "Coastal Band (B1)",
- "description": "Coastal Band Top Of the Atmosphere",
- }
- ),
- )
+ at pytest.fixture
+def landsat8_collection() -> Collection:
+ return Collection.from_file(
+ TestCases.get_path("data-files/item-assets/example-landsat8.json")
+ )
- def test_set_using_dict(self) -> None:
- collection = self.collection.clone()
- self.assertEqual(len(collection.item_assets), 13)
+def test_example(landsat8_collection: Collection) -> None:
+ assert len(landsat8_collection.item_assets) == 13
- collection.item_assets["Bx"] = {
+ assert landsat8_collection.item_assets["B1"] == ItemAssetDefinition(
+ {
"type": "image/tiff; application=geotiff",
"eo:bands": [
{
@@ -61,20 +34,35 @@ class TestItemAssets(unittest.TestCase):
],
"title": "Coastal Band (B1)",
"description": "Coastal Band Top Of the Atmosphere",
- } # type:ignore
+ }
+ )
- self.assertEqual(collection.item_assets["B1"], collection.item_assets["Bx"])
+def test_set_using_dict(landsat8_collection: Collection) -> None:
+ assert len(landsat8_collection.item_assets) == 13
-class TestAssetDefinition(unittest.TestCase):
- def setUp(self) -> None:
- self.maxDiff = None
- self.collection = Collection.from_file(
- TestCases.get_path("data-files/item-assets/example-landsat8.json")
- )
+ landsat8_collection.item_assets["Bx"] = {
+ "type": "image/tiff; application=geotiff",
+ "eo:bands": [
+ {
+ "name": "B1",
+ "common_name": "coastal",
+ "center_wavelength": 0.44,
+ "full_width_half_max": 0.02,
+ }
+ ],
+ "title": "Coastal Band (B1)",
+ "description": "Coastal Band Top Of the Atmosphere",
+ } # type:ignore
+
+ assert (
+ landsat8_collection.item_assets["B1"] == landsat8_collection.item_assets["Bx"]
+ )
- def test_eq(self) -> None:
- assert self.collection.item_assets["B1"] != {"title": "Coastal Band (B1)"}
+
+class TestAssetDefinition:
+ def test_eq(self, landsat8_collection: Collection) -> None:
+ assert landsat8_collection.item_assets["B1"] != {"title": "Coastal Band (B1)"}
def test_create(self) -> None:
title = "Coastal Band (B1)"
@@ -84,10 +72,12 @@ class TestAssetDefinition(unittest.TestCase):
asset_defn = ItemAssetDefinition.create(
title=title, description=description, media_type=media_type, roles=roles
)
- self.assertEqual(asset_defn.title, title)
- self.assertEqual(asset_defn.description, description)
- self.assertEqual(asset_defn.media_type, media_type)
- self.assertEqual(asset_defn.roles, roles)
+ assert (
+ asset_defn.title,
+ asset_defn.description,
+ asset_defn.media_type,
+ asset_defn.roles,
+ ) == (title, description, media_type, roles)
def test_title(self) -> None:
asset_defn = ItemAssetDefinition({})
@@ -95,8 +85,7 @@ class TestAssetDefinition(unittest.TestCase):
asset_defn.title = title
- self.assertEqual(asset_defn.title, title)
- self.assertEqual(asset_defn.to_dict()["title"], title)
+ assert asset_defn.title == asset_defn.to_dict()["title"] == title
def test_description(self) -> None:
asset_defn = ItemAssetDefinition({})
@@ -104,8 +93,9 @@ class TestAssetDefinition(unittest.TestCase):
asset_defn.description = description
- self.assertEqual(asset_defn.description, description)
- self.assertEqual(asset_defn.to_dict()["description"], description)
+ assert (
+ asset_defn.description == asset_defn.to_dict()["description"] == description
+ )
def test_media_type(self) -> None:
asset_defn = ItemAssetDefinition({})
@@ -113,8 +103,7 @@ class TestAssetDefinition(unittest.TestCase):
asset_defn.media_type = media_type
- self.assertEqual(asset_defn.media_type, media_type)
- self.assertEqual(asset_defn.to_dict()["type"], media_type)
+ assert asset_defn.media_type == asset_defn.to_dict()["type"] == media_type
def test_roles(self) -> None:
asset_defn = ItemAssetDefinition({})
@@ -122,10 +111,9 @@ class TestAssetDefinition(unittest.TestCase):
asset_defn.roles = roles
- self.assertEqual(asset_defn.roles, roles)
- self.assertEqual(asset_defn.to_dict()["roles"], roles)
+ assert asset_defn.roles == asset_defn.to_dict()["roles"] == roles
- def test_set_owner(self) -> None:
+ def test_set_owner(self, landsat8_collection: Collection) -> None:
asset_definition = ItemAssetDefinition(
{
"type": "image/tiff; application=geotiff",
@@ -141,8 +129,8 @@ class TestAssetDefinition(unittest.TestCase):
"description": "Coastal Band Top Of the Atmosphere",
}
)
- asset_definition.set_owner(self.collection)
- assert asset_definition.owner == self.collection
+ asset_definition.set_owner(landsat8_collection)
+ assert asset_definition.owner == landsat8_collection
def test_extra_fields(collection: Collection) -> None:
=====================================
tests/test_item_collection.py
=====================================
@@ -1,186 +1,208 @@
import json
-import unittest
from copy import deepcopy
from os.path import relpath
+from typing import Any, cast
+
+import pytest
import pystac
+from pystac import Item, StacIO
from pystac.item_collection import ItemCollection
from tests.utils import TestCases
from tests.utils.stac_io_mock import MockDefaultStacIO
+SIMPLE_ITEM = TestCases.get_path("data-files/examples/1.0.0-RC1/simple-item.json")
+CORE_ITEM = TestCases.get_path("data-files/examples/1.0.0-RC1/core-item.json")
+EXTENDED_ITEM = TestCases.get_path("data-files/examples/1.0.0-RC1/extended-item.json")
+
+ITEM_COLLECTION = TestCases.get_path(
+ "data-files/item-collection/sample-item-collection.json"
+)
+
+
+ at pytest.fixture
+def item_collection_dict() -> dict[str, Any]:
+ with open(ITEM_COLLECTION) as src:
+ return cast(dict[str, Any], json.load(src))
+
+
+ at pytest.fixture
+def items(item_collection_dict: dict[str, Any]) -> list[Item]:
+ return [Item.from_dict(f) for f in item_collection_dict["features"]]
+
+
+ at pytest.fixture
+def stac_io() -> StacIO:
+ return StacIO.default()
+
+
+def test_item_collection_length(
+ item_collection_dict: dict[str, Any], items: list[Item]
+) -> None:
+ item_collection = pystac.ItemCollection(items=items)
+
+ assert len(item_collection) == len(items)
+
+
+def test_item_collection_iter(items: list[Item]) -> None:
+ expected_ids = [item.id for item in items]
+ item_collection = pystac.ItemCollection(items=items)
+
+ actual_ids = [item.id for item in item_collection]
+
+ assert expected_ids == actual_ids
+
+
+def test_item_collection_get_item_by_index(items: list[Item]) -> None:
+ expected_id = items[0].id
+ item_collection = pystac.ItemCollection(items=items)
+
+ assert item_collection[0].id == expected_id
+
+
+def test_item_collection_contains() -> None:
+ item = pystac.Item.from_file(SIMPLE_ITEM)
+ item_collection = pystac.ItemCollection(items=[item], clone_items=False)
-class TestItemCollection(unittest.TestCase):
- SIMPLE_ITEM = TestCases.get_path("data-files/examples/1.0.0-RC1/simple-item.json")
- CORE_ITEM = TestCases.get_path("data-files/examples/1.0.0-RC1/core-item.json")
- EXTENDED_ITEM = TestCases.get_path(
- "data-files/examples/1.0.0-RC1/extended-item.json"
+ assert item in item_collection
+
+
+def test_item_collection_extra_fields(items: list[Item]) -> None:
+ item_collection = pystac.ItemCollection(
+ items=items, extra_fields={"custom_field": "My value"}
)
- ITEM_COLLECTION = TestCases.get_path(
- "data-files/item-collection/sample-item-collection.json"
+ assert item_collection.extra_fields.get("custom_field") == "My value"
+
+
+def test_item_collection_to_dict(items: list[Item]) -> None:
+ item_collection = pystac.ItemCollection(
+ items=items, extra_fields={"custom_field": "My value"}
)
- def setUp(self) -> None:
- self.maxDiff = None
- with open(self.ITEM_COLLECTION) as src:
- self.item_collection_dict = json.load(src)
- self.items = [
- pystac.Item.from_dict(f) for f in self.item_collection_dict["features"]
- ]
- self.stac_io = pystac.StacIO.default()
+ d = item_collection.to_dict()
- def test_item_collection_length(self) -> None:
- item_collection = pystac.ItemCollection(items=self.items)
+ assert len(d["features"]) == len(items)
+ assert d.get("custom_field") == "My value"
- self.assertEqual(len(item_collection), len(self.items))
- def test_item_collection_iter(self) -> None:
- expected_ids = [item.id for item in self.items]
- item_collection = pystac.ItemCollection(items=self.items)
+def test_item_collection_from_dict(items: list[Item]) -> None:
+ features = [item.to_dict(transform_hrefs=False) for item in items]
+ d = {
+ "type": "FeatureCollection",
+ "features": features,
+ "custom_field": "My value",
+ }
+ item_collection = pystac.ItemCollection.from_dict(d)
+ expected = len(features)
+ assert expected == len(item_collection.items)
+ assert item_collection.extra_fields.get("custom_field") == "My value"
- actual_ids = [item.id for item in item_collection]
- self.assertListEqual(expected_ids, actual_ids)
+def test_clone_item_collection() -> None:
+ item_collection_1 = pystac.ItemCollection.from_file(ITEM_COLLECTION)
+ item_collection_2 = item_collection_1.clone()
- def test_item_collection_get_item_by_index(self) -> None:
- expected_id = self.items[0].id
- item_collection = pystac.ItemCollection(items=self.items)
+ item_ids_1 = [item.id for item in item_collection_1]
+ item_ids_2 = [item.id for item in item_collection_2]
- self.assertEqual(item_collection[0].id, expected_id)
+ # All items from the original collection should be in the clone...
+ assert item_ids_1 == item_ids_2
+ # ... but they should not be the same objects
+ assert item_collection_1[0] is not item_collection_2[0]
- def test_item_collection_contains(self) -> None:
- item = pystac.Item.from_file(self.SIMPLE_ITEM)
- item_collection = pystac.ItemCollection(items=[item], clone_items=False)
- self.assertIn(item, item_collection)
+def test_raise_error_for_invalid_object(stac_io: StacIO) -> None:
+ item_dict = stac_io.read_json(SIMPLE_ITEM)
- def test_item_collection_extra_fields(self) -> None:
- item_collection = pystac.ItemCollection(
- items=self.items, extra_fields={"custom_field": "My value"}
- )
+ with pytest.raises(pystac.STACTypeError):
+ _ = pystac.ItemCollection.from_dict(item_dict)
- self.assertEqual(item_collection.extra_fields.get("custom_field"), "My value")
- def test_item_collection_to_dict(self) -> None:
- item_collection = pystac.ItemCollection(
- items=self.items, extra_fields={"custom_field": "My value"}
+def test_from_relative_path() -> None:
+ _ = pystac.ItemCollection.from_file(
+ relpath(
+ TestCases.get_path("data-files/item-collection/sample-item-collection.json")
)
+ )
- d = item_collection.to_dict()
-
- self.assertEqual(len(d["features"]), len(self.items))
- self.assertEqual(d.get("custom_field"), "My value")
-
- def test_item_collection_from_dict(self) -> None:
- features = [item.to_dict(transform_hrefs=False) for item in self.items]
- d = {
- "type": "FeatureCollection",
- "features": features,
- "custom_field": "My value",
- }
- item_collection = pystac.ItemCollection.from_dict(d)
- expected = len(features)
- self.assertEqual(expected, len(item_collection.items))
- self.assertEqual(item_collection.extra_fields.get("custom_field"), "My value")
-
- def test_clone_item_collection(self) -> None:
- item_collection_1 = pystac.ItemCollection.from_file(self.ITEM_COLLECTION)
- item_collection_2 = item_collection_1.clone()
-
- item_ids_1 = [item.id for item in item_collection_1]
- item_ids_2 = [item.id for item in item_collection_2]
-
- # All items from the original collection should be in the clone...
- self.assertListEqual(item_ids_1, item_ids_2)
- # ... but they should not be the same objects
- self.assertIsNot(item_collection_1[0], item_collection_2[0])
-
- def test_raise_error_for_invalid_object(self) -> None:
- item_dict = self.stac_io.read_json(self.SIMPLE_ITEM)
-
- with self.assertRaises(pystac.STACTypeError):
- _ = pystac.ItemCollection.from_dict(item_dict)
-
- def test_from_relative_path(self) -> None:
- _ = pystac.ItemCollection.from_file(
- relpath(
- TestCases.get_path(
- "data-files/item-collection/sample-item-collection.json"
- )
- )
- )
- def test_from_list_of_dicts(self) -> None:
- item_dict = self.stac_io.read_json(self.SIMPLE_ITEM)
- item_collection = pystac.ItemCollection(items=[item_dict], clone_items=True)
+def test_from_list_of_dicts(stac_io: StacIO) -> None:
+ item_dict = stac_io.read_json(SIMPLE_ITEM)
+ item_collection = pystac.ItemCollection(items=[item_dict], clone_items=True)
- self.assertEqual(item_collection[0].id, item_dict.get("id"))
+ assert item_collection[0].id == item_dict.get("id")
- def test_add_item_collections(self) -> None:
- item_1 = pystac.Item.from_file(self.SIMPLE_ITEM)
- item_2 = pystac.Item.from_file(self.EXTENDED_ITEM)
- item_3 = pystac.Item.from_file(self.CORE_ITEM)
- item_collection_1 = pystac.ItemCollection(items=[item_1, item_2])
- item_collection_2 = pystac.ItemCollection(items=[item_2, item_3])
+def test_add_item_collections() -> None:
+ item_1 = pystac.Item.from_file(SIMPLE_ITEM)
+ item_2 = pystac.Item.from_file(EXTENDED_ITEM)
+ item_3 = pystac.Item.from_file(CORE_ITEM)
- combined = item_collection_1 + item_collection_2
+ item_collection_1 = pystac.ItemCollection(items=[item_1, item_2])
+ item_collection_2 = pystac.ItemCollection(items=[item_2, item_3])
- self.assertEqual(len(combined), 4)
+ combined = item_collection_1 + item_collection_2
- def test_add_other_raises_error(self) -> None:
- item_collection = pystac.ItemCollection.from_file(self.ITEM_COLLECTION)
+ assert len(combined) == 4
- with self.assertRaises(TypeError):
- _ = item_collection + 2
- def test_identify_0_8_itemcollection_type(self) -> None:
- itemcollection_path = TestCases.get_path(
- "data-files/examples/0.8.1/item-spec/"
- "examples/itemcollection-sample-full.json"
- )
- itemcollection_dict = pystac.StacIO.default().read_json(itemcollection_path)
+def test_add_other_raises_error() -> None:
+ item_collection = pystac.ItemCollection.from_file(ITEM_COLLECTION)
- self.assertTrue(
- pystac.ItemCollection.is_item_collection(itemcollection_dict),
- msg="Did not correctly identify valid STAC 0.8 ItemCollection.",
- )
+ with pytest.raises(TypeError):
+ _ = item_collection + 2
- def test_identify_0_9_itemcollection(self) -> None:
- itemcollection_path = TestCases.get_path(
- "data-files/examples/0.9.0/item-spec/"
- "examples/itemcollection-sample-full.json"
- )
- itemcollection_dict = pystac.StacIO.default().read_json(itemcollection_path)
- self.assertTrue(
- pystac.ItemCollection.is_item_collection(itemcollection_dict),
- msg="Did not correctly identify valid STAC 0.9 ItemCollection.",
- )
+def test_identify_0_8_itemcollection_type(stac_io: StacIO) -> None:
+ itemcollection_path = TestCases.get_path(
+ "data-files/examples/0.8.1/item-spec/"
+ "examples/itemcollection-sample-full.json"
+ )
+ itemcollection_dict = stac_io.read_json(itemcollection_path)
+
+ assert pystac.ItemCollection.is_item_collection(
+ itemcollection_dict
+ ), "Did not correctly identify valid STAC 0.8 ItemCollection."
+
+
+def test_identify_0_9_itemcollection(stac_io: StacIO) -> None:
+ itemcollection_path = TestCases.get_path(
+ "data-files/examples/0.9.0/item-spec/"
+ "examples/itemcollection-sample-full.json"
+ )
+ itemcollection_dict = stac_io.read_json(itemcollection_path)
+
+ assert pystac.ItemCollection.is_item_collection(
+ itemcollection_dict
+ ), "Did not correctly identify valid STAC 0.9 ItemCollection."
+
+
+def test_from_dict_preserves_dict(item_collection_dict: dict[str, Any]) -> None:
+ param_dict = deepcopy(item_collection_dict)
+
+ # test that the parameter is preserved
+ _ = ItemCollection.from_dict(param_dict)
+ assert param_dict == item_collection_dict
- def test_from_dict_preserves_dict(self) -> None:
- param_dict = deepcopy(self.item_collection_dict)
+ # assert that the parameter is preserved regardless of
+ # preserve_dict
+ _ = ItemCollection.from_dict(param_dict, preserve_dict=False)
+ assert param_dict == item_collection_dict
- # test that the parameter is preserved
- _ = ItemCollection.from_dict(param_dict)
- self.assertEqual(param_dict, self.item_collection_dict)
- # assert that the parameter is preserved regardless of
- # preserve_dict
- _ = ItemCollection.from_dict(param_dict, preserve_dict=False)
- self.assertEqual(param_dict, self.item_collection_dict)
+def test_from_dict_sets_root(item_collection_dict: dict[str, Any]) -> None:
+ param_dict = deepcopy(item_collection_dict)
+ catalog = pystac.Catalog(id="test", description="test desc")
+ item_collection = ItemCollection.from_dict(param_dict, root=catalog)
+ for item in item_collection.items:
+ assert item.get_root() == catalog
- def test_from_dict_sets_root(self) -> None:
- param_dict = deepcopy(self.item_collection_dict)
- catalog = pystac.Catalog(id="test", description="test desc")
- item_collection = ItemCollection.from_dict(param_dict, root=catalog)
- for item in item_collection.items:
- self.assertEqual(item.get_root(), catalog)
- def test_to_dict_does_not_read_root_link_of_items(self) -> None:
- with MockDefaultStacIO() as mock_stac_io:
- item_collection = pystac.ItemCollection.from_file(self.ITEM_COLLECTION)
+def test_to_dict_does_not_read_root_link_of_items() -> None:
+ with MockDefaultStacIO() as mock_stac_io:
+ item_collection = pystac.ItemCollection.from_file(ITEM_COLLECTION)
- item_collection.to_dict()
+ item_collection.to_dict()
- self.assertEqual(mock_stac_io.mock.read_text.call_count, 1)
+ assert mock_stac_io.mock.read_text.call_count == 1
=====================================
tests/test_version.py
=====================================
@@ -1,25 +1,31 @@
import os
-import unittest
+from collections.abc import Generator
from unittest.mock import patch
+import pytest
+
import pystac
from tests.utils import TestCases
-class VersionTest(unittest.TestCase):
- def setUp(self) -> None:
- pystac.version.STACVersion._override_version = None
-
- def test_override_stac_version_with_environ(self) -> None:
- override_version = "1.0.0-gamma.2"
- with patch.dict(os.environ, {"PYSTAC_STAC_VERSION_OVERRIDE": override_version}):
- cat = TestCases.case_1()
- d = cat.to_dict()
- self.assertEqual(d["stac_version"], override_version)
-
- def test_override_stac_version_with_call(self) -> None:
- override_version = "1.0.0-delta.2"
- pystac.set_stac_version(override_version)
+def test_override_stac_version_with_environ() -> None:
+ override_version = "1.0.0-gamma.2"
+ with patch.dict(os.environ, {"PYSTAC_STAC_VERSION_OVERRIDE": override_version}):
cat = TestCases.case_1()
d = cat.to_dict()
- self.assertEqual(d["stac_version"], override_version)
+ assert d["stac_version"] == override_version
+
+
+ at pytest.fixture
+def override_pystac_version() -> Generator[str]:
+ stac_version = pystac.get_stac_version()
+ override_version = "1.0.0-delta.2"
+ pystac.set_stac_version(override_version)
+ yield override_version
+ pystac.set_stac_version(stac_version)
+
+
+def test_override_stac_version_with_call(override_pystac_version: str) -> None:
+ cat = TestCases.case_1()
+ d = cat.to_dict()
+ assert d["stac_version"] == override_pystac_version
=====================================
uv.lock
=====================================
@@ -1836,7 +1836,7 @@ wheels = [
[[package]]
name = "pystac"
-version = "1.11.0"
+version = "1.12.1"
source = { editable = "." }
dependencies = [
{ name = "python-dateutil" },
View it on GitLab: https://salsa.debian.org/debian-gis-team/pystac/-/commit/768dc38c30409bca26c6383ab9b04df4c2d3853c
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/pystac/-/commit/768dc38c30409bca26c6383ab9b04df4c2d3853c
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/20250222/37bd4afb/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list