diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index e986cfb18..616149f02 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -4,11 +4,6 @@ on: push: branches: - main - - "0.3" - - "0.4" - - "0.5" - - "1.0" - - "2.0" pull_request: merge_group: @@ -16,8 +11,6 @@ concurrency: group: ${{ github.ref }} cancel-in-progress: true -permissions: {} - jobs: test: name: test @@ -25,10 +18,9 @@ jobs: strategy: matrix: python-version: - - "3.10" - - "3.11" - "3.12" - "3.13" + - "3.14" os: - ubuntu-latest - windows-latest @@ -38,22 +30,21 @@ jobs: - uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7.2.0 with: 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: Lint + # if: runner.os != 'Windows' + # run: uv run prek run --all-files - name: Test on windows if: runner.os == 'Windows' shell: bash env: TMPDIR: 'D:\\a\\_temp' - run: uv run pytest tests + run: uv run pytest tests --block-network --record-mode=none - name: Test if: runner.os != 'Windows' run: uv run pytest tests --block-network --record-mode=none coverage: + if: false # exclude on pystac v2 for now name: coverage runs-on: ubuntu-latest permissions: @@ -64,11 +55,7 @@ jobs: - name: Install with dependencies run: uv sync --all-extras - name: Run coverage with orjson - run: uv run pytest tests --cov - - name: Uninstall orjson - run: uv pip uninstall orjson - - name: Run coverage without orjson, appending results - run: uv run pytest tests --cov --cov-append + run: uv run --all-extras pytest tests --cov - name: Prepare ./coverage.xml # Ignore the configured fail-under to ensure we upload the coverage report. We # will trigger a failure for coverage drops in a later job @@ -86,6 +73,7 @@ jobs: run: uv run coverage report without-orjson: + if: false # exclude on pystac v2 for now runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -101,6 +89,7 @@ jobs: # This checks to make sure any API changes haven't broken any of the # benchmarks. It doesn't do any actual benchmarking, since (IMO) that's not # appropriate for CI on GitHub actions. + if: false # exclude on pystac v2 for now runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -119,6 +108,7 @@ jobs: docs: runs-on: ubuntu-latest + if: false # exclude on pystac v2 for now steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7.2.0 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..bbf246c6c --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,42 @@ +name: Docs + +on: + push: + branches: + - main + - v2 # FIXME: remove before mergin + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + name: Build documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 + - uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 + - uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b + - name: Build docs + run: uv run mkdocs build --strict + - name: Upload artifact + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa + with: + path: ./site + + deploy: + name: Deploy to GitHub Pages + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - id: deployment + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 5e3d28398..78b535365 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -7,8 +7,6 @@ on: - edited - reopened -permissions: {} - jobs: lint: name: Lint @@ -16,6 +14,6 @@ jobs: permissions: pull-requests: read steps: - - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 + - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index b8f99df63..962c537f1 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -16,12 +16,12 @@ jobs: outputs: prs: ${{ steps.release-please.outputs.prs }} steps: - - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf id: generate-token with: app-id: ${{ vars.RELEASE_BOT_CLIENT_ID }} private-key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }} - - uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4.4.0 + - uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 id: release-please with: token: ${{ steps.generate-token.outputs.token }} @@ -32,7 +32,7 @@ jobs: needs: release-please if: ${{ needs.release-please.outputs.prs }} steps: - - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf id: generate-token with: app-id: ${{ vars.RELEASE_BOT_CLIENT_ID }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 26e17fdf4..547d309bc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,8 +5,6 @@ on: types: - published -permissions: {} - jobs: release: name: release @@ -27,7 +25,7 @@ jobs: run: | python -m pip install --upgrade pip pip install build - - name: Build + - name: Build run: python -m build - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 82a014321..99d0ac904 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,50 +1,19 @@ -# Configuration file for pre-commit (https://pre-commit.com/). -# Please run `pre-commit run --all-files` when adding or changing entries. - repos: - - repo: https://github.com/asottile/pyupgrade - rev: v3.19.1 + - repo: https://github.com/codespell-project/codespell + rev: v2.4.1 hooks: - - id: pyupgrade - args: - - "--py310-plus" - - - repo: local + - id: codespell + types_or: + - jupyter + - markdown + - python + - shell + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.14.9 hooks: - - id: ruff - name: ruff - entry: ruff check --force-exclude --fix --exit-non-zero-on-fix - language: system - types_or: [python, pyi, jupyter] - require_serial: true - + - id: ruff-check - id: ruff-format - name: ruff-format - entry: ruff format --force-exclude - language: system - stages: [pre-commit] - types_or: [python, pyi, jupyter] - require_serial: true - - - id: codespell - name: codespell - entry: codespell - language: system - stages: [pre-commit] - types_or: [jupyter, markdown, python, shell] - - - id: doc8 - name: doc8 - entry: doc8 - language: system - files: \.rst$ - require_serial: true - - - id: mypy - name: mypy - entry: mypy - args: [--no-incremental] - language: system - stages: [pre-commit] - types: [python] - require_serial: true + - repo: https://github.com/DetachHead/basedpyright-prek-mirror + rev: 1.36.1 + hooks: + - id: basedpyright diff --git a/.python-version b/.python-version index 7c7a975f4..e4fba2183 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.10 \ No newline at end of file +3.12 diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 991e44fe0..d7b5cf968 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -7,7 +7,7 @@ version: 2 build: os: ubuntu-22.04 tools: - python: "3.10" + python: "3.12" commands: # https://docs.readthedocs.io/en/stable/build-customization.html#install-dependencies-with-uv # with adaptations to use workspaces+projects instead of `uv pip` @@ -15,12 +15,11 @@ build: - asdf install uv latest - asdf global uv latest - uv sync --group docs - - uv run sphinx-build -T -b html -d docs/_build/doctrees -D language=en docs $READTHEDOCS_OUTPUT/html + - uv run mkdocs build --site-dir $READTHEDOCS_OUTPUT/html formats: - # Temporarily disabling PDF downloads due to problem with nbsphinx in LateX builds - # - pdf - htmlzip -sphinx: +mkdocs: + configuration: mkdocs.yml fail_on_warning: false diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index f9846e160..000000000 --- a/docs/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -livehtml: - sphinx-autobuild --watch ../pystac --host 0.0.0.0 ${SOURCEDIR} $(BUILDDIR)/html -d _build/doctrees - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/STAC-03.png b/docs/_static/STAC-03.png deleted file mode 100644 index 48783f210..000000000 Binary files a/docs/_static/STAC-03.png and /dev/null differ diff --git a/docs/api.rst b/docs/api.rst deleted file mode 100644 index 66d55e50a..000000000 --- a/docs/api.rst +++ /dev/null @@ -1,230 +0,0 @@ -API Reference -============= - -.. toctree:: - :hidden: - :maxdepth: 2 - :glob: - - api/pystac - api/* - -This API reference is auto-generated from the Python docstrings. The table of contents -on the left is organized by module. The sections below are organized based on concepts -and sections within the :stac-spec:`STAC Spec <>` and PySTAC itself. - -Base Structures & Classes -------------------------- - -These are the core Python classes representing entities within the STAC Spec. These -classes provide convenient methods for serializing and deserializing from JSON, -extracting properties, and creating relationships between entities. - -* :class:`pystac.Link`: Represents a :stac-spec:`Link Object - `. -* :class:`pystac.MediaType`: Provides common values used in the Link and Asset - ``"type"`` fields. -* :class:`pystac.RelType`: Provides common values used in the Link ``"rel"`` field. -* :class:`pystac.STACObject`: Base class implementing functionality common to - :class:`Catalog `, :class:`Collection ` and - :class:`Item `. - -Items ------ - -Representations of :stac-spec:`Items ` and related structures -like :stac-spec:`Asset Objects`. - -* :class:`pystac.Asset`: Represents an :stac-spec:`Asset Object - ` -* :class:`pystac.Item`: Represents an :stac-spec:`Item ` -* :class:`pystac.CommonMetadata`: A container for fields defined in the - :stac-spec:`Common Metadata ` section of the spec. - These fields are commonly found in STAC Item properties, but may be found elsewhere. - -Collections ------------ - -These are representations of :stac-spec:`Collections -` and related structures. - -* :class:`pystac.Collection`: Represents a :stac-spec:`Collection - `. -* :class:`pystac.Extent`: Represents an - :stac-spec:`Extent Object `, which - is composed of :class:`pystac.SpatialExtent` and :class:`pystac.TemporalExtent` - instances. -* :class:`pystac.Provider`: Represents a :stac-spec:`Provider Object - `. The - :class:`pystac.ProviderRole` enum provides common values used in the ``"roles"`` - field. -* :class:`pystac.Summaries`: Class for working with various types of - :stac-spec:`CollectionSummaries ` -* :class:`pystac.ItemCollection`: Represents a GeoJSON FeatureCollection in which all - Features are STAC Items. - -Catalogs --------- - -Representations of :stac-spec:`Catalogs ` and related -structures. - -* :class:`pystac.Catalog`: Represents a :stac-spec:`Catalog - `. -* :class:`pystac.CatalogType`: Enum representing the common types of Catalogs described - in the :stac-spec:`STAC Best Practices - ` - - -I/O ---- - -These classes are used to read and write files from disk or over the network, as well -as to serialize and deserialize STAC object to and from JSON. - -* :class:`pystac.StacIO`: Base class that can be inherited to provide custom I/O -* :class:`pystac.stac_io.DefaultStacIO`: The default :class:`pystac.StacIO` - implementation used throughout the library. - -Client ------- - -A convenience method for accessing `pystac-client `__ - -**Example:** - -.. code-block:: python - - from pystac.client import Client - - -Extensions ----------- - -PySTAC provides support for the following STAC Extensions: - -* :mod:`Datacube ` -* :mod:`Electro-Optical ` -* :mod:`File Info ` -* :mod:`Item Assets ` -* :mod:`MGRS ` -* :mod:`Point Cloud ` -* :mod:`Projection ` -* :mod:`Raster ` -* :mod:`SAR ` -* :mod:`Satellite ` -* :mod:`Scientific Citation ` -* :mod:`Table ` -* :mod:`Timestamps ` -* :mod:`Versioning Indicators ` -* :mod:`View Geometry ` -* :mod:`Xarray Assets ` - -The following classes are used internally to implement these extensions and may be used -to create custom implementations of STAC Extensions not supported by the library (see -:tutorial:`Adding New and Custom Extensions ` -for details): - -* :class:`pystac.extensions.base.SummariesExtension`: Base class for extending the - properties in :attr:`pystac.Collection.summaries` to include properties defined by a - STAC Extension. -* :class:`pystac.extensions.base.PropertiesExtension`: Abstract base class for - extending the properties of an :class:`~pystac.Item` to include properties defined - by a STAC Extension. -* :class:`pystac.extensions.base.ExtensionManagementMixin`: Abstract base class with - methods for adding and removing extensions from STAC Objects. -* :class:`pystac.extensions.hooks.ExtensionHooks`: Used to implement hooks when - extending a STAC Object. Primarily used to implement migrations from one extension - version to another. -* :class:`pystac.extensions.hooks.RegisteredExtensionHooks`: Used to register hooks - defined in :class:`~pystac.extensions.hooks.ExtensionHooks` instances to ensure they - are used in object deserialization. - - -Catalog Layout --------------- - -These classes are used to set the HREFs of a STAC according to some layout. -The templating functionality is also used when generating subcatalogs based on -a template. - -* :class:`pystac.layout.LayoutTemplate`: Represents a template that can be used for - deriving paths or other information based on properties of STAC objects supplied as a - template string. -* :class:`pystac.layout.BestPracticesLayoutStrategy`: Layout strategy that represents - the catalog layout described in the :stac-spec:`STAC Best Practices documentation - `. -* :class:`pystac.layout.APILayoutStrategy`: Layout strategy that represents - the catalog layout described in - the :stac-api-spec:`STAC API documentation `. -* :class:`pystac.layout.TemplateLayoutStrategy`: Layout strategy that can take strings - to be supplied to a :class:`~pystac.layout.LayoutTemplate` to derive paths. -* :class:`pystac.layout.CustomLayoutStrategy`: Layout strategy that allows users to - supply functions to dictate stac object paths. - -Errors ------- - -The following exceptions may be raised internally by the library. - -* :class:`pystac.STACError`: Generic STAC-related error -* :class:`pystac.STACTypeError`: Raised when a representation of a STAC entity is - encountered that is not correct for the context -* :class:`pystac.DuplicateObjectKeyError`: Raised when deserializing a JSON object - containing a duplicate key. -* :class:`pystac.ExtensionAlreadyExistsError`: Raised when deserializing a JSON object - containing a duplicate key. -* :class:`pystac.ExtensionTypeError`: Raised when an extension is used against an - object to which that the extension does not apply to. -* :class:`pystac.ExtensionNotImplemented`: Raised on an attempt to extend a STAC object - that does not implement the given extension. -* :class:`pystac.RequiredPropertyMissing`: Raised when a required value is expected to - be present but is missing or ``None``. -* :class:`pystac.STACValidationError`: Raised by validation calls if the STAC JSON is - invalid. -* :class:`pystac.TemplateError`: Raised when an error occurs while converting a - template string into data for :class:`~pystac.layout.LayoutTemplate`. - -Serialization -------------- - -The ``pystac.serialization`` sub-package contains tools used internally by PySTAC to -identify, serialize, and migrate STAC objects: - -* :mod:`pystac.serialization`: Tools for identifying and migrating STAC objects - - -Validation ----------- - -.. note:: - - The tools described here require that you install PySTAC with the ``validation`` - extra (see the documentation on :ref:`installing dependencies - ` for details). - -PySTAC includes a ``pystac.validation`` package for validating STAC objects, including -from PySTAC objects and directly from JSON. - -* :class:`pystac.validation.stac_validator.STACValidator`: Abstract base class defining - methods for validating STAC JSON. Implementations define methods for validating core - objects and extension. -* :class:`pystac.validation.stac_validator.JsonSchemaSTACValidator`: The default - :class:`~pystac.validation.stac_validator.STACValidator` implementation used by - PySTAC. Uses JSON schemas read from URIs provided by a - :class:`~pystac.validation.schema_uri_map.SchemaUriMap`, to validate STAC objects. -* :class:`pystac.validation.schema_uri_map.SchemaUriMap`: Defines methods for mapping - STAC versions, object types and extension ids to schema URIs. A default - implementation is included that uses known locations; however users can provide their - own schema URI maps in a - :class:`~pystac.validation.stac_validator.JsonSchemaSTACValidator` to modify the URIs - used. -* :class:`pystac.validation.schema_uri_map.DefaultSchemaUriMap`: The default - :class:`~pystac.validation.schema_uri_map.SchemaUriMap` used by PySTAC. - -Internal Classes ------------------------ - -These classes are used internally by PySTAC for caching. - -* :class:`pystac.cache.ResolvedObjectCache` diff --git a/docs/api/asset.rst b/docs/api/asset.rst deleted file mode 100644 index 062108ead..000000000 --- a/docs/api/asset.rst +++ /dev/null @@ -1,12 +0,0 @@ -pystac.asset -============ - -.. autoclass:: pystac.asset.Asset - :members: - :undoc-members: - :noindex: - -.. automodule:: pystac.asset - :members: - :undoc-members: - :exclude-members: Asset diff --git a/docs/api/cache.rst b/docs/api/cache.rst deleted file mode 100644 index cc0d2f2ab..000000000 --- a/docs/api/cache.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.cache -============ - -.. automodule:: pystac.cache - :members: - :undoc-members: diff --git a/docs/api/catalog.rst b/docs/api/catalog.rst deleted file mode 100644 index 99bfd1290..000000000 --- a/docs/api/catalog.rst +++ /dev/null @@ -1,12 +0,0 @@ -pystac.catalog -============== - -.. autoclass:: pystac.catalog.Catalog - :members: - :undoc-members: - :noindex: - -.. automodule:: pystac.catalog - :members: - :undoc-members: - :exclude-members: Catalog diff --git a/docs/api/collection.rst b/docs/api/collection.rst deleted file mode 100644 index de435c23a..000000000 --- a/docs/api/collection.rst +++ /dev/null @@ -1,12 +0,0 @@ -pystac.collection -================= - -.. autoclass:: pystac.collection.Collection - :members: - :undoc-members: - :noindex: - -.. automodule:: pystac.collection - :members: - :undoc-members: - :exclude-members: Collection diff --git a/docs/api/common_metadata.rst b/docs/api/common_metadata.rst deleted file mode 100644 index f3eb46a57..000000000 --- a/docs/api/common_metadata.rst +++ /dev/null @@ -1,7 +0,0 @@ -pystac.common_metadata -====================== - -.. automodule:: pystac.common_metadata - :members: - :undoc-members: - :noindex: diff --git a/docs/api/errors.rst b/docs/api/errors.rst deleted file mode 100644 index 62f1ea18e..000000000 --- a/docs/api/errors.rst +++ /dev/null @@ -1,7 +0,0 @@ -pystac.errors -============= - -.. automodule:: pystac.errors - :members: - :undoc-members: - :noindex: diff --git a/docs/api/extensions.rst b/docs/api/extensions.rst deleted file mode 100644 index ce37d3215..000000000 --- a/docs/api/extensions.rst +++ /dev/null @@ -1,38 +0,0 @@ -pystac.extensions -================= - -.. toctree:: - :hidden: - :maxdepth: 2 - :glob: - - extensions/* - - -.. currentmodule:: pystac.extensions - -.. autosummary:: - - classification.ClassificationExtension - datacube.DatacubeExtension - eo.EOExtension - file.FileExtension - grid.GridExtension - item_assets.ItemAssetsExtension - mgrs.MgrsExtension - mlm.MLMExtension - mlm.AssetGeneralMLMExtension - mlm.AssetDetailedMLMExtension - pointcloud.PointcloudExtension - projection.ProjectionExtension - raster.RasterExtension - render.RenderExtension - sar.SarExtension - sat.SatExtension - scientific.ScientificExtension - storage.StorageExtension - table.TableExtension - timestamps.TimestampsExtension - version.VersionExtension - view.ViewExtension - xarray_assets.XarrayAssetsExtension diff --git a/docs/api/extensions/base.rst b/docs/api/extensions/base.rst deleted file mode 100644 index 2085f3f94..000000000 --- a/docs/api/extensions/base.rst +++ /dev/null @@ -1,6 +0,0 @@ -pytac.extensions.base -===================== - -.. automodule:: pystac.extensions.base - :members: - :undoc-members: diff --git a/docs/api/extensions/classification.rst b/docs/api/extensions/classification.rst deleted file mode 100644 index 5b6e79aec..000000000 --- a/docs/api/extensions/classification.rst +++ /dev/null @@ -1,6 +0,0 @@ -pytac.extensions.classification -=============================== - -.. automodule:: pystac.extensions.classification - :members: - :undoc-members: diff --git a/docs/api/extensions/datacube.rst b/docs/api/extensions/datacube.rst deleted file mode 100644 index 24204dd26..000000000 --- a/docs/api/extensions/datacube.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.datacube -========================== - -.. automodule:: pystac.extensions.datacube - :members: - :undoc-members: diff --git a/docs/api/extensions/eo.rst b/docs/api/extensions/eo.rst deleted file mode 100644 index c46515d09..000000000 --- a/docs/api/extensions/eo.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.eo -==================== - -.. automodule:: pystac.extensions.eo - :members: - :undoc-members: diff --git a/docs/api/extensions/ext.rst b/docs/api/extensions/ext.rst deleted file mode 100644 index 23708b444..000000000 --- a/docs/api/extensions/ext.rst +++ /dev/null @@ -1,7 +0,0 @@ -pytac.extensions.ext -==================== - -.. automodule:: pystac.extensions.ext - :members: - :inherited-members: - :undoc-members: diff --git a/docs/api/extensions/file.rst b/docs/api/extensions/file.rst deleted file mode 100644 index 2e33794e1..000000000 --- a/docs/api/extensions/file.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.file -====================== - -.. automodule:: pystac.extensions.file - :members: - :undoc-members: diff --git a/docs/api/extensions/grid.rst b/docs/api/extensions/grid.rst deleted file mode 100644 index b8f31ca6e..000000000 --- a/docs/api/extensions/grid.rst +++ /dev/null @@ -1,6 +0,0 @@ -pytac.extensions.grid -===================== - -.. automodule:: pystac.extensions.grid - :members: - :undoc-members: diff --git a/docs/api/extensions/hooks.rst b/docs/api/extensions/hooks.rst deleted file mode 100644 index 949847bb4..000000000 --- a/docs/api/extensions/hooks.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.hooks -======================= - -.. automodule:: pystac.extensions.hooks - :members: - :undoc-members: diff --git a/docs/api/extensions/item_assets.rst b/docs/api/extensions/item_assets.rst deleted file mode 100644 index c46ce768d..000000000 --- a/docs/api/extensions/item_assets.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.item\_assets -============================== - -.. automodule:: pystac.extensions.item_assets - :members: - :undoc-members: diff --git a/docs/api/extensions/mgrs.rst b/docs/api/extensions/mgrs.rst deleted file mode 100644 index 5bcb6b389..000000000 --- a/docs/api/extensions/mgrs.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.mgrs -============================ - -.. automodule:: pystac.extensions.mgrs - :members: - :undoc-members: diff --git a/docs/api/extensions/mlm.rst b/docs/api/extensions/mlm.rst deleted file mode 100644 index 8a4e3531d..000000000 --- a/docs/api/extensions/mlm.rst +++ /dev/null @@ -1,8 +0,0 @@ -pystac.extensions.mlm -============================ - -.. automodule:: pystac.extensions.mlm - :members: - :inherited-members: StringEnum, Generic, PropertiesExtension, ExtensionManagementMixin - :undoc-members: - diff --git a/docs/api/extensions/pointcloud.rst b/docs/api/extensions/pointcloud.rst deleted file mode 100644 index 24dde5031..000000000 --- a/docs/api/extensions/pointcloud.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.pointcloud -============================ - -.. automodule:: pystac.extensions.pointcloud - :members: - :undoc-members: diff --git a/docs/api/extensions/projection.rst b/docs/api/extensions/projection.rst deleted file mode 100644 index 65f079f8d..000000000 --- a/docs/api/extensions/projection.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.projection -============================ - -.. automodule:: pystac.extensions.projection - :members: - :undoc-members: diff --git a/docs/api/extensions/raster.rst b/docs/api/extensions/raster.rst deleted file mode 100644 index 7f67d824d..000000000 --- a/docs/api/extensions/raster.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.raster -======================== - -.. automodule:: pystac.extensions.raster - :members: - :undoc-members: diff --git a/docs/api/extensions/render.rst b/docs/api/extensions/render.rst deleted file mode 100644 index b7b13ba1e..000000000 --- a/docs/api/extensions/render.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.render -======================== - -.. automodule:: pystac.extensions.render - :members: - :undoc-members: diff --git a/docs/api/extensions/sar.rst b/docs/api/extensions/sar.rst deleted file mode 100644 index 9f48cc5c8..000000000 --- a/docs/api/extensions/sar.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.sar -===================== - -.. automodule:: pystac.extensions.sar - :members: - :undoc-members: diff --git a/docs/api/extensions/sat.rst b/docs/api/extensions/sat.rst deleted file mode 100644 index 793a47862..000000000 --- a/docs/api/extensions/sat.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.sat -===================== - -.. automodule:: pystac.extensions.sat - :members: - :undoc-members: diff --git a/docs/api/extensions/scientific.rst b/docs/api/extensions/scientific.rst deleted file mode 100644 index 83d06361e..000000000 --- a/docs/api/extensions/scientific.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.scientific -============================ - -.. automodule:: pystac.extensions.scientific - :members: - :undoc-members: diff --git a/docs/api/extensions/storage.rst b/docs/api/extensions/storage.rst deleted file mode 100644 index 420e5dfb1..000000000 --- a/docs/api/extensions/storage.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.storage -============================ - -.. automodule:: pystac.extensions.storage - :members: - :undoc-members: diff --git a/docs/api/extensions/table.rst b/docs/api/extensions/table.rst deleted file mode 100644 index 80abd7dc1..000000000 --- a/docs/api/extensions/table.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.table -======================= - -.. automodule:: pystac.extensions.table - :members: - :undoc-members: diff --git a/docs/api/extensions/timestamps.rst b/docs/api/extensions/timestamps.rst deleted file mode 100644 index da5758255..000000000 --- a/docs/api/extensions/timestamps.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.timestamps -============================ - -.. automodule:: pystac.extensions.timestamps - :members: - :undoc-members: diff --git a/docs/api/extensions/version.rst b/docs/api/extensions/version.rst deleted file mode 100644 index 4c606ce75..000000000 --- a/docs/api/extensions/version.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.version -========================= - -.. automodule:: pystac.extensions.version - :members: - :undoc-members: diff --git a/docs/api/extensions/view.rst b/docs/api/extensions/view.rst deleted file mode 100644 index 1b395df28..000000000 --- a/docs/api/extensions/view.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.view -====================== - -.. automodule:: pystac.extensions.view - :members: - :undoc-members: diff --git a/docs/api/extensions/xarray_assets.rst b/docs/api/extensions/xarray_assets.rst deleted file mode 100644 index 494093068..000000000 --- a/docs/api/extensions/xarray_assets.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.extensions.xarray_assets -=============================== - -.. automodule:: pystac.extensions.xarray_assets - :members: - :undoc-members: diff --git a/docs/api/item.rst b/docs/api/item.rst deleted file mode 100644 index aedf84bcd..000000000 --- a/docs/api/item.rst +++ /dev/null @@ -1,12 +0,0 @@ -pystac.item -=========== - -.. autoclass:: pystac.item.Item - :members: - :undoc-members: - :noindex: - -.. automodule:: pystac.item - :members: - :undoc-members: - :exclude-members: Item diff --git a/docs/api/item_assets.rst b/docs/api/item_assets.rst deleted file mode 100644 index 501c56420..000000000 --- a/docs/api/item_assets.rst +++ /dev/null @@ -1,7 +0,0 @@ -pystac.item_assets -================== - -.. automodule:: pystac.item_assets - :members: - :undoc-members: - :noindex: diff --git a/docs/api/item_collection.rst b/docs/api/item_collection.rst deleted file mode 100644 index 9b01f7afa..000000000 --- a/docs/api/item_collection.rst +++ /dev/null @@ -1,12 +0,0 @@ -pystac.item_collection -====================== - -.. autoclass:: pystac.item_collection.ItemCollection - :members: - :undoc-members: - :noindex: - -.. automodule:: pystac.item_collection - :members: - :undoc-members: - :exclude-members: ItemCollection diff --git a/docs/api/layout.rst b/docs/api/layout.rst deleted file mode 100644 index 08853cf26..000000000 --- a/docs/api/layout.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.layout -============= - -.. automodule:: pystac.layout - :members: - :undoc-members: diff --git a/docs/api/link.rst b/docs/api/link.rst deleted file mode 100644 index 9756ef0b6..000000000 --- a/docs/api/link.rst +++ /dev/null @@ -1,12 +0,0 @@ -pystac.link -=========== - -.. autoclass:: pystac.link.Link - :members: - :undoc-members: - :noindex: - -.. automodule:: pystac.link - :members: - :undoc-members: - :exclude-members: Link diff --git a/docs/api/media_type.rst b/docs/api/media_type.rst deleted file mode 100644 index 174f42d0c..000000000 --- a/docs/api/media_type.rst +++ /dev/null @@ -1,7 +0,0 @@ -pystac.media_type -================= - -.. automodule:: pystac.media_type - :members: - :undoc-members: - :noindex: diff --git a/docs/api/provider.rst b/docs/api/provider.rst deleted file mode 100644 index b1f090e7d..000000000 --- a/docs/api/provider.rst +++ /dev/null @@ -1,7 +0,0 @@ -pystac.provider -=============== - -.. automodule:: pystac.provider - :members: - :undoc-members: - :noindex: diff --git a/docs/api/pystac.rst b/docs/api/pystac.rst deleted file mode 100644 index e16c4692d..000000000 --- a/docs/api/pystac.rst +++ /dev/null @@ -1,227 +0,0 @@ -pystac ------- - -.. automodule:: pystac - :members: read_file, write_file, read_dict, set_stac_version, get_stac_version - - .. autosummary:: - STACObject - Catalog - Collection - Extent - SpatialExtent - TemporalExtent - Provider - Summaries - Item - Asset - ItemAssetDefinition - CommonMetadata - ItemCollection - Link - StacIO - read_file - write_file - read_dict - set_stac_version - get_stac_version - - -STACObject ----------- - -.. autoclass:: pystac.STACObject - :members: - :inherited-members: - :undoc-members: - -.. autoclass:: pystac.STACObjectType - :members: - :undoc-members: - -Catalog -------- - -.. autoclass:: pystac.Catalog - :members: - :inherited-members: - :undoc-members: - -CatalogType ------------ - -.. autoclass:: pystac.CatalogType - :members: - :inherited-members: - :undoc-members: - -Collection ----------- - -.. autoclass:: pystac.Collection - :members: - :inherited-members: - :undoc-members: - -Extent ------- - -.. autoclass:: pystac.Extent - :members: - :undoc-members: - -SpatialExtent -------------- - -.. autoclass:: pystac.SpatialExtent - :members: - :undoc-members: - -TemporalExtent --------------- - -.. autoclass:: pystac.TemporalExtent - :members: - :undoc-members: - -ProviderRole ------------- - -.. autoclass:: pystac.ProviderRole - :members: - :undoc-members: - -Provider --------- - -.. autoclass:: pystac.Provider - :members: - :undoc-members: - -Summaries ---------- - -.. autoclass:: pystac.Summaries - :members: - :undoc-members: - -Item ----- - -.. autoclass:: pystac.Item - :members: - :inherited-members: - :undoc-members: - -Asset ------ - -.. autoclass:: pystac.Asset - :members: - :undoc-members: - -ItemAssetDefinition -------------------- - -.. autoclass:: pystac.ItemAssetDefinition - :members: - :undoc-members: - - -CommonMetadata --------------- - -.. autoclass:: pystac.CommonMetadata - :members: - :undoc-members: - -ItemCollection --------------- - -.. autoclass:: pystac.ItemCollection - :members: - :inherited-members: - :undoc-members: - -Link ----- - -.. autoclass:: pystac.Link - :members: - :inherited-members: - :undoc-members: - -MediaType ---------- - -.. autoclass:: pystac.MediaType - :members: - :undoc-members: - -RelType -------- - -.. autoclass:: pystac.RelType - :members: - :undoc-members: - -StacIO ------- - -.. autoclass:: pystac.StacIO - :members: - :undoc-members: - -Errors ------- - -STACError -~~~~~~~~~ - -.. autoclass:: pystac.STACError - -STACTypeError -~~~~~~~~~~~~~ - -.. autoclass:: pystac.STACTypeError - -DuplicateObjectKeyError -~~~~~~~~~~~~~~~~~~~~~~~ - -.. autoclass:: pystac.DuplicateObjectKeyError - -ExtensionAlreadyExistsError -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. autoclass:: pystac.ExtensionAlreadyExistsError - -ExtensionTypeError -~~~~~~~~~~~~~~~~~~ - -.. autoclass:: pystac.ExtensionTypeError - -ExtensionNotImplemented -~~~~~~~~~~~~~~~~~~~~~~~ - -.. autoclass:: pystac.ExtensionNotImplemented - -RequiredPropertyMissing -~~~~~~~~~~~~~~~~~~~~~~~ - -.. autoclass:: pystac.RequiredPropertyMissing - -STACValidationError -~~~~~~~~~~~~~~~~~~~ - -.. autoclass:: pystac.STACValidationError - -TemplateError -~~~~~~~~~~~~~ - -.. autoclass:: pystac.TemplateError - - -DeprecatedWarning -~~~~~~~~~~~~~~~~~ - -.. autoclass:: pystac.DeprecatedWarning diff --git a/docs/api/rel_type.rst b/docs/api/rel_type.rst deleted file mode 100644 index a13c99f86..000000000 --- a/docs/api/rel_type.rst +++ /dev/null @@ -1,7 +0,0 @@ -pystac.rel_type -=============== - -.. automodule:: pystac.rel_type - :members: - :undoc-members: - :noindex: diff --git a/docs/api/serialization.rst b/docs/api/serialization.rst deleted file mode 100644 index 4a21bea46..000000000 --- a/docs/api/serialization.rst +++ /dev/null @@ -1,12 +0,0 @@ -pystac.serialization -==================== - -.. toctree:: - :hidden: - :maxdepth: 2 - :glob: - - serialization/* - -.. automodule:: pystac.serialization - :members: diff --git a/docs/api/serialization/common_properties.rst b/docs/api/serialization/common_properties.rst deleted file mode 100644 index 5666ca965..000000000 --- a/docs/api/serialization/common_properties.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.serialization.common\_properties -======================================= - -.. automodule:: pystac.serialization.common_properties - :members: - :undoc-members: diff --git a/docs/api/serialization/identify.rst b/docs/api/serialization/identify.rst deleted file mode 100644 index a54e38f62..000000000 --- a/docs/api/serialization/identify.rst +++ /dev/null @@ -1,20 +0,0 @@ -pystac.serialization.identify -============================= - - -.. automodule:: pystac.serialization.identify - :members: STACVersionRange, identify_stac_object, identify_stac_object_type - :undoc-members: - :noindex: - - -.. autoclass:: pystac.serialization.identify.STACVersionRange - :members: - :undoc-members: - :noindex: - - -.. automodule:: pystac.serialization.identify - :members: - :undoc-members: - :exclude-members: STACVersionRange, identify_stac_object, identify_stac_object_type diff --git a/docs/api/serialization/migrate.rst b/docs/api/serialization/migrate.rst deleted file mode 100644 index df03f05ec..000000000 --- a/docs/api/serialization/migrate.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.serialization.migrate -============================ - -.. automodule:: pystac.serialization.migrate - :members: - :undoc-members: diff --git a/docs/api/stac_io.rst b/docs/api/stac_io.rst deleted file mode 100644 index 611d276f0..000000000 --- a/docs/api/stac_io.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.stac_io -============== - -.. automodule:: pystac.stac_io - :members: - :undoc-members: diff --git a/docs/api/stac_object.rst b/docs/api/stac_object.rst deleted file mode 100644 index a5a37fcec..000000000 --- a/docs/api/stac_object.rst +++ /dev/null @@ -1,19 +0,0 @@ -pystac.stac_object -================== - -.. autoclass:: pystac.stac_object.STACObject - :members: - :undoc-members: - :noindex: - -.. autoclass:: pystac.stac_object.STACObjectType - :members: - :undoc-members: - :noindex: - -.. automodule:: pystac.stac_object - :members: - :undoc-members: - :exclude-members: STACObject, STACObjectType - -.. autoclass:: pystac.stac_object.S diff --git a/docs/api/summaries.rst b/docs/api/summaries.rst deleted file mode 100644 index e7a7191d8..000000000 --- a/docs/api/summaries.rst +++ /dev/null @@ -1,12 +0,0 @@ -pystac.summaries -================ - -.. autoclass:: pystac.summaries.Summaries - :members: - :undoc-members: - :noindex: - -.. automodule:: pystac.summaries - :members: - :undoc-members: - :exclude-members: Summaries diff --git a/docs/api/utils.rst b/docs/api/utils.rst deleted file mode 100644 index 83f148ccf..000000000 --- a/docs/api/utils.rst +++ /dev/null @@ -1,10 +0,0 @@ -pystac.utils -============ - -.. automodule:: pystac.utils - :members: - :undoc-members: - -.. autoclass:: pystac.utils.T - -.. autoclass:: pystac.utils.U diff --git a/docs/api/validation.rst b/docs/api/validation.rst deleted file mode 100644 index 67f0ff09b..000000000 --- a/docs/api/validation.rst +++ /dev/null @@ -1,13 +0,0 @@ -pystac.validation -================= - -.. toctree:: - :hidden: - :maxdepth: 2 - :glob: - - validation/* - -.. automodule:: pystac.validation - :members: - :undoc-members: diff --git a/docs/api/validation/local_validator.rst b/docs/api/validation/local_validator.rst deleted file mode 100644 index 09096e349..000000000 --- a/docs/api/validation/local_validator.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.validation.local\_validator -================================== - -.. automodule:: pystac.validation.local_validator - :members: - :undoc-members: diff --git a/docs/api/validation/schema_uri_map.rst b/docs/api/validation/schema_uri_map.rst deleted file mode 100644 index 231bd2f28..000000000 --- a/docs/api/validation/schema_uri_map.rst +++ /dev/null @@ -1,6 +0,0 @@ -pystac.validation.schema\_uri\_map -================================== - -.. automodule:: pystac.validation.schema_uri_map - :members: - :undoc-members: diff --git a/docs/api/validation/stac_validator.rst b/docs/api/validation/stac_validator.rst deleted file mode 100644 index 651a5c669..000000000 --- a/docs/api/validation/stac_validator.rst +++ /dev/null @@ -1,12 +0,0 @@ -pystac.validation.stac\_validator -================================= - -.. autoclass:: pystac.validation.stac_validator.JsonSchemaSTACValidator - :members: - :undoc-members: - :noindex: - -.. automodule:: pystac.validation.stac_validator - :members: - :undoc-members: - :exclude-members: JsonSchemaSTACValidator diff --git a/docs/api/version.rst b/docs/api/version.rst deleted file mode 100644 index ea856258f..000000000 --- a/docs/api/version.rst +++ /dev/null @@ -1,7 +0,0 @@ -pystac.version -============== - -.. automodule:: pystac.version - :members: - :undoc-members: - :noindex: diff --git a/docs/concepts.rst b/docs/concepts.rst deleted file mode 100644 index 2c320f1e1..000000000 --- a/docs/concepts.rst +++ /dev/null @@ -1,1004 +0,0 @@ -Concepts -######## - -This page will give an overview of some important concepts to understand when working -with PySTAC. If you want to check code examples, see the :ref:`tutorials`. - -.. _stac_version_support: - -STAC Spec Version Support -========================= - -The latest version of PySTAC supports STAC Spec |stac_version| and will automatically -update any catalogs to this version. To work with older versions of the STAC Spec, -please use an older version of PySTAC: - -================= ============== -STAC Spec Version PySTAC Version -================= ============== ->=1.0 Latest -0.9 0.4.* -0.8 0.3.* -<0.8 *Not supported* -================= ============== - -Reading STACs -============= - -PySTAC can read STAC data from JSON. Generally users read in the root catalog, and then -use the python objects to crawl through the data. Once you read in the root of the STAC, -you can work with the STAC in memory. - -.. code-block:: python - - from pystac import Catalog - - catalog = Catalog.from_file('/some/example/catalog.json') - - for root, catalogs, items in catalog.walk(): - # Do interesting things with the STAC data. - -To see how to hook into PySTAC for reading from alternate URIs such as cloud object -storage, see :ref:`using stac_io`. - -Writing STACs -============= - -While working with STACs in-memory don't require setting file paths, in order to save a -STAC, you'll need to give each STAC object a ``self`` link that describes the location -of where it should be saved to. Luckily, PySTAC makes it easy to create a STAC catalog -with a :stac-spec:`canonical layout ` and with the -links that follow the :stac-spec:`best practices `. You -simply call ``normalize_hrefs`` with the root directory of where the STAC will be saved, -and then call ``save`` with the type of catalog (described in the :ref:`catalog types` -section) that matches your use case. - -.. code-block:: python - - from pystac import (Catalog, CatalogType) - - catalog = Catalog.from_file('/some/example/catalog.json') - catalog.normalize_hrefs('/some/copy/') - catalog.save(catalog_type=CatalogType.SELF_CONTAINED) - - copycat = Catalog.from_file('/some/copy/catalog.json') - - -Normalizing HREFs ------------------ - -The ``normalize_hrefs`` call sets HREFs for all the links in the STAC according to the -Catalog, Collection and Items, all based off of the root URI that is passed in: - -.. code-block:: python - - catalog.normalize_hrefs('/some/location') - catalog.save(catalog_type=CatalogType.SELF_CONTAINED) - -This will lay out the HREFs of the STAC according to the :stac-spec:`best practices -document `. - -Layouts -~~~~~~~ - -PySTAC provides a few different strategies for laying out the HREFs of a STAC. -To use them you can pass in a strategy when instantiating a catalog or when -calling `normalize_hrefs`. - -Using templates -''''''''''''''' - -You can utilize template strings to determine the file paths of HREFs set on Catalogs, -Collection or Items. These templates use python format strings, which can name -the property or attribute of the item you want to use for replacing the template -variable. For example: - -.. code-block:: python - - from pystac.layout import TemplateLayoutStrategy - - strategy = TemplateLayoutStrategy(item_template="${collection}/${year}/${month}") - catalog.normalize_hrefs('/some/location', strategy=strategy) - catalog.save(catalog_type=CatalogType.SELF_CONTAINED) - -The above code will save items in subfolders based on the collection ID, year and month -of it's datetime (or start_datetime if a date range is defined and no datetime is -defined). Note that the forward slash (``/``) should be used as path separator in the -template string regardless of the system path separator (thus both in POSIX-compliant -and Windows environments). - -You can use dot notation to specify attributes of objects or keys in dictionaries for -template variables. PySTAC will look at the object, it's ``properties`` and its -``extra_fields`` for property names or dictionary keys. Some special cases, like -``year``, ``month``, ``day`` and ``date`` exist for datetime on Items, as well as -``collection`` for Item's Collection's ID. - -See the documentation on :class:`~pystac.layout.LayoutTemplate` for more documentation -on how layout templates work. - -Using custom functions -'''''''''''''''''''''' - -If you want to build your own strategy, you can subclass ``HrefLayoutStrategy`` or use -:class:`~pystac.layout.CustomLayoutStrategy` to provide functions that work with -Catalogs, Collections or Items. Similar to the templating strategy, you can provide a -fallback strategy (which defaults to -:class:`~pystac.layout.BestPracticesLayoutStrategy`) for any stac object type that you -don't supply a function for. - - -Set a default catalog layout strategy -''''''''''''''''''''''''''''''''''''' - -Instead of fixing the HREFs of child objects retrospectively using `normalize_hrefs`, -you can also define a default strategy for a catalog. When instantiating a catalog, -pass in a custom strategy and base href. Consequently, the HREFs of all child -objects and items added to the catalog tree will be set correctly using that strategy. - - -.. code-block:: python - - from pystac import Catalog, Collection, Item - - catalog = Catalog(..., - href="/some/location/catalog.json", - strategy=custom_strategy) - collection = Collection(...) - item = Item(...) - catalog.add_child(collection) - collection.add_item(item) - catalog.save() - - -.. _catalog types: - -Catalog Types -------------- - -The STAC :stac-spec:`best practices document ` lays out different -catalog types, and how their links should be formatted. A brief description is below, -but check out the document for the official take on these types: - -The catalog types will also dictate the asset HREF formats. Asset HREFs in any catalog -type can be relative or absolute may be absolute depending on their location; see the -section on :ref:`rel vs abs asset` below. - - -Self-Contained Catalogs -~~~~~~~~~~~~~~~~~~~~~~~ - -A self-contained catalog (indicated by ``catalog_type=CatalogType.SELF_CONTAINED``) -applies to STACs that do not have a long term location, and can be moved around. These -STACs are useful for copying data to and from locations, without having to change any -link metadata. - -A self-contained catalog has two important properties: - -- It contains only relative links -- It contains **no** self links. - -For a catalog that is the most easy to copy around, it's recommended that item assets -use relative links, and reside in the same directory as the item's STAC metadata file. - -Relative Published Catalogs -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A relative published catalog (indicated by -``catalog_type=CatalogType.RELATIVE_PUBLISHED``) is one that is tied at it's root to a -specific location, but otherwise contains relative links. This is designed so that a -self-contained catalog can be 'published' online by just adding one field (the self -link) to its root catalog. - -A relative published catalog has the following properties: - -- It contains **only one** self link: the root of the catalog contains a (necessarily - absolute) link to it's published location. -- All other objects in the STAC contain relative links, and no self links. - - -Absolute Published Catalogs -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An absolute published catalog (indicated by -``catalog_type=CatalogType.ABSOLUTE_PUBLISHED``) uses absolute links for everything. It -is preferable where possible, since it allows for the easiest provenance tracking out of -all the catalog types. - -An absolute published catalog has the following properties: - -- Each STAC object contains only absolute links. -- Each STAC object has a self link. - -It is not recommended to have relative asset HREFs in an absolute published catalog. - - -Relative vs Absolute HREFs --------------------------- - -HREFs inside a STAC for either links or assets can be relative or absolute. - -Relative vs Absolute Link HREFs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Absolute links point to their file locations in a fully described way. Relative links -are relative to the linking object's file location. For example, if a catalog at -``/some/location/catalog.json`` has a link to an item that has an HREF set to -``item-id/item-id.json``, then that link should resolve to the absolute path -``/some/location/item-id/item-id.json``. - -Links are set as absolute or relative HREFs at save time, as determine by the root -catalog's catalog_type :attr:`~pystac.Catalog.catalog_type`. This means that, even if -the stored HREF of the link is absolute, if the root -``catalog_type=CatalogType.RELATIVE_PUBLISHED`` or -``catalog_type=CatalogType.SELF_CONTAINED`` and subsequent serializing of the any links -in the catalog will produce a relative link, based on the self link of the parent -object. - -You can make all the links of a catalog relative or absolute by setting the -:func:`~pystac.Catalog.catalog_type` field then resaving the entire catalog. - -.. _rel vs abs asset: - -Relative vs Absolute Asset HREFs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Asset HREFs can also be relative or absolute. If an asset HREF is relative, then it is -relative to the Item's metadata file. For example, if the item at -``/some/location/item-id/item-id.json`` had an asset with an HREF of ``./image.tif``, -then the fully resolved path for that image would be -``/some/location/item-id/image.tif`` - -You can make all the asset HREFs of a catalog relative or absolute using the -:func:`Catalog.make_all_asset_hrefs_relative -` and -:func:`Catalog.make_all_asset_hrefs_absolute -` methods. Note that these will not move -any files around, and if the file location does not share a common parent with the -asset's item's self HREF, then the asset HREF will remain absolute as no relative path -is possible. - -Including a ``self`` link -------------------------- - -Every stac object has a :func:`~pystac.STACObject.save_object` method, that takes as an -argument whether or not to include the object's self link. As noted in the section on -:ref:`catalog types`, a self link is necessarily absolute; if an object only contains -relative links, then it cannot contain the self link. PySTAC uses self links as a way of -tracking the object's file location, either what it was read from or it's pending save -location, so each object can have a self link even if you don't ever want that self link -written (e.g. if you are working with self-contained catalogs). - -.. _using stac_io: - -I/O in PySTAC -============= - -The :class:`pystac.StacIO` class defines fundamental methods for I/O -operations within PySTAC, including serialization and deserialization to and from -JSON files and conversion to and from Python dictionaries. This is an abstract class -and should not be instantiated directly. However, PySTAC provides a -:class:`pystac.stac_io.DefaultStacIO` class with minimal implementations of these -methods. This default implementation provides support for reading and writing files -from the local filesystem as well as HTTP URIs (using ``urllib``). This class is -created automatically by all of the object-specific I/O methods (e.g. -:meth:`pystac.Catalog.from_file`), so most users will not need to instantiate this -class themselves. - -If you are dealing with a STAC catalog with URIs that require authentication. -It is possible provide auth headers (or any other customer headers) to the -:class:`pystac.stac_io.DefaultStacIO`. - -.. code-block:: python - - from pystac import Catalog - from pystac import StacIO - - stac_io = StacIO.default() - stac_io.headers = {"Authorization": ""} - - catalog = Catalog.from_file("", stac_io=stac_io) - -You can double check that requests PySTAC is making by adjusting logging level so -that you see all API calls. - -.. code-block:: python - - import logging - - logging.basicConfig() - logger = logging.getLogger('pystac') - logger.setLevel(logging.DEBUG) - -If you require more custom logic for I/O operations or would like to use a -3rd-party library for I/O operations (e.g. ``requests``), -you can create a sub-class of :class:`pystac.StacIO` -(or :class:`pystac.stac_io.DefaultStacIO`) and customize the methods as -you see fit. You can then pass instances of this custom sub-class into the ``stac_io`` -argument of most object-specific I/O methods. You can also use -:meth:`pystac.StacIO.set_default` in your client's ``__init__.py`` file to make this -sub-class the default :class:`pystac.StacIO` implementation throughout the library. - -For example, the following code examples will allow -for reading from AWS's S3 cloud object storage using `boto3 -`__ -or Azure Blob Storage using the `Azure SDK for Python -`__: - -.. tab-set:: - .. tab-item:: AWS S3 - - .. code-block:: python - - from urllib.parse import urlparse - import boto3 - from pystac import Link - from pystac.stac_io import DefaultStacIO, StacIO - from typing import Union, Any - - class CustomStacIO(DefaultStacIO): - def __init__(self): - self.s3 = boto3.resource("s3") - super().__init__() - - def read_text( - self, source: Union[str, Link], *args: Any, **kwargs: Any - ) -> str: - parsed = urlparse(source) - if parsed.scheme == "s3": - bucket = parsed.netloc - key = parsed.path[1:] - - obj = self.s3.Object(bucket, key) - return obj.get()["Body"].read().decode("utf-8") - else: - return super().read_text(source, *args, **kwargs) - - def write_text( - self, dest: Union[str, Link], txt: str, *args: Any, **kwargs: Any - ) -> None: - parsed = urlparse(dest) - if parsed.scheme == "s3": - bucket = parsed.netloc - key = parsed.path[1:] - self.s3.Object(bucket, key).put(Body=txt, ContentEncoding="utf-8") - else: - super().write_text(dest, txt, *args, **kwargs) - - StacIO.set_default(CustomStacIO) - - .. tab-item:: Azure Blob Storage - - .. code-block:: python - - import os - import re - from typing import Any, Dict, Optional, Tuple, Union - from urllib.parse import urlparse - - from azure.core.credentials import ( - AzureNamedKeyCredential, - AzureSasCredential, - TokenCredential, - ) - from azure.storage.blob import BlobClient, ContentSettings - from pystac import Link - from pystac.stac_io import DefaultStacIO - - BLOB_HTTPS_URI_PATTERN = r"https:\/\/(.+?)\.blob\.core\.windows\.net" - - AzureCredentialType = Union[ - str, - Dict[str, str], - AzureNamedKeyCredential, - AzureSasCredential, - TokenCredential, - ] - - - class BlobStacIO(DefaultStacIO): - """A custom StacIO class for reading and writing STAC objects - from/to Azure Blob storage. - """ - - conn_str: Optional[str] = os.getenv("AZURE_STORAGE_CONNECTION_STRING") - account_url: Optional[str] = None - credential: Optional[AzureCredentialType] = None - overwrite: bool = True - - def _is_blob_uri(self, href: str) -> bool: - """Check if href matches Blob URI pattern.""" - if re.search( - re.compile(BLOB_HTTPS_URI_PATTERN), href - ) is not None or href.startswith("abfs://"): - return True - else: - return False - - def _parse_blob_uri(self, uri: str) -> Tuple[str, str]: - """Parse the container and blob name from a Blob URI. - - Parameters - ---------- - uri - An Azure Blob URI. - - Returns - ------- - The container and blob names. - """ - if uri.startswith("abfs://"): - path = uri.replace("abfs://", "/") - else: - path = urlparse(uri).path - - parts = path.split("/") - container = parts[1] - blob = "/".join(parts[2:]) - return container, blob - - def _get_blob_client(self, uri: str) -> BlobClient: - """Instantiate a `BlobClient` given a container and blob. - - Parameters - ---------- - uri - An Azure Blob URI. - - Returns - ------- - A `BlobClient` for interacting with `blob` in `container`. - """ - container, blob = self._parse_blob_uri(uri) - - if self.conn_str: - return BlobClient.from_connection_string( - self.conn_str, - container_name=container, - blob_name=blob, - ) - elif self.account_url: - return BlobClient( - account_url=self.account_url, - container_name=container, - blob_name=blob, - credential=self.credential, - ) - else: - raise ValueError( - "Must set conn_str or account_url (and credential if required)" - ) - - def read_text(self, source: Union[str, Link], *args: Any, **kwargs: Any) -> str: - if isinstance(source, Link): - source = source.href - if self._is_blob_uri(source): - blob_client = self._get_blob_client(source) - obj = blob_client.download_blob().readall().decode() - return obj - else: - return super().read_text(source, *args, **kwargs) - - def write_text( - self, dest: Union[str, Link], txt: str, *args: Any, **kwargs: Any - ) -> None: - """Write STAC Objects to Blob storage. Note: overwrites by default.""" - if isinstance(dest, Link): - dest = dest.href - if self._is_blob_uri(dest): - blob_client = self._get_blob_client(dest) - blob_client.upload_blob( - txt, - overwrite=self.overwrite, - content_settings=ContentSettings(content_type="application/json"), - ) - else: - super().write_text(dest, txt, *args, **kwargs) - - - # set Blob storage connection string - BlobStacIO.conn_str = "my-storage-connection-string" - - # OR set Blob account URL, credential - BlobStacIO.account_url = "https://myblobstorageaccount.blob.core.windows.net" - BlobStacIO.credential = AzureSasCredential("my-sas-token") - - # modify overwrite behavior - BlobStacIO.overwrite = False - - # set BlobStacIO as default StacIO - StacIO.set_default(BlobStacIO) - -If you only need to customize read operations you can inherit from -:class:`~pystac.stac_io.DefaultStacIO` and only overwrite the read method. For example, -to take advantage of connection pooling using a `requests.Session -`__: - -.. code-block:: python - - from urllib.parse import urlparse - import requests - from pystac.stac_io import DefaultStacIO, StacIO - from typing import Union, Any - - class ConnectionPoolingIO(DefaultStacIO): - def __init__(self): - self.session = requests.Session() - - def read_text( - self, source: Union[str, Link], *args: Any, **kwargs: Any - ) -> str: - parsed = urlparse(uri) - if parsed.scheme.startswith("http"): - return self.session.get(uri).text - else: - return super().read_text(source, *args, **kwargs) - - StacIO.set_default(ConnectionPoolingIO) - - -.. _validation_concepts: - -Validation -========== - -PySTAC includes validation functionality that allows users to validate PySTAC objects as -well JSON-encoded STAC objects from STAC versions `0.8.0` and later. - -Enabling validation -------------------- - -To enable the validation feature you'll need to have installed PySTAC with the optional -dependency via: - -.. code-block:: bash - - > pip install pystac[validation] - -This installs the ``jsonschema`` package which is used with the default validator. If -you define your own validation class as described below, you are not required to have -this extra dependency. - -Validating PySTAC objects -------------------------- - -You can validate any :class:`~pystac.Catalog`, :class:`~pystac.Collection` or -:class:`~pystac.Item` by calling the :meth:`~pystac.STACObject.validate` method: - -.. code-block:: python - - item.validate() - -This validates against the latest set of JSON schemas (which are included with the -PySTAC package) or older versions (which are hosted at https://schemas.stacspec.org). -This validation includes any extensions that the object extends (these are always -accessed remotely based on their URIs). - -If there are validation errors, a :class:`~pystac.STACValidationError` -is raised. - -You can also call :meth:`~pystac.Catalog.validate_all` on a Catalog or Collection to -recursively walk through a catalog and validate all objects within it. - -.. code-block:: python - - catalog.validate_all() - -Validating STAC JSON --------------------- - -You can validate STAC JSON represented as a ``dict`` using the -:func:`pystac.validation.validate_dict` method: - -.. code-block:: python - - import json - from pystac.validation import validate_dict - - with open('/path/to/item.json') as f: - js = json.load(f) - validate_dict(js) - -You can also recursively validate all of the catalogs, collections and items across STAC -versions using the :func:`pystac.validation.validate_all` method: - -.. code-block:: python - - import json - from pystac.validation import validate_all - - with open('/path/to/catalog.json') as f: - js = json.load(f) - validate_all(js) - -Using your own validator ------------------------- - -By default PySTAC uses the :class:`~pystac.validation.JsonSchemaSTACValidator` -implementation for validation. Users can define their own implementations of -:class:`~pystac.validation.stac_validator.STACValidator` and register it with pystac -using :func:`pystac.validation.set_validator`. - -The :class:`~pystac.validation.JsonSchemaSTACValidator` takes a -:class:`~pystac.validation.schema_uri_map.SchemaUriMap`, which by default uses the -:class:`~pystac.validation.schema_uri_map.DefaultSchemaUriMap`. If desirable, users can -create their own implementation of -:class:`~pystac.validation.schema_uri_map.SchemaUriMap` and register -a new instance of :class:`~pystac.validation.JsonSchemaSTACValidator` using that schema -map with :func:`pystac.validation.set_validator`. - -Extensions -========== - -From the documentation on :stac-spec:`STAC Spec Extensions `: - - Extensions to the core STAC specification provide additional JSON fields that can be - used to better describe the data. Most tend to be about describing a particular - domain or type of data, but some imply functionality. - -This library makes an effort to support all extensions that are part of the -`stac-extensions GitHub org -`__, and -we are committed to supporting all STAC Extensions at the "Candidate" maturity level or -above (see the `Extension Maturity -`__ documentation for details). - -Accessing Extension Functionality ---------------------------------- - -Extension functionality is encapsulated in classes that are specific to the STAC -Extension (e.g. Electro-Optical, Projection, etc.) and STAC Object -(:class:`~pystac.Collection`, :class:`pystac.Item`, or :class:`pystac.Asset`). All -classes that extend these objects inherit from -:class:`pystac.extensions.base.PropertiesExtension`, and you can use the -``ext`` accessor on the object to access the extension fields. - -For instance, if you have an item that implements the :stac-ext:`Electro-Optical -Extension `, you can access the fields associated with that extension using -:meth:`Item.ext `: - -.. code-block:: python - - import pystac - - item = pystac.Item.from_file("tests/data-files/eo/eo-landsat-example.json") - - # As long as the Item implements the EO Extension you can access all the - # EO properties directly - bands = item.ext.eo.bands - cloud_cover = item.ext.eo.cloud_cover - ... - -.. note:: ``ext`` will raise an :exc:`~pystac.ExtensionNotImplemented` - exception if the object does not implement that extension (e.g. if the extension - URI is not in that object's :attr:`~pystac.STACObject.stac_extensions` list). See - the `Adding an Extension`_ section below for details on adding an extension to an - object. - -If you don't want to raise an error you can use -:meth:`Item.ext.has ` -to first check if the extension is implemented on your pystac object: - -.. code-block:: python - - if item.ext.has("eo"): - bands = item.ext.eo.bands - -See the documentation for each extension implementation for details on the supported -properties and other functionality. - -Extensions have access to the properties of the object. *This attribute is a reference -to the properties of the* :class:`~pystac.Collection`, :class:`~pystac.Item` *or* -:class:`~pystac.Asset` *being extended and can therefore mutate those properties.* -For instance: - -.. code-block:: python - - item = pystac.Item.from_file("tests/data-files/eo/eo-landsat-example.json") - print(item.properties["eo:cloud_cover"]) - # 78 - - print(item.ext.eo.cloud_cover) - # 78 - - item.ext.eo.cloud_cover = 45 - print(item.properties["eo:cloud_cover"]) - # 45 - -There is also a -:attr:`~pystac.extensions.base.PropertiesExtension.additional_read_properties` attribute -that, if present, gives read-only access to properties of any objects that own the -extended object. For instance, an extended :class:`pystac.Asset` instance would have -read access to the properties of the :class:`pystac.Item` that owns it (if there is -one). If a property exists in both additional_read_properties and properties, the value -in additional_read_properties will take precedence. - - -An ``apply`` method is available on extended objects. This allows you to pass in -property values pertaining to the extension. Properties that are required by the -extension will be required arguments to the ``apply`` method. Optional properties will -have a default value of ``None``: - -.. code-block:: python - - # Can also omit cloud_cover entirely... - item.ext.eo.apply(0.5, bands, cloud_cover=None) - - -Adding an Extension -------------------- - -You can add an extension to a STAC object that does not already implement that extension -using the :meth:`Item.ext.add ` method. -The :meth:`Item.ext.add ` method adds the correct -schema URI to the :attr:`~pystac.Item.stac_extensions` list for the STAC object. - -.. code-block:: python - - # Load a basic item without any extensions - item = pystac.Item.from_file("tests/data-files/item/sample-item.json") - print(item.stac_extensions) - # [] - - # Add the Electro-Optical extension - item.ext.add("eo") - print(item.stac_extensions) - # ['https://stac-extensions.github.io/eo/v1.1.0/schema.json'] - -Extended Summaries ------------------- - -Extension classes like :class:`~pystac.extensions.projection.ProjectionExtension` may -also provide a ``summaries`` static method that can be used to extend the Collection -summaries. This method returns a class inheriting from -:class:`pystac.extensions.base.SummariesExtension` that provides tools for summarizing -the properties defined by that extension. These classes also hold a reference to the -Collection's :class:`pystac.Summaries` instance in the ``summaries`` attribute. - - -.. code-block:: python - - import pystac - from pystac.extensions.projection import ProjectionExtension - - # Load a collection that does not implement the Projection extension - collection = pystac.Collection.from_file( - "tests/data-files/examples/1.0.0/collection.json" - ) - - # Add Projection extension summaries to the collection - proj = ProjectionExtension.summaries(collection, add_if_missing=True) - print(collection.stac_extensions) - # [ - # ...., - # 'https://stac-extensions.github.io/projection/v1.1.0/schema.json' - # ] - - # Set the values for various extension fields - proj.epsg = [4326] - collection_as_dict = collection.to_dict() - collection_as_dict["summaries"]["proj:epsg"] - # [4326] - - -Item Asset properties -===================== - -Properties that apply to Items can be found in two places: the Item's properties or in -any of an Item's Assets. If the property is on an Asset, it applies only to that specific -asset. For example, gsd defined for an Item represents the best Ground Sample Distance -(resolution) for the data within the Item. However, some assets may be lower resolution -and thus have a higher gsd. In that case, the `gsd` can be found on the Asset. - -See the STAC documentation on :stac-spec:`Additional Fields for Assets -` and the relevant :stac-spec:`Best -Practices ` for more -information. - -The implementation of this feature in PySTAC uses the method described here and is -consistent across Item and ItemExtensions. The bare property names represent values for -the Item only, but for each property where it is possible to set on both the Item or the -Asset there is a ``get_`` and ``set_`` methods that optionally take an Asset. For the -``get_`` methods, if the property is found on the Asset, the Asset's value is used; -otherwise the Item's value will be used. For the ``set_`` method, if an Asset is passed -in the value will be applied to the Asset and not the Item. - -For example, if we have an Item with a ``gsd`` of 10 with three bands, and only asset -"band3" having a ``gsd`` of 20, the ``get_gsd`` method will behave in the following way: - - .. code-block:: python - - assert item.common_metadata.gsd == 10 - assert item.common_metadata.get_gsd() == 10 - assert item.common_metadata.get_gsd(item.asset['band1']) == 10 - assert item.common_metadata.get_gsd(item.asset['band3']) == 20 - -Similarly, if we set the asset at 'band2' to have a ``gsd`` of 30, it will only affect -that asset: - - .. code-block:: python - - item.common_metadata.set_gsd(30, item.assets['band2'] - assert item.common_metadata.gsd == 10 - assert item.common_metadata.get_gsd(item.asset['band2']) == 30 - -Manipulating STACs -================== - -PySTAC is designed to allow for STACs to be manipulated in-memory. This includes -:ref:`copy stacs`, walking over all objects in a STAC and mutating their properties, or -using collection-style `map` methods for mapping over items. - - -Walking over a STAC -------------------- - -You can walk through all sub-catalogs and items of a catalog with a method inspired -by the Python Standard Library `os.walk() -`_ method: :func:`Catalog.walk() -`: - -.. code-block:: python - - for root, subcats, items in catalog.walk(): - # Root represents a catalog currently being walked in the tree - root.title = '{} has been walked!'.format(root.id) - - # subcats represents any catalogs or collections owned by root - for cat in subcats: - cat.title = 'About to be walked!' - - # items represent all items that are contained by root - for item in items: - item.title = '{} - owned by {}'.format(item.id, root.id) - -Mapping over Items ------------------- - -The :func:`Catalog.map_items ` method is useful for -into smaller chunks (e.g. tiling out large image items). -item, you can return multiple items, in case you are generating new objects, or splitting items -manipulating items in a STAC. This will create a full copy of the STAC, so will leave -the original catalog unmodified. In the method that manipulates and returns the modified - -.. code-block:: python - - def modify_item_title(item): - item.title = 'Some new title' - return item - - def duplicate_item(item): - duplicated_item = item.clone() - duplicated_item.id += "-duplicated" - return [item, duplicated_item] - - - c = catalog.map_items(modify_item_title) - c = c.map_items(duplicate_item) - new_catalog = c - -.. _copy stacs: - -Copying STACs in-memory ------------------------ - -The in-memory copying of STACs to create new ones is crucial to correct manipulations -and mutations of STAC data. The :func:`STACObject.full_copy -` mechanism handles this in a way that ties the elements of -the copies STAC together correctly. This includes situations where there might be cycles -in the graph of connected objects of the STAC (which otherwise would be `a tree -`_). - -Resolving STAC objects -====================== - -PySTAC tries to only "resolve" STAC Objects - that is, load the metadata contained by -STAC files pointed to by links into Python objects in-memory - when necessary. It also -ensures that two links that point to the same object resolve to the same in-memory -object. - -Lazy resolution of STAC objects -------------------------------- - -Links are read only when they need to be. For instance, when you load a catalog using -:func:`Catalog.from_file `, the catalog and all of its links -are read into a :class:`~pystac.Catalog` instance. If you iterate through -:attr:`Catalog.links `, you'll see the :attr:`~pystac.Link.target` -of the :class:`~pystac.Link` will refer to a string - that is the HREF of the link. -However, if you call :func:`Catalog.get_items `, for instance, -you'll get back the actual :class:`~pystac.Item` instances that are referred to by each -item link in the Catalog. That's because at the time you call ``get_items``, PySTAC is -"resolving" the links for any link that represents an item in the catalog. - -The resolution mechanism is accomplished through :func:`Link.resolve_stac_object -`. Though this method is used extensively internally to -PySTAC, ideally this is completely transparent to users of PySTAC, and you won't have to -worry about how and when links get resolved. However, one important aspect to understand -is how object resolution caching happens. - -Resolution Caching ------------------- - -The root :class:`~pystac.Catalog` instance of a STAC (the Catalog which is linked to by -every associated object's ``root`` link) contains a cache of resolved objects. This -cache points to in-memory instances of :class:`~pystac.STACObject` s that have already -been resolved through PySTAC crawling links associated with that root catalog. The cache -works off of the stac object's ID, which is why **it is necessary for every STAC object -in the catalog to have a unique identifier, which is unique across the entire STAC**. - -When a link is being resolved from a STACObject that has it's root set, that root is -passed into the :func:`Link.resolve_stac_object ` call. -That root's :class:`~pystac.cache.ResolvedObjectCache` will be used to -ensure that if the link is pointing to an object that has already been resolved, then -that link will point to the same, single instance in the cache. This ensures working -with STAC objects in memory doesn't create a situation where multiple copies of the same -STAC objects are created from different links, manipulated, and written over each other. - -Working with STAC JSON -====================== - -The ``pystac.serialization`` package has some functionality around working directly with -STAC JSON objects, without utilizing PySTAC object types. This is used internally by -PySTAC, but might also be useful to users working directly with JSON (e.g. on -validation). - - -Identifying STAC objects from JSON ----------------------------------- - -Users can identify STAC information, including the object type, version and extensions, -from JSON. The main method for this is -:func:`~pystac.serialization.identify_stac_object`, which returns an object that -contains the object type, the range of versions this object is valid for (according to -PySTAC's best guess), the common extensions implemented by this object, and any custom -extensions (represented by URIs to JSON Schemas). - -.. code-block:: python - - from pystac.serialization import identify_stac_object - - json_dict = ... - - info = identify_stac_object(json_dict) - - # The object type - info.object_type - - # The version range - info.version_range - - # The common extensions - info.common_extensions - - # The custom Extensions - info.custom_extensions - -Merging common properties -------------------------- - -For pre-1.0.0 STAC, The :func:`~pystac.serialization.merge_common_properties` will take -a JSON dict that represents an item, and if it is associated with a collection, merge in -the collection's properties. You can pass in a dict that contains previously read -collections that caches collections by the HREF of the collection link and/or the -collection ID, which can help avoid multiple reads of -collection links. - -Note that this feature was dropped in STAC 1.0.0-beta.1 - -Geo interface -============= - -:class:`~pystac.Item` implements ``__geo_interface__``, a de-facto standard for -describing geospatial objects in Python: -https://gist.github.com/sgillies/2217756. Many packages can automatically use -objects that implement this protocol, e.g. `shapely -`_: - -.. code-block:: python - - >>> from pystac import Item - >>> from shapely.geometry import mapping, shape - >>> item = Item.from_file("data-files/item/sample-item.json") - >>> print(shape(item)) - POLYGON ((-122.308150179 37.488035566, -122.597502109 37.538869539, - -122.576687533 37.613537207, -122.2880486 37.562818007, -122.308150179 - 37.488035566)) diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 9af5c5707..000000000 --- a/docs/conf.py +++ /dev/null @@ -1,258 +0,0 @@ -# -# Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import subprocess -import sys -from typing import Any - -sys.path.insert(0, os.path.abspath(".")) -sys.path.insert(0, os.path.abspath("../")) -from pystac.version import STACVersion, __version__ # noqa:E402 - -git_branch = ( - subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]) - .decode("utf-8") - .strip() -) - -# -- Project information ----------------------------------------------------- - -project = "pystac" -copyright = "2019, Azavea" -author = "stac-utils" - -# The short X.Y version -version = __version__ -# The full version, including alpha/beta/rc tags -release = __version__ - - -# -- General configuration --------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - "sphinx_design", - "sphinx.ext.autodoc", - "sphinx.ext.autosummary", - "sphinx.ext.viewcode", - "sphinx.ext.intersphinx", - "sphinx.ext.napoleon", - "sphinx.ext.githubpages", - "sphinx.ext.extlinks", - "nbsphinx", -] - -extlinks = { - "tutorial": ( - "https://github.com/stac-utils/pystac/tree/{}/docs/tutorials/%s".format( - git_branch - ), - "%s tutorial", - ), - "stac-spec": ( - "https://github.com/radiantearth/stac-spec/tree/v{}/%s".format( - STACVersion.DEFAULT_STAC_VERSION - ), - "%s path", - ), - "stac-api-spec": ( - "https://github.com/radiantearth/stac-api-spec/tree/v{}/%s".format( - STACVersion.DEFAULT_STAC_API_VERSION - ), - "%s path", - ), - "stac-ext": ("https://github.com/stac-extensions/%s", "%s extension"), -} - -# Add any paths that contain templates here, relative to this directory. -# templates_path = ["_templates"] - -# Static CSS files -# html_css_files = ["custom.css"] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = ".rst" - -# The master toctree document. -master_doc = "index" - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = "en" - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "**.ipynb_checkpoints"] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = None - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "pydata_sphinx_theme" - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -html_theme_options = { - "icon_links": [ - { - "name": "GitHub", - "url": "https://github.com/stac-utils/pystac", - "icon": "fab fa-github-square", - }, - { - "name": "Gitter", - "url": "https://gitter.im/SpatioTemporal-Asset-Catalog/" - "python?utm_source=share-link&utm_medium=link&utm_campaign=share-link", - "icon": "fab fa-gitter", - }, - ], - "external_links": [ - {"name": "STAC Spec", "url": "https://github.com/radiantearth/stac-spec"} - ], - "header_links_before_dropdown": 7, - "navigation_with_keys": False, - # "navbar_end": ["navbar-icon-links.html", "search-field.html"] -} - -html_logo = "_static/STAC-03.png" - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -html_sidebars: dict[str, list[str]] = {"index": []} - - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = "pystacdoc" - - -# -- Options for LaTeX output ------------------------------------------------ - -latex_elements: dict[str, Any] = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, "pystac.tex", "pystac Documentation", "stac-utils", "manual"), -] - - -# -- Options for manual page output ------------------------------------------ - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [(master_doc, "pystac", "pystac Documentation", [author], 1)] - - -# -- Options for Texinfo output ---------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ( - master_doc, - "pystac", - "pystac Documentation", - author, - "pystac", - "Python library for SpatioTemporal Asset Catalogs (STAC).", - "Miscellaneous", - ), -] - - -# -- Options for Epub output ------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = project - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# -# epub_identifier = '' - -# A unique identification for the text. -# -# epub_uid = '' - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ["search.html"] - - -# -- Extension configuration ------------------------------------------------- - -intersphinx_mapping = { - "python": ("https://docs.python.org/3", None), - "dateutil": ("https://dateutil.readthedocs.io/en/stable", None), - "urllib3": ("https://urllib3.readthedocs.io/en/stable", None), -} - -# -- Substutition variables - -rst_epilog = f".. |stac_version| replace:: {STACVersion.DEFAULT_STAC_VERSION}" - -nitpick_ignore = [ - ("py:class", "Datetime"), - ("py:class", "L"), - ("py:class", "pystac.summaries.T"), - ("py:class", "HREF"), # this one partially works - ("py:class", "jsonschema.validators.Draft7Validator"), -] diff --git a/docs/contributing.rst b/docs/contributing.rst deleted file mode 100644 index 13029ec00..000000000 --- a/docs/contributing.rst +++ /dev/null @@ -1,154 +0,0 @@ -Contributing -============ - -A list of issues and ongoing work is available on the PySTAC `issues page -`_. If you want to contribute code, the best -way is to coordinate with the core developers via an issue or pull request conversation. - -Development installation -^^^^^^^^^^^^^^^^^^^^^^^^ -Fork PySTAC into your GitHub account. Then, clone the repo and install it locally with -`uv ` as follows: - -.. code-block:: bash - - git clone git@github.com:your_user_name/pystac.git - cd pystac - uv sync - source .venv/bin/activate - -Testing -^^^^^^^ - -PySTAC runs tests using `pytest `_. You can -find unit tests in the ``tests/`` directory. - -To run the tests: - -.. code-block:: bash - - $ pytest - -To run the tests and generate the coverage report: - -.. code-block:: bash - - $ 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 -an HTML report that can be opened in a browser). - -The PySTAC tests use `vcrpy `_ to mock API calls -with "pre-recorded" API responses. This often comes up when testing validation. - -When adding new tests that require pulling remote files use the ``@pytest.mark.vcr`` -decorator. Record the new responses and commit them to the repository. - -.. code-block:: bash - - $ pytest -v -s --record-mode new_episodes - $ git add - $ git commit -a -m 'new test episodes' - -Code quality checks -^^^^^^^^^^^^^^^^^^^ - -tl;dr: Run ``pre-commit install --overwrite`` to perform checks when committing, and -``pytest`` to run all tests. - -PySTAC uses - -- `ruff `_ for Python code linting -- `codespell `_ to check code for common misspellings -- `doc8 `__ for style checking on RST files in the docs -- `mypy `_ for Python type annotation checks - -Run all of these with ``pre-commit run --all-files`` or a single one using -``pre-commit run --all-files ID``, where ``ID`` is one of the command names above. For -example, to lint all the Python code, run ``pre-commit run --all-files ruff``. - -You can also install a Git pre-commit hook which will run the relevant linters and -formatters on any staged code when committing. This will be much faster than running on -all files, which is usually [#]_ only required when changing the pre-commit version or -configuration. Once installed you can bypass this check by adding the ``--no-verify`` -flag to Git commit commands, as in ``git commit --no-verify``. - -.. [#] In rare cases changes to one file might invalidate an unchanged file, such as - when modifying the return type of a function used in another file. - -Documentation -^^^^^^^^^^^^^ - -All new features or changes should include API documentation, in the form of -docstrings. Additionally, if you are updating an extension version, check to -see if that extension is used in the ``examples/`` STAC objects at the top level -of the repository. If so, update the extension version, then re-run -``docs/quickstart.ipynb`` to include the new extension versions in the notebook -cell output. - -Benchmarks -^^^^^^^^^^ - -PySTAC uses `asv `_ for benchmarking. Benchmarks are -defined in the ``./benchmarks`` directory. Due to the inherent uncertainty in -the environment of GitHub workflow runners, benchmarks are not executed in CI. -If your changes may affect performance, use the provided script to run the -benchmark suite locally. You'll need to install the benchmark dependencies -first. This script will compare your current ``HEAD`` with the **main** branch -and report any improvements or regressions. - -.. code-block:: bash - - 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 -v1.0.0 and v1.6.1 (from `@gadomski's `_ computer):: - - before after ratio - [eee06027] [579c071b] - - - 533±20μs 416±10μs 0.78 collection.CollectionBench.time_collection_from_file [gadomski/virtualenv-py3.10-orjson] - - 329±8μs 235±10μs 0.72 collection.CollectionBench.time_collection_from_dict [gadomski/virtualenv-py3.10-orjson] - - 332±10μs 231±4μs 0.70 collection.CollectionBench.time_collection_from_dict [gadomski/virtualenv-py3.10] - - 174±4μs 106±2μs 0.61 item.ItemBench.time_item_from_dict [gadomski/virtualenv-py3.10] - - 174±4μs 106±2μs 0.61 item.ItemBench.time_item_from_dict [gadomski/virtualenv-py3.10-orjson] - before after ratio - [eee06027] [579c071b] - - + 87.1±3μs 124±5μs 1.42 catalog.CatalogBench.time_catalog_from_dict [gadomski/virtualenv-py3.10] - + 87.1±4μs 122±5μs 1.40 catalog.CatalogBench.time_catalog_from_dict [gadomski/virtualenv-py3.10-orjson] - -When developing new benchmarks, you can run a shortened version of the benchmark suite: - -.. code-block:: bash - - asv dev - - -CHANGELOG -^^^^^^^^^ - -PySTAC maintains a `changelog `_ -to track changes between releases. This changelog is automatically kept up-to-date by -`release-please `_, specifically on an unmerged -release PR. - -Style -^^^^^ - -In an effort to maintain a consistent codebase, PySTAC conforms to the following rules: - -.. code-block:: python - - # DO - from datetime import datetime - - # DON't - import datetime - import datetime as dt - -The exception to this rule is when ``datetime`` is only imported for type checking and -using the class directly interferes with another variable name. In this case, in the -TYPE_CHECKING block you should do ``from datetime import datetime as Datetime``. diff --git a/docs/example-catalog/catalog.json b/docs/example-catalog/catalog.json deleted file mode 100644 index 285d412b6..000000000 --- a/docs/example-catalog/catalog.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "Catalog", - "stac_version": "1.1.0", - "stac_extensions": [], - "id": "landsat-stac-collection-catalog", - "title": "STAC for Landsat data", - "description": "STAC for Landsat data", - "links": [ - { - "href": "./catalog.json", - "rel": "self" - }, - { - "href": "./catalog.json", - "rel": "root" - }, - { - "href": "./landsat-8-l1/collection.json", - "rel": "child" - } - ] -} \ No newline at end of file diff --git a/docs/example-catalog/landsat-8-l1/2018-05/LC80150322018141LGN00.json b/docs/example-catalog/landsat-8-l1/2018-05/LC80150322018141LGN00.json deleted file mode 100644 index bf6a8d4d5..000000000 --- a/docs/example-catalog/landsat-8-l1/2018-05/LC80150322018141LGN00.json +++ /dev/null @@ -1,276 +0,0 @@ -{ - "type": "Feature", - "id": "LC80150322018141LGN00", - "stac_version" : "1.0.0", - "stac_extensions" : [ - "https://stac-extensions.github.io/eo/v1.1.0/schema.json", - "https://stac-extensions.github.io/view/v1.0.0/schema.json", - "https://stac-extensions.github.io/projection/v1.1.0/schema.json" - ], - "bbox": [ - -77.88298, - 39.23073, - -75.07535, - 41.41022 - ], - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [ - -77.28911976020206, - 41.40912394323429 - ], - [ - -75.07576783500748, - 40.97162247589133 - ], - [ - -75.66872631473827, - 39.23210949585851 - ], - [ - -77.87946700654118, - 39.67679918442899 - ], - [ - -77.28911976020206, - 41.40912394323429 - ] - ] - ] - }, - "collection": "landsat-8-l1", - "properties": { - "collection": "landsat-8-l1", - "datetime": "2018-05-21T15:44:59Z", - "view:sun_azimuth": 134.8082647, - "view:sun_elevation": 64.00406717, - "eo:cloud_cover": 4, - "instruments": ["OLI_TIRS"], - "view:off_nadir": 0, - "platform": "landsat-8", - "gsd": 30, - "proj:epsg": 32618, - "proj:transform": [258885.0, 30.0, 0.0, 4584315.0, 0.0, -30.0], - "proj:geometry": { - "type": "Polygon", - "coordinates": [ - [ - [258885.0, 4346085.0], - [258885.0, 4584315.0], - [493515.0, 4584315.0], - [493515.0, 4346085.0], - [258885.0, 4346085.0] - ] - ] - }, - "proj:shape": [7821, 7941] - }, - "assets": { - "index": { - "type": "text/html", - "title": "HTML index page", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/index.html", - "roles": [] - }, - "thumbnail": { - "title": "Thumbnail image", - "type": "image/jpeg", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_thumb_large.jpg", - "roles" : [ - "thumbnail" - ] - }, - "B1": { - "type": "image/tiff", - "title": "Band 1 (coastal)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_B1.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B1", - "full_width_half_max" : 0.02, - "center_wavelength" : 0.44, - "common_name" : "coastal" - } - ] - }, - "B2": { - "type": "image/tiff", - "title": "Band 2 (blue)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_B2.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B2", - "full_width_half_max" : 0.06, - "center_wavelength" : 0.48, - "common_name" : "blue" - } - ] - }, - "B3": { - "type": "image/tiff", - "title": "Band 3 (green)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_B3.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B3", - "full_width_half_max" : 0.06, - "center_wavelength" : 0.56, - "common_name" : "green" - } - ] - }, - "B4": { - "type": "image/tiff", - "title": "Band 4 (red)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_B4.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B4", - "full_width_half_max" : 0.04, - "center_wavelength" : 0.65, - "common_name" : "red" - } - ] - }, - "B5": { - "type": "image/tiff", - "title": "Band 5 (nir)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_B5.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B5", - "full_width_half_max" : 0.03, - "center_wavelength" : 0.86, - "common_name" : "nir" - } - ] - }, - "B6": { - "type": "image/tiff", - "title": "Band 6 (swir16)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_B6.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B6", - "full_width_half_max" : 0.08, - "center_wavelength" : 1.6, - "common_name" : "swir16" - } - ] - }, - "B7": { - "type": "image/tiff", - "title": "Band 7 (swir22)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_B7.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B7", - "full_width_half_max" : 0.22, - "center_wavelength" : 2.2, - "common_name" : "swir22" - } - ] - }, - "B8": { - "type": "image/tiff", - "title": "Band 8 (pan)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_B8.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B8", - "full_width_half_max" : 0.18, - "center_wavelength" : 0.59, - "common_name" : "pan" - } - ] - }, - "B9": { - "type": "image/tiff", - "title": "Band 9 (cirrus)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_B9.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B9", - "full_width_half_max" : 0.02, - "center_wavelength" : 1.37, - "common_name" : "cirrus" - } - ] - }, - "B10": { - "type": "image/tiff", - "title": "Band 10 (lwir)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_B10.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B10", - "full_width_half_max" : 0.8, - "center_wavelength" : 10.9, - "common_name" : "lwir11" - } - ] - }, - "B11": { - "type": "image/tiff", - "title": "Band 11 (lwir)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_B11.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B11", - "full_width_half_max" : 1, - "center_wavelength" : 12, - "common_name" : "lwir2" - } - ] - }, - "ANG": { - "title": "Angle coefficients file", - "type": "text/plain", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_ANG.txt", - "roles": [] - }, - "MTL": { - "title": "original metadata file", - "type": "text/plain", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_MTL.txt", - "roles": [] - }, - "BQA": { - "title": "Band quality data", - "type": "image/tiff", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/032/LC08_L1TP_015032_20180521_20180605_01_T1/LC08_L1TP_015032_20180521_20180605_01_T1_BQA.TIF", - "roles": [] - } - }, - "links": [ - { - "rel": "self", - "href": "./LC80150322018141LGN00.json" - }, - { - "rel": "parent", - "href": "../collection.json" - }, - { - "rel": "collection", - "href": "../collection.json" - }, - { - "rel": "root", - "href": "../../catalog.json" - } - ] -} diff --git a/docs/example-catalog/landsat-8-l1/2018-06/LC80140332018166LGN00.json b/docs/example-catalog/landsat-8-l1/2018-06/LC80140332018166LGN00.json deleted file mode 100644 index e4eecc3c5..000000000 --- a/docs/example-catalog/landsat-8-l1/2018-06/LC80140332018166LGN00.json +++ /dev/null @@ -1,276 +0,0 @@ -{ - "type": "Feature", - "id": "LC80140332018166LGN00", - "stac_version" : "1.0.0", - "stac_extensions" : [ - "https://stac-extensions.github.io/eo/v1.1.0/schema.json", - "https://stac-extensions.github.io/view/v1.0.0/schema.json", - "https://stac-extensions.github.io/projection/v1.1.0/schema.json" - ], - "bbox": [ - -76.66703, - 37.82561, - -73.94861, - 39.95958 - ], - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [ - -76.12180471942207, - 39.95810181489563 - ], - [ - -73.94910518227414, - 39.55117185146004 - ], - [ - -74.49564725552679, - 37.826064511480496 - ], - [ - -76.66550404911956, - 38.240699151776084 - ], - [ - -76.12180471942207, - 39.95810181489563 - ] - ] - ] - }, - "collection": "landsat-8-l1", - "properties": { - "collection": "landsat-8-l1", - "datetime": "2018-06-15T15:39:09Z", - "view:sun_azimuth": 125.59055137, - "view:sun_elevation": 66.54485226, - "eo:cloud_cover": 22, - "instruments": ["OLI_TIRS"], - "view:off_nadir": 0, - "platform": "landsat-8", - "gsd": 30, - "proj:epsg": 32618, - "proj:transform": [357585.0, 30.0, 0.0, 4423815.0, 0.0, -30.0], - "proj:geometry": { - "type": "Polygon", - "coordinates": [ - [ - [357585.0, 4187685.0], - [357585.0, 4423815.0], - [589815.0, 4423815.0], - [589815.0, 4187685.0], - [357585.0, 4187685.0] - ] - ] - }, - "proj:shape": [7741, 7871] - }, - "assets": { - "index": { - "type": "text/html", - "title": "HTML index page", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/index.html", - "roles": [] - }, - "thumbnail": { - "title": "Thumbnail image", - "type": "image/jpeg", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_thumb_large.jpg", - "roles" : [ - "thumbnail" - ] - }, - "B1": { - "type": "image/tiff", - "title": "Band 1 (coastal)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B1.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B1", - "full_width_half_max" : 0.02, - "center_wavelength" : 0.44, - "common_name" : "coastal" - } - ] - }, - "B2": { - "type": "image/tiff", - "title": "Band 2 (blue)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B2.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B2", - "full_width_half_max" : 0.06, - "center_wavelength" : 0.48, - "common_name" : "blue" - } - ] - }, - "B3": { - "type": "image/tiff", - "title": "Band 3 (green)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B3.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B3", - "full_width_half_max" : 0.06, - "center_wavelength" : 0.56, - "common_name" : "green" - } - ] - }, - "B4": { - "type": "image/tiff", - "title": "Band 4 (red)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B4.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B4", - "full_width_half_max" : 0.04, - "center_wavelength" : 0.65, - "common_name" : "red" - } - ] - }, - "B5": { - "type": "image/tiff", - "title": "Band 5 (nir)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B5.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B5", - "full_width_half_max" : 0.03, - "center_wavelength" : 0.86, - "common_name" : "nir" - } - ] - }, - "B6": { - "type": "image/tiff", - "title": "Band 6 (swir16)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B6.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B6", - "full_width_half_max" : 0.08, - "center_wavelength" : 1.6, - "common_name" : "swir16" - } - ] - }, - "B7": { - "type": "image/tiff", - "title": "Band 7 (swir22)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B7.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B7", - "full_width_half_max" : 0.22, - "center_wavelength" : 2.2, - "common_name" : "swir22" - } - ] - }, - "B8": { - "type": "image/tiff", - "title": "Band 8 (pan)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B8.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B8", - "full_width_half_max" : 0.18, - "center_wavelength" : 0.59, - "common_name" : "pan" - } - ] - }, - "B9": { - "type": "image/tiff", - "title": "Band 9 (cirrus)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B9.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B9", - "full_width_half_max" : 0.02, - "center_wavelength" : 1.37, - "common_name" : "cirrus" - } - ] - }, - "B10": { - "type": "image/tiff", - "title": "Band 10 (lwir)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B10.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B10", - "full_width_half_max" : 0.8, - "center_wavelength" : 10.9, - "common_name" : "lwir11" - } - ] - }, - "B11": { - "type": "image/tiff", - "title": "Band 11 (lwir)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B11.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B11", - "full_width_half_max" : 1, - "center_wavelength" : 12, - "common_name" : "lwir2" - } - ] - }, - "ANG": { - "title": "Angle coefficients file", - "type": "text/plain", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_ANG.txt", - "roles": [] - }, - "MTL": { - "title": "original metadata file", - "type": "text/plain", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_MTL.txt", - "roles": [] - }, - "BQA": { - "title": "Band quality data", - "type": "image/tiff", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_BQA.TIF", - "roles": [] - } - }, - "links": [ - { - "rel": "self", - "href": "./LC80140332018166LGN00.json" - }, - { - "rel": "parent", - "href": "../collection.json" - }, - { - "rel": "collection", - "href": "../collection.json" - }, - { - "rel": "root", - "href": "../../catalog.json" - } - ] -} diff --git a/docs/example-catalog/landsat-8-l1/2018-06/LC80300332018166LGN00.json b/docs/example-catalog/landsat-8-l1/2018-06/LC80300332018166LGN00.json deleted file mode 100644 index 4f62ac898..000000000 --- a/docs/example-catalog/landsat-8-l1/2018-06/LC80300332018166LGN00.json +++ /dev/null @@ -1,276 +0,0 @@ -{ - "type": "Feature", - "stac_version" : "1.0.0", - "stac_extensions" : [ - "https://stac-extensions.github.io/eo/v1.1.0/schema.json", - "https://stac-extensions.github.io/view/v1.0.0/schema.json", - "https://stac-extensions.github.io/projection/v1.1.0/schema.json" - ], - "id": "LC80300332018166LGN00", - "bbox": [ - -101.40793, - 37.81084, - -98.6721, - 39.97469 - ], - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [ - -100.84368079413701, - 39.97210491033466 - ], - [ - -98.67492641719046, - 39.54833037653145 - ], - [ - -99.23946071016417, - 37.81370881408165 - ], - [ - -101.40560438472555, - 38.24476872678675 - ], - [ - -100.84368079413701, - 39.97210491033466 - ] - ] - ] - }, - "collection": "landsat-8-l1", - "properties": { - "collection": "landsat-8-l1", - "datetime": "2018-06-15T17:18:03Z", - "view:sun_azimuth": 125.5799919, - "view:sun_elevation": 66.54407242, - "eo:cloud_cover": 0, - "instruments": ["OLI_TIRS"], - "view:off_nadir": 0, - "platform": "landsat-8", - "gsd": 30, - "proj:epsg": 32614, - "proj:transform": [294285.0, 30.0, 0.0, 4425015.0, 0.0, -30], - "proj:geometry": { - "type": "Polygon", - "coordinates": [ - [ - [294285.0, 4187385.0], - [294285.0, 4425015.0], - [528015.0, 4425015.0], - [528015.0, 4187385.0], - [294285.0, 4187385.0] - ] - ] - }, - "proj:shape": [7791, 7921] - }, - "assets": { - "index": { - "type": "text/html", - "title": "HTML index page", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/index.html", - "roles" : [] - }, - "thumbnail": { - "title": "Thumbnail image", - "type": "image/jpeg", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_thumb_large.jpg", - "roles" : [ - "thumbnail" - ] - }, - "B1": { - "type": "image/tiff", - "title": "Band 1 (coastal)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_B1.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B1", - "full_width_half_max" : 0.02, - "center_wavelength" : 0.44, - "common_name" : "coastal" - } - ] - }, - "B2": { - "type": "image/tiff", - "title": "Band 2 (blue)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_B2.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B2", - "full_width_half_max" : 0.06, - "center_wavelength" : 0.48, - "common_name" : "blue" - } - ] - }, - "B3": { - "type": "image/tiff", - "title": "Band 3 (green)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_B3.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B3", - "full_width_half_max" : 0.06, - "center_wavelength" : 0.56, - "common_name" : "green" - } - ] - }, - "B4": { - "type": "image/tiff", - "title": "Band 4 (red)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_B4.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B4", - "full_width_half_max" : 0.04, - "center_wavelength" : 0.65, - "common_name" : "red" - } - ] - }, - "B5": { - "type": "image/tiff", - "title": "Band 5 (nir)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_B5.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B5", - "full_width_half_max" : 0.03, - "center_wavelength" : 0.86, - "common_name" : "nir" - } - ] - }, - "B6": { - "type": "image/tiff", - "title": "Band 6 (swir16)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_B6.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B6", - "full_width_half_max" : 0.08, - "center_wavelength" : 1.6, - "common_name" : "swir16" - } - ] - }, - "B7": { - "type": "image/tiff", - "title": "Band 7 (swir22)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_B7.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B7", - "full_width_half_max" : 0.22, - "center_wavelength" : 2.2, - "common_name" : "swir22" - } - ] - }, - "B8": { - "type": "image/tiff", - "title": "Band 8 (pan)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_B8.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B8", - "full_width_half_max" : 0.18, - "center_wavelength" : 0.59, - "common_name" : "pan" - } - ] - }, - "B9": { - "type": "image/tiff", - "title": "Band 9 (cirrus)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_B9.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B9", - "full_width_half_max" : 0.02, - "center_wavelength" : 1.37, - "common_name" : "cirrus" - } - ] - }, - "B10": { - "type": "image/tiff", - "title": "Band 10 (lwir)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_B10.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B10", - "full_width_half_max" : 0.8, - "center_wavelength" : 10.9, - "common_name" : "lwir11" - } - ] - }, - "B11": { - "type": "image/tiff", - "title": "Band 11 (lwir)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_B11.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B11", - "full_width_half_max" : 1, - "center_wavelength" : 12, - "common_name" : "lwir2" - } - ] - }, - "ANG": { - "title": "Angle coefficients file", - "type": "text/plain", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_ANG.txt", - "roles": [] - }, - "MTL": { - "title": "original metadata file", - "type": "text/plain", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_MTL.txt", - "roles": [] - }, - "BQA": { - "title": "Band quality data", - "type": "image/tiff", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/030/033/LC08_L1TP_030033_20180615_20180703_01_T1/LC08_L1TP_030033_20180615_20180703_01_T1_BQA.TIF", - "roles": [] - } - }, - "links": [ - { - "rel": "self", - "href": "./LC80300332018166LGN00.json" - }, - { - "rel": "parent", - "href": "../collection.json" - }, - { - "rel": "collection", - "href": "../collection.json" - }, - { - "rel": "root", - "href": "../../catalog.json" - } - ] -} diff --git a/docs/example-catalog/landsat-8-l1/2018-07/LC80150332018189LGN00.json b/docs/example-catalog/landsat-8-l1/2018-07/LC80150332018189LGN00.json deleted file mode 100644 index b74e84eef..000000000 --- a/docs/example-catalog/landsat-8-l1/2018-07/LC80150332018189LGN00.json +++ /dev/null @@ -1,275 +0,0 @@ -{ - "type": "Feature", - "id": "LC80150332018189LGN00", - "stac_version" : "1.0.0", - "stac_extensions" : [ - "https://stac-extensions.github.io/eo/v1.1.0/schema.json", - "https://stac-extensions.github.io/view/v1.0.0/schema.json", - "https://stac-extensions.github.io/projection/v1.1.0/schema.json" - ], - "bbox": [ - -78.25028, - 37.79719, - -75.48983, - 39.98757 - ], - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [ - -77.66532657556414, - 39.987421383364385 - ], - [ - -75.49021499188945, - 39.54442448711656 - ], - [ - -76.07747288135147, - 37.799167045362736 - ], - [ - -78.25025639728777, - 38.24897728816149 - ], - [ - -77.66532657556414, - 39.987421383364385 - ] - ] - ] - }, - "collection": "landsat-8-l1", - "properties": { - "collection": "landsat-8-l1", - "datetime": "2018-07-08T15:45:34Z", - "view:sun_azimuth": 125.31095515, - "view:sun_elevation": 65.2014335, - "eo:cloud_cover": 0, - "instruments": ["OLI_TIRS"], - "view:off_nadir": 0, - "platform": "landsat-8", - "gsd": 30, - "proj:epsg": 32618, - "proj:transform": [222285.0, 30.0, 0.0, 4426515.0, 0.0, -30.0], - "proj:geometry": { - "type": "Polygon", - "coordinates": [ - [ - [222285.0, 4187985.0], - [222285.0, 4426515.0], - [456915.0, 4426515.0], - [456915.0, 4187985.0], - [222285.0,4187985.0]] - ] - }, - "proj:shape": [7821, 7951] - }, - "assets": { - "index": { - "type": "text/html", - "title": "HTML index page", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/index.html", - "roles": [] - }, - "thumbnail": { - "title": "Thumbnail image", - "type": "image/jpeg", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_thumb_large.jpg", - "roles" : [ - "thumbnail" - ] - }, - "B1": { - "type": "image/tiff", - "title": "Band 1 (coastal)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_B1.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B1", - "full_width_half_max" : 0.02, - "center_wavelength" : 0.44, - "common_name" : "coastal" - } - ] - }, - "B2": { - "type": "image/tiff", - "title": "Band 2 (blue)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_B2.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B2", - "full_width_half_max" : 0.06, - "center_wavelength" : 0.48, - "common_name" : "blue" - } - ] - }, - "B3": { - "type": "image/tiff", - "title": "Band 3 (green)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_B3.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B3", - "full_width_half_max" : 0.06, - "center_wavelength" : 0.56, - "common_name" : "green" - } - ] - }, - "B4": { - "type": "image/tiff", - "title": "Band 4 (red)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_B4.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B4", - "full_width_half_max" : 0.04, - "center_wavelength" : 0.65, - "common_name" : "red" - } - ] - }, - "B5": { - "type": "image/tiff", - "title": "Band 5 (nir)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_B5.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B5", - "full_width_half_max" : 0.03, - "center_wavelength" : 0.86, - "common_name" : "nir" - } - ] - }, - "B6": { - "type": "image/tiff", - "title": "Band 6 (swir16)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_B6.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B6", - "full_width_half_max" : 0.08, - "center_wavelength" : 1.6, - "common_name" : "swir16" - } - ] - }, - "B7": { - "type": "image/tiff", - "title": "Band 7 (swir22)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_B7.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B7", - "full_width_half_max" : 0.22, - "center_wavelength" : 2.2, - "common_name" : "swir22" - } - ] - }, - "B8": { - "type": "image/tiff", - "title": "Band 8 (pan)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_B8.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B8", - "full_width_half_max" : 0.18, - "center_wavelength" : 0.59, - "common_name" : "pan" - } - ] - }, - "B9": { - "type": "image/tiff", - "title": "Band 9 (cirrus)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_B9.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B9", - "full_width_half_max" : 0.02, - "center_wavelength" : 1.37, - "common_name" : "cirrus" - } - ] - }, - "B10": { - "type": "image/tiff", - "title": "Band 10 (lwir)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_B10.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B10", - "full_width_half_max" : 0.8, - "center_wavelength" : 10.9, - "common_name" : "lwir11" - } - ] - }, - "B11": { - "type": "image/tiff", - "title": "Band 11 (lwir)", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_B11.TIF", - "roles": [], - "eo:bands": [ - { - "name" : "B11", - "full_width_half_max" : 1, - "center_wavelength" : 12, - "common_name" : "lwir2" - } - ] - }, - "ANG": { - "title": "Angle coefficients file", - "type": "text/plain", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_ANG.txt", - "roles": [] - }, - "MTL": { - "title": "original metadata file", - "type": "text/plain", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_MTL.txt", - "roles": [] - }, - "BQA": { - "title": "Band quality data", - "type": "image/tiff", - "href": "https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/015/033/LC08_L1TP_015033_20180708_20180717_01_T1/LC08_L1TP_015033_20180708_20180717_01_T1_BQA.TIF", - "roles": [] - } - }, - "links": [ - { - "rel": "self", - "href": "./LC80150332018189LGN00.json" - }, - { - "rel": "parent", - "href": "../collection.json" - }, - { - "rel": "collection", - "href": "../collection.json" - }, - { - "rel": "root", - "href": "../../catalog.json" - } - ] -} diff --git a/docs/example-catalog/landsat-8-l1/collection.json b/docs/example-catalog/landsat-8-l1/collection.json deleted file mode 100644 index 173a33e4e..000000000 --- a/docs/example-catalog/landsat-8-l1/collection.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "type": "Collection", - "stac_version" : "1.0.0", - "stac_extensions" : [ - "eo", - "view", - "https://example.com/stac/landsat-extension/1.0/schema.json" - ], - "id" : "landsat-8-l1", - "title" : "Landsat 8 L1", - "description" : "Landsat 8 imagery radiometrically calibrated and orthorectified using ground points and Digital Elevation Model (DEM) data to correct relief displacement.", - "keywords" : [ - "landsat", - "earth observation", - "usgs" - ], - "license" : "proprietary", - "providers" : [ - { - "name" : "Development Seed", - "roles" : [ - "processor" - ], - "url" : "https://github.com/sat-utils/sat-api" - } - ], - "extent" : { - "spatial" : { - "bbox" : [ - [ - -180.0, - -90.0, - 180.0, - 90.0 - ] - ] - }, - "temporal" : { - "interval" : [ - [ - "2018-05-21T15:44:59Z", - "2018-07-08T15:45:34Z" - ] - ] - } - }, - "summaries": {}, - "properties" : { - "collection" : "landsat-8-l1", - "instruments" : ["OLI_TIRS"], - "view:sun_azimuth" : 149.01607154, - "eo:bands" : [ - { - "name" : "B1", - "full_width_half_max" : 0.02, - "center_wavelength" : 0.44, - "common_name" : "coastal" - }, - { - "name" : "B2", - "full_width_half_max" : 0.06, - "center_wavelength" : 0.48, - "common_name" : "blue" - }, - { - "name" : "B3", - "full_width_half_max" : 0.06, - "center_wavelength" : 0.56, - "common_name" : "green" - }, - { - "name" : "B4", - "full_width_half_max" : 0.04, - "center_wavelength" : 0.65, - "common_name" : "red" - }, - { - "name" : "B5", - "full_width_half_max" : 0.03, - "center_wavelength" : 0.86, - "common_name" : "nir" - }, - { - "name" : "B6", - "full_width_half_max" : 0.08, - "center_wavelength" : 1.6, - "common_name" : "swir16" - }, - { - "name" : "B7", - "full_width_half_max" : 0.22, - "center_wavelength" : 2.2, - "common_name" : "swir22" - }, - { - "name" : "B8", - "full_width_half_max" : 0.18, - "center_wavelength" : 0.59, - "common_name" : "pan" - }, - { - "name" : "B9", - "full_width_half_max" : 0.02, - "center_wavelength" : 1.37, - "common_name" : "cirrus" - }, - { - "name" : "B10", - "full_width_half_max" : 0.8, - "center_wavelength" : 10.9, - "common_name" : "lwir11" - }, - { - "name" : "B11", - "full_width_half_max" : 1, - "center_wavelength" : 12, - "common_name" : "lwir2" - } - ], - "view:off_nadir" : 0, - "view:azimuth" : 0, - "platform" : "landsat-8", - "gsd" : 15, - "view:sun_elevation" : 59.214247 - }, - "links" : [ - { - "href" : "../catalog.json", - "rel" : "root" - }, - { - "href" : "../catalog.json", - "rel" : "parent" - }, - { - "href" : "./collection.json", - "rel" : "self" - }, - { - "href" : "./2018-06/LC80140332018166LGN00.json", - "rel" : "item" - }, - { - "href" : "./2018-05/LC80150322018141LGN00.json", - "rel" : "item" - }, - { - "href" : "./2018-07/LC80150332018189LGN00.json", - "rel" : "item" - }, - { - "href" : "./2018-06/LC80300332018166LGN00.json", - "rel" : "item" - } - ] -} diff --git a/docs/img b/docs/img new file mode 120000 index 000000000..6ffc6ca9f --- /dev/null +++ b/docs/img @@ -0,0 +1 @@ +../img \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..40bb2bd1d --- /dev/null +++ b/docs/index.md @@ -0,0 +1,59 @@ +# PySTAC + +!!! warning + + These docs are for the work-in-progress v2 of PySTAC. + For the current PySTAC v1 docs, see . + +**PySTAC** is a Python library for reading and writing [SpatioTemporal Asset Catalog (STAC)](https://stacspec.org) metadata. +To install: + + +```shell +python -m pip install pystac +``` + +## Creating + +STAC has three data structure: `Item`, `Catalog`, and `Collection`. +Each can be created with sensible defaults: + +```python +from pystac import Item, Catalog, Collection + +item = Item("an-item-id") +catalog = Item("a-catalog-id", "A catalog description") +collection = Item("a-collection-id", "A collection description") +``` + +## Reading + +Reading STAC from the local filesystem is supported out-of-the-box: + +```python +item = pystac.read_file("item.json") +``` + +To read from remote locations, including HTTP(S) and blob storage, we use [obstore](https://developmentseed.org/obstore/). +Install with that optional dependency: + +```shell +python -m pip install 'pystac[obstore]' +``` + +Then: + +```python +from pystac.obstore import ObstoreReader +reader = ObstoreReader() # provide any configuration values here, e.g. ObstoreReader(aws_region="us-east-1") +item = reader.read_file("s3://bucket/item.json") +``` + +!!! todo + + Add more examples, and maybe put them in a notebook so we can execute them. + +## Supported versions + +PySTAC v2.0 supports STAC v1.0 and STAC v1.1. +For pre-STAC v1.0 versions, use `pystac<2`. diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index e44fb74fa..000000000 --- a/docs/index.rst +++ /dev/null @@ -1,57 +0,0 @@ -PySTAC Documentation -#################### - -PySTAC is a library for working with `SpatioTemporal Asset Catalogs (STAC) -`_ in `Python 3 `_. Some nice features -of PySTAC are: - -* Reading and writing STAC version 1.0. Future versions will read older versions of - STAC, but always write the latest supported version. See :ref:`stac_version_support` - for details. -* In-memory manipulations of STAC catalogs. -* Extend the I/O of STAC metadata to provide support for other platforms (e.g. cloud - providers). -* Easy, efficient crawling of STAC catalogs. STAC objects are only read in when needed. -* Easily write "absolute published", "relative published" and "self-contained" catalogs - as :stac-spec:`described in the best practices documentation - `. - -.. grid:: 1 2 2 2 - :gutter: 2 - - .. grid-item-card:: Get Started - - * :doc:`installation`: Instructions for installing the basic package as well as - extras. - * :doc:`quickstart`: Jupyter notebook tutorial on using PySTAC for reading & - writing STAC catalogs. - - .. grid-item-card:: Go Deeper - - * :doc:`concepts`: Overview of how various concepts and structures from the STAC - Specification are implemented within PySTAC. - * :doc:`tutorials`: In-depth tutorials on using PySTAC for a number of different - applications. - * :doc:`api`: Detailed API documentation of PySTAC classes, methods, and functions. - -Related Projects -================ - -* `pystac-client `__: A Python client for - working with STAC Catalogs and APIs. -* `stactools `__: A command line tool and - library for working with STAC. -* `sat-stac `__: A Python 3 library for reading - and working with existing Spatio-Temporal Asset Catalogs (STAC). *Much of PySTAC - builds on the code and concepts of* ``sat-stac``. - -.. toctree:: - :maxdepth: 2 - :hidden: - - installation - quickstart - concepts - api - tutorials - contributing diff --git a/docs/installation.rst b/docs/installation.rst deleted file mode 100644 index 1f87bab4a..000000000 --- a/docs/installation.rst +++ /dev/null @@ -1,121 +0,0 @@ -Installation -############ - -Install from PyPi (recommended) -=============================== - -.. code-block:: bash - - pip install pystac - -Install from conda-forge -======================== - -.. code-block:: bash - - conda install -c conda-forge pystac - -Install from source -=================== - -.. code-block:: bash - - pip install git+https://github.com/stac-utils/pystac.git - -.. _installation_dependencies: - -Dependencies -============ - -PySTAC requires Python >= 3.10. This project follows the recommendations of -`NEP-29 `__ in deprecating support -for Python versions. This means that users can expect support for Python 3.10 to be -removed from the ``main`` branch after Apr 04, 2025 and therefore from the next release -after that date. - -As a foundational component of the Python STAC ecosystem used in a number of downstream -libraries, PySTAC aims to minimize its dependencies. As a result, the only dependency -for the basic PySTAC library is `python-dateutil -`__. - -PySTAC also has the following extras, which can be optionally installed to provide -additional functionality: - -* ``validation`` - - Installs the additional `jsonschema - `__ dependency. When this - dependency is installed, the :ref:`validation methods ` may be - used to validate STAC objects against the appropriate JSON schemas. - - To install: - - .. code-block:: bash - - pip install pystac[validation] - -* ``orjson`` - - Installs the additional `orjson `__ dependency. When - this dependency is installed, `orjson` will be used as the default JSON - serialization/deserialization for all operations in PySTAC. - - To install: - - .. code-block:: bash - - pip install pystac[orjson] - -* ``urllib3`` - - Installs the additional `urllib3 `__ dependency. - For now, this is only used in :py:class:`pystac.stac_io.RetryStacIO`, but it - may be used more extensively in the future. - - To install: - - .. code-block:: bash - - pip install pystac[urllib3] - -* ``jinja2`` - - Installs the additional `jinja2 `__ dependency. - When this dependency is installed, jupyter notebooks display pretty representations - of PySTAC objects - - To install: - - .. code-block:: bash - - pip install pystac[jinja2] - -Versions -======== - -To install a version of PySTAC that works with a specific versions of the STAC -specification, install the matching version of PySTAC from the following table. - -.. list-table:: - :widths: 50 50 - :header-rows: 1 - - * - PySTAC - - STAC - * - 1.x - - 1.0.x - * - 0.5.x - - 1.0.0-beta.* - * - 0.4.x - - 0.9.x - * - 0.3.x - - 0.8.x - -For instance, to work with STAC v0.9.x: - - .. code-block:: bash - - pip install pystac==0.4.0 - - -STAC spec versions below 0.8 are not supported by PySTAC. diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 451f805a5..000000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/docs/overrides/main.html b/docs/overrides/main.html new file mode 100644 index 000000000..2058d21a6 --- /dev/null +++ b/docs/overrides/main.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block announce %} +

+ 👷 These are work-in-progress docs for PySTAC v2.0, which has not been released. + For the current PySTAC v1 docs, see https://pystac.readthedocs.io. +

+{% endblock %} \ No newline at end of file diff --git a/docs/overrides/stylesheets/extra.css b/docs/overrides/stylesheets/extra.css new file mode 100644 index 000000000..eb430eb28 --- /dev/null +++ b/docs/overrides/stylesheets/extra.css @@ -0,0 +1,4 @@ +:root, +[data-md-color-scheme="default"] { + --md-primary-fg-color: #4cb1ac; +} \ No newline at end of file diff --git a/docs/pystac-v2.0.md b/docs/pystac-v2.0.md new file mode 100644 index 000000000..ff9e5c989 --- /dev/null +++ b/docs/pystac-v2.0.md @@ -0,0 +1,36 @@ +# PySTAC v2.0 + +**PySTAC v2.0** is a ground-up re-write of **PySTAC**. +Our high-level design goals are a form of [Postel's law](https://en.wikipedia.org/wiki/Robustness_principle): + +- Help people make the best STAC possible +- Help people interact with existing (even poorly constructed or invalid) STAC as easily as possible + +To do so, we have some specific implementation strategies. + +- **Keep the core data structure APIs basically the same**: People are used to the basic methods on `Item`, `Catalog`, `Collection`, etc. + We shouldn't change the external APIs unless there's a good reason to. +- **Relax core data structure initializers to accept almost anything**, with warnings if something's being changed to make it valid +- **Stay low-dependency by default**: This keeps our maintenance burden lower, at the cost of having to hand-roll more stuff ourselves. +- **Replace "implementation" APIs**: `Item`, `Catalog`, etc are the "what" of **pystac**. + Things like `StacIO` are the "how". + We should create replacement structures for any of these "how" interfaces that we want to dramatically change, rather than try to "fix" the existing ones. + Backwards compatibility will be _much_ more difficult for these "how" interfaces, so we shouldn't even try. + _If possible_ we should re-write the existing "how" structure (e.g. `StacIO`) to use the new API, but this should be a lower-priority objective. +- **Do fewer things at once**: One of the biggest design problems of PySTAC v1.0 (in this author's opinion) was that many functions tried to be "helpful" by doing a lot of things at once. + When possible, we should simplify methods to do just one thing, and provide intuitive patterns for doing complex operations using multiple method calls. + Top-level functions can be used to "synthesize" complex operations, e.g. `pystac.read_file`. +- **Don't mutate on read** (aka auto-migration) + +## Key changes + +- **stac-geoparquet** and **async** support is provided via **rustac** +- **pystac-client** has been merged in, and is can be used via an optional dependency +- Rather than making `Collection` a subclass of `Catalog`, both `Catalog` and `Collection` are subclasses of an abstract `Container` class. +- `CatalogType` is deprecated and not used. + Instead, we provide arguments like `use_absolute_hrefs` and `include_self_href`. +- `StacIO` is deprecated in favor of `Reader` and `Writer`. + We provide simple wrappers to allow folks to continue using their existing custom `StacIO` classes. +- Link hrefs are now more explicit. + In PySTAC v1, `Link.to_dict()` mutated the `href` based on the `CatalogType` and other factors. + In PySTAC v2, all href mutation is done _before_ serialization. diff --git a/docs/quickstart.ipynb b/docs/quickstart.ipynb deleted file mode 100644 index 430411d2c..000000000 --- a/docs/quickstart.ipynb +++ /dev/null @@ -1,2401 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Quickstart\n", - "\n", - "This notebook is a quick introduction to using PySTAC for reading an existing STAC catalog. For more in-depth examples check out the other tutorials.\n", - "\n", - "## Dependencies\n", - "\n", - "- PySTAC" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Reading a Catalog\n", - "\n", - "[A STAC Catalog](https://github.com/radiantearth/stac-spec/tree/master/catalog-spec) is used to group other STAC objects like Items, Collections, or even other Catalogs.\n", - "\n", - "We will be using a small example catalog adapted from the [example Landsat Collection](https://github.com/geotrellis/geotrellis-server/tree/977bad7a64c409341479c281c8c72222008861fd/stac-example/catalog/landsat-stac-collection) in the [GeoTrellis](https://geotrellis.io) repository. All STAC Items and Collections can be found in the [docs/example-catalog](https://github.com/stac-utils/pystac/tree/main/docs/example-catalog) directory of this repo; all Assets are hosted in the Landsat S3 bucket.\n", - "\n", - "First, we import the PySTAC classes we will be working with." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:53:53.360681Z", - "iopub.status.busy": "2025-09-11T16:53:53.360397Z", - "iopub.status.idle": "2025-09-11T16:53:54.333411Z", - "shell.execute_reply": "2025-09-11T16:53:54.332865Z", - "shell.execute_reply.started": "2025-09-11T16:53:53.360653Z" - } - }, - "outputs": [], - "source": [ - "import shutil\n", - "import tempfile\n", - "from pathlib import Path\n", - "\n", - "from pystac import Catalog, get_stac_version" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we read the example catalog and print some basic metadata." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:53:54.334813Z", - "iopub.status.busy": "2025-09-11T16:53:54.334715Z", - "iopub.status.idle": "2025-09-11T16:53:54.337619Z", - "shell.execute_reply": "2025-09-11T16:53:54.337301Z", - "shell.execute_reply.started": "2025-09-11T16:53:54.334802Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ID: landsat-stac-collection-catalog\n", - "Title: STAC for Landsat data\n", - "Description: STAC for Landsat data\n" - ] - } - ], - "source": [ - "root_catalog = Catalog.from_file(\"./example-catalog/catalog.json\")\n", - "print(f\"ID: {root_catalog.id}\")\n", - "print(f\"Title: {root_catalog.title or 'N/A'}\")\n", - "print(f\"Description: {root_catalog.description or 'N/A'}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "*Note that we do not print the \"stac_version\" here. PySTAC automatically updates any Catalogs to the most recent supported STAC version and will automatically write this to the JSON object during serialization.*\n", - "\n", - "Let's confirm the latest STAC Spec version supported by PySTAC." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:53:54.338227Z", - "iopub.status.busy": "2025-09-11T16:53:54.338113Z", - "iopub.status.idle": "2025-09-11T16:53:54.355305Z", - "shell.execute_reply": "2025-09-11T16:53:54.354458Z", - "shell.execute_reply.started": "2025-09-11T16:53:54.338215Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.1.0\n" - ] - } - ], - "source": [ - "print(get_stac_version())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Crawling Child Catalogs/Collections\n", - "\n", - "[STAC Collections](https://github.com/radiantearth/stac-spec/tree/master/collection-spec) are used to group related Items and provide aggregate or summary metadata for those Items.\n", - "\n", - "STAC Catalogs may have many nested layers of Catalogs or Collections within the top-level collection. Our example catalog has one Collection within the main Catalog at [landsat-8-l1/collection.json](./example-catalog/landsat-8-l1/collection.json). We can list the Collections in a given Catalog using the [Catalog.get_collections](https://pystac.readthedocs.io/en/latest/api.html#pystac.Catalog.get_collections) method. This method returns an iterable of PySTAC [Collection](https://pystac.readthedocs.io/en/latest/api.html#collection) instances, which we will turn into a `list`." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:53:54.355878Z", - "iopub.status.busy": "2025-09-11T16:53:54.355747Z", - "iopub.status.idle": "2025-09-11T16:53:54.359285Z", - "shell.execute_reply": "2025-09-11T16:53:54.358889Z", - "shell.execute_reply.started": "2025-09-11T16:53:54.355865Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Number of collections: 1\n", - "Collections IDs:\n", - "- landsat-8-l1\n" - ] - } - ], - "source": [ - "collections = list(root_catalog.get_collections())\n", - "\n", - "print(f\"Number of collections: {len(collections)}\")\n", - "print(\"Collections IDs:\")\n", - "for collection in collections:\n", - " print(f\"- {collection.id}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's grab that Collection as a PySTAC [Collection](https://pystac.readthedocs.io/en/latest/api.html#collection) instance using the [Catalog.get_child method](https://pystac.readthedocs.io/en/latest/api.html#pystac.Catalog.get_child) so we can look at it in more detail. This method gets a child Catalog or Collection by ID, so we'll use the Collection ID that we printed above. Since this method returns `None` if no child exists with the given ID, we'll check to make sure we actually got the `Collection`." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:53:54.885476Z", - "iopub.status.busy": "2025-09-11T16:53:54.885235Z", - "iopub.status.idle": "2025-09-11T16:53:54.888030Z", - "shell.execute_reply": "2025-09-11T16:53:54.887496Z", - "shell.execute_reply.started": "2025-09-11T16:53:54.885463Z" - } - }, - "outputs": [], - "source": [ - "collection = root_catalog.get_child(\"landsat-8-l1\")\n", - "assert collection is not None" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Crawling Items\n", - "\n", - "[STAC Items](https://github.com/radiantearth/stac-spec/tree/master/item-spec) are the fundamental building blocks of a STAC Catalog. Each Item represents a single spatiotemporal resource (e.g. a satellite scene).\n", - "\n", - "Both Catalogs and Collections may have Items associated with them. Let's crawl our catalog, starting at the root, to see what Items we have. The [Catalog.get_items method](https://pystac.readthedocs.io/en/latest/api.html#pystac.Catalog.get_items) provides a convenient way of recursively listing all Items associated with a Catalog and all of its sub-Catalogs by including the `recursive=True` option." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:53:55.501459Z", - "iopub.status.busy": "2025-09-11T16:53:55.500987Z", - "iopub.status.idle": "2025-09-11T16:53:55.512529Z", - "shell.execute_reply": "2025-09-11T16:53:55.511976Z", - "shell.execute_reply.started": "2025-09-11T16:53:55.501423Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Number of items: 4\n", - "- LC80140332018166LGN00\n", - "- LC80150322018141LGN00\n", - "- LC80150332018189LGN00\n", - "- LC80300332018166LGN00\n" - ] - } - ], - "source": [ - "items = list(root_catalog.get_items(recursive=True))\n", - "\n", - "print(f\"Number of items: {len(items)}\")\n", - "for item in items:\n", - " print(f\"- {item.id}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "These IDs are not very descriptive; in the next section, we will take a look at how we can access the rich metadata associated with each Item." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Item Metadata\n", - "\n", - "Items can have *a lot* of metadata. This can be a bit overwhelming at first, but break the metadata fields down into a few categories:\n", - "\n", - "- Core Item Metadata\n", - "- Common Metadata\n", - "- STAC Extensions\n", - "\n", - "We will walk through each of these metadata categories in the following sections. \n", - "\n", - "First, let's grab one of the Items using the [Catalog.get_items method](https://pystac.readthedocs.io/en/latest/api.html#pystac.Catalog.get_items). We will use `recursive=True` to recursively crawl all child Catalogs and/or Collections to find the Item." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:53:56.581966Z", - "iopub.status.busy": "2025-09-11T16:53:56.581591Z", - "iopub.status.idle": "2025-09-11T16:53:56.586208Z", - "shell.execute_reply": "2025-09-11T16:53:56.585346Z", - "shell.execute_reply.started": "2025-09-11T16:53:56.581933Z" - } - }, - "outputs": [], - "source": [ - "item = next(root_catalog.get_items(\"LC80140332018166LGN00\", recursive=True))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Core Item Metadata\n", - "\n", - "The core Item metadata fields include spatiotemporal information and the ID of the collection to which the Item belongs. These fields are all at the top level of the Item JSON and we can access them through attributes on the [PySTAC Item](https://pystac.readthedocs.io/en/latest/api.html#item) instance." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:53:57.893519Z", - "iopub.status.busy": "2025-09-11T16:53:57.893024Z", - "iopub.status.idle": "2025-09-11T16:53:57.902625Z", - "shell.execute_reply": "2025-09-11T16:53:57.901939Z", - "shell.execute_reply.started": "2025-09-11T16:53:57.893480Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'type': 'Polygon',\n", - " 'coordinates': [[[-76.12180471942207, 39.95810181489563],\n", - " [-73.94910518227414, 39.55117185146004],\n", - " [-74.49564725552679, 37.826064511480496],\n", - " [-76.66550404911956, 38.240699151776084],\n", - " [-76.12180471942207, 39.95810181489563]]]}" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.geometry" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:53:58.207488Z", - "iopub.status.busy": "2025-09-11T16:53:58.207041Z", - "iopub.status.idle": "2025-09-11T16:53:58.213346Z", - "shell.execute_reply": "2025-09-11T16:53:58.212399Z", - "shell.execute_reply.started": "2025-09-11T16:53:58.207451Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[-76.66703, 37.82561, -73.94861, 39.95958]" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.bbox" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:53:58.485290Z", - "iopub.status.busy": "2025-09-11T16:53:58.484894Z", - "iopub.status.idle": "2025-09-11T16:53:58.489053Z", - "shell.execute_reply": "2025-09-11T16:53:58.488573Z", - "shell.execute_reply.started": "2025-09-11T16:53:58.485269Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "datetime.datetime(2018, 6, 15, 15, 39, 9, tzinfo=tzutc())" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.datetime" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:53:58.703103Z", - "iopub.status.busy": "2025-09-11T16:53:58.702620Z", - "iopub.status.idle": "2025-09-11T16:53:58.709779Z", - "shell.execute_reply": "2025-09-11T16:53:58.708528Z", - "shell.execute_reply.started": "2025-09-11T16:53:58.703063Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'landsat-8-l1'" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.collection_id" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we want the actual `Collection` instance instead of just the ID, we can use the [Item.get_collection](https://pystac.readthedocs.io/en/latest/api.html#pystac.Item.get_collection) method." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:53:59.325563Z", - "iopub.status.busy": "2025-09-11T16:53:59.325121Z", - "iopub.status.idle": "2025-09-11T16:53:59.354690Z", - "shell.execute_reply": "2025-09-11T16:53:59.354062Z", - "shell.execute_reply.started": "2025-09-11T16:53:59.325525Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - " <Collection id=landsat-8-l1>\n", - "
\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.get_collection()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Common Metadata\n", - "\n", - "Certain fields that are commonly used in Items, but may also be found in other objects (e.g. Assets) are defined in the [Common Metadata](https://github.com/radiantearth/stac-spec/blob/master/item-spec/common-metadata.md) section of the spec. These include licensing and instrument information, descriptions of datetime ranges, and some other common fields. These properties can be found as attributes of the `Item.common_metadata` property, which is an instance of the [CommonMetadata class](https://pystac.readthedocs.io/en/latest/api.html#pystac.CommonMetadata)." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:12.242671Z", - "iopub.status.busy": "2025-09-11T16:54:12.241943Z", - "iopub.status.idle": "2025-09-11T16:54:12.249301Z", - "shell.execute_reply": "2025-09-11T16:54:12.248687Z", - "shell.execute_reply.started": "2025-09-11T16:54:12.242599Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "['OLI_TIRS']" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.common_metadata.instruments" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:12.740988Z", - "iopub.status.busy": "2025-09-11T16:54:12.740804Z", - "iopub.status.idle": "2025-09-11T16:54:12.744503Z", - "shell.execute_reply": "2025-09-11T16:54:12.744071Z", - "shell.execute_reply.started": "2025-09-11T16:54:12.740974Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'landsat-8'" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.common_metadata.platform" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:13.012531Z", - "iopub.status.busy": "2025-09-11T16:54:13.012152Z", - "iopub.status.idle": "2025-09-11T16:54:13.018730Z", - "shell.execute_reply": "2025-09-11T16:54:13.017766Z", - "shell.execute_reply.started": "2025-09-11T16:54:13.012500Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "30" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.common_metadata.gsd" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### STAC Extensions\n", - "\n", - "[STAC Extensions](https://stac-extensions.github.io/) are a mechanism for providing additional metadata not covered by the core STAC Spec. We can see which STAC Extensions are implemented by this particular Item by examining the list of extension URIs in the `stac_extensions` field." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:13.867162Z", - "iopub.status.busy": "2025-09-11T16:54:13.866693Z", - "iopub.status.idle": "2025-09-11T16:54:13.874348Z", - "shell.execute_reply": "2025-09-11T16:54:13.872895Z", - "shell.execute_reply.started": "2025-09-11T16:54:13.867127Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "['https://stac-extensions.github.io/eo/v1.1.0/schema.json',\n", - " 'https://stac-extensions.github.io/view/v1.0.0/schema.json',\n", - " 'https://stac-extensions.github.io/projection/v2.0.0/schema.json']" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.stac_extensions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This Item implements the [Electro-Optical](https://github.com/stac-extensions/eo), [View Geometry](https://github.com/stac-extensions/view), and [Projection](https://github.com/stac-extensions/projection) Extensions. \n", - "\n", - "We can also check if a specific extension is implemented using [ext.has](https://pystac.readthedocs.io/en/latest/api.html#pystac.item.ext.has) with the name of that extension." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:14.391585Z", - "iopub.status.busy": "2025-09-11T16:54:14.391134Z", - "iopub.status.idle": "2025-09-11T16:54:14.404483Z", - "shell.execute_reply": "2025-09-11T16:54:14.403721Z", - "shell.execute_reply.started": "2025-09-11T16:54:14.391548Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.ext.has(\"eo\")" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:14.648775Z", - "iopub.status.busy": "2025-09-11T16:54:14.648176Z", - "iopub.status.idle": "2025-09-11T16:54:14.656069Z", - "shell.execute_reply": "2025-09-11T16:54:14.654806Z", - "shell.execute_reply.started": "2025-09-11T16:54:14.648726Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.ext.has(\"raster\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can access fields associated with the extension as attributes on the extension instance. For instance, the [\"eo:cloud_cover\" field](https://github.com/stac-extensions/eo#item-properties-or-asset-fields) defined in the Electro-Optical Extension can be accessed using the [item.ext.eo.cloud_cover](https://pystac.readthedocs.io/en/latest/api.html#pystac.extensions.eo.EOExtension.cloud_cover) attribute." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:15.194408Z", - "iopub.status.busy": "2025-09-11T16:54:15.193992Z", - "iopub.status.idle": "2025-09-11T16:54:15.198657Z", - "shell.execute_reply": "2025-09-11T16:54:15.198163Z", - "shell.execute_reply.started": "2025-09-11T16:54:15.194374Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "22" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.ext.eo.cloud_cover" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also access the cloud cover field directly in the Item properties." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:15.791443Z", - "iopub.status.busy": "2025-09-11T16:54:15.791262Z", - "iopub.status.idle": "2025-09-11T16:54:15.795077Z", - "shell.execute_reply": "2025-09-11T16:54:15.794406Z", - "shell.execute_reply.started": "2025-09-11T16:54:15.791428Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "22" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.properties[\"eo:cloud_cover\"]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can access the Item's assets through the `assets` attribute, which is a dictionary:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:16.454670Z", - "iopub.status.busy": "2025-09-11T16:54:16.454490Z", - "iopub.status.idle": "2025-09-11T16:54:16.458930Z", - "shell.execute_reply": "2025-09-11T16:54:16.457847Z", - "shell.execute_reply.started": "2025-09-11T16:54:16.454656Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "index: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/index.html (text/html)\n", - "thumbnail: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_thumb_large.jpg (image/jpeg)\n", - "B1: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B1.TIF (image/tiff)\n", - "B2: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B2.TIF (image/tiff)\n", - "B3: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B3.TIF (image/tiff)\n", - "B4: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B4.TIF (image/tiff)\n", - "B5: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B5.TIF (image/tiff)\n", - "B6: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B6.TIF (image/tiff)\n", - "B7: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B7.TIF (image/tiff)\n", - "B8: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B8.TIF (image/tiff)\n", - "B9: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B9.TIF (image/tiff)\n", - "B10: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B10.TIF (image/tiff)\n", - "B11: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B11.TIF (image/tiff)\n", - "ANG: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_ANG.txt (text/plain)\n", - "MTL: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_MTL.txt (text/plain)\n", - "BQA: https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_BQA.TIF (image/tiff)\n" - ] - } - ], - "source": [ - "for asset_key in item.assets:\n", - " asset = item.assets[asset_key]\n", - " print(\"{}: {} ({})\".format(asset_key, asset.href, asset.media_type))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use the `to_dict()` method to convert an Asset, or any PySTAC object, into a dictionary:" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:17.378612Z", - "iopub.status.busy": "2025-09-11T16:54:17.378438Z", - "iopub.status.idle": "2025-09-11T16:54:17.382463Z", - "shell.execute_reply": "2025-09-11T16:54:17.382127Z", - "shell.execute_reply.started": "2025-09-11T16:54:17.378598Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'href': 'https://s3-us-west-2.amazonaws.com/landsat-pds/c1/L8/014/033/LC08_L1TP_014033_20180615_20180703_01_T1/LC08_L1TP_014033_20180615_20180703_01_T1_B3.TIF',\n", - " 'type': 'image/tiff',\n", - " 'title': 'Band 3 (green)',\n", - " 'eo:bands': [{'name': 'B3',\n", - " 'full_width_half_max': 0.06,\n", - " 'center_wavelength': 0.56,\n", - " 'common_name': 'green'}],\n", - " 'roles': []}" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "asset = item.assets[\"B3\"]\n", - "asset.to_dict()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we use the eo extension to get the band information for the asset:" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:17.948428Z", - "iopub.status.busy": "2025-09-11T16:54:17.948185Z", - "iopub.status.idle": "2025-09-11T16:54:17.951893Z", - "shell.execute_reply": "2025-09-11T16:54:17.951535Z", - "shell.execute_reply.started": "2025-09-11T16:54:17.948409Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bands = asset.ext.eo.bands\n", - "bands" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:18.228225Z", - "iopub.status.busy": "2025-09-11T16:54:18.227811Z", - "iopub.status.idle": "2025-09-11T16:54:18.234103Z", - "shell.execute_reply": "2025-09-11T16:54:18.233361Z", - "shell.execute_reply.started": "2025-09-11T16:54:18.228191Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'name': 'B3',\n", - " 'full_width_half_max': 0.06,\n", - " 'center_wavelength': 0.56,\n", - " 'common_name': 'green'}" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bands[0].to_dict()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Writing STAC Objects\n", - "\n", - "We can also use PySTAC to create and/or update STAC objects and write them to disk. This Quickstart Tutorial will introduce you to some very basic concepts in writing STAC objects; for a more thorough tutorial, please see the [\"How to create STAC Catalogs\"](./tutorials/how-to-create-stac-catalogs.ipynb) tutorial.\n", - "\n", - "Suppose there was a mistake in the cloud cover value that we looked at earlier and that we would like to add a value for the `instrument` field, which is currently null. We can update these values using the same attributes and properties as before, then save the entire catalog to our local drive." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:18.881422Z", - "iopub.status.busy": "2025-09-11T16:54:18.880964Z", - "iopub.status.idle": "2025-09-11T16:54:18.885045Z", - "shell.execute_reply": "2025-09-11T16:54:18.884240Z", - "shell.execute_reply.started": "2025-09-11T16:54:18.881397Z" - } - }, - "outputs": [], - "source": [ - "new_catalog = root_catalog.clone()" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:19.363337Z", - "iopub.status.busy": "2025-09-11T16:54:19.362517Z", - "iopub.status.idle": "2025-09-11T16:54:19.370157Z", - "shell.execute_reply": "2025-09-11T16:54:19.368415Z", - "shell.execute_reply.started": "2025-09-11T16:54:19.363224Z" - } - }, - "outputs": [], - "source": [ - "item_to_update = next(root_catalog.get_items(\"LC80140332018166LGN00\", recursive=True))\n", - "\n", - "# Update the cloud cover\n", - "item_to_update.ext.eo.cloud_cover = 30\n", - "\n", - "# Add the instrument field\n", - "item_to_update.common_metadata.instruments = [\"LANDSAT\"]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can examine the Item properties directly to verify that the changes have taken effect." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:20.012978Z", - "iopub.status.busy": "2025-09-11T16:54:20.012710Z", - "iopub.status.idle": "2025-09-11T16:54:20.016632Z", - "shell.execute_reply": "2025-09-11T16:54:20.015924Z", - "shell.execute_reply.started": "2025-09-11T16:54:20.012957Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "New Cloud Cover: 30\n", - "New Instruments: ['LANDSAT']\n" - ] - } - ], - "source": [ - "print(f\"New Cloud Cover: {item_to_update.properties['eo:cloud_cover']}\")\n", - "print(f\"New Instruments: {item_to_update.properties['instruments']}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We will write this updated catalog to a temporary directory in our local drive using the [Catalog.normalize_and_save](https://pystac.readthedocs.io/en/latest/api.html#pystac.Catalog.normalize_and_save) method." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:20.831120Z", - "iopub.status.busy": "2025-09-11T16:54:20.830559Z", - "iopub.status.idle": "2025-09-11T16:54:20.837226Z", - "shell.execute_reply": "2025-09-11T16:54:20.835531Z", - "shell.execute_reply.started": "2025-09-11T16:54:20.831076Z" - } - }, - "outputs": [], - "source": [ - "# Create a temporary directory\n", - "tmp_dir = tempfile.mkdtemp()" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:21.086173Z", - "iopub.status.busy": "2025-09-11T16:54:21.085645Z", - "iopub.status.idle": "2025-09-11T16:54:21.095138Z", - "shell.execute_reply": "2025-09-11T16:54:21.094659Z", - "shell.execute_reply.started": "2025-09-11T16:54:21.086132Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Catalog saved to: /tmp/tmpmj8y75bk/catalog.json\n" - ] - } - ], - "source": [ - "# Save the catalog and normalize all paths\n", - "new_catalog.normalize_and_save(tmp_dir)\n", - "print(f\"Catalog saved to: {new_catalog.get_self_href()}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can open up Item that we just updated to verify that the new values were written to disk." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:22.007363Z", - "iopub.status.busy": "2025-09-11T16:54:22.006940Z", - "iopub.status.idle": "2025-09-11T16:54:22.009943Z", - "shell.execute_reply": "2025-09-11T16:54:22.009436Z", - "shell.execute_reply.started": "2025-09-11T16:54:22.007328Z" - } - }, - "outputs": [], - "source": [ - "item_path = Path(tmp_dir) / \"landsat-8-l1\" / \"LC80140332018166LGN00\" / \"\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, we clean up the temporary directory." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T16:54:25.139751Z", - "iopub.status.busy": "2025-09-11T16:54:25.139555Z", - "iopub.status.idle": "2025-09-11T16:54:25.143090Z", - "shell.execute_reply": "2025-09-11T16:54:25.141941Z", - "shell.execute_reply.started": "2025-09-11T16:54:25.139737Z" - } - }, - "outputs": [], - "source": [ - "shutil.rmtree(tmp_dir, ignore_errors=True)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.12" - }, - "vscode": { - "interpreter": { - "hash": "28618a729221ed2dc6301bcedf20e90b9d193b9b884dd15c675da71a09b73fa8" - } - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials.rst b/docs/tutorials.rst deleted file mode 100644 index 91b5159b9..000000000 --- a/docs/tutorials.rst +++ /dev/null @@ -1,71 +0,0 @@ -.. _tutorials: - -Tutorials -######### - -PySTAC Introduction -------------------- - -- :tutorial:`GitHub version ` -- :ref:`Docs version ` - -This tutorial gives an introduction to PySTAC concepts through code examples. - -How to read data from STAC --------------------------- - -- :tutorial:`GitHub version ` -- :ref:`Docs version ` - -This tutorial shows how to read data from PySTAC into xarray. - -PySTAC SpaceNet tutorial ------------------------- - -- :tutorial:`GitHub version ` -- :ref:`Docs version ` - -This tutorial shows how to create and manipulate a STAC of `SpaceNet `_ data. - -How to create STAC Catalogs with PySTAC ---------------------------------------- - -- :tutorial:`GitHub version ` -- :ref:`Docs version ` - -This was a tutorial that was part of a 30 minute presentation at the `community STAC -sprint -`_ -in Arlington, VA in November 2019. It runs through creating a STAC of images -from the `SpaceNet 5 `_ dataset. - -Creating a Landsat 8 STAC -------------------------- - -- :tutorial:`GitHub version ` -- :ref:`Docs version ` - -This tutorial was presented at [Cloud Native Geospatial Outreach -Day](https://sites.google.com/radiant.earth/cng-agenda/) on September 8th, 2020. It -shows how to create a STAC collection from a subset of Landsat 8 scenes over a location. - -Adding New and Custom Extensions --------------------------------- - -- :tutorial:`GitHub version ` -- :ref:`Docs version ` - -This tutorial goes over how to contribute new extensions to PySTAC as well as how to -implement your own custom extensions. - -.. toctree:: - :hidden: - :maxdepth: 2 - :glob: - - tutorials/pystac-introduction.ipynb - tutorials/how-to-read-data-from-stac.ipynb - tutorials/pystac-spacenet-tutorial.ipynb - tutorials/how-to-create-stac-catalogs.ipynb - tutorials/creating-a-landsat-stac.ipynb - tutorials/adding-new-and-custom-extensions.ipynb diff --git a/docs/tutorials/adding-new-and-custom-extensions.ipynb b/docs/tutorials/adding-new-and-custom-extensions.ipynb deleted file mode 100644 index df1ee2c3c..000000000 --- a/docs/tutorials/adding-new-and-custom-extensions.ipynb +++ /dev/null @@ -1,444 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Adding New and Custom Extensions\n", - "\n", - "This tutorial will cover using the `PropertiesExtension` and `ExtensionManagementMixin` classes in `pystac.extensions.base` to implement a new extension in PySTAC, and how to make that class accessible via the `pystac.Item.ext` interface.\n", - "\n", - "For this exercise, we will implement an imaginary Order Request Extension that allows us to track an internal order ID associated with a given satellite image, as well as the history of that imagery order. This use-case is specific enough that it would probably not be a good candidate for an actual STAC Extension, but it gives us an opportunity to highlight some of the key aspects and patterns used in implementing STAC Extensions in PySTAC." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First, we import the PySTAC modules and classes that we will be using throughout the tutorial." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from datetime import datetime, timedelta\n", - "from pprint import pprint\n", - "from typing import Any, Dict, List, Literal, Optional, Union\n", - "from uuid import uuid4\n", - "\n", - "import pystac\n", - "from pystac.extensions.base import ExtensionManagementMixin, PropertiesExtension\n", - "from pystac.utils import (\n", - " StringEnum,\n", - " datetime_to_str,\n", - " get_required,\n", - " map_opt,\n", - " str_to_datetime,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Define the Extension" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Our extension will extend STAC Items by adding the following properties:\n", - "\n", - "- `order:id`: A unique string ID associated with the internal order for this image. This field will be required.\n", - "- `order:history`: A chronological list of events associated with this order. Each of these \"events\" will have a timestamp and an event type, which will be one of the following: `submitted`, `started_processing`, `delivered`, `cancelled`. This field will be optional." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create Extension Classes\n", - "\n", - "Let's start by creating a class to represent the order history events." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "class OrderEventType(StringEnum):\n", - " SUBMITTED = \"submitted\"\n", - " STARTED_PROCESSING = \"started_processing\"\n", - " DELIVERED = \"delivered\"\n", - " CANCELLED = \"cancelled\"\n", - "\n", - "\n", - "class OrderEvent:\n", - " properties: Dict[str, Any]\n", - "\n", - " def __init__(self, properties: Dict[str, Any]) -> None:\n", - " self.properties = properties\n", - "\n", - " @property\n", - " def event_type(self) -> OrderEventType:\n", - " return get_required(self.properties.get(\"type\"), self, \"event_type\")\n", - "\n", - " @event_type.setter\n", - " def event_type(self, v: OrderEventType) -> None:\n", - " self.properties[\"type\"] = str(v)\n", - "\n", - " @property\n", - " def timestamp(self) -> datetime:\n", - " return str_to_datetime(\n", - " get_required(self.properties.get(\"timestamp\"), self, \"timestamp\")\n", - " )\n", - "\n", - " @timestamp.setter\n", - " def timestamp(self, v: datetime) -> None:\n", - " self.properties[\"timestamp\"] = datetime_to_str(v)\n", - "\n", - " def __repr__(self) -> str:\n", - " return f\"\"\n", - "\n", - " def apply(\n", - " self,\n", - " event_type: OrderEventType,\n", - " timestamp: datetime,\n", - " ) -> None:\n", - " self.event_type = event_type\n", - " self.timestamp = timestamp\n", - "\n", - " @classmethod\n", - " def create(\n", - " cls,\n", - " event_type: OrderEventType,\n", - " timestamp: datetime,\n", - " ) -> \"OrderEvent\":\n", - " oe = cls({})\n", - " oe.apply(event_type=event_type, timestamp=timestamp)\n", - " return oe\n", - "\n", - " def to_dict(self) -> Dict[str, Any]:\n", - " return self.properties" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A few important notes about how we constructed this:\n", - "\n", - "- We used PySTAC's [StringEnum class](https://pystac.readthedocs.io/en/latest/api/utils.html#pystac.utils.StringEnum), which inherits from the Python [Enum](https://docs.python.org/3/library/enum.html) class, to capture the allowed event type values. This class has built-in methods that will convert these instances to strings when serializing STAC Items to JSON.\n", - "- We use property getters and setters to manipulate a `properties` dictionary in our `OrderEvent` class. We will see later how this pattern allows us to mutate Item property dictionaries in-place so that updates to the `OrderEvent` object are synced to the Item they extend.\n", - "- The `timestamp` property is converted to a string before it is saved in the `properties` dictionary. This ensures that dictionary is always JSON-serializable but allows us to work with the values as a Python `datetime` instance when using the property getter.\n", - "- We use `event_type` as our property name so that we do not shadow the built-in `type` function in the `apply` method. However, this values is stored under the desired `\"type\"` key in the underlying `properties` dictionary." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we will create a new class inheriting from `PropertiesExtension` and `ExtensionManagementMixin`. Since this class only extends `pystac.Item` instance, we do not need to make it [generic](https://docs.python.org/3/library/typing.html#typing.Generic). If you were creating an extension that applied to multiple object types (e.g. `pystac.Item` and `pystac.Asset`) then you would need to inherit from `typing.Generic` as well and create concrete extension classed for each of these object types (see the [EOExtension](https://github.com/stac-utils/pystac/blob/3c5176f178a4345cb50d5dab83f1dab504ed2682/pystac/extensions/eo.py#L279), [ItemEOExtension](https://github.com/stac-utils/pystac/blob/3c5176f178a4345cb50d5dab83f1dab504ed2682/pystac/extensions/eo.py#L385), and [AssetEOExtension](https://github.com/stac-utils/pystac/blob/3c5176f178a4345cb50d5dab83f1dab504ed2682/pystac/extensions/eo.py#L429) classes for an example of this implementation)." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "SCHEMA_URI: str = \"https://example.com/image-order/v1.0.0/schema.json\"\n", - "PREFIX: str = \"order:\"\n", - "ID_PROP: str = PREFIX + \"id\"\n", - "HISTORY_PROP: str = PREFIX + \"history\"\n", - "\n", - "\n", - "class OrderExtension(\n", - " PropertiesExtension, ExtensionManagementMixin[Union[pystac.Item, pystac.Collection]]\n", - "):\n", - " name: Literal[\"order\"] = \"order\"\n", - "\n", - " def __init__(self, item: pystac.Item):\n", - " self.item = item\n", - " self.properties = item.properties\n", - "\n", - " def apply(\n", - " self, order_id: str = None, history: Optional[List[OrderEvent]] = None\n", - " ) -> None:\n", - " self.order_id = order_id\n", - " self.history = history\n", - "\n", - " @property\n", - " def order_id(self) -> str:\n", - " return get_required(self._get_property(ID_PROP, str), self, ID_PROP)\n", - "\n", - " @order_id.setter\n", - " def order_id(self, v: str) -> None:\n", - " self._set_property(ID_PROP, v, pop_if_none=False)\n", - "\n", - " @property\n", - " def history(self) -> Optional[List[OrderEvent]]:\n", - " return map_opt(\n", - " lambda history: [OrderEvent(d) for d in history],\n", - " self._get_property(HISTORY_PROP, List[OrderEvent]),\n", - " )\n", - "\n", - " @history.setter\n", - " def history(self, v: Optional[List[OrderEvent]]) -> None:\n", - " self._set_property(\n", - " HISTORY_PROP,\n", - " map_opt(lambda history: [event.to_dict() for event in history], v),\n", - " pop_if_none=True,\n", - " )\n", - "\n", - " @classmethod\n", - " def get_schema_uri(cls) -> str:\n", - " return SCHEMA_URI\n", - "\n", - " @classmethod\n", - " def ext(cls, obj: pystac.Item, add_if_missing: bool = False) -> \"OrderExtension\":\n", - " if isinstance(obj, pystac.Item):\n", - " cls.ensure_has_extension(obj, add_if_missing)\n", - " return OrderExtension(obj)\n", - " else:\n", - " raise pystac.ExtensionTypeError(\n", - " f\"OrderExtension does not apply to type '{type(obj).__name__}'\"\n", - " )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As with the `OrderEvent` class, we use property getters and setters for our extension fields (the `PropertiesExtension` class has a `properties` attribute where these are stored). Rather than setting these values directly in the dictionary, we use the `_get_property` and `_set_property` methods that are built into the `PropertiesExtension` class). We also add an `ext` method that will be used to extend `pystac.Item` instances, and a `get_schema_uri` method that is required for all `PropertiesExtension` classes." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Use the Extension\n", - "\n", - "Let's try using our new classes to extend an `Item` and access the extension properties. We'll start by loading the core Item example from the STAC spec examples [here](https://github.com/radiantearth/stac-spec/blob/master/examples/core-item.json) and printing the existing properties." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'title': 'Core Item',\n", - " 'description': 'A sample STAC Item that includes examples of all common metadata',\n", - " 'datetime': None,\n", - " 'start_datetime': '2020-12-11T22:38:32.125Z',\n", - " 'end_datetime': '2020-12-11T22:38:32.327Z',\n", - " 'created': '2020-12-12T01:48:13.725Z',\n", - " 'updated': '2020-12-12T01:48:13.725Z',\n", - " 'platform': 'cool_sat1',\n", - " 'instruments': ['cool_sensor_v1'],\n", - " 'constellation': 'ion',\n", - " 'mission': 'collection 5624',\n", - " 'gsd': 0.512}" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item = pystac.read_file(\n", - " \"https://raw.githubusercontent.com/radiantearth/stac-spec/master/examples/core-item.json\"\n", - ")\n", - "item.properties" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, let's verify that this Item does not implement our new Order Extension yet and that it does not already contain any of our Order Extension properties." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Implements Extension: False\n", - "Order ID: None\n", - "History:\n" - ] - } - ], - "source": [ - "print(f\"Implements Extension: {OrderExtension.has_extension(item)}\")\n", - "print(f\"Order ID: {item.properties.get(ID_PROP)}\")\n", - "print(\"History:\")\n", - "for event in item.properties.get(HISTORY_PROP, []):\n", - " pprint(event)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As expected, this Item does not implement the extension (i.e. the schema URI is not in the Item's `stac_extensions` list). Let's add it, create an instance of `OrderExtension` that extends the `Item`, and add some values for our extension fields." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "order_ext = OrderExtension.ext(item, add_if_missing=True)\n", - "\n", - "# Create a unique string ID for the order ID\n", - "order_ext.order_id = str(uuid4())\n", - "\n", - "# Create some fake order history and set it using the extension\n", - "event_1 = OrderEvent.create(\n", - " event_type=OrderEventType.SUBMITTED, timestamp=datetime.now() - timedelta(days=1)\n", - ")\n", - "event_2 = OrderEvent.create(\n", - " event_type=OrderEventType.STARTED_PROCESSING,\n", - " timestamp=datetime.now() - timedelta(hours=12),\n", - ")\n", - "event_3 = OrderEvent.create(\n", - " event_type=OrderEventType.DELIVERED, timestamp=datetime.now() - timedelta(hours=1)\n", - ")\n", - "order_ext.history = [event_1, event_2, event_3]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's check to see if these values were written to our Item properties." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Implements Extension: True\n", - "Order ID: 7a206229-78f0-46cb-afc2-acf45e14afab\n", - "History:\n", - "{'timestamp': '2023-10-11T11:21:50.989315Z', 'type': 'submitted'}\n", - "{'timestamp': '2023-10-11T23:21:50.989372Z', 'type': 'started_processing'}\n", - "{'timestamp': '2023-10-12T10:21:50.989403Z', 'type': 'delivered'}\n" - ] - } - ], - "source": [ - "print(f\"Implements Extension: {OrderExtension.has_extension(item)}\")\n", - "print(f\"Order ID: {item.properties.get(ID_PROP)}\")\n", - "print(\"History:\")\n", - "for event in item.properties.get(HISTORY_PROP, []):\n", - " pprint(event)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## (Optional) Add access via `Item.ext`\n", - "\n", - "_This applies if you are planning on opening a Pull Request to add this implementation of the extension class to the pystac library_\n", - "\n", - "Now that you have a complete extension class, you can add access to it via the `pystac.Item.ext` interface by following these steps:\n", - "\n", - "1) Make sure that your Extension class has a `name` attribute with `Literal()` as the type.\n", - "2) Import your Extension class in `pystac/extensions/ext.py`\n", - "3) Add the `name` to `EXTENSION_NAMES`\n", - "4) Add the mapping from name to class to `EXTENSION_NAME_MAPPING`\n", - "5) Add a getter method to the Ext class for any object type that this extension works with.\n", - "\n", - "Here is an example of the diff:\n", - "\n", - "```diff\n", - "diff --git a/pystac/extensions/ext.py b/pystac/extensions/ext.py\n", - "index 93a30fe..2dbe5ca 100644\n", - "--- a/pystac/extensions/ext.py\n", - "+++ b/pystac/extensions/ext.py\n", - "@@ -9,6 +9,7 @@ from pystac.extensions.file import FileExtension\n", - " from pystac.extensions.grid import GridExtension\n", - " from pystac.extensions.item_assets import AssetDefinition, ItemAssetsExtension\n", - " from pystac.extensions.mgrs import MgrsExtension\n", - "+from pystac.extensions.order import OrderExtension\n", - " from pystac.extensions.pointcloud import PointcloudExtension\n", - " from pystac.extensions.projection import ProjectionExtension\n", - " from pystac.extensions.raster import RasterExtension\n", - "@@ -32,6 +33,7 @@ EXTENSION_NAMES = Literal[\n", - " \"grid\",\n", - " \"item_assets\",\n", - " \"mgrs\",\n", - "+ \"order\",\n", - " \"pc\",\n", - " \"proj\",\n", - " \"raster\",\n", - "@@ -54,6 +56,7 @@ EXTENSION_NAME_MAPPING: Dict[EXTENSION_NAMES, Any] = {\n", - " GridExtension.name: GridExtension,\n", - " ItemAssetsExtension.name: ItemAssetsExtension,\n", - " MgrsExtension.name: MgrsExtension,\n", - "+ OrderExtension.name: OrderExtension,\n", - " PointcloudExtension.name: PointcloudExtension,\n", - " ProjectionExtension.name: ProjectionExtension,\n", - " RasterExtension.name: RasterExtension,\n", - "@@ -150,6 +153,10 @@ class ItemExt:\n", - " def mgrs(self) -> MgrsExtension:\n", - " return MgrsExtension.ext(self.stac_object)\n", - " \n", - "+ @property\n", - "+ def order(self) -> OrderExtension:\n", - "+ return OrderExtension.ext(self.stac_object)\n", - "+\n", - " @property\n", - " def pc(self) -> PointcloudExtension[pystac.Item]:\n", - " return PointcloudExtension.ext(self.stac_object)\n", - "```\n", - "\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/tutorials/creating-a-landsat-stac.ipynb b/docs/tutorials/creating-a-landsat-stac.ipynb deleted file mode 100644 index 0aad54ffb..000000000 --- a/docs/tutorials/creating-a-landsat-stac.ipynb +++ /dev/null @@ -1,2542 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Creating a STAC of Landsat data\n", - "\n", - "In this tutorial we create a STAC of Landsat data provided by Microsoft's [Planetary Computer](https://planetarycomputer.microsoft.com/dataset/landsat-c2-l2). There's a lot of Landsat scenes, so we'll only take a subset of scenes that are from a specific year and over a specific location. We'll translate existing metadata about each scene to STAC information, utilizing the `eo`, `view`, `proj`, `raster` and `classification` extensions. Finally we'll write out the STAC catalog to our local machine, allowing us to use [stac-browser](https://github.com/radiantearth/stac-browser) to preview the images.\n", - "\n", - "### Requirements\n", - "\n", - "To run this tutorial you'll need to have installed PySTAC with the validation extra and the Planetary Computer package. To do this, use:\n", - "\n", - "```\n", - "pip install 'pystac[validation]' planetary-computer\n", - "```\n", - "\n", - "Also to run this notebook you'll need [jupyter](https://jupyter.org/) installed locally as well. If you're running in a docker container, make sure that port `5555` is exposed if you want to run the server at the end of the notebook." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import json\n", - "from datetime import datetime\n", - "from functools import partial\n", - "from os.path import dirname, join\n", - "from typing import Any, Callable, Dict, List, Optional, Tuple, Union, cast\n", - "from urllib.parse import urlparse, urlunparse\n", - "\n", - "import planetary_computer as pc\n", - "from dateutil.parser import parse\n", - "from typing_extensions import TypedDict\n", - "\n", - "import pystac\n", - "from pystac.extensions.classification import (\n", - " Bitfield,\n", - " Classification,\n", - " ClassificationExtension,\n", - ")\n", - "from pystac.extensions.eo import Band as EOBand\n", - "from pystac.extensions.eo import EOExtension\n", - "from pystac.extensions.projection import ProjectionExtension\n", - "from pystac.extensions.raster import RasterBand, RasterExtension\n", - "from pystac.extensions.view import ViewExtension" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Identify target scenes\n", - "\n", - "The Planetary Computer provides a STAC API that we could use to search for data within an area and time of interest, but since this notebook is intended to be a tutorial on creating STAC in the first place, doing so would put the cart ahead of the horse. Instead, we supply a list of metadata files for Landsat-8 and Landsat-9 scenes covering the center of Philadelphia, Pennsylvania in autumn of 2022 that we will work with:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "location_name = \"Philly\"\n", - "scene_mtls = [\n", - " \"https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_MTL.xml\",\n", - " \"https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC09_L2SP_014032_20221211_20221213_02_T2/LC09_L2SP_014032_20221211_20221213_02_T2_MTL.xml\",\n", - " \"https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221203_20221212_02_T2/LC08_L2SP_014032_20221203_20221212_02_T2_MTL.xml\",\n", - " \"https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC09_L2SP_014032_20221125_20230320_02_T2/LC09_L2SP_014032_20221125_20230320_02_T2_MTL.xml\",\n", - " \"https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221117_20221128_02_T1/LC08_L2SP_014032_20221117_20221128_02_T1_MTL.xml\",\n", - " \"https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC09_L2SP_014032_20221109_20221111_02_T1/LC09_L2SP_014032_20221109_20221111_02_T1_MTL.xml\",\n", - " \"https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221101_20221114_02_T1/LC08_L2SP_014032_20221101_20221114_02_T1_MTL.xml\",\n", - " \"https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC09_L2SP_014032_20221024_20221026_02_T2/LC09_L2SP_014032_20221024_20221026_02_T2_MTL.xml\",\n", - " \"https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC09_L2SP_014032_20221008_20221010_02_T1/LC09_L2SP_014032_20221008_20221010_02_T1_MTL.xml\",\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Read metadata from the MTL file\n", - "\n", - "Landsat metadata is contained in an `MTL` file that comes in either `.txt` or `.xml` formats. We'll rely on the XML version since it is more consistently available. This will require that we provide some facility for parsing the XML into a more usable format:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# Taken from https://stackoverflow.com/questions/2148119/how-to-convert-an-xml-string-to-a-dictionary\n", - "from xml.etree import cElementTree as ElementTree\n", - "\n", - "\n", - "class XmlListConfig(list):\n", - " def __init__(self, aList):\n", - " for element in aList:\n", - " if element:\n", - " if len(element) == 1 or element[0].tag != element[1].tag:\n", - " self.append(XmlDictConfig(element))\n", - " elif element[0].tag == element[1].tag:\n", - " self.append(XmlListConfig(element))\n", - " elif element.text:\n", - " text = element.text.strip()\n", - " if text:\n", - " self.append(text)\n", - "\n", - "\n", - "class XmlDictConfig(dict):\n", - " def __init__(self, parent_element):\n", - " if parent_element.items():\n", - " self.update(dict(parent_element.items()))\n", - " for element in parent_element:\n", - " if element:\n", - " if len(element) == 1 or element[0].tag != element[1].tag:\n", - " aDict = XmlDictConfig(element)\n", - " else:\n", - " aDict = {element[0].tag: XmlListConfig(element)}\n", - " if element.items():\n", - " aDict.update(dict(element.items()))\n", - " self.update({element.tag: aDict})\n", - " elif element.items():\n", - " self.update({element.tag: dict(element.items())})\n", - " else:\n", - " self.update({element.tag: element.text})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can then use these classes to get the MTL file for our scene. Notice we use `pystac.STAC_IO.read_text`; this is the method that PySTAC uses to read text as it crawls a STAC. It can read from the local filesystem or HTTP/HTTPS by default. Also, it can be extended to read from other sources such as cloud providers—[see the documentation here](https://pystac.readthedocs.io/en/latest/concepts.html#using-stac-io). For now we'll use it directly as an easy way to read a text file from an HTTPS source." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "stac_io = pystac.StacIO.default()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Since we're reading our files from the Planetary Computer's blob storage, we're also going to have to take the additional step of signing our requests using the `planetary-computer` package's `sign()` function. The raw URL is passed in, and the result has a shared access token applied. See the [planetary-computer Python package](https://github.com/microsoft/planetary-computer-sdk-for-python) for more details. We'll see the use of `pc.sign()` throughout the code below, and it will be necessary for asset HREFs to be passed through this function by the user of the catalog as well." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def get_metadata(xml_url: str) -> Dict[str, Any]:\n", - " result = XmlDictConfig(ElementTree.XML(stac_io.read_text(pc.sign(xml_url))))\n", - " result[\"ORIGINAL_URL\"] = (\n", - " xml_url # Include the original URL in the metadata for use later\n", - " )\n", - " return result" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's read the MTL file for the first scene and see what it looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"PRODUCT_CONTENTS\": {\n", - " \"ORIGIN\": \"Image courtesy of the U.S. Geological Survey\",\n", - " \"DIGITAL_OBJECT_IDENTIFIER\": \"https://doi.org/10.5066/P9OGBGM6\",\n", - " \"LANDSAT_PRODUCT_ID\": \"LC08_L2SP_014032_20221219_20230113_02_T1\",\n", - " \"PROCESSING_LEVEL\": \"L2SP\",\n", - " \"COLLECTION_NUMBER\": \"02\",\n", - " \"COLLECTION_CATEGORY\": \"T1\",\n", - " \"OUTPUT_FORMAT\": \"GEOTIFF\",\n", - " \"FILE_NAME_BAND_1\": \"LC08_L2SP_014032_20221219_20230113_02_T1_SR_B1.TIF\",\n", - " \"FILE_NAME_BAND_2\": \"LC08_L2SP_014032_20221219_20230113_02_T1_SR_B2.TIF\",\n", - " \"FILE_NAME_BAND_3\": \"LC08_L2SP_014032_20221219_20230113_02_T1_SR_B3.TIF\",\n", - " \"FILE_NAME_BAND_4\": \"LC08_L2SP_014032_20221219_20230113_02_T1_SR_B4.TIF\",\n", - " \"FILE_NAME_BAND_5\": \"LC08_L2SP_014032_20221219_20230113_02_T1_SR_B5.TIF\",\n", - " \"FILE_NAME_BAND_6\": \"LC08_L2SP_014032_20221219_20230113_02_T1_SR_B6.TIF\",\n", - " \"FILE_NAME_BAND_7\": \"LC08_L2SP_014032_20221219_20230113_02_T1_SR_B7.TIF\",\n", - " \"FILE_NAME_BAND_ST_B10\": \"LC08_L2SP_014032_20221219_20230113_02_T1_ST_B10.TIF\",\n", - " \"FILE_NAME_THERMAL_RADIANCE\": \"LC08_L2SP_014032_20221219_20230113_02_T1_ST_TRAD.TIF\",\n", - " \"FILE_NAME_UPWELL_RADIANCE\": \"LC08_L2SP_014032_20221219_20230113_02_T1_ST_URAD.TIF\",\n", - " \"FILE_NAME_DOWNWELL_RADIANCE\": \"LC08_L2SP_014032_20221219_20230113_02_T1_ST_DRAD.TIF\",\n", - " \"FILE_NAME_ATMOSPHERIC_TRANSMITTANCE\": \"LC08_L2SP_014032_20221219_20230113_02_T1_ST_ATRAN.TIF\",\n", - " \"FILE_NAME_EMISSIVITY\": \"LC08_L2SP_014032_20221219_20230113_02_T1_ST_EMIS.TIF\",\n", - " \"FILE_NAME_EMISSIVITY_STDEV\": \"LC08_L2SP_014032_20221219_20230113_02_T1_ST_EMSD.TIF\",\n", - " \"FILE_NAME_CLOUD_DISTANCE\": \"LC08_L2SP_014032_20221219_20230113_02_T1_ST_CDIST.TIF\",\n", - " \"FILE_NAME_QUALITY_L2_AEROSOL\": \"LC08_L2SP_014032_20221219_20230113_02_T1_SR_QA_AEROSOL.TIF\",\n", - " \"FILE_NAME_QUALITY_L2_SURFACE_TEMPERATURE\": \"LC08_L2SP_014032_20221219_20230113_02_T1_ST_QA.TIF\",\n", - " \"FILE_NAME_QUALITY_L1_PIXEL\": \"LC08_L2SP_014032_20221219_20230113_02_T1_QA_PIXEL.TIF\",\n", - " \"FILE_NAME_QUALITY_L1_RADIOMETRIC_SATURATION\": \"LC08_L2SP_014032_20221219_20230113_02_T1_QA_RADSAT.TIF\",\n", - " \"FILE_NAME_ANGLE_COEFFICIENT\": \"LC08_L2SP_014032_20221219_20230113_02_T1_ANG.txt\",\n", - " \"FILE_NAME_METADATA_ODL\": \"LC08_L2SP_014032_20221219_20230113_02_T1_MTL.txt\",\n", - " \"FILE_NAME_METADATA_XML\": \"LC08_L2SP_014032_20221219_20230113_02_T1_MTL.xml\",\n", - " \"DATA_TYPE_BAND_1\": \"UINT16\",\n", - " \"DATA_TYPE_BAND_2\": \"UINT16\",\n", - " \"DATA_TYPE_BAND_3\": \"UINT16\",\n", - " \"DATA_TYPE_BAND_4\": \"UINT16\",\n", - " \"DATA_TYPE_BAND_5\": \"UINT16\",\n", - " \"DATA_TYPE_BAND_6\": \"UINT16\",\n", - " \"DATA_TYPE_BAND_7\": \"UINT16\",\n", - " \"DATA_TYPE_BAND_ST_B10\": \"UINT16\",\n", - " \"DATA_TYPE_THERMAL_RADIANCE\": \"INT16\",\n", - " \"DATA_TYPE_UPWELL_RADIANCE\": \"INT16\",\n", - " \"DATA_TYPE_DOWNWELL_RADIANCE\": \"INT16\",\n", - " \"DATA_TYPE_ATMOSPHERIC_TRANSMITTANCE\": \"INT16\",\n", - " \"DATA_TYPE_EMISSIVITY\": \"INT16\",\n", - " \"DATA_TYPE_EMISSIVITY_STDEV\": \"INT16\",\n", - " \"DATA_TYPE_CLOUD_DISTANCE\": \"INT16\",\n", - " \"DATA_TYPE_QUALITY_L2_AEROSOL\": \"UINT8\",\n", - " \"DATA_TYPE_QUALITY_L2_SURFACE_TEMPERATURE\": \"INT16\",\n", - " \"DATA_TYPE_QUALITY_L1_PIXEL\": \"UINT16\",\n", - " \"DATA_TYPE_QUALITY_L1_RADIOMETRIC_SATURATION\": \"UINT16\"\n", - " },\n", - " \"IMAGE_ATTRIBUTES\": {\n", - " \"SPACECRAFT_ID\": \"LANDSAT_8\",\n", - " \"SENSOR_ID\": \"OLI_TIRS\",\n", - " \"WRS_TYPE\": \"2\",\n", - " \"WRS_PATH\": \"14\",\n", - " \"WRS_ROW\": \"32\",\n", - " \"NADIR_OFFNADIR\": \"NADIR\",\n", - " \"TARGET_WRS_PATH\": \"14\",\n", - " \"TARGET_WRS_ROW\": \"32\",\n", - " \"DATE_ACQUIRED\": \"2022-12-19\",\n", - " \"SCENE_CENTER_TIME\": \"15:40:17.7299160Z\",\n", - " \"STATION_ID\": \"LGN\",\n", - " \"CLOUD_COVER\": \"43.42\",\n", - " \"CLOUD_COVER_LAND\": \"48.41\",\n", - " \"IMAGE_QUALITY_OLI\": \"9\",\n", - " \"IMAGE_QUALITY_TIRS\": \"9\",\n", - " \"SATURATION_BAND_1\": \"N\",\n", - " \"SATURATION_BAND_2\": \"Y\",\n", - " \"SATURATION_BAND_3\": \"N\",\n", - " \"SATURATION_BAND_4\": \"Y\",\n", - " \"SATURATION_BAND_5\": \"Y\",\n", - " \"SATURATION_BAND_6\": \"Y\",\n", - " \"SATURATION_BAND_7\": \"Y\",\n", - " \"SATURATION_BAND_8\": \"N\",\n", - " \"SATURATION_BAND_9\": \"N\",\n", - " \"ROLL_ANGLE\": \"-0.001\",\n", - " \"SUN_AZIMUTH\": \"160.86021018\",\n", - " \"SUN_ELEVATION\": \"23.81656674\",\n", - " \"EARTH_SUN_DISTANCE\": \"0.9839500\",\n", - " \"TRUNCATION_OLI\": \"UPPER\",\n", - " \"TIRS_SSM_MODEL\": \"FINAL\",\n", - " \"TIRS_SSM_POSITION_STATUS\": \"ESTIMATED\"\n", - " },\n", - " \"PROJECTION_ATTRIBUTES\": {\n", - " \"MAP_PROJECTION\": \"UTM\",\n", - " \"DATUM\": \"WGS84\",\n", - " \"ELLIPSOID\": \"WGS84\",\n", - " \"UTM_ZONE\": \"18\",\n", - " \"GRID_CELL_SIZE_REFLECTIVE\": \"30.00\",\n", - " \"GRID_CELL_SIZE_THERMAL\": \"30.00\",\n", - " \"REFLECTIVE_LINES\": \"7861\",\n", - " \"REFLECTIVE_SAMPLES\": \"7731\",\n", - " \"THERMAL_LINES\": \"7861\",\n", - " \"THERMAL_SAMPLES\": \"7731\",\n", - " \"ORIENTATION\": \"NORTH_UP\",\n", - " \"CORNER_UL_LAT_PRODUCT\": \"41.38441\",\n", - " \"CORNER_UL_LON_PRODUCT\": \"-76.26178\",\n", - " \"CORNER_UR_LAT_PRODUCT\": \"41.38140\",\n", - " \"CORNER_UR_LON_PRODUCT\": \"-73.48833\",\n", - " \"CORNER_LL_LAT_PRODUCT\": \"39.26052\",\n", - " \"CORNER_LL_LON_PRODUCT\": \"-76.22284\",\n", - " \"CORNER_LR_LAT_PRODUCT\": \"39.25773\",\n", - " \"CORNER_LR_LON_PRODUCT\": \"-73.53498\",\n", - " \"CORNER_UL_PROJECTION_X_PRODUCT\": \"394500.000\",\n", - " \"CORNER_UL_PROJECTION_Y_PRODUCT\": \"4582200.000\",\n", - " \"CORNER_UR_PROJECTION_X_PRODUCT\": \"626400.000\",\n", - " \"CORNER_UR_PROJECTION_Y_PRODUCT\": \"4582200.000\",\n", - " \"CORNER_LL_PROJECTION_X_PRODUCT\": \"394500.000\",\n", - " \"CORNER_LL_PROJECTION_Y_PRODUCT\": \"4346400.000\",\n", - " \"CORNER_LR_PROJECTION_X_PRODUCT\": \"626400.000\",\n", - " \"CORNER_LR_PROJECTION_Y_PRODUCT\": \"4346400.000\"\n", - " },\n", - " \"LEVEL2_PROCESSING_RECORD\": {\n", - " \"ORIGIN\": \"Image courtesy of the U.S. Geological Survey\",\n", - " \"DIGITAL_OBJECT_IDENTIFIER\": \"https://doi.org/10.5066/P9OGBGM6\",\n", - " \"REQUEST_ID\": \"1626123_00008\",\n", - " \"LANDSAT_PRODUCT_ID\": \"LC08_L2SP_014032_20221219_20230113_02_T1\",\n", - " \"PROCESSING_LEVEL\": \"L2SP\",\n", - " \"OUTPUT_FORMAT\": \"GEOTIFF\",\n", - " \"DATE_PRODUCT_GENERATED\": \"2023-01-13T02:53:40Z\",\n", - " \"PROCESSING_SOFTWARE_VERSION\": \"LPGS_16.1.0\",\n", - " \"ALGORITHM_SOURCE_SURFACE_REFLECTANCE\": \"LaSRC_1.5.0\",\n", - " \"DATA_SOURCE_OZONE\": \"MODIS\",\n", - " \"DATA_SOURCE_PRESSURE\": \"Calculated\",\n", - " \"DATA_SOURCE_WATER_VAPOR\": \"MODIS\",\n", - " \"DATA_SOURCE_AIR_TEMPERATURE\": \"MODIS\",\n", - " \"ALGORITHM_SOURCE_SURFACE_TEMPERATURE\": \"st_1.3.0\",\n", - " \"DATA_SOURCE_REANALYSIS\": \"GEOS-5 FP-IT\"\n", - " },\n", - " \"LEVEL2_SURFACE_REFLECTANCE_PARAMETERS\": {\n", - " \"REFLECTANCE_MAXIMUM_BAND_1\": \"1.602213\",\n", - " \"REFLECTANCE_MINIMUM_BAND_1\": \"-0.199972\",\n", - " \"REFLECTANCE_MAXIMUM_BAND_2\": \"1.602213\",\n", - " \"REFLECTANCE_MINIMUM_BAND_2\": \"-0.199972\",\n", - " \"REFLECTANCE_MAXIMUM_BAND_3\": \"1.602213\",\n", - " \"REFLECTANCE_MINIMUM_BAND_3\": \"-0.199972\",\n", - " \"REFLECTANCE_MAXIMUM_BAND_4\": \"1.602213\",\n", - " \"REFLECTANCE_MINIMUM_BAND_4\": \"-0.199972\",\n", - " \"REFLECTANCE_MAXIMUM_BAND_5\": \"1.602213\",\n", - " \"REFLECTANCE_MINIMUM_BAND_5\": \"-0.199972\",\n", - " \"REFLECTANCE_MAXIMUM_BAND_6\": \"1.602213\",\n", - " \"REFLECTANCE_MINIMUM_BAND_6\": \"-0.199972\",\n", - " \"REFLECTANCE_MAXIMUM_BAND_7\": \"1.602213\",\n", - " \"REFLECTANCE_MINIMUM_BAND_7\": \"-0.199972\",\n", - " \"QUANTIZE_CAL_MAX_BAND_1\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_1\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_2\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_2\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_3\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_3\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_4\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_4\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_5\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_5\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_6\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_6\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_7\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_7\": \"1\",\n", - " \"REFLECTANCE_MULT_BAND_1\": \"2.75e-05\",\n", - " \"REFLECTANCE_MULT_BAND_2\": \"2.75e-05\",\n", - " \"REFLECTANCE_MULT_BAND_3\": \"2.75e-05\",\n", - " \"REFLECTANCE_MULT_BAND_4\": \"2.75e-05\",\n", - " \"REFLECTANCE_MULT_BAND_5\": \"2.75e-05\",\n", - " \"REFLECTANCE_MULT_BAND_6\": \"2.75e-05\",\n", - " \"REFLECTANCE_MULT_BAND_7\": \"2.75e-05\",\n", - " \"REFLECTANCE_ADD_BAND_1\": \"-0.2\",\n", - " \"REFLECTANCE_ADD_BAND_2\": \"-0.2\",\n", - " \"REFLECTANCE_ADD_BAND_3\": \"-0.2\",\n", - " \"REFLECTANCE_ADD_BAND_4\": \"-0.2\",\n", - " \"REFLECTANCE_ADD_BAND_5\": \"-0.2\",\n", - " \"REFLECTANCE_ADD_BAND_6\": \"-0.2\",\n", - " \"REFLECTANCE_ADD_BAND_7\": \"-0.2\"\n", - " },\n", - " \"LEVEL2_SURFACE_TEMPERATURE_PARAMETERS\": {\n", - " \"TEMPERATURE_MAXIMUM_BAND_ST_B10\": \"372.999941\",\n", - " \"TEMPERATURE_MINIMUM_BAND_ST_B10\": \"149.003418\",\n", - " \"QUANTIZE_CAL_MAXIMUM_BAND_ST_B10\": \"65535\",\n", - " \"QUANTIZE_CAL_MINIMUM_BAND_ST_B10\": \"1\",\n", - " \"TEMPERATURE_MULT_BAND_ST_B10\": \"0.00341802\",\n", - " \"TEMPERATURE_ADD_BAND_ST_B10\": \"149.0\"\n", - " },\n", - " \"LEVEL1_PROCESSING_RECORD\": {\n", - " \"ORIGIN\": \"Image courtesy of the U.S. Geological Survey\",\n", - " \"DIGITAL_OBJECT_IDENTIFIER\": \"https://doi.org/10.5066/P975CC9B\",\n", - " \"REQUEST_ID\": \"1626123_00008\",\n", - " \"LANDSAT_SCENE_ID\": \"LC80140322022353LGN00\",\n", - " \"LANDSAT_PRODUCT_ID\": \"LC08_L1TP_014032_20221219_20230113_02_T1\",\n", - " \"PROCESSING_LEVEL\": \"L1TP\",\n", - " \"COLLECTION_CATEGORY\": \"T1\",\n", - " \"OUTPUT_FORMAT\": \"GEOTIFF\",\n", - " \"DATE_PRODUCT_GENERATED\": \"2023-01-13T02:38:55Z\",\n", - " \"PROCESSING_SOFTWARE_VERSION\": \"LPGS_16.1.0\",\n", - " \"FILE_NAME_BAND_1\": \"LC08_L1TP_014032_20221219_20230113_02_T1_B1.TIF\",\n", - " \"FILE_NAME_BAND_2\": \"LC08_L1TP_014032_20221219_20230113_02_T1_B2.TIF\",\n", - " \"FILE_NAME_BAND_3\": \"LC08_L1TP_014032_20221219_20230113_02_T1_B3.TIF\",\n", - " \"FILE_NAME_BAND_4\": \"LC08_L1TP_014032_20221219_20230113_02_T1_B4.TIF\",\n", - " \"FILE_NAME_BAND_5\": \"LC08_L1TP_014032_20221219_20230113_02_T1_B5.TIF\",\n", - " \"FILE_NAME_BAND_6\": \"LC08_L1TP_014032_20221219_20230113_02_T1_B6.TIF\",\n", - " \"FILE_NAME_BAND_7\": \"LC08_L1TP_014032_20221219_20230113_02_T1_B7.TIF\",\n", - " \"FILE_NAME_BAND_8\": \"LC08_L1TP_014032_20221219_20230113_02_T1_B8.TIF\",\n", - " \"FILE_NAME_BAND_9\": \"LC08_L1TP_014032_20221219_20230113_02_T1_B9.TIF\",\n", - " \"FILE_NAME_BAND_10\": \"LC08_L1TP_014032_20221219_20230113_02_T1_B10.TIF\",\n", - " \"FILE_NAME_BAND_11\": \"LC08_L1TP_014032_20221219_20230113_02_T1_B11.TIF\",\n", - " \"FILE_NAME_QUALITY_L1_PIXEL\": \"LC08_L1TP_014032_20221219_20230113_02_T1_QA_PIXEL.TIF\",\n", - " \"FILE_NAME_QUALITY_L1_RADIOMETRIC_SATURATION\": \"LC08_L1TP_014032_20221219_20230113_02_T1_QA_RADSAT.TIF\",\n", - " \"FILE_NAME_ANGLE_COEFFICIENT\": \"LC08_L1TP_014032_20221219_20230113_02_T1_ANG.txt\",\n", - " \"FILE_NAME_ANGLE_SENSOR_AZIMUTH_BAND_4\": \"LC08_L1TP_014032_20221219_20230113_02_T1_VAA.TIF\",\n", - " \"FILE_NAME_ANGLE_SENSOR_ZENITH_BAND_4\": \"LC08_L1TP_014032_20221219_20230113_02_T1_VZA.TIF\",\n", - " \"FILE_NAME_ANGLE_SOLAR_AZIMUTH_BAND_4\": \"LC08_L1TP_014032_20221219_20230113_02_T1_SAA.TIF\",\n", - " \"FILE_NAME_ANGLE_SOLAR_ZENITH_BAND_4\": \"LC08_L1TP_014032_20221219_20230113_02_T1_SZA.TIF\",\n", - " \"FILE_NAME_METADATA_ODL\": \"LC08_L1TP_014032_20221219_20230113_02_T1_MTL.txt\",\n", - " \"FILE_NAME_METADATA_XML\": \"LC08_L1TP_014032_20221219_20230113_02_T1_MTL.xml\",\n", - " \"FILE_NAME_CPF\": \"LC08CPF_20221001_20221231_02.03\",\n", - " \"FILE_NAME_BPF_OLI\": \"LO8BPF20221219152831_20221219170353.01\",\n", - " \"FILE_NAME_BPF_TIRS\": \"LT8BPF20221215135451_20221222101440.01\",\n", - " \"FILE_NAME_RLUT\": \"LC08RLUT_20150303_20431231_02_01.h5\",\n", - " \"DATA_SOURCE_TIRS_STRAY_LIGHT_CORRECTION\": \"TIRS\",\n", - " \"DATA_SOURCE_ELEVATION\": \"GLS2000\",\n", - " \"GROUND_CONTROL_POINTS_VERSION\": \"5\",\n", - " \"GROUND_CONTROL_POINTS_MODEL\": \"462\",\n", - " \"GEOMETRIC_RMSE_MODEL\": \"8.179\",\n", - " \"GEOMETRIC_RMSE_MODEL_Y\": \"7.213\",\n", - " \"GEOMETRIC_RMSE_MODEL_X\": \"3.856\",\n", - " \"GROUND_CONTROL_POINTS_VERIFY\": \"120\",\n", - " \"GEOMETRIC_RMSE_VERIFY\": \"7.426\"\n", - " },\n", - " \"LEVEL1_MIN_MAX_RADIANCE\": {\n", - " \"RADIANCE_MAXIMUM_BAND_1\": \"785.06079\",\n", - " \"RADIANCE_MINIMUM_BAND_1\": \"-64.83057\",\n", - " \"RADIANCE_MAXIMUM_BAND_2\": \"803.91187\",\n", - " \"RADIANCE_MINIMUM_BAND_2\": \"-66.38730\",\n", - " \"RADIANCE_MAXIMUM_BAND_3\": \"740.79791\",\n", - " \"RADIANCE_MINIMUM_BAND_3\": \"-61.17533\",\n", - " \"RADIANCE_MAXIMUM_BAND_4\": \"624.68250\",\n", - " \"RADIANCE_MINIMUM_BAND_4\": \"-51.58648\",\n", - " \"RADIANCE_MAXIMUM_BAND_5\": \"382.27454\",\n", - " \"RADIANCE_MINIMUM_BAND_5\": \"-31.56836\",\n", - " \"RADIANCE_MAXIMUM_BAND_6\": \"95.06820\",\n", - " \"RADIANCE_MINIMUM_BAND_6\": \"-7.85076\",\n", - " \"RADIANCE_MAXIMUM_BAND_7\": \"32.04307\",\n", - " \"RADIANCE_MINIMUM_BAND_7\": \"-2.64613\",\n", - " \"RADIANCE_MAXIMUM_BAND_8\": \"706.96869\",\n", - " \"RADIANCE_MINIMUM_BAND_8\": \"-58.38170\",\n", - " \"RADIANCE_MAXIMUM_BAND_9\": \"149.40157\",\n", - " \"RADIANCE_MINIMUM_BAND_9\": \"-12.33763\",\n", - " \"RADIANCE_MAXIMUM_BAND_10\": \"22.00180\",\n", - " \"RADIANCE_MINIMUM_BAND_10\": \"0.10033\",\n", - " \"RADIANCE_MAXIMUM_BAND_11\": \"22.00180\",\n", - " \"RADIANCE_MINIMUM_BAND_11\": \"0.10033\"\n", - " },\n", - " \"LEVEL1_MIN_MAX_REFLECTANCE\": {\n", - " \"REFLECTANCE_MAXIMUM_BAND_1\": \"1.210700\",\n", - " \"REFLECTANCE_MINIMUM_BAND_1\": \"-0.099980\",\n", - " \"REFLECTANCE_MAXIMUM_BAND_2\": \"1.210700\",\n", - " \"REFLECTANCE_MINIMUM_BAND_2\": \"-0.099980\",\n", - " \"REFLECTANCE_MAXIMUM_BAND_3\": \"1.210700\",\n", - " \"REFLECTANCE_MINIMUM_BAND_3\": \"-0.099980\",\n", - " \"REFLECTANCE_MAXIMUM_BAND_4\": \"1.210700\",\n", - " \"REFLECTANCE_MINIMUM_BAND_4\": \"-0.099980\",\n", - " \"REFLECTANCE_MAXIMUM_BAND_5\": \"1.210700\",\n", - " \"REFLECTANCE_MINIMUM_BAND_5\": \"-0.099980\",\n", - " \"REFLECTANCE_MAXIMUM_BAND_6\": \"1.210700\",\n", - " \"REFLECTANCE_MINIMUM_BAND_6\": \"-0.099980\",\n", - " \"REFLECTANCE_MAXIMUM_BAND_7\": \"1.210700\",\n", - " \"REFLECTANCE_MINIMUM_BAND_7\": \"-0.099980\",\n", - " \"REFLECTANCE_MAXIMUM_BAND_8\": \"1.210700\",\n", - " \"REFLECTANCE_MINIMUM_BAND_8\": \"-0.099980\",\n", - " \"REFLECTANCE_MAXIMUM_BAND_9\": \"1.210700\",\n", - " \"REFLECTANCE_MINIMUM_BAND_9\": \"-0.099980\"\n", - " },\n", - " \"LEVEL1_MIN_MAX_PIXEL_VALUE\": {\n", - " \"QUANTIZE_CAL_MAX_BAND_1\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_1\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_2\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_2\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_3\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_3\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_4\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_4\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_5\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_5\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_6\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_6\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_7\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_7\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_8\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_8\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_9\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_9\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_10\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_10\": \"1\",\n", - " \"QUANTIZE_CAL_MAX_BAND_11\": \"65535\",\n", - " \"QUANTIZE_CAL_MIN_BAND_11\": \"1\"\n", - " },\n", - " \"LEVEL1_RADIOMETRIC_RESCALING\": {\n", - " \"RADIANCE_MULT_BAND_1\": \"1.2969E-02\",\n", - " \"RADIANCE_MULT_BAND_2\": \"1.3280E-02\",\n", - " \"RADIANCE_MULT_BAND_3\": \"1.2238E-02\",\n", - " \"RADIANCE_MULT_BAND_4\": \"1.0319E-02\",\n", - " \"RADIANCE_MULT_BAND_5\": \"6.3149E-03\",\n", - " \"RADIANCE_MULT_BAND_6\": \"1.5705E-03\",\n", - " \"RADIANCE_MULT_BAND_7\": \"5.2933E-04\",\n", - " \"RADIANCE_MULT_BAND_8\": \"1.1679E-02\",\n", - " \"RADIANCE_MULT_BAND_9\": \"2.4680E-03\",\n", - " \"RADIANCE_MULT_BAND_10\": \"3.3420E-04\",\n", - " \"RADIANCE_MULT_BAND_11\": \"3.3420E-04\",\n", - " \"RADIANCE_ADD_BAND_1\": \"-64.84355\",\n", - " \"RADIANCE_ADD_BAND_2\": \"-66.40058\",\n", - " \"RADIANCE_ADD_BAND_3\": \"-61.18757\",\n", - " \"RADIANCE_ADD_BAND_4\": \"-51.59680\",\n", - " \"RADIANCE_ADD_BAND_5\": \"-31.57467\",\n", - " \"RADIANCE_ADD_BAND_6\": \"-7.85233\",\n", - " \"RADIANCE_ADD_BAND_7\": \"-2.64666\",\n", - " \"RADIANCE_ADD_BAND_8\": \"-58.39338\",\n", - " \"RADIANCE_ADD_BAND_9\": \"-12.34010\",\n", - " \"RADIANCE_ADD_BAND_10\": \"0.10000\",\n", - " \"RADIANCE_ADD_BAND_11\": \"0.10000\",\n", - " \"REFLECTANCE_MULT_BAND_1\": \"2.0000E-05\",\n", - " \"REFLECTANCE_MULT_BAND_2\": \"2.0000E-05\",\n", - " \"REFLECTANCE_MULT_BAND_3\": \"2.0000E-05\",\n", - " \"REFLECTANCE_MULT_BAND_4\": \"2.0000E-05\",\n", - " \"REFLECTANCE_MULT_BAND_5\": \"2.0000E-05\",\n", - " \"REFLECTANCE_MULT_BAND_6\": \"2.0000E-05\",\n", - " \"REFLECTANCE_MULT_BAND_7\": \"2.0000E-05\",\n", - " \"REFLECTANCE_MULT_BAND_8\": \"2.0000E-05\",\n", - " \"REFLECTANCE_MULT_BAND_9\": \"2.0000E-05\",\n", - " \"REFLECTANCE_ADD_BAND_1\": \"-0.100000\",\n", - " \"REFLECTANCE_ADD_BAND_2\": \"-0.100000\",\n", - " \"REFLECTANCE_ADD_BAND_3\": \"-0.100000\",\n", - " \"REFLECTANCE_ADD_BAND_4\": \"-0.100000\",\n", - " \"REFLECTANCE_ADD_BAND_5\": \"-0.100000\",\n", - " \"REFLECTANCE_ADD_BAND_6\": \"-0.100000\",\n", - " \"REFLECTANCE_ADD_BAND_7\": \"-0.100000\",\n", - " \"REFLECTANCE_ADD_BAND_8\": \"-0.100000\",\n", - " \"REFLECTANCE_ADD_BAND_9\": \"-0.100000\"\n", - " },\n", - " \"LEVEL1_THERMAL_CONSTANTS\": {\n", - " \"K1_CONSTANT_BAND_10\": \"774.8853\",\n", - " \"K2_CONSTANT_BAND_10\": \"1321.0789\",\n", - " \"K1_CONSTANT_BAND_11\": \"480.8883\",\n", - " \"K2_CONSTANT_BAND_11\": \"1201.1442\"\n", - " },\n", - " \"LEVEL1_PROJECTION_PARAMETERS\": {\n", - " \"MAP_PROJECTION\": \"UTM\",\n", - " \"DATUM\": \"WGS84\",\n", - " \"ELLIPSOID\": \"WGS84\",\n", - " \"UTM_ZONE\": \"18\",\n", - " \"GRID_CELL_SIZE_PANCHROMATIC\": \"15.00\",\n", - " \"GRID_CELL_SIZE_REFLECTIVE\": \"30.00\",\n", - " \"GRID_CELL_SIZE_THERMAL\": \"30.00\",\n", - " \"ORIENTATION\": \"NORTH_UP\",\n", - " \"RESAMPLING_OPTION\": \"CUBIC_CONVOLUTION\"\n", - " },\n", - " \"ORIGINAL_URL\": \"https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_MTL.xml\"\n", - "}\n" - ] - } - ], - "source": [ - "metadata = get_metadata(scene_mtls[0])\n", - "print(json.dumps(metadata, indent=4))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There are a number of files referred to by this metadata file which are in the same tree in the cloud. We must provide an easy means for creating a URL for these sidecar files. We can then use `partial` to create a helper function to turn a file name into a URL." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def download_sidecar(metadata: Dict[str, Any], filename: str) -> str:\n", - " parsed = urlparse(metadata[\"ORIGINAL_URL\"])\n", - " return urlunparse(parsed._replace(path=join(dirname(parsed.path), filename)))" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "download_url = partial(download_sidecar, metadata)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create a STAC Item from a scene\n", - "\n", - "Now that we have metadata for the scene let's use it to create a [STAC Item](https://github.com/radiantearth/stac-spec/blob/master/item-spec/item-spec.md).\n", - "\n", - "We can use the `help` method to see the signature of the `__init__` method on `pystac.Item`. You can also call `help` directly on `pystac.Item` for broader documentation, or check the [API docs for Item here](https://pystac.readthedocs.io/en/latest/api.html#item)." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Help on function __init__ in module pystac.item:\n", - "\n", - "__init__(self, id: 'str', geometry: 'Optional[Dict[str, Any]]', bbox: 'Optional[List[float]]', datetime: 'Optional[Datetime]', properties: 'Dict[str, Any]', start_datetime: 'Optional[Datetime]' = None, end_datetime: 'Optional[Datetime]' = None, stac_extensions: 'Optional[List[str]]' = None, href: 'Optional[str]' = None, collection: 'Optional[Union[str, Collection]]' = None, extra_fields: 'Optional[Dict[str, Any]]' = None, assets: 'Optional[Dict[str, Asset]]' = None)\n", - " Initialize self. See help(type(self)) for accurate signature.\n", - "\n" - ] - } - ], - "source": [ - "help(pystac.Item.__init__)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see we'll need at least an `id`, `geometry`, `bbox`, and `datetime`. Properties is required, but can be an empty dictionary that we fill out on the Item once it's created.\n", - "\n", - "> Caution! The `Optional` type hint is used when None can be provided in place of a meaningful argument; it does not indicate that the argument does not need to be supplied—that is only true if a default value is indicated in the type signature." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Item `id`\n", - "\n", - "For the Item's `id`, we'll use the scene ID. We'll chop off the last 5 characters as they are repeated for each ID and so aren't necessary: " - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def get_item_id(metadata: Dict[str, Any]) -> str:\n", - " return cast(str, metadata[\"LEVEL1_PROCESSING_RECORD\"][\"LANDSAT_SCENE_ID\"][:-5])" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'LC80140322022353'" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item_id = get_item_id(metadata)\n", - "item_id" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Item `datetime`\n", - "\n", - "Here we parse the datetime of the Item from two metadata fields that describe the date and time the scene was captured:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def get_datetime(metadata: Dict[str, Any]) -> datetime:\n", - " return parse(\n", - " \"%sT%s\"\n", - " % (\n", - " metadata[\"IMAGE_ATTRIBUTES\"][\"DATE_ACQUIRED\"],\n", - " metadata[\"IMAGE_ATTRIBUTES\"][\"SCENE_CENTER_TIME\"],\n", - " )\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "datetime.datetime(2022, 12, 19, 15, 40, 17, 729916, tzinfo=tzutc())" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item_datetime = get_datetime(metadata)\n", - "item_datetime" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Item `bbox`\n", - "\n", - "Here we read in the bounding box information from the scene and transform it into the format of the Item's `bbox` property:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def get_bbox(metadata: Dict[str, Any]) -> List[float]:\n", - " metadata = metadata[\"PROJECTION_ATTRIBUTES\"]\n", - " coords = [\n", - " [\n", - " [\n", - " float(metadata[\"CORNER_UL_LON_PRODUCT\"]),\n", - " float(metadata[\"CORNER_UL_LAT_PRODUCT\"]),\n", - " ],\n", - " [\n", - " float(metadata[\"CORNER_UR_LON_PRODUCT\"]),\n", - " float(metadata[\"CORNER_UR_LAT_PRODUCT\"]),\n", - " ],\n", - " [\n", - " float(metadata[\"CORNER_LR_LON_PRODUCT\"]),\n", - " float(metadata[\"CORNER_LR_LAT_PRODUCT\"]),\n", - " ],\n", - " [\n", - " float(metadata[\"CORNER_LL_LON_PRODUCT\"]),\n", - " float(metadata[\"CORNER_LL_LAT_PRODUCT\"]),\n", - " ],\n", - " [\n", - " float(metadata[\"CORNER_UL_LON_PRODUCT\"]),\n", - " float(metadata[\"CORNER_UL_LAT_PRODUCT\"]),\n", - " ],\n", - " ]\n", - " ]\n", - " lats = [c[1] for c in coords[0]]\n", - " lons = [c[0] for c in coords[0]]\n", - " return [min(lons), min(lats), max(lons), max(lats)]" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[-76.26178, 39.25773, -73.48833, 41.38441]" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item_bbox = get_bbox(metadata)\n", - "item_bbox" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Item `geometry`\n", - "\n", - "Getting the geometry of the scene is a little more tricky. The bounding box will be a axis-aligned rectangle of the area the scene occupies, but will not represent the true footprint of the image - Landsat scenes are \"tilted\" according the the coordinate reference system, so there will be areas in the corner where no image data exists. When constructing a STAC Item it's best if you have the Item geometry represent the true footprint of the assets.\n", - "\n", - "To get the footprint of the scene we'll read in another metadata file that lives alongside the MTL - the `ANG.txt` file. This function uses the ANG file and the bbox to construct the GeoJSON polygon that represents the footprint of the scene:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "def get_geometry(metadata: Dict[str, Any], bbox: List[float]) -> Dict[str, Any]:\n", - " url = download_sidecar(\n", - " metadata, metadata[\"PRODUCT_CONTENTS\"][\"FILE_NAME_ANGLE_COEFFICIENT\"]\n", - " )\n", - " sz = []\n", - " coords = []\n", - " ang_text = stac_io.read_text(pc.sign(url))\n", - " if not ang_text.startswith(\"GROUP\"):\n", - " raise ValueError(f\"ANG file for url {url} is incorrectly formatted\")\n", - " for line in ang_text.split(\"\\n\"):\n", - " if \"BAND01_NUM_L1T_LINES\" in line or \"BAND01_NUM_L1T_SAMPS\" in line:\n", - " sz.append(float(line.split(\"=\")[1]))\n", - " if (\n", - " \"BAND01_L1T_IMAGE_CORNER_LINES\" in line\n", - " or \"BAND01_L1T_IMAGE_CORNER_SAMPS\" in line\n", - " ):\n", - " coords.append(\n", - " [float(v) for v in line.split(\"=\")[1].strip().strip(\"()\").split(\",\")]\n", - " )\n", - " if len(coords) == 2:\n", - " break\n", - " dlon = bbox[2] - bbox[0]\n", - " dlat = bbox[3] - bbox[1]\n", - " lons = [c / sz[1] * dlon + bbox[0] for c in coords[1]]\n", - " lats = [((sz[0] - c) / sz[0]) * dlat + bbox[1] for c in coords[0]]\n", - " coordinates = [\n", - " [\n", - " [lons[0], lats[0]],\n", - " [lons[1], lats[1]],\n", - " [lons[2], lats[2]],\n", - " [lons[3], lats[3]],\n", - " [lons[0], lats[0]],\n", - " ]\n", - " ]\n", - "\n", - " return {\"type\": \"Polygon\", \"coordinates\": coordinates}" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"type\": \"Polygon\",\n", - " \"coordinates\": [\n", - " [\n", - " [\n", - " -75.71075270108336,\n", - " 41.3823086369878\n", - " ],\n", - " [\n", - " -73.48924866988654,\n", - " 40.980654308234485\n", - " ],\n", - " [\n", - " -74.0425618957281,\n", - " 39.25823722657151\n", - " ],\n", - " [\n", - " -76.26093009667797,\n", - " 39.66800780107756\n", - " ],\n", - " [\n", - " -75.71075270108336,\n", - " 41.3823086369878\n", - " ]\n", - " ]\n", - " ]\n", - "}\n" - ] - } - ], - "source": [ - "item_geometry = get_geometry(metadata, item_bbox)\n", - "print(json.dumps(item_geometry, indent=2))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This would be a good time to check our work - we can print out the GeoJSON and use [geojson.io](https://geojson.io/) to check and make sure we're using scenes that overlap our location. If this footprint is somewhere unexpected in the world, make sure the Lat/Long coordinates are correct and in the right order!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Create the item\n", - "\n", - "Now that we have the required attributes for an Item we can create it:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "item = pystac.Item(\n", - " id=item_id,\n", - " datetime=item_datetime,\n", - " geometry=item_geometry,\n", - " bbox=item_bbox,\n", - " properties={},\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "PySTAC has a `validate` method on STAC objects, which you can use to make sure you're constructing things correctly. If there's an issue the following line will throw an exception:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/item.json']" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.validate()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Add Ground Sample Distance to common metadata\n", - "\n", - "We'll add the Ground Sample Distance that is defined as part of the Item [Common Metadata](https://github.com/radiantearth/stac-spec/blob/master/item-spec/common-metadata.md). We define this on the Item level as 30 meters, which is the GSD for most of the Landsat bands. However, if some bands have a different resolution; we can account for this by setting the GSD explicitly for each of those bands below." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "item.common_metadata.gsd = 30.0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Adding the EO extension\n", - "\n", - "STAC has a rich [set of extensions](https://stac-extensions.github.io/) that allow STAC objects to encode information that is not part of the core spec but is used widely and standardized. These extensions allow us to augment STAC objects with additional structured metadata that describe referenced data with semantically-meaningful fields. An example of this is the [eo extension](https://github.com/stac-extensions/eo), which captures fields needed for electro-optical data, like center wavelength and full-width half maximum values.\n", - "\n", - "This notebook will also rely on other extensions; but as they will apply to different objects, not just the item itself, they will be invoked later.\n", - "\n", - "For now, we will enable the EO extension for this item by using the `ext` property provided by the extension object:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "eo_ext = EOExtension.ext(item, add_if_missing=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Add cloud cover\n", - "\n", - "Here we add cloud cover from the metadata as part of the `eo` extension." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "def get_cloud_cover(metadata: Dict[str, Any]) -> float:\n", - " return float(metadata[\"IMAGE_ATTRIBUTES\"][\"CLOUD_COVER\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "43.42" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "eo_ext.cloud_cover = get_cloud_cover(metadata)\n", - "eo_ext.cloud_cover" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Adding assets\n", - "\n", - "STAC Items contain a list of [Assets](https://github.com/radiantearth/stac-spec/blob/master/item-spec/item-spec.md#asset-object), which are a list of files that relate to the Item. In our case we'll be cataloging each file related to the scene, including the Landsat band files as well as the metadata files associated with the scene.\n", - "\n", - "Each asset will have a name, some basic properties, and then possibly some properties defined by the various extensions in use (`eo`, `raster`, and `classification`). So, we begin by defining a type alias for this package of information and some helper functions for creating them:" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "class BandInfo(TypedDict):\n", - " name: str\n", - " asset_fields: Dict[str, str]\n", - " extensions: List[Union[EOBand, RasterBand, List[Bitfield]]]\n", - "\n", - "\n", - "def eo_band_info(\n", - " common_name: str,\n", - " href: str,\n", - " name: str,\n", - " description: str,\n", - " center: float,\n", - " fwhm: float,\n", - " default_raster_band: Optional[RasterBand] = None,\n", - "):\n", - " raster_band = (\n", - " RasterBand.create(\n", - " spatial_resolution=30.0,\n", - " scale=0.0000275,\n", - " nodata=0,\n", - " offset=-0.2,\n", - " data_type=\"uint16\",\n", - " )\n", - " if default_raster_band is None\n", - " else default_raster_band\n", - " )\n", - " return {\n", - " \"name\": common_name,\n", - " \"asset_fields\": {\n", - " \"href\": href,\n", - " \"media_type\": str(pystac.media_type.MediaType.COG),\n", - " },\n", - " \"extensions\": [\n", - " EOBand.create(\n", - " name=name,\n", - " common_name=common_name,\n", - " description=description,\n", - " center_wavelength=center,\n", - " full_width_half_max=fwhm,\n", - " ),\n", - " raster_band,\n", - " ],\n", - " }\n", - "\n", - "\n", - "def plain_band_info(name: str, href: str, title: str, ext: RasterBand):\n", - " return {\n", - " \"name\": name,\n", - " \"asset_fields\": {\n", - " \"href\": href,\n", - " \"media_type\": str(pystac.media_type.MediaType.COG),\n", - " \"title\": title,\n", - " },\n", - " \"extensions\": [ext],\n", - " }" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Some common raster band information definitions will also be useful." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "thermal_raster_band = RasterBand.create(\n", - " spatial_resolution=30.0,\n", - " scale=0.00341802,\n", - " nodata=0,\n", - " offset=149.0,\n", - " data_type=\"uint6\",\n", - " unit=\"kelvin\",\n", - ")\n", - "radiance_raster_band = RasterBand.create(\n", - " unit=\"watt/steradian/square_meter/micrometer\",\n", - " scale=1e-3,\n", - " nodata=-9999,\n", - " data_type=\"uint16\",\n", - " spatial_resolution=30.0,\n", - ")\n", - "emissivity_transmission_raster_band = RasterBand.create(\n", - " scale=1e-4,\n", - " nodata=-9999,\n", - " data_type=\"int16\",\n", - " spatial_resolution=30.0,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Several QA bands are provided that utilize bit-wise masks which we can define using the [classification extension](https://github.com/stac-extensions/classification). Because these definitions can be verbose, we provide some additional helper functions to minimize the length of their definition." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "def create_bitfield(\n", - " offset: int,\n", - " length: int,\n", - " name: str,\n", - " field_names_descriptions: List[Tuple[str, str]],\n", - " description: Optional[str] = None,\n", - ") -> Bitfield:\n", - " return Bitfield.create(\n", - " offset=offset,\n", - " length=length,\n", - " name=name,\n", - " description=description,\n", - " classes=[\n", - " Classification.create(value=i, name=n, description=d)\n", - " for (i, (n, d)) in enumerate(field_names_descriptions)\n", - " ],\n", - " )\n", - "\n", - "\n", - "def create_qa_bitfield(\n", - " offset: int,\n", - " class_name: str,\n", - " description: Optional[Union[str, Tuple[str, str]]] = None,\n", - ") -> Bitfield:\n", - " if description is None:\n", - " descr0 = f\"{class_name.replace('_', ' ').capitalize()} confidence is not high\"\n", - " descr1 = f\"High confidence {class_name.replace('_', ' ')}\"\n", - " elif isinstance(description, str):\n", - " descr0 = f\"{description.capitalize()} confidence is not high\"\n", - " descr1 = f\"High confidence {description.lower()}\"\n", - " else:\n", - " descr0 = description[0]\n", - " descr1 = description[1]\n", - "\n", - " return create_bitfield(\n", - " offset, 1, class_name, [(f\"not_{class_name}\", descr0), (class_name, descr1)]\n", - " )\n", - "\n", - "\n", - "def create_confidence_bitfield(\n", - " offset: int, class_name: str, use_medium: bool = False\n", - ") -> Bitfield:\n", - " label = class_name.replace(\"_\", \" \")\n", - " return create_bitfield(\n", - " offset,\n", - " 2,\n", - " f\"{class_name}_confidence\",\n", - " [\n", - " (\"not_set\", \"No confidence level set\"),\n", - " (\"low\", f\"Low confidence {label}\"),\n", - " (\n", - " (\"medium\", f\"Medium confidence {label}\")\n", - " if use_medium\n", - " else (\"reserved\", \"Reserved - value not used\")\n", - " ),\n", - " (\"high\", f\"High confidence {label}\"),\n", - " ],\n", - " )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can now create the `BandInfo` definitions for the Landsat scenes. This begins with the definition of a function to convert metadata into a list of `BandInfo` records, which is lengthy but ultimately straightforward." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "def landsat_band_info(\n", - " metadata: Dict[str, Any], downloader: Callable[[str], str]\n", - ") -> List[BandInfo]:\n", - " product_contents = metadata[\"PRODUCT_CONTENTS\"]\n", - " return [\n", - " {\n", - " \"name\": \"ang\",\n", - " \"asset_fields\": {\n", - " \"href\": downloader(product_contents[\"FILE_NAME_ANGLE_COEFFICIENT\"]),\n", - " \"media_type\": \"text/plain\",\n", - " \"title\": \"Angle coefficients\",\n", - " },\n", - " \"extensions\": [],\n", - " },\n", - " {\n", - " \"name\": \"mtl.txt\",\n", - " \"asset_fields\": {\n", - " \"href\": downloader(product_contents[\"FILE_NAME_METADATA_ODL\"]),\n", - " \"media_type\": \"text/plain\",\n", - " \"title\": \"Product metadata\",\n", - " },\n", - " \"extensions\": [],\n", - " },\n", - " eo_band_info(\n", - " \"coastal\",\n", - " downloader(product_contents[\"FILE_NAME_BAND_1\"]),\n", - " \"OLI_B1\",\n", - " \"Coastal/Aerosol (Operational Land Imager)\",\n", - " 0.44,\n", - " 0.02,\n", - " ),\n", - " eo_band_info(\n", - " \"blue\",\n", - " downloader(product_contents[\"FILE_NAME_BAND_2\"]),\n", - " \"OLI_B2\",\n", - " \"Visible blue (Operational Land Imager)\",\n", - " 0.48,\n", - " 0.06,\n", - " ),\n", - " eo_band_info(\n", - " \"green\",\n", - " downloader(product_contents[\"FILE_NAME_BAND_3\"]),\n", - " \"OLI_B3\",\n", - " \"Visible green (Operational Land Imager)\",\n", - " 0.56,\n", - " 0.06,\n", - " ),\n", - " eo_band_info(\n", - " \"red\",\n", - " downloader(product_contents[\"FILE_NAME_BAND_4\"]),\n", - " \"OLI_B4\",\n", - " \"Visible red (Operational Land Imager)\",\n", - " 0.65,\n", - " 0.04,\n", - " ),\n", - " eo_band_info(\n", - " \"nir08\",\n", - " downloader(product_contents[\"FILE_NAME_BAND_5\"]),\n", - " \"OLI_B5\",\n", - " \"Near infrared (Operational Land Imager)\",\n", - " 0.87,\n", - " 0.03,\n", - " ),\n", - " eo_band_info(\n", - " \"swir16\",\n", - " downloader(product_contents[\"FILE_NAME_BAND_6\"]),\n", - " \"OLI_B6\",\n", - " \"Short-wave infrared (Operational Land Imager)\",\n", - " 1.61,\n", - " 0.09,\n", - " ),\n", - " eo_band_info(\n", - " \"swir22\",\n", - " downloader(product_contents[\"FILE_NAME_BAND_7\"]),\n", - " \"OLI_B7\",\n", - " \"Short-wave infrared (Operational Land Imager)\",\n", - " 2.2,\n", - " 0.19,\n", - " ),\n", - " eo_band_info(\n", - " \"lwir11\",\n", - " downloader(product_contents[\"FILE_NAME_BAND_ST_B10\"]),\n", - " \"TIRS_B10\",\n", - " \"Long-wave infrared (Thermal InfraRed Sensor)\",\n", - " 10.9,\n", - " 0.59,\n", - " thermal_raster_band,\n", - " ),\n", - " plain_band_info(\n", - " \"trad\",\n", - " downloader(product_contents[\"FILE_NAME_THERMAL_RADIANCE\"]),\n", - " \"Thermal radiance\",\n", - " radiance_raster_band,\n", - " ),\n", - " plain_band_info(\n", - " \"urad\",\n", - " downloader(product_contents[\"FILE_NAME_UPWELL_RADIANCE\"]),\n", - " \"Upwelled radiance\",\n", - " radiance_raster_band,\n", - " ),\n", - " plain_band_info(\n", - " \"drad\",\n", - " downloader(product_contents[\"FILE_NAME_DOWNWELL_RADIANCE\"]),\n", - " \"Downwelled radiance\",\n", - " radiance_raster_band,\n", - " ),\n", - " plain_band_info(\n", - " \"emis\",\n", - " downloader(product_contents[\"FILE_NAME_EMISSIVITY\"]),\n", - " \"Emissivity\",\n", - " emissivity_transmission_raster_band,\n", - " ),\n", - " plain_band_info(\n", - " \"emsd\",\n", - " downloader(product_contents[\"FILE_NAME_EMISSIVITY_STDEV\"]),\n", - " \"Emissivity standard deviation\",\n", - " emissivity_transmission_raster_band,\n", - " ),\n", - " plain_band_info(\n", - " \"atran\",\n", - " downloader(product_contents[\"FILE_NAME_ATMOSPHERIC_TRANSMITTANCE\"]),\n", - " \"Atmospheric transmission\",\n", - " emissivity_transmission_raster_band,\n", - " ),\n", - " plain_band_info(\n", - " \"cdist\",\n", - " downloader(product_contents[\"FILE_NAME_CLOUD_DISTANCE\"]),\n", - " \"Cloud distance\",\n", - " RasterBand.create(\n", - " unit=\"kilometer\",\n", - " scale=1e-2,\n", - " nodata=-9999,\n", - " data_type=\"uint16\",\n", - " spatial_resolution=30.0,\n", - " ),\n", - " ),\n", - " {\n", - " \"name\": \"qa\",\n", - " \"asset_fields\": {\n", - " \"href\": downloader(\n", - " product_contents[\"FILE_NAME_QUALITY_L2_SURFACE_TEMPERATURE\"]\n", - " ),\n", - " \"title\": \"Surface Temperature Quality Assessment Band\",\n", - " },\n", - " \"extensions\": [\n", - " RasterBand.create(\n", - " unit=\"kelvin\",\n", - " scale=1e-2,\n", - " nodata=-9999,\n", - " data_type=\"int16\",\n", - " spatial_resolution=30,\n", - " )\n", - " ],\n", - " },\n", - " {\n", - " \"name\": \"qa_pixel\",\n", - " \"asset_fields\": {\n", - " \"href\": downloader(product_contents[\"FILE_NAME_QUALITY_L1_PIXEL\"]),\n", - " \"media_type\": str(pystac.media_type.MediaType.COG),\n", - " \"title\": \"Pixel quality assessment\",\n", - " },\n", - " \"extensions\": [\n", - " [\n", - " create_qa_bitfield(0, \"fill\", (\"Image data\", \"Fill data\")),\n", - " create_qa_bitfield(\n", - " 1,\n", - " \"dilated_cloud\",\n", - " (\"Cloud is not dilated or no cloud\", \"Dilated cloud\"),\n", - " ),\n", - " create_qa_bitfield(2, \"cirrus\"),\n", - " create_qa_bitfield(3, \"cloud\"),\n", - " create_qa_bitfield(4, \"cloud_shadow\"),\n", - " create_qa_bitfield(5, \"snow\"),\n", - " create_qa_bitfield(6, \"clear\"),\n", - " create_qa_bitfield(7, \"water\"),\n", - " create_confidence_bitfield(8, \"cloud\", True),\n", - " create_confidence_bitfield(10, \"cloud_shadow\"),\n", - " create_confidence_bitfield(12, \"snow\"),\n", - " create_confidence_bitfield(14, \"cirrus\"),\n", - " ]\n", - " ],\n", - " },\n", - " {\n", - " \"name\": \"qa_radsat\",\n", - " \"asset_fields\": {\n", - " \"href\": downloader(\n", - " product_contents[\"FILE_NAME_QUALITY_L1_RADIOMETRIC_SATURATION\"]\n", - " ),\n", - " \"media_type\": str(pystac.media_type.MediaType.COG),\n", - " \"description\": (\n", - " \"Collection 2 Level-1 Radiometric Saturation and \"\n", - " \"Terrain Occlusion Quality Assessment \"\n", - " \"Band (QA_RADSAT)\"\n", - " ),\n", - " },\n", - " \"extensions\": [\n", - " [\n", - " Bitfield.create(\n", - " offset=i - 1,\n", - " length=1,\n", - " description=f\"Band {i} radiometric saturation\",\n", - " classes=[\n", - " Classification.create(\n", - " 0, f\"Band {i} not saturated\", \"not_saturated\"\n", - " ),\n", - " Classification.create(\n", - " 1, f\"Band {i} saturated\", \"saturated\"\n", - " ),\n", - " ],\n", - " )\n", - " for i in [1, 2, 3, 4, 5, 6, 7, 9]\n", - " ]\n", - " + [\n", - " Bitfield.create(\n", - " offset=11,\n", - " length=1,\n", - " description=(\n", - " \"Terrain not visible from sensor due to intervening terrain\"\n", - " ),\n", - " classes=[\n", - " Classification.create(\n", - " 0, \"Terrain is not occluded\", \"not_occluded\"\n", - " ),\n", - " Classification.create(1, \"Terrain is occluded\", \"occluded\"),\n", - " ],\n", - " )\n", - " ]\n", - " ],\n", - " },\n", - " {\n", - " \"name\": \"qa_aerosol\",\n", - " \"asset_fields\": {\n", - " \"href\": downloader(product_contents[\"FILE_NAME_QUALITY_L2_AEROSOL\"]),\n", - " \"media_type\": str(pystac.media_type.MediaType.COG),\n", - " \"title\": \"Aerosol Quality Assessment Band\",\n", - " },\n", - " \"extensions\": [\n", - " [\n", - " create_bitfield(\n", - " 0,\n", - " 1,\n", - " \"fill\",\n", - " [(\"not_fill\", \"Pixel is not fill\"), (\"fill\", \"Pixel is fill\")],\n", - " \"Image or fill data\",\n", - " ),\n", - " create_bitfield(\n", - " 1,\n", - " 1,\n", - " \"retrieval\",\n", - " [\n", - " (\"not_valid\", \"Pixel retrieval is not valid\"),\n", - " (\"valid\", \"Pixel retrieval is valid\"),\n", - " ],\n", - " \"Valid aerosol retrieval\",\n", - " ),\n", - " create_bitfield(\n", - " 2,\n", - " 1,\n", - " \"water\",\n", - " [\n", - " (\"not_water\", \"Pixel is not water\"),\n", - " (\"water\", \"Pixel is water\"),\n", - " ],\n", - " \"Water mask\",\n", - " ),\n", - " create_bitfield(\n", - " 5,\n", - " 1,\n", - " \"interpolated\",\n", - " [\n", - " (\"not_interpolated\", \"Pixel is not interpolated\"),\n", - " (\"interpolated\", \"Pixel is interpolated\"),\n", - " ],\n", - " \"Aerosol interpolation\",\n", - " ),\n", - " create_bitfield(\n", - " 6,\n", - " 2,\n", - " \"level\",\n", - " [\n", - " (\"climatology\", \"No aerosol correction applied\"),\n", - " (\"low\", \"Low aerosol level\"),\n", - " (\"medium\", \"Medium aerosol level\"),\n", - " (\"high\", \"High aerosol level\"),\n", - " ],\n", - " \"Aerosol level\",\n", - " ),\n", - " ]\n", - " ],\n", - " },\n", - " ]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For illustration purposes, we can look at the band info records for an example scene:" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[{'name': 'ang',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_ANG.txt',\n", - " 'media_type': 'text/plain',\n", - " 'title': 'Angle coefficients'},\n", - " 'extensions': []},\n", - " {'name': 'mtl.txt',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_MTL.txt',\n", - " 'media_type': 'text/plain',\n", - " 'title': 'Product metadata'},\n", - " 'extensions': []},\n", - " {'name': 'coastal',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_SR_B1.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized'},\n", - " 'extensions': []},\n", - " {'name': 'blue',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_SR_B2.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized'},\n", - " 'extensions': []},\n", - " {'name': 'green',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_SR_B3.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized'},\n", - " 'extensions': []},\n", - " {'name': 'red',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_SR_B4.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized'},\n", - " 'extensions': []},\n", - " {'name': 'nir08',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_SR_B5.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized'},\n", - " 'extensions': []},\n", - " {'name': 'swir16',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_SR_B6.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized'},\n", - " 'extensions': []},\n", - " {'name': 'swir22',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_SR_B7.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized'},\n", - " 'extensions': []},\n", - " {'name': 'lwir11',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_ST_B10.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized'},\n", - " 'extensions': []},\n", - " {'name': 'trad',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_ST_TRAD.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized',\n", - " 'title': 'Thermal radiance'},\n", - " 'extensions': []},\n", - " {'name': 'urad',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_ST_URAD.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized',\n", - " 'title': 'Upwelled radiance'},\n", - " 'extensions': []},\n", - " {'name': 'drad',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_ST_DRAD.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized',\n", - " 'title': 'Downwelled radiance'},\n", - " 'extensions': []},\n", - " {'name': 'emis',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_ST_EMIS.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized',\n", - " 'title': 'Emissivity'},\n", - " 'extensions': []},\n", - " {'name': 'emsd',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_ST_EMSD.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized',\n", - " 'title': 'Emissivity standard deviation'},\n", - " 'extensions': []},\n", - " {'name': 'atran',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_ST_ATRAN.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized',\n", - " 'title': 'Atmospheric transmission'},\n", - " 'extensions': []},\n", - " {'name': 'cdist',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_ST_CDIST.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized',\n", - " 'title': 'Cloud distance'},\n", - " 'extensions': []},\n", - " {'name': 'qa',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_ST_QA.TIF',\n", - " 'title': 'Surface Temperature Quality Assessment Band'},\n", - " 'extensions': []},\n", - " {'name': 'qa_pixel',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_QA_PIXEL.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized',\n", - " 'title': 'Pixel quality assessment'},\n", - " 'extensions': [[, ]>,\n", - " , ]>,\n", - " , ]>,\n", - " , ]>,\n", - " , ]>,\n", - " , ]>,\n", - " , ]>,\n", - " , ]>,\n", - " , , , ]>,\n", - " , , , ]>,\n", - " , , , ]>,\n", - " , , , ]>]]},\n", - " {'name': 'qa_radsat',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_QA_RADSAT.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized',\n", - " 'description': 'Collection 2 Level-1 Radiometric Saturation and Terrain Occlusion Quality Assessment Band (QA_RADSAT)'},\n", - " 'extensions': [[, ]>,\n", - " , ]>,\n", - " , ]>,\n", - " , ]>,\n", - " , ]>,\n", - " , ]>,\n", - " , ]>,\n", - " , ]>,\n", - " , ]>]]},\n", - " {'name': 'qa_aerosol',\n", - " 'asset_fields': {'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_SR_QA_AEROSOL.TIF',\n", - " 'media_type': 'image/tiff; application=geotiff; profile=cloud-optimized',\n", - " 'title': 'Aerosol Quality Assessment Band'},\n", - " 'extensions': [[, ]>,\n", - " , ]>,\n", - " , ]>,\n", - " , ]>,\n", - " , , , ]>]]}]" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "landsat_band_info(metadata, download_url)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With this information we can now define a method that adds all the relevant assets for a scene to an item:" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "def add_assets(item: pystac.Item, band_info: List[BandInfo]) -> None:\n", - " for band in band_info:\n", - " asset = pystac.Asset(**band[\"asset_fields\"])\n", - " asset.set_owner(item)\n", - " for ext_data in band[\"extensions\"]:\n", - " if isinstance(ext_data, EOBand):\n", - " EOExtension.ext(asset, add_if_missing=True).bands = [ext_data]\n", - " elif isinstance(ext_data, RasterBand):\n", - " RasterExtension.ext(asset, add_if_missing=True).bands = [ext_data]\n", - " elif isinstance(ext_data, list):\n", - " ClassificationExtension.ext(\n", - " asset, add_if_missing=True\n", - " ).bitfields = ext_data\n", - " item.add_asset(band[\"name\"], asset)" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "add_assets(item, landsat_band_info(metadata, download_url))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can examine the item to ensure that the assets appear as expected." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_SR_B5.TIF',\n", - " 'type': 'image/tiff; application=geotiff; profile=cloud-optimized',\n", - " 'eo:bands': [{'name': 'OLI_B5',\n", - " 'common_name': 'nir08',\n", - " 'description': 'Near infrared (Operational Land Imager)',\n", - " 'center_wavelength': 0.87,\n", - " 'full_width_half_max': 0.03}]}" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.assets[\"nir08\"].to_dict()" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'href': 'https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC08_L2SP_014032_20221219_20230113_02_T1/LC08_L2SP_014032_20221219_20230113_02_T1_SR_QA_AEROSOL.TIF',\n", - " 'type': 'image/tiff; application=geotiff; profile=cloud-optimized',\n", - " 'title': 'Aerosol Quality Assessment Band',\n", - " 'classification:bitfields': [{'offset': 0,\n", - " 'length': 1,\n", - " 'classes': [{'value': 0,\n", - " 'name': 'not_fill',\n", - " 'description': 'Pixel is not fill'},\n", - " {'value': 1, 'name': 'fill', 'description': 'Pixel is fill'}],\n", - " 'description': 'Image or fill data',\n", - " 'name': 'fill'},\n", - " {'offset': 1,\n", - " 'length': 1,\n", - " 'classes': [{'value': 0,\n", - " 'name': 'not_valid',\n", - " 'description': 'Pixel retrieval is not valid'},\n", - " {'value': 1, 'name': 'valid', 'description': 'Pixel retrieval is valid'}],\n", - " 'description': 'Valid aerosol retrieval',\n", - " 'name': 'retrieval'},\n", - " {'offset': 2,\n", - " 'length': 1,\n", - " 'classes': [{'value': 0,\n", - " 'name': 'not_water',\n", - " 'description': 'Pixel is not water'},\n", - " {'value': 1, 'name': 'water', 'description': 'Pixel is water'}],\n", - " 'description': 'Water mask',\n", - " 'name': 'water'},\n", - " {'offset': 5,\n", - " 'length': 1,\n", - " 'classes': [{'value': 0,\n", - " 'name': 'not_interpolated',\n", - " 'description': 'Pixel is not interpolated'},\n", - " {'value': 1,\n", - " 'name': 'interpolated',\n", - " 'description': 'Pixel is interpolated'}],\n", - " 'description': 'Aerosol interpolation',\n", - " 'name': 'interpolated'},\n", - " {'offset': 6,\n", - " 'length': 2,\n", - " 'classes': [{'value': 0,\n", - " 'name': 'climatology',\n", - " 'description': 'No aerosol correction applied'},\n", - " {'value': 1, 'name': 'low', 'description': 'Low aerosol level'},\n", - " {'value': 2, 'name': 'medium', 'description': 'Medium aerosol level'},\n", - " {'value': 3, 'name': 'high', 'description': 'High aerosol level'}],\n", - " 'description': 'Aerosol level',\n", - " 'name': 'level'}]}" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.assets[\"qa_aerosol\"].to_dict()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Add projection information\n", - "\n", - "We can specify the EPSG code for the scene as part of the [projection extension](https://github.com/stac-extensions/projection). The below method, adapted from [stactools](https://github.com/stactools-packages/landsat/blob/9f595a9d5ed6b62a2e96338e79f5bb502a7d90d0/src/stactools/landsat/mtl_metadata.py#L86-L109), figures out the correct UTM Zone EPSG:" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "def get_epsg(metadata: Dict[str, Any], min_lat: float, max_lat: float) -> Optional[int]:\n", - " if \"UTM_ZONE\" in metadata[\"PROJECTION_ATTRIBUTES\"]:\n", - " utm_zone_integer = metadata[\"PROJECTION_ATTRIBUTES\"][\"UTM_ZONE\"].zfill(2)\n", - " return int(f\"326{utm_zone_integer}\")\n", - " else:\n", - " lat_ts = metadata[\"PROJECTION_ATTRIBUTES\"][\"TRUE_SCALE_LAT\"]\n", - " if lat_ts == \"-71.00000\":\n", - " # Antarctic\n", - " return 3031\n", - " elif lat_ts == \"71.00000\":\n", - " # Arctic\n", - " return 3995\n", - " else:\n", - " raise ValueError(\n", - " f\"Unexpeced value for PROJECTION_ATTRIBUTES/TRUE_SCALE_LAT: {lat_ts} \"\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "32618" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "proj_ext = ProjectionExtension.ext(item, add_if_missing=True)\n", - "assert item.bbox is not None\n", - "proj_ext.epsg = get_epsg(metadata, item.bbox[1], item.bbox[3])\n", - "proj_ext.epsg" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Add view geometry information\n", - "\n", - "The [View Geometry](https://github.com/stac-extensions/view) extension specifies information related to angles of sensors and other radiance angles that affect the view of resulting data. The Landsat metadata specifies two of these parameters, so we add them to our Item:" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "def get_view_info(metadata: Dict[str, Any]) -> Dict[str, float]:\n", - " return {\n", - " \"sun_azimuth\": float(metadata[\"IMAGE_ATTRIBUTES\"][\"SUN_AZIMUTH\"]),\n", - " \"sun_elevation\": float(metadata[\"IMAGE_ATTRIBUTES\"][\"SUN_ELEVATION\"]),\n", - " }" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'datetime': '2022-12-19T15:40:17.729916Z',\n", - " 'gsd': 30.0,\n", - " 'eo:cloud_cover': 43.42,\n", - " 'proj:epsg': 32618,\n", - " 'view:sun_azimuth': 160.86021018,\n", - " 'view:sun_elevation': 23.81656674}" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "view_ext = ViewExtension.ext(item, add_if_missing=True)\n", - "view_info = get_view_info(metadata)\n", - "view_ext.sun_azimuth = view_info[\"sun_azimuth\"]\n", - "view_ext.sun_elevation = view_info[\"sun_elevation\"]\n", - "item.properties" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we've added all the metadata to the Item, let's check the validator to make sure we've specified everything correctly. The validation logic will take into account the new extensions that have been enabled and validate against the proper schemas for those extensions." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/item.json',\n", - " 'https://stac-extensions.github.io/eo/v1.1.0/schema.json',\n", - " 'https://stac-extensions.github.io/raster/v1.1.0/schema.json',\n", - " 'https://stac-extensions.github.io/classification/v1.1.0/schema.json',\n", - " 'https://stac-extensions.github.io/projection/v1.1.0/schema.json',\n", - " 'https://stac-extensions.github.io/view/v1.0.0/schema.json']" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.validate()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Building the Collection\n", - "\n", - "Now that we know how to build an Item for a scene, let's build the Collection that will contain all the Items.\n", - "\n", - "If we look at the `__init__` method for `pystac.Collection`, we can see what properties are required:" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Help on function __init__ in module pystac.collection:\n", - "\n", - "__init__(self, id: 'str', description: 'str', extent: 'Extent', title: 'Optional[str]' = None, stac_extensions: 'Optional[List[str]]' = None, href: 'Optional[str]' = None, extra_fields: 'Optional[Dict[str, Any]]' = None, catalog_type: 'Optional[CatalogType]' = None, license: 'str' = 'proprietary', keywords: 'Optional[List[str]]' = None, providers: 'Optional[List[Provider]]' = None, summaries: 'Optional[Summaries]' = None, assets: 'Optional[Dict[str, Asset]]' = None)\n", - " Initialize self. See help(type(self)) for accurate signature.\n", - "\n" - ] - } - ], - "source": [ - "help(pystac.Collection.__init__)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Collection `id`\n", - "\n", - "We'll use the location name we defined above in the ID for our Collection:" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'philly-landsat-collection-2'" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "collection_id = \"{}-landsat-collection-2\".format(location_name.lower())\n", - "collection_id" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Collection `title`\n", - "\n", - "Here we set a simple title for our collection." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'2022 Landsat images over philly'" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "collection_title = \"2022 Landsat images over {}\".format(location_name.lower())\n", - "collection_title" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Collection `description`\n", - "\n", - "Here we give a brief description of the Collection. If this were a real Collection that were being published, I'd recommend putting a much more detailed description to ensure anyone using your STAC knows what they are working with!\n", - "\n", - "Notice we are using [Markdown](https://www.markdownguide.org/) to write the description. The `description` field can be Markdown to help tools that render information about STAC to display the information in a more readable way." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "### Philly Landsat Collection 2\n", - "\n", - "A collection of Landsat scenes around Philly in 2022.\n", - "\n" - ] - } - ], - "source": [ - "collection_description = \"\"\"### {} Landsat Collection 2\n", - "\n", - "A collection of Landsat scenes around {} in 2022.\n", - "\"\"\".format(location_name, location_name)\n", - "print(collection_description)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Collection `extent`\n", - "\n", - "A Collection specifies the spatial and temporal extent of all the item it contains. Since Landsat spans the globe, we'll simply put a global extent here. We'll also specify an open-ended time interval.\n", - "\n", - "Towards the end of the notebook, we'll use a method to easily scope this down to cover the times and space the Items occupy once we've added all the items." - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [], - "source": [ - "spatial_extent = pystac.SpatialExtent([[-180.0, -90.0, 180.0, 90.0]])\n", - "temporal_extent = pystac.TemporalExtent([[datetime(2013, 6, 1), None]])\n", - "collection_extent = pystac.Extent(spatial_extent, temporal_extent)" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [], - "source": [ - "collection = pystac.Collection(\n", - " id=collection_id,\n", - " title=collection_title,\n", - " description=collection_description,\n", - " extent=collection_extent,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can now look at our Collection as a `dict` to check our values." - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'type': 'Collection',\n", - " 'id': 'philly-landsat-collection-2',\n", - " 'stac_version': '1.0.0',\n", - " 'description': '### Philly Landsat Collection 2\\n\\nA collection of Landsat scenes around Philly in 2022.\\n',\n", - " 'links': [],\n", - " 'title': '2022 Landsat images over philly',\n", - " 'extent': {'spatial': {'bbox': [[-180.0, -90.0, 180.0, 90.0]]},\n", - " 'temporal': {'interval': [['2013-06-01T00:00:00Z', None]]}},\n", - " 'license': 'proprietary'}" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "collection.to_dict()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Set the license\n", - "\n", - "Notice the `license` above is `proprietary`. This is the default in PySTAC if no license is specified; however Landsat is certainly not proprietary (thankfully!), so let's change the license to the correct [SPDX](https://spdx.org/licenses/) string for public domain data:" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [], - "source": [ - "collection_license = \"PDDL-1.0\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Set the providers\n", - "\n", - "A collection will specify the providers of the data, including what role they have played. We can set our provider information by instantiating `pystac.Provider` objects:" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [], - "source": [ - "collection.providers = [\n", - " pystac.Provider(\n", - " name=\"NASA\",\n", - " roles=[pystac.ProviderRole.PRODUCER, pystac.ProviderRole.LICENSOR],\n", - " url=\"https://landsat.gsfc.nasa.gov/\",\n", - " ),\n", - " pystac.Provider(\n", - " name=\"USGS\",\n", - " roles=[\n", - " pystac.ProviderRole.PROCESSOR,\n", - " pystac.ProviderRole.PRODUCER,\n", - " pystac.ProviderRole.LICENSOR,\n", - " ],\n", - " url=\"https://www.usgs.gov/landsat-missions/landsat-collection-2-level-2-science-products\",\n", - " ),\n", - " pystac.Provider(\n", - " name=\"Microsoft\",\n", - " roles=[pystac.ProviderRole.HOST],\n", - " url=\"https://planetarycomputer.microsoft.com\",\n", - " ),\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Create items for each scene\n", - "\n", - "We created an Item for a single scene above. This method consolidates that logic into a single method that can construct an Item from a scene, so we can create an Item for every scene in our subset:" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [], - "source": [ - "def item_from_metadata(mtl_xml_url: str) -> pystac.Item:\n", - " metadata = get_metadata(mtl_xml_url)\n", - " download_url = partial(download_sidecar, metadata)\n", - "\n", - " bbox = get_bbox(metadata)\n", - " item = pystac.Item(\n", - " id=get_item_id(metadata),\n", - " datetime=get_datetime(metadata),\n", - " geometry=get_geometry(metadata, bbox),\n", - " bbox=bbox,\n", - " properties={},\n", - " )\n", - "\n", - " item.common_metadata.gsd = 30.0\n", - "\n", - " item_eo_ext = EOExtension.ext(item, add_if_missing=True)\n", - " item_eo_ext.cloud_cover = get_cloud_cover(metadata)\n", - "\n", - " add_assets(item, landsat_band_info(metadata, download_url))\n", - "\n", - " item_proj_ext = ProjectionExtension.ext(item, add_if_missing=True)\n", - " assert item.bbox is not None\n", - " item_proj_ext.epsg = get_epsg(metadata, item.bbox[1], item.bbox[3])\n", - "\n", - " item_view_ext = ViewExtension.ext(item, add_if_missing=True)\n", - " view_info = get_view_info(metadata)\n", - " item_view_ext.sun_azimuth = view_info[\"sun_azimuth\"]\n", - " item_view_ext.sun_elevation = view_info[\"sun_elevation\"]\n", - "\n", - " item.validate()\n", - "\n", - " return item" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we create an item per scene and add it to our collection. Since this is reading multiple metadata files per scene from the internet, it may take a little bit to run:" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ANG file for url https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2022/014/032/LC09_L2SP_014032_20221024_20221026_02_T2/LC09_L2SP_014032_20221024_20221026_02_T2_ANG.txt is incorrectly formatted\n" - ] - } - ], - "source": [ - "for url in scene_mtls:\n", - " try:\n", - " item = item_from_metadata(url)\n", - " collection.add_item(item)\n", - " except Exception as e:\n", - " print(e)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Reset collection extent based on items\n", - "\n", - "Now that we've added all the item we can use the `update_extent_from_items` method on the Collection to set the extent based on the contained items:" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'spatial': {'bbox': [[-76.29048, 39.25502, -73.48833, 41.38441]]},\n", - " 'temporal': {'interval': [['2022-10-08T15:40:14.577173Z',\n", - " '2022-12-19T15:40:17.729916Z']]}}" - ] - }, - "execution_count": 49, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "collection.update_extent_from_items()\n", - "collection.extent.to_dict()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Set the HREFs for everything in the catalog\n", - "\n", - "We've been building up our Collection and Items in memory. This has been convenient as it allows us not to think about file paths as we construct our Catalog. However, a STAC is not valid without any HREFs! \n", - "\n", - "We can use the `normalize_hrefs` method to set all the HREFs in the entire STAC based on a root directory. This will use the [STAC Best Practices](https://github.com/radiantearth/stac-spec/blob/master/best-practices.md#catalog-layout) recommendations for STAC file layout for each Catalog, Collection and Item in the STAC.\n", - "\n", - "Here we use that method and set the root directory to a subdirectory of our user's `home` directory:" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": {}, - "outputs": [], - "source": [ - "from pathlib import Path\n", - "\n", - "root_path = str(Path.home() / \"{}-landsat-stac\".format(location_name))\n", - "\n", - "collection.normalize_hrefs(root_path)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we have all the Collection's data set and HREFs in place we can validate the entire STAC using `validate_all`, which recursively crawls through a catalog and validates every STAC object in the catalog:" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "8" - ] - }, - "execution_count": 51, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "collection.validate_all()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Write the catalog locally\n", - "\n", - "Now that we have our complete, validated STAC in memory, let's write it out. This is as simple as calling `save` on the Collection. We need to specify the type of catalog in order to property write out links - these types are described again in the STAC [Best Practices](https://github.com/radiantearth/stac-spec/blob/master/best-practices.md#use-of-links) documentation.\n", - "\n", - "We'll use the \"self contained\" type, which uses relative paths and does not specify absolute \"self\" links to any object. This makes the catalog more portable, as it remains valid even if you copy the STAC to new locations." - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [], - "source": [ - "collection.save(pystac.CatalogType.SELF_CONTAINED)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we've written our STAC out we probably want to view it. We can use the `describe` method to print out a simple representation of the catalog:" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "* \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n" - ] - } - ], - "source": [ - "collection.describe()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also use the `to_dict` method on individual STAC objects in order to see the data, as we've been doing in the tutorial:" - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'type': 'Collection',\n", - " 'id': 'philly-landsat-collection-2',\n", - " 'stac_version': '1.0.0',\n", - " 'description': '### Philly Landsat Collection 2\\n\\nA collection of Landsat scenes around Philly in 2022.\\n',\n", - " 'links': [{'rel': 'root',\n", - " 'href': './collection.json',\n", - " 'type': 'application/json',\n", - " 'title': '2022 Landsat images over philly'},\n", - " {'rel': 'item',\n", - " 'href': './LC80140322022353/LC80140322022353.json',\n", - " 'type': 'application/json'},\n", - " {'rel': 'item',\n", - " 'href': './LC90140322022345/LC90140322022345.json',\n", - " 'type': 'application/json'},\n", - " {'rel': 'item',\n", - " 'href': './LC80140322022337/LC80140322022337.json',\n", - " 'type': 'application/json'},\n", - " {'rel': 'item',\n", - " 'href': './LC90140322022329/LC90140322022329.json',\n", - " 'type': 'application/json'},\n", - " {'rel': 'item',\n", - " 'href': './LC80140322022321/LC80140322022321.json',\n", - " 'type': 'application/json'},\n", - " {'rel': 'item',\n", - " 'href': './LC90140322022313/LC90140322022313.json',\n", - " 'type': 'application/json'},\n", - " {'rel': 'item',\n", - " 'href': './LC80140322022305/LC80140322022305.json',\n", - " 'type': 'application/json'},\n", - " {'rel': 'item',\n", - " 'href': './LC90140322022281/LC90140322022281.json',\n", - " 'type': 'application/json'},\n", - " {'rel': 'self',\n", - " 'href': '/Users/pjh/Philly-landsat-stac/collection.json',\n", - " 'type': 'application/json'}],\n", - " 'title': '2022 Landsat images over philly',\n", - " 'extent': {'spatial': {'bbox': [[-76.29048, 39.25502, -73.48833, 41.38441]]},\n", - " 'temporal': {'interval': [['2022-10-08T15:40:14.577173Z',\n", - " '2022-12-19T15:40:17.729916Z']]}},\n", - " 'license': 'proprietary',\n", - " 'providers': [{'name': 'NASA',\n", - " 'roles': [,\n", - " ],\n", - " 'url': 'https://landsat.gsfc.nasa.gov/'},\n", - " {'name': 'USGS',\n", - " 'roles': [,\n", - " ,\n", - " ],\n", - " 'url': 'https://www.usgs.gov/landsat-missions/landsat-collection-2-level-2-science-products'},\n", - " {'name': 'Microsoft',\n", - " 'roles': [],\n", - " 'url': 'https://planetarycomputer.microsoft.com'}]}" - ] - }, - "execution_count": 54, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "collection.to_dict()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "However, if we want to browse our STAC more interactively, we can serve the Collection from a local webserver and then browse the Collection with [stac-browser](https://github.com/radiantearth/stac-browser).\n", - "\n", - "We can use this simple Python server (copied from [this gist](https://gist.github.com/acdha/925e9ffc3d74ad59c3ea)) to serve our our directory at port 5555:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from http.server import HTTPServer, SimpleHTTPRequestHandler\n", - "\n", - "os.chdir(root_path)\n", - "\n", - "\n", - "class CORSRequestHandler(SimpleHTTPRequestHandler):\n", - " def end_headers(self) -> None:\n", - " self.send_header(\"Access-Control-Allow-Origin\", \"*\")\n", - " self.send_header(\"Access-Control-Allow-Methods\", \"GET\")\n", - " self.send_header(\"Cache-Control\", \"no-store, no-cache, must-revalidate\")\n", - " return super(CORSRequestHandler, self).end_headers()\n", - "\n", - "\n", - "with HTTPServer((\"localhost\", 5555), CORSRequestHandler) as httpd:\n", - " httpd.serve_forever()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can browse our STAC Collection with stac-browser in a few different ways:\n", - "1. Follow the [instructions](https://github.com/radiantearth/stac-browser/blob/main/local_files.md) for starting a stac-browser instance and point it at `http://localhost:5555/collection.json` to serve out the STAC.\n", - "2. If you want to avoid setting up your own stac-browser instance, you can use the [STAC Browser Demo](https://radiantearth.github.io/stac-browser/) hosted by Radiant Earth: [https://radiantearth.github.io/stac-browser/#/http://localhost:5555/collection.json](https://radiantearth.github.io/stac-browser/#/http://localhost:5555/collection.json)\n", - "\n", - "To quit the server, use the `Kernel` -> `Interrupt` menu option." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Acknowledgements\n", - "\n", - "Credit to [sat-stac-landsat](https://github.com/sat-utils/sat-stac-landsat) from which a lot of this code was based." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "pystac", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/tutorials/how-to-create-stac-catalogs.ipynb b/docs/tutorials/how-to-create-stac-catalogs.ipynb deleted file mode 100644 index 97523f4aa..000000000 --- a/docs/tutorials/how-to-create-stac-catalogs.ipynb +++ /dev/null @@ -1,4341 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# How to create STAC Catalogs \n", - "## STAC Community Sprint, Arlington, November 7th 2019" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This notebook runs through some of the basics of using PySTAC to create a static STAC. It was part of a 30 minute presentation at the [community STAC sprint](https://github.com/radiantearth/community-sprints/tree/master/11052019-arlignton-va) in Arlington, VA in November 2019, updated to work with current PySTAC." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This tutorial will require the `boto3`, `rasterio`, and `shapely` libraries:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:34.023332Z", - "iopub.status.busy": "2025-09-11T17:34:34.021839Z", - "iopub.status.idle": "2025-09-11T17:34:34.786566Z", - "shell.execute_reply": "2025-09-11T17:34:34.781537Z", - "shell.execute_reply.started": "2025-09-11T17:34:34.023279Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/home/jsignell/pystac/.venv/bin/python: No module named pip\n", - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "%pip install boto3 rasterio shapely pystac --quiet" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can import pystac and access most of the functionality we need with the single import:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:34.789047Z", - "iopub.status.busy": "2025-09-11T17:34:34.788742Z", - "iopub.status.idle": "2025-09-11T17:34:37.682573Z", - "shell.execute_reply": "2025-09-11T17:34:37.681902Z", - "shell.execute_reply.started": "2025-09-11T17:34:34.789020Z" - } - }, - "outputs": [], - "source": [ - "import pystac" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating a catalog from a local file" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To give us some material to work with, lets download a single image from the [Spacenet 5 challenge](https://www.topcoder.com/challenges/30099956). We'll use a temporary directory to save off our single-item STAC." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:37.683455Z", - "iopub.status.busy": "2025-09-11T17:34:37.683245Z", - "iopub.status.idle": "2025-09-11T17:34:37.692742Z", - "shell.execute_reply": "2025-09-11T17:34:37.689106Z", - "shell.execute_reply.started": "2025-09-11T17:34:37.683430Z" - } - }, - "outputs": [], - "source": [ - "import os\n", - "import urllib.request\n", - "from tempfile import TemporaryDirectory\n", - "\n", - "tmp_dir = TemporaryDirectory()\n", - "img_path = os.path.join(tmp_dir.name, \"image.tif\")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:37.695556Z", - "iopub.status.busy": "2025-09-11T17:34:37.695277Z", - "iopub.status.idle": "2025-09-11T17:34:38.289508Z", - "shell.execute_reply": "2025-09-11T17:34:38.288573Z", - "shell.execute_reply.started": "2025-09-11T17:34:37.695525Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "('/tmp/tmpjzy2xbol/image.tif', )" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "url = (\n", - " \"https://spacenet-dataset.s3.amazonaws.com/\"\n", - " \"spacenet/SN5_roads/train/AOI_7_Moscow/MS/\"\n", - " \"SN5_roads_train_AOI_7_Moscow_MS_chip996.tif\"\n", - ")\n", - "urllib.request.urlretrieve(url, img_path)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We want to create a Catalog. Let's check the docs for `Catalog` to see what information we'll need." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:38.291556Z", - "iopub.status.busy": "2025-09-11T17:34:38.291249Z", - "iopub.status.idle": "2025-09-11T17:34:38.347577Z", - "shell.execute_reply": "2025-09-11T17:34:38.345637Z", - "shell.execute_reply.started": "2025-09-11T17:34:38.291519Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;31mInit signature:\u001b[0m\n", - "\u001b[0mpystac\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCatalog\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mdescription\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mtitle\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mstac_extensions\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'list[str] | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mextra_fields\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'dict[str, Any] | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mhref\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mcatalog_type\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'CatalogType'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'ABSOLUTE_PUBLISHED'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mstrategy\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'HrefLayoutStrategy | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mDocstring:\u001b[0m \n", - "A PySTAC Catalog represents a STAC catalog in memory.\n", - "\n", - "A Catalog is a :class:`~pystac.STACObject` that may contain children,\n", - "which are instances of :class:`~pystac.Catalog` or :class:`~pystac.Collection`,\n", - "as well as :class:`~pystac.Item` s.\n", - "\n", - "Args:\n", - " id : Identifier for the catalog. Must be unique within the STAC.\n", - " description : Detailed multi-line description to fully explain the catalog.\n", - " `CommonMark 0.29 syntax `_ MAY be used for rich\n", - " text representation.\n", - " title : Optional short descriptive one-line title for the catalog.\n", - " stac_extensions : Optional list of extensions the Catalog implements.\n", - " href : Optional HREF for this catalog, which be set as the\n", - " catalog's self link's HREF.\n", - " catalog_type : Optional catalog type for this catalog. Must\n", - " be one of the values in :class:`~pystac.CatalogType`.\n", - " strategy : The layout strategy to use for setting the\n", - " HREFs of the catalog child objects and items.\n", - " If not provided, it will default to the strategy of the root and fallback to\n", - " :class:`~pystac.layout.BestPracticesLayoutStrategy`.\n", - "\u001b[0;31mFile:\u001b[0m ~/pystac/pystac/catalog.py\n", - "\u001b[0;31mType:\u001b[0m ABCMeta\n", - "\u001b[0;31mSubclasses:\u001b[0m Collection" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "?pystac.Catalog" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's just give an ID and a description. We don't have to worry about the HREF right now; that will be set later." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:38.348654Z", - "iopub.status.busy": "2025-09-11T17:34:38.348454Z", - "iopub.status.idle": "2025-09-11T17:34:38.356583Z", - "shell.execute_reply": "2025-09-11T17:34:38.355631Z", - "shell.execute_reply.started": "2025-09-11T17:34:38.348634Z" - } - }, - "outputs": [], - "source": [ - "catalog = pystac.Catalog(id=\"test-catalog\", description=\"Tutorial catalog.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There are no children or items in the catalog, since we haven't added anything yet." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:38.360126Z", - "iopub.status.busy": "2025-09-11T17:34:38.358977Z", - "iopub.status.idle": "2025-09-11T17:34:38.370844Z", - "shell.execute_reply": "2025-09-11T17:34:38.366059Z", - "shell.execute_reply.started": "2025-09-11T17:34:38.360096Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[]\n", - "[]\n" - ] - } - ], - "source": [ - "print(list(catalog.get_children()))\n", - "print(list(catalog.get_items()))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll now create an Item to represent the image. Check the pydocs to see what you need to supply:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:38.372631Z", - "iopub.status.busy": "2025-09-11T17:34:38.371828Z", - "iopub.status.idle": "2025-09-11T17:34:38.383667Z", - "shell.execute_reply": "2025-09-11T17:34:38.380181Z", - "shell.execute_reply.started": "2025-09-11T17:34:38.372599Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;31mInit signature:\u001b[0m\n", - "\u001b[0mpystac\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mItem\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mgeometry\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'dict[str, Any] | None'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mbbox\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'list[float] | None'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mdatetime\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'Datetime | None'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mproperties\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'dict[str, Any]'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mstart_datetime\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'Datetime | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mend_datetime\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'Datetime | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mstac_extensions\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'list[str] | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mhref\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mcollection\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str | Collection | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mextra_fields\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'dict[str, Any] | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0massets\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'dict[str, Asset] | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mDocstring:\u001b[0m \n", - "An Item is the core granular entity in a STAC, containing the core metadata\n", - "that enables any client to search or crawl online catalogs of spatial 'assets' -\n", - "satellite imagery, derived data, DEM's, etc.\n", - "\n", - "Args:\n", - " id : Provider identifier. Must be unique within the STAC.\n", - " geometry : Defines the full footprint of the asset represented by this\n", - " item, formatted according to\n", - " `RFC 7946, section 3.1 (GeoJSON) `_.\n", - " bbox : Bounding Box of the asset represented by this item\n", - " using either 2D or 3D geometries. The length of the array must be 2*n\n", - " where n is the number of dimensions. Could also be None in the case of a\n", - " null geometry.\n", - " datetime : datetime associated with this item. If None,\n", - " a start_datetime and end_datetime must be supplied.\n", - " properties : A dictionary of additional metadata for the item.\n", - " start_datetime : Optional inclusive start datetime, part of common metadata.\n", - " This value will override any `start_datetime` key in properties.\n", - " end_datetime : Optional inclusive end datetime, part of common metadata.\n", - " This value will override any `end_datetime` key in properties.\n", - " stac_extensions : Optional list of extensions the Item implements.\n", - " href : Optional HREF for this item, which be set as the item's\n", - " self link's HREF.\n", - " collection : The Collection or Collection ID that this item\n", - " belongs to.\n", - " extra_fields : Extra fields that are part of the top-level JSON\n", - " properties of the Item.\n", - " assets : A dictionary mapping string keys to :class:`~pystac.Asset` objects. All\n", - " :class:`~pystac.Asset` values in the dictionary will have their\n", - " :attr:`~pystac.Asset.owner` attribute set to the created Item.\n", - "\u001b[0;31mFile:\u001b[0m ~/pystac/pystac/item.py\n", - "\u001b[0;31mType:\u001b[0m _ProtocolMeta\n", - "\u001b[0;31mSubclasses:\u001b[0m " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "?pystac.Item" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Using [rasterio](https://rasterio.readthedocs.io/en/stable/), we can pull out the bounding box of the image to use for the image metadata. If the image contained a NoData border, we would ideally pull out the footprint and save it as the geometry; in this case, we're working with a small chip that most likely has no NoData values." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:38.388240Z", - "iopub.status.busy": "2025-09-11T17:34:38.386541Z", - "iopub.status.idle": "2025-09-11T17:34:39.087296Z", - "shell.execute_reply": "2025-09-11T17:34:39.083512Z", - "shell.execute_reply.started": "2025-09-11T17:34:38.388077Z" - } - }, - "outputs": [], - "source": [ - "import rasterio\n", - "from shapely.geometry import Polygon, mapping\n", - "\n", - "\n", - "def get_bbox_and_footprint(raster_uri):\n", - " with rasterio.open(raster_uri) as ds:\n", - " bounds = ds.bounds\n", - " bbox = [bounds.left, bounds.bottom, bounds.right, bounds.top]\n", - " footprint = Polygon(\n", - " [\n", - " [bounds.left, bounds.bottom],\n", - " [bounds.left, bounds.top],\n", - " [bounds.right, bounds.top],\n", - " [bounds.right, bounds.bottom],\n", - " ]\n", - " )\n", - "\n", - " return (bbox, mapping(footprint))" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.091178Z", - "iopub.status.busy": "2025-09-11T17:34:39.090715Z", - "iopub.status.idle": "2025-09-11T17:34:39.223785Z", - "shell.execute_reply": "2025-09-11T17:34:39.218544Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.091145Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[37.6616853489879, 55.73478197572927, 37.66573047610874, 55.73882710285011]\n", - "{'type': 'Polygon', 'coordinates': (((37.6616853489879, 55.73478197572927), (37.6616853489879, 55.73882710285011), (37.66573047610874, 55.73882710285011), (37.66573047610874, 55.73478197572927), (37.6616853489879, 55.73478197572927)),)}\n" - ] - } - ], - "source": [ - "bbox, footprint = get_bbox_and_footprint(img_path)\n", - "print(bbox)\n", - "print(footprint)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We're also using `datetime.utcnow()` to supply the required datetime property for our Item. Since this is a required property, you might often find yourself making up a time to fill in if you don't know the exact capture time." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.231210Z", - "iopub.status.busy": "2025-09-11T17:34:39.229462Z", - "iopub.status.idle": "2025-09-11T17:34:39.250212Z", - "shell.execute_reply": "2025-09-11T17:34:39.245522Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.231016Z" - } - }, - "outputs": [], - "source": [ - "from datetime import datetime\n", - "\n", - "item = pystac.Item(\n", - " id=\"local-image\",\n", - " geometry=footprint,\n", - " bbox=bbox,\n", - " datetime=datetime.utcnow(),\n", - " properties={},\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We haven't added it to a catalog yet, so it's parent isn't set. Once we add it to the catalog, we can see it correctly links to it's parent." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.254710Z", - "iopub.status.busy": "2025-09-11T17:34:39.254243Z", - "iopub.status.idle": "2025-09-11T17:34:39.264605Z", - "shell.execute_reply": "2025-09-11T17:34:39.260741Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.254672Z" - } - }, - "outputs": [], - "source": [ - "assert item.get_parent() is None" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.269432Z", - "iopub.status.busy": "2025-09-11T17:34:39.267610Z", - "iopub.status.idle": "2025-09-11T17:34:39.337330Z", - "shell.execute_reply": "2025-09-11T17:34:39.336596Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.269272Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - " <Link rel=item target=<Item id=local-image>>\n", - "
\n", - "\n", - "
" - ], - "text/plain": [ - ">" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "catalog.add_item(item)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.340406Z", - "iopub.status.busy": "2025-09-11T17:34:39.340122Z", - "iopub.status.idle": "2025-09-11T17:34:39.345974Z", - "shell.execute_reply": "2025-09-11T17:34:39.345064Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.340384Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - " <Catalog id=test-catalog>\n", - "
\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.get_parent()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`describe()` is a useful method on `Catalog` - but be careful when using it on large catalogs, as it will walk the entire tree of the STAC." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.346688Z", - "iopub.status.busy": "2025-09-11T17:34:39.346487Z", - "iopub.status.idle": "2025-09-11T17:34:39.351538Z", - "shell.execute_reply": "2025-09-11T17:34:39.350797Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.346669Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "* \n", - " * \n" - ] - } - ], - "source": [ - "catalog.describe()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Adding Assets\n", - "\n", - "We've created an Item, but there aren't any assets associated with it. Let's create one:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.352797Z", - "iopub.status.busy": "2025-09-11T17:34:39.352545Z", - "iopub.status.idle": "2025-09-11T17:34:39.359463Z", - "shell.execute_reply": "2025-09-11T17:34:39.358442Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.352773Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;31mInit signature:\u001b[0m\n", - "\u001b[0mpystac\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mAsset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mhref\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mtitle\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mdescription\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mmedia_type\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mroles\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'list[str] | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mextra_fields\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'dict[str, Any] | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;34m'None'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mDocstring:\u001b[0m \n", - "An object that contains a link to data associated with an Item or Collection that\n", - "can be downloaded or streamed.\n", - "\n", - "Args:\n", - " href : Link to the asset object. Relative and absolute links are both\n", - " allowed.\n", - " title : Optional displayed title for clients and users.\n", - " description : A description of the Asset providing additional details,\n", - " such as how it was processed or created. CommonMark 0.29 syntax MAY be used\n", - " for rich text representation.\n", - " media_type : Optional description of the media type. Registered Media Types\n", - " are preferred. See :class:`~pystac.MediaType` for common media types.\n", - " roles : Optional, Semantic roles (i.e. thumbnail, overview,\n", - " data, metadata) of the asset.\n", - " extra_fields : Optional, additional fields for this asset. This is used\n", - " by extensions as a way to serialize and deserialize properties on asset\n", - " object JSON.\n", - "\u001b[0;31mFile:\u001b[0m ~/pystac/pystac/asset.py\n", - "\u001b[0;31mType:\u001b[0m type\n", - "\u001b[0;31mSubclasses:\u001b[0m " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "?pystac.Asset" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.360281Z", - "iopub.status.busy": "2025-09-11T17:34:39.360057Z", - "iopub.status.idle": "2025-09-11T17:34:39.363197Z", - "shell.execute_reply": "2025-09-11T17:34:39.362512Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.360259Z" - } - }, - "outputs": [], - "source": [ - "item.add_asset(\n", - " key=\"image\", asset=pystac.Asset(href=img_path, media_type=pystac.MediaType.GEOTIFF)\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "At any time we can call `to_dict()` on STAC objects to see how the STAC JSON is shaping up. Notice the asset is now set:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.364270Z", - "iopub.status.busy": "2025-09-11T17:34:39.364047Z", - "iopub.status.idle": "2025-09-11T17:34:39.377746Z", - "shell.execute_reply": "2025-09-11T17:34:39.374789Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.364244Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"type\": \"Feature\",\n", - " \"stac_version\": \"1.1.0\",\n", - " \"stac_extensions\": [],\n", - " \"id\": \"local-image\",\n", - " \"geometry\": {\n", - " \"type\": \"Polygon\",\n", - " \"coordinates\": [\n", - " [\n", - " [\n", - " 37.6616853489879,\n", - " 55.73478197572927\n", - " ],\n", - " [\n", - " 37.6616853489879,\n", - " 55.73882710285011\n", - " ],\n", - " [\n", - " 37.66573047610874,\n", - " 55.73882710285011\n", - " ],\n", - " [\n", - " 37.66573047610874,\n", - " 55.73478197572927\n", - " ],\n", - " [\n", - " 37.6616853489879,\n", - " 55.73478197572927\n", - " ]\n", - " ]\n", - " ]\n", - " },\n", - " \"bbox\": [\n", - " 37.6616853489879,\n", - " 55.73478197572927,\n", - " 37.66573047610874,\n", - " 55.73882710285011\n", - " ],\n", - " \"properties\": {\n", - " \"datetime\": \"2025-09-11T17:34:39.240259Z\"\n", - " },\n", - " \"links\": [\n", - " {\n", - " \"rel\": \"root\",\n", - " \"href\": null,\n", - " \"type\": \"application/json\"\n", - " },\n", - " {\n", - " \"rel\": \"parent\",\n", - " \"href\": null,\n", - " \"type\": \"application/json\"\n", - " }\n", - " ],\n", - " \"assets\": {\n", - " \"image\": {\n", - " \"href\": \"/tmp/tmpjzy2xbol/image.tif\",\n", - " \"type\": \"image/tiff; application=geotiff\"\n", - " }\n", - " }\n", - "}\n" - ] - } - ], - "source": [ - "import json\n", - "\n", - "print(json.dumps(item.to_dict(), indent=4))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that the link `href` properties are `null`. This is OK, as we're working with the STAC in memory. Next, we'll talk about writing the catalog out, and how to set those HREFs." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Saving the catalog" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As the JSON above indicates, there's no HREFs set on these in-memory items. PySTAC uses the `self` link on STAC objects to track where the file lives. Because we haven't set them, they evaluate to `None`:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.385246Z", - "iopub.status.busy": "2025-09-11T17:34:39.383246Z", - "iopub.status.idle": "2025-09-11T17:34:39.391640Z", - "shell.execute_reply": "2025-09-11T17:34:39.388897Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.385205Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "True\n", - "True\n" - ] - } - ], - "source": [ - "print(catalog.get_self_href() is None)\n", - "print(item.get_self_href() is None)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In order to set them, we can use `normalize_hrefs`. This method will create a normalized set of HREFs for each STAC object in the catalog, according to the [best practices document](https://github.com/radiantearth/stac-spec/blob/v0.8.1/best-practices.md#catalog-layout)'s recommendations on how to lay out a catalog." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.395029Z", - "iopub.status.busy": "2025-09-11T17:34:39.394764Z", - "iopub.status.idle": "2025-09-11T17:34:39.400859Z", - "shell.execute_reply": "2025-09-11T17:34:39.400153Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.395006Z" - } - }, - "outputs": [], - "source": [ - "catalog.normalize_hrefs(os.path.join(tmp_dir.name, \"stac\"))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we've normalized to a root directory (the temporary directory), we see that the `self` links are set:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.402183Z", - "iopub.status.busy": "2025-09-11T17:34:39.401919Z", - "iopub.status.idle": "2025-09-11T17:34:39.408402Z", - "shell.execute_reply": "2025-09-11T17:34:39.407484Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.402159Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/tmp/tmpjzy2xbol/stac/catalog.json\n", - "/tmp/tmpjzy2xbol/stac/local-image/local-image.json\n" - ] - } - ], - "source": [ - "print(catalog.get_self_href())\n", - "print(item.get_self_href())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can now call `save` on the catalog, which will recursively save all the STAC objects to their respective self HREFs.\n", - "\n", - "Save requires a `CatalogType` to be set. You can review the [API docs](https://pystac.readthedocs.io/en/stable/api.html#catalogtype) on `CatalogType` to see what each type means (unfortunately `help` doesn't show docstrings for attributes)." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.409703Z", - "iopub.status.busy": "2025-09-11T17:34:39.409361Z", - "iopub.status.idle": "2025-09-11T17:34:39.418646Z", - "shell.execute_reply": "2025-09-11T17:34:39.415257Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.409670Z" - } - }, - "outputs": [], - "source": [ - "catalog.save(catalog_type=pystac.CatalogType.SELF_CONTAINED)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.423656Z", - "iopub.status.busy": "2025-09-11T17:34:39.422133Z", - "iopub.status.idle": "2025-09-11T17:34:39.903665Z", - "shell.execute_reply": "2025-09-11T17:34:39.899167Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.423520Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/tmp/tmpjzy2xbol/stac/catalog.json\n", - "\n", - "/tmp/tmpjzy2xbol/stac/local-image:\n", - "local-image.json\n" - ] - } - ], - "source": [ - "!ls {tmp_dir.name}/stac/*" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.912197Z", - "iopub.status.busy": "2025-09-11T17:34:39.909380Z", - "iopub.status.idle": "2025-09-11T17:34:39.944091Z", - "shell.execute_reply": "2025-09-11T17:34:39.939296Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.911968Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"type\": \"Catalog\",\n", - " \"id\": \"test-catalog\",\n", - " \"stac_version\": \"1.1.0\",\n", - " \"description\": \"Tutorial catalog.\",\n", - " \"links\": [\n", - " {\n", - " \"rel\": \"root\",\n", - " \"href\": \"./catalog.json\",\n", - " \"type\": \"application/json\"\n", - " },\n", - " {\n", - " \"rel\": \"item\",\n", - " \"href\": \"./local-image/local-image.json\",\n", - " \"type\": \"application/geo+json\"\n", - " }\n", - " ]\n", - "}\n" - ] - } - ], - "source": [ - "with open(catalog.self_href) as f:\n", - " print(f.read())" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.951241Z", - "iopub.status.busy": "2025-09-11T17:34:39.949644Z", - "iopub.status.idle": "2025-09-11T17:34:39.977350Z", - "shell.execute_reply": "2025-09-11T17:34:39.974102Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.951022Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"type\": \"Feature\",\n", - " \"stac_version\": \"1.1.0\",\n", - " \"stac_extensions\": [],\n", - " \"id\": \"local-image\",\n", - " \"geometry\": {\n", - " \"type\": \"Polygon\",\n", - " \"coordinates\": [\n", - " [\n", - " [\n", - " 37.6616853489879,\n", - " 55.73478197572927\n", - " ],\n", - " [\n", - " 37.6616853489879,\n", - " 55.73882710285011\n", - " ],\n", - " [\n", - " 37.66573047610874,\n", - " 55.73882710285011\n", - " ],\n", - " [\n", - " 37.66573047610874,\n", - " 55.73478197572927\n", - " ],\n", - " [\n", - " 37.6616853489879,\n", - " 55.73478197572927\n", - " ]\n", - " ]\n", - " ]\n", - " },\n", - " \"bbox\": [\n", - " 37.6616853489879,\n", - " 55.73478197572927,\n", - " 37.66573047610874,\n", - " 55.73882710285011\n", - " ],\n", - " \"properties\": {\n", - " \"datetime\": \"2025-09-11T17:34:39.240259Z\"\n", - " },\n", - " \"links\": [\n", - " {\n", - " \"rel\": \"root\",\n", - " \"href\": \"../catalog.json\",\n", - " \"type\": \"application/json\"\n", - " },\n", - " {\n", - " \"rel\": \"parent\",\n", - " \"href\": \"../catalog.json\",\n", - " \"type\": \"application/json\"\n", - " }\n", - " ],\n", - " \"assets\": {\n", - " \"image\": {\n", - " \"href\": \"/tmp/tmpjzy2xbol/image.tif\",\n", - " \"type\": \"image/tiff; application=geotiff\"\n", - " }\n", - " }\n", - "}\n" - ] - } - ], - "source": [ - "with open(item.self_href) as f:\n", - " print(f.read())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can see, all links are saved with relative paths. That's because we used `catalog_type=CatalogType.SELF_CONTAINED`. If we save an Absolute Published catalog, we'll see absolute paths:" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:39.982814Z", - "iopub.status.busy": "2025-09-11T17:34:39.980791Z", - "iopub.status.idle": "2025-09-11T17:34:40.024036Z", - "shell.execute_reply": "2025-09-11T17:34:40.014479Z", - "shell.execute_reply.started": "2025-09-11T17:34:39.982669Z" - } - }, - "outputs": [], - "source": [ - "catalog.save(catalog_type=pystac.CatalogType.ABSOLUTE_PUBLISHED)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now the links included in the STAC item are all absolute:" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.026179Z", - "iopub.status.busy": "2025-09-11T17:34:40.025917Z", - "iopub.status.idle": "2025-09-11T17:34:40.038197Z", - "shell.execute_reply": "2025-09-11T17:34:40.036140Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.026156Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"type\": \"Feature\",\n", - " \"stac_version\": \"1.1.0\",\n", - " \"stac_extensions\": [],\n", - " \"id\": \"local-image\",\n", - " \"geometry\": {\n", - " \"type\": \"Polygon\",\n", - " \"coordinates\": [\n", - " [\n", - " [\n", - " 37.6616853489879,\n", - " 55.73478197572927\n", - " ],\n", - " [\n", - " 37.6616853489879,\n", - " 55.73882710285011\n", - " ],\n", - " [\n", - " 37.66573047610874,\n", - " 55.73882710285011\n", - " ],\n", - " [\n", - " 37.66573047610874,\n", - " 55.73478197572927\n", - " ],\n", - " [\n", - " 37.6616853489879,\n", - " 55.73478197572927\n", - " ]\n", - " ]\n", - " ]\n", - " },\n", - " \"bbox\": [\n", - " 37.6616853489879,\n", - " 55.73478197572927,\n", - " 37.66573047610874,\n", - " 55.73882710285011\n", - " ],\n", - " \"properties\": {\n", - " \"datetime\": \"2025-09-11T17:34:39.240259Z\"\n", - " },\n", - " \"links\": [\n", - " {\n", - " \"rel\": \"root\",\n", - " \"href\": \"/tmp/tmpjzy2xbol/stac/catalog.json\",\n", - " \"type\": \"application/json\"\n", - " },\n", - " {\n", - " \"rel\": \"parent\",\n", - " \"href\": \"/tmp/tmpjzy2xbol/stac/catalog.json\",\n", - " \"type\": \"application/json\"\n", - " },\n", - " {\n", - " \"rel\": \"self\",\n", - " \"href\": \"/tmp/tmpjzy2xbol/stac/local-image/local-image.json\",\n", - " \"type\": \"application/json\"\n", - " }\n", - " ],\n", - " \"assets\": {\n", - " \"image\": {\n", - " \"href\": \"/tmp/tmpjzy2xbol/image.tif\",\n", - " \"type\": \"image/tiff; application=geotiff\"\n", - " }\n", - " }\n", - "}\n" - ] - } - ], - "source": [ - "with open(item.get_self_href()) as f:\n", - " print(f.read())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that the Asset HREF is absolute in both cases. We can make the Asset HREF relative to the STAC Item by using `.make_all_asset_hrefs_relative()`:" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.039260Z", - "iopub.status.busy": "2025-09-11T17:34:40.038939Z", - "iopub.status.idle": "2025-09-11T17:34:40.051146Z", - "shell.execute_reply": "2025-09-11T17:34:40.049037Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.039226Z" - } - }, - "outputs": [], - "source": [ - "catalog.make_all_asset_hrefs_relative()\n", - "catalog.save(catalog_type=pystac.CatalogType.SELF_CONTAINED)" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.056354Z", - "iopub.status.busy": "2025-09-11T17:34:40.054822Z", - "iopub.status.idle": "2025-09-11T17:34:40.067961Z", - "shell.execute_reply": "2025-09-11T17:34:40.064026Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.056213Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"type\": \"Feature\",\n", - " \"stac_version\": \"1.1.0\",\n", - " \"stac_extensions\": [],\n", - " \"id\": \"local-image\",\n", - " \"geometry\": {\n", - " \"type\": \"Polygon\",\n", - " \"coordinates\": [\n", - " [\n", - " [\n", - " 37.6616853489879,\n", - " 55.73478197572927\n", - " ],\n", - " [\n", - " 37.6616853489879,\n", - " 55.73882710285011\n", - " ],\n", - " [\n", - " 37.66573047610874,\n", - " 55.73882710285011\n", - " ],\n", - " [\n", - " 37.66573047610874,\n", - " 55.73478197572927\n", - " ],\n", - " [\n", - " 37.6616853489879,\n", - " 55.73478197572927\n", - " ]\n", - " ]\n", - " ]\n", - " },\n", - " \"bbox\": [\n", - " 37.6616853489879,\n", - " 55.73478197572927,\n", - " 37.66573047610874,\n", - " 55.73882710285011\n", - " ],\n", - " \"properties\": {\n", - " \"datetime\": \"2025-09-11T17:34:39.240259Z\"\n", - " },\n", - " \"links\": [\n", - " {\n", - " \"rel\": \"root\",\n", - " \"href\": \"../catalog.json\",\n", - " \"type\": \"application/json\"\n", - " },\n", - " {\n", - " \"rel\": \"parent\",\n", - " \"href\": \"../catalog.json\",\n", - " \"type\": \"application/json\"\n", - " }\n", - " ],\n", - " \"assets\": {\n", - " \"image\": {\n", - " \"href\": \"../../image.tif\",\n", - " \"type\": \"image/tiff; application=geotiff\"\n", - " }\n", - " }\n", - "}\n" - ] - } - ], - "source": [ - "with open(item.get_self_href()) as f:\n", - " print(f.read())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Creating an Item that implements the EO extension\n", - "\n", - "In the code above our item only implemented the core STAC Item specification. With [extensions](https://github.com/radiantearth/stac-spec/tree/v0.9.0/extensions) we can record more information and add additional functionality to the Item. Given that we know this is a World View 3 image that has earth observation data, we can enable the [eo extension](https://github.com/radiantearth/stac-spec/tree/v0.8.1/extensions/eo) to add band information." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To add eo information to an item we'll need to specify some more data. First, let's define the bands of World View 3:" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.074454Z", - "iopub.status.busy": "2025-09-11T17:34:40.072859Z", - "iopub.status.idle": "2025-09-11T17:34:40.089077Z", - "shell.execute_reply": "2025-09-11T17:34:40.084569Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.074311Z" - } - }, - "outputs": [], - "source": [ - "from pystac.extensions.eo import Band\n", - "\n", - "# From: https://www.spaceimagingme.com/downloads/sensors/datasheets/DG_WorldView3_DS_2014.pdf\n", - "\n", - "wv3_bands = [\n", - " Band.create(\n", - " name=\"Coastal\", description=\"Coastal: 400 - 450 nm\", common_name=\"coastal\"\n", - " ),\n", - " Band.create(name=\"Blue\", description=\"Blue: 450 - 510 nm\", common_name=\"blue\"),\n", - " Band.create(name=\"Green\", description=\"Green: 510 - 580 nm\", common_name=\"green\"),\n", - " Band.create(\n", - " name=\"Yellow\", description=\"Yellow: 585 - 625 nm\", common_name=\"yellow\"\n", - " ),\n", - " Band.create(name=\"Red\", description=\"Red: 630 - 690 nm\", common_name=\"red\"),\n", - " Band.create(\n", - " name=\"Red Edge\", description=\"Red Edge: 705 - 745 nm\", common_name=\"rededge\"\n", - " ),\n", - " Band.create(\n", - " name=\"Near-IR1\", description=\"Near-IR1: 770 - 895 nm\", common_name=\"nir08\"\n", - " ),\n", - " Band.create(\n", - " name=\"Near-IR2\", description=\"Near-IR2: 860 - 1040 nm\", common_name=\"nir09\"\n", - " ),\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that we used the `.create` method create new band information." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can now create an Item, enable the eo extension, add the band information and add it to our catalog:" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.091982Z", - "iopub.status.busy": "2025-09-11T17:34:40.090896Z", - "iopub.status.idle": "2025-09-11T17:34:40.109370Z", - "shell.execute_reply": "2025-09-11T17:34:40.108494Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.091797Z" - } - }, - "outputs": [], - "source": [ - "eo_item = pystac.Item(\n", - " id=\"local-image-eo\",\n", - " geometry=footprint,\n", - " bbox=bbox,\n", - " datetime=datetime.utcnow(),\n", - " properties={},\n", - ")\n", - "eo_item.ext.add(\"eo\")\n", - "eo_item.ext.eo.bands = wv3_bands" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There are also [common metadata](https://github.com/radiantearth/stac-spec/blob/v0.9.0/item-spec/common-metadata.md) fields that we can use to capture additional information about the WorldView 3 imagery:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.110474Z", - "iopub.status.busy": "2025-09-11T17:34:40.110247Z", - "iopub.status.idle": "2025-09-11T17:34:40.118516Z", - "shell.execute_reply": "2025-09-11T17:34:40.113515Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.110452Z" - } - }, - "outputs": [], - "source": [ - "eo_item.common_metadata.platform = \"Maxar\"\n", - "eo_item.common_metadata.instruments = [\"WorldView3\"]\n", - "eo_item.common_metadata.gsd = 0.3" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.120144Z", - "iopub.status.busy": "2025-09-11T17:34:40.119873Z", - "iopub.status.idle": "2025-09-11T17:34:40.146749Z", - "shell.execute_reply": "2025-09-11T17:34:40.141220Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.120119Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - " <Item id=local-image-eo>\n", - "
\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "eo_item" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use the eo extension to add bands to the assets we add to the item:" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.151104Z", - "iopub.status.busy": "2025-09-11T17:34:40.150774Z", - "iopub.status.idle": "2025-09-11T17:34:40.159942Z", - "shell.execute_reply": "2025-09-11T17:34:40.156499Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.151079Z" - } - }, - "outputs": [], - "source": [ - "asset = pystac.Asset(href=img_path, media_type=pystac.MediaType.GEOTIFF)\n", - "eo_item.add_asset(\"image\", asset)\n", - "asset.ext.eo.bands = wv3_bands" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we look at the asset, we can see the appropriate band indexes are set:" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.161890Z", - "iopub.status.busy": "2025-09-11T17:34:40.161460Z", - "iopub.status.idle": "2025-09-11T17:34:40.168427Z", - "shell.execute_reply": "2025-09-11T17:34:40.167804Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.161856Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - " <Asset href=/tmp/tmpjzy2xbol/image.tif>\n", - "
\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "asset" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's clear the in-memory catalog, add the EO item, and save to a new STAC:" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.169341Z", - "iopub.status.busy": "2025-09-11T17:34:40.169117Z", - "iopub.status.idle": "2025-09-11T17:34:40.175972Z", - "shell.execute_reply": "2025-09-11T17:34:40.175157Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.169320Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "catalog.clear_items()\n", - "list(catalog.get_items())" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.176949Z", - "iopub.status.busy": "2025-09-11T17:34:40.176713Z", - "iopub.status.idle": "2025-09-11T17:34:40.182432Z", - "shell.execute_reply": "2025-09-11T17:34:40.181574Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.176928Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "catalog.add_item(eo_item)\n", - "list(catalog.get_items())" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.183488Z", - "iopub.status.busy": "2025-09-11T17:34:40.183163Z", - "iopub.status.idle": "2025-09-11T17:34:40.191846Z", - "shell.execute_reply": "2025-09-11T17:34:40.189964Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.183452Z" - } - }, - "outputs": [], - "source": [ - "catalog.normalize_and_save(\n", - " root_href=os.path.join(tmp_dir.name, \"stac-eo\"),\n", - " catalog_type=pystac.CatalogType.SELF_CONTAINED,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, if we read the catalog from the filesystem, PySTAC recognizes that the item implements eo and so use it's functionality, e.g. getting the bands off the asset:" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.193027Z", - "iopub.status.busy": "2025-09-11T17:34:40.192739Z", - "iopub.status.idle": "2025-09-11T17:34:40.200721Z", - "shell.execute_reply": "2025-09-11T17:34:40.200122Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.192999Z" - } - }, - "outputs": [], - "source": [ - "catalog2 = pystac.read_file(os.path.join(tmp_dir.name, \"stac-eo\", \"catalog.json\"))" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.204907Z", - "iopub.status.busy": "2025-09-11T17:34:40.203318Z", - "iopub.status.idle": "2025-09-11T17:34:40.222371Z", - "shell.execute_reply": "2025-09-11T17:34:40.219982Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.204765Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "assert isinstance(catalog2, pystac.Catalog)\n", - "list(catalog2.get_items())" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.239082Z", - "iopub.status.busy": "2025-09-11T17:34:40.238693Z", - "iopub.status.idle": "2025-09-11T17:34:40.246510Z", - "shell.execute_reply": "2025-09-11T17:34:40.243414Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.239036Z" - } - }, - "outputs": [], - "source": [ - "item: pystac.Item = next(catalog2.get_items(recursive=True))" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.249481Z", - "iopub.status.busy": "2025-09-11T17:34:40.249043Z", - "iopub.status.idle": "2025-09-11T17:34:40.253396Z", - "shell.execute_reply": "2025-09-11T17:34:40.252592Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.249431Z" - } - }, - "outputs": [], - "source": [ - "assert item.ext.has(\"eo\")" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.254625Z", - "iopub.status.busy": "2025-09-11T17:34:40.254329Z", - "iopub.status.idle": "2025-09-11T17:34:40.265308Z", - "shell.execute_reply": "2025-09-11T17:34:40.262693Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.254595Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ]" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.assets[\"image\"].ext.eo.bands" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Collections\n", - "\n", - "Collections are a subtype of Catalog that have some additional properties to make them more searchable. They also can define common properties so that items in the collection don't have to duplicate common data for each item. Let's create a collection to hold common properties between two images from the Spacenet 5 challenge.\n", - "\n", - "First we'll get another image, and it's bbox and footprint:" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.270138Z", - "iopub.status.busy": "2025-09-11T17:34:40.268568Z", - "iopub.status.idle": "2025-09-11T17:34:40.674409Z", - "shell.execute_reply": "2025-09-11T17:34:40.673622Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.269944Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "('/tmp/tmpjzy2xbol/image.tif', )" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "url2 = (\n", - " \"https://spacenet-dataset.s3.amazonaws.com/\"\n", - " \"spacenet/SN5_roads/train/AOI_7_Moscow/MS/\"\n", - " \"SN5_roads_train_AOI_7_Moscow_MS_chip997.tif\"\n", - ")\n", - "img_path2 = os.path.join(tmp_dir.name, \"image.tif\")\n", - "urllib.request.urlretrieve(url2, img_path2)" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.675340Z", - "iopub.status.busy": "2025-09-11T17:34:40.675037Z", - "iopub.status.idle": "2025-09-11T17:34:40.684175Z", - "shell.execute_reply": "2025-09-11T17:34:40.682564Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.675308Z" - } - }, - "outputs": [], - "source": [ - "bbox2, footprint2 = get_bbox_and_footprint(img_path2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can take a look at the pydocs for Collection to see what information we need to supply in order to satisfy the spec." - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.687122Z", - "iopub.status.busy": "2025-09-11T17:34:40.686818Z", - "iopub.status.idle": "2025-09-11T17:34:40.699666Z", - "shell.execute_reply": "2025-09-11T17:34:40.695288Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.687088Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;31mInit signature:\u001b[0m\n", - "\u001b[0mpystac\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCollection\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mdescription\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mextent\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'Extent'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mtitle\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mstac_extensions\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'list[str] | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mhref\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mextra_fields\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'dict[str, Any] | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mcatalog_type\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'CatalogType | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mlicense\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'other'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mkeywords\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'list[str] | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mproviders\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'list[Provider] | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0msummaries\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'Summaries | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0massets\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'dict[str, Asset] | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mstrategy\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'HrefLayoutStrategy | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mDocstring:\u001b[0m \n", - "A Collection extends the Catalog spec with additional metadata that helps\n", - "enable discovery.\n", - "\n", - "Args:\n", - " id : Identifier for the collection. Must be unique within the STAC.\n", - " description : Detailed multi-line description to fully explain the\n", - " collection. `CommonMark 0.29 syntax `_ MAY\n", - " be used for rich text representation.\n", - " extent : Spatial and temporal extents that describe the bounds of\n", - " all items contained within this Collection.\n", - " title : Optional short descriptive one-line title for the\n", - " collection.\n", - " stac_extensions : Optional list of extensions the Collection\n", - " implements.\n", - " href : Optional HREF for this collection, which be set as the\n", - " collection's self link's HREF.\n", - " catalog_type : Optional catalog type for this catalog. Must\n", - " be one of the values in :class`~pystac.CatalogType`.\n", - " license : Collection's license(s) as a\n", - " `SPDX License identifier `_,\n", - " or `other`. If collection includes data with multiple\n", - " different licenses, use `other` and add a link for\n", - " each. The licenses `various` and `proprietary` are deprecated.\n", - " Defaults to 'other'.\n", - " keywords : Optional list of keywords describing the collection.\n", - " providers : Optional list of providers of this Collection.\n", - " summaries : An optional map of property summaries,\n", - " either a set of values or statistics such as a range.\n", - " extra_fields : Extra fields that are part of the top-level\n", - " JSON properties of the Collection.\n", - " assets : A dictionary mapping string keys to :class:`~pystac.Asset` objects. All\n", - " :class:`~pystac.Asset` values in the dictionary will have their\n", - " :attr:`~pystac.Asset.owner` attribute set to the created Collection.\n", - " strategy : The layout strategy to use for setting the\n", - " HREFs of the catalog child objects and items.\n", - " If not provided, it will default to strategy of the parent and fallback to\n", - " :class:`~pystac.layout.BestPracticesLayoutStrategy`.\n", - "\u001b[0;31mFile:\u001b[0m ~/pystac/pystac/collection.py\n", - "\u001b[0;31mType:\u001b[0m _ProtocolMeta\n", - "\u001b[0;31mSubclasses:\u001b[0m " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "?pystac.Collection" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Beyond what a Catalog requires, a Collection requires a license, and an `Extent` that describes the range of space and time that the items it hold occupy." - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.700661Z", - "iopub.status.busy": "2025-09-11T17:34:40.700439Z", - "iopub.status.idle": "2025-09-11T17:34:40.706589Z", - "shell.execute_reply": "2025-09-11T17:34:40.705603Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.700640Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;31mInit signature:\u001b[0m\n", - "\u001b[0mpystac\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mExtent\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mspatial\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'SpatialExtent'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mtemporal\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'TemporalExtent'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mextra_fields\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'dict[str, Any] | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mDocstring:\u001b[0m \n", - "Describes the spatiotemporal extents of a Collection.\n", - "\n", - "Args:\n", - " spatial : Potential spatial extent covered by the collection.\n", - " temporal : Potential temporal extent covered by the collection.\n", - " extra_fields : Dictionary containing additional top-level fields defined on the\n", - " Extent object.\n", - "\u001b[0;31mFile:\u001b[0m ~/pystac/pystac/collection.py\n", - "\u001b[0;31mType:\u001b[0m type\n", - "\u001b[0;31mSubclasses:\u001b[0m " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "?pystac.Extent" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "An Extent is comprised of a SpatialExtent and a TemporalExtent. These hold one or more bounding boxes and time intervals, respectively, that completely cover the items contained in the collections.\n", - "\n", - "Let's start with creating two new items - these will be core Items. We can set these items to implement the `eo` extension by specifying them in the `stac_extensions`." - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.707924Z", - "iopub.status.busy": "2025-09-11T17:34:40.707538Z", - "iopub.status.idle": "2025-09-11T17:34:40.719902Z", - "shell.execute_reply": "2025-09-11T17:34:40.716919Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.707895Z" - } - }, - "outputs": [], - "source": [ - "collection_item = pystac.Item(\n", - " id=\"local-image-col-1\",\n", - " geometry=footprint,\n", - " bbox=bbox,\n", - " datetime=datetime.utcnow(),\n", - " properties={},\n", - ")\n", - "\n", - "collection_item.common_metadata.gsd = 0.3\n", - "collection_item.common_metadata.platform = \"Maxar\"\n", - "collection_item.common_metadata.instruments = [\"WorldView3\"]\n", - "\n", - "asset = pystac.Asset(href=img_path, media_type=pystac.MediaType.GEOTIFF)\n", - "collection_item.add_asset(\"image\", asset)\n", - "asset.ext.add(\"eo\")\n", - "asset.ext.eo.bands = wv3_bands\n", - "\n", - "collection_item2 = pystac.Item(\n", - " id=\"local-image-col-2\",\n", - " geometry=footprint2,\n", - " bbox=bbox2,\n", - " datetime=datetime.utcnow(),\n", - " properties={},\n", - ")\n", - "\n", - "collection_item2.common_metadata.gsd = 0.3\n", - "collection_item2.common_metadata.platform = \"Maxar\"\n", - "collection_item2.common_metadata.instruments = [\"WorldView3\"]\n", - "\n", - "asset2 = pystac.Asset(href=img_path, media_type=pystac.MediaType.GEOTIFF)\n", - "collection_item2.add_asset(\"image\", asset2)\n", - "asset2.ext.add(\"eo\")\n", - "asset2.ext.eo.bands = [\n", - " band for band in wv3_bands if band.name in [\"Red\", \"Green\", \"Blue\"]\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use our two items' metadata to find out what the proper bounds are:" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.720929Z", - "iopub.status.busy": "2025-09-11T17:34:40.720675Z", - "iopub.status.idle": "2025-09-11T17:34:40.725156Z", - "shell.execute_reply": "2025-09-11T17:34:40.724438Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.720904Z" - } - }, - "outputs": [], - "source": [ - "from shapely.geometry import shape\n", - "\n", - "unioned_footprint = shape(footprint).union(shape(footprint2))\n", - "collection_bbox = list(unioned_footprint.bounds)\n", - "spatial_extent = pystac.SpatialExtent(bboxes=[collection_bbox])" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.726167Z", - "iopub.status.busy": "2025-09-11T17:34:40.725796Z", - "iopub.status.idle": "2025-09-11T17:34:40.730562Z", - "shell.execute_reply": "2025-09-11T17:34:40.728577Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.726140Z" - } - }, - "outputs": [], - "source": [ - "collection_interval = sorted([collection_item.datetime, collection_item2.datetime])\n", - "temporal_extent = pystac.TemporalExtent(intervals=[collection_interval])" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.732100Z", - "iopub.status.busy": "2025-09-11T17:34:40.731809Z", - "iopub.status.idle": "2025-09-11T17:34:40.736376Z", - "shell.execute_reply": "2025-09-11T17:34:40.734729Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.732073Z" - } - }, - "outputs": [], - "source": [ - "collection_extent = pystac.Extent(spatial=spatial_extent, temporal=temporal_extent)" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.737721Z", - "iopub.status.busy": "2025-09-11T17:34:40.737479Z", - "iopub.status.idle": "2025-09-11T17:34:40.741105Z", - "shell.execute_reply": "2025-09-11T17:34:40.740424Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.737699Z" - } - }, - "outputs": [], - "source": [ - "collection = pystac.Collection(\n", - " id=\"wv3-images\",\n", - " description=\"Spacenet 5 images over Moscow\",\n", - " extent=collection_extent,\n", - " license=\"CC-BY-SA-4.0\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now if we add our items to our Collection, and our Collection to our Catalog, we get the following STAC that can be saved:" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.744836Z", - "iopub.status.busy": "2025-09-11T17:34:40.742919Z", - "iopub.status.idle": "2025-09-11T17:34:40.760500Z", - "shell.execute_reply": "2025-09-11T17:34:40.759918Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.744674Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[>,\n", - " >]" - ] - }, - "execution_count": 53, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "collection.add_items([collection_item, collection_item2])" - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.761397Z", - "iopub.status.busy": "2025-09-11T17:34:40.761176Z", - "iopub.status.idle": "2025-09-11T17:34:40.768145Z", - "shell.execute_reply": "2025-09-11T17:34:40.765795Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.761377Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - " <Link rel=child target=<Collection id=wv3-images>>\n", - "
\n", - "\n", - "
" - ], - "text/plain": [ - ">" - ] - }, - "execution_count": 54, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "catalog.clear_items()\n", - "catalog.clear_children()\n", - "catalog.add_child(collection)" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.769946Z", - "iopub.status.busy": "2025-09-11T17:34:40.769691Z", - "iopub.status.idle": "2025-09-11T17:34:40.774752Z", - "shell.execute_reply": "2025-09-11T17:34:40.773922Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.769922Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "* \n", - " * \n", - " * \n", - " * \n" - ] - } - ], - "source": [ - "catalog.describe()" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.775801Z", - "iopub.status.busy": "2025-09-11T17:34:40.775544Z", - "iopub.status.idle": "2025-09-11T17:34:40.784824Z", - "shell.execute_reply": "2025-09-11T17:34:40.784015Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.775777Z" - } - }, - "outputs": [], - "source": [ - "catalog.normalize_and_save(\n", - " root_href=os.path.join(tmp_dir.name, \"stac-collection\"),\n", - " catalog_type=pystac.CatalogType.SELF_CONTAINED,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Cleanup\n", - "\n", - "Don't forget to clean up the temporary directory!" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.785964Z", - "iopub.status.busy": "2025-09-11T17:34:40.785696Z", - "iopub.status.idle": "2025-09-11T17:34:40.790373Z", - "shell.execute_reply": "2025-09-11T17:34:40.789749Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.785939Z" - } - }, - "outputs": [], - "source": [ - "tmp_dir.cleanup()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating a STAC of imagery from Spacenet 5 data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, let's take what we've learned and create a Catalog with more data in it.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Allowing PySTAC to read from AWS S3\n", - "\n", - "PySTAC aims to be virtually zero-dependency (notwithstanding the why-isn't-this-in-stdlib datetime-util), so it doesn't have the ability to read from or write to anything but the local file system. However, we can hook into PySTAC's IO in the following way. Learn more about how to customize I/O in STAC from the [documentation](https://pystac.readthedocs.io/en/stable/concepts.html#i-o-in-pystac):" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.791263Z", - "iopub.status.busy": "2025-09-11T17:34:40.791013Z", - "iopub.status.idle": "2025-09-11T17:34:40.800283Z", - "shell.execute_reply": "2025-09-11T17:34:40.798485Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.791240Z" - } - }, - "outputs": [], - "source": [ - "from typing import Any, Union\n", - "from urllib.parse import urlparse\n", - "\n", - "import boto3\n", - "\n", - "from pystac import Link\n", - "from pystac.stac_io import DefaultStacIO\n", - "\n", - "\n", - "class CustomStacIO(DefaultStacIO):\n", - " def __init__(self):\n", - " self.s3 = boto3.resource(\"s3\")\n", - " super().__init__()\n", - "\n", - " def read_text(self, source: Union[str, Link], *args: Any, **kwargs: Any) -> str:\n", - " parsed = urlparse(source)\n", - " if parsed.scheme == \"s3\":\n", - " bucket = parsed.netloc\n", - " key = parsed.path[1:]\n", - "\n", - " obj = self.s3.Object(bucket, key)\n", - " return obj.get()[\"Body\"].read().decode(\"utf-8\")\n", - " else:\n", - " return super().read_text(source, *args, **kwargs)\n", - "\n", - " def write_text(\n", - " self, dest: Union[str, Link], txt: str, *args: Any, **kwargs: Any\n", - " ) -> None:\n", - " parsed = urlparse(dest)\n", - " if parsed.scheme == \"s3\":\n", - " bucket = parsed.netloc\n", - " key = parsed.path[1:]\n", - " self.s3.Object(bucket, key).put(Body=txt, ContentEncoding=\"utf-8\")\n", - " else:\n", - " super().write_text(dest, txt, *args, **kwargs)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll need a utility to list keys for reading the lists of files from S3:" - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.805001Z", - "iopub.status.busy": "2025-09-11T17:34:40.803327Z", - "iopub.status.idle": "2025-09-11T17:34:40.812484Z", - "shell.execute_reply": "2025-09-11T17:34:40.810463Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.804855Z" - } - }, - "outputs": [], - "source": [ - "# From https://alexwlchan.net/2017/07/listing-s3-keys/\n", - "from botocore import UNSIGNED\n", - "from botocore.config import Config\n", - "\n", - "\n", - "def get_s3_keys(bucket, prefix):\n", - " \"\"\"Generate all the keys in an S3 bucket.\"\"\"\n", - " s3 = boto3.client(\"s3\", config=Config(signature_version=UNSIGNED))\n", - " kwargs = {\"Bucket\": bucket, \"Prefix\": prefix}\n", - " while True:\n", - " resp = s3.list_objects_v2(**kwargs)\n", - " for obj in resp[\"Contents\"]:\n", - " yield obj[\"Key\"]\n", - "\n", - " try:\n", - " kwargs[\"ContinuationToken\"] = resp[\"NextContinuationToken\"]\n", - " except KeyError:\n", - " break" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's make a STAC of imagery over Moscow as part of the Spacenet 5 challenge. As a first step, we can list out the imagery and extract IDs from each of the chips." - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:40.813459Z", - "iopub.status.busy": "2025-09-11T17:34:40.813221Z", - "iopub.status.idle": "2025-09-11T17:34:46.867759Z", - "shell.execute_reply": "2025-09-11T17:34:46.867159Z", - "shell.execute_reply.started": "2025-09-11T17:34:40.813436Z" - } - }, - "outputs": [], - "source": [ - "moscow_training_chip_uris = list(\n", - " get_s3_keys(\n", - " bucket=\"spacenet-dataset\", prefix=\"spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/\"\n", - " )\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:46.868640Z", - "iopub.status.busy": "2025-09-11T17:34:46.868433Z", - "iopub.status.idle": "2025-09-11T17:34:46.879549Z", - "shell.execute_reply": "2025-09-11T17:34:46.878830Z", - "shell.execute_reply.started": "2025-09-11T17:34:46.868620Z" - } - }, - "outputs": [], - "source": [ - "import re\n", - "\n", - "chip_id_to_data = {}\n", - "\n", - "\n", - "def get_chip_id(uri):\n", - " return re.search(r\".*\\_chip(\\d+)\\.\", uri).group(1)\n", - "\n", - "\n", - "for uri in moscow_training_chip_uris:\n", - " chip_id = get_chip_id(uri)\n", - " chip_id_to_data[chip_id] = {\"img\": \"s3://spacenet-dataset/{}\".format(uri)}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For this tutorial, we'll only take a subset of the data." - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:46.880764Z", - "iopub.status.busy": "2025-09-11T17:34:46.880497Z", - "iopub.status.idle": "2025-09-11T17:34:46.896891Z", - "shell.execute_reply": "2025-09-11T17:34:46.895894Z", - "shell.execute_reply.started": "2025-09-11T17:34:46.880741Z" - } - }, - "outputs": [], - "source": [ - "chip_id_to_data = dict(list(chip_id_to_data.items())[:10])" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:46.901454Z", - "iopub.status.busy": "2025-09-11T17:34:46.899631Z", - "iopub.status.idle": "2025-09-11T17:34:46.919356Z", - "shell.execute_reply": "2025-09-11T17:34:46.918698Z", - "shell.execute_reply.started": "2025-09-11T17:34:46.901291Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'0': {'img': 's3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip0.tif'},\n", - " '1': {'img': 's3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1.tif'},\n", - " '10': {'img': 's3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip10.tif'},\n", - " '100': {'img': 's3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip100.tif'},\n", - " '1000': {'img': 's3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1000.tif'},\n", - " '1001': {'img': 's3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1001.tif'},\n", - " '1002': {'img': 's3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1002.tif'},\n", - " '1003': {'img': 's3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1003.tif'},\n", - " '1004': {'img': 's3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1004.tif'},\n", - " '1005': {'img': 's3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1005.tif'}}" - ] - }, - "execution_count": 63, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chip_id_to_data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's turn each of those chips into a STAC Item that represents the image." - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:46.920294Z", - "iopub.status.busy": "2025-09-11T17:34:46.920044Z", - "iopub.status.idle": "2025-09-11T17:34:46.925925Z", - "shell.execute_reply": "2025-09-11T17:34:46.923655Z", - "shell.execute_reply.started": "2025-09-11T17:34:46.920272Z" - } - }, - "outputs": [], - "source": [ - "chip_id_to_items = {}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll create core `Item`s for our imagery, but mark them with the `eo` extension as we did above, and store the `eo` data in a `Collection`.\n", - "\n", - "Note that the image CRS is in WGS:84 (Lat/Lng). If it wasn't, we'd have to reproject the footprint to WGS:84 in order to be compliant with the spec (which can easily be done with [pyproj](https://github.com/pyproj4/pyproj)).\n", - "\n", - "Here we're taking advantage of `rasterio`'s ability to read S3 URIs, which only grabs the GeoTIFF metadata and does not pull the whole file down." - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:46.927740Z", - "iopub.status.busy": "2025-09-11T17:34:46.927417Z", - "iopub.status.idle": "2025-09-11T17:34:51.498898Z", - "shell.execute_reply": "2025-09-11T17:34:51.495995Z", - "shell.execute_reply.started": "2025-09-11T17:34:46.927713Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Processing s3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip0.tif\n", - "Processing s3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1.tif\n", - "Processing s3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip10.tif\n", - "Processing s3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip100.tif\n", - "Processing s3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1000.tif\n", - "Processing s3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1001.tif\n", - "Processing s3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1002.tif\n", - "Processing s3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1003.tif\n", - "Processing s3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1004.tif\n", - "Processing s3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1005.tif\n" - ] - } - ], - "source": [ - "import os\n", - "\n", - "os.environ[\"AWS_NO_SIGN_REQUEST\"] = \"true\"\n", - "\n", - "for chip_id in chip_id_to_data:\n", - " img_uri = chip_id_to_data[chip_id][\"img\"]\n", - " print(\"Processing {}\".format(img_uri))\n", - " bbox, footprint = get_bbox_and_footprint(img_uri)\n", - "\n", - " item = pystac.Item(\n", - " id=\"img_{}\".format(chip_id),\n", - " geometry=footprint,\n", - " bbox=bbox,\n", - " datetime=datetime.utcnow(),\n", - " properties={},\n", - " )\n", - "\n", - " item.common_metadata.gsd = 0.3\n", - " item.common_metadata.platform = \"Maxar\"\n", - " item.common_metadata.instruments = [\"WorldView3\"]\n", - "\n", - " item.ext.add(\"eo\")\n", - " item.ext.eo.bands = wv3_bands\n", - " asset = pystac.Asset(href=img_uri, media_type=pystac.MediaType.COG)\n", - " item.add_asset(key=\"ps-ms\", asset=asset)\n", - " asset.ext.eo.bands = wv3_bands\n", - " chip_id_to_items[chip_id] = item" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Creating the Collection\n", - "\n", - "All of these images are over Moscow. In Spacenet 5, we have a couple cities that have imagery; a good way to separate these collections of imagery. We can store all of the common `eo` metadata in the collection." - ] - }, - { - "cell_type": "code", - "execution_count": 66, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:51.504759Z", - "iopub.status.busy": "2025-09-11T17:34:51.502523Z", - "iopub.status.idle": "2025-09-11T17:34:51.512282Z", - "shell.execute_reply": "2025-09-11T17:34:51.511530Z", - "shell.execute_reply.started": "2025-09-11T17:34:51.504250Z" - } - }, - "outputs": [], - "source": [ - "from shapely.geometry import MultiPolygon, shape\n", - "\n", - "footprints = list(map(lambda i: shape(i.geometry).envelope, chip_id_to_items.values()))\n", - "collection_bbox = MultiPolygon(footprints).bounds\n", - "spatial_extent = pystac.SpatialExtent(bboxes=[collection_bbox])" - ] - }, - { - "cell_type": "code", - "execution_count": 67, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:51.513268Z", - "iopub.status.busy": "2025-09-11T17:34:51.513029Z", - "iopub.status.idle": "2025-09-11T17:34:51.537453Z", - "shell.execute_reply": "2025-09-11T17:34:51.533824Z", - "shell.execute_reply.started": "2025-09-11T17:34:51.513247Z" - } - }, - "outputs": [], - "source": [ - "datetimes = sorted(list(map(lambda i: i.datetime, chip_id_to_items.values())))\n", - "temporal_extent = pystac.TemporalExtent(intervals=[[datetimes[0], datetimes[-1]]])" - ] - }, - { - "cell_type": "code", - "execution_count": 68, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:51.538921Z", - "iopub.status.busy": "2025-09-11T17:34:51.538656Z", - "iopub.status.idle": "2025-09-11T17:34:51.544139Z", - "shell.execute_reply": "2025-09-11T17:34:51.542713Z", - "shell.execute_reply.started": "2025-09-11T17:34:51.538896Z" - } - }, - "outputs": [], - "source": [ - "collection_extent = pystac.Extent(spatial=spatial_extent, temporal=temporal_extent)" - ] - }, - { - "cell_type": "code", - "execution_count": 69, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:51.546614Z", - "iopub.status.busy": "2025-09-11T17:34:51.546338Z", - "iopub.status.idle": "2025-09-11T17:34:51.550776Z", - "shell.execute_reply": "2025-09-11T17:34:51.549729Z", - "shell.execute_reply.started": "2025-09-11T17:34:51.546590Z" - } - }, - "outputs": [], - "source": [ - "collection = pystac.Collection(\n", - " id=\"wv3-images\",\n", - " description=\"Spacenet 5 images over Moscow\",\n", - " extent=collection_extent,\n", - " license=\"CC-BY-SA-4.0\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 70, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:51.552351Z", - "iopub.status.busy": "2025-09-11T17:34:51.551710Z", - "iopub.status.idle": "2025-09-11T17:34:51.559532Z", - "shell.execute_reply": "2025-09-11T17:34:51.558758Z", - "shell.execute_reply.started": "2025-09-11T17:34:51.552121Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[>,\n", - " >,\n", - " >,\n", - " >,\n", - " >,\n", - " >,\n", - " >,\n", - " >,\n", - " >,\n", - " >]" - ] - }, - "execution_count": 70, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "collection.add_items(chip_id_to_items.values())" - ] - }, - { - "cell_type": "code", - "execution_count": 71, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:51.563283Z", - "iopub.status.busy": "2025-09-11T17:34:51.562799Z", - "iopub.status.idle": "2025-09-11T17:34:51.568210Z", - "shell.execute_reply": "2025-09-11T17:34:51.566752Z", - "shell.execute_reply.started": "2025-09-11T17:34:51.563252Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "* \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n" - ] - } - ], - "source": [ - "collection.describe()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, we can create a Catalog and add the collection." - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:51.570857Z", - "iopub.status.busy": "2025-09-11T17:34:51.570555Z", - "iopub.status.idle": "2025-09-11T17:34:51.579217Z", - "shell.execute_reply": "2025-09-11T17:34:51.576538Z", - "shell.execute_reply.started": "2025-09-11T17:34:51.570830Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - " <Link rel=child target=<Collection id=wv3-images>>\n", - "
\n", - "\n", - "
" - ], - "text/plain": [ - ">" - ] - }, - "execution_count": 72, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "catalog = pystac.Catalog(id=\"spacenet5\", description=\"Spacenet 5 Data (Test)\")\n", - "catalog.add_child(collection)" - ] - }, - { - "cell_type": "code", - "execution_count": 73, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:51.582829Z", - "iopub.status.busy": "2025-09-11T17:34:51.582174Z", - "iopub.status.idle": "2025-09-11T17:34:51.588996Z", - "shell.execute_reply": "2025-09-11T17:34:51.588228Z", - "shell.execute_reply.started": "2025-09-11T17:34:51.582628Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "* \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n" - ] - } - ], - "source": [ - "catalog.describe()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.12" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/how-to-read-data-from-stac.ipynb b/docs/tutorials/how-to-read-data-from-stac.ipynb deleted file mode 100644 index ec262c1f8..000000000 --- a/docs/tutorials/how-to-read-data-from-stac.ipynb +++ /dev/null @@ -1,7466 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "be2c57c1-798a-4eaf-b2b8-41c261b657d1", - "metadata": {}, - "source": [ - "# How to read data from STAC\n", - "\n", - "This notebook shows how to read the data in from a STAC asset using [xarray](https://docs.xarray.dev/en/stable/) and a little hidden helper library called [xpystac](https://pypi.org/project/xpystac/).\n", - "\n", - "## tl;dr\n", - "\n", - "For any PySTAC asset that can be represented as an xarray dataset you can read the data using the following command:\n", - "\n", - "```python\n", - "xr.open_dataset(asset)\n", - "```\n", - "\n", - "If you want to load multiple assets from the same item and/or many items at once use:\n", - "\n", - "\n", - "```python\n", - "odc.stac.load([items])\n", - "```\n", - "\n", - "## Dependencies\n", - "\n", - "There are lots of optional dependencies depending on where and how the data you are interested in are stored. Here are some of the libraries that you will probably need:\n", - "\n", - "- dask - to delay data loading until access\n", - "- pystac - STAC object structures\n", - "- xarray, rioxarray - data structures\n", - "- xpystac, odc-stac - helper for loading pystac into xarray objects" - ] - }, - { - "cell_type": "markdown", - "id": "ad3fb6dc-3529-47bd-a5b3-f5260f23db88", - "metadata": {}, - "source": [ - "Despite all these install instructions, the import block is very straightforward" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "2a8afebd-b397-4e7a-b448-0f59cc030e66", - "metadata": { - "execution": { - "iopub.execute_input": "2025-08-21T19:55:58.808745Z", - "iopub.status.busy": "2025-08-21T19:55:58.808345Z", - "iopub.status.idle": "2025-08-21T19:56:01.983744Z", - "shell.execute_reply": "2025-08-21T19:56:01.983228Z", - "shell.execute_reply.started": "2025-08-21T19:55:58.808709Z" - } - }, - "outputs": [], - "source": [ - "import odc.stac\n", - "import planetary_computer\n", - "import rioxarray\n", - "import xarray as xr\n", - "\n", - "import pystac" - ] - }, - { - "cell_type": "markdown", - "id": "6b24745c-b2d5-43d6-9c7e-66458b3a88e3", - "metadata": {}, - "source": [ - "## Examples\n", - "\n", - "Here are a few examples of the different types of objects that you can open in xarray." - ] - }, - { - "cell_type": "markdown", - "id": "30da7cfd-2861-4095-b15b-9952a7d824d9", - "metadata": {}, - "source": [ - "### COGs\n", - "\n", - "Read all the data from the COGs referenced by the assets on an item." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "c77432e6-8b0d-44d2-a947-ec74a529b8cb", - "metadata": { - "execution": { - "iopub.execute_input": "2025-08-21T19:56:03.945808Z", - "iopub.status.busy": "2025-08-21T19:56:03.945372Z", - "iopub.status.idle": "2025-08-21T19:56:05.448439Z", - "shell.execute_reply": "2025-08-21T19:56:05.448060Z", - "shell.execute_reply.started": "2025-08-21T19:56:03.945773Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset> Size: 2GB\n",
-       "Dimensions:      (y: 7801, x: 7761, time: 1)\n",
-       "Coordinates:\n",
-       "  * y            (y) float64 62kB -3.713e+06 -3.713e+06 ... -3.947e+06\n",
-       "  * x            (x) float64 62kB 3.774e+05 3.774e+05 ... 6.102e+05 6.102e+05\n",
-       "    spatial_ref  int32 4B 32656\n",
-       "  * time         (time) datetime64[ns] 8B 2023-04-08T23:37:51.630731\n",
-       "Data variables: (12/19)\n",
-       "    qa           (time, y, x) int16 121MB dask.array<chunksize=(1, 1024, 1024), meta=np.ndarray>\n",
-       "    red          (time, y, x) uint16 121MB dask.array<chunksize=(1, 1024, 1024), meta=np.ndarray>\n",
-       "    blue         (time, y, x) uint16 121MB dask.array<chunksize=(1, 1024, 1024), meta=np.ndarray>\n",
-       "    drad         (time, y, x) int16 121MB dask.array<chunksize=(1, 1024, 1024), meta=np.ndarray>\n",
-       "    emis         (time, y, x) int16 121MB dask.array<chunksize=(1, 1024, 1024), meta=np.ndarray>\n",
-       "    emsd         (time, y, x) int16 121MB dask.array<chunksize=(1, 1024, 1024), meta=np.ndarray>\n",
-       "    ...           ...\n",
-       "    swir16       (time, y, x) uint16 121MB dask.array<chunksize=(1, 1024, 1024), meta=np.ndarray>\n",
-       "    swir22       (time, y, x) uint16 121MB dask.array<chunksize=(1, 1024, 1024), meta=np.ndarray>\n",
-       "    coastal      (time, y, x) uint16 121MB dask.array<chunksize=(1, 1024, 1024), meta=np.ndarray>\n",
-       "    qa_pixel     (time, y, x) uint16 121MB dask.array<chunksize=(1, 1024, 1024), meta=np.ndarray>\n",
-       "    qa_radsat    (time, y, x) uint16 121MB dask.array<chunksize=(1, 1024, 1024), meta=np.ndarray>\n",
-       "    qa_aerosol   (time, y, x) uint8 61MB dask.array<chunksize=(1, 1024, 1024), meta=np.ndarray>
" - ], - "text/plain": [ - " Size: 2GB\n", - "Dimensions: (y: 7801, x: 7761, time: 1)\n", - "Coordinates:\n", - " * y (y) float64 62kB -3.713e+06 -3.713e+06 ... -3.947e+06\n", - " * x (x) float64 62kB 3.774e+05 3.774e+05 ... 6.102e+05 6.102e+05\n", - " spatial_ref int32 4B 32656\n", - " * time (time) datetime64[ns] 8B 2023-04-08T23:37:51.630731\n", - "Data variables: (12/19)\n", - " qa (time, y, x) int16 121MB dask.array\n", - " red (time, y, x) uint16 121MB dask.array\n", - " blue (time, y, x) uint16 121MB dask.array\n", - " drad (time, y, x) int16 121MB dask.array\n", - " emis (time, y, x) int16 121MB dask.array\n", - " emsd (time, y, x) int16 121MB dask.array\n", - " ... ...\n", - " swir16 (time, y, x) uint16 121MB dask.array\n", - " swir22 (time, y, x) uint16 121MB dask.array\n", - " coastal (time, y, x) uint16 121MB dask.array\n", - " qa_pixel (time, y, x) uint16 121MB dask.array\n", - " qa_radsat (time, y, x) uint16 121MB dask.array\n", - " qa_aerosol (time, y, x) uint8 61MB dask.array" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "landsat_item = pystac.Item.from_file(\n", - " \"https://planetarycomputer.microsoft.com/api/stac/v1/collections/landsat-c2-l2/items/LC09_L2SP_088084_20230408_02_T2\",\n", - ")\n", - "\n", - "ds = odc.stac.load(\n", - " [landsat_item], chunks={\"x\": 1024, \"y\": 1024}, patch_url=planetary_computer.sign\n", - ")\n", - "ds" - ] - }, - { - "cell_type": "markdown", - "id": "db92558d-4d8c-4afd-873e-76de202048b7", - "metadata": {}, - "source": [ - "Let's prove that we really can access the data within the COGs." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "add6347a-df37-495e-9794-7d86795d18f8", - "metadata": { - "execution": { - "iopub.execute_input": "2025-08-21T19:56:05.449425Z", - "iopub.status.busy": "2025-08-21T19:56:05.449167Z", - "iopub.status.idle": "2025-08-21T19:56:10.768178Z", - "shell.execute_reply": "2025-08-21T19:56:10.767351Z", - "shell.execute_reply.started": "2025-08-21T19:56:05.449403Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 2.68 s, sys: 459 ms, total: 3.14 s\n", - "Wall time: 5.31 s\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.DataArray 'blue' ()> Size: 8B\n",
-       "array(13940.01841915)\n",
-       "Coordinates:\n",
-       "    spatial_ref  int32 4B 32656
" - ], - "text/plain": [ - " Size: 8B\n", - "array(13940.01841915)\n", - "Coordinates:\n", - " spatial_ref int32 4B 32656" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "%%time\n", - "ds.blue.mean().compute()" - ] - }, - { - "cell_type": "markdown", - "id": "65ed7b6d-2e24-46cf-a61f-bc17df20588c", - "metadata": {}, - "source": [ - "For more control over the individual COGs referenced by the assets, you can grab the href off the asset, sign it and then use it directly." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "5873b326-820b-4eec-8ba3-c3207bf8f9ce", - "metadata": { - "execution": { - "iopub.execute_input": "2025-08-21T19:56:10.769208Z", - "iopub.status.busy": "2025-08-21T19:56:10.768974Z", - "iopub.status.idle": "2025-08-21T19:56:11.425713Z", - "shell.execute_reply": "2025-08-21T19:56:11.424874Z", - "shell.execute_reply.started": "2025-08-21T19:56:10.769187Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.DataArray (band: 1, y: 122, x: 122)> Size: 30kB\n",
-       "[14884 values with dtype=uint16]\n",
-       "Coordinates:\n",
-       "  * band         (band) int64 8B 1\n",
-       "  * x            (x) float64 976B 3.783e+05 3.802e+05 ... 6.074e+05 6.093e+05\n",
-       "  * y            (y) float64 976B -3.714e+06 -3.716e+06 ... -3.946e+06\n",
-       "    spatial_ref  int64 8B 0\n",
-       "Attributes:\n",
-       "    AREA_OR_POINT:  Point\n",
-       "    _FillValue:     0\n",
-       "    scale_factor:   1.0\n",
-       "    add_offset:     0.0
" - ], - "text/plain": [ - " Size: 30kB\n", - "[14884 values with dtype=uint16]\n", - "Coordinates:\n", - " * band (band) int64 8B 1\n", - " * x (x) float64 976B 3.783e+05 3.802e+05 ... 6.074e+05 6.093e+05\n", - " * y (y) float64 976B -3.714e+06 -3.716e+06 ... -3.946e+06\n", - " spatial_ref int64 8B 0\n", - "Attributes:\n", - " AREA_OR_POINT: Point\n", - " _FillValue: 0\n", - " scale_factor: 1.0\n", - " add_offset: 0.0" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "landsat_asset = landsat_item.assets[\"blue\"]\n", - "landsat_asset_href = planetary_computer.sign(landsat_asset).href\n", - "\n", - "da = rioxarray.open_rasterio(landsat_asset_href, overview_level=5)\n", - "da" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "156a1c68-2d3f-48f1-8833-92d014b148c1", - "metadata": { - "execution": { - "iopub.execute_input": "2025-08-21T19:56:11.426154Z", - "iopub.status.busy": "2025-08-21T19:56:11.426030Z", - "iopub.status.idle": "2025-08-21T19:56:11.622313Z", - "shell.execute_reply": "2025-08-21T19:56:11.621036Z", - "shell.execute_reply.started": "2025-08-21T19:56:11.426142Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 3.44 ms, sys: 0 ns, total: 3.44 ms\n", - "Wall time: 186 ms\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.DataArray ()> Size: 8B\n",
-       "array(14472.21996775)\n",
-       "Coordinates:\n",
-       "    spatial_ref  int64 8B 0
" - ], - "text/plain": [ - " Size: 8B\n", - "array(14472.21996775)\n", - "Coordinates:\n", - " spatial_ref int64 8B 0" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "%%time\n", - "da.mean()" - ] - }, - { - "cell_type": "markdown", - "id": "b2eceda5-7dc7-482c-a95a-7f24f7698933", - "metadata": {}, - "source": [ - "### Zarr\n", - "\n", - "Read from an asset that references data stored in zarr" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "5176680b-3d58-4c72-bf58-d80051f6de75", - "metadata": { - "execution": { - "iopub.execute_input": "2025-08-21T19:56:11.624714Z", - "iopub.status.busy": "2025-08-21T19:56:11.624335Z", - "iopub.status.idle": "2025-08-21T19:56:22.617465Z", - "shell.execute_reply": "2025-08-21T19:56:22.616757Z", - "shell.execute_reply.started": "2025-08-21T19:56:11.624682Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset> Size: 69GB\n",
-       "Dimensions:                  (time: 14965, y: 584, x: 284, nv: 2)\n",
-       "Coordinates:\n",
-       "    lat                      (y, x) float32 663kB dask.array<chunksize=(584, 284), meta=np.ndarray>\n",
-       "    lon                      (y, x) float32 663kB dask.array<chunksize=(584, 284), meta=np.ndarray>\n",
-       "  * time                     (time) datetime64[ns] 120kB 1980-01-01T12:00:00 ...\n",
-       "  * x                        (x) float32 1kB -5.802e+06 ... -5.519e+06\n",
-       "  * y                        (y) float32 2kB -3.9e+04 -4e+04 ... -6.22e+05\n",
-       "Dimensions without coordinates: nv\n",
-       "Data variables:\n",
-       "    dayl                     (time, y, x) float32 10GB dask.array<chunksize=(365, 584, 284), meta=np.ndarray>\n",
-       "    lambert_conformal_conic  int16 2B ...\n",
-       "    prcp                     (time, y, x) float32 10GB dask.array<chunksize=(365, 584, 284), meta=np.ndarray>\n",
-       "    srad                     (time, y, x) float32 10GB dask.array<chunksize=(365, 584, 284), meta=np.ndarray>\n",
-       "    swe                      (time, y, x) float32 10GB dask.array<chunksize=(365, 584, 284), meta=np.ndarray>\n",
-       "    time_bnds                (time, nv) datetime64[ns] 239kB dask.array<chunksize=(365, 2), meta=np.ndarray>\n",
-       "    tmax                     (time, y, x) float32 10GB dask.array<chunksize=(365, 584, 284), meta=np.ndarray>\n",
-       "    tmin                     (time, y, x) float32 10GB dask.array<chunksize=(365, 584, 284), meta=np.ndarray>\n",
-       "    vp                       (time, y, x) float32 10GB dask.array<chunksize=(365, 584, 284), meta=np.ndarray>\n",
-       "    yearday                  (time) int16 30kB dask.array<chunksize=(365,), meta=np.ndarray>\n",
-       "Attributes:\n",
-       "    Conventions:       CF-1.6\n",
-       "    Version_data:      Daymet Data Version 4.0\n",
-       "    Version_software:  Daymet Software Version 4.0\n",
-       "    citation:          Please see http://daymet.ornl.gov/ for current Daymet ...\n",
-       "    references:        Please see http://daymet.ornl.gov/ for current informa...\n",
-       "    source:            Daymet Software Version 4.0\n",
-       "    start_year:        1980
" - ], - "text/plain": [ - " Size: 69GB\n", - "Dimensions: (time: 14965, y: 584, x: 284, nv: 2)\n", - "Coordinates:\n", - " lat (y, x) float32 663kB dask.array\n", - " lon (y, x) float32 663kB dask.array\n", - " * time (time) datetime64[ns] 120kB 1980-01-01T12:00:00 ...\n", - " * x (x) float32 1kB -5.802e+06 ... -5.519e+06\n", - " * y (y) float32 2kB -3.9e+04 -4e+04 ... -6.22e+05\n", - "Dimensions without coordinates: nv\n", - "Data variables:\n", - " dayl (time, y, x) float32 10GB dask.array\n", - " lambert_conformal_conic int16 2B ...\n", - " prcp (time, y, x) float32 10GB dask.array\n", - " srad (time, y, x) float32 10GB dask.array\n", - " swe (time, y, x) float32 10GB dask.array\n", - " time_bnds (time, nv) datetime64[ns] 239kB dask.array\n", - " tmax (time, y, x) float32 10GB dask.array\n", - " tmin (time, y, x) float32 10GB dask.array\n", - " vp (time, y, x) float32 10GB dask.array\n", - " yearday (time) int16 30kB dask.array\n", - "Attributes:\n", - " Conventions: CF-1.6\n", - " Version_data: Daymet Data Version 4.0\n", - " Version_software: Daymet Software Version 4.0\n", - " citation: Please see http://daymet.ornl.gov/ for current Daymet ...\n", - " references: Please see http://daymet.ornl.gov/ for current informa...\n", - " source: Daymet Software Version 4.0\n", - " start_year: 1980" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "daymet_collection = pystac.Collection.from_file(\n", - " \"https://planetarycomputer.microsoft.com/api/stac/v1/collections/daymet-daily-hi\"\n", - ")\n", - "daymet_asset = daymet_collection.assets[\"zarr-abfs\"]\n", - "daymet_asset_signed = planetary_computer.sign(daymet_asset)\n", - "\n", - "ds = xr.open_dataset(daymet_asset_signed, chunks={})\n", - "ds" - ] - }, - { - "cell_type": "markdown", - "id": "fd4e0c53-90b0-4276-9caf-9014aa0a31f9", - "metadata": {}, - "source": [ - "### Reference file\n", - "\n", - "If the collection has a reference file we can use that.\n", - "\n", - "
\n", - " \n", - "This will not work for kerchunk>=0.2.8 and xpystac<=0.2.0. Track the xpystac ticket here: [xpystac #48](https://github.com/stac-utils/xpystac/issues/48)\n", - " \n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "e69db448-c493-4972-ab1d-a8fb3f8ab71f", - "metadata": { - "execution": { - "iopub.execute_input": "2025-08-21T19:56:22.618362Z", - "iopub.status.busy": "2025-08-21T19:56:22.618022Z", - "iopub.status.idle": "2025-08-21T19:56:33.446768Z", - "shell.execute_reply": "2025-08-21T19:56:33.446285Z", - "shell.execute_reply.started": "2025-08-21T19:56:22.618347Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset> Size: 738GB\n",
-       "Dimensions:  (time: 23741, lat: 600, lon: 1440)\n",
-       "Coordinates:\n",
-       "  * lat      (lat) float64 5kB -59.88 -59.62 -59.38 -59.12 ... 89.38 89.62 89.88\n",
-       "  * lon      (lon) float64 12kB 0.125 0.375 0.625 0.875 ... 359.4 359.6 359.9\n",
-       "  * time     (time) datetime64[us] 190kB 1950-01-01T12:00:00 ... 2014-12-31T1...\n",
-       "Data variables:\n",
-       "    hurs     (time, lat, lon) float32 82GB dask.array<chunksize=(1, 600, 1440), meta=np.ndarray>\n",
-       "    huss     (time, lat, lon) float32 82GB dask.array<chunksize=(1, 600, 1440), meta=np.ndarray>\n",
-       "    pr       (time, lat, lon) float32 82GB dask.array<chunksize=(1, 600, 1440), meta=np.ndarray>\n",
-       "    rlds     (time, lat, lon) float32 82GB dask.array<chunksize=(1, 600, 1440), meta=np.ndarray>\n",
-       "    rsds     (time, lat, lon) float32 82GB dask.array<chunksize=(1, 600, 1440), meta=np.ndarray>\n",
-       "    sfcWind  (time, lat, lon) float32 82GB dask.array<chunksize=(1, 600, 1440), meta=np.ndarray>\n",
-       "    tas      (time, lat, lon) float32 82GB dask.array<chunksize=(1, 600, 1440), meta=np.ndarray>\n",
-       "    tasmax   (time, lat, lon) float32 82GB dask.array<chunksize=(1, 600, 1440), meta=np.ndarray>\n",
-       "    tasmin   (time, lat, lon) float32 82GB dask.array<chunksize=(1, 600, 1440), meta=np.ndarray>\n",
-       "Attributes: (12/22)\n",
-       "    Conventions:           CF-1.7\n",
-       "    activity:              NEX-GDDP-CMIP6\n",
-       "    cmip6_institution_id:  CSIRO-ARCCSS\n",
-       "    cmip6_license:         CC-BY-SA 4.0\n",
-       "    cmip6_source_id:       ACCESS-CM2\n",
-       "    contact:               Dr. Rama Nemani: rama.nemani@nasa.gov, Dr. Bridget...\n",
-       "    ...                    ...\n",
-       "    scenario:              historical\n",
-       "    source:                BCSD\n",
-       "    title:                 ACCESS-CM2, r1i1p1f1, historical, global downscale...\n",
-       "    tracking_id:           16d27564-470f-41ea-8077-f4cc3efa5bfe\n",
-       "    variant_label:         r1i1p1f1\n",
-       "    version:               1.0
" - ], - "text/plain": [ - " Size: 738GB\n", - "Dimensions: (time: 23741, lat: 600, lon: 1440)\n", - "Coordinates:\n", - " * lat (lat) float64 5kB -59.88 -59.62 -59.38 -59.12 ... 89.38 89.62 89.88\n", - " * lon (lon) float64 12kB 0.125 0.375 0.625 0.875 ... 359.4 359.6 359.9\n", - " * time (time) datetime64[us] 190kB 1950-01-01T12:00:00 ... 2014-12-31T1...\n", - "Data variables:\n", - " hurs (time, lat, lon) float32 82GB dask.array\n", - " huss (time, lat, lon) float32 82GB dask.array\n", - " pr (time, lat, lon) float32 82GB dask.array\n", - " rlds (time, lat, lon) float32 82GB dask.array\n", - " rsds (time, lat, lon) float32 82GB dask.array\n", - " sfcWind (time, lat, lon) float32 82GB dask.array\n", - " tas (time, lat, lon) float32 82GB dask.array\n", - " tasmax (time, lat, lon) float32 82GB dask.array\n", - " tasmin (time, lat, lon) float32 82GB dask.array\n", - "Attributes: (12/22)\n", - " Conventions: CF-1.7\n", - " activity: NEX-GDDP-CMIP6\n", - " cmip6_institution_id: CSIRO-ARCCSS\n", - " cmip6_license: CC-BY-SA 4.0\n", - " cmip6_source_id: ACCESS-CM2\n", - " contact: Dr. Rama Nemani: rama.nemani@nasa.gov, Dr. Bridget...\n", - " ... ...\n", - " scenario: historical\n", - " source: BCSD\n", - " title: ACCESS-CM2, r1i1p1f1, historical, global downscale...\n", - " tracking_id: 16d27564-470f-41ea-8077-f4cc3efa5bfe\n", - " variant_label: r1i1p1f1\n", - " version: 1.0" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "cmip6_collection = pystac.Collection.from_file(\n", - " \"https://planetarycomputer.microsoft.com/api/stac/v1/collections/nasa-nex-gddp-cmip6\"\n", - ")\n", - "cmip6_asset = cmip6_collection.assets[\"ACCESS-CM2.historical\"]\n", - "cmip6_asset_signed = planetary_computer.sign(cmip6_asset)\n", - "\n", - "ds = xr.open_dataset(cmip6_asset_signed, chunks={}, patch_url=planetary_computer.sign)\n", - "ds" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/tutorials/pystac-introduction.ipynb b/docs/tutorials/pystac-introduction.ipynb deleted file mode 100644 index f6f656725..000000000 --- a/docs/tutorials/pystac-introduction.ipynb +++ /dev/null @@ -1,6660 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# PySTAC Introduction" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This tutorial includes a basic introduction on reading, writing, and creating STAC objects using Pystac.\n", - "\n", - "It is adapted from the tutorials within the [sat-stac repo](https://github.com/sat-utils/sat-stac/blob/master/tutorial-1.ipynb).\n", - "\n", - "It uses an example stac stored in the `../example-catalog` directory along-side this notebook. The example stac has the following format:\n", - "\n", - "```\n", - "../example-catalog\n", - "├── catalog.json\n", - "└── landsat-8-l1\n", - " ├── 2018-05\n", - " │ └── LC80150322018141LGN00.json\n", - " ├── 2018-06\n", - " │ ├── LC80140332018166LGN00.json\n", - " │ └── LC80300332018166LGN00.json\n", - " ├── 2018-07\n", - " │ └── LC80150332018189LGN00.json\n", - " └── collection.json\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:53.916701Z", - "iopub.status.busy": "2025-09-11T17:34:53.916377Z", - "iopub.status.idle": "2025-09-11T17:34:55.929815Z", - "shell.execute_reply": "2025-09-11T17:34:55.926407Z", - "shell.execute_reply.started": "2025-09-11T17:34:53.916674Z" - } - }, - "outputs": [], - "source": [ - "import pystac" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Working with existing catalogs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Open a root catalog from it's json file" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:55.931977Z", - "iopub.status.busy": "2025-09-11T17:34:55.931669Z", - "iopub.status.idle": "2025-09-11T17:34:55.939757Z", - "shell.execute_reply": "2025-09-11T17:34:55.939055Z", - "shell.execute_reply.started": "2025-09-11T17:34:55.931942Z" - } - }, - "outputs": [], - "source": [ - "cat = pystac.Catalog.from_file(\"../example-catalog/catalog.json\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see all elements of the STAC using the `describe` method" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:55.940651Z", - "iopub.status.busy": "2025-09-11T17:34:55.940428Z", - "iopub.status.idle": "2025-09-11T17:34:55.963983Z", - "shell.execute_reply": "2025-09-11T17:34:55.960476Z", - "shell.execute_reply.started": "2025-09-11T17:34:55.940630Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "* \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n" - ] - } - ], - "source": [ - "cat.describe()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Each STAC object has links that you can use to traverse the STAC tree" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:55.969764Z", - "iopub.status.busy": "2025-09-11T17:34:55.967863Z", - "iopub.status.idle": "2025-09-11T17:34:55.976737Z", - "shell.execute_reply": "2025-09-11T17:34:55.976171Z", - "shell.execute_reply.started": "2025-09-11T17:34:55.969721Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[,\n", - " >,\n", - " >]" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "cat.links" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Pystac has several methods that allow you to access links:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:55.978253Z", - "iopub.status.busy": "2025-09-11T17:34:55.978017Z", - "iopub.status.idle": "2025-09-11T17:34:55.987054Z", - "shell.execute_reply": "2025-09-11T17:34:55.986426Z", - "shell.execute_reply.started": "2025-09-11T17:34:55.978232Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[>]" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Get all child links\n", - "cat.get_child_links()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "or the children directly:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:55.987841Z", - "iopub.status.busy": "2025-09-11T17:34:55.987620Z", - "iopub.status.idle": "2025-09-11T17:34:55.992811Z", - "shell.execute_reply": "2025-09-11T17:34:55.992213Z", - "shell.execute_reply.started": "2025-09-11T17:34:55.987821Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "list(cat.get_children())" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:55.993642Z", - "iopub.status.busy": "2025-09-11T17:34:55.993427Z", - "iopub.status.idle": "2025-09-11T17:34:56.049234Z", - "shell.execute_reply": "2025-09-11T17:34:56.047339Z", - "shell.execute_reply.started": "2025-09-11T17:34:55.993621Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - " <Collection id=landsat-8-l1>\n", - "
\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# or a single child by id\n", - "cat.get_child(\"landsat-8-l1\")" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.050223Z", - "iopub.status.busy": "2025-09-11T17:34:56.049995Z", - "iopub.status.idle": "2025-09-11T17:34:56.054626Z", - "shell.execute_reply": "2025-09-11T17:34:56.054045Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.050201Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - " <Link rel=self target=/home/jsignell/pystac/docs/example-catalog/catalog.json>\n", - "
\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Get a single link by 'rel'\n", - "cat.get_single_link(\"self\")" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.055429Z", - "iopub.status.busy": "2025-09-11T17:34:56.055221Z", - "iopub.status.idle": "2025-09-11T17:34:56.063587Z", - "shell.execute_reply": "2025-09-11T17:34:56.061222Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.055409Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Get item links directly within this catalog (there are none for this catalog)\n", - "cat.get_item_links()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "or the items directly:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.064607Z", - "iopub.status.busy": "2025-09-11T17:34:56.064322Z", - "iopub.status.idle": "2025-09-11T17:34:56.070842Z", - "shell.execute_reply": "2025-09-11T17:34:56.069267Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.064578Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# get item objects\n", - "list(cat.get_items())" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.071984Z", - "iopub.status.busy": "2025-09-11T17:34:56.071747Z", - "iopub.status.idle": "2025-09-11T17:34:56.077495Z", - "shell.execute_reply": "2025-09-11T17:34:56.076770Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.071962Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[,\n", - " ,\n", - " ,\n", - " ]" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# get all items anywhere below this catalog on the STAC tree\n", - "list(cat.get_items(recursive=True))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can access the stac item from a link using the `target` property" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.078735Z", - "iopub.status.busy": "2025-09-11T17:34:56.078490Z", - "iopub.status.idle": "2025-09-11T17:34:56.086510Z", - "shell.execute_reply": "2025-09-11T17:34:56.084230Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.078712Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - ">\n" - ] - } - ], - "source": [ - "link = cat.get_single_link(\"child\")\n", - "print(link)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.087287Z", - "iopub.status.busy": "2025-09-11T17:34:56.087076Z", - "iopub.status.idle": "2025-09-11T17:34:56.093043Z", - "shell.execute_reply": "2025-09-11T17:34:56.092303Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.087267Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "print(link.target)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can convert any stac item to a python dict using the `to_dict` method." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.096152Z", - "iopub.status.busy": "2025-09-11T17:34:56.095881Z", - "iopub.status.idle": "2025-09-11T17:34:56.103324Z", - "shell.execute_reply": "2025-09-11T17:34:56.101132Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.096127Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'type': 'Catalog',\n", - " 'id': 'landsat-stac-collection-catalog',\n", - " 'stac_version': '1.1.0',\n", - " 'description': 'STAC for Landsat data',\n", - " 'links': [{'rel': 'root',\n", - " 'href': './catalog.json',\n", - " 'type': 'application/json',\n", - " 'title': 'STAC for Landsat data'},\n", - " {'rel': 'child',\n", - " 'href': './landsat-8-l1/collection.json',\n", - " 'title': 'Landsat 8 L1'}],\n", - " 'title': 'STAC for Landsat data'}" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "cat.to_dict(include_self_link=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.104964Z", - "iopub.status.busy": "2025-09-11T17:34:56.104626Z", - "iopub.status.idle": "2025-09-11T17:34:56.113491Z", - "shell.execute_reply": "2025-09-11T17:34:56.111517Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.104927Z" - } - }, - "outputs": [], - "source": [ - "# get first (and only in this case) sub-catalog\n", - "subcat = next(cat.get_children())" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.114990Z", - "iopub.status.busy": "2025-09-11T17:34:56.114699Z", - "iopub.status.idle": "2025-09-11T17:34:56.129738Z", - "shell.execute_reply": "2025-09-11T17:34:56.127763Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.114961Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Root Catalog: landsat-stac-collection-catalog\n", - "Sub Catalog: landsat-8-l1\n", - "Sub Catalog parent: landsat-stac-collection-catalog\n", - "Sub Catalog children:\n" - ] - } - ], - "source": [ - "# print some IDs\n", - "print(\"Root Catalog: \", cat.id)\n", - "print(\"Sub Catalog: \", subcat.id)\n", - "print(\"Sub Catalog parent: \", subcat.get_parent().id)\n", - "\n", - "# iterate through child catalogs of the sub-catalog\n", - "print(\"Sub Catalog children:\")\n", - "for child in subcat.get_children():\n", - " print(\" \", child.id)" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.130807Z", - "iopub.status.busy": "2025-09-11T17:34:56.130481Z", - "iopub.status.idle": "2025-09-11T17:34:56.138253Z", - "shell.execute_reply": "2025-09-11T17:34:56.136570Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.130771Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "**Items**\n", - "LC80140332018166LGN00\n", - "LC80150322018141LGN00\n", - "LC80150332018189LGN00\n", - "LC80300332018166LGN00\n" - ] - } - ], - "source": [ - "print(\"\\n**Items**\")\n", - "for i in cat.get_items(recursive=True):\n", - " print(i.id)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Creating new catalogs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can initialize a new Catalog with an id and a description. Note that by default it sets a new catalog as root." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.139204Z", - "iopub.status.busy": "2025-09-11T17:34:56.138960Z", - "iopub.status.idle": "2025-09-11T17:34:56.145229Z", - "shell.execute_reply": "2025-09-11T17:34:56.142675Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.139181Z" - }, - "scrolled": true - }, - "outputs": [], - "source": [ - "# create a Catalog object with JSON\n", - "mycat = pystac.Catalog(id=\"mycat\", description=\"My shiny new STAC catalog\")" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.146216Z", - "iopub.status.busy": "2025-09-11T17:34:56.145970Z", - "iopub.status.idle": "2025-09-11T17:34:56.152561Z", - "shell.execute_reply": "2025-09-11T17:34:56.151741Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.146194Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[>]" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mycat.links" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Adding catalogs to catalogs" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.153760Z", - "iopub.status.busy": "2025-09-11T17:34:56.153409Z", - "iopub.status.idle": "2025-09-11T17:34:56.158861Z", - "shell.execute_reply": "2025-09-11T17:34:56.158231Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.153724Z" - }, - "scrolled": true - }, - "outputs": [], - "source": [ - "# add a new catalog to a root catalog\n", - "kitten = pystac.Catalog(\n", - " id=\"mykitten\", description=\"A child catalog of my shiny new STAC catalog\"\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When you add a child catalog to a parent catalog, the child catalog assumes the root catalog of it's parent. 'Child' and 'parent' links are also added to the parent and child catalogs, respectively." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.159902Z", - "iopub.status.busy": "2025-09-11T17:34:56.159578Z", - "iopub.status.idle": "2025-09-11T17:34:56.171683Z", - "shell.execute_reply": "2025-09-11T17:34:56.168429Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.159872Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[>]" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "kitten.links" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.173473Z", - "iopub.status.busy": "2025-09-11T17:34:56.173193Z", - "iopub.status.idle": "2025-09-11T17:34:56.178246Z", - "shell.execute_reply": "2025-09-11T17:34:56.177623Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.173448Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - " <Link rel=child target=<Catalog id=mykitten>>\n", - "
\n", - "\n", - "
" - ], - "text/plain": [ - ">" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mycat.add_child(kitten)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.179097Z", - "iopub.status.busy": "2025-09-11T17:34:56.178865Z", - "iopub.status.idle": "2025-09-11T17:34:56.183594Z", - "shell.execute_reply": "2025-09-11T17:34:56.182503Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.179071Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[>,\n", - " >]" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "kitten.links" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.184382Z", - "iopub.status.busy": "2025-09-11T17:34:56.184174Z", - "iopub.status.idle": "2025-09-11T17:34:56.188808Z", - "shell.execute_reply": "2025-09-11T17:34:56.188241Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.184363Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[>,\n", - " >]" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mycat.links" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.189795Z", - "iopub.status.busy": "2025-09-11T17:34:56.189531Z", - "iopub.status.idle": "2025-09-11T17:34:56.195712Z", - "shell.execute_reply": "2025-09-11T17:34:56.194797Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.189767Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "* \n", - " * \n" - ] - } - ], - "source": [ - "mycat.describe()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Adding collections to catalogs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the next two steps we will work with Pystac Collections and Items. We will pull them out of our example catalog and add them to the new STAC that we have created." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Collections are Catalogs but also include spatial and temporal extents as well as additional properties. " - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.197285Z", - "iopub.status.busy": "2025-09-11T17:34:56.197016Z", - "iopub.status.idle": "2025-09-11T17:34:56.205927Z", - "shell.execute_reply": "2025-09-11T17:34:56.204972Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.197263Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - " <Collection id=landsat-8-l1>\n", - "
\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# open the Landsat collection\n", - "collection = pystac.Collection.from_file(\n", - " \"../example-catalog/landsat-8-l1/collection.json\"\n", - ")\n", - "collection" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "See the spatial and temporal extent of this collection" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.208716Z", - "iopub.status.busy": "2025-09-11T17:34:56.206890Z", - "iopub.status.idle": "2025-09-11T17:34:56.221383Z", - "shell.execute_reply": "2025-09-11T17:34:56.220708Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.208548Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'spatial': {'bbox': [[-180.0, -90.0, 180.0, 90.0]]},\n", - " 'temporal': {'interval': [['2018-05-21T15:44:59Z', '2018-07-08T15:45:34Z']]}}" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "collection.extent.to_dict()" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.223704Z", - "iopub.status.busy": "2025-09-11T17:34:56.223445Z", - "iopub.status.idle": "2025-09-11T17:34:56.239443Z", - "shell.execute_reply": "2025-09-11T17:34:56.234219Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.223681Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ]" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "collection.links" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.241560Z", - "iopub.status.busy": "2025-09-11T17:34:56.241256Z", - "iopub.status.idle": "2025-09-11T17:34:56.252570Z", - "shell.execute_reply": "2025-09-11T17:34:56.251608Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.241528Z" - }, - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - " <Link rel=child target=<Collection id=landsat-8-l1>>\n", - "
\n", - "\n", - "
" - ], - "text/plain": [ - ">" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# add it to the child catalog created above\n", - "kitten.add_child(collection)" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.253485Z", - "iopub.status.busy": "2025-09-11T17:34:56.253183Z", - "iopub.status.idle": "2025-09-11T17:34:56.266789Z", - "shell.execute_reply": "2025-09-11T17:34:56.264749Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.253441Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[,\n", - " >,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " >]" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "collection.links" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Adding items to collection" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Items are stac objects whose parents can be either Catalogs or Collections. They also have spatio-temporal information and assets. Assets point directly to the data included in the STAC." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.268153Z", - "iopub.status.busy": "2025-09-11T17:34:56.267790Z", - "iopub.status.idle": "2025-09-11T17:34:56.293988Z", - "shell.execute_reply": "2025-09-11T17:34:56.290445Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.268120Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - " <Item id=LC80150322018141LGN00>\n", - "
\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# open a Landsat item\n", - "item = pystac.read_file(\n", - " \"../example-catalog/landsat-8-l1/2018-05/LC80150322018141LGN00.json\"\n", - ")\n", - "item" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.295175Z", - "iopub.status.busy": "2025-09-11T17:34:56.294664Z", - "iopub.status.idle": "2025-09-11T17:34:56.311450Z", - "shell.execute_reply": "2025-09-11T17:34:56.310721Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.294899Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[,\n", - " ,\n", - " ,\n", - " ]" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.links" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.314221Z", - "iopub.status.busy": "2025-09-11T17:34:56.312434Z", - "iopub.status.idle": "2025-09-11T17:34:56.331974Z", - "shell.execute_reply": "2025-09-11T17:34:56.331263Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.314000Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'index': ,\n", - " 'thumbnail': ,\n", - " 'B1': ,\n", - " 'B2': ,\n", - " 'B3': ,\n", - " 'B4': ,\n", - " 'B5': ,\n", - " 'B6': ,\n", - " 'B7': ,\n", - " 'B8': ,\n", - " 'B9': ,\n", - " 'B10': ,\n", - " 'B11': ,\n", - " 'ANG': ,\n", - " 'MTL': ,\n", - " 'BQA': }" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item.assets" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.332797Z", - "iopub.status.busy": "2025-09-11T17:34:56.332548Z", - "iopub.status.idle": "2025-09-11T17:34:56.341914Z", - "shell.execute_reply": "2025-09-11T17:34:56.341088Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.332773Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - " <Link rel=item target=<Item id=LC80150322018141LGN00>>\n", - "
\n", - "\n", - "
" - ], - "text/plain": [ - ">" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# add it to the collection created above\n", - "collection.add_item(item)" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.342876Z", - "iopub.status.busy": "2025-09-11T17:34:56.342655Z", - "iopub.status.idle": "2025-09-11T17:34:56.364261Z", - "shell.execute_reply": "2025-09-11T17:34:56.361623Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.342856Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "* \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n", - " * \n" - ] - } - ], - "source": [ - "# now look at the catalog we've created\n", - "mycat.describe()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Currently, this STAC only exists in memory. We can use `normalize_and_save` to save off the STAC with the canonical \"absolute published\" form:" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.369342Z", - "iopub.status.busy": "2025-09-11T17:34:56.367815Z", - "iopub.status.idle": "2025-09-11T17:34:56.380914Z", - "shell.execute_reply": "2025-09-11T17:34:56.380260Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.369195Z" - } - }, - "outputs": [], - "source": [ - "mycat.normalize_and_save(\n", - " \"pystac-example-absolute\", catalog_type=pystac.CatalogType.ABSOLUTE_PUBLISHED\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice now that the 'parent' link of an item is a absolute HREF:" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.381821Z", - "iopub.status.busy": "2025-09-11T17:34:56.381578Z", - "iopub.status.idle": "2025-09-11T17:34:56.396723Z", - "shell.execute_reply": "2025-09-11T17:34:56.395724Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.381800Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'/home/jsignell/pystac/docs/tutorials/pystac-example-absolute/mykitten/landsat-8-l1/collection.json'" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item = next(mycat.get_items(recursive=True))\n", - "item.get_single_link(\"parent\").get_href()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also normalize and save the catalog to the other types described in the best practices documentation: \"relative published\" and \"self contained\". A self contained catalog contains all relative links, and no self links. Notice how saving a self contained catalog will produce relative links:" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.402199Z", - "iopub.status.busy": "2025-09-11T17:34:56.400504Z", - "iopub.status.idle": "2025-09-11T17:34:56.419288Z", - "shell.execute_reply": "2025-09-11T17:34:56.418402Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.401994Z" - } - }, - "outputs": [], - "source": [ - "mycat.normalize_and_save(\n", - " \"pystac-example-relative\", catalog_type=pystac.CatalogType.SELF_CONTAINED\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": { - "execution": { - "iopub.execute_input": "2025-09-11T17:34:56.420192Z", - "iopub.status.busy": "2025-09-11T17:34:56.419955Z", - "iopub.status.idle": "2025-09-11T17:34:56.425475Z", - "shell.execute_reply": "2025-09-11T17:34:56.424373Z", - "shell.execute_reply.started": "2025-09-11T17:34:56.420171Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'../collection.json'" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "item = next(mycat.get_items(recursive=True))\n", - "item.get_single_link(\"parent\").get_href()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.12" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/pystac-spacenet-tutorial.ipynb b/docs/tutorials/pystac-spacenet-tutorial.ipynb deleted file mode 100644 index b79a2773d..000000000 --- a/docs/tutorials/pystac-spacenet-tutorial.ipynb +++ /dev/null @@ -1,377 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Create and manipulate SpaceNet Vegas STAC" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This tutorial shows how to create and manipulate STACs using pystac.\n", - "\n", - "- Create (in memory) a pystac catalog of [SpaceNet 2 imagery from the Las Vegas AOI](https://spacenetchallenge.github.io/AOI_Lists/AOI_2_Vegas.html) using data hosted in a public s3 bucket\n", - "- Set relative paths for all STAC object\n", - "- Normalize links from a root directory and save the STAC there" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "import sys\n", - "\n", - "sys.path.append(\"..\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You may need install the following packages that are not included in the Python 3 standard library. If you do not have any of these installed, you can do do with pip:\n", - "\n", - "[boto3](https://pypi.org/project/boto3/): `pip install boto3` \n", - "[botocore](https://pypi.org/project/botocore/): `pip install botocore` \n", - "[rasterio](https://pypi.org/project/rasterio/): `pip install rasterio` \n", - "[shapely](https://pypi.org/project/Shapely/): `pip install Shapely` \n", - "[rio-cogeo](https://github.com/cogeotiff/rio-cogeo): `pip install rio-cogeo`" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "from datetime import datetime\n", - "from os.path import basename, join\n", - "\n", - "import boto3\n", - "import rasterio\n", - "from shapely.geometry import GeometryCollection, box, mapping, shape\n", - "\n", - "import pystac" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Create SpaceNet Vegas STAC" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Initialize a STAC for the SpaceNet 2 dataset" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "spacenet = pystac.Catalog(id=\"spacenet\", description=\"SpaceNet 2 STAC\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We do not yet know the spatial extent of the Vegas AOI. We will need to determine it when we download all of the images. As a placeholder we will create a spatial extent of null values." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "sp_extent = pystac.SpatialExtent([None, None, None, None])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The capture date for SpaceNet 2 Vegas imagery is October 22, 2015. Create a python datetime object using that date" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "capture_date = datetime.strptime(\"2015-10-22\", \"%Y-%m-%d\")\n", - "tmp_extent = pystac.TemporalExtent([(capture_date, None)])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create an Extent object that will define both the spatial and temporal extents of the Vegas collection" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "extent = pystac.Extent(sp_extent, tmp_extent)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a collection that will encompass the Vegas data and add to the spacenet catalog" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
\n", - "
\n", - "
    \n", - " \n", - " \n", - " \n", - "
  • \n", - " rel\n", - " \"child\"\n", - "
  • \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
  • \n", - " href\n", - " None\n", - "
  • \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
  • \n", - " type\n", - " \"application/json\"\n", - "
  • \n", - " \n", - " \n", - " \n", - "
\n", - "
\n", - "
" - ], - "text/plain": [ - ">" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "vegas = pystac.Collection(\n", - " id=\"vegas\", description=\"Vegas SpaceNet 2 dataset\", extent=extent\n", - ")\n", - "spacenet.add_child(vegas)" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "* \n", - " * \n" - ] - } - ], - "source": [ - "spacenet.describe()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Find the locations of SpaceNet images. In order to make this example quicker, we will limit the number of scenes that we use to 10." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "client = boto3.client(\"s3\")\n", - "scenes = client.list_objects(\n", - " Bucket=\"spacenet-dataset\",\n", - " Prefix=\"spacenet/SN2_buildings/train/AOI_2_Vegas/PS-RGB/\",\n", - " MaxKeys=20,\n", - ")\n", - "scenes = [s[\"Key\"] for s in scenes[\"Contents\"] if s[\"Key\"].endswith(\".tif\")][0:10]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For each scene, create and item with a defined bounding box. Each item will include the geotiff as an asset. We will add labels in the next section." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "for scene in scenes:\n", - " uri = join(\"s3://spacenet-dataset/\", scene)\n", - " params = {}\n", - " params[\"id\"] = basename(uri).split(\".\")[0]\n", - " with rasterio.open(uri) as src:\n", - " params[\"bbox\"] = list(src.bounds)\n", - " params[\"geometry\"] = mapping(box(*params[\"bbox\"]))\n", - " params[\"datetime\"] = capture_date\n", - " params[\"properties\"] = {}\n", - " i = pystac.Item(**params)\n", - " i.add_asset(\n", - " key=\"image\",\n", - " asset=pystac.Asset(\n", - " href=uri, title=\"Geotiff\", media_type=pystac.MediaType.GEOTIFF\n", - " ),\n", - " )\n", - " vegas.add_item(i)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now reset the spatial extent of the Vegas collection using the geometry objects from from the items we just added." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "bounds = [\n", - " list(\n", - " GeometryCollection(\n", - " [shape(s.geometry) for s in spacenet.get_items(recursive=True)]\n", - " ).bounds\n", - " )\n", - "]\n", - "vegas.extent.spatial = pystac.SpatialExtent(bounds)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Currently, this STAC only exists in memory. We need to set all of the paths based on the root directory we want to save off that catalog too, and then save a \"self contained\" catalog, which will have all links be relative and contain no 'self' links. We can do this by using the `normalize` method to set the HREFs of all of our STAC objects. We'll then validate the catalog, and then save:" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "spacenet.normalize_hrefs(\"spacenet-stac\")" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "10" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "spacenet.validate_all()" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "spacenet.save(catalog_type=pystac.CatalogType.SELF_CONTAINED)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.3" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/img/stac-python.png b/img/stac-python.png new file mode 100644 index 000000000..798c91ea0 Binary files /dev/null and b/img/stac-python.png differ diff --git a/img/stac-python.xcf b/img/stac-python.xcf new file mode 100644 index 000000000..f2b67a870 Binary files /dev/null and b/img/stac-python.xcf differ diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 000000000..312f6dd9d --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,65 @@ +site_name: PySTAC +site_url: https://stac-utils.github.com/pystac/ +repo_name: stac-utils/pystac +repo_url: https://github.com/stac-utils/pystac +theme: + name: material + custom_dir: docs/overrides + logo: img/stac-python.png + favicon: img/stac-python.png + features: + - navigation.instant + - navigation.indexes + palette: + - media: "(prefers-color-scheme)" + toggle: + icon: material/brightness-auto + name: Switch to light mode + - media: "(prefers-color-scheme: light)" + primary: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: default + toggle: + icon: material/brightness-4 + name: Switch to system preference + +watch: + - docs + - src + +nav: + - Home: index.md + - pystac-v2.0.md + +extra: + alias: true + provider: mike + +extra_css: + - overrides/stylesheets/extra.css + +plugins: + - mkdocstrings: + handlers: + python: + options: + show_bases: true + show_root_heading: true + show_root_full_path: true + show_symbol_type_heading: true + show_symbol_type_toc: true + separate_signature: true + show_signature_annotations: true + +markdown_extensions: + - admonition + - pymdownx.details + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format diff --git a/pyproject.toml b/pyproject.toml index b3a30f8b7..4132d57cb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,8 @@ readme = "README.md" authors = [ { name = "Rob Emanuele", email = "rdemanuele@gmail.com" }, { name = "Jon Duckworth", email = "duckontheweb@gmail.com" }, + { name = "Pete Gadomski", email = "pete.gadomski@gmail.com" }, + { name = "Julia Signell", email = "jsignell@gmail.com" }, ] maintainers = [{ name = "Pete Gadomski", email = "pete.gadomski@gmail.com" }] keywords = ["pystac", "imagery", "raster", "catalog", "STAC"] @@ -15,71 +17,48 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", +] +requires-python = ">=3.12" +dependencies = [ + "python-dateutil>=2.9.0.post0", + "typing-extensions>=4.15.0", ] -requires-python = ">=3.10" -dependencies = ["python-dateutil>=2.7.0"] dynamic = ["version"] [project.optional-dependencies] -jinja2 = ["jinja2<4.0"] -orjson = ["orjson>=3.5"] -urllib3 = ["urllib3>=2.6.3"] -validation = ["jsonschema~=4.18"] +obstore = [ + "obspec>=0.1.0", + "obstore>=0.8.2", +] +validation = [ + "jsonschema>=4.25.1", + "referencing>=0.37.0", +] +jinja2 = [ + "jinja2>=3.1.6", +] [dependency-groups] +bench = [ + "asv>=0.6.5", +] dev = [ - "asv>=0.6.4", - "codespell<2.5", - "coverage>=7.6.2", - "doc8>=1.1.2", - "filelock>=3.20.1", # No direct dependency, avoid CVE-2025-68146. + "basedpyright>=1.36.1", "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", + "jsonschema>=4.25.1", + "prek>=0.2.27", + "pytest>=9.0.2", + "pytest-mock>=3.15.1", + "pytest-recording>=0.13.4", "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", - "urllib3>=1.26.19", - "virtualenv>=20.26.6", + "ruff>=0.14.9", ] docs = [ - "adlfs>=2024.12.0", - "aiohttp>=3.13.3", # No direct dependency, avoid CVE-2025-69223 - "boto3>=1.35.39", - "ipython>=8.28.0", - "jinja2>=3.1.4", - "jupyter>=1.1.1", - "jupyterlab>=4.4.8", # No direct dependency, avoid CVE-2025-59842. - "nbsphinx>=0.9.5", - "odc-stac>=0.4.0", - "planetary-computer>=1.0.0", - "pydata-sphinx-theme>=0.15.4", - "rasterio>=1.4.1", - "rioxarray>=0.19.0", - "shapely>=2.0.6", - "sphinx>=8.1.1", - "sphinx-autobuild>=2024.10.3", - "sphinx-design>=0.6.1", - "sphinxcontrib-fulltoc>=1.2.0", - "starlette>=0.49.1", # No direct dependency, avoid CVE-2025-62727. - "xpystac>=0.2.0", - "zarr>=2.18.3", + "mkdocs-material>=9.7.0", + "mkdocstrings-python>=2.0.1", ] [project.urls] @@ -89,45 +68,32 @@ Issues = "https://github.com/stac-utils/pystac/issues" Changelog = "https://github.com/stac-utils/pystac/blob/main/CHANGELOG.md" Discussions = "https://github.com/radiantearth/stac-spec/discussions/categories/stac-software" -[tool.codespell] -skip = "CHANGELOG.md" - -[tool.coverage.run] -branch = true -source = ["pystac"] -omit = ["pystac/extensions/label.py"] - -[tool.coverage.report] -fail_under = 90 -exclude_lines = ["if TYPE_CHECKING:"] - -[tool.doc8] -ignore-path = ["docs/_build", "docs/tutorials"] -max-line-length = 88 - -[tool.mypy] -show_error_codes = true -strict = true - -[[tool.mypy.overrides]] -module = ["jinja2"] -ignore_missing_imports = true +[tool.basedpyright] +ignore = ["tests/v1", "src/pystac/stac_io.py"] +reportExplicitAny = false +reportAny = false +reportImportCycles = false +reportImplicitStringConcatenation = false [tool.ruff] -line-length = 88 -lint.select = ["E", "F", "I"] +lint.select = ["E", "F", "I", "UP"] +extend-exclude = ["tests/v1", "src/pystac/validation", "src/pystac/stac_io.py"] [tool.pytest.ini_options] -filterwarnings = ["error"] +filterwarnings = [ + "ignore", # FIXME: turn this into error +] addopts = "--block-network --record-mode=none" +markers = [ + "passing_v2: v1 tests that are passing on pystac v2", +] -[tool.setuptools.packages.find] -include = ["pystac*"] -exclude = ["tests*", "benchmarks*"] - -[tool.setuptools.dynamic] -version = { attr = "pystac.version.__version__" } +[tool.uv] +default-groups = ["dev", "docs"] [build-system] -requires = ["setuptools>=61.0"] -build-backend = "setuptools.build_meta" +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.version] +path = "src/pystac/version.py" diff --git a/pystac/__init__.py b/pystac/__init__.py deleted file mode 100644 index edf89eb13..000000000 --- a/pystac/__init__.py +++ /dev/null @@ -1,256 +0,0 @@ -# isort: skip_file -""" -PySTAC is a library for working with SpatioTemporal Asset Catalogs (STACs) -""" - -__all__ = [ - "__version__", - "TemplateError", - "STACError", - "STACTypeError", - "DuplicateObjectKeyError", - "ExtensionAlreadyExistsError", - "ExtensionNotImplemented", - "ExtensionTypeError", - "RequiredPropertyMissing", - "STACValidationError", - "DeprecatedWarning", - "MediaType", - "RelType", - "StacIO", - "STACObject", - "STACObjectType", - "Link", - "HIERARCHICAL_LINKS", - "Catalog", - "CatalogType", - "Collection", - "Extent", - "SpatialExtent", - "TemporalExtent", - "Summaries", - "CommonMetadata", - "RangeSummary", - "Item", - "Asset", - "ItemAssetDefinition", - "ItemCollection", - "Provider", - "ProviderRole", - "read_file", - "read_dict", - "write_file", - "get_stac_version", - "set_stac_version", -] - -import warnings -from typing import Any - -from pystac.errors import ( - TemplateError, - STACError, - STACTypeError, - DuplicateObjectKeyError, - ExtensionAlreadyExistsError, - ExtensionNotImplemented, - ExtensionTypeError, - RequiredPropertyMissing, - STACValidationError, - DeprecatedWarning, -) - -from pystac.version import ( - __version__, - get_stac_version, - set_stac_version, -) -from pystac.media_type import MediaType -from pystac.rel_type import RelType -from pystac.stac_io import StacIO -from pystac.stac_object import STACObject, STACObjectType -from pystac.link import Link, HIERARCHICAL_LINKS -from pystac.catalog import Catalog, CatalogType -from pystac.collection import ( - Collection, - Extent, - SpatialExtent, - TemporalExtent, -) -from pystac.common_metadata import CommonMetadata -from pystac.summaries import RangeSummary, Summaries -from pystac.asset import Asset -from pystac.item import Item -from pystac.item_assets import ItemAssetDefinition -from pystac.item_collection import ItemCollection -from pystac.provider import ProviderRole, Provider -from pystac.utils import HREF - -import pystac.extensions.hooks -import pystac.extensions.classification -import pystac.extensions.datacube -import pystac.extensions.eo -import pystac.extensions.file -import pystac.extensions.grid -import pystac.extensions.item_assets - -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - import pystac.extensions.label -import pystac.extensions.mgrs -import pystac.extensions.mlm -import pystac.extensions.pointcloud -import pystac.extensions.projection -import pystac.extensions.raster -import pystac.extensions.sar -import pystac.extensions.sat -import pystac.extensions.scientific -import pystac.extensions.storage -import pystac.extensions.table -import pystac.extensions.timestamps -import pystac.extensions.version -import pystac.extensions.view -import pystac.extensions.xarray_assets - -EXTENSION_HOOKS = pystac.extensions.hooks.RegisteredExtensionHooks( - [ - pystac.extensions.classification.CLASSIFICATION_EXTENSION_HOOKS, - pystac.extensions.datacube.DATACUBE_EXTENSION_HOOKS, - pystac.extensions.eo.EO_EXTENSION_HOOKS, - pystac.extensions.file.FILE_EXTENSION_HOOKS, - pystac.extensions.grid.GRID_EXTENSION_HOOKS, - pystac.extensions.item_assets.ITEM_ASSETS_EXTENSION_HOOKS, - pystac.extensions.label.LABEL_EXTENSION_HOOKS, - pystac.extensions.mgrs.MGRS_EXTENSION_HOOKS, - pystac.extensions.mlm.MLM_EXTENSION_HOOKS, - pystac.extensions.pointcloud.POINTCLOUD_EXTENSION_HOOKS, - pystac.extensions.projection.PROJECTION_EXTENSION_HOOKS, - pystac.extensions.raster.RASTER_EXTENSION_HOOKS, - pystac.extensions.sar.SAR_EXTENSION_HOOKS, - pystac.extensions.sat.SAT_EXTENSION_HOOKS, - pystac.extensions.scientific.SCIENTIFIC_EXTENSION_HOOKS, - pystac.extensions.storage.STORAGE_EXTENSION_HOOKS, - pystac.extensions.table.TABLE_EXTENSION_HOOKS, - pystac.extensions.timestamps.TIMESTAMPS_EXTENSION_HOOKS, - pystac.extensions.version.VERSION_EXTENSION_HOOKS, - pystac.extensions.view.VIEW_EXTENSION_HOOKS, - pystac.extensions.xarray_assets.XARRAY_ASSETS_EXTENSION_HOOKS, - ] -) - - -def read_file(href: HREF, stac_io: StacIO | None = None) -> STACObject: - """Reads a STAC object from a file. - - This method will return either a Catalog, a Collection, or an Item based on what - the file contains. - - This is a convenience method for :meth:`StacIO.read_stac_object - ` - - Args: - href : The HREF to read the object from. - stac_io: Optional :class:`~StacIO` instance to use for I/O operations. If not - provided, will use :meth:`StacIO.default` to create an instance. - - Returns: - The specific STACObject implementation class that is represented - by the JSON read from the file located at HREF. - - Raises: - STACTypeError : If the file at ``href`` does not represent a valid - :class:`~pystac.STACObject`. Note that an :class:`~pystac.ItemCollection` - is not a :class:`~pystac.STACObject` and must be read using - :meth:`ItemCollection.from_file ` - """ - if stac_io is None: - stac_io = StacIO.default() - return stac_io.read_stac_object(href) - - -def write_file( - obj: STACObject, - include_self_link: bool = True, - dest_href: HREF | None = None, - stac_io: StacIO | None = None, -) -> None: - """Writes a STACObject to a file. - - This will write only the Catalog, Collection or Item ``obj``. It will not attempt - to write any other objects that are linked to ``obj``; if you'd like functionality - to save off catalogs recursively see :meth:`Catalog.save `. - - This method will write the JSON of the object to the object's assigned "self" link - or to the dest_href if provided. To set the self link, see - :meth:`STACObject.set_self_href `. - - Convenience method for :meth:`STACObject.from_file ` - - Args: - obj : The STACObject to save. - include_self_link : If ``True``, include the ``"self"`` link with this object. - Otherwise, leave out the self link. - dest_href : Optional HREF to save the file to. If ``None``, the object will be - saved to the object's ``"self"`` href. - stac_io: Optional :class:`~StacIO` instance to use for I/O operations. If not - provided, will use :meth:`StacIO.default` to create an instance. - """ - if stac_io is None: - stac_io = StacIO.default() - import os - - dest_href = None if dest_href is None else str(os.fspath(dest_href)) - obj.save_object( - include_self_link=include_self_link, dest_href=dest_href, stac_io=stac_io - ) - - -def read_dict( - d: dict[str, Any], - href: str | None = None, - root: Catalog | None = None, - stac_io: StacIO | None = None, -) -> STACObject: - """Reads a :class:`~STACObject` or :class:`~ItemCollection` from a JSON-like dict - representing a serialized STAC object. - - This method will return either a :class:`~Catalog`, :class:`~Collection`, - or :class`~Item` based on the contents of the dict. - - This is a convenience method for either - :meth:`StacIO.stac_object_from_dict `. - - Args: - d : The dict to parse. - href : Optional href that is the file location of the object being - parsed. - root : Optional root of the catalog for this object. - If provided, the root's resolved object cache can be used to search for - previously resolved instances of the STAC object. - stac_io: Optional :class:`~StacIO` instance to use for reading. If ``None``, - the default instance will be used. - - Raises: - STACTypeError : If the ``d`` dictionary does not represent a valid - :class:`~pystac.STACObject`. Note that an :class:`~pystac.ItemCollection` - is not a :class:`~pystac.STACObject` and must be read using - :meth:`ItemCollection.from_dict ` - """ - if stac_io is None: - stac_io = StacIO.default() - return stac_io.stac_object_from_dict(d, href, root) - - -def __getattr__(name: str) -> Any: - if name == "validation": - import warnings - import pystac.validation - - warnings.warn( - "pystac.validation will not be automatically imported to the package in " - "pystac v2.0. Instead, import it directly: `import pystac.validation`", - DeprecationWarning, - stacklevel=2, - ) - return pystac.validation - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") diff --git a/pystac/asset.py b/pystac/asset.py deleted file mode 100644 index cfffc1337..000000000 --- a/pystac/asset.py +++ /dev/null @@ -1,395 +0,0 @@ -from __future__ import annotations - -import shutil -from copy import copy, deepcopy -from typing import TYPE_CHECKING, Any, Protocol, TypeVar - -from pystac import MediaType, STACError, common_metadata, utils -from pystac.utils import is_absolute_href, make_absolute_href, make_relative_href - -if TYPE_CHECKING: - from pystac.common_metadata import CommonMetadata - from pystac.extensions.ext import AssetExt - -#: Generalized version of :class:`Asset` -A = TypeVar("A", bound="Asset") - - -class Asset: - """An object that contains a link to data associated with an Item or Collection that - can be downloaded or streamed. - - Args: - href : Link to the asset object. Relative and absolute links are both - allowed. - title : Optional displayed title for clients and users. - description : A description of the Asset providing additional details, - such as how it was processed or created. CommonMark 0.29 syntax MAY be used - for rich text representation. - media_type : Optional description of the media type. Registered Media Types - are preferred. See :class:`~pystac.MediaType` for common media types. - roles : Optional, Semantic roles (i.e. thumbnail, overview, - data, metadata) of the asset. - extra_fields : Optional, additional fields for this asset. This is used - by extensions as a way to serialize and deserialize properties on asset - object JSON. - """ - - href: str - """Link to the asset object. Relative and absolute links are both allowed.""" - - title: str | None - """Optional displayed title for clients and users.""" - - description: str | None - """A description of the Asset providing additional details, such as how it was - processed or created. CommonMark 0.29 syntax MAY be used for rich text - representation.""" - - media_type: str | None - """Optional description of the media type. Registered Media Types are preferred. - See :class:`~pystac.MediaType` for common media types.""" - - roles: list[str] | None - """Optional, Semantic roles (i.e. thumbnail, overview, data, metadata) of the - asset.""" - - owner: Assets | None - """The :class:`~pystac.Item` or :class:`~pystac.Collection` that this asset belongs - to, or ``None`` if it has no owner.""" - - extra_fields: dict[str, Any] - """Optional, additional fields for this asset. This is used by extensions as a - way to serialize and deserialize properties on asset object JSON.""" - - def __init__( - self, - href: str, - title: str | None = None, - description: str | None = None, - media_type: str | None = None, - roles: list[str] | None = None, - extra_fields: dict[str, Any] | None = None, - ) -> None: - self.href = utils.make_posix_style(href) - self.title = title - self.description = description - self.media_type = media_type - self.roles = roles - self.extra_fields = extra_fields or {} - - # The Item which owns this Asset. - self.owner = None - - def set_owner(self, obj: Assets) -> None: - """Sets the owning item of this Asset. - - The owning item will be used to resolve relative HREFs of this asset. - - Args: - obj: The Collection or Item that owns this asset. - """ - self.owner = obj - - def get_absolute_href(self) -> str | None: - """Gets the absolute href for this asset, if possible. - - If this Asset has no associated Item, and the asset HREF is a relative path, - this method will return ``None``. If the Item that owns the Asset has no - self HREF, this will also return ``None``. - - Returns: - str: The absolute HREF of this asset, or None if an absolute HREF could not - be determined. - """ - item_self = self.owner.get_self_href() if self.owner is not None else None - if utils.is_absolute_href(self.href, item_self): - return self.href - else: - if item_self is not None: - return utils.make_absolute_href(self.href, item_self) - return None - - def to_dict(self) -> dict[str, Any]: - """Returns this Asset as a dictionary. - - Returns: - dict: A serialization of the Asset. - """ - - d: dict[str, Any] = {"href": self.href} - - if self.media_type is not None: - d["type"] = self.media_type - - if self.title is not None: - d["title"] = self.title - - if self.description is not None: - d["description"] = self.description - - if self.extra_fields is not None and len(self.extra_fields) > 0: - for k, v in self.extra_fields.items(): - d[k] = v - - if self.roles is not None: - d["roles"] = self.roles - - return d - - def clone(self) -> Asset: - """Clones this asset. Makes a ``deepcopy`` of the - :attr:`~pystac.Asset.extra_fields`. - - Returns: - Asset: The clone of this asset. - """ - cls = self.__class__ - return cls( - href=self.href, - title=self.title, - description=self.description, - media_type=self.media_type, - roles=self.roles, - extra_fields=deepcopy(self.extra_fields), - ) - - def has_role(self, role: str) -> bool: - """Check if a role exists in the Asset role list. - - Args: - role: Role to check for existence. - - Returns: - bool: True if role exists, else False. - """ - if self.roles is None: - return False - else: - return role in self.roles - - @property - def common_metadata(self) -> CommonMetadata: - """Access the asset's common metadata fields as a - :class:`~pystac.CommonMetadata` object.""" - return common_metadata.CommonMetadata(self) - - def __repr__(self) -> str: - return f"" - - def _repr_html_(self) -> str: - from html import escape - - from pystac.html.jinja_env import get_jinja_env - - jinja_env = get_jinja_env() - if jinja_env: - template = jinja_env.get_template("JSON.jinja2") - return str(template.render(dict=self.to_dict(), plain=escape(repr(self)))) - else: - return escape(repr(self)) - - @classmethod - def from_dict(cls: type[A], d: dict[str, Any]) -> A: - """Constructs an Asset from a dict. - - Returns: - Asset: The Asset deserialized from the JSON dict. - """ - d = copy(d) - href = d.pop("href") - media_type = d.pop("type", None) - title = d.pop("title", None) - description = d.pop("description", None) - roles = d.pop("roles", None) - properties = None - if any(d): - properties = d - - return cls( - href=href, - media_type=media_type, - title=title, - description=description, - roles=roles, - extra_fields=properties, - ) - - def move(self, href: str) -> Asset: - """Moves this asset's file to a new location on the local filesystem, - setting the asset href accordingly. - - Modifies the asset in place, and returns the same asset. - - Args: - href: The new asset location. Must be a local path. If relative - it must be relative to the owner object. - - Returns: - Asset: The asset with the updated href. - """ - src = _absolute_href(self.href, self.owner, "move") - dst = _absolute_href(href, self.owner, "move") - shutil.move(src, dst) - self.href = href - return self - - def copy(self, href: str) -> Asset: - """Copies this asset's file to a new location on the local filesystem, - setting the asset href accordingly. - - Modifies the asset in place, and returns the same asset. - - Args: - href: The new asset location. Must be a local path. If relative - it must be relative to the owner object. - - Returns: - Asset: The asset with the updated href. - """ - src = _absolute_href(self.href, self.owner, "copy") - dst = _absolute_href(href, self.owner, "copy") - shutil.copy2(src, dst) - self.href = href - return self - - def delete(self) -> None: - """Delete this asset's file. Does not delete the asset from the item - that owns it. See :meth:`~pystac.Item.delete_asset` for that. - - Does not modify the asset. - """ - import os - - href = _absolute_href(self.href, self.owner, "delete") - os.remove(href) - - @property - def ext(self) -> AssetExt: - """Accessor for extension classes on this asset - - Example:: - - asset.ext.proj.code = "EPSG:4326" - """ - from pystac.extensions.ext import AssetExt - - return AssetExt(stac_object=self) - - -class Assets(Protocol): - """Protocol, with functionality, for STAC objects that have assets.""" - - assets: dict[str, Asset] - """The asset dictionary.""" - - def get_assets( - self, - media_type: str | MediaType | None = None, - role: str | None = None, - ) -> dict[str, Asset]: - """Get this object's assets. - - Args: - media_type: If set, filter the assets such that only those with a - matching ``media_type`` are returned. - role: If set, filter the assets such that only those with a matching - ``role`` are returned. - - Returns: - Dict[str, Asset]: A dictionary of assets that match ``media_type`` - and/or ``role`` if set or else all of this object's assets. - """ - return { - k: deepcopy(v) - for k, v in self.assets.items() - if (media_type is None or v.media_type == media_type) - and (role is None or v.has_role(role)) - } - - def add_asset(self, key: str, asset: Asset) -> None: - """Adds an Asset to this object. - - Args: - key : The unique key of this asset. - asset : The Asset to add. - """ - asset.set_owner(self) - self.assets[key] = asset - - def delete_asset(self, key: str) -> None: - """Deletes the asset at the given key, and removes the asset's data - file from the local filesystem. - - It is an error to attempt to delete an asset's file if it is on a - remote filesystem. - - To delete the asset without removing the file, use `del item.assets["key"]`. - - Args: - key: The unique key of this asset. - """ - asset = self.assets[key] - asset.set_owner(self) - asset.delete() - - del self.assets[key] - - def make_asset_hrefs_relative(self) -> Assets: - """Modify each asset's HREF to be relative to this object's self HREF. - - Returns: - Item: self - """ - self_href = self.get_self_href() - for asset in self.assets.values(): - if is_absolute_href(asset.href, self_href): - if self_href is None: - raise STACError( - "Cannot make asset HREFs relative if no self_href is set." - ) - asset.href = make_relative_href(asset.href, self_href) - return self - - def make_asset_hrefs_absolute(self) -> Assets: - """Modify each asset's HREF to be absolute. - - Any asset HREFs that are relative will be modified to absolute based on this - item's self HREF. - - Returns: - Assets: self - """ - self_href = self.get_self_href() - for asset in self.assets.values(): - if not is_absolute_href(asset.href, self_href): - if self_href is None: - raise STACError( - "Cannot make relative asset HREFs absolute " - "if no self_href is set." - ) - asset.href = make_absolute_href(asset.href, self_href) - return self - - def get_self_href(self) -> str | None: - """Abstract definition of STACObject.get_self_href. - - Needed to make the `make_asset_hrefs_{absolute|relative}` methods pass - type checking. Refactoring out all the link behavior in STACObject to - its own protocol would be too heavy, so we just use this stub instead. - """ - ... - - -def _absolute_href(href: str, owner: Assets | None, action: str = "access") -> str: - item_self = owner.get_self_href() if owner else None - if utils.is_absolute_href(href, item_self): - return href - else: - if item_self is None: - raise ValueError( - f"Cannot {action} file if asset href ('{href}') is relative " - "and owner item is not set. Hint: try using " - ":func:`~pystac.Item.make_asset_hrefs_absolute`" - ) - return utils.make_absolute_href(href, item_self) diff --git a/pystac/catalog.py b/pystac/catalog.py deleted file mode 100644 index 3a81ad2f2..000000000 --- a/pystac/catalog.py +++ /dev/null @@ -1,1307 +0,0 @@ -from __future__ import annotations - -import os -from collections.abc import Callable, Iterable, Iterator -from copy import deepcopy -from typing import ( - TYPE_CHECKING, - Any, - TypeVar, - cast, -) - -import pystac -import pystac.media_type -from pystac.cache import ResolvedObjectCache -from pystac.errors import STACError, STACTypeError -from pystac.layout import ( - APILayoutStrategy, - BestPracticesLayoutStrategy, - HrefLayoutStrategy, - LayoutTemplate, -) -from pystac.link import Link -from pystac.serialization import ( - identify_stac_object, - identify_stac_object_type, - migrate_to_latest, -) -from pystac.stac_object import STACObject, STACObjectType -from pystac.utils import ( - HREF, - StringEnum, - _is_url, - is_absolute_href, - make_absolute_href, - make_relative_href, -) - -if TYPE_CHECKING: - from pystac.asset import Asset - from pystac.collection import Collection - from pystac.extensions.ext import CatalogExt - from pystac.item import Item - -#: Generalized version of :class:`Catalog` -C = TypeVar("C", bound="Catalog") - - -class CatalogType(StringEnum): - SELF_CONTAINED = "SELF_CONTAINED" - """A 'self-contained catalog' is one that is designed for portability. - Users may want to download an online catalog from and be able to use it on their - local computer, so all links need to be relative. - - See: - :stac-spec:`The best practices documentation on self-contained catalogs - ` - """ - - ABSOLUTE_PUBLISHED = "ABSOLUTE_PUBLISHED" - """ - Absolute Published Catalog is a catalog that uses absolute links for everything, - both in the links objects and in the asset hrefs. - - See: - :stac-spec:`The best practices documentation on published catalogs - ` - """ - - RELATIVE_PUBLISHED = "RELATIVE_PUBLISHED" - """ - Relative Published Catalog is a catalog that uses relative links for everything, but - includes an absolute self link at the root catalog, to identify its online location. - - See: - :stac-spec:`The best practices documentation on published catalogs - ` - """ - - @classmethod - def determine_type(cls, stac_json: dict[str, Any]) -> CatalogType | None: - """Determines the catalog type based on a STAC JSON dict. - - Only applies to Catalogs or Collections - - Args: - stac_json : The STAC JSON dict to determine the catalog type - - Returns: - Optional[CatalogType]: The catalog type of the catalog or collection. - Will return None if it cannot be determined. - """ - self_link = None - relative = False - for link in stac_json["links"]: - if link["rel"] == pystac.RelType.SELF: - self_link = link - else: - relative |= not is_absolute_href(link["href"]) - - if self_link: - if relative: - return cls.RELATIVE_PUBLISHED - else: - return cls.ABSOLUTE_PUBLISHED - else: - if relative: - return cls.SELF_CONTAINED - else: - return None - - -class Catalog(STACObject): - """A PySTAC Catalog represents a STAC catalog in memory. - - A Catalog is a :class:`~pystac.STACObject` that may contain children, - which are instances of :class:`~pystac.Catalog` or :class:`~pystac.Collection`, - as well as :class:`~pystac.Item` s. - - Args: - id : Identifier for the catalog. Must be unique within the STAC. - description : Detailed multi-line description to fully explain the catalog. - `CommonMark 0.29 syntax `_ MAY be used for rich - text representation. - title : Optional short descriptive one-line title for the catalog. - stac_extensions : Optional list of extensions the Catalog implements. - href : Optional HREF for this catalog, which be set as the - catalog's self link's HREF. - catalog_type : Optional catalog type for this catalog. Must - be one of the values in :class:`~pystac.CatalogType`. - strategy : The layout strategy to use for setting the - HREFs of the catalog child objects and items. - If not provided, it will default to the strategy of the root and fallback to - :class:`~pystac.layout.BestPracticesLayoutStrategy`. - """ - - catalog_type: CatalogType - """The catalog type. Defaults to :attr:`CatalogType.ABSOLUTE_PUBLISHED`.""" - - description: str - """Detailed multi-line description to fully explain the catalog.""" - - extra_fields: dict[str, Any] - """Extra fields that are part of the top-level JSON properties of the Catalog.""" - - id: str - """Identifier for the catalog.""" - - links: list[Link] - """A list of :class:`~pystac.Link` objects representing all links associated with - this Catalog.""" - - title: str | None - """Optional short descriptive one-line title for the catalog.""" - - stac_extensions: list[str] - """List of extensions the Catalog implements.""" - - _resolved_objects: ResolvedObjectCache - - STAC_OBJECT_TYPE = pystac.STACObjectType.CATALOG - - _stac_io: pystac.StacIO | None = None - """Optional instance of StacIO that will be used by default - for any IO operations on objects contained by this catalog. - Set while reading in a catalog. This is set when a catalog - is read by a StacIO instance.""" - - DEFAULT_FILE_NAME = "catalog.json" - """Default file name that will be given to this STAC object in - a canonical format. - """ - - _fallback_strategy: HrefLayoutStrategy = BestPracticesLayoutStrategy() - """Fallback layout strategy""" - - def __init__( - self, - id: str, - description: str, - title: str | None = None, - stac_extensions: list[str] | None = None, - extra_fields: dict[str, Any] | None = None, - href: str | None = None, - catalog_type: CatalogType = CatalogType.ABSOLUTE_PUBLISHED, - strategy: HrefLayoutStrategy | None = None, - ): - super().__init__(stac_extensions or []) - - self.id = id - self.description = description - self.title = title - if extra_fields is None: - self.extra_fields = {} - else: - self.extra_fields = extra_fields - - self._resolved_objects = ResolvedObjectCache() - - self.add_link(Link.root(self)) - - if href is not None: - self.set_self_href(href) - - self.catalog_type: CatalogType = catalog_type - - self.strategy: HrefLayoutStrategy | None = strategy - - self._resolved_objects.cache(self) - - def __repr__(self) -> str: - return f"" - - def set_root(self, root: Catalog | None) -> None: - STACObject.set_root(self, root) - if root is not None: - root._resolved_objects = ResolvedObjectCache.merge( - root._resolved_objects, self._resolved_objects - ) - - # Walk through resolved object links and update the root - for link in self.links: - if link.rel == pystac.RelType.CHILD or link.rel == pystac.RelType.ITEM: - target = link.target - if isinstance(target, STACObject): - target.set_root(root) - - def is_relative(self) -> bool: - return self.catalog_type in [ - CatalogType.RELATIVE_PUBLISHED, - CatalogType.SELF_CONTAINED, - ] - - def _get_strategy(self, strategy: HrefLayoutStrategy | None) -> HrefLayoutStrategy: - if strategy is not None: - return strategy - elif self.strategy is not None: - return self.strategy - elif root := self.get_root(): - if root.strategy is not None: - return root.strategy - else: - return root._fallback_strategy - else: - return self._fallback_strategy - - def add_child( - self, - child: Catalog | Collection, - title: str | None = None, - strategy: HrefLayoutStrategy | None = None, - set_parent: bool = True, - ) -> Link: - """Adds a link to a child :class:`~pystac.Catalog` or - :class:`~pystac.Collection`. - - This method will set the child's parent to this object and potentially - override its self_link (unless ``set_parent`` is False). - - It will always set its root to this Catalog's root. - - Args: - child : The child to add. - title : Optional title to give to the :class:`~pystac.Link` - strategy : The layout strategy to use for setting the - self href of the child. If not provided, defaults to - the layout strategy of the parent or root and falls back to - :class:`~pystac.layout.BestPracticesLayoutStrategy`. - set_parent : Whether to set the parent on the child as well. - Defaults to True. - - Returns: - Link: The link created for the child - """ - - # Prevent typo confusion - if isinstance(child, pystac.Item): - raise pystac.STACError("Cannot add item as child. Use add_item instead.") - - strategy = self._get_strategy(strategy) - - child.set_root(self.get_root()) - if set_parent: - child.set_parent(self) - else: - child._allow_parent_to_override_href = False - - # set self link - self_href = self.get_self_href() - if self_href and set_parent: - child_href = strategy.get_href(child, self_href) - child.set_self_href(child_href) - - child_link = Link.child(child, title=title) - self.add_link(child_link) - return child_link - - def add_children( - self, - children: Iterable[Catalog | Collection], - strategy: HrefLayoutStrategy | None = None, - ) -> list[Link]: - """Adds links to multiple :class:`~pystac.Catalog` or `~pystac.Collection` - objects. This method will set each child's parent to this object, and their - root to this Catalog's root. - - Args: - children : The children to add. - strategy : The layout strategy to use for setting the - self href of the children. If not provided, defaults to - the layout strategy of the parent or root and falls back to - :class:`~pystac.layout.BestPracticesLayoutStrategy`. - - Returns: - List[Link]: An array of links created for the children - """ - return [self.add_child(child, strategy=strategy) for child in children] - - def add_item( - self, - item: Item, - title: str | None = None, - strategy: HrefLayoutStrategy | None = None, - set_parent: bool = True, - ) -> Link: - """Adds a link to an :class:`~pystac.Item`. - - This method will set the item's parent to this object and potentially - override its self_link (unless ``set_parent`` is False) - - It will always set its root to this Catalog's root. - - Args: - item : The item to add. - title : Optional title to give to the :class:`~pystac.Link` - strategy : The layout strategy to use for setting the - self href of the item. If not provided, defaults to - the layout strategy of the parent or root and falls back to - :class:`~pystac.layout.BestPracticesLayoutStrategy`. - set_parent : Whether to set the parent on the item as well. - Defaults to True. - - Returns: - Link: The link created for the item - """ - - # Prevent typo confusion - if isinstance(item, pystac.Catalog): - raise pystac.STACError("Cannot add catalog as item. Use add_child instead.") - - strategy = self._get_strategy(strategy) - - item.set_root(self.get_root()) - if set_parent: - item.set_parent(self) - else: - item._allow_parent_to_override_href = False - - # set self link - self_href = self.get_self_href() - if self_href and set_parent: - item_href = strategy.get_href(item, self_href) - item.set_self_href(item_href) - - item_link = Link.item(item, title=title) - self.add_link(item_link) - return item_link - - def add_items( - self, - items: Iterable[Item], - strategy: HrefLayoutStrategy | None = None, - ) -> list[Link]: - """Adds links to multiple :class:`Items `. - - This method will set each item's parent to this object, and their root to - this Catalog's root. - - Args: - items : The items to add. - strategy : The layout strategy to use for setting the - self href of the items. If not provided, defaults to - the layout strategy of the parent or root and falls back to - :class:`~pystac.layout.BestPracticesLayoutStrategy`. - - Returns: - List[Link]: A list of links created for the item - """ - return [self.add_item(item, strategy=strategy) for item in items] - - def get_child( - self, id: str, recursive: bool = False, sort_links_by_id: bool = True - ) -> Catalog | Collection | None: - """Gets the child of this catalog with the given ID, if it exists. - - Args: - id : The ID of the child to find. - recursive : If True, search this catalog and all children for the - item; otherwise, only search the children of this catalog. Defaults - to False. - sort_links_by_id : If True, links containing the ID will be checked - first. If links do not contain the ID then setting this to False - will improve performance. Defaults to True. - - Return: - Catalog or Collection or None: The child with the given ID, - or None if not found. - """ - if not recursive: - children: Iterable[pystac.Catalog | pystac.Collection] - if not sort_links_by_id: - children = self.get_children() - else: - - def sort_function(links: list[Link]) -> list[Link]: - return sorted( - links, - key=lambda x: (href := x.get_href()) is None or id not in href, - ) - - children = map( - lambda x: cast(pystac.Catalog | pystac.Collection, x), - self.get_stac_objects( - pystac.RelType.CHILD, modify_links=sort_function - ), - ) - return next((c for c in children if c.id == id), None) - else: - for root, _, _ in self.walk(): - child = root.get_child(id, recursive=False) - if child is not None: - return child - return None - - def get_children(self) -> Iterable[Catalog | Collection]: - """Return all children of this catalog. - - Return: - Iterable[Catalog or Collection]: Iterable of children whose parent - is this catalog. - """ - return map( - lambda x: cast(pystac.Catalog | pystac.Collection, x), - self.get_stac_objects(pystac.RelType.CHILD), - ) - - def get_collections(self) -> Iterable[Collection]: - """Return all children of this catalog that are :class:`~pystac.Collection` - instances.""" - return map( - lambda x: cast(pystac.Collection, x), - self.get_stac_objects(pystac.RelType.CHILD, pystac.Collection), - ) - - def get_all_collections(self) -> Iterable[Collection]: - """Get all collections from this catalog and all subcatalogs. Will traverse - any subcatalogs recursively.""" - yield from self.get_collections() - for child in self.get_children(): - yield from child.get_all_collections() - - def get_child_links(self) -> list[Link]: - """Return all child links of this catalog. - - Return: - List[Link]: List of links of this catalog with ``rel == 'child'`` - """ - return self.get_links( - rel=pystac.RelType.CHILD, - media_type=pystac.media_type.STAC_JSON, - ) - - def clear_children(self) -> None: - """Removes all children from this catalog. - - Return: - Catalog: Returns ``self`` - """ - child_ids = [child.id for child in self.get_children()] - for child_id in child_ids: - self.remove_child(child_id) - - def remove_child(self, child_id: str) -> None: - """Removes a child from this catalog. - - Args: - child_id : The ID of the child to remove. - """ - new_links: list[pystac.Link] = [] - root = self.get_root() - for link in self.links: - if link.rel != pystac.RelType.CHILD: - new_links.append(link) - else: - link.resolve_stac_object(root=root) - child = cast("Catalog", link.target) - if child.id != child_id: - new_links.append(link) - else: - child.set_parent(None) - child.set_root(None) - self.links = new_links - - def get_item(self, id: str, recursive: bool = False) -> Item | None: - """ - DEPRECATED. - - .. deprecated:: 1.8 - Use ``next(pystac.Catalog.get_items(id), None)`` instead. - - Returns an item with a given ID. - - Args: - id : The ID of the item to find. - recursive : If True, search this catalog and all children for the - item; otherwise, only search the items of this catalog. Defaults - to False. - - Return: - Item or None: The item with the given ID, or None if not found. - """ - import warnings - - warnings.warn( - "get_item is deprecated and will be removed in v2. " - "Use next(self.get_items(id), None) instead", - DeprecationWarning, - ) - if not recursive: - return next((i for i in self.get_items() if i.id == id), None) - else: - for root, _, _ in self.walk(): - item = root.get_item(id, recursive=False) - if item is not None: - return item - return None - - def get_items(self, *ids: str, recursive: bool = False) -> Iterator[Item]: - """Return all items or specific items of this catalog. - - Args: - *ids : The IDs of the items to include. - recursive : If True, search this catalog and all children for the - item; otherwise, only search the items of this catalog. Defaults - to False. - - Return: - Iterator[Item]: Generator of items whose parent is this catalog, and - (if recursive) all catalogs or collections connected to this catalog - through child links. - """ - from itertools import chain - - items: Iterator[Item] - if not recursive: - items = map( - lambda x: cast(pystac.Item, x), - self.get_stac_objects(pystac.RelType.ITEM), - ) - else: - items = chain( - self.get_items(recursive=False), - *(child.get_items(recursive=True) for child in self.get_children()), - ) - if ids: - yield from (i for i in items if i.id in ids) - else: - yield from items - - def clear_items(self) -> None: - """Removes all items from this catalog. - - Return: - Catalog: Returns ``self`` - """ - for link in self.get_item_links(): - if link.is_resolved(): - item = cast(pystac.Item, link.target) - item.set_parent(None) - item.set_root(None) - - self.links = [link for link in self.links if link.rel != pystac.RelType.ITEM] - - def remove_item(self, item_id: str) -> None: - """Removes an item from this catalog. - - Args: - item_id : The ID of the item to remove. - """ - new_links: list[pystac.Link] = [] - root = self.get_root() - for link in self.links: - if link.rel != pystac.RelType.ITEM: - new_links.append(link) - else: - link.resolve_stac_object(root=root) - item = cast(pystac.Item, link.target) - if item.id != item_id: - new_links.append(link) - else: - item.set_parent(None) - item.set_root(None) - self.links = new_links - - def get_all_items(self) -> Iterator[Item]: - """ - DEPRECATED. - - .. deprecated:: 1.8 - Use ``pystac.Catalog.get_items(recursive=True)`` instead. - - Get all items from this catalog and all subcatalogs. Will traverse - any subcatalogs recursively. - - Returns: - Generator[Item]: All items that belong to this catalog, and all - catalogs or collections connected to this catalog through - child links. - """ - import warnings - from itertools import chain - - warnings.warn( - "get_all_items is deprecated and will be removed in v2", - DeprecationWarning, - ) - return chain( - self.get_items(), - *(child.get_items(recursive=True) for child in self.get_children()), - ) - - def get_item_links(self) -> list[Link]: - """Return all item links of this catalog. - - Return: - List[Link]: List of links of this catalog with ``rel == 'item'`` - """ - return self.get_links( - rel=pystac.RelType.ITEM, media_type=pystac.media_type.STAC_JSON - ) - - def to_dict( - self, include_self_link: bool = True, transform_hrefs: bool = True - ) -> dict[str, Any]: - links = [ - x - for x in self.links - if x.rel != pystac.RelType.ROOT or x.get_href(transform_hrefs) is not None - ] - if not include_self_link: - links = [x for x in links if x.rel != pystac.RelType.SELF] - - d: dict[str, Any] = { - "type": self.STAC_OBJECT_TYPE.value.title(), - "id": self.id, - "stac_version": pystac.get_stac_version(), - "description": self.description, - "links": [link.to_dict(transform_href=transform_hrefs) for link in links], - } - - if self.stac_extensions: - d["stac_extensions"] = self.stac_extensions - - for key in self.extra_fields: - d[key] = self.extra_fields[key] - - if self.title is not None: - d["title"] = self.title - - return d - - def clone(self) -> Catalog: - cls = self.__class__ - clone = cls( - id=self.id, - description=self.description, - title=self.title, - stac_extensions=self.stac_extensions.copy(), - extra_fields=deepcopy(self.extra_fields), - catalog_type=self.catalog_type, - ) - clone._resolved_objects.cache(clone) - - for link in self.links: - if link.rel == pystac.RelType.ROOT: - # Catalog __init__ sets correct root to clone; don't reset - # if the root link points to self - root_is_self = link.is_resolved() and link.target is self - if not root_is_self: - clone.set_root(None) - clone.add_link(link.clone()) - else: - clone.add_link(link.clone()) - - return clone - - def make_all_asset_hrefs_relative(self) -> None: - """Recursively makes all the HREFs of assets in this catalog relative""" - for item in self.get_items(recursive=True): - item.make_asset_hrefs_relative() - for collection in self.get_all_collections(): - collection.make_asset_hrefs_relative() - - def make_all_asset_hrefs_absolute(self) -> None: - """Recursively makes all the HREFs of assets in this catalog absolute""" - for item in self.get_items(recursive=True): - item.make_asset_hrefs_absolute() - for collection in self.get_all_collections(): - collection.make_asset_hrefs_absolute() - - def normalize_and_save( - self, - root_href: str, - catalog_type: CatalogType | None = None, - strategy: HrefLayoutStrategy | None = None, - stac_io: pystac.StacIO | None = None, - skip_unresolved: bool = False, - ) -> None: - """Normalizes link HREFs to the given root_href, and saves the catalog. - - This is a convenience method that simply calls :func:`Catalog.normalize_hrefs - ` and :func:`Catalog.save ` - in sequence. - - Args: - root_href : The absolute HREF that all links will be normalized - against. - catalog_type : The catalog type that dictates the structure of - the catalog to save. Use a member of :class:`~pystac.CatalogType`. - Defaults to the root catalog.catalog_type or the current catalog - catalog_type if there is no root catalog. - strategy : The layout strategy to use in setting the - HREFS for this catalog. If not provided, defaults to - the layout strategy of the parent or root and falls back to - :class:`~pystac.layout.BestPracticesLayoutStrategy` - stac_io : Optional instance of :class:`~pystac.StacIO` to use. If not - provided, will use the instance set while reading in the catalog, - or the default instance if this is not available. - skip_unresolved : Skip unresolved links when normalizing the tree. - Defaults to False. Because unresolved links are not saved, this - argument can be used to normalize and save only newly-added - objects. - """ - self.normalize_hrefs( - root_href, strategy=strategy, skip_unresolved=skip_unresolved - ) - self.save(catalog_type, stac_io=stac_io) - - def normalize_hrefs( - self, - root_href: str, - strategy: HrefLayoutStrategy | None = None, - skip_unresolved: bool = False, - ) -> None: - """Normalize HREFs will regenerate all link HREFs based on - an absolute root_href and the canonical catalog layout as specified - in the STAC specification's best practices. - - This method mutates the entire catalog tree, unless ``skip_unresolved`` - is True, in which case only resolved links are modified. This is useful - in the case when you have loaded a large catalog and you've added a few - items/children, and you only want to update those newly-added objects, - not the whole tree. - - Args: - root_href : The absolute HREF that all links will be normalized against. - strategy : The layout strategy to use in setting the HREFS - for this catalog. If not provided, defaults to - the layout strategy of the parent or root and falls back to - :class:`~pystac.layout.BestPracticesLayoutStrategy` - skip_unresolved : Skip unresolved links when normalizing the tree. - Defaults to False. - - See: - :stac-spec:`STAC best practices document ` - for the canonical layout of a STAC. - """ - - _strategy = self._get_strategy(strategy) - - # Normalizing requires an absolute path - if not is_absolute_href(root_href): - root_href = make_absolute_href(root_href, os.getcwd(), start_is_dir=True) - - if isinstance(_strategy, APILayoutStrategy) and not _is_url(root_href): - raise STACError("When using APILayoutStrategy the root_href must be a URL") - - def process_item( - item: Item, _root_href: str, is_root: bool, parent: Catalog | None - ) -> Callable[[], None] | None: - if not skip_unresolved: - item.resolve_links() - - # Abort as the intended parent is not the actual parent - # https://github.com/stac-utils/pystac/issues/1116 - if parent is not None and item.get_parent() != parent: - return None - - new_self_href = _strategy.get_href(item, _root_href, is_root) - - def fn() -> None: - item.set_self_href(new_self_href) - - return fn - - def process_catalog( - cat: Catalog, - _root_href: str, - is_root: bool, - parent: Catalog | None = None, - ) -> list[Callable[[], None]]: - setter_funcs: list[Callable[[], None]] = [] - - if not skip_unresolved: - cat.resolve_links() - - # Abort as the intended parent is not the actual parent - # https://github.com/stac-utils/pystac/issues/1116 - if parent is not None and cat.get_parent() != parent: - return setter_funcs - - new_self_href = _strategy.get_href(cat, _root_href, is_root) - new_root = new_self_href - - for link in cat.get_links(): - if skip_unresolved and not link.is_resolved(): - continue - elif link.rel == pystac.RelType.ITEM: - link.resolve_stac_object(root=self.get_root()) - item_fn = process_item( - cast(pystac.Item, link.target), new_root, is_root, cat - ) - if item_fn is not None: - setter_funcs.append(item_fn) - elif link.rel == pystac.RelType.CHILD: - link.resolve_stac_object(root=self.get_root()) - setter_funcs.extend( - process_catalog( - cast(pystac.Catalog | pystac.Collection, link.target), - new_root, - is_root=False, - parent=cat, - ) - ) - - def fn() -> None: - cat.set_self_href(new_self_href) - - setter_funcs.append(fn) - - return setter_funcs - - # Collect functions that will actually mutate the objects. - # Delay mutation as setting hrefs while walking the catalog - # can result in bad links. - setter_funcs = process_catalog(self, root_href, is_root=True) - - for fn in setter_funcs: - fn() - - def generate_subcatalogs( - self, - template: str, - defaults: dict[str, Any] | None = None, - parent_ids: list[str] | None = None, - ) -> list[Catalog]: - """Walks through the catalog and generates subcatalogs - for items based on the template string. - - See :class:`~pystac.layout.LayoutTemplate` - for details on the construction of template strings. This template string - will be applied to the items, and subcatalogs will be created that separate - and organize the items based on template values. - - Args: - template : A template string that - can be consumed by a :class:`~pystac.layout.LayoutTemplate` - defaults : Default values for the template variables - that will be used if the property cannot be found on - the item. - parent_ids : Optional list of the parent catalogs' - identifiers. If the bottom-most subcatalogs already match the - template, no subcatalog is added. - - Returns: - list[Catalog]: List of new catalogs created - """ - result: list[Catalog] = [] - parent_ids = parent_ids or list() - parent_ids.append(self.id) - for child in self.get_children(): - result.extend( - child.generate_subcatalogs( - template, defaults=defaults, parent_ids=parent_ids.copy() - ) - ) - - layout_template = LayoutTemplate(template, defaults=defaults) - - keep_item_links: list[Link] = [] - item_links = [lk for lk in self.links if lk.rel == pystac.RelType.ITEM] - for link in item_links: - link.resolve_stac_object(root=self.get_root()) - item = cast(pystac.Item, link.target) - subcat_ids = layout_template.substitute(item).split("/") - id_iter = reversed(parent_ids) - if all([f"{id}" == next(id_iter, None) for id in reversed(subcat_ids)]): - # Skip items for which the sub-catalog structure already - # matches the template. The list of parent IDs can include more - # elements on the root side, so compare the reversed sequences. - keep_item_links.append(link) - continue - curr_parent = self - for subcat_id in subcat_ids: - subcat = curr_parent.get_child(subcat_id) - if subcat is None: - subcat_desc = "Catalog of items from {} with id {}".format( - curr_parent.id, subcat_id - ) - subcat = pystac.Catalog(id=subcat_id, description=subcat_desc) - curr_parent.add_child(subcat) - result.append(subcat) - curr_parent = subcat - - # resolve collection link so when added back points to correct location - col_link = item.get_single_link(pystac.RelType.COLLECTION) - if col_link is not None: - col_link.resolve_stac_object() - - curr_parent.add_item(item) - - # keep only non-item links and item links that have not been moved elsewhere - self.links = [ - lk for lk in self.links if lk.rel != pystac.RelType.ITEM - ] + keep_item_links - - return result - - def save( - self, - catalog_type: CatalogType | None = None, - dest_href: str | None = None, - stac_io: pystac.StacIO | None = None, - ) -> None: - """Save this catalog and all it's children/item to files determined by the - object's self link HREF or a specified path. - - Args: - catalog_type : The catalog type that dictates the structure of - the catalog to save. Use a member of :class:`~pystac.CatalogType`. - If not supplied, the catalog_type of this catalog will be used. - If that attribute is not set, an exception will be raised. - dest_href : The location where the catalog is to be saved. - If not supplied, the catalog's self link HREF is used to determine - the location of the catalog file and children's files. - stac_io : Optional instance of :class:`~pystac.StacIO` to use. If not - provided, will use the instance set while reading in the catalog, - or the default instance if this is not available. - Note: - If the catalog type is ``CatalogType.ABSOLUTE_PUBLISHED``, - all self links will be included, and hierarchical links be absolute URLs. - If the catalog type is ``CatalogType.RELATIVE_PUBLISHED``, this catalog's - self link will be included, but no child catalog will have self links, and - hierarchical links will be relative URLs - If the catalog type is ``CatalogType.SELF_CONTAINED``, no self links will - be included and hierarchical links will be relative URLs. - """ - - root = self.get_root() - if root is None: - raise Exception("There is no root catalog") - - if catalog_type is not None: - root.catalog_type = catalog_type - - items_include_self_link = root.catalog_type in [CatalogType.ABSOLUTE_PUBLISHED] - - for child_link in self.get_child_links(): - if child_link.is_resolved(): - child = cast(Catalog, child_link.target) - if dest_href is not None: - rel_href = make_relative_href(child.self_href, self.self_href) - child_dest_href = make_absolute_href( - rel_href, dest_href, start_is_dir=True - ) - child.save( - dest_href=os.path.dirname(child_dest_href), - stac_io=stac_io, - ) - else: - child.save(stac_io=stac_io) - - for item_link in self.get_item_links(): - if item_link.is_resolved(): - item = cast(pystac.Item, item_link.target) - if dest_href is not None: - rel_href = make_relative_href(item.self_href, self.self_href) - item_dest_href = make_absolute_href( - rel_href, dest_href, start_is_dir=True - ) - item.save_object( - include_self_link=items_include_self_link, - dest_href=item_dest_href, - stac_io=stac_io, - ) - else: - item.save_object( - include_self_link=items_include_self_link, stac_io=stac_io - ) - - include_self_link = False - # include a self link if this is the root catalog - # or if ABSOLUTE_PUBLISHED catalog - if root.catalog_type == CatalogType.ABSOLUTE_PUBLISHED: - include_self_link = True - elif root.catalog_type != CatalogType.SELF_CONTAINED: - root_link = self.get_root_link() - if root_link and root_link.get_absolute_href() == self.get_self_href(): - include_self_link = True - - catalog_dest_href = None - if dest_href is not None: - rel_href = make_relative_href(self.self_href, self.self_href) - catalog_dest_href = make_absolute_href( - rel_href, dest_href, start_is_dir=True - ) - self.save_object( - include_self_link=include_self_link, - dest_href=catalog_dest_href, - stac_io=stac_io, - ) - if catalog_type is not None: - self.catalog_type = catalog_type - - def walk( - self, - ) -> Iterable[tuple[Catalog, Iterable[Catalog], Iterable[Item]]]: - """Walks through children and items of catalogs. - - For each catalog in the STAC's tree rooted at this catalog (including this - catalog itself), it yields a 3-tuple (root, subcatalogs, items). The root in - that 3-tuple refers to the current catalog being walked, the subcatalogs are any - catalogs or collections for which the root is a parent, and items represents - any items that have the root as a parent. - - This has similar functionality to Python's :func:`os.walk`. - - Returns: - Generator[(Catalog, Generator[Catalog], Generator[Item])]: A generator that - yields a 3-tuple (parent_catalog, children, items). - """ - children = self.get_children() - items = self.get_items() - - yield self, children, items - for child in self.get_children(): - yield from child.walk() - - def fully_resolve(self) -> None: - """Resolves every link in this catalog. - - Useful if, e.g., you'd like to read a catalog from a filesystem, upgrade - every object in the catalog to the latest STAC version, and save it back - to the filesystem. By default, :py:meth:`~pystac.Catalog.save` skips - unresolved links. - """ - for _, _, items in self.walk(): - # items is a generator, so we need to consume it to resolve the - # items - for item in items: - pass - - def validate_all(self, max_items: int | None = None, recursive: bool = True) -> int: - """Validates each catalog, collection, item contained within this catalog. - - Walks through the children and items of the catalog and validates each - stac object. - - Args: - max_items : The maximum number of STAC items to validate. Default - is None which means, validate them all. - recursive : Whether to validate catalog, collections, and items contained - within child objects. - - Returns: - int : Number of STAC items validated. - - Raises: - STACValidationError: Raises this error on any item that is invalid. - Will raise on the first invalid stac object encountered. - """ - n = 0 - self.validate() - for child in self.get_children(): - if recursive: - inner_max_items = None if max_items is None else max_items - n - n += child.validate_all(max_items=inner_max_items, recursive=True) - else: - child.validate() - for item in self.get_items(): - if max_items is not None and n >= max_items: - break - item.validate() - n += 1 - return n - - def _object_links(self) -> list[str | pystac.RelType]: - return [ - pystac.RelType.CHILD, - pystac.RelType.ITEM, - *pystac.EXTENSION_HOOKS.get_extended_object_links(self), - ] - - def map_items( - self, - item_mapper: Callable[[Item], Item | list[Item]], - ) -> Catalog: - """Creates a copy of a catalog, with each item passed through the - item_mapper function. - - Args: - item_mapper : A function that takes in an item, and returns - either an item or list of items. The item that is passed into the - item_mapper is a copy, so the method can mutate it safely. - - Returns: - Catalog: A full copy of this catalog, with items manipulated according - to the item_mapper function. - """ - - new_cat = self.full_copy() - - def process_catalog(catalog: Catalog) -> None: - for child in catalog.get_children(): - process_catalog(child) - - item_links: list[Link] = [] - for item_link in catalog.get_item_links(): - item_link.resolve_stac_object(root=self.get_root()) - mapped = item_mapper(cast(pystac.Item, item_link.target)) - if mapped is None: - raise Exception("item_mapper cannot return None.") - if isinstance(mapped, pystac.Item): - item_link.target = mapped - item_links.append(item_link) - else: - for i in mapped: - new_link = item_link.clone() - new_link.target = i - item_links.append(new_link) - catalog.clear_items() - catalog.add_links(item_links) - - process_catalog(new_cat) - return new_cat - - def map_assets( - self, - asset_mapper: Callable[ - [str, Asset], - Asset | tuple[str, Asset] | dict[str, Asset], - ], - ) -> Catalog: - """Creates a copy of a catalog, with each Asset for each Item passed - through the asset_mapper function. - - Args: - asset_mapper : A function that takes in a key and an Asset, and - returns either an Asset, a (key, Asset), or a dictionary of Assets with - unique keys. The Asset that is passed into the item_mapper is a copy, - so the method can mutate it safely. - - Returns: - Catalog: A full copy of this catalog, with assets manipulated according - to the asset_mapper function. - """ - - def apply_asset_mapper( - tup: tuple[str, Asset], - ) -> list[tuple[str, pystac.Asset]]: - k, v = tup - result = asset_mapper(k, v) - if result is None: - raise Exception("asset_mapper cannot return None.") - if isinstance(result, pystac.Asset): - return [(k, result)] - elif isinstance(result, tuple): - return [result] - else: - assets = list(result.items()) - if len(assets) < 1: - raise Exception("asset_mapper must return a non-empty list") - return assets - - def item_mapper(item: pystac.Item) -> pystac.Item: - new_assets = [ - x - for result in map(apply_asset_mapper, item.assets.items()) - for x in result - ] - item.assets = dict(new_assets) - return item - - return self.map_items(item_mapper) - - def describe(self, include_hrefs: bool = False, _indent: int = 0) -> None: - """Prints out information about this Catalog and all contained - STACObjects. - - Args: - include_hrefs (bool) - If True, print out each object's self link - HREF along with the object ID. - """ - s = "{}* {}".format(" " * _indent, self) - if include_hrefs: - s += f" {self.get_self_href()}" - print(s) - for child in self.get_children(): - child.describe(include_hrefs=include_hrefs, _indent=_indent + 4) - for item in self.get_items(): - s = "{}* {}".format(" " * (_indent + 2), item) - if include_hrefs: - s += f" {item.get_self_href()}" - print(s) - - @classmethod - def from_dict( - cls: type[C], - d: dict[str, Any], - href: str | None = None, - root: Catalog | None = None, - migrate: bool = True, - preserve_dict: bool = True, - ) -> C: - if migrate: - info = identify_stac_object(d) - d = migrate_to_latest(d, info) - - if not cls.matches_object_type(d): - raise STACTypeError(d, cls) - - catalog_type = CatalogType.determine_type(d) - - if preserve_dict: - d = deepcopy(d) - - id = d.pop("id") - description = d.pop("description") - title = d.pop("title", None) - stac_extensions = d.pop("stac_extensions", None) - links = d.pop("links") - - d.pop("stac_version") - - cat = cls( - id=id, - description=description, - title=title, - stac_extensions=stac_extensions, - extra_fields=d, - href=href, - catalog_type=catalog_type or CatalogType.ABSOLUTE_PUBLISHED, - ) - - for link in links: - if link["rel"] == pystac.RelType.ROOT: - # Remove the link that's generated in Catalog's constructor. - cat.remove_links(pystac.RelType.ROOT) - - if link["rel"] != pystac.RelType.SELF or href is None: - cat.add_link(Link.from_dict(link)) - - if root: - cat.set_root(root) - - return cat - - def full_copy( - self, root: Catalog | None = None, parent: Catalog | None = None - ) -> Catalog: - return cast(Catalog, super().full_copy(root, parent)) - - @classmethod - def from_file(cls: type[C], href: HREF, stac_io: pystac.StacIO | None = None) -> C: - if stac_io is None: - stac_io = pystac.StacIO.default() - - result = super().from_file(href, stac_io) - result._stac_io = stac_io - - return result - - @classmethod - def matches_object_type(cls, d: dict[str, Any]) -> bool: - return identify_stac_object_type(d) == STACObjectType.CATALOG - - @property - def ext(self) -> CatalogExt: - """Accessor for extension classes on this catalog - - Example:: - - print(collection.ext.version) - """ - from pystac.extensions.ext import CatalogExt - - return CatalogExt(stac_object=self) diff --git a/pystac/client.py b/pystac/client.py deleted file mode 100644 index f429bf10b..000000000 --- a/pystac/client.py +++ /dev/null @@ -1,23 +0,0 @@ -# mypy: ignore-errors - -_import_error_message = ( - "pystac-client is not installed.\n\n" - "Please install pystac-client:\n\n" - " pip install pystac-client" -) - -try: - from pystac_client import * # noqa: F403 -except ImportError as e: - if e.msg == "No module named 'pystac_client'": - raise ImportError(_import_error_message) from e - else: - raise - - -def __getattr__(value: str): - try: - import pystac_client - except ImportError as e: - raise ImportError(_import_error_message) from e - return getattr(pystac_client, value) diff --git a/pystac/collection.py b/pystac/collection.py deleted file mode 100644 index 536fd2830..000000000 --- a/pystac/collection.py +++ /dev/null @@ -1,899 +0,0 @@ -from __future__ import annotations - -from collections.abc import Iterable, Sequence -from copy import deepcopy -from datetime import datetime, timezone -from typing import ( - TYPE_CHECKING, - Any, - Optional, - TypeVar, - cast, -) - -import pystac -from pystac import CatalogType, STACObjectType -from pystac.asset import Asset, Assets -from pystac.catalog import Catalog -from pystac.errors import DeprecatedWarning, ExtensionNotImplemented, STACTypeError -from pystac.item_assets import ItemAssetDefinition, _ItemAssets -from pystac.layout import HrefLayoutStrategy -from pystac.link import Link -from pystac.provider import Provider -from pystac.serialization import ( - identify_stac_object, - identify_stac_object_type, - migrate_to_latest, -) -from pystac.summaries import Summaries -from pystac.utils import ( - datetime_to_str, - str_to_datetime, -) - -if TYPE_CHECKING: - from pystac.extensions.ext import CollectionExt - from pystac.item import Item - -#: Generalized version of :class:`Collection` -C = TypeVar("C", bound="Collection") - -Bboxes = list[list[float | int]] -TemporalIntervals = list[list[datetime]] | list[list[Optional[datetime]]] -TemporalIntervalsLike = TemporalIntervals | list[datetime] | list[Optional[datetime]] - - -class SpatialExtent: - """Describes the spatial extent of a Collection. - - Args: - bboxes : A list of bboxes that represent the spatial - extent of the collection. Each bbox can be 2D or 3D. The length of the bbox - array must be 2*n where n is the number of dimensions. For example, a - 2D Collection with only one bbox would be [[xmin, ymin, xmax, ymax]] - - extra_fields : Dictionary containing additional top-level fields defined on the - Spatial Extent object. - """ - - bboxes: Bboxes - """A list of bboxes that represent the spatial - extent of the collection. Each bbox can be 2D or 3D. The length of the bbox - array must be 2*n where n is the number of dimensions. For example, a - 2D Collection with only one bbox would be [[xmin, ymin, xmax, ymax]]""" - - extra_fields: dict[str, Any] - """Dictionary containing additional top-level fields defined on the Spatial - Extent object.""" - - def __init__( - self, - bboxes: Bboxes | Sequence[float | int], - extra_fields: dict[str, Any] | None = None, - ) -> None: - if not isinstance(bboxes, list): - raise TypeError("bboxes must be a list") - - # A common mistake is to pass in a single bbox instead of a list of bboxes. - # Account for this by transforming the input in that case. - if isinstance(bboxes[0], (float, int)): - self.bboxes = [cast(list[float | int], bboxes)] - else: - self.bboxes = cast(Bboxes, bboxes) - - self.extra_fields = extra_fields or {} - - def to_dict(self) -> dict[str, Any]: - """Returns this spatial extent as a dictionary. - - Returns: - dict: A serialization of the SpatialExtent. - """ - d = {"bbox": self.bboxes, **self.extra_fields} - return d - - def clone(self) -> SpatialExtent: - """Clones this object. - - Returns: - SpatialExtent: The clone of this object. - """ - cls = self.__class__ - return cls( - bboxes=deepcopy(self.bboxes), extra_fields=deepcopy(self.extra_fields) - ) - - @staticmethod - def from_dict(d: dict[str, Any]) -> SpatialExtent: - """Constructs a SpatialExtent from a dict. - - Returns: - SpatialExtent: The SpatialExtent deserialized from the JSON dict. - """ - return SpatialExtent( - bboxes=d["bbox"], extra_fields={k: v for k, v in d.items() if k != "bbox"} - ) - - @staticmethod - def from_coordinates( - coordinates: list[Any], extra_fields: dict[str, Any] | None = None - ) -> SpatialExtent: - """Constructs a SpatialExtent from a set of coordinates. - - This method will only produce a single bbox that covers all points - in the coordinate set. - - Args: - coordinates : Coordinates to derive the bbox from. - extra_fields : Dictionary containing additional top-level fields defined on - the SpatialExtent object. - - Returns: - SpatialExtent: A SpatialExtent with a single bbox that covers the - given coordinates. - """ - - def process_coords( - coord_lists: list[Any], - xmin: float | None = None, - ymin: float | None = None, - xmax: float | None = None, - ymax: float | None = None, - ) -> tuple[float | None, float | None, float | None, float | None]: - for coord in coord_lists: - if isinstance(coord[0], list): - xmin, ymin, xmax, ymax = process_coords( - coord, xmin, ymin, xmax, ymax - ) - else: - x, y = coord - if xmin is None or x < xmin: - xmin = x - elif xmax is None or xmax < x: - xmax = x - if ymin is None or y < ymin: - ymin = y - elif ymax is None or ymax < y: - ymax = y - return xmin, ymin, xmax, ymax - - xmin, ymin, xmax, ymax = process_coords(coordinates) - - if xmin is None or ymin is None or xmax is None or ymax is None: - raise ValueError( - f"Could not determine bounds from coordinate sequence {coordinates}" - ) - - return SpatialExtent( - bboxes=[[xmin, ymin, xmax, ymax]], extra_fields=extra_fields - ) - - -class TemporalExtent: - """Describes the temporal extent of a Collection. - - Args: - intervals : A list of two datetimes wrapped in a list, - representing the temporal extent of a Collection. Open date ranges are - supported by setting either the start (the first element of the interval) - or the end (the second element of the interval) to None. - - extra_fields : Dictionary containing additional top-level fields defined on the - Temporal Extent object. - Note: - Datetimes are required to be in UTC. - """ - - intervals: TemporalIntervals - """A list of two datetimes wrapped in a list, - representing the temporal extent of a Collection. Open date ranges are - represented by either the start (the first element of the interval) or the - end (the second element of the interval) being None.""" - - extra_fields: dict[str, Any] - """Dictionary containing additional top-level fields defined on the Temporal - Extent object.""" - - def __init__( - self, - intervals: TemporalIntervals | Sequence[datetime | None], - extra_fields: dict[str, Any] | None = None, - ): - if not isinstance(intervals, list): - raise TypeError("intervals must be a list") - # A common mistake is to pass in a single interval instead of a - # list of intervals. Account for this by transforming the input - # in that case. - if isinstance(intervals[0], datetime) or intervals[0] is None: - self.intervals = [cast(list[Optional[datetime]], intervals)] - else: - self.intervals = cast(TemporalIntervals, intervals) - - self.extra_fields = extra_fields or {} - - def to_dict(self) -> dict[str, Any]: - """Returns this temporal extent as a dictionary. - - Returns: - dict: A serialization of the TemporalExtent. - """ - encoded_intervals: list[list[str | None]] = [] - for i in self.intervals: - start = None - end = None - - if i[0] is not None: - start = datetime_to_str(i[0]) - - if i[1] is not None: - end = datetime_to_str(i[1]) - - encoded_intervals.append([start, end]) - - d = {"interval": encoded_intervals, **self.extra_fields} - return d - - def clone(self) -> TemporalExtent: - """Clones this object. - - Returns: - TemporalExtent: The clone of this object. - """ - cls = self.__class__ - return cls( - intervals=deepcopy(self.intervals), extra_fields=deepcopy(self.extra_fields) - ) - - @staticmethod - def from_dict(d: dict[str, Any]) -> TemporalExtent: - """Constructs an TemporalExtent from a dict. - - Returns: - TemporalExtent: The TemporalExtent deserialized from the JSON dict. - """ - parsed_intervals: list[list[datetime | None]] = [] - for i in d["interval"]: - if isinstance(i, str): - import warnings - - # d["interval"] is a list of strings, so we correct the list and - # try again - # https://github.com/stac-utils/pystac/issues/1221 - warnings.warn( - "A collection's temporal extent should be a list of lists, but " - "is instead a " - "list of strings. pystac is fixing this issue and continuing " - "deserialization, but note that the source " - "collection is invalid STAC.", - UserWarning, - ) - d["interval"] = [d["interval"]] - return TemporalExtent.from_dict(d) - start = None - end = None - - if i[0]: - start = str_to_datetime(i[0]) - if i[1]: - end = str_to_datetime(i[1]) - parsed_intervals.append([start, end]) - - return TemporalExtent( - intervals=parsed_intervals, - extra_fields={k: v for k, v in d.items() if k != "interval"}, - ) - - @staticmethod - def from_now() -> TemporalExtent: - """Constructs an TemporalExtent with a single open interval that has - the start time as the current time. - - Returns: - TemporalExtent: The resulting TemporalExtent. - """ - return TemporalExtent( - intervals=[[datetime.now(timezone.utc).replace(microsecond=0), None]] - ) - - -class Extent: - """Describes the spatiotemporal extents of a Collection. - - Args: - spatial : Potential spatial extent covered by the collection. - temporal : Potential temporal extent covered by the collection. - extra_fields : Dictionary containing additional top-level fields defined on the - Extent object. - """ - - spatial: SpatialExtent - """Potential spatial extent covered by the collection.""" - - temporal: TemporalExtent - """Potential temporal extent covered by the collection.""" - - extra_fields: dict[str, Any] - """Dictionary containing additional top-level fields defined on the Extent - object.""" - - def __init__( - self, - spatial: SpatialExtent, - temporal: TemporalExtent, - extra_fields: dict[str, Any] | None = None, - ): - self.spatial = spatial - self.temporal = temporal - self.extra_fields = extra_fields or {} - - def to_dict(self) -> dict[str, Any]: - """Returns this extent as a dictionary. - - Returns: - dict: A serialization of the Extent. - """ - d = { - "spatial": self.spatial.to_dict(), - "temporal": self.temporal.to_dict(), - **self.extra_fields, - } - - return d - - def clone(self) -> Extent: - """Clones this object. - - Returns: - Extent: The clone of this extent. - """ - cls = self.__class__ - return cls( - spatial=self.spatial.clone(), - temporal=self.temporal.clone(), - extra_fields=deepcopy(self.extra_fields), - ) - - @staticmethod - def from_dict(d: dict[str, Any]) -> Extent: - """Constructs an Extent from a dict. - - Returns: - Extent: The Extent deserialized from the JSON dict. - """ - return Extent( - spatial=SpatialExtent.from_dict(d["spatial"]), - temporal=TemporalExtent.from_dict(d["temporal"]), - extra_fields={ - k: v for k, v in d.items() if k not in {"spatial", "temporal"} - }, - ) - - @staticmethod - def from_items( - items: Iterable[Item], extra_fields: dict[str, Any] | None = None - ) -> Extent: - """Create an Extent based on the datetimes and bboxes of a list of items. - - Args: - items : A list of items to derive the extent from. - extra_fields : Optional dictionary containing additional top-level fields - defined on the Extent object. - - Returns: - Extent: An Extent that spatially and temporally covers all of the - given items. - """ - from dateutil import tz - - bounds_values: list[list[float]] = [ - [float("inf")], - [float("inf")], - [float("-inf")], - [float("-inf")], - ] - datetimes: list[datetime] = [] - starts: list[datetime] = [] - ends: list[datetime] = [] - - for item in items: - if item.bbox is not None: - for i in range(0, 4): - bounds_values[i].append(item.bbox[i]) - if item.datetime is not None: - datetimes.append(item.datetime) - if item.common_metadata.start_datetime is not None: - starts.append(item.common_metadata.start_datetime) - if item.common_metadata.end_datetime is not None: - ends.append(item.common_metadata.end_datetime) - - if not any(datetimes + starts): - start_timestamp = None - else: - start_timestamp = min( - [ - dt if dt.tzinfo else dt.replace(tzinfo=tz.UTC) - for dt in datetimes + starts - ] - ) - if not any(datetimes + ends): - end_timestamp = None - else: - end_timestamp = max( - [ - dt if dt.tzinfo else dt.replace(tzinfo=tz.UTC) - for dt in datetimes + ends - ] - ) - - spatial = SpatialExtent( - [ - [ - min(bounds_values[0]), - min(bounds_values[1]), - max(bounds_values[2]), - max(bounds_values[3]), - ] - ] - ) - temporal = TemporalExtent([[start_timestamp, end_timestamp]]) - - return Extent(spatial=spatial, temporal=temporal, extra_fields=extra_fields) - - -class Collection(Catalog, Assets): - """A Collection extends the Catalog spec with additional metadata that helps - enable discovery. - - Args: - id : Identifier for the collection. Must be unique within the STAC. - description : Detailed multi-line description to fully explain the - collection. `CommonMark 0.29 syntax `_ MAY - be used for rich text representation. - extent : Spatial and temporal extents that describe the bounds of - all items contained within this Collection. - title : Optional short descriptive one-line title for the - collection. - stac_extensions : Optional list of extensions the Collection - implements. - href : Optional HREF for this collection, which be set as the - collection's self link's HREF. - catalog_type : Optional catalog type for this catalog. Must - be one of the values in :class`~pystac.CatalogType`. - license : Collection's license(s) as a - `SPDX License identifier `_, - or `other`. If collection includes data with multiple - different licenses, use `other` and add a link for - each. The licenses `various` and `proprietary` are deprecated. - Defaults to 'other'. - keywords : Optional list of keywords describing the collection. - providers : Optional list of providers of this Collection. - summaries : An optional map of property summaries, - either a set of values or statistics such as a range. - extra_fields : Extra fields that are part of the top-level - JSON properties of the Collection. - assets : A dictionary mapping string keys to :class:`~pystac.Asset` objects. All - :class:`~pystac.Asset` values in the dictionary will have their - :attr:`~pystac.Asset.owner` attribute set to the created Collection. - strategy : The layout strategy to use for setting the - HREFs of the catalog child objects and items. - If not provided, it will default to strategy of the parent and fallback to - :class:`~pystac.layout.BestPracticesLayoutStrategy`. - """ - - description: str - """Detailed multi-line description to fully explain the collection.""" - - extent: Extent - """Spatial and temporal extents that describe the bounds of all items contained - within this Collection.""" - - id: str - """Identifier for the collection.""" - - stac_extensions: list[str] - """List of extensions the Collection implements.""" - - title: str | None - """Optional short descriptive one-line title for the collection.""" - - keywords: list[str] | None - """Optional list of keywords describing the collection.""" - - providers: list[Provider] | None - """Optional list of providers of this Collection.""" - - summaries: Summaries - """A map of property summaries, either a set of values or statistics such as a - range.""" - - links: list[Link] - """A list of :class:`~pystac.Link` objects representing all links associated with - this Collection.""" - - extra_fields: dict[str, Any] - """Extra fields that are part of the top-level JSON properties of the Collection.""" - - STAC_OBJECT_TYPE = STACObjectType.COLLECTION - - DEFAULT_FILE_NAME = "collection.json" - """Default file name that will be given to this STAC object - in a canonical format.""" - - def __init__( - self, - id: str, - description: str, - extent: Extent, - title: str | None = None, - stac_extensions: list[str] | None = None, - href: str | None = None, - extra_fields: dict[str, Any] | None = None, - catalog_type: CatalogType | None = None, - license: str = "other", - keywords: list[str] | None = None, - providers: list[Provider] | None = None, - summaries: Summaries | None = None, - assets: dict[str, Asset] | None = None, - strategy: HrefLayoutStrategy | None = None, - ): - super().__init__( - id, - description, - title, - stac_extensions, - extra_fields, - href, - catalog_type or CatalogType.ABSOLUTE_PUBLISHED, - strategy, - ) - self.extent = extent - self.license = license - - self.stac_extensions: list[str] = stac_extensions or [] - self.keywords = keywords - self.providers = providers - self.summaries = summaries or Summaries.empty() - self._item_assets: _ItemAssets | None = None - - self.assets = {} - if assets is not None: - for k, asset in assets.items(): - self.add_asset(k, asset) - - def __repr__(self) -> str: - return f"" - - def add_item( - self, - item: Item, - title: str | None = None, - strategy: HrefLayoutStrategy | None = None, - set_parent: bool = True, - ) -> Link: - link = super().add_item(item, title, strategy, set_parent) - item.set_collection(self) - return link - - def to_dict( - self, include_self_link: bool = True, transform_hrefs: bool = True - ) -> dict[str, Any]: - d = super().to_dict( - include_self_link=include_self_link, transform_hrefs=transform_hrefs - ) - d["extent"] = self.extent.to_dict() - d["license"] = self.license - if self.stac_extensions: - d["stac_extensions"] = self.stac_extensions - if self.keywords: - d["keywords"] = self.keywords - if self.providers: - d["providers"] = list(map(lambda x: x.to_dict(), self.providers)) - if not self.summaries.is_empty(): - d["summaries"] = self.summaries.to_dict() - if any(self.assets): - d["assets"] = {k: v.to_dict() for k, v in self.assets.items()} - - return d - - def clone(self) -> Collection: - cls = self.__class__ - clone = cls( - id=self.id, - description=self.description, - extent=self.extent.clone(), - title=self.title, - stac_extensions=self.stac_extensions.copy(), - extra_fields=deepcopy(self.extra_fields), - catalog_type=self.catalog_type, - license=self.license, - keywords=self.keywords.copy() if self.keywords is not None else None, - providers=deepcopy(self.providers), - summaries=self.summaries.clone(), - assets={k: asset.clone() for k, asset in self.assets.items()}, - ) - - clone._resolved_objects.cache(clone) - - for link in self.links: - if link.rel == pystac.RelType.ROOT: - # Collection __init__ sets correct root to clone; don't reset - # if the root link points to self - root_is_self = link.is_resolved() and link.target is self - if not root_is_self: - clone.set_root(None) - clone.add_link(link.clone()) - else: - clone.add_link(link.clone()) - - return clone - - @classmethod - def from_dict( - cls: type[C], - d: dict[str, Any], - href: str | None = None, - root: Catalog | None = None, - migrate: bool = True, - preserve_dict: bool = True, - ) -> C: - import warnings - - from pystac.extensions.version import CollectionVersionExtension - - if migrate: - info = identify_stac_object(d) - d = migrate_to_latest(d, info) - - if not cls.matches_object_type(d): - raise STACTypeError(d, cls) - - catalog_type = CatalogType.determine_type(d) - - if preserve_dict: - d = deepcopy(d) - - id = d.pop("id") - description = d.pop("description") - license = d.pop("license") - if extent_dict := d.pop("extent", None): - extent = Extent.from_dict(extent_dict) - else: - warnings.warn( - "Collection is missing extent, setting default spatial and " - "temporal extents" - ) - extent = Extent( - spatial=SpatialExtent([-90, -180, 90, 180]), - temporal=TemporalExtent([None, None]), - ) - title = d.pop("title", None) - stac_extensions = d.pop("stac_extensions", None) - keywords = d.pop("keywords", None) - providers = d.pop("providers", None) - if providers is not None: - providers = list(map(lambda x: pystac.Provider.from_dict(x), providers)) - summaries = d.pop("summaries", None) - if summaries is not None: - summaries = Summaries(summaries) - - assets = d.pop("assets", None) - if assets: - assets = {k: Asset.from_dict(v) for k, v in assets.items()} - links = d.pop("links") - - d.pop("stac_version") - - collection = cls( - id=id, - description=description, - extent=extent, - title=title, - stac_extensions=stac_extensions, - extra_fields=d, - license=license, - keywords=keywords, - providers=providers, - summaries=summaries, - href=href, - catalog_type=catalog_type, - assets=assets, - ) - - for link in links: - if link["rel"] == pystac.RelType.ROOT: - # Remove the link that's generated in Catalog's constructor. - collection.remove_links(pystac.RelType.ROOT) - - if link["rel"] != pystac.RelType.SELF or href is None: - collection.add_link(Link.from_dict(link)) - - if root: - collection.set_root(root) - - try: - version = CollectionVersionExtension.ext(collection) - if version.deprecated: - warnings.warn( - f"The collection '{collection.id}' is deprecated.", - DeprecatedWarning, - ) - # Collection asset deprecation checks pending version extension support - except ExtensionNotImplemented: - pass - - return collection - - @classmethod - def from_items( - cls: type[Collection], - items: Iterable[Item] | pystac.ItemCollection, - *, - id: str | None = None, - strategy: HrefLayoutStrategy | None = None, - ) -> Collection: - """Create a :class:`Collection` from iterable of items or an - :class:`~pystac.ItemCollection`. - - Will try to pull collection attributes from - :attr:`~pystac.ItemCollection.extra_fields` and items when possible. - - Args: - items : Iterable of :class:`~pystac.Item` instances to include in the - :class:`Collection`. This can be a :class:`~pystac.ItemCollection`. - id : Identifier for the collection. If not set, must be available on the - items and they must all match. - strategy : The layout strategy to use for setting the - HREFs of the catalog child objects and items. - If not provided, it will default to strategy of the parent and fallback - to :class:`~pystac.layout.BestPracticesLayoutStrategy`. - """ - - def extract(attr: str) -> Any: - """Extract attrs from items or item.properties as long as they all match""" - value = None - values = {getattr(item, attr, None) for item in items} - if len(values) == 1: - value = next(iter(values)) - if value is None: - values = {item.properties.get(attr, None) for item in items} - if len(values) == 1: - value = next(iter(values)) - return value - - if isinstance(items, pystac.ItemCollection): - extra_fields = deepcopy(items.extra_fields) - links = extra_fields.pop("links", {}) - providers = extra_fields.pop("providers", None) - if providers is not None: - providers = [pystac.Provider.from_dict(p) for p in providers] - else: - extra_fields = {} - links = {} - providers = [] - - id = id or extract("collection_id") - if id is None: - raise ValueError( - "Collection id must be defined. Either by specifying collection_id " - "on every item, or as a keyword argument to this function." - ) - - collection = cls( - id=id, - description=extract("description"), - extent=Extent.from_items(items), - title=extract("title"), - providers=providers, - extra_fields=extra_fields, - strategy=strategy, - ) - collection.add_items(items) - - for link in links: - collection.add_link(Link.from_dict(link)) - - return collection - - def get_item(self, id: str, recursive: bool = False) -> Item | None: - """Returns an item with a given ID. - - Args: - id : The ID of the item to find. - recursive : If True, search this collection and all children for the - item; otherwise, only search the items of this collection. Defaults - to False. - - Return: - Item or None: The item with the given ID, or None if not found. - """ - try: - return next(self.get_items(id, recursive=recursive), None) - except TypeError as e: - if any("recursive" in arg for arg in e.args): - # For inherited classes that do not yet support recursive - # See https://github.com/stac-utils/pystac-client/issues/485 - return super().get_item(id, recursive=recursive) - raise e - - @property - def item_assets(self) -> dict[str, ItemAssetDefinition]: - """Accessor for `item_assets - `__ - on this collection. - - Example:: - - .. code-block:: python - - >>> print(collection.item_assets) - {'thumbnail': , - 'metadata': , - 'B5': , - 'B6': , - 'B7': , - 'B8': } - >>> collection.item_assets["thumbnail"].title - 'Thumbnail' - - Set attributes on :class:`~pystac.ItemAssetDefinition` objects - - .. code-block:: python - - >>> collection.item_assets["thumbnail"].title = "New Title" - - Add to the ``item_assets`` dict: - - .. code-block:: python - - >>> collection.item_assets["B4"] = { - 'type': 'image/tiff; application=geotiff; profile=cloud-optimized', - 'eo:bands': [{'name': 'B4', 'common_name': 'red'}] - } - >>> collection.item_assets["B4"].owner == collection - True - """ - if self._item_assets is None: - self._item_assets = _ItemAssets(self) - return self._item_assets - - @item_assets.setter - def item_assets( - self, item_assets: dict[str, ItemAssetDefinition | dict[str, Any]] | None - ) -> None: - # clear out the cached value - self._item_assets = None - - if item_assets is None: - self.extra_fields.pop("item_assets") - else: - self.extra_fields["item_assets"] = { - k: v if isinstance(v, dict) else v.to_dict() - for k, v in item_assets.items() - } - - def update_extent_from_items(self) -> None: - """ - Update datetime and bbox based on all items to a single bbox and time window. - """ - self.extent = Extent.from_items(self.get_items(recursive=True)) - - def full_copy( - self, root: Catalog | None = None, parent: Catalog | None = None - ) -> Collection: - return cast(Collection, super().full_copy(root, parent)) - - @classmethod - def matches_object_type(cls, d: dict[str, Any]) -> bool: - return identify_stac_object_type(d) == STACObjectType.COLLECTION - - @property - def ext(self) -> CollectionExt: - """Accessor for extension classes on this collection - - Example:: - - print(collection.ext.xarray) - """ - from pystac.extensions.ext import CollectionExt - - return CollectionExt(stac_object=self) diff --git a/pystac/common_metadata.py b/pystac/common_metadata.py deleted file mode 100644 index 4dac97dae..000000000 --- a/pystac/common_metadata.py +++ /dev/null @@ -1,251 +0,0 @@ -from __future__ import annotations - -from datetime import datetime -from typing import TYPE_CHECKING, Any, Optional, TypeVar, cast - -import pystac -from pystac import utils -from pystac.errors import STACError - -if TYPE_CHECKING: - from pystac.asset import Asset - from pystac.item import Item - from pystac.provider import Provider - - -P = TypeVar("P") - - -class CommonMetadata: - """Object containing fields that are not included in core item schema but - are still commonly used. All attributes are defined within the properties of - this item and are optional - - Args: - properties : Dictionary of attributes that is the Item's properties - """ - - object: Asset | Item - """The object from which common metadata is obtained.""" - - def __init__(self, object: Asset | Item): - self.object = object - - def _set_field(self, prop_name: str, v: Any | None) -> None: - if hasattr(self.object, prop_name): - setattr(self.object, prop_name, v) - elif hasattr(self.object, "properties"): - item = cast(pystac.Item, self.object) - if v is None: - item.properties.pop(prop_name, None) - else: - item.properties[prop_name] = v - elif hasattr(self.object, "extra_fields") and isinstance( - self.object.extra_fields, dict - ): - if v is None: - self.object.extra_fields.pop(prop_name, None) - else: - self.object.extra_fields[prop_name] = v - else: - raise pystac.STACError(f"Cannot set field {prop_name} on {self}.") - - def _get_field(self, prop_name: str, _typ: type[P]) -> P | None: - if hasattr(self.object, prop_name): - return cast(Optional[P], getattr(self.object, prop_name)) - elif hasattr(self.object, "properties"): - item = cast(pystac.Item, self.object) - return item.properties.get(prop_name) - elif hasattr(self.object, "extra_fields") and isinstance( - self.object.extra_fields, dict - ): - return self.object.extra_fields.get(prop_name) - else: - raise STACError(f"Cannot get field {prop_name} from {self}.") - - # Basics - @property - def title(self) -> str | None: - """Gets or set the object's title.""" - return self._get_field("title", str) - - @title.setter - def title(self, v: str | None) -> None: - self._set_field("title", v) - - @property - def description(self) -> str | None: - """Gets or set the object's description.""" - return self._get_field("description", str) - - @description.setter - def description(self, v: str | None) -> None: - if v == "": - raise ValueError("description cannot be an empty string") - self._set_field("description", v) - - # Date and Time Range - @property - def start_datetime(self) -> datetime | None: - """Get or set the object's start_datetime. - - Note: - ``start_datetime`` is an inclusive datetime. - """ - return utils.map_opt( - utils.str_to_datetime, self._get_field("start_datetime", str) - ) - - @start_datetime.setter - def start_datetime(self, v: datetime | None) -> None: - self._set_field("start_datetime", utils.map_opt(utils.datetime_to_str, v)) - - @property - def end_datetime(self) -> datetime | None: - """Get or set the item's end_datetime. - - Note: - ``end_datetime`` is an inclusive datetime. - """ - return utils.map_opt( - utils.str_to_datetime, self._get_field("end_datetime", str) - ) - - @end_datetime.setter - def end_datetime(self, v: datetime | None) -> None: - self._set_field("end_datetime", utils.map_opt(utils.datetime_to_str, v)) - - # License - @property - def license(self) -> str | None: - """Get or set the current license. License should be provided - as a `SPDX License identifier `_, - or `other`. If object includes data with multiple - different licenses, use `other` and add a link for - each. - - Note: - The licenses `various` and `proprietary` are deprecated. - """ - return self._get_field("license", str) - - @license.setter - def license(self, v: str | None) -> None: - self._set_field("license", v) - - # Providers - @property - def providers(self) -> list[Provider] | None: - """Get or set a list of the object's providers.""" - return utils.map_opt( - lambda providers: [pystac.Provider.from_dict(d) for d in providers], - self._get_field("providers", list[dict[str, Any]]), - ) - - @providers.setter - def providers(self, v: list[Provider] | None) -> None: - self._set_field( - "providers", - utils.map_opt(lambda providers: [p.to_dict() for p in providers], v), - ) - - # Instrument - @property - def platform(self) -> str | None: - """Gets or set the object's platform attribute.""" - return self._get_field("platform", str) - - @platform.setter - def platform(self, v: str | None) -> None: - self._set_field("platform", v) - - @property - def instruments(self) -> list[str] | None: - """Gets or sets the names of the instruments used.""" - return self._get_field("instruments", list[str]) - - @instruments.setter - def instruments(self, v: list[str] | None) -> None: - self._set_field("instruments", v) - - @property - def constellation(self) -> str | None: - """Gets or set the name of the constellation associate with an object.""" - return self._get_field("constellation", str) - - @constellation.setter - def constellation(self, v: str | None) -> None: - self._set_field("constellation", v) - - @property - def mission(self) -> str | None: - """Gets or set the name of the mission associated with an object.""" - return self._get_field("mission", str) - - @mission.setter - def mission(self, v: str | None) -> None: - self._set_field("mission", v) - - @property - def gsd(self) -> float | None: - """Gets or sets the Ground Sample Distance at the sensor.""" - return self._get_field("gsd", float) - - @gsd.setter - def gsd(self, v: float | None) -> None: - self._set_field("gsd", v) - - # Metadata - @property - def created(self) -> datetime | None: - """Get or set the metadata file's creation date. All datetime attributes have - setters that can take either a string or a datetime, but always stores - the attribute as a string. - - Note: - ``created`` has a different meaning depending on the type of STAC object. - On an :class:`~pystac.Item`, it refers to the creation time of the - metadata. On an :class:`~pystac.Asset`, it refers to the creation time of - the actual data linked to in :attr:`~pystac.Asset.href`. - """ - return utils.map_opt(utils.str_to_datetime, self._get_field("created", str)) - - @created.setter - def created(self, v: datetime | None) -> None: - self._set_field("created", utils.map_opt(utils.datetime_to_str, v)) - - @property - def updated(self) -> datetime | None: - """Get or set the metadata file's update date. All datetime attributes have - setters that can take either a string or a datetime, but always stores - the attribute as a string - - Note: - ``updated`` has a different meaning depending on the type of STAC object. - On an :class:`~pystac.Item`, it refers to the update time of the - metadata. On an :class:`~pystac.Asset`, it refers to the update time of - the actual data linked to in :attr:`~pystac.Asset.href`. - """ - return utils.map_opt(utils.str_to_datetime, self._get_field("updated", str)) - - @updated.setter - def updated(self, v: datetime | None) -> None: - self._set_field("updated", utils.map_opt(utils.datetime_to_str, v)) - - @property - def keywords(self) -> list[str] | None: - """Get or set the keywords describing the STAC entity.""" - return self._get_field("keywords", list[str]) - - @keywords.setter - def keywords(self, v: list[str] | None) -> None: - self._set_field("keywords", v) - - @property - def roles(self) -> list[str] | None: - """Get or set the semantic roles of the entity.""" - return self._get_field("roles", list[str]) - - @roles.setter - def roles(self, v: list[str] | None) -> None: - self._set_field("roles", v) diff --git a/pystac/item.py b/pystac/item.py deleted file mode 100644 index 8eb53ef55..000000000 --- a/pystac/item.py +++ /dev/null @@ -1,534 +0,0 @@ -from __future__ import annotations - -from copy import copy, deepcopy -from typing import TYPE_CHECKING, Any, TypeVar, cast - -import pystac -from pystac import RelType, STACError, STACObjectType -from pystac.asset import Asset, Assets -from pystac.catalog import Catalog -from pystac.collection import Collection -from pystac.errors import DeprecatedWarning, ExtensionNotImplemented -from pystac.link import Link -from pystac.serialization import ( - identify_stac_object, - identify_stac_object_type, - migrate_to_latest, -) -from pystac.stac_object import STACObject -from pystac.utils import ( - datetime_to_str, - is_absolute_href, - make_absolute_href, - make_relative_href, - str_to_datetime, -) - -#: Generalized version of :class:`Item` -T = TypeVar("T", bound="Item") - -if TYPE_CHECKING: - # avoids conflicts since there are also kwargs and attrs called `datetime` - from datetime import datetime as Datetime - - from pystac.extensions.ext import ItemExt - - -class Item(STACObject, Assets): - """An Item is the core granular entity in a STAC, containing the core metadata - that enables any client to search or crawl online catalogs of spatial 'assets' - - satellite imagery, derived data, DEM's, etc. - - Args: - id : Provider identifier. Must be unique within the STAC. - geometry : Defines the full footprint of the asset represented by this - item, formatted according to - `RFC 7946, section 3.1 (GeoJSON) `_. - bbox : Bounding Box of the asset represented by this item - using either 2D or 3D geometries. The length of the array must be 2*n - where n is the number of dimensions. Could also be None in the case of a - null geometry. - datetime : datetime associated with this item. If None, - a start_datetime and end_datetime must be supplied. - properties : A dictionary of additional metadata for the item. - start_datetime : Optional inclusive start datetime, part of common metadata. - This value will override any `start_datetime` key in properties. - end_datetime : Optional inclusive end datetime, part of common metadata. - This value will override any `end_datetime` key in properties. - stac_extensions : Optional list of extensions the Item implements. - href : Optional HREF for this item, which be set as the item's - self link's HREF. - collection : The Collection or Collection ID that this item - belongs to. - extra_fields : Extra fields that are part of the top-level JSON - properties of the Item. - assets : A dictionary mapping string keys to :class:`~pystac.Asset` objects. All - :class:`~pystac.Asset` values in the dictionary will have their - :attr:`~pystac.Asset.owner` attribute set to the created Item. - """ - - assets: dict[str, Asset] - """Dictionary of :class:`~pystac.Asset` objects, each with a unique key.""" - - bbox: list[float] | None - """Bounding Box of the asset represented by this item using either 2D or 3D - geometries. The length of the array is 2*n where n is the number of dimensions. - Could also be None in the case of a null geometry.""" - - collection: Collection | None - """:class:`~pystac.Collection` to which this Item belongs, if any.""" - - collection_id: str | None - """The Collection ID that this item belongs to, if any.""" - - datetime: Datetime | None - """Datetime associated with this item. If ``None``, then - :attr:`~pystac.CommonMetadata.start_datetime` and - :attr:`~pystac.CommonMetadata.end_datetime` in :attr:`~pystac.Item.common_metadata` - will supply the datetime range of the Item.""" - - extra_fields: dict[str, Any] - """Extra fields that are part of the top-level JSON fields the Item.""" - - geometry: dict[str, Any] | None - """Defines the full footprint of the asset represented by this item, formatted - according to `RFC 7946, section 3.1 (GeoJSON) - `_.""" - - id: str - """Provider identifier. Unique within the STAC.""" - - links: list[Link] - """A list of :class:`~pystac.Link` objects representing all links associated with - this Item.""" - - properties: dict[str, Any] - """A dictionary of additional metadata for the Item.""" - - stac_extensions: list[str] - """List of extensions the Item implements.""" - - STAC_OBJECT_TYPE = STACObjectType.ITEM - - def __init__( - self, - id: str, - geometry: dict[str, Any] | None, - bbox: list[float] | None, - datetime: Datetime | None, - properties: dict[str, Any], - start_datetime: Datetime | None = None, - end_datetime: Datetime | None = None, - stac_extensions: list[str] | None = None, - href: str | None = None, - collection: str | Collection | None = None, - extra_fields: dict[str, Any] | None = None, - assets: dict[str, Asset] | None = None, - ): - super().__init__(stac_extensions or []) - - self.id = id - self.geometry = geometry - self.bbox = bbox - self.properties = properties - if extra_fields is None: - self.extra_fields = {} - else: - self.extra_fields = extra_fields - - self.assets: dict[str, Asset] = {} - - self.datetime: Datetime | None = None - if start_datetime: - properties["start_datetime"] = datetime_to_str(start_datetime) - if end_datetime: - properties["end_datetime"] = datetime_to_str(end_datetime) - if datetime is None: - if "start_datetime" not in properties or "end_datetime" not in properties: - raise STACError( - "Invalid Item: If datetime is None, " - "a start_datetime and end_datetime " - "must be supplied." - ) - self.datetime = None - else: - self.datetime = datetime - - if href is not None: - self.set_self_href(href) - - self.collection_id: str | None = None - if collection is None: - self.collection = None - else: - if isinstance(collection, Collection): - self.set_collection(collection) - else: - self.collection_id = collection - - self.assets = {} - if assets is not None: - for k, asset in assets.items(): - self.add_asset(k, asset) - - def __repr__(self) -> str: - return f"" - - def __getstate__(self) -> dict[str, Any]: - """Ensure that pystac does not encode too much information when pickling""" - d = self.__dict__.copy() - - d["links"] = [ - ( - link.to_dict(transform_href=False) - if link.get_href(transform_href=False) - else link - ) - for link in d["links"] - ] - - return d - - def __setstate__(self, state: dict[str, Any]) -> None: - """Ensure that pystac knows how to decode the pickled object""" - d = state.copy() - - d["links"] = [ - Link.from_dict(link).set_owner(self) if isinstance(link, dict) else link - for link in d["links"] - ] - - self.__dict__ = d - - def set_self_href(self, href: str | None) -> None: - """Sets the absolute HREF that is represented by the ``rel == 'self'`` - :class:`~pystac.Link`. - - Changing the self HREF of the item will ensure that all asset HREFs - remain valid. If asset HREFs are relative, the HREFs will change - to point to the same location based on the new item self HREF, - either by making them relative to the new location or making them - absolute links if the new location does not share the same protocol - as the old location. - - Args: - href : The absolute HREF of this object. If the given HREF - is not absolute, it will be transformed to an absolute - HREF based on the current working directory. If this is None - the call will clear the self HREF link. - """ - prev_href = self.get_self_href() - super().set_self_href(href) - new_href = self.get_self_href() # May have been made absolute. - - if prev_href is not None and new_href is not None: - # Make sure relative asset links remain valid. - for asset in self.assets.values(): - asset_href = asset.href - if not is_absolute_href(asset_href): - abs_href = make_absolute_href(asset_href, prev_href) - new_relative_href = make_relative_href(abs_href, new_href) - asset.href = new_relative_href - - def get_datetime(self, asset: Asset | None = None) -> Datetime | None: - """Gets an Item or an Asset datetime. - - If an Asset is supplied and the Item property exists on the Asset, - returns the Asset's value. Otherwise returns the Item's value. - - Returns: - datetime or None - """ - if asset is None or "datetime" not in asset.extra_fields: - return self.datetime - else: - asset_dt = asset.extra_fields.get("datetime") - if asset_dt is None: - return None - else: - return str_to_datetime(asset_dt) - - def set_datetime(self, datetime: Datetime, asset: Asset | None = None) -> None: - """Set an Item or an Asset datetime. - - If an Asset is supplied, sets the property on the Asset. - Otherwise sets the Item's value. - """ - if asset is None: - self.datetime = datetime - else: - asset.extra_fields["datetime"] = datetime_to_str(datetime) - - def set_collection(self, collection: Collection | None) -> Item: - """Set the collection of this item. - - This method will replace any existing Collection link and attribute for - this item. - - Args: - collection : The collection to set as this - item's collection. If None, will clear the collection. - - Returns: - Item: self - """ - self.remove_links(pystac.RelType.COLLECTION) - self.collection_id = None - if collection is not None: - self.add_link(Link.collection(collection)) - self.collection_id = collection.id - - return self - - def get_collection(self) -> Collection | None: - """Gets the collection of this item, if one exists. - - Returns: - Collection or None: If this item belongs to a collection, returns - a reference to the collection. Otherwise returns None. - """ - collection_link = self.get_single_link(pystac.RelType.COLLECTION) - if collection_link is None: - return None - else: - return cast( - Collection, collection_link.resolve_stac_object(self.get_root()).target - ) - - def add_derived_from(self, *items: Item | str) -> Item: - """Add one or more items that this is derived from. - - This method will add to any existing "derived_from" links. - - Args: - items : Items (or href of items) to add to derived_from links. - - Returns: - Item: self - """ - for item in items: - self.add_link(Link.derived_from(item)) - return self - - def remove_derived_from(self, item_id: str) -> None: - """Remove an item that this is derived from. - - This method will remove from existing "derived_from" links. - - Args: - item_id : ID of item to remove from derived_from links. - """ - new_links: list[pystac.Link] = [] - - for link in self.links: - if link.rel != pystac.RelType.DERIVED_FROM: - new_links.append(link) - else: - try: - item = cast(Item, link.resolve_stac_object().target) - except Exception as e: - raise pystac.STACError( - "Link failed to resolve. Use remove_links instead." - ) from e - if item.id != item_id: - new_links.append(link) - self.links = new_links - - def get_derived_from(self) -> list[Item]: - """Get the items that this is derived from. - - Returns: - List[Item]: Returns a reference to the derived_from items. - """ - links = self.get_links(pystac.RelType.DERIVED_FROM) - try: - return [cast(Item, link.resolve_stac_object().target) for link in links] - except Exception as e: - raise pystac.STACError( - "Link failed to resolve. Use get_links instead." - ) from e - - def to_dict( - self, include_self_link: bool = True, transform_hrefs: bool = True - ) -> dict[str, Any]: - links = self.links - if not include_self_link: - links = [x for x in links if x.rel != pystac.RelType.SELF] - - assets = {k: v.to_dict() for k, v in self.assets.items()} - - if self.datetime is not None: - self.properties["datetime"] = datetime_to_str(self.datetime) - else: - self.properties["datetime"] = None - - d: dict[str, Any] = { - "type": "Feature", - "stac_version": pystac.get_stac_version(), - "stac_extensions": self.stac_extensions if self.stac_extensions else [], - "id": self.id, - "geometry": self.geometry, - "bbox": self.bbox if self.bbox is not None else [], - "properties": self.properties, - "links": [link.to_dict(transform_href=transform_hrefs) for link in links], - "assets": assets, - } - - if self.collection_id: - d["collection"] = self.collection_id - - for key in self.extra_fields: - d[key] = self.extra_fields[key] - - # This field is prohibited if there's no geometry - if not self.geometry: - d.pop("bbox") - - return d - - def clone(self) -> Item: - cls = self.__class__ - clone = cls( - id=self.id, - geometry=deepcopy(self.geometry), - bbox=copy(self.bbox), - datetime=copy(self.datetime), - properties=deepcopy(self.properties), - stac_extensions=deepcopy(self.stac_extensions), - collection=self.collection_id, - assets={k: asset.clone() for k, asset in self.assets.items()}, - extra_fields=deepcopy(self.extra_fields), - ) - for link in self.links: - clone.add_link(link.clone()) - - return clone - - def _object_links(self) -> list[str | pystac.RelType]: - return [ - pystac.RelType.COLLECTION, - *pystac.EXTENSION_HOOKS.get_extended_object_links(self), - ] - - @classmethod - def from_dict( - cls: type[T], - d: dict[str, Any], - href: str | None = None, - root: Catalog | None = None, - migrate: bool = True, - preserve_dict: bool = True, - ) -> T: - import warnings - - from pystac.extensions.version import ItemVersionExtension - - if preserve_dict: - d = deepcopy(d) - - if migrate: - info = identify_stac_object(d) - d = migrate_to_latest(d, info) - - if not cls.matches_object_type(d): - raise pystac.STACTypeError(d, cls) - - # some fields are passed through to __init__ - pass_through_fields = [ - "id", - "geometry", - "bbox", - "stac_extensions", - "collection", - ] - - # some fields need some parsing or special handling - parse_fields = ["links", "assets", "properties"] - links = d.get("links", []) - assets = d.get("assets", {}) - properties = d.get("properties", {}) - datetime = properties.get("datetime") - if datetime is not None: - datetime = str_to_datetime(datetime) - - # some fields are excluded from __init__ entirely - exclude_fields = ["type", "stac_version"] - - # get all the fields that are not passed through, parsed, or excluded - extra_fields = { - k: v - for k, v in d.items() - if k not in [*pass_through_fields, *parse_fields, *exclude_fields] - } - - item = cls( - **{k: d.get(k) for k in pass_through_fields}, # type: ignore - datetime=datetime, - properties=properties, - extra_fields=extra_fields, - href=href, - assets={k: Asset.from_dict(v) for k, v in assets.items()}, - ) - - for link in links: - if href is None or link.get("rel", None) != RelType.SELF: - item.add_link(Link.from_dict(link)) - - if root: - item.set_root(root) - - try: - version = ItemVersionExtension.ext(item) - if version.deprecated: - warnings.warn( - f"The item '{item.id}' is deprecated.", - DeprecatedWarning, - ) - # Item asset deprecation checks pending version extension support - except ExtensionNotImplemented: - pass - - return item - - @property - def common_metadata(self) -> pystac.CommonMetadata: - """Access the item's common metadata fields as a - :class:`~pystac.CommonMetadata` object.""" - return pystac.CommonMetadata(self) - - def full_copy( - self, root: Catalog | None = None, parent: Catalog | None = None - ) -> Item: - return cast(Item, super().full_copy(root, parent)) - - @classmethod - def matches_object_type(cls, d: dict[str, Any]) -> bool: - for field in ("type", "stac_version"): - if field not in d: - raise pystac.STACTypeError(d, cls, f"'{field}' is missing.") - return identify_stac_object_type(d) == STACObjectType.ITEM - - @property - def __geo_interface__(self) -> dict[str, Any]: - """Returns this item as a dictionary. - - This just calls `to_dict` without self links or transforming any hrefs. - - https://gist.github.com/sgillies/2217756 - - Returns: - Dict[str, Any]: This item as a dictionary. - """ - return self.to_dict(include_self_link=False, transform_hrefs=False) - - @property - def ext(self) -> ItemExt: - """Accessor for extension classes on this item - - Example:: - - item.ext.proj.code = "EPSG:4326" - """ - from pystac.extensions.ext import ItemExt - - return ItemExt(stac_object=self) diff --git a/pystac/item_collection.py b/pystac/item_collection.py deleted file mode 100644 index 2e6f9e32c..000000000 --- a/pystac/item_collection.py +++ /dev/null @@ -1,257 +0,0 @@ -from __future__ import annotations - -from collections.abc import Collection, Iterable, Iterator -from typing import ( - Any, - TypeAlias, - TypeVar, -) - -import pystac -from pystac.errors import STACTypeError -from pystac.serialization.identify import identify_stac_object_type -from pystac.utils import HREF, is_absolute_href, make_absolute_href, make_posix_style - -ItemLike: TypeAlias = pystac.Item | dict[str, Any] - -#: Generalized version of :class:`ItemCollection` -C = TypeVar("C", bound="ItemCollection") - - -class ItemCollection(Collection[pystac.Item]): - """Implementation of a GeoJSON FeatureCollection whose features are all STAC - Items, - as defined by `STAC API - ItemCollection Fragment `_. - - All :class:`~pystac.Item` instances passed to the :class:`ItemCollection` instance - during instantiation are cloned and have their ``"root"`` URL cleared. Instances of - this class implement the abstract methods of :class:`typing.Collection` and can also - be added together (see below for examples using these methods). - - Any additional top-level fields in the FeatureCollection are retained in - :attr:`~pystac.ItemCollection.extra_fields` by the - :meth:`~pystac.ItemCollection.from_dict` and - :meth:`~pystac.ItemCollection.from_file` - methods and will be present in the serialized file - from :meth:`~pystac.ItemCollection.save_object`. - - Arguments: - - items : List of :class:`~pystac.Item` instances to include in the - :class:`ItemCollection`. - extra_fields : Dictionary of additional top-level fields included in the - :class:`ItemCollection`. - clone_items : Optional flag indicating whether :class:`~pystac.Item` instances - should be cloned before storing in the :class:`ItemCollection`. Setting to - ``False`` will result in faster instantiation, but changes made to - :class:`~pystac.Item` instances in the :class:`ItemCollection` will mutate - the original ``Item``. Defaults to ``True``. - - Examples: - - Loop over all items in the :class`ItemCollection` - - >>> item_collection: ItemCollection = ... - >>> for item in item_collection: - ... ... - - Get the number of :class:`~pystac.Item` instances in the - :class:`ItemCollection` - - >>> length: int = len(item_collection) - - Check if an :class:`~pystac.Item` is in the :class:`ItemCollection`. Note - that the ``clone_items`` argument must be ``False`` for this to return - ``True``, since equality of PySTAC objects is currently evaluated using default - object equality (i.e. ``item_1 is item_2``). - - >>> item: Item = ... - >>> item_collection = ItemCollection(items=[item], clone_items=False) - >>> assert item in item_collection - - Combine :class:`ItemCollection` instances - - >>> item_1: Item = ... - >>> item_2: Item = ... - >>> item_3: Item = ... - >>> item_collection_1 = ItemCollection(items=[item_1, item_2]) - >>> item_collection_2 = ItemCollection(items=[item_2, item_3]) - >>> combined = item_collection_1 + item_collection_2 - >>> assert len(combined) == 4 - # If an item is present in both ItemCollections it will occur twice - """ - - items: list[pystac.Item] - """List of :class:`pystac.Item` instances contained in this ``ItemCollection``.""" - - extra_fields: dict[str, Any] - """Dictionary of additional top-level fields for the GeoJSON - FeatureCollection.""" - - def __init__( - self, - items: Iterable[ItemLike], - extra_fields: dict[str, Any] | None = None, - clone_items: bool = True, - ): - def map_item(item_or_dict: ItemLike) -> pystac.Item: - # Converts dicts to pystac.Items and clones if necessary - if isinstance(item_or_dict, pystac.Item): - return item_or_dict.clone() if clone_items else item_or_dict - else: - return pystac.Item.from_dict(item_or_dict, preserve_dict=clone_items) - - self.items = list(map(map_item, items)) - self.extra_fields = extra_fields or {} - - def __getitem__(self, idx: int) -> pystac.Item: - return self.items[idx] - - def __iter__(self) -> Iterator[pystac.Item]: - return iter(self.items) - - def __len__(self) -> int: - return len(self.items) - - def __contains__(self, __x: object) -> bool: - return __x in self.items - - def __add__(self, other: object) -> ItemCollection: - if not isinstance(other, ItemCollection): - return NotImplemented - - combined = [*self.items, *other.items] - return ItemCollection(items=combined) - - def to_dict(self, transform_hrefs: bool = False) -> dict[str, Any]: - """Serializes an :class:`ItemCollection` instance to a dictionary. - - Args: - transform_hrefs: If True, transform the HREF of hierarchical links - of Items based on the type of catalog the Item belongs to (if any). - I.e. if the item belongs to a root catalog that is - RELATIVE_PUBLISHED or SELF_CONTAINED, - hierarchical link HREFs will be transformed to be relative to the - catalog root. This can be slow if the Items have root links that - have not yet been resolved. Defaults to False. - """ - return { - "type": "FeatureCollection", - "features": [ - item.to_dict(transform_hrefs=transform_hrefs) for item in self.items - ], - **self.extra_fields, - } - - def _repr_html_(self) -> str: - from html import escape - - from pystac.html.jinja_env import get_jinja_env - - jinja_env = get_jinja_env() - if jinja_env: - template = jinja_env.get_template("JSON.jinja2") - return str(template.render(dict=self.to_dict(), plain=escape(repr(self)))) - else: - return escape(repr(self)) - - def clone(self) -> ItemCollection: - """Creates a clone of this instance. This clone is a deep copy; all - :class:`~pystac.Item` instances are cloned and all additional top-level fields - are deep copied.""" - from copy import deepcopy - - return self.__class__( - items=[item.clone() for item in self.items], - extra_fields=deepcopy(self.extra_fields), - ) - - @classmethod - def from_dict( - cls: type[C], - d: dict[str, Any], - preserve_dict: bool = True, - root: pystac.Catalog | None = None, - ) -> C: - """Creates a :class:`ItemCollection` instance from a dictionary. - - Arguments: - d : The dictionary from which the :class:`ItemCollection` will be created - preserve_dict: If False, the dict parameter ``d`` may be modified - during this method call. Otherwise, the dict is not mutated. - Defaults to True, which results in a deepcopy of the - parameter. Set to False when possible to avoid the performance - hit of a deepcopy. - """ - if not cls.is_item_collection(d): - raise STACTypeError(d, cls) - - items = [ - pystac.Item.from_dict(item, preserve_dict=preserve_dict, root=root) - for item in d.get("features", []) - ] - extra_fields = {k: v for k, v in d.items() if k not in ("features", "type")} - - return cls(items=items, extra_fields=extra_fields) - - @classmethod - def from_file(cls: type[C], href: HREF, stac_io: pystac.StacIO | None = None) -> C: - """Reads a :class:`ItemCollection` from a JSON file. - - Arguments: - href : Path to the file. - stac_io : A :class:`~pystac.StacIO` instance to use for file I/O - """ - if stac_io is None: - stac_io = pystac.StacIO.default() - - href = make_posix_style(href) - if not is_absolute_href(href): - href = make_absolute_href(href) - - d = stac_io.read_json(href) - - return cls.from_dict(d, preserve_dict=False) - - def save_object( - self, - dest_href: str, - stac_io: pystac.StacIO | None = None, - ) -> None: - """Saves this instance to the ``dest_href`` location. - - Args: - dest_href : Location to which the file will be saved. - stac_io: Optional :class:`~pystac.StacIO` instance to use. If not provided, - will use the default instance. - """ - if stac_io is None: - stac_io = pystac.StacIO.default() - - stac_io.save_json(dest_href, self.to_dict()) - - @staticmethod - def is_item_collection(d: dict[str, Any]) -> bool: - """Checks if the given dictionary represents a valid :class:`ItemCollection`. - - Args: - d : Dictionary to check - """ - typ = d.get("type") - - # All ItemCollections are GeoJSON FeatureCollections - if typ != "FeatureCollection": - return False - - # If it is a FeatureCollection and has a "stac_version" field, then it is an - # ItemCollection. This will cover ItemCollections from STAC 0.9 to - # <1.0.0-beta.1, when ItemCollections were removed from the core STAC Spec - if "stac_version" in d: - return True - - # Prior to STAC 0.9 ItemCollections did not have a stac_version field and could - # only be identified by the fact that all of their 'features' are STAC Items. - return all( - identify_stac_object_type(feature) == pystac.STACObjectType.ITEM - for feature in d.get("features", []) - ) diff --git a/pystac/link.py b/pystac/link.py deleted file mode 100644 index eabbe2339..000000000 --- a/pystac/link.py +++ /dev/null @@ -1,528 +0,0 @@ -from __future__ import annotations - -import os -from typing import TYPE_CHECKING, Any, TypeVar - -import pystac -from pystac.errors import STACError -from pystac.utils import ( - HREF as HREF, -) -from pystac.utils import ( - is_absolute_href, - make_absolute_href, - make_posix_style, - make_relative_href, -) - -if TYPE_CHECKING: - from pystac.catalog import Catalog - from pystac.collection import Collection - from pystac.extensions.ext import LinkExt - from pystac.item import Item - from pystac.stac_object import STACObject - - PathLike = os.PathLike[str] - -else: - PathLike = os.PathLike - -#: Generalized version of :class:`Link` -L = TypeVar("L", bound="Link") - -#: Hierarchical links provide structure to STAC catalogs. -HIERARCHICAL_LINKS = [ - pystac.RelType.ROOT, - pystac.RelType.CHILD, - pystac.RelType.PARENT, - pystac.RelType.COLLECTION, - pystac.RelType.ITEM, - pystac.RelType.ITEMS, -] - - -class Link(PathLike): - """A link connects a :class:`~pystac.STACObject` to another entity. - - The target of a link can be either another STACObject, or - an HREF. When serialized, links always refer to the HREF of the target. - Links are lazily deserialized - this is, when you read in a link, it does - not automatically load in the STACObject that is the target - (if the link is pointing to a STACObject). When a user is crawling - through a catalog or when additional metadata is required, PySTAC uses the - :func:`~pystac.Link.resolve_stac_object` method - to load in and deserialize STACObjects. This mechanism is used within - the PySTAC codebase and normally does not need to be considered by the user - - ideally the lazy deserialization of STACObjects is transparent to clients of PySTAC. - - Args: - rel : The relation of the link (e.g. 'child', 'item'). Registered rel Types - are preferred. See :class:`~pystac.RelType` for common media types. - target : The target of the link. If the link is - unresolved, or the link is to something that is not a STACObject, - the target is an HREF. If resolved, the target is a STACObject. - media_type : Optional description of the media type. Registered Media Types - are preferred. See :class:`~pystac.MediaType` for common media types. - title : Optional title for this link. - extra_fields : Optional, additional fields for this link. This is used - by extensions as a way to serialize and deserialize properties on link - object JSON. - """ - - rel: str | pystac.RelType - """The relation of the link (e.g. 'child', 'item'). Registered rel Types are - preferred. See :class:`~pystac.RelType` for common media types.""" - - media_type: str | pystac.MediaType | None - """Optional description of the media type. Registered Media Types are preferred. - See :class:`~pystac.MediaType` for common media types.""" - - extra_fields: dict[str, Any] - """Optional, additional fields for this link. This is used by extensions as a - way to serialize and deserialize properties on link object JSON.""" - - owner: STACObject | None - """The owner of this link. The link will use its owner's root catalog - :class:`~pystac.cache.ResolvedObjectCache` to resolve objects, and - will create absolute HREFs from relative HREFs against the owner's self HREF.""" - - _target_href: str | None - _target_object: STACObject | None - _title: str | None - - def __init__( - self, - rel: str | pystac.RelType, - target: str | STACObject, - media_type: str | pystac.MediaType | None = None, - title: str | None = None, - extra_fields: dict[str, Any] | None = None, - ) -> None: - self.rel = rel - if isinstance(target, str): - if rel == pystac.RelType.SELF: - self._target_href = make_absolute_href(target) - else: - self._target_href = make_posix_style(target) - self._target_object = None - else: - self._target_href = None - self._target_object = target - self.media_type = media_type - self.title = title - self.extra_fields = extra_fields or {} - self.owner = None - - def set_owner(self, owner: STACObject | None) -> Link: - """Sets the owner of this link. - - Args: - owner: The owner of this link. Pass None to clear. - """ - self.owner = owner - return self - - @property - def title(self) -> str | None: - """Optional title for this link. If not provided during instantiation, this will - attempt to get the title from the STAC object that the link references.""" - if self._title is not None: - return self._title - if self._target_object is not None and isinstance( - self._target_object, pystac.Catalog - ): - return self._target_object.title - return None - - @title.setter - def title(self, v: str | None) -> None: - self._title = v - - @property - def href(self) -> str: - """Returns the HREF for this link. - - If the href is None, this will throw an exception. - Use get_href if there may not be an HREF. - """ - result = self.get_href() - if result is None: - raise ValueError(f"{self} does not have an HREF set.") - return result - - def get_href(self, transform_href: bool = True) -> str | None: - """Gets the HREF for this link. - - Args: - transform_href: If True, transform the HREF based on the type of - catalog the owner belongs to (if any). I.e. if the link owner - belongs to a root catalog that is RELATIVE_PUBLISHED or SELF_CONTAINED, - the HREF will be transformed to be relative to the catalog root - if this is a hierarchical link relation. - - Returns: - str: Returns this link's HREF. If there is an owner of the link and - the root catalog (if there is one) is of type RELATIVE_PUBLISHED, - then the HREF returned will be relative. - In all other cases, this method will return an absolute HREF. - """ - # get the self href - if self._target_object: - href = self._target_object.get_self_href() - else: - href = self._target_href - - if ( - transform_href - and href - and is_absolute_href(href) - and self.owner - and self.owner.get_root() - ): - root = self.owner.get_root() - rel_links = [ - *HIERARCHICAL_LINKS, - *pystac.EXTENSION_HOOKS.get_extended_object_links(self.owner), - ] - # if a hierarchical link with an owner and root, and relative catalog - if root and root.is_relative(): - if self.rel in rel_links or root.target_in_hierarchy(self.target): - owner_href = self.owner.get_self_href() - if owner_href is not None: - href = make_relative_href(href, owner_href) - - return href - - @property - def absolute_href(self) -> str: - """Returns the absolute HREF for this link. - - If the href is None, this will throw an exception. - Use get_absolute_href if there may not be an HREF set. - """ - result = self.get_absolute_href() - if result is None: - raise ValueError(f"{self} does not have an HREF set.") - return result - - def get_absolute_href(self) -> str | None: - """Gets the absolute href for this link, if possible. - - Returns: - str: Returns this link's HREF. It attempts to derive an absolute HREF - from this link; however, if the link is relative, has no owner, - and has an unresolved target, this will return a relative HREF. - """ - if self._target_object: - href = self._target_object.get_self_href() - else: - href = self._target_href - - if href is not None and self.owner is not None: - href = make_absolute_href(href, self.owner.get_self_href()) - - return href - - @property - def target(self) -> str | STACObject: - """The target of the link. If the link is unresolved, or the link is to - something that is not a STACObject, the target is an HREF. If resolved, the - target is a STACObject.""" - if self._target_object: - return self._target_object - elif self._target_href: - return self._target_href - else: - raise ValueError("No target defined for link.") - - @target.setter - def target(self, target: str | STACObject) -> None: - """Sets this link's target to a string or a STAC object.""" - if isinstance(target, str): - self._target_href = target - self._target_object = None - else: - self._target_href = None - self._target_object = target - - def get_target_str(self) -> str | None: - """Returns this link's target as a string. - - If a string href was provided, returns that. If not, tries to resolve - the self link of the target object. - """ - if self._target_href: - return self._target_href - elif self._target_object: - return self._target_object.get_self_href() - else: - return None - - def has_target_href(self) -> bool: - """Returns true if this link has a string href in its target information.""" - return self._target_href is not None - - def __fspath__(self) -> str: - return self.absolute_href - - def __repr__(self) -> str: - return f"" - - def _repr_html_(self) -> str: - from html import escape - - from pystac.html.jinja_env import get_jinja_env - - jinja_env = get_jinja_env() - if jinja_env: - template = jinja_env.get_template("JSON.jinja2") - return str( - template.render( - dict=self.to_dict(transform_href=False), plain=escape(repr(self)) - ) - ) - else: - return escape(repr(self)) - - def resolve_stac_object(self, root: Catalog | None = None) -> Link: - """Resolves a STAC object from the HREF of this link, if the link is not - already resolved. - - Args: - root : Optional root of the catalog for this link. - If provided, the root's resolved object cache is used to search for - previously resolved instances of the STAC object. - """ - if self._target_object: - pass - elif self._target_href: - target_href = self._target_href - - # If it's a relative link, base it off the parent. - if not is_absolute_href(target_href): - if self.owner is None: - raise pystac.STACError( - "Relative path {} encountered " - "without owner or start_href.".format(target_href) - ) - start_href = self.owner.get_self_href() - - if start_href is None: - raise pystac.STACError( - "Relative path {} encountered " - 'without owner "self" link set.'.format(target_href) - ) - - target_href = make_absolute_href(target_href, start_href) - obj = None - - stac_io: pystac.StacIO | None = None - - if root is not None: - obj = root._resolved_objects.get_by_href(target_href) - stac_io = root._stac_io - - if obj is None: - if stac_io is None: - if self.owner is not None: - if isinstance(self.owner, pystac.Catalog): - stac_io = self.owner._stac_io - elif self.rel != pystac.RelType.ROOT: - owner_root = self.owner.get_root() - if owner_root is not None: - stac_io = owner_root._stac_io - if stac_io is None: - stac_io = pystac.StacIO.default() - try: - obj = stac_io.read_stac_object(target_href, root=root) - except Exception as e: - raise STACError( - f"HREF: '{target_href}' does not resolve to a STAC object" - ) from e - obj.set_self_href(target_href) - if root is not None: - obj = root._resolved_objects.get_or_cache(obj) - obj.set_root(root) - self._target_object = obj - else: - raise ValueError("Cannot resolve STAC object without a target") - - if ( - self.owner - and self.rel in [pystac.RelType.CHILD, pystac.RelType.ITEM] - and isinstance(self.owner, pystac.Catalog) - ): - assert self._target_object - # Do nothing if the object wants to keep its parent - # https://github.com/stac-utils/pystac/issues/1116 - if self._target_object._allow_parent_to_override_href: - self._target_object.set_parent(self.owner) - - return self - - def is_resolved(self) -> bool: - """Determines if the link's target is a resolved STACObject. - - Returns: - bool: True if this link is resolved. - """ - return self._target_object is not None - - def is_hierarchical(self) -> bool: - """Returns true if this link's rel type is hierarchical. - - Hierarchical links are used to build relationships in STAC, e.g. - "parent", "child", "item", etc. For a complete list of hierarchical - relation types, see :py:const:`~pystac.link.HIERARCHICAL_LINKS`. - - Returns: - bool: True if the link's rel type is hierarchical. - """ - return self.rel in HIERARCHICAL_LINKS - - def to_dict(self, transform_href: bool = True) -> dict[str, Any]: - """Returns this link as a dictionary. - - Args: - transform_href : If ``True``, transform the HREF based on the type of - catalog the owner belongs to (if any). I.e. if the link owner - belongs to a root catalog that is RELATIVE_PUBLISHED or SELF_CONTAINED, - the HREF will be transformed to be relative to the catalog root - if this is a hierarchical link relation. - Returns: - dict : A serialization of the Link. - """ - - d: dict[str, Any] = { - "rel": str(self.rel), - "href": self.get_href(transform_href=transform_href), - } - - if self.media_type is not None: - d["type"] = str(self.media_type) - - if self.title is not None: - d["title"] = self.title - - for k, v in self.extra_fields.items(): - d[k] = v - - return d - - def clone(self) -> Link: - """Clones this link. - - This makes a copy of all link information, but does not clone a STACObject - if one is the target of this link. - - Returns: - Link: The cloned link. - """ - cls = self.__class__ - return cls( - rel=self.rel, - target=self.target, - media_type=self.media_type, - title=self.title, - ) - - @classmethod - def from_dict(cls: type[L], d: dict[str, Any]) -> L: - """Deserializes a :class:`Link` from a dict. - - Args: - d : The dict that represents the Link in JSON - - Returns: - Link: Link instance constructed from the dict. - """ - from copy import copy - - d = copy(d) - rel = d.pop("rel") - href = d.pop("href") - media_type = d.pop("type", None) - title = d.pop("title", None) - - extra_fields = None - if any(d): - extra_fields = d - - return cls( - rel=rel, - target=href, - media_type=media_type, - title=title, - extra_fields=extra_fields, - ) - - @classmethod - def root(cls: type[L], c: Catalog) -> L: - """Creates a link to a root Catalog or Collection.""" - return cls(pystac.RelType.ROOT, c, media_type=pystac.MediaType.JSON) - - @classmethod - def parent(cls: type[L], c: Catalog) -> L: - """Creates a link to a parent Catalog or Collection.""" - return cls(pystac.RelType.PARENT, c, media_type=pystac.MediaType.JSON) - - @classmethod - def collection(cls: type[L], c: Collection) -> L: - """Creates a link to a Collection.""" - return cls(pystac.RelType.COLLECTION, c, media_type=pystac.MediaType.JSON) - - @classmethod - def self_href(cls: type[L], href: HREF) -> L: - """Creates a self link to a file's location.""" - href_str = str(os.fspath(href)) - return cls(pystac.RelType.SELF, href_str, media_type=pystac.MediaType.JSON) - - @classmethod - def child(cls: type[L], c: Catalog, title: str | None = None) -> L: - """Creates a link to a child Catalog or Collection.""" - return cls( - pystac.RelType.CHILD, c, title=title, media_type=pystac.MediaType.JSON - ) - - @classmethod - def item(cls: type[L], item: Item, title: str | None = None) -> L: - """Creates a link to an Item.""" - return cls( - pystac.RelType.ITEM, item, title=title, media_type=pystac.MediaType.GEOJSON - ) - - @classmethod - def derived_from(cls: type[L], item: Item | str, title: str | None = None) -> L: - """Creates a link to a derived_from Item.""" - return cls( - pystac.RelType.DERIVED_FROM, - item, - title=title, - media_type=pystac.MediaType.JSON, - ) - - @classmethod - def canonical( - cls: type[L], - item_or_collection: Item | Collection, - title: str | None = None, - ) -> L: - """Creates a canonical link to an Item or Collection.""" - return cls( - pystac.RelType.CANONICAL, - item_or_collection, - title=title, - media_type=pystac.MediaType.JSON, - ) - - @property - def ext(self) -> LinkExt: - """Accessor for extension classes on this link - - Example:: - - link.ext.file.size = 8675309 - """ - from pystac.extensions.ext import LinkExt - - return LinkExt(stac_object=self) diff --git a/pystac/provider.py b/pystac/provider.py deleted file mode 100644 index 673a41d85..000000000 --- a/pystac/provider.py +++ /dev/null @@ -1,122 +0,0 @@ -from typing import Any - -from pystac.utils import StringEnum - - -class ProviderRole(StringEnum): - """Enumerates the allows values of the Provider "role" field.""" - - LICENSOR = "licensor" - PRODUCER = "producer" - PROCESSOR = "processor" - HOST = "host" - - -class Provider: - """Provides information about a provider of STAC data. A provider is any of the - organizations that captured or processed the content of the collection and therefore - influenced the data offered by this collection. May also include information about - the final storage provider hosting the data. - - Args: - name : The name of the organization or the individual. - description : Optional multi-line description to add further provider - information such as processing details for processors and producers, - hosting details for hosts or basic contact information. - roles : Optional roles of the provider. Any of - licensor, producer, processor or host. - url : Optional homepage on which the provider describes the dataset - and publishes contact information. - extra_fields : Optional dictionary containing additional top-level fields - defined on the Provider object. - """ - - name: str - """The name of the organization or the individual.""" - - description: str | None - """Optional multi-line description to add further provider - information such as processing details for processors and producers, - hosting details for hosts or basic contact information.""" - - roles: list[ProviderRole] | None - """Optional roles of the provider. Any of - licensor, producer, processor or host.""" - - url: str | None - """Optional homepage on which the provider describes the dataset - and publishes contact information.""" - - extra_fields: dict[str, Any] - """Dictionary containing additional top-level fields defined on the Provider - object.""" - - def __init__( - self, - name: str, - description: str | None = None, - roles: list[ProviderRole] | None = None, - url: str | None = None, - extra_fields: dict[str, Any] | None = None, - ): - self.name = name - self.description = description - self.roles = roles - self.url = url - self.extra_fields = extra_fields or {} - - def __eq__(self, o: object) -> bool: - if not isinstance(o, Provider): - return NotImplemented - return self.to_dict() == o.to_dict() - - def _repr_html_(self) -> str: - from html import escape - - from pystac.html.jinja_env import get_jinja_env - - jinja_env = get_jinja_env() - if jinja_env: - template = jinja_env.get_template("JSON.jinja2") - return str(template.render(dict=self.to_dict(), plain=escape(repr(self)))) - else: - return escape(repr(self)) - - def to_dict(self) -> dict[str, Any]: - """Returns this provider as a dictionary. - - Returns: - dict: A serialization of the Provider. - """ - d: dict[str, Any] = {"name": self.name} - if self.description is not None: - d["description"] = self.description - if self.roles is not None: - d["roles"] = self.roles - if self.url is not None: - d["url"] = self.url - - d.update(self.extra_fields) - - return d - - @staticmethod - def from_dict(d: dict[str, Any]) -> "Provider": - """Constructs a Provider from a dict. - - Returns: - Provider: The Provider deserialized from the JSON dict. - """ - return Provider( - name=d["name"], - description=d.get("description"), - roles=d.get( - "roles", - ), - url=d.get("url"), - extra_fields={ - k: v - for k, v in d.items() - if k not in {"name", "description", "roles", "url"} - }, - ) diff --git a/pystac/stac_object.py b/pystac/stac_object.py deleted file mode 100644 index b80ebdd21..000000000 --- a/pystac/stac_object.py +++ /dev/null @@ -1,701 +0,0 @@ -from __future__ import annotations - -import warnings -from abc import ABC, abstractmethod -from collections.abc import Callable, Iterable -from typing import TYPE_CHECKING, Any, TypeAlias, TypeVar, cast - -import pystac -from pystac import STACError -from pystac.link import Link -from pystac.utils import ( - HREF, - StringEnum, - is_absolute_href, - make_absolute_href, - make_posix_style, -) - -if TYPE_CHECKING: - from pystac.catalog import Catalog - -S = TypeVar("S", bound="STACObject") - -OptionalMediaType: TypeAlias = str | pystac.MediaType | None - - -class STACObjectType(StringEnum): - CATALOG = "Catalog" - COLLECTION = "Collection" - ITEM = "Feature" - - -class STACObject(ABC): - """A base class for other PySTAC classes that contains a variety of useful - methods for dealing with links, copying objects, accessing extensions, and reading - and writing files. You shouldn't use STACObject directly, but instead access this - functionality through the implementing classes. - """ - - id: str - """The ID of the STAC Object.""" - - links: list[Link] - """A list of :class:`~pystac.Link` objects representing all links associated with - this STAC Object.""" - - stac_extensions: list[str] - """A list of schema URIs for STAC Extensions implemented by this STAC Object.""" - - STAC_OBJECT_TYPE: STACObjectType - - _allow_parent_to_override_href: bool = True - """Private attribute for whether parent objects should override on normalization""" - - def __init__(self, stac_extensions: list[str]) -> None: - self.links = [] - self.stac_extensions = stac_extensions - - def validate( - self, - validator: pystac.validation.stac_validator.STACValidator | None = None, - ) -> list[Any]: - """Validate this STACObject. - - Returns a list of validation results, which depends on the validation - implementation. For JSON Schema validation (default validator), this - will be a list of schema URIs that were used during validation. - - Args: - validator : A custom validator to use for validation of the object. - If omitted, the default validator from - :class:`~pystac.validation.RegisteredValidator` - will be used instead. - Raises: - STACValidationError - """ - import pystac.validation - - return pystac.validation.validate(self, validator=validator) - - def add_link(self, link: Link) -> None: - """Add a link to this object's set of links. - - Args: - link : The link to add. - """ - link.set_owner(self) - self.links.append(link) - - def add_links(self, links: list[Link]) -> None: - """Add links to this object's set of links. - - Args: - links : The links to add. - """ - - for link in links: - self.add_link(link) - - def remove_links(self, rel: str | pystac.RelType) -> None: - """Remove links to this object's set of links that match the given ``rel``. - - Args: - rel : The :class:`~pystac.Link` ``rel`` to match on. - """ - - self.links = [link for link in self.links if link.rel != rel] - - def remove_hierarchical_links(self, add_canonical: bool = False) -> list[Link]: - """Removes all hierarchical links from this object. - - See :py:const:`pystac.link.HIERARCHICAL_LINKS` for a list of all - hierarchical links. If the object has a ``self`` href and - ``add_canonical`` is True, a link with ``rel="canonical"`` is added. - - Args: - add_canonical : If true, and this item has a ``self`` href, that - href is used to build a ``canonical`` link. - - Returns: - List[Link]: All removed links - """ - keep = list() - self_href = self.get_self_href() - if add_canonical and self_href is not None: - keep.append( - Link("canonical", self_href, media_type=pystac.MediaType.GEOJSON) - ) - remove = list() - for link in self.links: - if link.is_hierarchical(): - remove.append(link) - else: - keep.append(link) - self.links = keep - return remove - - def target_in_hierarchy(self, target: str | STACObject) -> bool: - """Determine if target is somewhere in the hierarchical link tree of - a STACObject. - - Args: - target: A string or STACObject to search for - - Returns: - bool: Returns True if the target was found in the hierarchical link tree - for the current STACObject - """ - - def traverse(obj: str | STACObject, visited: set[str | STACObject]) -> bool: - if obj == target: - return True - if isinstance(obj, str): - return False - - new_targets = [ - link.target - for link in obj.links - if link.is_hierarchical() and link.target not in visited - ] - if target in new_targets: - return True - - for subtree in new_targets: - visited.add(subtree) - if traverse(subtree, visited): - return True - - return False - - return traverse(self, {self}) - - def get_single_link( - self, - rel: str | pystac.RelType | None = None, - media_type: OptionalMediaType | Iterable[OptionalMediaType] = None, - ) -> Link | None: - """Get a single :class:`~pystac.Link` instance associated with this - object. - - Args: - rel : If set, filter links such that only those - matching this relationship are returned. - media_type: If set, filter the links such that only - those matching media_type are returned. media_type can - be a single value or a list of values. - - Returns: - :class:`~pystac.Link` | None: First link that matches ``rel`` - and/or ``media_type``, or else the first link associated with - this object. - """ - if rel is None and media_type is None: - return next(iter(self.links), None) - if media_type and isinstance(media_type, (str, pystac.MediaType)): - media_type = [media_type] - return next( - ( - link - for link in self.links - if (rel is None or link.rel == rel) - and (media_type is None or link.media_type in media_type) - ), - None, - ) - - def get_links( - self, - rel: str | pystac.RelType | None = None, - media_type: OptionalMediaType | Iterable[OptionalMediaType] = None, - ) -> list[Link]: - """Gets the :class:`~pystac.Link` instances associated with this object. - - Args: - rel : If set, filter links such that only those - matching this relationship are returned. - media_type: If set, filter the links such that only - those matching media_type are returned. media_type can - be a single value or a list of values. - - Returns: - List[:class:`~pystac.Link`]: A list of links that match ``rel`` and/ - or ``media_type`` if set, or else all links associated with this - object. - """ - if rel is None and media_type is None: - return self.links - if media_type and isinstance(media_type, (str, pystac.MediaType)): - media_type = [media_type] - return [ - link - for link in self.links - if (rel is None or link.rel == rel) - and (media_type is None or link.media_type in media_type) - ] - - def clear_links(self, rel: str | pystac.RelType | None = None) -> None: - """Clears all :class:`~pystac.Link` instances associated with this object. - - Args: - rel : If set, only clear links that match this relationship. - """ - if rel is not None: - self.links = [link for link in self.links if link.rel != rel] - else: - self.links = [] - - def get_root_link(self) -> Link | None: - """Get the :class:`~pystac.Link` representing - the root for this object. - - Returns: - :class:`~pystac.Link` or None: The root link for this object, - or ``None`` if no root link is set. - """ - return self.get_single_link( - rel=pystac.RelType.ROOT, - media_type=pystac.media_type.STAC_JSON, - ) - - @property - def self_href(self) -> str: - """Gets the absolute HREF that is represented by the ``rel == 'self'`` - :class:`~pystac.Link`. - - Raises: - ValueError: If the self_href is not set, this method will throw - a ValueError. Use get_self_href if there may not be an HREF set. - """ - result = self.get_self_href() - if result is None: - raise ValueError(f"{self} does not have a self_href set.") - return result - - def get_self_href(self) -> str | None: - """Gets the absolute HREF that is represented by the ``rel == 'self'`` - :class:`~pystac.Link`. - - Returns: - str or None: The absolute HREF of this object, or ``None`` if - there is no self link defined. - - Note: - A self link can exist for objects, even if the link is not read or - written to the JSON-serialized version of the object. Any object - read from :func:`STACObject.from_file ` will - have the HREF the file was read from set as it's self HREF. All self - links have absolute (as opposed to relative) HREFs. - """ - self_link = self.get_single_link(pystac.RelType.SELF) - if self_link and self_link.has_target_href(): - return self_link.get_target_str() - else: - return None - - def set_self_href(self, href: str | None) -> None: - """Sets the absolute HREF that is represented by the ``rel == 'self'`` - :class:`~pystac.Link`. - - Args: - href : The absolute HREF of this object. If the given HREF - is not absolute, it will be transformed to an absolute - HREF based on the current working directory. If this is None - the call will clear the self HREF link. - """ - root_link = self.get_root_link() - if root_link is not None and root_link.is_resolved(): - cast(pystac.Catalog, root_link.target)._resolved_objects.remove(self) - - self.remove_links(pystac.RelType.SELF) - if href is not None: - self.add_link(Link.self_href(href)) - - if root_link is not None and root_link.is_resolved(): - cast(pystac.Catalog, root_link.target)._resolved_objects.cache(self) - - def get_root(self) -> Catalog | None: - """Get the :class:`~pystac.Catalog` or :class:`~pystac.Collection` to - the root for this object. The root is represented by a - :class:`~pystac.Link` with ``rel == 'root'``. - - Returns: - Catalog, Collection, or None: - The root object for this object, or ``None`` if no root link is set. - """ - root_link = self.get_root_link() - if root_link: - if not root_link.is_resolved(): - root_link.resolve_stac_object() - # Use set_root, so Catalogs can merge ResolvedObjectCache instances. - self.set_root(cast(pystac.Catalog, root_link.target)) - return cast(pystac.Catalog, root_link.target) - else: - return None - - def set_root(self, root: Catalog | None) -> None: - """Sets the root :class:`~pystac.Catalog` or :class:`~pystac.Collection` - for this object. - - Args: - root : The root - object to set. Passing in None will clear the root. - """ - root_link_index = next( - iter( - [ - i - for i, link in enumerate(self.links) - if link.rel == pystac.RelType.ROOT - ] - ), - None, - ) - - # Remove from old root resolution cache - if root_link_index is not None: - root_link = self.links[root_link_index] - if root_link.is_resolved(): - cast(pystac.Catalog, root_link.target)._resolved_objects.remove(self) - - if root is None: - self.remove_links(pystac.RelType.ROOT) - else: - new_root_link = Link.root(root) - if root_link_index is not None: - self.links[root_link_index] = new_root_link - new_root_link.set_owner(self) - else: - self.add_link(new_root_link) - root._resolved_objects.cache(self) - - def get_parent(self) -> Catalog | None: - """Get the :class:`~pystac.Catalog` or :class:`~pystac.Collection` to - the parent for this object. The root is represented by a - :class:`~pystac.Link` with ``rel == 'parent'``. - - Returns: - Catalog, Collection, or None: - The parent object for this object, - or ``None`` if no root link is set. - """ - parent_link = self.get_single_link(pystac.RelType.PARENT) - if parent_link: - return cast(pystac.Catalog, parent_link.resolve_stac_object().target) - else: - return None - - def set_parent(self, parent: Catalog | None) -> None: - """Sets the parent :class:`~pystac.Catalog` or :class:`~pystac.Collection` - for this object. - - Args: - parent : The parent - object to set. Passing in None will clear the parent. - """ - - self.remove_links(pystac.RelType.PARENT) - if parent is not None: - self.add_link(Link.parent(parent)) - - def get_stac_objects( - self, - rel: str | pystac.RelType, - typ: type[STACObject] | None = None, - modify_links: Callable[[list[Link]], list[Link]] | None = None, - ) -> Iterable[STACObject]: - """Gets the :class:`STACObject` instances that are linked to - by links with their ``rel`` property matching the passed in argument. - - Args: - rel : The relation to match each :class:`~pystac.Link`'s - ``rel`` property against. - typ : If not ``None``, objects will only be yielded if they are instances of - ``typ``. - modify_links : A function that modifies the list of links before they are - iterated over. For instance, this option can be used to sort the list - so that links matching a particular pattern are earlier in the iterator. - - Returns: - Iterable[STACObject]: A possibly empty iterable of STACObjects that are - connected to this object through links with the given ``rel`` and are of - type ``typ`` (if given). - """ - links = self.links[:] - if modify_links: - links = modify_links(links) - - has_next_link = False - for i in range(0, len(links)): - link = links[i] - if link.rel == rel: - link.resolve_stac_object(root=self.get_root()) - if typ is None or isinstance(link.target, typ): - yield cast(STACObject, link.target) - if link.rel == "next": - has_next_link = True - if has_next_link: - warnings.warn( - "This STAC object has a 'next' link, but pystac does not support " - "pagination. If you need to paginate through responses from a STAC " - "API, use pystac-client: https://github.com/stac-utils/pystac-client" - ) - - def save_object( - self, - include_self_link: bool = True, - dest_href: str | None = None, - stac_io: pystac.StacIO | None = None, - ) -> None: - """Saves this :class:`STACObject` to it's 'self' HREF. - - Args: - include_self_link : If this is true, include the 'self' link with - this object. Otherwise, leave out the self link. - dest_href : Optional HREF to save the file to. If None, the object - will be saved to the object's self href. - stac_io: Optional instance of StacIO to use. If not provided, will use the - instance set on the object's root if available, otherwise will use the - default instance. - - - Raises: - STACError: If no self href is set, this error will be raised. - - Note: - When to include a self link is described in the :stac-spec:`Use of Links - section of the STAC best practices document - ` - """ - if stac_io is None: - root = self.get_root() - if root is not None: - root_stac_io = root._stac_io - if root_stac_io is not None: - stac_io = root_stac_io - - if stac_io is None: - stac_io = pystac.StacIO.default() - - if dest_href is None: - self_href = self.get_self_href() - if self_href is None: - raise STACError( - "Self HREF must be set before saving without an explicit dest_href." - ) - dest_href = self_href - - stac_io.save_json(dest_href, self.to_dict(include_self_link=include_self_link)) - - def full_copy( - self, - root: Catalog | None = None, - parent: Catalog | None = None, - ) -> STACObject: - """Create a full copy of this STAC object and any STAC objects linked to by - this object. - - Args: - root : Optional root to set as the root of the copied object, - and any other copies that are contained by this object. - parent : Optional parent to set as the parent of the copy - of this object. - - Returns: - STACObject: A full copy of this object, as well as any objects this object - links to. - """ - clone = self.clone() - - if root is None and isinstance(clone, pystac.Catalog): - root = clone - - # Set the root of the STAC Object using the base class, - # avoiding child class overrides - # extra logic which can be incompatible with the full copy. - STACObject.set_root(clone, cast(pystac.Catalog, root)) - - if parent: - clone.set_parent(parent) - - link_rels = set(self._object_links()) - for link in clone.links: - if link.rel in link_rels: - link.resolve_stac_object() - target = cast(STACObject, link.target) - if root is not None and target in root._resolved_objects: - cached_target = root._resolved_objects.get(target) - assert cached_target is not None - target = cached_target - else: - target_parent = None - if link.rel in [ - pystac.RelType.CHILD, - pystac.RelType.ITEM, - ] and isinstance(clone, pystac.Catalog): - target_parent = clone - copied_target = target.full_copy(root=root, parent=target_parent) - if root is not None: - root._resolved_objects.cache(copied_target) - target = copied_target - if link.rel in [pystac.RelType.CHILD, pystac.RelType.ITEM]: - target.set_root(root) - if isinstance(clone, pystac.Catalog): - target.set_parent(clone) - link.target = target - - return clone - - def resolve_links(self) -> None: - """Ensure all STACObjects linked to by this STACObject are - resolved. This is important for operations such as changing - HREFs. - - This method mutates the entire catalog tree. - """ - link_rels = set(self._object_links()) | { - pystac.RelType.ROOT, - pystac.RelType.PARENT, - } - - for link in self.links: - if link.rel in link_rels: - if not link.is_resolved(): - link.resolve_stac_object(root=self.get_root()) - - @abstractmethod - def _object_links(self) -> list[str]: - """Inherited classes return a list of link 'rel' types that represent - STACObjects linked to by this object (not including root, parent or self). - This can include optional relations (which may not be present). - """ - raise NotImplementedError - - @abstractmethod - def to_dict( - self, include_self_link: bool = True, transform_hrefs: bool = True - ) -> dict[str, Any]: - """Returns this object as a dictionary. - - Args: - include_self_link : If True, the dict will contain a self link - to this object. If False, the self link will be omitted. - transform_hrefs: If True, transform the HREF of hierarchical links - based on the type of catalog this object belongs to (if any). - I.e. if this object belongs to a root catalog that is - RELATIVE_PUBLISHED or SELF_CONTAINED, - hierarchical link HREFs will be transformed to be relative to the - catalog root. - - dict: A serialization of the object. - """ - raise NotImplementedError - - def _repr_html_(self) -> str: - from html import escape - - from pystac.html.jinja_env import get_jinja_env - - jinja_env = get_jinja_env() - if jinja_env: - template = jinja_env.get_template("JSON.jinja2") - return str( - template.render( - dict=self.to_dict(transform_hrefs=False), plain=escape(repr(self)) - ) - ) - else: - return escape(repr(self)) - - @abstractmethod - def clone(self) -> STACObject: - """Clones this object. - - Cloning an object will make a copy of all properties and links of the object; - however, it will not make copies of the targets of links (i.e. it is not a - deep copy). To copy a STACObject fully, with all linked elements also copied, - use :func:`~pystac.STACObject.full_copy`. - - Returns: - STACObject: The clone of this object. - """ - raise NotImplementedError - - @classmethod - def from_file(cls: type[S], href: HREF, stac_io: pystac.StacIO | None = None) -> S: - """Reads a STACObject implementation from a file. - - Args: - href : The HREF to read the object from. - stac_io: Optional instance of StacIO to use. If not provided, will use the - default instance. - - Returns: - The specific STACObject implementation class that is represented - by the JSON read from the file located at HREF. - """ - if cls == STACObject: - return cast(S, pystac.read_file(href)) - - href = make_posix_style(href) - - if stac_io is None: - stac_io = pystac.StacIO.default() - - if not is_absolute_href(href): - href = make_absolute_href(href) - - d = stac_io.read_json(href) - o = cls.from_dict(d, href=href, migrate=True, preserve_dict=False) - - # If this is a root catalog, set the root to the catalog instance. - root_link = o.get_root_link() - if root_link is not None: - if not root_link.is_resolved(): - if root_link.get_absolute_href() == href: - o.set_root(cast(pystac.Catalog, o)) - return o - - @classmethod - @abstractmethod - def from_dict( - cls: type[S], - d: dict[str, Any], - href: str | None = None, - root: Catalog | None = None, - migrate: bool = True, - preserve_dict: bool = True, - ) -> S: - """Parses this STACObject from the passed in dictionary. - - Args: - d : The dict to parse. - href : Optional href that is the file location of the object being - parsed. - root : Optional root catalog for this object. - If provided, the root of the returned STACObject will be set - to this parameter. - migrate: By default, STAC objects and extensions are migrated to - their latest supported version. Set this to False to disable - this behavior. - preserve_dict: If False, the dict parameter ``d`` may be modified - during this method call. Otherwise, the dict is not mutated. - Defaults to True, which results in a deepcopy of the - parameter. Set to False when possible to avoid the performance - hit of a deepcopy. - - Returns: - STACObject: The STACObject parsed from this dict. - """ - raise NotImplementedError - - @classmethod - @abstractmethod - def matches_object_type(cls, d: dict[str, Any]) -> bool: - """Returns a boolean indicating whether the given dictionary represents a valid - instance of this :class:`~pystac.STACObject` subclass. - - Args: - d : A dictionary to identify - """ - raise NotImplementedError diff --git a/pystac/summaries.py b/pystac/summaries.py deleted file mode 100644 index 6a0106251..000000000 --- a/pystac/summaries.py +++ /dev/null @@ -1,334 +0,0 @@ -from __future__ import annotations - -from abc import abstractmethod -from collections.abc import Iterable -from enum import Enum -from functools import lru_cache -from typing import ( - TYPE_CHECKING, - Any, - Generic, - Protocol, - TypeVar, -) - -import pystac -from pystac.utils import get_required - -if TYPE_CHECKING: - from pystac.collection import Collection - from pystac.item import Item - - -def __getattr__(name: str) -> Any: - if name == "FIELDS_JSON_URL": - import warnings - - warnings.warn( - "FIELDS_JSON_URL is deprecated and will be removed in v2", - DeprecationWarning, - ) - return ( - "https://cdn.jsdelivr.net/npm/@radiantearth/" - "stac-fields/fields-normalized.json" - ) - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") - - -class _Comparable_x(Protocol): - """Protocol for annotating comparable types. - - For matching __lt__ that takes an 'x' parameter - (e.g. float) - """ - - @abstractmethod - def __lt__(self: T, x: T) -> bool: - return NotImplemented - - -class _Comparable_other(Protocol): - """Protocol for annotating comparable types. - - For matching __lt___ that takes an 'other' parameter - (e.g. datetime) - """ - - @abstractmethod - def __lt__(self: T, other: T) -> bool: - return NotImplemented - - -T = TypeVar("T", bound=_Comparable_x | _Comparable_other) - - -class RangeSummary(Generic[T]): - minimum: T - maximum: T - - def __init__(self, minimum: T, maximum: T): - self.minimum = minimum - self.maximum = maximum - - def to_dict(self) -> dict[str, Any]: - return {"minimum": self.minimum, "maximum": self.maximum} - - def update_with_value(self, v: T) -> None: - self.minimum = min(self.minimum, v) - self.maximum = max(self.maximum, v) - - @classmethod - def from_dict(cls, d: dict[str, Any]) -> RangeSummary[T]: - minimum: T = get_required(d.get("minimum"), "RangeSummary", "minimum") - maximum: T = get_required(d.get("maximum"), "RangeSummary", "maximum") - return cls(minimum=minimum, maximum=maximum) - - def __eq__(self, o: object) -> bool: - if not isinstance(o, RangeSummary): - return NotImplemented - - return self.to_dict() == o.to_dict() - - def __repr__(self) -> str: - return self.to_dict().__repr__() - - -@lru_cache(maxsize=None) -def _get_fields_json(url: str | None) -> dict[str, Any]: - if url is None: - import importlib.resources - import json - - # Every time pystac is released this file gets pulled from - # https://cdn.jsdelivr.net/npm/@radiantearth/stac-fields/fields-normalized.json - jsonfields: dict[str, Any] = json.loads( - importlib.resources.files("pystac.static") - .joinpath("fields-normalized.json") - .read_text() - ) - return jsonfields - return pystac.StacIO.default().read_json(url) - - -class SummaryStrategy(Enum): - ARRAY = "v" - RANGE = "r" - SCHEMA = "s" - DONT_SUMMARIZE = False - DEFAULT = True - - -class Summarizer: - """The Summarizer computes summaries from values, following the definition of fields - to summarize. - - The fields to summarize can be provided as a JSON file or as a dictionary of - field names and SummaryStrategys. If nothing is provided, a default JSON file - will be used. - - Only fields that are in the Item `properties` can be summarized. - Thus, it is not possible to summarize the top-level fields such as `id` or `assets`. - - For more information about the structure of the fields JSON file, see: - https://github.com/stac-utils/stac-fields - - The default JSON file used is a snapshot of the following file at the time of - the pystac release: - https://cdn.jsdelivr.net/npm/@radiantearth/stac-fields/fields-normalized.json - - Args: - fields: A string containing the path to the JSON file with field descriptions. - Alternatively, a dict with the field names as keys and SummaryStrategys - as values. - If nothing is passed, a default file with field descriptions will be used. - """ - - summaryfields: dict[str, SummaryStrategy] - - def __init__(self, fields: str | dict[str, SummaryStrategy] | None = None): - if isinstance(fields, dict): - self._set_field_definitions(fields) - else: - jsonfields = _get_fields_json(fields) - self._set_field_definitions(jsonfields["metadata"]) - - def _set_field_definitions(self, fields: dict[str, Any]) -> None: - self.summaryfields = {} - for name, desc in fields.items(): - strategy: SummaryStrategy = SummaryStrategy.DEFAULT - if isinstance(desc, SummaryStrategy): - strategy = desc - elif isinstance(desc, dict): - strategy_value = desc.get("summary", True) - try: - strategy = SummaryStrategy(strategy_value) - except ValueError: - pass - - if strategy != SummaryStrategy.DONT_SUMMARIZE: - self.summaryfields[name] = strategy - - def _update_with_item(self, summaries: Summaries, item: Item) -> None: - import numbers - - for k, v in item.properties.items(): - if k in self.summaryfields: - strategy = self.summaryfields[k] - if strategy == SummaryStrategy.RANGE or ( - strategy == SummaryStrategy.DEFAULT - and isinstance(v, numbers.Number) - and not isinstance(v, bool) - ): - rangesummary: RangeSummary[Any] | None = summaries.get_range(k) - if rangesummary is None: - summaries.add(k, RangeSummary(v, v)) - else: - rangesummary.update_with_value(v) - elif strategy == SummaryStrategy.ARRAY or ( - strategy == SummaryStrategy.DEFAULT and isinstance(v, list) - ): - listsummary: list[Any] = summaries.get_list(k) or [] - if not isinstance(v, list): - v = [v] - for element in v: - if element not in listsummary: - listsummary.append(element) - summaries.add(k, listsummary) - else: - summary: list[Any] = summaries.get_list(k) or [] - if v not in summary: - summary.append(v) - summaries.add(k, summary) - - def summarize(self, source: Collection | Iterable[Item]) -> Summaries: - """Creates summaries from items""" - summaries = Summaries.empty() - if isinstance(source, pystac.Collection): - for item in source.get_items(recursive=True): - self._update_with_item(summaries, item) - else: - for item in source: - self._update_with_item(summaries, item) - - return summaries - - -DEFAULT_MAXCOUNT = 25 - - -class Summaries: - _summaries: dict[str, Any] - - lists: dict[str, list[Any]] - other: dict[str, Any] - ranges: dict[str, RangeSummary[Any]] - schemas: dict[str, dict[str, Any]] - maxcount: int - - def __init__( - self, summaries: dict[str, Any], maxcount: int = DEFAULT_MAXCOUNT - ) -> None: - self._summaries = summaries - self.maxcount = maxcount - - self.lists = {} - self.ranges = {} - self.schemas = {} - self.other = {} - - for prop_key, summary in summaries.items(): - self.add(prop_key, summary) - - def get_list(self, prop: str) -> list[Any] | None: - return self.lists.get(prop) - - def get_range(self, prop: str) -> RangeSummary[Any] | None: - return self.ranges.get(prop) - - def get_schema(self, prop: str) -> dict[str, Any] | None: - return self.schemas.get(prop) - - def add( - self, - prop_key: str, - summary: list[Any] | RangeSummary[Any] | dict[str, Any], - ) -> None: - if isinstance(summary, list): - self.lists[prop_key] = summary - elif isinstance(summary, dict): - if "minimum" in summary: - self.ranges[prop_key] = RangeSummary[Any].from_dict(summary) - else: - self.schemas[prop_key] = summary - elif isinstance(summary, RangeSummary): - self.ranges[prop_key] = summary - else: - self.other[prop_key] = summary - - def remove(self, prop_key: str) -> None: - self.lists.pop(prop_key, None) - self.ranges.pop(prop_key, None) - self.schemas.pop(prop_key, None) - self.other.pop(prop_key, None) - - def update(self, summaries: Summaries) -> None: - self.lists.update(summaries.lists) - self.ranges.update(summaries.ranges) - self.schemas.update(summaries.schemas) - self.other.update(summaries.other) - - def combine(self, summaries: Summaries) -> None: - for listname, listvalue in summaries.lists.items(): - if listname in self.lists: - self.lists[listname].extend(listvalue) - else: - self.lists[listname] = listvalue - for rangename, rang in summaries.ranges.items(): - if rangename in self.ranges: - self.ranges[rangename].update_with_value(rang.minimum) - self.ranges[rangename].update_with_value(rang.maximum) - else: - self.ranges[rangename] = rang - for schemaname, schema in summaries.schemas.items(): - if schemaname in self.schemas: - self.schemas[schemaname].update(schema) - else: - self.schemas[schemaname] = schema - for k, v in summaries.other.items(): - if k in self.other: - self.other[k].update(v) - else: - self.other[k] = v - - def is_empty(self) -> bool: - return not ( - any(self.lists) or any(self.ranges) or any(self.schemas) or any(self.other) - ) - - def clone(self) -> Summaries: - """Clones this object. - - Returns: - Summaries: The clone of this object - """ - from copy import deepcopy - - cls = self.__class__ - summaries = cls(summaries=deepcopy(self._summaries), maxcount=self.maxcount) - summaries.lists = deepcopy(self.lists) - summaries.other = deepcopy(self.other) - summaries.ranges = deepcopy(self.ranges) - summaries.schemas = deepcopy(self.schemas) - return summaries - - def to_dict(self) -> dict[str, Any]: - return { - **{k: v for k, v in self.lists.items() if len(v) < self.maxcount}, - **{k: v.to_dict() for k, v in self.ranges.items()}, - **self.schemas, - **self.other, - } - - @classmethod - def empty(cls) -> Summaries: - return Summaries({}) diff --git a/release-please-config.json b/release-please-config.json index 4ef108150..81f9864a8 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -3,10 +3,8 @@ ".": { "changelog-path": "CHANGELOG.md", "release-type": "python", - "bump-minor-pre-major": false, - "bump-patch-for-minor-pre-major": false, - "draft": false, - "prerelease": false + "prerelease": true, + "prerelease-type": "dev" } }, "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json" diff --git a/scripts/pull-static b/scripts/pull-static index 46b6b27b8..fab104a27 100755 --- a/scripts/pull-static +++ b/scripts/pull-static @@ -5,7 +5,7 @@ set -e 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 +DEST=$(dirname "$HERE")/src/pystac/static/fields-normalized.json echo "Pulling fields-normalized.json from cdn to $DEST" curl "$SRC" | jq -c '{metadata: .metadata}' > "$DEST" diff --git a/src/pystac/__init__.py b/src/pystac/__init__.py new file mode 100644 index 000000000..056221244 --- /dev/null +++ b/src/pystac/__init__.py @@ -0,0 +1,95 @@ +from typing import Any + +from typing_extensions import deprecated + +from .asset import Asset, ItemAsset +from .band import Band +from .catalog import Catalog +from .collection import ( + Collection, + Extent, + SpatialExtent, + TemporalExtent, +) +from .common_metadata import CommonMetadata +from .constants import DEFAULT_STAC_VERSION +from .deserialize import from_dict +from .errors import ( + ExtensionNotImplemented, + ExtensionTypeError, + STACError, + STACTypeError, + STACValidationError, + TemplateError, +) +from .io import read_file +from .item import Item +from .item_assets import ItemAssetDefinition +from .item_collection import ItemCollection +from .link import HIERARCHICAL_LINKS, Link +from .media_type import MediaType +from .provider import Provider, ProviderRole +from .rel_type import RelType +from .stac_object import STACObject +from .summaries import Summaries +from .version import __version__ + + +def __getattr__(name: str) -> Any: + match name: + case "StacIO": + from .stac_io import StacIO + + return StacIO + + case "STACObjectType": + from .stac_object import STACObjectType # pyright: ignore[reportDeprecated] + + return STACObjectType # pyright: ignore[reportDeprecated] + case "CatalogType": + from .catalog import CatalogType # pyright: ignore[reportDeprecated] + + return CatalogType # pyright: ignore[reportDeprecated] + case "get_stac_version": + + @deprecated("get_stac_version is deprecated") + def get_stac_version() -> str: + return DEFAULT_STAC_VERSION + + return get_stac_version # pyright: ignore[reportDeprecated] + case _: + raise AttributeError(f"module {__name__} has no attribute {name}") + + +__all__ = [ + "Asset", + "Band", + "Catalog", + "Collection", + "CommonMetadata", + "ExtensionNotImplemented", + "ExtensionTypeError", + "Extent", + "HIERARCHICAL_LINKS", + "RelType", + "DEFAULT_STAC_VERSION", + "Item", + "ItemAsset", + "ItemAssetDefinition", + "ItemCollection", + "Link", + "MediaType", + "Provider", + "from_dict", + "ProviderRole", + "STACError", + "STACObject", + "TemplateError", + "STACTypeError", + "STACValidationError", + "SpatialExtent", + "Summaries", + "TemporalExtent", + "read_file", + "__version__", +] diff --git a/src/pystac/asset.py b/src/pystac/asset.py new file mode 100644 index 000000000..cf7751d55 --- /dev/null +++ b/src/pystac/asset.py @@ -0,0 +1,201 @@ +from __future__ import annotations + +import copy +from typing import TYPE_CHECKING, Any, Protocol, override + +from typing_extensions import deprecated + +from .band import Band +from .common_metadata import DataValue, DateTime, Instrument +from .media_type import MediaType +from .utils import ( + get_absolute_href, + is_absolute_href, + make_absolute_href, + make_posix_style, + make_relative_href, +) +from .writer import Writer + +if TYPE_CHECKING: + from .extensions.ext import AssetExt + from .stac_object import STACObject + + +class ItemAsset(DateTime, DataValue, Instrument): + def __init__( + self, + *, + title: str | None = None, + description: str | None = None, + type: str | None = None, + roles: list[str] | None = None, + bands: list[Band] | list[dict[str, Any]] | None = None, + **kwargs: Any, + ): + self.title: str | None = title + self.description: str | None = description + self.type: str | None = type + self.roles: list[str] | None = roles + if bands is not None: + self.bands: list[Band] | None = [Band.try_from(band) for band in bands] + else: + self.bands = None + self.extra_fields: dict[str, Any] = kwargs + self.extra_fields.update(DateTime.from_str(kwargs)) + + @override + def __eq__(self, other: Any) -> bool: + if not isinstance(other, ItemAsset): + return NotImplemented + return self.to_dict() == other.to_dict() + + @classmethod + @deprecated("Instantiate the class directly instead") + def create[T: ItemAsset](cls: type[T], **kwargs: Any) -> T: + return cls(**kwargs) + + @classmethod + def try_from[T: ItemAsset](cls: type[T], data: T | dict[str, Any]) -> T: + if isinstance(data, cls): + return data + elif isinstance(data, dict): + return cls.from_dict(data) + else: + raise ValueError(f"Invalid data type for item asset: {type(data)}") + + @classmethod + def from_dict[T: ItemAsset](cls: type[T], data: dict[str, Any]) -> T: + return cls(**data) + + def clone[T: ItemAsset](self: T) -> T: + return self.from_dict(self.to_dict()) + + def to_dict(self) -> dict[str, Any]: + data: dict[str, Any] = copy.deepcopy(self.extra_fields) + data.update(DateTime.to_str(self.extra_fields)) + data["title"] = self.title + data["description"] = self.description + data["type"] = self.type + data["roles"] = self.roles + data["statistics"] = self.statistics.to_dict() or None + if self.bands is not None: + data["bands"] = [band.to_dict() for band in self.bands] + return {k: v for k, v in data.items() if v is not None} + + +class Asset(ItemAsset): + def __init__( + self, + href: str, + *, + title: str | None = None, + description: str | None = None, + type: str | None = None, + roles: list[str] | None = None, + bands: list[Band] | list[dict[str, Any]] | None = None, + **kwargs: Any, + ): + super().__init__( + title=title, + description=description, + type=type, + roles=roles, + bands=bands, + **kwargs, + ) + self.href: str = href + + self.owner: STACObject | None = None + + @staticmethod + def update_hrefs( + assets: dict[str, Asset], start_href: str | None, end_href: str | None + ) -> None: + for asset in assets.values(): + if not is_absolute_href(asset.href): + if start_href and end_href: + asset.href = make_relative_href( + make_absolute_href(asset.href, start_href), end_href + ) + else: + asset.href = make_posix_style(asset.href) + + def set_owner(self, owner: STACObject | None) -> None: + self.owner = owner + + def get_absolute_href(self) -> str | None: + owner_href = self.owner.get_self_href() if self.owner else None + return get_absolute_href(self.href, owner_href) + + @override + def to_dict(self) -> dict[str, Any]: + data = super().to_dict() + return {"href": self.href, **data} + + @property + def ext(self) -> AssetExt: + """Accessor for extension classes on this asset + + Example:: + + asset.ext.proj.code = "EPSG:4326" + """ + from pystac.extensions.ext import AssetExt + + return AssetExt(stac_object=self) + + +class Assets(Protocol): + assets: dict[str, Asset] + writer: Writer + + def get_self_href(self) -> str | None: ... + + def get_assets( + self, media_type: MediaType | None = None, role: str | None = None + ) -> dict[str, Asset]: + return { + key: copy.deepcopy(asset) + for (key, asset) in self.assets.items() + if (not media_type or asset.type == media_type) + and (not role or (asset.roles and role in asset.roles)) + } + + @deprecated( + "add_asset is deprecated, just add the asset directly to the assets dictionary" + ) + def add_asset(self, key: str, asset: Asset) -> None: + self.assets[key] = asset + + def make_asset_hrefs_relative(self) -> None: + if owner_href := self.get_self_href(): + for asset in self.assets.values(): + if is_absolute_href(asset.href, owner_href): + asset.href = make_relative_href(asset.href, owner_href) + else: + raise ValueError( + "Cannot make asset hrefs relative, item does not have a self href" + ) + + def make_asset_hrefs_absolute(self) -> None: + if owner_href := self.get_self_href(): + for asset in self.assets.values(): + if not is_absolute_href(asset.href, owner_href): + asset.href = make_absolute_href(asset.href, owner_href) + else: + raise ValueError( + "Cannot make asset hrefs absolute, item does not have a self href" + ) + + @deprecated( + "delete_asset is deprecated, just delete the asset directly from the " + "assets dictionary" + ) + def delete_asset(self, key: str) -> None: + asset = self.assets[key] + try: + self.writer.delete(make_absolute_href(asset.href, self.get_self_href())) + except Exception as e: + raise ValueError(f"Cannot delete file {asset.href}: {e}") + del self.assets[key] diff --git a/src/pystac/band.py b/src/pystac/band.py new file mode 100644 index 000000000..837161600 --- /dev/null +++ b/src/pystac/band.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +import copy +from typing import Any, override + + +class Band: + def __init__( + self, + name: str | None = None, + **kwargs: Any, + ): + self.name: str | None = name + self.extra_fields: dict[str, Any] = kwargs + + def __getitem__(self, key: str) -> Any: + if hasattr(self, key): + return getattr(self, key) + else: + return self.extra_fields[key] + + def __setitem__(self, key: str, value: Any) -> None: + if hasattr(self, key): + setattr(self, key, value) + else: + self.extra_fields[key] = value + + def __contains__(self, key: str) -> bool: + return key == "name" or key in self.extra_fields + + def __deepcopy__(self, memo: dict[int, Any]) -> Band: + return Band( + name=self.name, + **copy.deepcopy(self.extra_fields, memo), + ) + + @override + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Band): + return NotImplemented + return self.to_dict() == other.to_dict() + + @override + def __repr__(self) -> str: + return f"Band(name={self.name!r})" + + @classmethod + def try_from(cls, data: dict[str, Any] | Band) -> Band: + if isinstance(data, Band): + return data + else: + return cls(**data) + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> Band: + return cls(**data) + + def to_dict(self) -> dict[str, Any]: + data = copy.deepcopy(self.extra_fields) + if self.name: + data["name"] = self.name + return data diff --git a/pystac/cache.py b/src/pystac/cache.py similarity index 99% rename from pystac/cache.py rename to src/pystac/cache.py index 4a923332f..aa369b9a3 100644 --- a/pystac/cache.py +++ b/src/pystac/cache.py @@ -1,5 +1,12 @@ from __future__ import annotations +import warnings + +warnings.warn( + "pystac.cache is deprecated", + DeprecationWarning, +) + from collections import ChainMap from copy import copy from typing import TYPE_CHECKING, Any, cast diff --git a/src/pystac/catalog.py b/src/pystac/catalog.py new file mode 100644 index 000000000..8286e17fc --- /dev/null +++ b/src/pystac/catalog.py @@ -0,0 +1,164 @@ +from __future__ import annotations + +import copy +import warnings +from enum import StrEnum +from typing import TYPE_CHECKING, Any, ClassVar, override + +from typing_extensions import deprecated + +from . import utils +from .constants import DEFAULT_STAC_VERSION, STAC_OBJECT_TYPE +from .container import Container +from .link import Link +from .rel_type import RelType + +if TYPE_CHECKING: + from .extensions.ext import CatalogExt + + +class Catalog(Container): + type: ClassVar[STAC_OBJECT_TYPE] = "Catalog" + + def __init__( + self, + id: str, + description: str, + type: str = "Catalog", + stac_version: str = DEFAULT_STAC_VERSION, + stac_extensions: list[str] | None = None, + links: list[Link] | list[dict[str, Any]] | None = None, + **kwargs: Any, + ): + super().__init__( + type=type, + id=id, + stac_version=stac_version, + stac_extensions=stac_extensions, + links=links, + **kwargs, + ) + self.description: str = description + + @property + @deprecated("catalog_type is deprecated") + def catalog_type(self) -> CatalogType | None: # pyright: ignore[reportDeprecated] + return CatalogType.determine_type(self.to_dict()) # pyright: ignore[reportDeprecated] + + @catalog_type.setter + @deprecated("catalog_type is deprecated, and setting it has no effect") + def catalog_type(self, _: CatalogType) -> None: # pyright: ignore[reportDeprecated] + pass + + @override + @classmethod + def from_dict( + cls, + data: dict[str, Any], + preserve_dict: bool = True, + migrate: bool | None = None, + root: Container | None = None, + ) -> Catalog: + if preserve_dict: + data = copy.deepcopy(data) + catalog = cls(**data) + + # TODO deprecate migrate + if migrate: + raise NotImplementedError + if root: + warnings.warn( + "The root parameter is deprecated, call `set_root` directly after " + "creating the item", + DeprecationWarning, + ) + catalog.set_root(root) + + return catalog + + @override + def to_dict( + self, include_self_link: bool = True, transform_hrefs: bool | None = None + ) -> dict[str, Any]: + data = super().to_dict( + include_self_link=include_self_link, transform_hrefs=transform_hrefs + ) + data["description"] = self.description + return data + + @property + def ext(self) -> CatalogExt: + """Accessor for extension classes on this catalog + + Example:: + + print(collection.ext.version) + """ + from pystac.extensions.ext import CatalogExt + + return CatalogExt(stac_object=self) + + +@deprecated("CatalogType is deprecated") +class CatalogType(StrEnum): + SELF_CONTAINED = "SELF_CONTAINED" + """A 'self-contained catalog' is one that is designed for portability. + Users may want to download an online catalog from and be able to use it on their + local computer, so all links need to be relative. + + See: + :stac-spec:`The best practices documentation on self-contained catalogs + ` + """ + + ABSOLUTE_PUBLISHED = "ABSOLUTE_PUBLISHED" + """ + Absolute Published Catalog is a catalog that uses absolute links for everything, + both in the links objects and in the asset hrefs. + + See: + :stac-spec:`The best practices documentation on published catalogs + ` + """ + + RELATIVE_PUBLISHED = "RELATIVE_PUBLISHED" + """ + Relative Published Catalog is a catalog that uses relative links for everything, but + includes an absolute self link at the root catalog, to identify its online location. + + See: + :stac-spec:`The best practices documentation on published catalogs + ` + """ + + @classmethod + def determine_type(cls, stac_json: dict[str, Any]) -> CatalogType | None: # pyright: ignore[reportDeprecated] + """Determines the catalog type based on a STAC JSON dict. + + Only applies to Catalogs or Collections + + Args: + stac_json : The STAC JSON dict to determine the catalog type + + Returns: + Optional[CatalogType]: The catalog type of the catalog or collection. + Will return None if it cannot be determined. + """ + self_link = None + relative = False + for link in stac_json["links"]: + if link["rel"] == RelType.SELF: + self_link = link + else: + relative |= not utils.is_absolute_href(link["href"]) + + if self_link: + if relative: + return cls.RELATIVE_PUBLISHED + else: + return cls.ABSOLUTE_PUBLISHED + else: + if relative: + return cls.SELF_CONTAINED + else: + return None diff --git a/src/pystac/collection.py b/src/pystac/collection.py new file mode 100644 index 000000000..5016769a5 --- /dev/null +++ b/src/pystac/collection.py @@ -0,0 +1,444 @@ +from __future__ import annotations + +import copy +import datetime as dt +import warnings +from collections.abc import Iterable +from typing import ( + TYPE_CHECKING, + Any, + ClassVar, + Self, + TypedDict, + cast, + override, +) + +from typing_extensions import deprecated + +from .asset import Asset, Assets, ItemAsset +from .band import Band +from .constants import DEFAULT_LICENSE, DEFAULT_STAC_VERSION, STAC_OBJECT_TYPE +from .container import Container +from .link import Link +from .provider import Provider +from .summaries import Summaries +from .utils import to_datetime_str + +if TYPE_CHECKING: + from .extensions.ext import CollectionExt + from .item import Item + from .item_collection import ItemCollection + +SpatialExtentBboxType = list[float | int] | list[list[float | int]] +TemporalExtentIntervalType = ( + list[dt.datetime | str | None] | list[list[dt.datetime | str | None]] +) + + +class SpatialExtentDict(TypedDict): + bbox: SpatialExtentBboxType + + +class TemporalExtentDict(TypedDict): + interval: TemporalExtentIntervalType + + +class Collection(Container, Assets): + type: ClassVar[STAC_OBJECT_TYPE] = "Collection" + + def __init__( + self, + id: str, + description: str, + extent: dict[str, Any] | Extent | None = None, + type: str = "Collection", + title: str | None = None, + keywords: list[str] | None = None, + license: str = DEFAULT_LICENSE, + providers: list[Provider | dict[str, Any]] | None = None, + summaries: Summaries | dict[str, Any] | None = None, + assets: dict[str, Asset | dict[str, Any]] | None = None, + item_assets: dict[str, ItemAsset] | None = None, + stac_version: str = DEFAULT_STAC_VERSION, + stac_extensions: list[str] | None = None, + links: list[Link] | list[dict[str, Any]] | None = None, + bands: list[Band] | list[dict[str, Any]] | None = None, + **kwargs: Any, + ) -> None: + super().__init__(type, id, stac_version, stac_extensions, links, **kwargs) + self.description: str = description + self.title: str | None = title + self.keywords: list[str] | None = keywords + self.license: str = license + self.providers: list[Provider] | None = ( + [Provider.try_from(provider) for provider in providers] + if providers is not None + else None + ) + if bands is not None: + self.bands: list[Band] | None = [Band.try_from(band) for band in bands] + else: + self.bands = None + + if extent is None: + warnings.warn( + "Collection is missing extent, setting default spatial and " + "temporal extents" + ) + self.extent: Extent = Extent.try_from(extent) + self.summaries: Summaries = Summaries.try_from(summaries) + + self.assets: dict[str, Asset] = {} + if assets is not None: + for key, value in assets.items(): + asset = Asset.try_from(value) + asset.set_owner(self) + self.assets[key] = asset + + self.item_assets: dict[str, ItemAsset] | None = ( + {key: ItemAsset.try_from(value) for (key, value) in item_assets.items()} + if item_assets is not None + else None + ) + + @override + @classmethod + def from_dict[T: Collection]( + cls: type[T], + data: dict[str, Any], + preserve_dict: bool = True, + migrate: bool | None = None, + root: Container | None = None, + ) -> T: + if preserve_dict: + data = copy.deepcopy(data) + return cls(**data) + + @classmethod + def from_items[T: Collection]( + cls: type[T], + items: Iterable[Item] | ItemCollection, + *, + id: str | None = None, + ) -> T: + """Create a :class:`Collection` from iterable of items or an + :class:`~pystac.ItemCollection`. + + Will try to pull collection attributes from + :attr:`~pystac.ItemCollection.extra_fields` and items when possible. + + Args: + items : Iterable of :class:`~pystac.Item` instances to include in the + :class:`Collection`. This can be a :class:`~pystac.ItemCollection`. + id : Identifier for the collection. If not set, must be available on the + items and they must all match. + """ + from .item_collection import ItemCollection + + if id is None: + values = {item.collection_id for item in items} + if len(values) == 1: + id = next(iter(values)) + if id is None: + raise ValueError( + "Collection id must be defined. Either by specifying collection_id " + "on every item, or as a keyword argument to this function." + ) + + kwargs: dict[str, Any] = {} + if isinstance(items, ItemCollection): + kwargs = copy.deepcopy(items.extra_fields) + + if "description" not in kwargs: + values = {item.properties.description for item in items} + if len(values) == 1: + kwargs["description"] = next(iter(values)) + + if "title" not in kwargs: + values = {item.properties.title for item in items} + if len(values) == 1: + kwargs["title"] = next(iter(values)) + + collection = cls( + id=id, + description=kwargs.pop("description"), + extent=Extent.from_items(items), + **kwargs, + ) + + _ = collection.add_items(items) + + return collection + + @property + @deprecated("use .type instead") + def STAC_OBJECT_TYPE(self) -> STAC_OBJECT_TYPE: + return self.type + + @override + def set_self_href(self, href: str | None) -> None: + if self.assets: + Asset.update_hrefs(self.assets, self.get_self_href(), href) + return super().set_self_href(href) + + @override + def to_dict( + self, include_self_link: bool = True, transform_hrefs: bool | None = None + ) -> dict[str, Any]: + data = super().to_dict( + include_self_link=include_self_link, transform_hrefs=transform_hrefs + ) + data["description"] = self.description + if self.title: + data["title"] = self.title + if self.keywords: + data["keywords"] = self.keywords + data["license"] = self.license + if self.providers: + data["providers"] = [provider.to_dict() for provider in self.providers] + data["extent"] = self.extent.to_dict() + if self.summaries: + data["summaries"] = self.summaries.to_dict() + if self.bands is not None: + data["bands"] = [band.to_dict() for band in self.bands] + if self.assets: + data["assets"] = { + key: asset.to_dict() for key, asset in self.assets.items() + } + if self.item_assets: + data["item_assets"] = { + key: item_asset.to_dict() + for key, item_asset in self.item_assets.items() + } + return data + + def update_extent_from_items(self) -> None: + """ + Update datetime and bbox based on all items to a single bbox and time window. + """ + self.extent = Extent.from_items(self.get_items(recursive=True)) + + @property + def ext(self) -> CollectionExt: + """Accessor for extension classes on this collection + + Example:: + + print(collection.ext.xarray) + """ + from pystac.extensions.ext import CollectionExt + + return CollectionExt(stac_object=self) + + +class Extent: + def __init__( + self, + spatial: SpatialExtent | SpatialExtentDict | None = None, + temporal: TemporalExtent | TemporalExtentDict | None = None, + **kwargs: Any, + ): + self.spatial: SpatialExtent = SpatialExtent.try_from(spatial) + self.temporal: TemporalExtent = TemporalExtent.try_from(temporal) + self.extra_fields: dict[str, Any] = kwargs + + @classmethod + def try_from(cls, extent: Extent | dict[str, Any] | None) -> Extent: + if isinstance(extent, Extent): + return extent + elif isinstance(extent, dict): + return Extent.from_dict(extent) + else: + return Extent() + + @classmethod + def from_dict[T: Extent](cls: type[T], data: dict[str, Any]) -> T: + return cls(**data) + + @classmethod + def from_items(cls: type[Self], items: Iterable[Item]) -> Extent: + """Create an Extent based on the datetimes and bboxes of a list of items. + + Args: + items : A list of items to derive the extent from. + + Returns: + Extent: An Extent that spatially and temporally covers all of the + given items. + """ + bounds_values: list[list[float]] = [ + [float("inf")], + [float("inf")], + [float("-inf")], + [float("-inf")], + ] + datetimes: list[dt.datetime] = [] + starts: list[dt.datetime] = [] + ends: list[dt.datetime] = [] + + for item in items: + if item.bbox is not None: + for i in range(0, 4): + bounds_values[i].append(item.bbox[i]) + if item.datetime is not None: + datetimes.append(item.datetime) + if item.properties.start_datetime is not None: + starts.append(item.properties.start_datetime) + if item.properties.end_datetime is not None: + ends.append(item.properties.end_datetime) + + if not any(datetimes + starts): + start_timestamp = None + else: + start_timestamp = min( + [ + d if d.tzinfo else d.replace(tzinfo=dt.UTC) + for d in datetimes + starts + ] + ) + if not any(datetimes + ends): + end_timestamp = None + else: + end_timestamp = max( + [d if d.tzinfo else d.replace(tzinfo=dt.UTC) for d in datetimes + ends] + ) + + spatial = SpatialExtent( + [ + [ + min(bounds_values[0]), + min(bounds_values[1]), + max(bounds_values[2]), + max(bounds_values[3]), + ] + ] + ) + temporal = TemporalExtent( + [ + [ + to_datetime_str(start_timestamp), + to_datetime_str(end_timestamp), + ] + ] + ) + + return Extent(spatial=spatial, temporal=temporal) + + def clone[T: Extent](self: T) -> T: + return self.from_dict(self.to_dict()) + + def to_dict(self) -> dict[str, Any]: + data = copy.deepcopy(self.extra_fields) + return { + "spatial": self.spatial.to_dict(), + "temporal": self.temporal.to_dict(), + **data, + } + + +class TemporalExtent: + def __init__( + self, + interval: TemporalExtentIntervalType | str | dt.datetime | None = None, + **kwargs: Any, + ) -> None: + if interval is None and "intervals" in kwargs: + warnings.warn( + "intervals is deprecated and will be removed in a future version. " + "Use interval instead", + FutureWarning, + ) + interval = kwargs.pop("intervals") + + self.interval: list[list[str | None]] + if interval is None: + self.interval = [[None, None]] + elif isinstance(interval, (str, dt.datetime)): + self.interval = [to_interval([interval, None])] + elif isinstance(interval[0], list): + self.interval = [ + to_interval(cast(list[dt.datetime | str | None], dates)) + for dates in interval + ] + else: + self.interval = [ + to_interval(cast(list[dt.datetime | str | None], interval)) + ] + + self.extra_fields: dict[str, Any] = kwargs + + @classmethod + def try_from( + cls, + data: TemporalExtent | TemporalExtentDict | None, + ) -> TemporalExtent: + if isinstance(data, TemporalExtent): + return data + elif not data: + return TemporalExtent() + else: + return TemporalExtent(**data) + + @property + @deprecated("Use interval instead") + def intervals(self) -> list[list[str | None]]: + return self.interval + + @classmethod + @deprecated("Use `TemporalExtent(dt.datetime.now())` instead") + def from_now(cls) -> TemporalExtent: + return TemporalExtent(interval=dt.datetime.now()) + + def to_dict(self) -> dict[str, Any]: + data = copy.deepcopy(self.extra_fields) + return {"interval": self.interval, **data} + + +class SpatialExtent: + def __init__( + self, bbox: SpatialExtentBboxType | None = None, **kwargs: Any + ) -> None: + if bbox is None and "bboxes" in kwargs: + warnings.warn( + "bboxes is deprecated and will be removed in a future version. " + "Use bbox instead", + FutureWarning, + ) + bbox = kwargs.pop("bboxes") + + self.bbox: list[list[float | int]] + if bbox is None: + self.bbox = [[-180, -90, 180, 90]] + elif isinstance(bbox[0], list): + self.bbox = cast(list[list[float | int]], bbox) + else: + self.bbox = [cast(list[float | int], bbox)] + self.extra_fields: dict[str, Any] = kwargs + + @classmethod + def try_from( + cls, + data: SpatialExtent | SpatialExtentDict | None, + ) -> SpatialExtent: + if isinstance(data, SpatialExtent): + return data + elif not data: + return SpatialExtent() + else: + return SpatialExtent(**data) + + @property + @deprecated("Use bbox instead") + def bboxes(self) -> list[list[float | int]]: + return self.bbox + + def to_dict(self) -> dict[str, Any]: + data = copy.deepcopy(self.extra_fields) + return {"bbox": self.bbox, **data} + + +def to_interval(interval: list[dt.datetime | str | None]) -> list[str | None]: + if len(interval) != 2: + raise ValueError("Interval must have exactly two elements") + return [to_datetime_str(interval[0]), to_datetime_str(interval[1])] diff --git a/src/pystac/common_metadata.py b/src/pystac/common_metadata.py new file mode 100644 index 000000000..a8bc40afd --- /dev/null +++ b/src/pystac/common_metadata.py @@ -0,0 +1,393 @@ +from __future__ import annotations + +import datetime as dt +from dataclasses import dataclass +from enum import StrEnum +from typing import TYPE_CHECKING, Any, Protocol, override + +from .provider import Provider +from .utils import from_datetime_str, to_datetime_str + +if TYPE_CHECKING: + from .asset import Asset + from .item import Item + + +class Basics(Protocol): + """Protocol for basic descriptive fields.""" + + extra_fields: dict[str, Any] + + @property + def title(self) -> str | None: + """A human readable title describing the STAC entity.""" + return self.extra_fields.get("title") + + @title.setter + def title(self, value: str | None) -> None: + self.extra_fields["title"] = value + + @property + def description(self) -> str | None: + """ + Detailed multi-line description to fully explain the STAC entity. + CommonMark 0.29 syntax MAY be used for rich text representation. + """ + return self.extra_fields.get("description") + + @description.setter + def description(self, value: str | None) -> None: + if value == "": + raise ValueError("description cannot be an empty string") + self.extra_fields["description"] = value + + @property + def keywords(self) -> list[str] | None: + """List of keywords describing the STAC entity.""" + return self.extra_fields.get("keywords") + + @keywords.setter + def keywords(self, value: list[str] | None) -> None: + self.extra_fields["keywords"] = value + + @property + def roles(self) -> list[str] | None: + """ + The semantic roles of the entity, e.g. for assets, + links, providers, bands, etc. + """ + return self.extra_fields.get("roles") + + @roles.setter + def roles(self, value: list[str] | None) -> None: + self.extra_fields["roles"] = value + + +class DateTime(Protocol): + """Protocol for date and time fields.""" + + extra_fields: dict[str, Any] + + @property + def datetime(self) -> dt.datetime | None: + """See the Item Specification Fields for more information.""" + return self.extra_fields.get("datetime") + + @datetime.setter + def datetime(self, value: str | dt.datetime | None) -> None: # pyright: ignore[reportPropertyTypeMismatch] + self.extra_fields["datetime"] = from_datetime_str(value) + + @property + def created(self) -> dt.datetime | None: + """Creation date and time of the corresponding STAC entity or Asset, in UTC.""" + return self.extra_fields.get("created") + + @created.setter + def created(self, value: str | dt.datetime | None) -> None: # pyright: ignore[reportPropertyTypeMismatch] + self.extra_fields["created"] = from_datetime_str(value) + + @property + def updated(self) -> dt.datetime | None: + """ + Date and time the corresponding STAC entity or Asset + was updated last, in UTC. + """ + return self.extra_fields.get("updated") + + @updated.setter + def updated(self, value: str | dt.datetime | None) -> None: # pyright: ignore[reportPropertyTypeMismatch] + self.extra_fields["updated"] = from_datetime_str(value) + + @property + def start_datetime(self) -> dt.datetime | None: + """The first or start date and time for the resource, in UTC.""" + return self.extra_fields.get("start_datetime") + + @start_datetime.setter + def start_datetime(self, value: str | dt.datetime | None) -> None: # pyright: ignore[reportPropertyTypeMismatch] + self.extra_fields["start_datetime"] = from_datetime_str(value) + + @property + def end_datetime(self) -> dt.datetime | None: + """The last or end date and time for the resource, in UTC.""" + return self.extra_fields.get("end_datetime") + + @end_datetime.setter + def end_datetime(self, value: str | dt.datetime | None) -> None: # pyright: ignore[reportPropertyTypeMismatch] + self.extra_fields["end_datetime"] = from_datetime_str(value) + + @staticmethod + def from_str(extra_fields: dict[str, Any]) -> dict[str, dt.datetime | None]: + """Create new dict with iso strings replaced by dt.datetimes""" + data: dict[str, dt.datetime | None] = {} + for field in ( + "datetime", + "created", + "updated", + "start_datetime", + "end_datetime", + ): + if field in extra_fields: + data[field] = from_datetime_str(extra_fields[field]) + return data + + @staticmethod + def to_str(extra_fields: dict[str, Any]) -> dict[str, str | None]: + """Create new dict with dt.datetimes replaced by iso strings""" + data: dict[str, str | None] = {} + for field in ( + "datetime", + "created", + "updated", + "start_datetime", + "end_datetime", + ): + if field in extra_fields: + data[field] = to_datetime_str(extra_fields[field]) + return data + + +class Licensing(Protocol): + """Protocol for information about the license(s) of the data""" + + extra_fields: dict[str, Any] + + @property + def license(self) -> str | None: + """License(s) of the data that the STAC entity provides.""" + return self.extra_fields.get("license") + + @license.setter + def license(self, value: str | None) -> None: + self.extra_fields["license"] = value + + +class Providers(Protocol): + """Protocol for nformation about the organizations capturing, producing, + processing, hosting or publishing this data.""" + + extra_fields: dict[str, Any] + + @property + def providers(self) -> list[Provider] | None: + """Provider(s) of the data that the STAC entity provides.""" + return [ + Provider.try_from(p) for p in self.extra_fields.get("providers", []) + ] or None + + @providers.setter + def providers(self, value: list[Provider] | None) -> None: + providers: list[dict[str, Any]] | None = None + if isinstance(value, list): + providers = [Provider.try_from(p).to_dict() for p in value] + self.extra_fields["providers"] = providers + + +class Instrument(Protocol): + """Protocol for instrument metadata fields.""" + + extra_fields: dict[str, Any] + + @property + def platform(self) -> str | None: + """Unique name of the specific platform to which the instrument is attached.""" + return self.extra_fields.get("platform") + + @platform.setter + def platform(self, value: str | None) -> None: + self.extra_fields["platform"] = value + + @property + def instruments(self) -> list[str] | None: + """Name of instrument or sensor used (e.g., MODIS, ASTER, OLI, Canon F-1).""" + return self.extra_fields.get("instruments") + + @instruments.setter + def instruments(self, value: list[str] | None) -> None: + self.extra_fields["instruments"] = value + + @property + def constellation(self) -> str | None: + """Name of the constellation to which the platform belongs.""" + return self.extra_fields.get("constellation") + + @constellation.setter + def constellation(self, value: str | None) -> None: + self.extra_fields["constellation"] = value + + @property + def mission(self) -> str | None: + """Name of the mission for which data is collected.""" + return self.extra_fields.get("mission") + + @mission.setter + def mission(self, value: str | None) -> None: + self.extra_fields["mission"] = value + + @property + def gsd(self) -> int | None: + """ + Ground Sample Distance at the sensor, in meters (m), + must be greater than 0. + """ + return self.extra_fields.get("gsd") + + @gsd.setter + def gsd(self, value: int | None) -> None: + self.extra_fields["gsd"] = value + + +class DataType(StrEnum): + int8 = "int8" + int16 = "int16" + int32 = "int32" + int64 = "int64" + uint8 = "uint8" + uint16 = "uint16" + uint32 = "uint32" + uint64 = "uint64" + float16 = "float16" + float32 = "float32" + float64 = "float64" + cint16 = "cint16" + cint32 = "cint32" + cfloat32 = "cfloat32" + cfloat64 = "cfloat64" + other = "other" + + @override + def __repr__(self): + return self.value + + +@dataclass +class Statistics: + statistics: dict[str, Any] + + @property + def minimum(self) -> float | None: + return self.statistics.get("minimum") + + @minimum.setter + def minimum(self, value: float | None) -> None: + self.statistics["minimum"] = value + + @property + def maximum(self) -> float | None: + return self.statistics.get("maximum") + + @maximum.setter + def maximum(self, value: float | None) -> None: + self.statistics["maximum"] = value + + @property + def mean(self) -> float | None: + return self.statistics.get("mean") + + @mean.setter + def mean(self, value: float | None) -> None: + self.statistics["mean"] = value + + @property + def stddev(self) -> float | None: + return self.statistics.get("stddev") + + @stddev.setter + def stddev(self, value: float | None) -> None: + self.statistics["stddev"] = value + + @property + def count(self) -> int | None: + return self.statistics.get("count") + + @count.setter + def count(self, value: int | None) -> None: + self.statistics["count"] = value + + @property + def valid_percent(self) -> float | None: + return self.statistics.get("valid_percent") + + @valid_percent.setter + def valid_percent(self, value: float | None) -> None: + self.statistics["valid_percent"] = value + + def to_dict(self) -> dict[str, Any]: + return {k: v for k, v in self.statistics.items() if v is not None} + + +class DataValue(Protocol): + extra_fields: dict[str, Any] + + @property + def nodata(self) -> float | str | None: + """ + The no-data value must be provided either as: + + - a number + - a string: + nan - NaN (not a number) as defined in IEEE-754 + inf - Positive Infinity + -inf - Negative Infinity + """ + return self.extra_fields.get("nodata") + + @nodata.setter + def nodata(self, value: float | str | None) -> None: + self.extra_fields["nodata"] = value + + @property + def unit(self) -> str | None: + """ + It is STRONGLY RECOMMENDED to provide units in one of the following two formats: + + UCUM: The unit code that is compliant to the UCUM specification. + UDUNITS-2: The unit symbol if available, otherwise the singular unit name. + """ + return self.extra_fields.get("unit") + + @unit.setter + def unit(self, value: str | None) -> None: + self.extra_fields["unit"] = value + + @property + def data_type(self) -> DataType | str | None: + return self.extra_fields.get("data_type") + + @data_type.setter + def data_type(self, value: DataType | str | None) -> None: + self.extra_fields["data_type"] = DataType(value) + + @property + def statistics(self) -> Statistics: + if not self.extra_fields.get("statistics"): + statistics: dict[str, Any] = {} + self.extra_fields["statistics"] = statistics + return Statistics(self.extra_fields["statistics"]) + + +class CommonMetadata(Basics, DateTime, Licensing, Providers, Instrument, DataValue): + object: Asset | Item + """The object from which common metadata is obtained.""" + + def __init__(self, /, object: Asset | Item): + self.object = object + self.extra_fields.update(DateTime.from_str(self.extra_fields)) + + @property + @override + def extra_fields(self) -> dict[str, Any]: # pyright: ignore[reportIncompatibleVariableOverride] + from pystac import Item + + if isinstance(self.object, Item): + return self.object.properties.extra_fields + return self.object.extra_fields + + @override + def __repr__(self): + fields: str = ", ".join( + f"{k}={v}" for k, v in self.extra_fields.items() if v is not None + ) + if len(fields) > 100: + fields = f"{fields[:50]}...{fields[-50:]}" + return f"" diff --git a/src/pystac/constants.py b/src/pystac/constants.py new file mode 100644 index 000000000..caad78e6c --- /dev/null +++ b/src/pystac/constants.py @@ -0,0 +1,5 @@ +from typing import Literal + +STAC_OBJECT_TYPE = Literal["Catalog", "Collection", "Feature"] +DEFAULT_LICENSE = "other" +DEFAULT_STAC_VERSION = "1.1.0" diff --git a/src/pystac/container.py b/src/pystac/container.py new file mode 100644 index 000000000..674643a33 --- /dev/null +++ b/src/pystac/container.py @@ -0,0 +1,518 @@ +from __future__ import annotations + +import copy +import os.path +import warnings +from abc import ABC +from collections.abc import Callable, Iterable, Iterator +from typing import TYPE_CHECKING, Any + +from typing_extensions import deprecated + +from pystac.errors import STACError +from pystac.utils import make_absolute_href, make_relative_href + +from .asset import Asset +from .layout import LayoutTemplate +from .link import Link +from .rel_type import RelType +from .stac_object import STACObject + +if TYPE_CHECKING: + from .catalog import CatalogType # pyright: ignore[reportDeprecated] + from .collection import Collection + from .href_generator import HrefGenerator + from .item import Item + from .stac_io import StacIO + from .writer import Writer + + +class Container(STACObject, ABC): + def get_item(self, id: str, recursive: bool = False) -> Item | None: + return next(self.get_items(id, recursive=recursive), None) + + def get_items(self, *ids: str, recursive: bool = False) -> Iterator[Item]: + for link in self.links: + if link.is_item(): + from .item import Item + + stac_object = link.get_target(start_href=self._href, reader=self.reader) + if isinstance(stac_object, Item) and (not ids or stac_object.id in ids): + yield stac_object + elif recursive and link.is_child(): + stac_object = link.get_target(start_href=self._href, reader=self.reader) + if isinstance(stac_object, Container): + yield from stac_object.get_items(*ids, recursive=True) + + def get_item_links(self) -> list[Link]: + return [link for link in self.links if link.is_item()] + + def add_item( + self, item: Item, set_parent: bool = True, title: str | None = None + ) -> Link: + from .item import Item + + if not isinstance(item, Item): # pyright: ignore[reportUnnecessaryIsInstance] + raise STACError("Cannot add a non-item as an item") # pyright: ignore[reportUnreachable] + + item_link = Link(target=item, rel=RelType.ITEM, title=title) + self.links.append(item_link) + + if set_parent: + parent_link = Link(target=self, rel=RelType.PARENT) + item.remove_links(RelType.PARENT) + item.links.append(parent_link) + + return item_link + + def add_items(self, items: Iterable[Item]) -> list[Link]: + links: list[Link] = [] + for item in items: + links.append(self.add_item(item)) + return links + + def map_items(self, item_mapper: Callable[[Item], Item | list[Item]]) -> Container: + from .item import Item + + def apply(container: Container) -> None: + links: list[Link] = [] + for link in container.links: + if not link.is_item(): + links.append(link) + continue + + item = link.get_target(container.get_self_href(), container.reader) + if not isinstance(item, Item): + warnings.warn("Item link does not point to an item") + links.append(link) + continue + + item_or_items = item_mapper(item) + if isinstance(item_or_items, list): + for new_item in item_or_items: + new_link = copy.deepcopy(link) + new_link.set_target(new_item) + links.append(new_link) + else: + link.set_target(item_or_items) + links.append(link) + container.links = links + + container = self.clone() + apply(container) + for child in container.get_children(recursive=True): + apply(child) + + return container + + def map_assets( + self, + asset_mapper: Callable[ + [str, Asset], Asset | tuple[str, Asset] | dict[str, Asset] + ], + ) -> Container: + def apply(item: Item) -> Item: + assets: dict[str, Asset] = dict() + for key, asset in item.assets.items(): + value = asset_mapper(key, asset) + if isinstance(value, Asset): + assets[key] = value + elif isinstance(value, tuple): + assets[value[0]] = value[1] + else: + assets.update(value) + item.assets = assets + return item + + return self.map_items(apply) + + def remove_item(self, id: str) -> Item | None: + from .item import Item + + removed_item = None + index = None + for i, link in enumerate(self.links): + if link.is_item(): + stac_object = link.get_target( + start_href=self.get_self_href(), reader=self.reader + ) + if isinstance(stac_object, Item) and stac_object.id == id: + removed_item = stac_object + index = i + break + + if index is not None: + _ = self.links.pop(index) + + return removed_item + + def clear_items(self) -> None: + self.links: list[Link] = [link for link in self.links if not link.is_item()] + + def get_collections( + self, *ids: str, recursive: bool = False, sort_links_by_id: bool = True + ) -> Iterator[Collection]: + from .collection import Collection + + for child in self.get_children( + *ids, recursive=recursive, sort_links_by_id=sort_links_by_id + ): + if isinstance(child, Collection) and (not ids or child.id in ids): + yield child + + @deprecated( + "Get all collections is deprecated, use `get_collections(recursive=True)`" + ) + def get_all_collections(self) -> Iterator[Collection]: + yield from self.get_collections(recursive=True) + + def get_child( + self, id: str, recursive: bool = False, sort_links_by_id: bool = True + ) -> Container | None: + return next( + self.get_children( + id, recursive=recursive, sort_links_by_id=sort_links_by_id + ), + None, + ) + + def get_children( + self, *ids: str, recursive: bool = False, sort_links_by_id: bool = True + ) -> Iterator[Container]: + links = self.get_child_links() + if ids and sort_links_by_id: + links = sorted( + links, + key=lambda x: (href := x.get_href()) is None + or all(id not in href for id in ids), + ) + + for link in links: + stac_object = link.get_target(start_href=self._href, reader=self.reader) + + if isinstance(stac_object, Container): + if not ids or stac_object.id in ids: + yield stac_object + if recursive: + yield from stac_object.get_children( + *ids, recursive=recursive, sort_links_by_id=sort_links_by_id + ) + + def get_child_links(self) -> list[Link]: + return [link for link in self.links if link.is_child()] + + def add_child(self, child: Container, set_parent: bool = True) -> Link: + if not isinstance(child, Container): # pyright: ignore[reportUnnecessaryIsInstance] + raise STACError("Only catalogs or collections can be children") # pyright: ignore[reportUnreachable] + + child_link = Link(target=child, rel=RelType.CHILD) + self.links.append(child_link) + + if set_parent: + parent_link = Link(target=self, rel=RelType.PARENT) + child.remove_links(RelType.PARENT) + child.links.append(parent_link) + + if root := self.get_root(): + child.set_root(root) + else: + child.set_root(self) + + return child_link + + def add_children(self, children: list[Container]) -> list[Link]: + links: list[Link] = [] + for child in children: + links.append(self.add_child(child)) + return links + + def remove_child(self, id: str) -> Container | None: + removed_child = None + index = None + for i, link in enumerate(self.links): + if link.is_child(): + stac_object = link.get_target( + start_href=self.get_self_href(), reader=self.reader + ) + if isinstance(stac_object, Container) and stac_object.id == id: + removed_child = stac_object + index = i + break + + if index is not None: + _ = self.links.pop(index) + assert removed_child + removed_child.remove_links(RelType.PARENT) + _ = removed_child.remove_root() + + return removed_child + + def clear_children(self) -> None: + links: list[Link] = [] + for link in self.links: + if link.is_child(): + child = link.get_target(self.get_self_href(), self.reader) + child.remove_links(RelType.PARENT) + _ = child.remove_root() + else: + links.append(link) + self.links = links + + @deprecated("Call .normalize_hrefs() and then .save_all()") + def normalize_and_save( + self, + root_href: str, + catalog_type: CatalogType | None = None, # pyright: ignore[reportDeprecated] + skip_unresolved: bool = False, + ) -> None: + from .catalog import CatalogType # pyright: ignore[reportDeprecated] + + self.normalize_hrefs( + root_href, + use_absolute_links=catalog_type == CatalogType.ABSOLUTE_PUBLISHED, # pyright: ignore[reportDeprecated] + skip_unresolved=skip_unresolved, + ) + self.save_all() + + @deprecated("Use .save_all()") + def save( + self, + catalog_type: CatalogType | None = None, # pyright: ignore[reportDeprecated] + dest_href: str | None = None, + stac_io: StacIO | None = None, + ) -> None: + from .catalog import CatalogType # pyright: ignore[reportDeprecated] + + if catalog_type: + # In PySTAC v1, Link.to_dict() implicitly converted hrefs. In PySTAC + # v2, we have to explicitly convert the hrefs before saving. + self.render_all( + use_absolute_links=catalog_type == CatalogType.ABSOLUTE_PUBLISHED # pyright: ignore[reportDeprecated] + ) + + include_self_links = catalog_type not in ( + CatalogType.RELATIVE_PUBLISHED, # pyright: ignore[reportDeprecated] + CatalogType.SELF_CONTAINED, # pyright: ignore[reportDeprecated] + ) + else: + include_self_links = True + + if stac_io: + from .stac_io import StacIOWriter + + writer = StacIOWriter(stac_io) + else: + writer = None + + self.save_all( + dest_href=dest_href, writer=writer, include_self_links=include_self_links + ) + + def generate_subcatalogs( + self, + template: str, + defaults: dict[str, Any] | None = None, + parent_ids: list[str] | None = None, + ) -> list[Container]: + from .catalog import Catalog + + result: list[Container] = [] + parent_ids = parent_ids or list() + parent_ids.append(self.id) + for child in self.get_children(): + result.extend( + child.generate_subcatalogs( + template, defaults=defaults, parent_ids=parent_ids.copy() + ) + ) + + layout_template = LayoutTemplate(template, defaults=defaults) + keep_item_links: list[Link] = [] + for link in self.get_item_links(): + item = link.get_target(self.get_self_href(), self.reader) + subcatalog_ids = layout_template.substitute(item).split("/") + id_iter = reversed(parent_ids) + if all([f"{id}" == next(id_iter, None) for id in reversed(subcatalog_ids)]): + # Skip items for which the sub-catalog structure already + # matches the template. The list of parent IDs can include more + # elements on the root side, so compare the reversed sequences. + keep_item_links.append(link) + continue + current_parent = self + for subcatalog_id in subcatalog_ids: + subcatalog = current_parent.get_child(subcatalog_id) + if subcatalog is None: + subcatalog_description = ( + f"Catalog of items from {current_parent.id} with " + f"id {subcatalog_id}" + ) + subcatalog = Catalog( + id=subcatalog_id, description=subcatalog_description + ) + _ = current_parent.add_child(subcatalog) + result.append(subcatalog) + current_parent = subcatalog + + # resolve collection link so when added back points to correct location + collection_link = item.get_link(RelType.COLLECTION) + if collection_link is not None: + _ = collection_link.get_target(item.get_self_href(), self.reader) + + _ = current_parent.add_item(item) + + # keep only non-item links and item links that have not been moved elsewhere + self.links = [ + link for link in self.links if link.rel != RelType.ITEM + ] + keep_item_links + + return result + + def normalize_hrefs( + self, + root_href: str, + use_absolute_links: bool = False, + skip_unresolved: bool = False, + ) -> None: + from .href_generator import BestPracticesHrefGenerator + + href_generator = BestPracticesHrefGenerator() + previous_self_href = self.get_self_href() + self.set_self_href(href_generator.get_root(make_absolute_href(root_href), self)) + self.render_all( + use_absolute_links=use_absolute_links, + href_generator=href_generator, + previous_self_href=previous_self_href, + skip_unresolved=skip_unresolved, + ) + + def render_all( + self, + use_absolute_links: bool = False, + href_generator: HrefGenerator | None = None, + previous_self_href: str | None = None, + skip_unresolved: bool = False, + ) -> None: + for _ in self.render( + use_absolute_links, href_generator, previous_self_href, skip_unresolved + ): + pass + + def save_all( + self, + dest_href: str | None = None, + writer: Writer | None = None, + include_self_links: bool = True, + ) -> None: + for _ in self.save_iter( + dest_href=dest_href, writer=writer, include_self_links=include_self_links + ): + pass + + def save_iter( + self, + dest_href: str | None = None, + writer: Writer | None = None, + include_self_links: bool = True, + ) -> Iterator[STACObject]: + self_href = self.get_self_href() + if dest_href is not None: + if self_href: + self_dest_href = make_absolute_href( + make_relative_href(self_href, self_href), + dest_href, + start_is_dir=True, + ) + else: + raise ValueError("Cannot use dest_href without a self href") + + self.save_object( + dest_href=self_dest_href, + writer=writer, + include_self_link=include_self_links, + ) + else: + self.save_object( + dest_href=None, writer=writer, include_self_link=include_self_links + ) + + yield self + + for child_link in self.get_child_links(): + child = child_link.maybe_get_target() + if not child or not isinstance(child, Container): + continue + + if dest_href is not None: + if (child_self_href := child.get_self_href()) and self_href: + child_dest_href = make_absolute_href( + make_relative_href(child_self_href, self_href), + dest_href, + start_is_dir=True, + ) + else: + raise ValueError( + "Cannot use dest_href unless both self and child have self " + "hrefs" + ) + yield from child.save_iter( + dest_href=os.path.dirname(child_dest_href), + writer=writer, + include_self_links=include_self_links, + ) + else: + yield from child.save_iter( + dest_href=None, writer=writer, include_self_links=include_self_links + ) + + for item_link in self.get_item_links(): + item = item_link.maybe_get_target() + if not item: + continue + + if dest_href is not None: + if (item_self_href := item.get_self_href()) and self_href: + item_dest_href = make_absolute_href( + make_relative_href(item_self_href, self_href), + dest_href, + start_is_dir=True, + ) + else: + raise ValueError( + "Cannot use dest_href unless both self and item have self hrefs" + ) + item.save_object( + dest_href=item_dest_href, + writer=writer, + include_self_link=include_self_links, + ) + else: + item.save_object( + dest_href=None, writer=writer, include_self_link=include_self_links + ) + yield item + + def walk(self) -> Iterator[tuple[Container, list[Container], list[Item]]]: + from .item import Item + + self_href = self.get_self_href() + children: list[Container] = [] + items: list[Item] = [] + for link in self.links: + if link.is_child() or link.is_item(): + stac_object = link.get_target(self_href, self.reader) + if isinstance(stac_object, Container): + children.append(stac_object) + elif isinstance(stac_object, Item): + items.append(stac_object) + + yield (self, children, items) + + for child in children: + yield from child.walk() + + def target_in_hierarchy(self, target: STACObject) -> bool: + for root, _, items in self.walk(): + if root == target or any(item == target for item in items): + return True + + return False diff --git a/src/pystac/deserialize.py b/src/pystac/deserialize.py new file mode 100644 index 000000000..ba9f6adc1 --- /dev/null +++ b/src/pystac/deserialize.py @@ -0,0 +1,21 @@ +from typing import Any + +from .stac_object import STACObject + + +def from_dict(data: dict[str, Any]) -> STACObject: + match data.get("type"): + case "Catalog": + from .catalog import Catalog + + return Catalog.from_dict(data) + case "Collection": + from .collection import Collection + + return Collection.from_dict(data) + case "Feature": + from .item import Item + + return Item.from_dict(data) + case _: + raise ValueError(f"Unknown STAC type: {data['type']}") diff --git a/pystac/errors.py b/src/pystac/errors.py similarity index 100% rename from pystac/errors.py rename to src/pystac/errors.py diff --git a/pystac/extensions/__init__.py b/src/pystac/extensions/__init__.py similarity index 100% rename from pystac/extensions/__init__.py rename to src/pystac/extensions/__init__.py diff --git a/pystac/extensions/base.py b/src/pystac/extensions/base.py similarity index 99% rename from pystac/extensions/base.py rename to src/pystac/extensions/base.py index 356851ecb..7abd404fa 100644 --- a/pystac/extensions/base.py +++ b/src/pystac/extensions/base.py @@ -37,9 +37,9 @@ def _set_summary( v: list[Any] | pystac.RangeSummary[Any] | dict[str, Any] | None, ) -> None: if v is None: - self.summaries.remove(prop_key) + del self.summaries[prop_key] else: - self.summaries.add(prop_key, v) + self.summaries[prop_key] = v P = TypeVar("P") diff --git a/pystac/extensions/classification.py b/src/pystac/extensions/classification.py similarity index 100% rename from pystac/extensions/classification.py rename to src/pystac/extensions/classification.py diff --git a/pystac/extensions/datacube.py b/src/pystac/extensions/datacube.py similarity index 100% rename from pystac/extensions/datacube.py rename to src/pystac/extensions/datacube.py diff --git a/pystac/extensions/eo.py b/src/pystac/extensions/eo.py similarity index 100% rename from pystac/extensions/eo.py rename to src/pystac/extensions/eo.py diff --git a/pystac/extensions/ext.py b/src/pystac/extensions/ext.py similarity index 100% rename from pystac/extensions/ext.py rename to src/pystac/extensions/ext.py diff --git a/pystac/extensions/file.py b/src/pystac/extensions/file.py similarity index 100% rename from pystac/extensions/file.py rename to src/pystac/extensions/file.py diff --git a/pystac/extensions/grid.py b/src/pystac/extensions/grid.py similarity index 100% rename from pystac/extensions/grid.py rename to src/pystac/extensions/grid.py diff --git a/pystac/extensions/hooks.py b/src/pystac/extensions/hooks.py similarity index 100% rename from pystac/extensions/hooks.py rename to src/pystac/extensions/hooks.py diff --git a/pystac/extensions/item_assets.py b/src/pystac/extensions/item_assets.py similarity index 100% rename from pystac/extensions/item_assets.py rename to src/pystac/extensions/item_assets.py diff --git a/pystac/extensions/label.py b/src/pystac/extensions/label.py similarity index 100% rename from pystac/extensions/label.py rename to src/pystac/extensions/label.py diff --git a/pystac/extensions/mgrs.py b/src/pystac/extensions/mgrs.py similarity index 100% rename from pystac/extensions/mgrs.py rename to src/pystac/extensions/mgrs.py diff --git a/pystac/extensions/mlm.py b/src/pystac/extensions/mlm.py similarity index 100% rename from pystac/extensions/mlm.py rename to src/pystac/extensions/mlm.py diff --git a/pystac/extensions/pointcloud.py b/src/pystac/extensions/pointcloud.py similarity index 100% rename from pystac/extensions/pointcloud.py rename to src/pystac/extensions/pointcloud.py diff --git a/pystac/extensions/projection.py b/src/pystac/extensions/projection.py similarity index 100% rename from pystac/extensions/projection.py rename to src/pystac/extensions/projection.py diff --git a/pystac/extensions/raster.py b/src/pystac/extensions/raster.py similarity index 99% rename from pystac/extensions/raster.py rename to src/pystac/extensions/raster.py index cbd9f5bd1..7160fd862 100644 --- a/pystac/extensions/raster.py +++ b/src/pystac/extensions/raster.py @@ -786,9 +786,7 @@ def __init__(self, item_asset: pystac.ItemAssetDefinition): self.asset_definition = item_asset def __repr__(self) -> str: - return "".format( - self.asset_definition - ) + return f"" class SummariesRasterExtension(SummariesExtension): diff --git a/pystac/extensions/render.py b/src/pystac/extensions/render.py similarity index 100% rename from pystac/extensions/render.py rename to src/pystac/extensions/render.py diff --git a/pystac/extensions/sar.py b/src/pystac/extensions/sar.py similarity index 100% rename from pystac/extensions/sar.py rename to src/pystac/extensions/sar.py diff --git a/pystac/extensions/sat.py b/src/pystac/extensions/sat.py similarity index 100% rename from pystac/extensions/sat.py rename to src/pystac/extensions/sat.py diff --git a/pystac/extensions/scientific.py b/src/pystac/extensions/scientific.py similarity index 99% rename from pystac/extensions/scientific.py rename to src/pystac/extensions/scientific.py index 9f6316b70..9029b3d9c 100644 --- a/pystac/extensions/scientific.py +++ b/src/pystac/extensions/scientific.py @@ -286,9 +286,7 @@ def __init__(self, collection: pystac.Collection): super().__init__(self.collection) def __repr__(self) -> str: - return "".format( - self.collection.id - ) + return f"" class ItemScientificExtension(ScientificExtension[pystac.Item]): diff --git a/pystac/extensions/storage.py b/src/pystac/extensions/storage.py similarity index 100% rename from pystac/extensions/storage.py rename to src/pystac/extensions/storage.py diff --git a/pystac/extensions/table.py b/src/pystac/extensions/table.py similarity index 100% rename from pystac/extensions/table.py rename to src/pystac/extensions/table.py diff --git a/pystac/extensions/timestamps.py b/src/pystac/extensions/timestamps.py similarity index 100% rename from pystac/extensions/timestamps.py rename to src/pystac/extensions/timestamps.py diff --git a/pystac/extensions/version.py b/src/pystac/extensions/version.py similarity index 100% rename from pystac/extensions/version.py rename to src/pystac/extensions/version.py diff --git a/pystac/extensions/view.py b/src/pystac/extensions/view.py similarity index 100% rename from pystac/extensions/view.py rename to src/pystac/extensions/view.py diff --git a/pystac/extensions/xarray_assets.py b/src/pystac/extensions/xarray_assets.py similarity index 100% rename from pystac/extensions/xarray_assets.py rename to src/pystac/extensions/xarray_assets.py diff --git a/src/pystac/geo_interface.py b/src/pystac/geo_interface.py new file mode 100644 index 000000000..9a02d1af0 --- /dev/null +++ b/src/pystac/geo_interface.py @@ -0,0 +1,7 @@ +from typing import Any, Protocol, runtime_checkable + + +@runtime_checkable +class GeoInterface(Protocol): + @property + def __geo_interface__(self) -> dict[str, Any]: ... diff --git a/src/pystac/href_generator.py b/src/pystac/href_generator.py new file mode 100644 index 000000000..f9d573751 --- /dev/null +++ b/src/pystac/href_generator.py @@ -0,0 +1,44 @@ +from typing import Protocol + +from .container import Container +from .item import Item +from .utils import make_absolute_href + + +class HrefGenerator(Protocol): + def get_root(self, prefix: str, container: Container) -> str: ... + def get_child(self, parent_href: str, container: Container) -> str: ... + def get_item(self, parent_href: str, item: Item) -> str: ... + + +class BestPracticesHrefGenerator: + def get_root(self, prefix: str, container: Container) -> str: + from .catalog import Catalog + from .collection import Collection + + if isinstance(container, Catalog): + return make_absolute_href("catalog.json", prefix, start_is_dir=True) + elif isinstance(container, Collection): + return make_absolute_href("collection.json", prefix, start_is_dir=True) + else: + raise ValueError(f"Unsupported root type: {type(container)}") + + def get_child(self, parent_href: str, container: Container) -> str: + from .catalog import Catalog + from .collection import Collection + + if isinstance(container, Catalog): + file_name = "catalog.json" + elif isinstance(container, Collection): + file_name = "collection.json" + else: + raise ValueError(f"Unsupported child type: {type(container)}") + + return make_absolute_href( + "/".join((container.id, file_name)), parent_href, start_is_dir=False + ) + + def get_item(self, parent_href: str, item: Item) -> str: + return make_absolute_href( + "/".join((item.id, f"{item.id}.json")), parent_href, start_is_dir=False + ) diff --git a/pystac/html/JSON.jinja2 b/src/pystac/html/JSON.jinja2 similarity index 100% rename from pystac/html/JSON.jinja2 rename to src/pystac/html/JSON.jinja2 diff --git a/pystac/html/Macros.jinja2 b/src/pystac/html/Macros.jinja2 similarity index 100% rename from pystac/html/Macros.jinja2 rename to src/pystac/html/Macros.jinja2 diff --git a/pystac/html/__init__.py b/src/pystac/html/__init__.py similarity index 100% rename from pystac/html/__init__.py rename to src/pystac/html/__init__.py diff --git a/pystac/html/jinja_env.py b/src/pystac/html/jinja_env.py similarity index 100% rename from pystac/html/jinja_env.py rename to src/pystac/html/jinja_env.py diff --git a/src/pystac/io.py b/src/pystac/io.py new file mode 100644 index 000000000..9c35382b7 --- /dev/null +++ b/src/pystac/io.py @@ -0,0 +1,12 @@ +from pathlib import Path + +from .deserialize import from_dict +from .reader import DEFAULT_READER, Reader +from .stac_object import STACObject + + +def read_file(href: str | Path, reader: Reader = DEFAULT_READER) -> STACObject: + data = reader.get_json(href) + obj = from_dict(data) + obj.set_self_href(str(href)) + return obj diff --git a/src/pystac/item.py b/src/pystac/item.py new file mode 100644 index 000000000..bf46f678d --- /dev/null +++ b/src/pystac/item.py @@ -0,0 +1,335 @@ +from __future__ import annotations + +import copy +import datetime as dt +import warnings +from collections.abc import Iterator, MutableMapping +from typing import TYPE_CHECKING, Any, ClassVar, final, override + +from typing_extensions import deprecated + +from pystac.rel_type import RelType + +from .asset import Asset, Assets +from .band import Band +from .common_metadata import ( + Basics, + CommonMetadata, + DateTime, + Instrument, + Licensing, + Providers, +) +from .constants import DEFAULT_STAC_VERSION, STAC_OBJECT_TYPE +from .container import Container +from .geo_interface import GeoInterface +from .link import Link +from .stac_object import STACObject +from .utils import make_posix_style + +if TYPE_CHECKING: + from .collection import Collection + from .extensions.ext import ItemExt + + +class Item(STACObject, Assets): + """The STAC Item object is the most important object in a STAC system. + + An Item is the entity that contains metadata for a scene and links to the + assets. Item objects are the leaf nodes for a graph of Catalog and + Collection objects. + """ + + type: ClassVar[STAC_OBJECT_TYPE] = "Feature" + + def __init__( + self, + id: str, + geometry: dict[str, Any] | GeoInterface | None = None, + bbox: list[float | int] | None = None, + datetime: dt.datetime | str | None = None, + properties: dict[str, Any] | Properties | None = None, + type: str = "Feature", + stac_version: str = DEFAULT_STAC_VERSION, + stac_extensions: list[str] | None = None, + links: list[Link] | list[dict[str, Any]] | None = None, + assets: dict[str, Asset] | dict[str, dict[str, Any]] | None = None, + collection: str | Collection | None = None, + **kwargs: Any, + ): + super().__init__( + type=type, + id=id, + stac_version=stac_version, + stac_extensions=stac_extensions, + links=links, + **kwargs, + ) + + if isinstance(geometry, GeoInterface): + self.geometry: dict[str, Any] | None = copy.deepcopy( + geometry.__geo_interface__ + ) + else: + self.geometry = geometry + + # TODO add validation + self.bbox: list[float | int] | None = list(bbox) if bbox else None + + self.properties: Properties = Properties.try_from(properties) + if datetime: + self.properties.datetime = datetime + + if assets: + self.assets: dict[str, Asset] = {} + for key, value in assets.items(): + asset = Asset.try_from(value) + asset.set_owner(self) + self.assets[key] = asset + else: + self.assets = {} + + self._collection: Collection | str | None = collection + + @classmethod + def try_from(cls, data: dict[str, Any] | Item) -> Item: + if isinstance(data, Item): + return data + else: + return cls(**data) + + @override + @classmethod + def from_dict( + cls, + data: dict[str, Any], + preserve_dict: bool = True, + migrate: bool | None = None, + root: Container | None = None, + ) -> Item: + if preserve_dict: + data = copy.deepcopy(data) + item = cls(**data) + + # TODO deprecate migrate + if migrate: + raise NotImplementedError + if root: + warnings.warn( + "The root parameter is deprecated, call `set_root` directly after " + "creating the item", + DeprecationWarning, + ) + item.set_root(root) + return item + + @property + def datetime(self) -> dt.datetime | None: + return self.properties.datetime + + @deprecated("Get the datetime from the asset directly") + def get_datetime(self, asset: Asset | None = None) -> dt.datetime | None: + if asset and (datetime := asset.datetime): + return datetime + else: + return self.properties.datetime + + @deprecated("Set the datetime on the asset directly") + def set_datetime(self, datetime: dt.datetime, asset: Asset | None = None) -> None: + if asset: + asset.datetime = datetime + else: + self.properties.datetime = datetime + + @override + def set_self_href(self, href: str | None) -> None: + new_href = None if href is None else make_posix_style(href) + Asset.update_hrefs( + self.assets, + self.get_self_href(), + new_href, + ) + return super().set_self_href(new_href) + + @property + @deprecated("Access common metadata fields directly on properties") + def common_metadata(self): + return CommonMetadata(self) + + def get_collection(self) -> Collection | None: + from .collection import Collection + + if isinstance(self._collection, Collection): + return self._collection + elif self._collection is None and ( + collection_link := self.get_link(RelType.COLLECTION) + ): + # TODO can we just make get_target just take a STACObject? + stac_object = collection_link.get_target(self.get_self_href(), self.reader) + if isinstance(stac_object, Collection): + if root := self.get_root(): + stac_object.set_root(root) + self._collection = stac_object + return stac_object + else: + return None + else: + return None + + @property + @deprecated("Use get_collection") + def collection(self) -> Collection | None: + return self.get_collection() + + @property + def collection_id(self) -> str | None: + from .collection import Collection + + if isinstance(self._collection, Collection): + return self._collection.id + elif isinstance(self._collection, str): + return self._collection + else: + return None + + def set_collection(self, collection: Collection | str | None) -> None: + self._collection = collection + self.remove_links("collection") + + @override + def to_dict( + self, + include_self_link: bool = True, + transform_hrefs: bool | None = None, + order_keys: bool = True, + ) -> dict[str, Any]: + data = super().to_dict( + include_self_link=include_self_link, transform_hrefs=transform_hrefs + ) + data.update( + { + "geometry": copy.deepcopy(self.geometry), + "properties": self.properties.to_dict(), + "assets": {key: asset.to_dict() for key, asset in self.assets.items()}, + } + ) + if self.bbox: + data["bbox"] = self.bbox + if self.collection_id: + data["collection"] = self.collection_id + if order_keys: + ordered_data: dict[str, Any] = {} + for key in ( + "type", + "stac_version", + "stac_extensions", + "id", + "geometry", + "bbox", + "properties", + "links", + "assets", + "collection", + ): + if key in data: + ordered_data[key] = data.pop(key) + ordered_data.update(data) + return ordered_data + else: + return data + + @property + def __geo_interface__(self) -> dict[str, Any]: + return self.to_dict(include_self_link=False) + + @property + def ext(self) -> ItemExt: + """Accessor for extension classes on this item + + Example:: + + item.ext.proj.code = "EPSG:4326" + """ + from pystac.extensions.ext import ItemExt + + return ItemExt(stac_object=self) + + +@final +class Properties( + MutableMapping[str, Any], Basics, DateTime, Licensing, Providers, Instrument +): + def __init__( + self, + *, + bands: list[Band] | list[dict[str, Any]] | None = None, + **kwargs: Any, + ): + self.extra_fields: dict[str, Any] = kwargs + self.extra_fields.update(DateTime.from_str(kwargs)) + + if not any([self.datetime, self.start_datetime, self.end_datetime]): + self.datetime = dt.datetime.now(tz=dt.UTC) + + if bands is not None: + self.bands: list[Band] | None = [Band.try_from(band) for band in bands] + else: + self.bands = None + + @override + def __getitem__(self, key: str) -> Any: + return self.extra_fields[key] + + @override + def __setitem__(self, name: str, value: Any, /) -> None: + self.extra_fields[name] = value + + @override + def __delitem__(self, key: str) -> None: + del self.extra_fields[key] + + @override + def __iter__(self) -> Iterator[str]: + if self.bands is not None: + return iter({**self.extra_fields, "bands": self.bands}) + return iter(self.extra_fields) + + @override + def __len__(self) -> int: + if self.bands is not None: + return len(self.extra_fields) + 1 + return len(self.extra_fields) + + @override + def __contains__(self, key: object) -> bool: + return key == "bands" or key in self.extra_fields + + def __deepcopy__(self, memo: dict[int, Any]) -> Properties: + return Properties( + bands=self.bands, + **copy.deepcopy(self.extra_fields, memo), + ) + + @classmethod + def try_from( + cls, + data: Properties | dict[str, Any] | None, + ) -> Properties: + if isinstance(data, Properties): + return data + elif not data: + return Properties() + else: + return Properties.from_dict(data) + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> Properties: + return cls(**data) + + def to_dict(self) -> dict[str, Any]: + data = copy.deepcopy(self.extra_fields) + data.update(DateTime.to_str(self.extra_fields)) + + if self.bands is not None: + data["bands"] = [band.to_dict() for band in self.bands] + return {k: v for k, v in data.items() if v is not None or k == "datetime"} diff --git a/pystac/item_assets.py b/src/pystac/item_assets.py similarity index 100% rename from pystac/item_assets.py rename to src/pystac/item_assets.py diff --git a/src/pystac/item_collection.py b/src/pystac/item_collection.py new file mode 100644 index 000000000..1ec2e4e8d --- /dev/null +++ b/src/pystac/item_collection.py @@ -0,0 +1,116 @@ +from __future__ import annotations + +import copy +import warnings +from collections.abc import Iterator +from pathlib import Path +from typing import Any, Literal, TypedDict, override + +from typing_extensions import deprecated + +from .container import Container +from .errors import STACTypeError +from .item import Item +from .reader import DEFAULT_READER, Reader +from .utils import make_absolute_href + + +class T_ItemCollection(TypedDict): + type: Literal["FeatureCollection"] + features: list[dict[str, Any]] + + +class ItemCollection: + def __init__(self, items: list[Item], root: Container | None = None, **kwargs: Any): + self.items: list[Item] = [Item.try_from(item) for item in items] + + if "root" in kwargs: + raise KeyError( + "root is not a valid key word argument to ``ItemCollection()``. " + "If root is required try instantiating the items directly and " + "providing root." + ) + + extra_fields = kwargs.pop("extra_fields", {}) + self.extra_fields: dict[str, Any] = kwargs + if extra_fields: + warnings.warn( + "Pass extra_fields entries as kwargs, " + "instead of in extra_fields dictionary." + ) + self.extra_fields.update(extra_fields) + + def __len__(self) -> int: + return len(self.items) + + def __getitem__(self, index: int) -> Item: + return self.items[index] + + def __iter__(self) -> Iterator[Item]: + return iter(self.items) + + def __contains__(self, __x: Item) -> bool: + return __x in self.items + + def __add__(self, other: Any) -> ItemCollection: + if not isinstance(other, ItemCollection): + return NotImplemented + + combined = [*self.items, *other.items] + return ItemCollection(items=combined) + + @override + def __repr__(self) -> str: + return f"ItemCollection({self.items})" + + def to_dict(self) -> dict[str, Any]: + data = copy.deepcopy(self.extra_fields) + return { + "type": "FeatureCollection", + "features": [item.to_dict() for item in self.items], + **data, + } + + def clone(self) -> ItemCollection: + return copy.deepcopy(self) + + @deprecated("Try `ItemCollection.from_dict` and handle any exceptions instead") + @staticmethod + def is_item_collection(data: dict[str, Any]) -> bool: + return data.get("type", "") == "FeatureCollection" + + @classmethod + def try_from(cls, data: dict[str, Any] | ItemCollection) -> ItemCollection: + if isinstance(data, ItemCollection): + return data + else: + return cls(**data) + + @classmethod + def from_dict( + cls, + data: dict[str, Any], + preserve_dict: bool = True, + root: Container | None = None, + ) -> ItemCollection: + if data.get("type", "") != "FeatureCollection": + raise STACTypeError(data, cls) + + if preserve_dict: + data = copy.deepcopy(data) + + items = data.get("features", []) + extra_fields = {k: v for k, v in data.items() if k not in ("features", "type")} + + return cls( + items=[Item.from_dict(item, root=root) for item in items], **extra_fields + ) + + @classmethod + def from_file( + cls, path: str | Path, reader: Reader = DEFAULT_READER + ) -> ItemCollection: + href = make_absolute_href(str(path)) + data = reader.get_json(href) + + return ItemCollection.from_dict(data) diff --git a/src/pystac/jsonschema.py b/src/pystac/jsonschema.py new file mode 100644 index 000000000..a2829b97d --- /dev/null +++ b/src/pystac/jsonschema.py @@ -0,0 +1,112 @@ +import importlib.resources +import json +import warnings +from collections.abc import Iterator +from typing import Any + +import referencing.retrieval +from jsonschema.exceptions import ValidationError +from jsonschema.protocols import Validator +from jsonschema.validators import Draft7Validator +from referencing import Registry + +from pystac.errors import STACValidationError + +from .constants import STAC_OBJECT_TYPE +from .reader import DEFAULT_READER +from .utils import get_stac_type + + +class JSONSchemaValidator: + def __init__(self) -> None: + self.registry: Registry = Registry(retrieve=cached_retrieve).with_contents( + registry_contents() + ) + self.cache: dict[str, Validator] = {} + + def get_validator(self, type: STAC_OBJECT_TYPE, version: str) -> Any: + stac_type = get_stac_type(type).lower() + path = f"stac/v{version}/{stac_type}.json" + if path not in self.cache: + try: + schema_data = read_schema(path) + except FileNotFoundError: + warnings.warn(f"Local schema not found for {stac_type} v{version}") + url = f"https://schemas.stacspec.org/v{version}/{stac_type}-spec/json-schema/{stac_type}.json" + schema_data = DEFAULT_READER.get_json(url) + self.cache[path] = Draft7Validator(schema_data, registry=self.registry) + return self.cache[path] + + def validate_core( + self, type: STAC_OBJECT_TYPE, version: str, data: dict[str, Any] + ) -> None: + validator = self.get_validator(type, version) + try: + validator.validate(data) + except ValidationError as e: + raise STACValidationError(str(e)) from e + + def validate_extension(self, extension: str, data: dict[str, Any]) -> None: + if extension not in self.cache: + schema_data = DEFAULT_READER.get_json(extension) + self.cache[extension] = Draft7Validator(schema_data, registry=self.registry) + validator = self.cache[extension] + try: + validator.validate(data) + except ValidationError as e: + raise STACValidationError(str(e)) from e + + +@referencing.retrieval.to_cached_resource() +def cached_retrieve(uri: str) -> str: + return json.dumps(DEFAULT_READER.get_json(uri)) + + +def registry_contents() -> Iterator[tuple[str, dict[str, Any]]]: + for name in ( + "bands", + "basics", + "common", + "data-values", + "item", + "datetime", + "instrument", + "licensing", + "provider", + ): + uri = f"https://schemas.stacspec.org/v1.1.0/item-spec/json-schema/{name}.json" + path = f"stac/v1.1.0/{name}.json" + yield uri, read_schema(path) + + for name in ( + "basics", + "datetime", + "instrument", + "item", + "licensing", + "provider", + ): + uri = f"https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/{name}.json" + path = f"stac/v1.0.0/{name}.json" + yield uri, read_schema(path) + + for name in ( + "Feature", + "Geometry", + ): + uri = f"https://geojson.org/schema/{name}.json" + path = f"geojson/{name}.json" + yield uri, read_schema(path) + + +def read_schema(path: str) -> dict[str, Any]: + with importlib.resources.files("pystac.schemas").joinpath(path).open("r") as f: + return json.load(f) + + +def set_default_json_schema_validator(validator: JSONSchemaValidator) -> None: + global DEFAULT_JSON_SCHEMA_VALIDATOR + DEFAULT_JSON_SCHEMA_VALIDATOR = validator # pyright: ignore[reportConstantRedefinition] + + +DEFAULT_JSON_SCHEMA_VALIDATOR = JSONSchemaValidator() diff --git a/pystac/layout.py b/src/pystac/layout.py similarity index 94% rename from pystac/layout.py rename to src/pystac/layout.py index cb380bb13..a5c7f59a0 100644 --- a/pystac/layout.py +++ b/src/pystac/layout.py @@ -6,14 +6,14 @@ from collections.abc import Callable from typing import TYPE_CHECKING, Any -import pystac -from pystac.utils import is_file_path +from .errors import STACError +from .utils import is_file_path if TYPE_CHECKING: - from pystac.catalog import Catalog - from pystac.collection import Collection - from pystac.item import Item - from pystac.stac_object import STACObject + from .catalog import Catalog + from .collection import Collection + from .item import Item + from .stac_object import STACObject class TemplateError(Exception): @@ -124,18 +124,19 @@ def __init__(self, template: str, defaults: dict[str, str] | None = None) -> Non self.template_vars = template_vars def _get_template_value(self, stac_object: STACObject, template_var: str) -> Any: + from .item import Item + if template_var in self.ITEM_TEMPLATE_VARS: - if isinstance(stac_object, pystac.Item): + if isinstance(stac_object, Item): # Datetime dt = stac_object.datetime if dt is None: dt = stac_object.common_metadata.start_datetime if dt is None: - raise pystac.TemplateError( - "Item {} does not have a datetime or " - "datetime range set; cannot template {} in {}".format( - stac_object, template_var, self.template - ) + raise TemplateError( + f"Item {stac_object} does not have a datetime or " + f"datetime range set; cannot template {template_var} " + f"in {self.template}" ) if template_var == "year": @@ -151,24 +152,22 @@ def _get_template_value(self, stac_object: STACObject, template_var: str) -> Any if template_var == "collection": if stac_object.collection_id is not None: return stac_object.collection_id - raise pystac.TemplateError( + raise TemplateError( f"Item {stac_object} does not have a collection ID set; " f"cannot template {template_var} in {self.template}" ) else: - raise pystac.TemplateError( - '"{}" cannot be used to template non-Item {} in {}'.format( - template_var, stac_object, self.template - ) + raise TemplateError( + f'"{template_var}" cannot be used to template non-Item "' + f"{stac_object} in {self.template}" ) # Allow dot-notation properties for arbitrary object values. props = template_var.split(".") - prop_source: pystac.STACObject | dict[str, Any] | None = None - error = pystac.TemplateError( - "Cannot find property {} on {} for template {}".format( - template_var, stac_object, self.template - ) + prop_source: STACObject | dict[str, Any] | None = None + error = TemplateError( + f"Cannot find property {template_var} on {stac_object} for template " + f"{self.template}" ) try: @@ -190,15 +189,13 @@ def _get_template_value(self, stac_object: STACObject, template_var: str) -> Any v: Any = prop_source for prop in template_var.split("."): - if isinstance(v, dict): - if prop not in v: - raise error + if prop in v: v = v[prop] - else: - if not hasattr(v, prop): - raise error + elif hasattr(v, prop): v = getattr(v, prop) - except pystac.TemplateError as e: + else: + raise error + except TemplateError as e: if template_var in self.defaults: return self.defaults[template_var] raise e @@ -221,7 +218,7 @@ def get_template_values(self, stac_object: STACObject) -> dict[str, Any]: stac object. Raises: - pystac.TemplateError: If a value for a template variable cannot be + TemplateError: If a value for a template variable cannot be derived from the stac object and there is no default, this error will be raised. """ @@ -267,14 +264,14 @@ def get_href( if is_file_path(parent_dir): parent_dir = os.path.dirname(parent_dir) - if isinstance(stac_object, pystac.Item): + if isinstance(stac_object, Item): return self.get_item_href(stac_object, parent_dir) - elif isinstance(stac_object, pystac.Collection): + elif isinstance(stac_object, Collection): return self.get_collection_href(stac_object, parent_dir, is_root) - elif isinstance(stac_object, pystac.Catalog): + elif isinstance(stac_object, Catalog): return self.get_catalog_href(stac_object, parent_dir, is_root) else: - raise pystac.STACError(f"Unknown STAC object type {stac_object}") + raise STACError(f"Unknown STAC object type {stac_object}") @abstractmethod def get_catalog_href(self, cat: Catalog, parent_dir: str, is_root: bool) -> str: diff --git a/src/pystac/link.py b/src/pystac/link.py new file mode 100644 index 000000000..19e21841a --- /dev/null +++ b/src/pystac/link.py @@ -0,0 +1,333 @@ +from __future__ import annotations + +import copy +import warnings +from typing import TYPE_CHECKING, Any, Self, override + +from typing_extensions import deprecated + +import pystac +from pystac.errors import STACError +from pystac.media_type import MediaType +from pystac.rel_type import RelType +from pystac.utils import is_absolute_href, make_absolute_href, make_posix_style + +from .reader import Reader + +if TYPE_CHECKING: + from . import Catalog, Collection, Item + from .extensions.ext import LinkExt + from .stac_object import STACObject + +HIERARCHICAL_LINKS = [ + RelType.SELF, + RelType.CHILD, + RelType.PARENT, + RelType.ROOT, + RelType.COLLECTION, +] + + +class Link: + def __init__( + self, + rel: str, + target: str | STACObject | None = None, + href: str | None = None, + media_type: str | None = None, + title: str | None = None, + method: str | None = None, + headers: dict[str, str | list[str]] | None = None, + body: Any | None = None, + **kwargs: Any, + ): + from .stac_object import STACObject + + self.rel: str = rel + self.media_type: str | None = media_type + self.title: str | None = title + self.method: str | None = method + self.headers: dict[str, str | list[str]] | None = headers + self.body: Any | None = body + extra_fields = kwargs.pop("extra_fields", None) + self.extra_fields: dict[str, Any] = kwargs + if extra_fields: + warnings.warn( + "Pass extra_fields entries as kwargs " + "instead of extra_fields as keyword argument." + ) + self.extra_fields.update(extra_fields) + + if isinstance(target, STACObject): + self._href: str | None = href or target.get_self_href() + self.title = title or getattr(target, "title", None) + self._target: STACObject | None = target + elif href and target: + raise ValueError("Both target and href were provided as strings") + elif not href and not target: + raise ValueError("Neither href nor target were provided") + else: + self._href = href or target + self._target = None + # backwards compat allows 'target' parameter to be str, + # so we delay in posix conversion until here + if self._href is not None: + self._href = make_posix_style(self._href) + + def __fspath__(self) -> str: + if self._target and (href := self._target.get_self_href()): + return href + elif self._href: + return self._href + else: + raise ValueError( + "Link cannot be used for an __fspath__, target does not have a self " + "link and _href is not set" + ) + + @override + def __getattribute__(self, name: str, /) -> Any: + if name == "owner": + warnings.warn("Link.owner is deprecated, and is always None in pystac v2") + return None + else: + return super().__getattribute__(name) + + def clone(self: Self) -> Self: + return copy.deepcopy(self) + + @classmethod + def try_from(cls, data: dict[str, Any] | Link) -> Link: + if isinstance(data, Link): + return data + else: + return cls(**data) + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> Link: + return cls(**data) + + def is_hierarchical(self) -> bool: + return self.rel in HIERARCHICAL_LINKS + + def is_self(self) -> bool: + return self.rel == RelType.SELF + + def is_root(self) -> bool: + return self.rel == RelType.ROOT + + def is_item(self) -> bool: + return self.rel == RelType.ITEM and self.media_type is None or self.is_json() + + def is_child(self) -> bool: + return self.rel == RelType.CHILD and self.media_type is None or self.is_json() + + def is_derived_from(self) -> bool: + return self.rel == RelType.DERIVED_FROM + + def is_resolved(self) -> bool: + return self._target is not None + + def is_json(self) -> bool: + return self.media_type in (MediaType.JSON, MediaType.GEOJSON) + + def get_href(self) -> str | None: + return self._href or self._target and self._target.get_self_href() + + def get_absolute_href(self, start_href: str = "") -> str | None: + href = self.get_href() + if href is None: + return href + return make_absolute_href(href, start_href, start_is_dir=False) + + def set_href(self, href: str) -> None: + self._href = href + + @property + @deprecated("href is deprecated, use .get_href()") + def href(self) -> str | None: + return self.get_href() + + @property + @deprecated("target is deprecated, either use .get_href() or .get_target()") + def target(self) -> str | STACObject | None: + return self._target or self._href + + @target.setter + @deprecated("target is deprecated, either use .set_href() or .set_target()") + def target(self, value: str | STACObject) -> None: + if isinstance(value, str): + warnings.warn( + "Setting Link.target to href is no longer supported pystac v2. " + "Assigning value to href instead." + ) + self.set_href(value) + else: + self.set_target(value) + if href := value.get_self_href(): + self.set_href(href) + + @deprecated("use .get_href()") + def get_target_str(self) -> str | None: + """Returns this link's target as a string. + + If a string href was provided, returns that. If not, tries to resolve + the self link of the target object. + """ + return self.get_href() + + @deprecated("use bool(link.get_href())") + def has_target_href(self) -> bool: + """Returns true if this link has a string href in its target information.""" + return bool(self.get_href()) + + def get_target(self, start_href: str | None, reader: Reader) -> STACObject: + from .stac_object import STACObject + + if not self._target: + if not self._href: + raise ValueError("No target and no href on the link") + + href = make_absolute_href(self._href, start_href, start_is_dir=False) + try: + self._target = STACObject.from_file(href, reader=reader) + except Exception as e: + raise STACError(f"Error while resolving link: {e}") + + return self._target + + def maybe_get_target(self) -> STACObject | None: + return self._target + + def set_target(self, target: STACObject) -> None: + self._target = target + + def to_dict(self, transform_href: bool | None = None) -> dict[str, Any]: + if transform_href is not None: + warnings.warn( + "Transforming href is deprecated and will be removed in a " + "future version.", + DeprecationWarning, + ) + if transform_href: + raise NotImplementedError + data = copy.deepcopy(self.extra_fields) + if self._href: + data["href"] = self._href + elif self._target and (href := self._target.get_self_href()): + data["href"] = href + else: + raise ValueError("No href or target self href on the link") + data["rel"] = self.rel + if self.media_type is not None: + data["type"] = self.media_type + if self.title is not None: + data["title"] = self.title + if self.method is not None: + data["method"] = self.method + if self.headers is not None: + data["headers"] = self.headers + if self.body is not None: + data["body"] = self.body + return data + + def resolve_stac_object(self, start_href: str = "") -> Link: + """Resolves a STAC object from the HREF of this link, if the link is not + already resolved. + + Args: + start_href : Optional string to put ahead of the href in this Link. + + NOTE: This uses reader.DEFAULT_READER to read the HREF, if necessary. + """ + if self._target: + return self + elif self._href: + # If it's a relative link, base it off the parent. + target_href = self._href + if not is_absolute_href(target_href): + target_href = make_absolute_href(self._href, start_href=start_href) + try: + obj = pystac.read_file(target_href) + except Exception as e: + raise STACError( + f"HREF: '{target_href}' does not resolve to a STAC object" + ) from e + self._target = obj + else: + raise ValueError("Cannot resolve STAC object without a target") + + return self + + @override + def __repr__(self) -> str: + return f"Link(rel={self.rel}, href={self._href})" + + ##### Convenience methods for Link creation ##### + @classmethod + def root(cls: type[Self], c: Catalog) -> Self: + """Creates a link to a root Catalog or Collection.""" + return cls(RelType.ROOT, c, media_type=MediaType.JSON) + + @classmethod + def parent(cls: type[Self], c: Catalog, title: str | None = None) -> Self: + """Creates a link to a parent Catalog or Collection.""" + return cls(RelType.PARENT, c, title=title, media_type=MediaType.JSON) + + @classmethod + def collection(cls: type[Self], c: Collection) -> Self: + """Creates a link to a Collection.""" + return cls(RelType.COLLECTION, c, media_type=MediaType.JSON) + + @classmethod + def self_href(cls: type[Self], href: str) -> Self: + """Creates a self link to a file's location.""" + return cls(RelType.SELF, href, media_type=MediaType.JSON) + + @classmethod + def child(cls: type[Self], c: Catalog, title: str | None = None) -> Self: + """Creates a link to a child Catalog or Collection.""" + return cls(RelType.CHILD, c, title=title, media_type=MediaType.JSON) + + @classmethod + def item(cls: type[Self], item: Item, title: str | None = None) -> Self: + """Creates a link to an Item.""" + return cls(RelType.ITEM, item, title=title, media_type=MediaType.GEOJSON) + + @classmethod + def derived_from( + cls: type[Self], item: Item | str, title: str | None = None + ) -> Self: + """Creates a link to a derived_from Item.""" + return cls( + RelType.DERIVED_FROM, + item, + title=title, + media_type=MediaType.JSON, + ) + + @classmethod + def canonical( + cls: type[Self], + item_or_collection: Item | Collection, + title: str | None = None, + ) -> Self: + """Creates a canonical link to an Item or Collection.""" + return cls( + RelType.CANONICAL, + item_or_collection, + title=title, + media_type=MediaType.JSON, + ) + + @property + def ext(self) -> LinkExt: + """Accessor for extension classes on this link + + Example:: + + link.ext.file.size = 8675309 + """ + from pystac.extensions.ext import LinkExt + + return LinkExt(stac_object=self) diff --git a/pystac/media_type.py b/src/pystac/media_type.py similarity index 100% rename from pystac/media_type.py rename to src/pystac/media_type.py diff --git a/src/pystac/provider.py b/src/pystac/provider.py new file mode 100644 index 000000000..e520ca67b --- /dev/null +++ b/src/pystac/provider.py @@ -0,0 +1,56 @@ +from __future__ import annotations + +import copy +from enum import StrEnum +from typing import Any, override + + +class Provider: + def __init__( + self, + name: str, + description: str | None = None, + roles: list[ProviderRole | str] | None = None, + url: str | None = None, + **kwargs: Any, + ) -> None: + self.name: str = name + self.description: str | None = description + self.roles: list[ProviderRole | str] | None = roles + self.url: str | None = url + self.extra_fields: dict[str, Any] = kwargs + + @override + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Provider): + return NotImplemented + return self.to_dict() == other.to_dict() + + @classmethod + def try_from(cls, data: Provider | dict[str, Any]) -> Provider: + if isinstance(data, Provider): + return data + else: + return Provider.from_dict(data) + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> Provider: + return cls(**data) + + def to_dict(self) -> dict[str, Any]: + data = copy.deepcopy(self.extra_fields) + data["name"] = self.name + if self.description: + data["description"] = self.description + if self.roles: + data["roles"] = self.roles + if self.url: + data["url"] = self.url + return data + + +class ProviderRole(StrEnum): + LICENSOR = "licensor" + PRODUCER = "producer" + PROCESSOR = "processor" + HOST = "host" diff --git a/pystac/py.typed b/src/pystac/py.typed similarity index 100% rename from pystac/py.typed rename to src/pystac/py.typed diff --git a/src/pystac/reader.py b/src/pystac/reader.py new file mode 100644 index 000000000..aceca1915 --- /dev/null +++ b/src/pystac/reader.py @@ -0,0 +1,38 @@ +import json +import os +import urllib.parse +import urllib.request +from pathlib import Path +from typing import Any, Protocol +from urllib.request import Request + +from .utils import get_user_agent + + +class Reader(Protocol): + def get_json(self, href: str | Path) -> dict[str, Any]: ... + + +class StandardLibraryReader: + def get_json(self, href: str | Path) -> dict[str, Any]: + if isinstance(href, Path): + with open(href) as f: + return json.load(f) + parsed_url = urllib.parse.urlparse(href) + if parsed_url.scheme in ["http", "https"]: + request = Request(href, headers={"User-Agent": get_user_agent()}) + with urllib.request.urlopen(request) as f: + return json.load(f) + elif not parsed_url.scheme or os.path.exists(href): + with open(href) as f: + return json.load(f) + else: + raise ValueError(f"Unsupported scheme: {parsed_url.scheme} for {href}") + + +def set_default_reader(reader: Reader) -> None: + global DEFAULT_READER + DEFAULT_READER = reader # pyright: ignore[reportConstantRedefinition] + + +DEFAULT_READER = StandardLibraryReader() diff --git a/pystac/rel_type.py b/src/pystac/rel_type.py similarity index 100% rename from pystac/rel_type.py rename to src/pystac/rel_type.py diff --git a/src/pystac/schemas/geojson/Feature.json b/src/pystac/schemas/geojson/Feature.json new file mode 100644 index 000000000..89136eafe --- /dev/null +++ b/src/pystac/schemas/geojson/Feature.json @@ -0,0 +1,505 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://geojson.org/schema/Feature.json", + "title": "GeoJSON Feature", + "type": "object", + "required": [ + "type", + "properties", + "geometry" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Feature" + ] + }, + "id": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "properties": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object" + } + ] + }, + "geometry": { + "oneOf": [ + { + "type": "null" + }, + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON GeometryCollection", + "type": "object", + "required": [ + "type", + "geometries" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "GeometryCollection" + ] + }, + "geometries": { + "type": "array", + "items": { + "oneOf": [ + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } +} \ No newline at end of file diff --git a/src/pystac/schemas/geojson/Geometry.json b/src/pystac/schemas/geojson/Geometry.json new file mode 100644 index 000000000..6f5ed9833 --- /dev/null +++ b/src/pystac/schemas/geojson/Geometry.json @@ -0,0 +1,218 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://geojson.org/schema/Geometry.json", + "title": "GeoJSON Geometry", + "oneOf": [ + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] +} \ No newline at end of file diff --git a/src/pystac/schemas/stac/v1.0.0/basics.json b/src/pystac/schemas/stac/v1.0.0/basics.json new file mode 100644 index 000000000..68e8f37ae --- /dev/null +++ b/src/pystac/schemas/stac/v1.0.0/basics.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/basics.json#", + "title": "Basic Descriptive Fields", + "type": "object", + "properties": { + "title": { + "title": "Item Title", + "description": "A human-readable title describing the Item.", + "type": "string" + }, + "description": { + "title": "Item Description", + "description": "Detailed multi-line description to fully explain the Item.", + "type": "string" + } + } +} \ No newline at end of file diff --git a/src/pystac/schemas/stac/v1.0.0/catalog.json b/src/pystac/schemas/stac/v1.0.0/catalog.json new file mode 100644 index 000000000..31b2c8f8a --- /dev/null +++ b/src/pystac/schemas/stac/v1.0.0/catalog.json @@ -0,0 +1,94 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.0.0/catalog-spec/json-schema/catalog.json#", + "title": "STAC Catalog Specification", + "description": "This object represents Catalogs in a SpatioTemporal Asset Catalog.", + "allOf": [ + { + "$ref": "#/definitions/catalog" + } + ], + "definitions": { + "catalog": { + "title": "STAC Catalog", + "type": "object", + "required": [ + "stac_version", + "type", + "id", + "description", + "links" + ], + "properties": { + "stac_version": { + "title": "STAC version", + "type": "string", + "const": "1.0.0" + }, + "stac_extensions": { + "title": "STAC extensions", + "type": "array", + "uniqueItems": true, + "items": { + "title": "Reference to a JSON Schema", + "type": "string", + "format": "iri" + } + }, + "type": { + "title": "Type of STAC entity", + "const": "Catalog" + }, + "id": { + "title": "Identifier", + "type": "string", + "minLength": 1 + }, + "title": { + "title": "Title", + "type": "string" + }, + "description": { + "title": "Description", + "type": "string", + "minLength": 1 + }, + "links": { + "title": "Links", + "type": "array", + "items": { + "$ref": "#/definitions/link" + } + } + } + }, + "link": { + "type": "object", + "required": [ + "rel", + "href" + ], + "properties": { + "href": { + "title": "Link reference", + "type": "string", + "format": "iri-reference", + "minLength": 1 + }, + "rel": { + "title": "Link relation type", + "type": "string", + "minLength": 1 + }, + "type": { + "title": "Link type", + "type": "string" + }, + "title": { + "title": "Link title", + "type": "string" + } + } + } + } +} diff --git a/src/pystac/schemas/stac/v1.0.0/collection.json b/src/pystac/schemas/stac/v1.0.0/collection.json new file mode 100644 index 000000000..2ad20902d --- /dev/null +++ b/src/pystac/schemas/stac/v1.0.0/collection.json @@ -0,0 +1,270 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.0.0/collection-spec/json-schema/collection.json#", + "title": "STAC Collection Specification", + "description": "This object represents Collections in a SpatioTemporal Asset Catalog.", + "allOf": [ + { + "$ref": "#/definitions/collection" + } + ], + "definitions": { + "collection": { + "title": "STAC Collection", + "description": "These are the fields specific to a STAC Collection. All other fields are inherited from STAC Catalog.", + "type": "object", + "required": [ + "stac_version", + "type", + "id", + "description", + "license", + "extent", + "links" + ], + "properties": { + "stac_version": { + "title": "STAC version", + "type": "string", + "const": "1.0.0" + }, + "stac_extensions": { + "title": "STAC extensions", + "type": "array", + "uniqueItems": true, + "items": { + "title": "Reference to a JSON Schema", + "type": "string", + "format": "iri" + } + }, + "type": { + "title": "Type of STAC entity", + "const": "Collection" + }, + "id": { + "title": "Identifier", + "type": "string", + "minLength": 1 + }, + "title": { + "title": "Title", + "type": "string" + }, + "description": { + "title": "Description", + "type": "string", + "minLength": 1 + }, + "keywords": { + "title": "Keywords", + "type": "array", + "items": { + "type": "string" + } + }, + "license": { + "title": "Collection License Name", + "type": "string", + "pattern": "^[\\w\\-\\.\\+]+$" + }, + "providers": { + "type": "array", + "items": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "title": "Organization name", + "type": "string" + }, + "description": { + "title": "Organization description", + "type": "string" + }, + "roles": { + "title": "Organization roles", + "type": "array", + "items": { + "type": "string", + "enum": [ + "producer", + "licensor", + "processor", + "host" + ] + } + }, + "url": { + "title": "Organization homepage", + "type": "string", + "format": "iri" + } + } + } + }, + "extent": { + "title": "Extents", + "type": "object", + "required": [ + "spatial", + "temporal" + ], + "properties": { + "spatial": { + "title": "Spatial extent object", + "type": "object", + "required": [ + "bbox" + ], + "properties": { + "bbox": { + "title": "Spatial extents", + "type": "array", + "minItems": 1, + "items": { + "title": "Spatial extent", + "type": "array", + "oneOf": [ + { + "minItems":4, + "maxItems":4 + }, + { + "minItems":6, + "maxItems":6 + } + ], + "items": { + "type": "number" + } + } + } + } + }, + "temporal": { + "title": "Temporal extent object", + "type": "object", + "required": [ + "interval" + ], + "properties": { + "interval": { + "title": "Temporal extents", + "type": "array", + "minItems": 1, + "items": { + "title": "Temporal extent", + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": [ + "string", + "null" + ], + "format": "date-time", + "pattern": "(\\+00:00|Z)$" + } + } + } + } + } + } + }, + "assets": { + "$ref": "../../item-spec/json-schema/item.json#/definitions/assets" + }, + "links": { + "title": "Links", + "type": "array", + "items": { + "$ref": "#/definitions/link" + } + }, + "summaries": { + "$ref": "#/definitions/summaries" + } + } + }, + "link": { + "type": "object", + "required": [ + "rel", + "href" + ], + "properties": { + "href": { + "title": "Link reference", + "type": "string", + "format": "iri-reference", + "minLength": 1 + }, + "rel": { + "title": "Link relation type", + "type": "string", + "minLength": 1 + }, + "type": { + "title": "Link type", + "type": "string" + }, + "title": { + "title": "Link title", + "type": "string" + } + } + }, + "summaries": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "title": "JSON Schema", + "type": "object", + "minProperties": 1, + "allOf": [ + { + "$ref": "http://json-schema.org/draft-07/schema" + } + ] + }, + { + "title": "Range", + "type": "object", + "required": [ + "minimum", + "maximum" + ], + "properties": { + "minimum": { + "title": "Minimum value", + "type": [ + "number", + "string" + ] + }, + "maximum": { + "title": "Maximum value", + "type": [ + "number", + "string" + ] + } + } + }, + { + "title": "Set of values", + "type": "array", + "minItems": 1, + "items": { + "description": "For each field only the original data type of the property can occur (except for arrays), but we can't validate that in JSON Schema yet. See the sumamry description in the STAC specification for details." + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/src/pystac/schemas/stac/v1.0.0/datetime.json b/src/pystac/schemas/stac/v1.0.0/datetime.json new file mode 100644 index 000000000..4c7a3a143 --- /dev/null +++ b/src/pystac/schemas/stac/v1.0.0/datetime.json @@ -0,0 +1,53 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/datetime.json#", + "title": "Date and Time Fields", + "type": "object", + "dependencies": { + "start_datetime": { + "required": [ + "end_datetime" + ] + }, + "end_datetime": { + "required": [ + "start_datetime" + ] + } + }, + "properties": { + "datetime": { + "title": "Date and Time", + "description": "The searchable date/time of the assets, in UTC (Formatted in RFC 3339) ", + "type": ["string", "null"], + "format": "date-time", + "pattern": "(\\+00:00|Z)$" + }, + "start_datetime": { + "title": "Start Date and Time", + "description": "The searchable start date/time of the assets, in UTC (Formatted in RFC 3339) ", + "type": "string", + "format": "date-time", + "pattern": "(\\+00:00|Z)$" + }, + "end_datetime": { + "title": "End Date and Time", + "description": "The searchable end date/time of the assets, in UTC (Formatted in RFC 3339) ", + "type": "string", + "format": "date-time", + "pattern": "(\\+00:00|Z)$" + }, + "created": { + "title": "Creation Time", + "type": "string", + "format": "date-time", + "pattern": "(\\+00:00|Z)$" + }, + "updated": { + "title": "Last Update Time", + "type": "string", + "format": "date-time", + "pattern": "(\\+00:00|Z)$" + } + } +} \ No newline at end of file diff --git a/src/pystac/schemas/stac/v1.0.0/instrument.json b/src/pystac/schemas/stac/v1.0.0/instrument.json new file mode 100644 index 000000000..688c4a497 --- /dev/null +++ b/src/pystac/schemas/stac/v1.0.0/instrument.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/instrument.json#", + "title": "Instrument Fields", + "type": "object", + "properties": { + "platform": { + "title": "Platform", + "type": "string" + }, + "instruments": { + "title": "Instruments", + "type": "array", + "items": { + "type": "string" + } + }, + "constellation": { + "title": "Constellation", + "type": "string" + }, + "mission": { + "title": "Mission", + "type": "string" + }, + "gsd": { + "title": "Ground Sample Distance", + "type": "number", + "exclusiveMinimum": 0 + } + } +} \ No newline at end of file diff --git a/src/pystac/schemas/stac/v1.0.0/item.json b/src/pystac/schemas/stac/v1.0.0/item.json new file mode 100644 index 000000000..8d4286783 --- /dev/null +++ b/src/pystac/schemas/stac/v1.0.0/item.json @@ -0,0 +1,272 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/item.json#", + "title": "STAC Item", + "type": "object", + "description": "This object represents the metadata for an item in a SpatioTemporal Asset Catalog.", + "allOf": [ + { + "$ref": "#/definitions/core" + } + ], + "definitions": { + "common_metadata": { + "allOf": [ + { + "$ref": "basics.json" + }, + { + "$ref": "datetime.json" + }, + { + "$ref": "instrument.json" + }, + { + "$ref": "licensing.json" + }, + { + "$ref": "provider.json" + } + ] + }, + "core": { + "allOf": [ + { + "$ref": "https://geojson.org/schema/Feature.json" + }, + { + "oneOf": [ + { + "type": "object", + "required": [ + "geometry", + "bbox" + ], + "properties": { + "geometry": { + "$ref": "https://geojson.org/schema/Geometry.json" + }, + "bbox": { + "type": "array", + "oneOf": [ + { + "minItems": 4, + "maxItems": 4 + }, + { + "minItems": 6, + "maxItems": 6 + } + ], + "items": { + "type": "number" + } + } + } + }, + { + "type": "object", + "required": [ + "geometry" + ], + "properties": { + "geometry": { + "type": "null" + }, + "bbox": { + "not": {} + } + } + } + ] + }, + { + "type": "object", + "required": [ + "stac_version", + "id", + "links", + "assets", + "properties" + ], + "properties": { + "stac_version": { + "title": "STAC version", + "type": "string", + "const": "1.0.0" + }, + "stac_extensions": { + "title": "STAC extensions", + "type": "array", + "uniqueItems": true, + "items": { + "title": "Reference to a JSON Schema", + "type": "string", + "format": "iri" + } + }, + "id": { + "title": "Provider ID", + "description": "Provider item ID", + "type": "string", + "minLength": 1 + }, + "links": { + "title": "Item links", + "description": "Links to item relations", + "type": "array", + "items": { + "$ref": "#/definitions/link" + } + }, + "assets": { + "$ref": "#/definitions/assets" + }, + "properties": { + "allOf": [ + { + "$ref": "#/definitions/common_metadata" + }, + { + "anyOf": [ + { + "required": [ + "datetime" + ], + "properties": { + "datetime": { + "not": { + "type": "null" + } + } + } + }, + { + "required": [ + "datetime", + "start_datetime", + "end_datetime" + ] + } + ] + } + ] + } + }, + "if": { + "properties": { + "links": { + "contains": { + "required": [ + "rel" + ], + "properties": { + "rel": { + "const": "collection" + } + } + } + } + } + }, + "then": { + "required": [ + "collection" + ], + "properties": { + "collection": { + "title": "Collection ID", + "description": "The ID of the STAC Collection this Item references to.", + "type": "string", + "minLength": 1 + } + } + }, + "else": { + "properties": { + "collection": { + "not": {} + } + } + } + } + ] + }, + "link": { + "type": "object", + "required": [ + "rel", + "href" + ], + "properties": { + "href": { + "title": "Link reference", + "type": "string", + "format": "iri-reference", + "minLength": 1 + }, + "rel": { + "title": "Link relation type", + "type": "string", + "minLength": 1 + }, + "type": { + "title": "Link type", + "type": "string" + }, + "title": { + "title": "Link title", + "type": "string" + } + } + }, + "assets": { + "title": "Asset links", + "description": "Links to assets", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/asset" + } + }, + "asset": { + "allOf": [ + { + "type": "object", + "required": [ + "href" + ], + "properties": { + "href": { + "title": "Asset reference", + "type": "string", + "format": "iri-reference", + "minLength": 1 + }, + "title": { + "title": "Asset title", + "type": "string" + }, + "description": { + "title": "Asset description", + "type": "string" + }, + "type": { + "title": "Asset type", + "type": "string" + }, + "roles": { + "title": "Asset roles", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + { + "$ref": "#/definitions/common_metadata" + } + ] + } + } +} diff --git a/src/pystac/schemas/stac/v1.0.0/licensing.json b/src/pystac/schemas/stac/v1.0.0/licensing.json new file mode 100644 index 000000000..ca0eed8b9 --- /dev/null +++ b/src/pystac/schemas/stac/v1.0.0/licensing.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/licensing.json#", + "title": "Licensing Fields", + "type": "object", + "properties": { + "license": { + "type": "string", + "pattern": "^[\\w\\-\\.\\+]+$" + } + } +} \ No newline at end of file diff --git a/src/pystac/schemas/stac/v1.0.0/provider.json b/src/pystac/schemas/stac/v1.0.0/provider.json new file mode 100644 index 000000000..01cfadcec --- /dev/null +++ b/src/pystac/schemas/stac/v1.0.0/provider.json @@ -0,0 +1,47 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/provider.json#", + "title": "Provider Fields", + "type": "object", + "properties": { + "providers": { + "title": "Providers", + "type": "array", + "items": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "title": "Organization name", + "type": "string", + "minLength": 1 + }, + "description": { + "title": "Organization description", + "type": "string" + }, + "roles": { + "title": "Organization roles", + "type": "array", + "items": { + "type": "string", + "enum": [ + "producer", + "licensor", + "processor", + "host" + ] + } + }, + "url": { + "title": "Organization homepage", + "type": "string", + "format": "iri" + } + } + } + } + } +} \ No newline at end of file diff --git a/pystac/validation/jsonschemas/stac-spec/v1.1.0/bands.json b/src/pystac/schemas/stac/v1.1.0/bands.json similarity index 100% rename from pystac/validation/jsonschemas/stac-spec/v1.1.0/bands.json rename to src/pystac/schemas/stac/v1.1.0/bands.json diff --git a/pystac/validation/jsonschemas/stac-spec/v1.1.0/basics.json b/src/pystac/schemas/stac/v1.1.0/basics.json similarity index 100% rename from pystac/validation/jsonschemas/stac-spec/v1.1.0/basics.json rename to src/pystac/schemas/stac/v1.1.0/basics.json diff --git a/pystac/validation/jsonschemas/stac-spec/v1.1.0/catalog.json b/src/pystac/schemas/stac/v1.1.0/catalog.json similarity index 100% rename from pystac/validation/jsonschemas/stac-spec/v1.1.0/catalog.json rename to src/pystac/schemas/stac/v1.1.0/catalog.json diff --git a/pystac/validation/jsonschemas/stac-spec/v1.1.0/collection.json b/src/pystac/schemas/stac/v1.1.0/collection.json similarity index 100% rename from pystac/validation/jsonschemas/stac-spec/v1.1.0/collection.json rename to src/pystac/schemas/stac/v1.1.0/collection.json diff --git a/pystac/validation/jsonschemas/stac-spec/v1.1.0/common.json b/src/pystac/schemas/stac/v1.1.0/common.json similarity index 100% rename from pystac/validation/jsonschemas/stac-spec/v1.1.0/common.json rename to src/pystac/schemas/stac/v1.1.0/common.json diff --git a/pystac/validation/jsonschemas/stac-spec/v1.1.0/data-values.json b/src/pystac/schemas/stac/v1.1.0/data-values.json similarity index 100% rename from pystac/validation/jsonschemas/stac-spec/v1.1.0/data-values.json rename to src/pystac/schemas/stac/v1.1.0/data-values.json diff --git a/pystac/validation/jsonschemas/stac-spec/v1.1.0/datetime.json b/src/pystac/schemas/stac/v1.1.0/datetime.json similarity index 100% rename from pystac/validation/jsonschemas/stac-spec/v1.1.0/datetime.json rename to src/pystac/schemas/stac/v1.1.0/datetime.json diff --git a/pystac/validation/jsonschemas/stac-spec/v1.1.0/instrument.json b/src/pystac/schemas/stac/v1.1.0/instrument.json similarity index 100% rename from pystac/validation/jsonschemas/stac-spec/v1.1.0/instrument.json rename to src/pystac/schemas/stac/v1.1.0/instrument.json diff --git a/pystac/validation/jsonschemas/stac-spec/v1.1.0/item.json b/src/pystac/schemas/stac/v1.1.0/item.json similarity index 100% rename from pystac/validation/jsonschemas/stac-spec/v1.1.0/item.json rename to src/pystac/schemas/stac/v1.1.0/item.json diff --git a/pystac/validation/jsonschemas/stac-spec/v1.1.0/licensing.json b/src/pystac/schemas/stac/v1.1.0/licensing.json similarity index 100% rename from pystac/validation/jsonschemas/stac-spec/v1.1.0/licensing.json rename to src/pystac/schemas/stac/v1.1.0/licensing.json diff --git a/pystac/validation/jsonschemas/stac-spec/v1.1.0/provider.json b/src/pystac/schemas/stac/v1.1.0/provider.json similarity index 100% rename from pystac/validation/jsonschemas/stac-spec/v1.1.0/provider.json rename to src/pystac/schemas/stac/v1.1.0/provider.json diff --git a/pystac/serialization/__init__.py b/src/pystac/serialization/__init__.py similarity index 80% rename from pystac/serialization/__init__.py rename to src/pystac/serialization/__init__.py index 3c89c7903..e782b1ed6 100644 --- a/pystac/serialization/__init__.py +++ b/src/pystac/serialization/__init__.py @@ -1,3 +1,10 @@ +import warnings + +warnings.warn( + "pystac.serialization is deprecated", + DeprecationWarning, +) + __all__ = [ "merge_common_properties", "migrate_to_latest", diff --git a/pystac/serialization/common_properties.py b/src/pystac/serialization/common_properties.py similarity index 96% rename from pystac/serialization/common_properties.py rename to src/pystac/serialization/common_properties.py index 5109782c8..ac94e0ef5 100644 --- a/pystac/serialization/common_properties.py +++ b/src/pystac/serialization/common_properties.py @@ -89,9 +89,7 @@ def merge_common_properties( collection_props = collection["properties"] else: raise ValueError( - "{} is expected to be a Collection or dict but is neither.".format( - collection - ) + f"{collection} is expected to be a Collection or dict but is neither." ) if collection_props is not None: diff --git a/pystac/serialization/identify.py b/src/pystac/serialization/identify.py similarity index 100% rename from pystac/serialization/identify.py rename to src/pystac/serialization/identify.py diff --git a/pystac/serialization/migrate.py b/src/pystac/serialization/migrate.py similarity index 100% rename from pystac/serialization/migrate.py rename to src/pystac/serialization/migrate.py diff --git a/pystac/stac_io.py b/src/pystac/stac_io.py similarity index 96% rename from pystac/stac_io.py rename to src/pystac/stac_io.py index 9baa9b443..f2f1a07bd 100644 --- a/pystac/stac_io.py +++ b/src/pystac/stac_io.py @@ -1,9 +1,16 @@ from __future__ import annotations +import warnings + +warnings.warn( + "pystac.stac_io is deprecated. Use Reader and Writer instead", DeprecationWarning +) + import json import os from abc import ABC, abstractmethod from collections.abc import Callable +from pathlib import Path from typing import TYPE_CHECKING, Any import pystac @@ -470,3 +477,22 @@ def read_text_from_href(self, href: str) -> str: raise Exception(f"Could not read uri {href}") from e else: return super().read_text_from_href(href) + + +class StacIOReader: + def __init__(self, stac_io: StacIO) -> None: + self.stac_io = stac_io + + def get_json(self, href: str | Path) -> dict[str, Any]: + return self.stac_io.read_json(href) + + +class StacIOWriter: + def __init__(self, stac_io: StacIO) -> None: + self.stac_io: StacIO = stac_io + + def put_json(self, data: dict[str, Any], href: str | Path) -> None: + self.stac_io.save_json(href, data) + + def delete(self, href: str | Path) -> None: # pyright: ignore[reportUnusedParameter] + raise NotImplementedError diff --git a/src/pystac/stac_object.py b/src/pystac/stac_object.py new file mode 100644 index 000000000..bccb33c26 --- /dev/null +++ b/src/pystac/stac_object.py @@ -0,0 +1,456 @@ +from __future__ import annotations + +import copy +import warnings +from abc import ABC, abstractmethod +from collections.abc import Iterator +from enum import StrEnum +from pathlib import Path +from typing import TYPE_CHECKING, Any, ClassVar, override + +from typing_extensions import deprecated + +from pystac.errors import STACTypeError +from pystac.rel_type import RelType +from pystac.writer import DEFAULT_WRITER + +from .constants import STAC_OBJECT_TYPE +from .link import Link +from .reader import DEFAULT_READER, Reader +from .utils import ( + is_absolute_href, + make_absolute_href, + make_relative_href, +) +from .validator import Validator +from .writer import Writer + +if TYPE_CHECKING: + from .collection import Collection + from .container import Container + from .href_generator import HrefGenerator + from .writer import Writer + + +class STACObject(ABC): + type: ClassVar[STAC_OBJECT_TYPE] + + def __init__( + self, + type: str, + id: str, + stac_version: str, + stac_extensions: list[str] | None, + links: list[Link] | list[dict[str, Any]] | None, + **kwargs: Any, + ) -> None: + if type != self.type: + d = { + "type": type, + "id": id, + "stac_version": stac_version, + "stac_extensions": stac_extensions, + "links": links, + } + d.update(kwargs) + raise STACTypeError( + d, + self.__class__, + ) + + self.id: str = id + self.stac_version: str = stac_version + self.stac_extensions: list[str] | None = stac_extensions + if links: + self.links: list[Link] = [Link.try_from(link) for link in links] + else: + self.links = [] + self.extra_fields: dict[str, Any] = kwargs + + self.reader: Reader = DEFAULT_READER + self.writer: Writer = DEFAULT_WRITER + + self._root: Container | None = None + self._href: str | None = None + + @override + def __setattr__(self, name: str, value: Any, /) -> None: + match name: + case "_stac_io": + warnings.warn( + "_stac_io is a deprecated attribute. Set .reader or .writer" + ) + + from .stac_io import StacIOReader, StacIOWriter + + self.reader = StacIOReader(value) + self.writer = StacIOWriter(value) + case _: + return super().__setattr__(name, value) + + @classmethod + def from_file[T: STACObject]( + cls: type[T], path: str | Path, reader: Reader = DEFAULT_READER + ) -> T: + href = make_absolute_href(str(path)) + data = reader.get_json(href) + if "type" not in data: + if "properties" in data: + type_value = "Feature" + elif "extents" in data or "license" in data: + type_value = "Collection" + else: + type_value = "Catalog" + warnings.warn(f"Data does not have a 'type' field, guessed as {type_value}") + data["type"] = type_value + + stac_object = cls.from_dict(data) + if not isinstance(stac_object, cls): + raise ValueError( + f"Expected {cls.__name__}, got {type(stac_object).__name__}" + ) + stac_object.reader = reader + stac_object.set_self_href(str(href)) + return stac_object + + @classmethod + @abstractmethod + def from_dict[T: STACObject]( + cls: type[T], + data: dict[str, Any], + preserve_dict: bool = True, + migrate: bool | None = None, + root: Container | None = None, + ) -> T: + from .deserialize import from_dict + + if preserve_dict: + data = copy.deepcopy(data) + return from_dict(data) # pyright: ignore[reportReturnType] + + @abstractmethod + def to_dict( + self, include_self_link: bool = True, transform_hrefs: bool | None = None + ) -> dict[str, Any]: + if transform_hrefs is not None: + warnings.warn( + "transform_hrefs is deprecated as an argument", + DeprecationWarning, + ) + if transform_hrefs: + raise NotImplementedError + data = copy.deepcopy(self.extra_fields) + links = [ + link.to_dict() + for link in self.links + # Keep the self link if include_self_link is true and the object + # doesn't have an href + if include_self_link and not self._href or not link.is_self() + ] + if include_self_link and self._href: + links.append(Link(rel="self", target=self._href).to_dict()) + data.update( + { + "type": self.type, + "stac_version": self.stac_version, + "id": self.id, + "links": links, + } + ) + if self.stac_extensions is not None: + data["stac_extensions"] = self.stac_extensions + return data + + def get_link(self, rel: str) -> Link | None: + return next((link for link in self.links if link.rel == rel), None) + + def get_links(self, rel: str | None = None) -> list[Link]: + return list(link for link in self.links if rel is None or link.rel == rel) + + @deprecated("Use .get_link()") + def get_single_link(self, rel: str) -> Link | None: + return self.get_link(rel) + + def get_derived_from(self) -> list[STACObject]: + derived_from: list[STACObject] = [] + for link in self.links: + if link.is_derived_from(): + derived_from.append(link.get_target(self._href, self.reader)) + return derived_from + + @deprecated("Append to .links") + def add_link(self, link: Link) -> None: + self.links.append(link) + + def add_derived_from(self, *values: STACObject | str) -> None: + for value in values: + self.links.append(Link(target=value, rel=RelType.DERIVED_FROM)) + + def remove_derived_from(self, id: str) -> None: + links: list[Link] = [] + for link in self.links: + if link.is_derived_from(): + stac_object = link.get_target(self._href, self.reader) + if stac_object.id == id: + continue + links.append(link) + self.links = links + + def remove_links(self, rel: str) -> None: + self.links = list(link for link in self.links if not link.rel == rel) + + def remove_hierarchical_links(self, add_canonical: bool = False) -> None: + self.links = list(link for link in self.links if not link.is_hierarchical()) + if add_canonical and self._href: + self.links.append(Link(rel="canonical", target=self._href)) + + @property + @deprecated("Use get_self_href") + def self_href(self) -> str | None: + return self.get_self_href() + + def get_self_href(self) -> str | None: + return self._href + + def set_self_href(self, href: str | None) -> None: + self._href = make_absolute_href(href) if href is not None else None + + def get_root(self) -> Container | None: + from .container import Container + + if self._root is None: + root = self._maybe_get_link_target(RelType.ROOT) + if isinstance(root, Container): + self._root = root + else: + warnings.warn( + "The 'root' link does not point to a collection or catalog" + ) + return self._root + + def remove_root(self) -> Container | None: + root = self._root + self._root = None + self.remove_links(RelType.ROOT) + return root + + def get_parent(self) -> Container | None: + from .container import Container + + stac_object = self._maybe_get_link_target(RelType.PARENT) + if isinstance(stac_object, Container): + return stac_object + else: + warnings.warn("The 'parent' link does not point to a collection or catalog") + return None + + def get_root_link(self) -> Link | None: + return next((link for link in self.links if link.is_root()), None) + + def set_root(self, root: Container) -> None: + self._root = root + + def validate( + self, validate_extensions: bool = True, validator: Validator | None = None + ) -> None: + if validator is None: + from .jsonschema import DEFAULT_JSON_SCHEMA_VALIDATOR + + validator = DEFAULT_JSON_SCHEMA_VALIDATOR + + data = self.to_dict() + validator.validate_core(self.type, self.stac_version, data) + + if validate_extensions and self.stac_extensions: + for extension in self.stac_extensions: + if not is_absolute_href(extension): + extension = make_absolute_href(extension, self._href, False) + validator.validate_extension(extension, data) + + def render( + self, + /, + use_absolute_links: bool = False, + href_generator: HrefGenerator | None = None, + previous_self_href: str | None = None, + skip_unresolved: bool = False, + ) -> Iterator[STACObject]: + """Remove and re-create all hierarchical links on this STAC object and + all children and items, yielding each.""" + yield from self._render( + use_absolute_links=use_absolute_links, + href_generator=href_generator, + previous_self_href=previous_self_href, + skip_unresolved=skip_unresolved, + root=None, + parent=None, + collection=None, + ) + + def _render( + self, + /, + root: Container | None = None, + parent: Container | None = None, + collection: Collection | None = None, + use_absolute_links: bool = False, + skip_unresolved: bool = False, + href_generator: HrefGenerator | None = None, + previous_self_href: str | None = None, + ) -> Iterator[STACObject]: + from .catalog import Catalog + from .collection import Collection + from .container import Container + from .item import Item + + if href_generator is None: + from .href_generator import BestPracticesHrefGenerator + + href_generator = BestPracticesHrefGenerator() + + self_href = self.get_self_href() + if self_href is None: + raise ValueError("Cannot render without a self href") + if root is None and isinstance(self, Container): + root = self + + links: list[Link] = [] + if root and (root_href := root.get_self_href()): + links.append( + self._update_link_href( + Link(rel=RelType.ROOT, target=root), + href=root_href, + make_absolute=use_absolute_links, + ) + ) + if parent and (parent_href := parent.get_self_href()): + links.append( + self._update_link_href( + Link(rel=RelType.PARENT, target=parent), + href=parent_href, + make_absolute=use_absolute_links, + ) + ) + if collection and (collection_href := collection.get_self_href()): + links.append( + self._update_link_href( + Link(rel=RelType.COLLECTION, target=collection), + href=collection_href, + make_absolute=use_absolute_links, + ) + ) + for link in self.links: + if skip_unresolved and not link.is_resolved(): + links.append(link) + continue + + if link.is_child() or link.is_item(): + stac_object = link.get_target(previous_self_href, self.reader) + stac_object_previous_self_href = stac_object.get_self_href() + if isinstance(stac_object, Container): + href = href_generator.get_child(self_href, stac_object) + stac_object.set_self_href(href) + links.append( + self._update_link_href( + link, + href=href, + make_absolute=use_absolute_links, + ) + ) + elif isinstance(stac_object, Item): + href = href_generator.get_item(self_href, stac_object) + stac_object.set_self_href(href) + links.append( + self._update_link_href( + link, + href=href, + make_absolute=use_absolute_links, + ) + ) + + if isinstance(self, Collection): + yield from stac_object._render( + root=root, + parent=self, + collection=self, + use_absolute_links=use_absolute_links, + href_generator=href_generator, + previous_self_href=stac_object_previous_self_href, + ) + elif isinstance(self, Catalog): + yield from stac_object._render( + root=root, + parent=self, + use_absolute_links=use_absolute_links, + href_generator=href_generator, + previous_self_href=stac_object_previous_self_href, + ) + else: + yield from stac_object._render( + root=root, + use_absolute_links=use_absolute_links, + href_generator=href_generator, + previous_self_href=stac_object_previous_self_href, + ) + elif not link.is_hierarchical(): + links.append(link) + + self.links = links + yield self + + def save_object( + self, + include_self_link: bool = True, + dest_href: str | None = None, + writer: Writer | None = None, + ) -> None: + href = dest_href or self._href + if not href: + raise ValueError( + "dest_href was not provided, and object does not have a self href" + ) + href = make_absolute_href(href) + if writer is None: + writer = self.writer + writer.put_json(self.to_dict(include_self_link), href) + + def clone[T: STACObject](self: T) -> T: + return copy.deepcopy(self) + + @override + def __repr__(self) -> str: + return f"<{self.__class__} id={self.id}>" + + def _update_link_href(self, link: Link, href: str, make_absolute: bool) -> Link: + self_href = self.get_self_href() + href = make_absolute_href(href, self_href, start_is_dir=False) + if not make_absolute and self_href: + href = make_relative_href(href, self_href, start_is_dir=False) + link.set_href(href) + return link + + def _maybe_get_link_target(self, rel: str) -> STACObject | None: + link = self.get_link(rel) + if link: + return link.get_target(start_href=self._href, reader=self.reader) + else: + return None + + +@deprecated("STACObjectType is deprecated, use pystac.constants.STAC_OBJECT_TYPE") +class STACObjectType(StrEnum): + CATALOG = "Catalog" + COLLECTION = "Collection" + ITEM = "Feature" + + +def __getattr__(name: str) -> Any: + from typing import TypeVar + + if name == "S": + warnings.warn( + "pystac.stac_object.S is deprecated, use `type[STACObject]`", + DeprecationWarning, + ) + return TypeVar("S", bound="STACObject") + raise AttributeError(f"module {__name__} has no attribute {name}") diff --git a/pystac/static/__init__.py b/src/pystac/static/__init__.py similarity index 100% rename from pystac/static/__init__.py rename to src/pystac/static/__init__.py diff --git a/pystac/static/fields-normalized.json b/src/pystac/static/fields-normalized.json similarity index 100% rename from pystac/static/fields-normalized.json rename to src/pystac/static/fields-normalized.json diff --git a/src/pystac/summaries.py b/src/pystac/summaries.py new file mode 100644 index 000000000..ec0b1a11e --- /dev/null +++ b/src/pystac/summaries.py @@ -0,0 +1,262 @@ +from __future__ import annotations + +import copy +import datetime as dt +from collections.abc import Iterable, Iterator, MutableMapping +from enum import Enum +from functools import cache +from typing import TYPE_CHECKING, Any, TypedDict, cast, override + +from .reader import DEFAULT_READER + +if TYPE_CHECKING: + from .collection import Collection + from .item import Item + + +class RangeSummaryDict[T: (int, float, dt.datetime)](TypedDict): + minimum: T + maximum: T + + +class RangeSummary[T: (int, float, dt.datetime)]: + minimum: T + maximum: T + + def __init__(self, minimum: T, maximum: T): + self.minimum = minimum + self.maximum = maximum + + def to_dict(self) -> RangeSummaryDict[T]: + return {"minimum": self.minimum, "maximum": self.maximum} + + def update_with_value(self, v: T) -> None: + self.minimum = min(self.minimum, v) + self.maximum = max(self.maximum, v) + + @classmethod + def from_dict(cls, d: RangeSummaryDict[T]) -> RangeSummary[T]: + minimum: T = d["minimum"] + maximum: T = d["maximum"] + return cls(minimum=minimum, maximum=maximum) + + @override + def __eq__(self, other: object) -> bool: + if not isinstance(other, RangeSummary): + return NotImplemented + + return self.to_dict() == other.to_dict() + + @override + def __repr__(self) -> str: + return self.to_dict().__repr__() + + +@cache +def _get_fields_json(url: str | None) -> dict[str, Any]: + if url is None: + import importlib.resources + import json + + # Every time pystac is released this file gets pulled from + # https://cdn.jsdelivr.net/npm/@radiantearth/stac-fields/fields-normalized.json + jsonfields: dict[str, Any] = json.loads( + importlib.resources.files("pystac.static") + .joinpath("fields-normalized.json") + .read_text() + ) + return jsonfields + return DEFAULT_READER.get_json(url) + + +class SummaryStrategy(Enum): + ARRAY = "v" + RANGE = "r" + SCHEMA = "s" + DONT_SUMMARIZE = False + DEFAULT = True + + @classmethod + def try_from( + cls, strategy: SummaryStrategy | dict[str, Any] | None + ) -> SummaryStrategy: + if isinstance(strategy, SummaryStrategy): + return strategy + + if isinstance(strategy, dict): + strategy_value = strategy.get("summary", True) + if strategy_value in cls: + return SummaryStrategy(strategy_value) + + return SummaryStrategy.DEFAULT + + +class Summarizer: + """The Summarizer computes summaries from values, following the definition of fields + to summarize. + + The fields to summarize can be provided as a JSON file or as a dictionary of + field names and SummaryStrategys. If nothing is provided, a default JSON file + will be used. + + Only fields that are in the Item `properties` can be summarized. + Thus, it is not possible to summarize the top-level fields such as `id` or `assets`. + + For more information about the structure of the fields JSON file, see: + https://github.com/stac-utils/stac-fields + + The default JSON file used is a snapshot of the following file at the time of + the pystac release: + https://cdn.jsdelivr.net/npm/@radiantearth/stac-fields/fields-normalized.json + + Args: + fields: A string containing the path to the JSON file with field descriptions. + Alternatively, a dict with the field names as keys and SummaryStrategys + as values. + If nothing is passed, a default file with field descriptions will be used. + """ + + summaryfields: dict[str, SummaryStrategy] + + def __init__( + self, + fields: str | dict[str, SummaryStrategy | dict[str, Any] | None] | None = None, + ): + json_fields: dict[str, SummaryStrategy | dict[str, Any] | None] + if isinstance(fields, dict): + json_fields = fields + else: + json_fields = _get_fields_json(fields)["metadata"] + + self.summaryfields = {} + for name, desc in json_fields.items(): + strategy = SummaryStrategy.try_from(desc) + if strategy != SummaryStrategy.DONT_SUMMARIZE: + self.summaryfields[name] = strategy + + def _update_with_item(self, summaries: Summaries, item: Item) -> None: + for k, v in item.properties.items(): + if k in self.summaryfields: + strategy = self.summaryfields[k] + summary = summaries.get(k) + if strategy in [ + SummaryStrategy.RANGE, + SummaryStrategy.DEFAULT, + ] and isinstance(v, (int, float, dt.datetime)): + if isinstance(summary, RangeSummary): + summary.update_with_value(v) # pyright: ignore[reportArgumentType] + else: + summaries[k] = RangeSummary(minimum=v, maximum=v) # pyright: ignore[reportArgumentType] + elif strategy in [ + SummaryStrategy.ARRAY, + SummaryStrategy.DEFAULT, + ] and isinstance(v, list): + if summary is None: + summaries[k] = v + elif isinstance(summary, list): + summaries[k] = [ + *summary, + [ + item + for item in cast(list[Any], v) + if item not in summary + ], + ] + else: + if summary is None: + summaries[k] = [v] + elif v not in summary: + summaries[k] = [*summary, v] + + def summarize(self, source: Collection | Iterable[Item]) -> Summaries: + """Creates summaries from items""" + from .collection import Collection + + summaries = Summaries.empty() + if isinstance(source, Collection): + for item in source.get_items(recursive=True): + self._update_with_item(summaries, item) + else: + for item in source: + self._update_with_item(summaries, item) + + return summaries + + +DEFAULT_MAXCOUNT = 25 + + +def _maybe_range(value: Any) -> RangeSummary[Any] | Any: + if isinstance(value, dict) and "minimum" in value: + return RangeSummary[Any](**value) + return cast(Any, value) + + +class Summaries(MutableMapping[str, Any]): + _store: dict[str, Any] + maxcount: int + + def __init__( + self, summaries: dict[str, Any], maxcount: int = DEFAULT_MAXCOUNT + ) -> None: + self._store = {k: _maybe_range(v) for k, v in summaries.items()} + self.maxcount = maxcount + + @classmethod + def try_from(cls, summaries: Summaries | dict[str, Any] | None) -> Summaries: + if isinstance(summaries, Summaries): + return summaries + elif isinstance(summaries, dict): + return Summaries(summaries) + else: + return Summaries({}) + + @override + def __getitem__(self, key: str) -> Any: + return self._store[key] + + @override + def __setitem__(self, name: str, value: Any, /) -> None: + self._store[name] = value + + @override + def __delitem__(self, key: str) -> None: + del self._store[key] + + @override + def __iter__(self) -> Iterator[str]: + return iter(self._store) + + @override + def __len__(self) -> int: + return len(self._store) + + def get_list(self, prop: str) -> list[Any] | None: + value = self._store.get(prop) + if value is not None: + assert isinstance(value, list) + return cast(list[Any], value) + + def get_range(self, prop: str) -> RangeSummary[Any] | None: + value = self._store.get(prop) + if value is not None: + assert isinstance(value, RangeSummary) + return value + + def to_dict(self) -> dict[str, Any]: + out: dict[str, Any] = {} + for k, v in self._store.items(): + if isinstance(v, list) and len(cast(list[Any], v)) >= self.maxcount: + continue + out[k] = v.to_dict() if isinstance(v, RangeSummary) else v + return out + + def is_empty(self) -> bool: + return len(self) == 0 + + @classmethod + def empty(cls) -> Summaries: + return Summaries({}) + + def clone(self) -> Summaries: + return copy.deepcopy(self) diff --git a/pystac/utils.py b/src/pystac/utils.py similarity index 93% rename from pystac/utils.py rename to src/pystac/utils.py index 0bf673585..198d6edb0 100644 --- a/pystac/utils.py +++ b/src/pystac/utils.py @@ -3,11 +3,11 @@ import os import posixpath from collections.abc import Callable -from datetime import datetime, timezone +from datetime import UTC, datetime from enum import Enum from typing import ( Any, - TypeAlias, + Literal, TypeVar, cast, ) @@ -16,8 +16,18 @@ from pystac.errors import RequiredPropertyMissing +from .constants import STAC_OBJECT_TYPE +from .version import __version__ + #: HREF string or path-like object. -HREF: TypeAlias = str | os.PathLike[str] +HREF = str | os.PathLike[str] + + +def get_stac_type(type: STAC_OBJECT_TYPE) -> Literal["Catalog", "Collection", "Item"]: + if type == "Feature": + return "Item" + else: + return type def make_posix_style(href: HREF) -> str: @@ -59,12 +69,9 @@ def safe_urlparse(href: str) -> URLParseResult: return URLParseResult( scheme="", netloc="", - path="{}:{}".format( - # We use this more complicated formulation because parsed.scheme - # converts to lower-case - href[: len(parsed.scheme)], - parsed.path, - ), + # We use this more complicated formulation because parsed.scheme + # converts to lower-case + path=f"{href[: len(parsed.scheme)]}:{parsed.path}", params=parsed.params, query=parsed.query, fragment=parsed.fragment, @@ -404,6 +411,15 @@ def is_absolute_href(href: str, start_href: str | None = None) -> bool: return parsed_start_scheme in ["", "file"] and os.path.isabs(parsed.path) +def get_absolute_href(href: str, start_href: str | None) -> str | None: + if is_absolute_href(href, start_href): + return href + elif start_href: + return make_absolute_href(href, start_href, False) + else: + return None + + def datetime_to_str(dt: datetime, timespec: str = "auto") -> str: """Converts a :class:`datetime.datetime` instance to an ISO8601 string in the `RFC 3339, section 5.6 @@ -421,7 +437,7 @@ def datetime_to_str(dt: datetime, timespec: str = "auto") -> str: str: The ISO8601 (RFC 3339) formatted string representing the datetime. """ if dt.tzinfo is None: - dt = dt.replace(tzinfo=timezone.utc) + dt = dt.replace(tzinfo=UTC) timestamp = dt.isoformat(timespec=timespec) zulu = "+00:00" @@ -448,9 +464,27 @@ def str_to_datetime(s: str) -> datetime: return dateutil.parser.isoparse(s) +def to_datetime_str(value: datetime | str | None) -> str | None: + if value is None: + return None + elif isinstance(value, str): + return value + else: + return datetime_to_str(value) + + +def from_datetime_str(value: datetime | str | None) -> datetime | None: + if value is None: + return None + elif isinstance(value, datetime): + return value + else: + return str_to_datetime(value) + + def now_in_utc() -> datetime: """Returns a datetime value of now with the UTC timezone applied""" - return datetime.now(timezone.utc) + return datetime.now(UTC) def now_to_rfc3339_str() -> str: @@ -625,3 +659,7 @@ def _is_url(href: str) -> bool: """Checks if an HREF is a url rather than a local path""" parsed = safe_urlparse(href) return parsed.scheme not in ["", "file"] + + +def get_user_agent() -> str: + return f"pystac/{__version__}" diff --git a/pystac/validation/__init__.py b/src/pystac/validation/__init__.py similarity index 98% rename from pystac/validation/__init__.py rename to src/pystac/validation/__init__.py index a179976af..f33206079 100644 --- a/pystac/validation/__init__.py +++ b/src/pystac/validation/__init__.py @@ -1,5 +1,12 @@ from __future__ import annotations +import warnings + +warnings.warn( + "pystac.validation` is deprecated. Use `pystac.validator` instead", + DeprecationWarning, +) + from collections.abc import Iterable, Mapping from typing import TYPE_CHECKING, Any, cast diff --git a/pystac/validation/jsonschemas/__init__.py b/src/pystac/validation/jsonschemas/__init__.py similarity index 100% rename from pystac/validation/jsonschemas/__init__.py rename to src/pystac/validation/jsonschemas/__init__.py diff --git a/pystac/validation/jsonschemas/geojson/Feature.json b/src/pystac/validation/jsonschemas/geojson/Feature.json similarity index 100% rename from pystac/validation/jsonschemas/geojson/Feature.json rename to src/pystac/validation/jsonschemas/geojson/Feature.json diff --git a/pystac/validation/jsonschemas/geojson/Geometry.json b/src/pystac/validation/jsonschemas/geojson/Geometry.json similarity index 100% rename from pystac/validation/jsonschemas/geojson/Geometry.json rename to src/pystac/validation/jsonschemas/geojson/Geometry.json diff --git a/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/bands.json b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/bands.json new file mode 100644 index 000000000..94ec087a2 --- /dev/null +++ b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/bands.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.1.0/item-spec/json-schema/bands.json", + "title": "Bands Field", + "type": "object", + "properties": { + "bands": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "allOf": [ + { + "$ref": "common.json" + } + ] + } + } + } +} \ No newline at end of file diff --git a/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/basics.json b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/basics.json new file mode 100644 index 000000000..cc2f49a61 --- /dev/null +++ b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/basics.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.1.0/item-spec/json-schema/basics.json", + "title": "Basic Descriptive Fields", + "type": "object", + "properties": { + "title": { + "title": "Title", + "description": "A human-readable title describing the entity.", + "type": "string" + }, + "description": { + "title": "Description", + "description": "Detailed multi-line description to fully explain the entity.", + "type": "string", + "minLength": 1 + }, + "keywords": { + "title": "Keywords", + "description": "List of keywords describing the entity.", + "type": "array", + "items": { + "type": "string" + } + }, + "roles": { + "title": "Roles", + "type": "array", + "items": { + "type": "string" + } + } + } +} \ No newline at end of file diff --git a/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/catalog.json b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/catalog.json new file mode 100644 index 000000000..ca7efc15e --- /dev/null +++ b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/catalog.json @@ -0,0 +1,57 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.1.0/catalog-spec/json-schema/catalog.json", + "title": "STAC Catalog Specification", + "description": "This object represents Catalogs in a SpatioTemporal Asset Catalog.", + "allOf": [ + { + "$ref": "#/definitions/catalog" + }, + { + "$ref": "../../item-spec/json-schema/common.json" + } + ], + "definitions": { + "catalog": { + "title": "STAC Catalog", + "type": "object", + "$comment": "title and description is validated through the common metadata.", + "required": [ + "stac_version", + "type", + "id", + "description", + "links" + ], + "properties": { + "stac_version": { + "title": "STAC version", + "type": "string", + "const": "1.1.0" + }, + "stac_extensions": { + "title": "STAC extensions", + "type": "array", + "uniqueItems": true, + "items": { + "title": "Reference to a JSON Schema", + "type": "string", + "format": "iri" + } + }, + "type": { + "title": "Type of STAC entity", + "const": "Catalog" + }, + "id": { + "title": "Identifier", + "type": "string", + "minLength": 1 + }, + "links": { + "$ref": "../../item-spec/json-schema/item.json#/definitions/links" + } + } + } + } +} diff --git a/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/collection.json b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/collection.json new file mode 100644 index 000000000..e9ef74311 --- /dev/null +++ b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/collection.json @@ -0,0 +1,230 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.1.0/collection-spec/json-schema/collection.json", + "title": "STAC Collection Specification", + "description": "This object represents Collections in a SpatioTemporal Asset Catalog.", + "allOf": [ + { + "$ref": "#/definitions/collection" + }, + { + "$ref": "../../item-spec/json-schema/common.json" + } + ], + "definitions": { + "collection": { + "title": "STAC Collection", + "description": "These are the fields specific to a STAC Collection.", + "type": "object", + "$comment": "title, description, keywords, providers and license is validated through the common metadata.", + "required": [ + "stac_version", + "type", + "id", + "description", + "license", + "extent", + "links" + ], + "properties": { + "stac_version": { + "title": "STAC version", + "type": "string", + "const": "1.1.0" + }, + "stac_extensions": { + "title": "STAC extensions", + "type": "array", + "uniqueItems": true, + "items": { + "title": "Reference to a JSON Schema", + "type": "string", + "format": "iri" + } + }, + "type": { + "title": "Type of STAC entity", + "const": "Collection" + }, + "id": { + "title": "Identifier", + "type": "string", + "minLength": 1 + }, + "extent": { + "title": "Extents", + "type": "object", + "required": [ + "spatial", + "temporal" + ], + "properties": { + "spatial": { + "title": "Spatial extent object", + "type": "object", + "required": [ + "bbox" + ], + "properties": { + "bbox": { + "title": "Spatial extents", + "type": "array", + "oneOf": [ + { + "minItems": 1, + "maxItems": 1 + }, + { + "minItems": 3 + } + ], + "items": { + "title": "Spatial extent", + "type": "array", + "oneOf": [ + { + "minItems": 4, + "maxItems": 4 + }, + { + "minItems": 6, + "maxItems": 6 + } + ], + "items": { + "type": "number" + } + } + } + } + }, + "temporal": { + "title": "Temporal extent object", + "type": "object", + "required": [ + "interval" + ], + "properties": { + "interval": { + "title": "Temporal extents", + "type": "array", + "minItems": 1, + "items": { + "title": "Temporal extent", + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": [ + "string", + "null" + ], + "format": "date-time", + "pattern": "(\\+00:00|Z)$" + } + } + } + } + } + } + }, + "assets": { + "$ref": "../../item-spec/json-schema/item.json#/definitions/assets" + }, + "item_assets": { + "additionalProperties": { + "allOf": [ + { + "type": "object", + "minProperties": 2, + "properties": { + "href": { + "title": "Disallow href", + "not": {} + }, + "title": { + "title": "Asset title", + "type": "string" + }, + "description": { + "title": "Asset description", + "type": "string" + }, + "type": { + "title": "Asset type", + "type": "string" + }, + "roles": { + "title": "Asset roles", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + { + "$ref": "../../item-spec/json-schema/common.json" + } + ] + } + }, + "links": { + "$ref": "../../item-spec/json-schema/item.json#/definitions/links" + }, + "summaries": { + "$ref": "#/definitions/summaries" + } + } + }, + "summaries": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "title": "JSON Schema", + "type": "object", + "minProperties": 1, + "allOf": [ + { + "$ref": "http://json-schema.org/draft-07/schema" + } + ] + }, + { + "title": "Range", + "type": "object", + "required": [ + "minimum", + "maximum" + ], + "properties": { + "minimum": { + "title": "Minimum value", + "type": [ + "number", + "string" + ] + }, + "maximum": { + "title": "Maximum value", + "type": [ + "number", + "string" + ] + } + } + }, + { + "title": "Set of values", + "type": "array", + "minItems": 1, + "items": { + "description": "For each field only the original data type of the property can occur (except for arrays), but we can't validate that in JSON Schema yet. See the sumamry description in the STAC specification for details." + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/common.json b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/common.json new file mode 100644 index 000000000..2b947b03d --- /dev/null +++ b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/common.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.1.0/item-spec/json-schema/commonjson", + "title": "STAC Common Metadata", + "type": "object", + "description": "This schema includes all common metadata fields.", + "allOf": [ + { + "$ref": "basics.json" + }, + { + "$ref": "bands.json" + }, + { + "$ref": "datetime.json" + }, + { + "$ref": "data-values.json" + }, + { + "$ref": "instrument.json" + }, + { + "$ref": "licensing.json" + }, + { + "$ref": "provider.json" + } + ] +} diff --git a/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/data-values.json b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/data-values.json new file mode 100644 index 000000000..55fea87cf --- /dev/null +++ b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/data-values.json @@ -0,0 +1,84 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.1.0/item-spec/json-schema/data-values.json#", + "title": "Fields related to data values", + "type": "object", + "properties": { + "data_type": { + "title": "Data type of the values", + "type": "string", + "enum": [ + "int8", + "int16", + "int32", + "int64", + "uint8", + "uint16", + "uint32", + "uint64", + "float16", + "float32", + "float64", + "cint16", + "cint32", + "cfloat32", + "cfloat64", + "other" + ] + }, + "nodata": { + "title": "No data value", + "oneOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "nan", + "inf", + "-inf" + ] + } + ] + }, + "statistics": { + "title": "Statistics", + "type": "object", + "minProperties": 1, + "properties": { + "minimum": { + "title": "Minimum value of all the data values", + "type": "number" + }, + "maximum": { + "title": "Maximum value of all the data values", + "type": "number" + }, + "mean": { + "title": "Mean value of all the data values", + "type": "number" + }, + "stddev": { + "title": "Standard deviation value of all the data values", + "type": "number" + }, + "count": { + "title": "Total number of all data values", + "type": "integer", + "minimum": 0 + }, + "valid_percent": { + "title": "Percentage of valid (not nodata) values", + "type": "number", + "minimum": 0, + "maximum": 100 + } + } + }, + "unit": { + "title": "Unit denomination of the data value", + "type": "string" + } + } +} \ No newline at end of file diff --git a/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/datetime.json b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/datetime.json new file mode 100644 index 000000000..b4bc9261f --- /dev/null +++ b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/datetime.json @@ -0,0 +1,53 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.1.0/item-spec/json-schema/datetime.json", + "title": "Date and Time Fields", + "type": "object", + "dependencies": { + "start_datetime": { + "required": [ + "end_datetime" + ] + }, + "end_datetime": { + "required": [ + "start_datetime" + ] + } + }, + "properties": { + "datetime": { + "title": "Date and Time", + "description": "The searchable date/time of the data, in UTC (Formatted in RFC 3339) ", + "type": ["string", "null"], + "format": "date-time", + "pattern": "(\\+00:00|Z)$" + }, + "start_datetime": { + "title": "Start Date and Time", + "description": "The searchable start date/time of the data, in UTC (Formatted in RFC 3339) ", + "type": "string", + "format": "date-time", + "pattern": "(\\+00:00|Z)$" + }, + "end_datetime": { + "title": "End Date and Time", + "description": "The searchable end date/time of the data, in UTC (Formatted in RFC 3339) ", + "type": "string", + "format": "date-time", + "pattern": "(\\+00:00|Z)$" + }, + "created": { + "title": "Creation Time", + "type": "string", + "format": "date-time", + "pattern": "(\\+00:00|Z)$" + }, + "updated": { + "title": "Last Update Time", + "type": "string", + "format": "date-time", + "pattern": "(\\+00:00|Z)$" + } + } +} \ No newline at end of file diff --git a/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/instrument.json b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/instrument.json new file mode 100644 index 000000000..ab9742713 --- /dev/null +++ b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/instrument.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.1.0/item-spec/json-schema/instrument.json", + "title": "Instrument Fields", + "type": "object", + "properties": { + "platform": { + "title": "Platform", + "type": "string" + }, + "instruments": { + "title": "Instruments", + "type": "array", + "items": { + "type": "string" + } + }, + "constellation": { + "title": "Constellation", + "type": "string" + }, + "mission": { + "title": "Mission", + "type": "string" + }, + "gsd": { + "title": "Ground Sample Distance", + "type": "number", + "exclusiveMinimum": 0 + } + } +} \ No newline at end of file diff --git a/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/item.json b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/item.json new file mode 100644 index 000000000..1c8c945b9 --- /dev/null +++ b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/item.json @@ -0,0 +1,347 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.1.0/item-spec/json-schema/item.json", + "title": "STAC Item", + "type": "object", + "description": "This object represents the metadata for an item in a SpatioTemporal Asset Catalog.", + "allOf": [ + { + "$ref": "#/definitions/core" + } + ], + "definitions": { + "core": { + "allOf": [ + { + "$ref": "https://geojson.org/schema/Feature.json" + }, + { + "oneOf": [ + { + "type": "object", + "required": [ + "geometry", + "bbox" + ], + "properties": { + "geometry": { + "$ref": "https://geojson.org/schema/Geometry.json" + }, + "bbox": { + "type": "array", + "oneOf": [ + { + "minItems": 4, + "maxItems": 4 + }, + { + "minItems": 6, + "maxItems": 6 + } + ], + "items": { + "type": "number" + } + } + } + }, + { + "type": "object", + "required": [ + "geometry" + ], + "properties": { + "geometry": { + "type": "null" + }, + "bbox": { + "not": {} + } + } + } + ] + }, + { + "type": "object", + "required": [ + "stac_version", + "id", + "links", + "assets", + "properties" + ], + "properties": { + "stac_version": { + "title": "STAC version", + "type": "string", + "const": "1.1.0" + }, + "stac_extensions": { + "title": "STAC extensions", + "type": "array", + "uniqueItems": true, + "items": { + "title": "Reference to a JSON Schema", + "type": "string", + "format": "iri" + } + }, + "id": { + "title": "Provider ID", + "description": "Provider item ID", + "type": "string", + "minLength": 1 + }, + "links": { + "$ref": "#/definitions/links" + }, + "assets": { + "$ref": "#/definitions/assets" + }, + "properties": { + "allOf": [ + { + "$ref": "common.json" + }, + { + "anyOf": [ + { + "required": [ + "datetime" + ], + "properties": { + "datetime": { + "not": { + "type": "null" + } + } + } + }, + { + "required": [ + "datetime", + "start_datetime", + "end_datetime" + ] + } + ] + } + ] + } + }, + "$comment": "Rules enforcement for STAC Item", + "allOf": [ + { + "if": { + "properties": { + "links": { + "contains": { + "required": [ + "rel" + ], + "properties": { + "rel": { + "const": "collection" + } + } + } + } + } + }, + "then": { + "required": [ + "collection" + ], + "properties": { + "collection": { + "title": "Collection ID", + "description": "The ID of the STAC Collection this Item references to.", + "type": "string", + "minLength": 1 + } + } + }, + "else": { + "properties": { + "collection": { + "not": {} + } + } + } + }, + { + "$comment": "The if-then-else below checks whether the bands field is given in assets or not. If not, allows bands in properties (then), otherwise, disallows bands in properties (else).", + "if": { + "$comment": "If there is no asset with bands...", + "required": [ + "assets" + ], + "properties": { + "assets": { + "type": "object", + "additionalProperties": { + "properties": { + "bands": false + } + } + } + } + }, + "then": { + "$comment": "... then bands are not allowed in properties...", + "properties": { + "properties": { + "properties": { + "bands": false + } + } + } + }, + "else": { + "$comment": "... otherwise bands are allowed in properties.", + "properties": { + "properties": { + "$ref": "bands.json" + } + } + } + } + ] + } + ] + }, + "links": { + "title": "Item links", + "description": "Links to item relations", + "type": "array", + "items": { + "$ref": "#/definitions/link" + } + }, + "link": { + "allOf": [ + { + "type": "object", + "required": [ + "rel", + "href" + ], + "properties": { + "href": { + "title": "Link reference", + "type": "string", + "format": "iri-reference", + "minLength": 1 + }, + "rel": { + "title": "Link relation type", + "type": "string", + "minLength": 1 + }, + "type": { + "title": "Link type", + "type": "string" + }, + "title": { + "title": "Link title", + "type": "string" + }, + "method": { + "title": "Link method", + "type": "string", + "pattern": "^[A-Z]+$", + "default": "GET" + }, + "headers": { + "title": "Link headers", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "body": { + "title": "Link body", + "$comment": "Any type is allowed." + } + }, + "$comment": "Link with relationship `self` must be absolute URI", + "if": { + "properties": { + "rel": { + "const": "self" + } + } + }, + "then": { + "properties": { + "href": { + "format": "iri" + } + } + } + }, + { + "$ref": "common.json" + } + ] + }, + "assets": { + "title": "Asset links", + "description": "Links to assets", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/asset" + } + }, + "asset": { + "allOf": [ + { + "type": "object", + "required": [ + "href" + ], + "properties": { + "href": { + "title": "Asset reference", + "type": "string", + "format": "iri-reference", + "minLength": 1 + }, + "title": { + "title": "Asset title", + "type": "string" + }, + "description": { + "title": "Asset description", + "type": "string" + }, + "type": { + "title": "Asset type", + "type": "string" + }, + "roles": { + "title": "Asset roles", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + { + "$ref": "common.json" + } + ] + } + } +} \ No newline at end of file diff --git a/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/licensing.json b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/licensing.json new file mode 100644 index 000000000..8f650712d --- /dev/null +++ b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/licensing.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.1.0/item-spec/json-schema/licensing.json", + "title": "Licensing Fields", + "type": "object", + "properties": { + "license": { + "type": "string", + "pattern": "^[\\w\\-\\.\\+]+$" + } + } +} \ No newline at end of file diff --git a/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/provider.json b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/provider.json new file mode 100644 index 000000000..92a980406 --- /dev/null +++ b/src/pystac/validation/jsonschemas/stac-spec/v1.1.0/provider.json @@ -0,0 +1,47 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://schemas.stacspec.org/v1.1.0/item-spec/json-schema/provider.json", + "title": "Provider Fields", + "type": "object", + "properties": { + "providers": { + "title": "Providers", + "type": "array", + "items": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "title": "Organization name", + "type": "string", + "minLength": 1 + }, + "description": { + "title": "Organization description", + "type": "string" + }, + "roles": { + "title": "Organization roles", + "type": "array", + "items": { + "type": "string", + "enum": [ + "producer", + "licensor", + "processor", + "host" + ] + } + }, + "url": { + "title": "Organization homepage", + "type": "string", + "format": "iri" + } + } + } + } + } +} \ No newline at end of file diff --git a/pystac/validation/local_validator.py b/src/pystac/validation/local_validator.py similarity index 100% rename from pystac/validation/local_validator.py rename to src/pystac/validation/local_validator.py diff --git a/pystac/validation/schema_uri_map.py b/src/pystac/validation/schema_uri_map.py similarity index 100% rename from pystac/validation/schema_uri_map.py rename to src/pystac/validation/schema_uri_map.py diff --git a/pystac/validation/stac_validator.py b/src/pystac/validation/stac_validator.py similarity index 100% rename from pystac/validation/stac_validator.py rename to src/pystac/validation/stac_validator.py diff --git a/src/pystac/validator.py b/src/pystac/validator.py new file mode 100644 index 000000000..052a2d0e3 --- /dev/null +++ b/src/pystac/validator.py @@ -0,0 +1,11 @@ +from typing import Any, Protocol + +from .constants import STAC_OBJECT_TYPE + + +class Validator(Protocol): + def validate_core( + self, type: STAC_OBJECT_TYPE, version: str, data: dict[str, Any] + ) -> None: ... + + def validate_extension(self, extension: str, data: dict[str, Any]) -> None: ... diff --git a/pystac/version.py b/src/pystac/version.py similarity index 94% rename from pystac/version.py rename to src/pystac/version.py index 8ceb1f479..01d311a5b 100644 --- a/pystac/version.py +++ b/src/pystac/version.py @@ -3,16 +3,16 @@ class STACVersion: - DEFAULT_STAC_VERSION = "1.1.0" + DEFAULT_STAC_VERSION: str = "1.1.0" """Latest STAC version supported by PySTAC""" - DEFAULT_STAC_API_VERSION = "1.0.0" + DEFAULT_STAC_API_VERSION: str = "1.0.0" """Latest STAC API version supported by PySTAC""" # Version that holds a user-set STAC version to use. _override_version: str | None = None - OVERRIDE_VERSION_ENV_VAR = "PYSTAC_STAC_VERSION_OVERRIDE" + OVERRIDE_VERSION_ENV_VAR: str = "PYSTAC_STAC_VERSION_OVERRIDE" @classmethod def get_stac_version(cls) -> str: diff --git a/src/pystac/writer.py b/src/pystac/writer.py new file mode 100644 index 000000000..341c47a70 --- /dev/null +++ b/src/pystac/writer.py @@ -0,0 +1,45 @@ +import json +import os +import urllib.parse +from pathlib import Path +from typing import Any, Protocol + + +class Writer(Protocol): + def put_json(self, data: dict[str, Any], href: str | Path) -> None: ... + def delete(self, href: str | Path) -> None: ... + + +class StandardLibraryWriter: + def put_json(self, data: dict[str, Any], href: str | Path) -> None: + if ( + isinstance(href, Path) + or not (scheme := urllib.parse.urlparse(href).scheme) + or (os.name == "nt" and scheme not in ("http", "https")) + ): + Path(href).parent.mkdir(parents=True, exist_ok=True) + with open(href, "w") as f: + json.dump( + data, + f, + ) + else: + raise ValueError("StandardLibraryWriter cannot write to urls") + + def delete(self, href: str | Path) -> None: + if ( + isinstance(href, Path) + or not (scheme := urllib.parse.urlparse(href).scheme) + or (os.name == "nt" and scheme not in ("http", "https")) + ): + Path(href).unlink() + else: + raise ValueError("StandardLibraryWriter cannot delete urls") + + +def set_default_writer(writer: Writer) -> None: + global DEFAULT_WRITER + DEFAULT_WRITER = writer # pyright: ignore[reportConstantRedefinition] + + +DEFAULT_WRITER = StandardLibraryWriter() diff --git a/tests/cassettes/test_collection/test_collection_from_dict[v1.0.0].yaml b/tests/cassettes/test_collection/test_collection_from_dict[v1.0.0].yaml new file mode 100644 index 000000000..92b20ee3b --- /dev/null +++ b/tests/cassettes/test_collection/test_collection_from_dict[v1.0.0].yaml @@ -0,0 +1,216 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v1.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Items.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"properties\": {\n \"$ref\": + \"#/definitions/fields\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"$comment\": + \"The if-then-else below checks whether the eo:bands is given in assets or + not. If yes, allows eo:bands in properties (else), otherwise, disallows eo:bands + in properties (then).\",\n \"if\": {\n \"required\": [\n + \ \"assets\"\n ],\n \"properties\": {\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"properties\": {\n \"eo:bands\": + false\n }\n }\n }\n }\n + \ },\n \"then\": {\n \"properties\": {\n \"properties\": + {\n \"properties\": {\n \"eo:bands\": false\n + \ }\n }\n }\n },\n \"else\": + {\n \"properties\": {\n \"properties\": {\n \"properties\": + {\n \"eo:bands\": {\n \"$ref\": \"#/definitions/bands\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"eo:cloud_cover\": {\n \"title\": + \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!eo:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"title\": \"Band\",\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"properties\": + {\n \"name\": {\n \"title\": \"Name of the band\",\n \"type\": + \"string\"\n },\n \"common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"red\",\n \"rededge\",\n \"yellow\",\n + \ \"pan\",\n \"nir\",\n \"nir08\",\n + \ \"nir09\",\n \"cirrus\",\n \"swir16\",\n + \ \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"center_wavelength\": + {\n \"title\": \"Center Wavelength\",\n \"type\": \"number\"\n + \ },\n \"full_width_half_max\": {\n \"title\": + \"Full Width Half Max (FWHM)\",\n \"type\": \"number\"\n }\n + \ }\n }\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the + schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Require fields here for item properties.\",\n \"required\": + [\n \"proj:epsg\"\n ]\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"proj:epsg\":{\n + \ \"title\":\"EPSG code\",\n \"type\":[\n \"integer\",\n + \ \"null\"\n ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.2/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_collection/test_collection_from_dict[v1.1.0].yaml b/tests/cassettes/test_collection/test_collection_from_dict[v1.1.0].yaml new file mode 100644 index 000000000..b543e00c0 --- /dev/null +++ b/tests/cassettes/test_collection/test_collection_from_dict[v1.1.0].yaml @@ -0,0 +1,263 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v2.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items and + STAC Collections.\",\n \"type\": \"object\",\n \"required\": [\"stac_extensions\", + \"type\"],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v2.0.0/schema.json\"\n + \ }\n }\n },\n \"allOf\": [\n {\n \"$comment\": \"Items\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/validate_assets\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"required\": + [\"properties\"],\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/require_assets\"\n + \ }\n ]\n }\n },\n {\n \"$comment\": \"Collections\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/validate_bands\"\n },\n + \ {\n \"$ref\": \"#/definitions/validate_assets\"\n },\n + \ {\n \"properties\": {\n \"item_assets\": + {\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/validate_properties\"\n }\n }\n + \ }\n },\n {\n \"properties\": {\n + \ \"summaries\": {\n \"type\": \"object\",\n \"$comment\": + \"We can't properly validate summary objects types (min/max or schemas) yet.\",\n + \ \"allOf\": [\n {\n \"$ref\": + \"#/definitions/validate_bands\"\n },\n {\n + \ \"properties\": {\n \"eo:cloud_cover\": + {\n \"type\": [\"array\", \"object\"],\n \"items\": + {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ }\n }\n }\n + \ },\n {\n \"properties\": + {\n \"eo:snow_cover\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:snow_cover\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:common_name\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:common_name\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:center_wavelength\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:center_wavelength\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:full_width_half_max\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:full_width_half_max\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:solar_illumination\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:solar_illumination\"\n }\n }\n + \ }\n }\n ]\n }\n + \ }\n }\n ],\n \"anyOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_in_bands\"\n },\n {\n + \ \"$ref\": \"#/definitions/require_assets\"\n },\n {\n + \ \"required\": [\"item_assets\"],\n \"properties\": + {\n \"item_assets\": {\n \"$ref\": \"#/definitions/asset_contains\"\n + \ }\n }\n },\n {\n \"required\": + [\"summaries\"],\n \"properties\": {\n \"summaries\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n ]\n }\n }\n ],\n \"definitions\": + {\n \"require_properties\": {\n \"anyOf\": [\n {\n \"$ref\": + \"#/definitions/require_fields\"\n },\n {\n \"$ref\": + \"#/definitions/require_in_bands\"\n }\n ]\n },\n \"validate_bands\": + {\n \"type\": \"object\",\n \"properties\": {\n \"bands\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n \"validate_properties\": + {\n \"allOf\": [\n {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n {\n \"$ref\": \"#/definitions/validate_bands\"\n + \ }\n ]\n },\n \"require_assets\": {\n \"required\": + [\"assets\"],\n \"properties\": {\n \"assets\": {\n \"$ref\": + \"#/definitions/asset_contains\"\n }\n }\n },\n \"validate_assets\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n }\n },\n \"asset_contains\": {\n \"type\": \"object\",\n + \ \"not\": {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n },\n \"require_in_bands\": {\n \"required\": + [\"bands\"],\n \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"contains\": {\n \"$ref\": \"#/definitions/require_fields\"\n + \ }\n }\n }\n },\n \"require_fields\": {\n \"anyOf\": + [\n { \"required\": [\"eo:cloud_cover\"] },\n { \"required\": + [\"eo:snow_cover\"] },\n { \"required\": [\"eo:common_name\"] },\n + \ { \"required\": [\"eo:center_wavelength\"] },\n { \"required\": + [\"eo:full_width_half_max\"] },\n { \"required\": [\"eo:solar_illumination\"] + }\n ]\n },\n \"fields\": {\n \"type\": \"object\",\n \"properties\": + {\n \"eo:cloud_cover\": {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ },\n \"eo:snow_cover\": {\n \"$ref\": \"#/definitions/eo:snow_cover\"\n + \ },\n \"eo:common_name\": {\n \"$ref\": \"#/definitions/eo:common_name\"\n + \ },\n \"eo:center_wavelength\": {\n \"$ref\": \"#/definitions/eo:center_wavelength\"\n + \ },\n \"eo:full_width_half_max\": {\n \"$ref\": \"#/definitions/eo:full_width_half_max\"\n + \ },\n \"eo:solar_illumination\": {\n \"$ref\": \"#/definitions/eo:solar_illumination\"\n + \ }\n },\n \"patternProperties\": {\n \"^(?!eo:)\": + {}\n },\n \"additionalProperties\": false\n },\n \"eo:cloud_cover\": + {\n \"title\": \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:snow_cover\": {\n \"title\": + \"Snow and Ice Cover\",\n \"type\": \"number\",\n \"minimum\": 0,\n + \ \"maximum\": 100\n },\n \"eo:common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"pan\",\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"green05\",\n \"yellow\",\n \"red\",\n \"rededge\",\n + \ \"rededge071\",\n \"rededge075\",\n \"rededge078\",\n + \ \"nir\",\n \"nir08\",\n \"nir09\",\n \"cirrus\",\n + \ \"swir16\",\n \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"eo:center_wavelength\": {\n \"title\": + \"Center Wavelength\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:full_width_half_max\": {\n \"title\": \"Full Width + Half Max (FWHM)\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:solar_illumination\": {\n \"title\": \"Solar Illumination\",\n + \ \"type\": \"number\",\n \"minimum\": 0\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"$comment\": \"This schema succeeds if the proj: fields + are not used at all, please keep this in mind.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"properties\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"properties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n }\n ]\n },\n + \ {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"proj:code\":{\n \"title\":\"Projection + code\",\n \"type\":[\n \"string\",\n \"null\"\n + \ ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.7/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_from_dict[path11].yaml b/tests/cassettes/test_examples/test_example_from_dict[path11].yaml new file mode 100644 index 000000000..5562e8a96 --- /dev/null +++ b/tests/cassettes/test_examples/test_example_from_dict[path11].yaml @@ -0,0 +1,255 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v2.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items and + STAC Collections.\",\n \"type\": \"object\",\n \"required\": [\"stac_extensions\", + \"type\"],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v2.0.0/schema.json\"\n + \ }\n }\n },\n \"allOf\": [\n {\n \"$comment\": \"Items\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/validate_assets\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"required\": + [\"properties\"],\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/require_assets\"\n + \ }\n ]\n }\n },\n {\n \"$comment\": \"Collections\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/validate_bands\"\n },\n + \ {\n \"$ref\": \"#/definitions/validate_assets\"\n },\n + \ {\n \"properties\": {\n \"item_assets\": + {\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/validate_properties\"\n }\n }\n + \ }\n },\n {\n \"properties\": {\n + \ \"summaries\": {\n \"type\": \"object\",\n \"$comment\": + \"We can't properly validate summary objects types (min/max or schemas) yet.\",\n + \ \"allOf\": [\n {\n \"$ref\": + \"#/definitions/validate_bands\"\n },\n {\n + \ \"properties\": {\n \"eo:cloud_cover\": + {\n \"type\": [\"array\", \"object\"],\n \"items\": + {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ }\n }\n }\n + \ },\n {\n \"properties\": + {\n \"eo:snow_cover\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:snow_cover\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:common_name\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:common_name\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:center_wavelength\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:center_wavelength\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:full_width_half_max\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:full_width_half_max\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:solar_illumination\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:solar_illumination\"\n }\n }\n + \ }\n }\n ]\n }\n + \ }\n }\n ],\n \"anyOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_in_bands\"\n },\n {\n + \ \"$ref\": \"#/definitions/require_assets\"\n },\n {\n + \ \"required\": [\"item_assets\"],\n \"properties\": + {\n \"item_assets\": {\n \"$ref\": \"#/definitions/asset_contains\"\n + \ }\n }\n },\n {\n \"required\": + [\"summaries\"],\n \"properties\": {\n \"summaries\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n ]\n }\n }\n ],\n \"definitions\": + {\n \"require_properties\": {\n \"anyOf\": [\n {\n \"$ref\": + \"#/definitions/require_fields\"\n },\n {\n \"$ref\": + \"#/definitions/require_in_bands\"\n }\n ]\n },\n \"validate_bands\": + {\n \"type\": \"object\",\n \"properties\": {\n \"bands\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n \"validate_properties\": + {\n \"allOf\": [\n {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n {\n \"$ref\": \"#/definitions/validate_bands\"\n + \ }\n ]\n },\n \"require_assets\": {\n \"required\": + [\"assets\"],\n \"properties\": {\n \"assets\": {\n \"$ref\": + \"#/definitions/asset_contains\"\n }\n }\n },\n \"validate_assets\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n }\n },\n \"asset_contains\": {\n \"type\": \"object\",\n + \ \"not\": {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n },\n \"require_in_bands\": {\n \"required\": + [\"bands\"],\n \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"contains\": {\n \"$ref\": \"#/definitions/require_fields\"\n + \ }\n }\n }\n },\n \"require_fields\": {\n \"anyOf\": + [\n { \"required\": [\"eo:cloud_cover\"] },\n { \"required\": + [\"eo:snow_cover\"] },\n { \"required\": [\"eo:common_name\"] },\n + \ { \"required\": [\"eo:center_wavelength\"] },\n { \"required\": + [\"eo:full_width_half_max\"] },\n { \"required\": [\"eo:solar_illumination\"] + }\n ]\n },\n \"fields\": {\n \"type\": \"object\",\n \"properties\": + {\n \"eo:cloud_cover\": {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ },\n \"eo:snow_cover\": {\n \"$ref\": \"#/definitions/eo:snow_cover\"\n + \ },\n \"eo:common_name\": {\n \"$ref\": \"#/definitions/eo:common_name\"\n + \ },\n \"eo:center_wavelength\": {\n \"$ref\": \"#/definitions/eo:center_wavelength\"\n + \ },\n \"eo:full_width_half_max\": {\n \"$ref\": \"#/definitions/eo:full_width_half_max\"\n + \ },\n \"eo:solar_illumination\": {\n \"$ref\": \"#/definitions/eo:solar_illumination\"\n + \ }\n },\n \"patternProperties\": {\n \"^(?!eo:)\": + {}\n },\n \"additionalProperties\": false\n },\n \"eo:cloud_cover\": + {\n \"title\": \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:snow_cover\": {\n \"title\": + \"Snow and Ice Cover\",\n \"type\": \"number\",\n \"minimum\": 0,\n + \ \"maximum\": 100\n },\n \"eo:common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"pan\",\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"green05\",\n \"yellow\",\n \"red\",\n \"rededge\",\n + \ \"rededge071\",\n \"rededge075\",\n \"rededge078\",\n + \ \"nir\",\n \"nir08\",\n \"nir09\",\n \"cirrus\",\n + \ \"swir16\",\n \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"eo:center_wavelength\": {\n \"title\": + \"Center Wavelength\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:full_width_half_max\": {\n \"title\": \"Full Width + Half Max (FWHM)\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:solar_illumination\": {\n \"title\": \"Solar Illumination\",\n + \ \"type\": \"number\",\n \"minimum\": 0\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/sat/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/sat/v1.0.0/schema.json\",\n \"title\": + \"Satellite Extension\",\n \"description\": \"STAC Sat Extension to a STAC + Item.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC Items.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\n \"required\": [\n \"sat:platform_international_designator\"\n + \ ]\n },\n {\n \"required\": + [\n \"sat:orbit_state\"\n ]\n + \ },\n {\n \"required\": + [\n \"sat:absolute_orbit\"\n ]\n + \ },\n {\n \"required\": + [\n \"sat:relative_orbit\"\n ]\n + \ },\n {\n \"required\": + [\n \"sat:anx_datetime\"\n ]\n + \ }\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/sat/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"sat:platform_international_designator\": + {\n \"type\": \"string\"\n },\n \"sat:orbit_state\": + {\n \"title\": \"Orbit State\",\n \"type\": \"string\",\n + \ \"enum\": [\n \"ascending\",\n \"descending\",\n + \ \"geostationary\"\n ]\n },\n \"sat:absolute_orbit\": + {\n \"type\": \"integer\",\n \"minimum\": 1\n },\n + \ \"sat:relative_orbit\": {\n \"type\": \"integer\",\n \"minimum\": + 1\n },\n \"sat:anx_datetime\": {\n \"type\": \"string\",\n + \ \"format\": \"date-time\"\n }\n },\n \"patternProperties\": + {\n \"^(?!sat:)\": {\n \"$comment\": \"Do not allow unspecified + fields prefixed with sat:\"\n }\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_from_dict[path12].yaml b/tests/cassettes/test_examples/test_example_from_dict[path12].yaml new file mode 100644 index 000000000..b543e00c0 --- /dev/null +++ b/tests/cassettes/test_examples/test_example_from_dict[path12].yaml @@ -0,0 +1,263 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v2.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items and + STAC Collections.\",\n \"type\": \"object\",\n \"required\": [\"stac_extensions\", + \"type\"],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v2.0.0/schema.json\"\n + \ }\n }\n },\n \"allOf\": [\n {\n \"$comment\": \"Items\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/validate_assets\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"required\": + [\"properties\"],\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/require_assets\"\n + \ }\n ]\n }\n },\n {\n \"$comment\": \"Collections\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/validate_bands\"\n },\n + \ {\n \"$ref\": \"#/definitions/validate_assets\"\n },\n + \ {\n \"properties\": {\n \"item_assets\": + {\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/validate_properties\"\n }\n }\n + \ }\n },\n {\n \"properties\": {\n + \ \"summaries\": {\n \"type\": \"object\",\n \"$comment\": + \"We can't properly validate summary objects types (min/max or schemas) yet.\",\n + \ \"allOf\": [\n {\n \"$ref\": + \"#/definitions/validate_bands\"\n },\n {\n + \ \"properties\": {\n \"eo:cloud_cover\": + {\n \"type\": [\"array\", \"object\"],\n \"items\": + {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ }\n }\n }\n + \ },\n {\n \"properties\": + {\n \"eo:snow_cover\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:snow_cover\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:common_name\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:common_name\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:center_wavelength\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:center_wavelength\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:full_width_half_max\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:full_width_half_max\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:solar_illumination\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:solar_illumination\"\n }\n }\n + \ }\n }\n ]\n }\n + \ }\n }\n ],\n \"anyOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_in_bands\"\n },\n {\n + \ \"$ref\": \"#/definitions/require_assets\"\n },\n {\n + \ \"required\": [\"item_assets\"],\n \"properties\": + {\n \"item_assets\": {\n \"$ref\": \"#/definitions/asset_contains\"\n + \ }\n }\n },\n {\n \"required\": + [\"summaries\"],\n \"properties\": {\n \"summaries\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n ]\n }\n }\n ],\n \"definitions\": + {\n \"require_properties\": {\n \"anyOf\": [\n {\n \"$ref\": + \"#/definitions/require_fields\"\n },\n {\n \"$ref\": + \"#/definitions/require_in_bands\"\n }\n ]\n },\n \"validate_bands\": + {\n \"type\": \"object\",\n \"properties\": {\n \"bands\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n \"validate_properties\": + {\n \"allOf\": [\n {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n {\n \"$ref\": \"#/definitions/validate_bands\"\n + \ }\n ]\n },\n \"require_assets\": {\n \"required\": + [\"assets\"],\n \"properties\": {\n \"assets\": {\n \"$ref\": + \"#/definitions/asset_contains\"\n }\n }\n },\n \"validate_assets\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n }\n },\n \"asset_contains\": {\n \"type\": \"object\",\n + \ \"not\": {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n },\n \"require_in_bands\": {\n \"required\": + [\"bands\"],\n \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"contains\": {\n \"$ref\": \"#/definitions/require_fields\"\n + \ }\n }\n }\n },\n \"require_fields\": {\n \"anyOf\": + [\n { \"required\": [\"eo:cloud_cover\"] },\n { \"required\": + [\"eo:snow_cover\"] },\n { \"required\": [\"eo:common_name\"] },\n + \ { \"required\": [\"eo:center_wavelength\"] },\n { \"required\": + [\"eo:full_width_half_max\"] },\n { \"required\": [\"eo:solar_illumination\"] + }\n ]\n },\n \"fields\": {\n \"type\": \"object\",\n \"properties\": + {\n \"eo:cloud_cover\": {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ },\n \"eo:snow_cover\": {\n \"$ref\": \"#/definitions/eo:snow_cover\"\n + \ },\n \"eo:common_name\": {\n \"$ref\": \"#/definitions/eo:common_name\"\n + \ },\n \"eo:center_wavelength\": {\n \"$ref\": \"#/definitions/eo:center_wavelength\"\n + \ },\n \"eo:full_width_half_max\": {\n \"$ref\": \"#/definitions/eo:full_width_half_max\"\n + \ },\n \"eo:solar_illumination\": {\n \"$ref\": \"#/definitions/eo:solar_illumination\"\n + \ }\n },\n \"patternProperties\": {\n \"^(?!eo:)\": + {}\n },\n \"additionalProperties\": false\n },\n \"eo:cloud_cover\": + {\n \"title\": \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:snow_cover\": {\n \"title\": + \"Snow and Ice Cover\",\n \"type\": \"number\",\n \"minimum\": 0,\n + \ \"maximum\": 100\n },\n \"eo:common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"pan\",\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"green05\",\n \"yellow\",\n \"red\",\n \"rededge\",\n + \ \"rededge071\",\n \"rededge075\",\n \"rededge078\",\n + \ \"nir\",\n \"nir08\",\n \"nir09\",\n \"cirrus\",\n + \ \"swir16\",\n \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"eo:center_wavelength\": {\n \"title\": + \"Center Wavelength\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:full_width_half_max\": {\n \"title\": \"Full Width + Half Max (FWHM)\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:solar_illumination\": {\n \"title\": \"Solar Illumination\",\n + \ \"type\": \"number\",\n \"minimum\": 0\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"$comment\": \"This schema succeeds if the proj: fields + are not used at all, please keep this in mind.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"properties\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"properties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n }\n ]\n },\n + \ {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"proj:code\":{\n \"title\":\"Projection + code\",\n \"type\":[\n \"string\",\n \"null\"\n + \ ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.7/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_from_dict[path13].yaml b/tests/cassettes/test_examples/test_example_from_dict[path13].yaml new file mode 100644 index 000000000..b543e00c0 --- /dev/null +++ b/tests/cassettes/test_examples/test_example_from_dict[path13].yaml @@ -0,0 +1,263 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v2.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items and + STAC Collections.\",\n \"type\": \"object\",\n \"required\": [\"stac_extensions\", + \"type\"],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v2.0.0/schema.json\"\n + \ }\n }\n },\n \"allOf\": [\n {\n \"$comment\": \"Items\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/validate_assets\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"required\": + [\"properties\"],\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/require_assets\"\n + \ }\n ]\n }\n },\n {\n \"$comment\": \"Collections\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/validate_bands\"\n },\n + \ {\n \"$ref\": \"#/definitions/validate_assets\"\n },\n + \ {\n \"properties\": {\n \"item_assets\": + {\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/validate_properties\"\n }\n }\n + \ }\n },\n {\n \"properties\": {\n + \ \"summaries\": {\n \"type\": \"object\",\n \"$comment\": + \"We can't properly validate summary objects types (min/max or schemas) yet.\",\n + \ \"allOf\": [\n {\n \"$ref\": + \"#/definitions/validate_bands\"\n },\n {\n + \ \"properties\": {\n \"eo:cloud_cover\": + {\n \"type\": [\"array\", \"object\"],\n \"items\": + {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ }\n }\n }\n + \ },\n {\n \"properties\": + {\n \"eo:snow_cover\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:snow_cover\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:common_name\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:common_name\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:center_wavelength\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:center_wavelength\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:full_width_half_max\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:full_width_half_max\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:solar_illumination\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:solar_illumination\"\n }\n }\n + \ }\n }\n ]\n }\n + \ }\n }\n ],\n \"anyOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_in_bands\"\n },\n {\n + \ \"$ref\": \"#/definitions/require_assets\"\n },\n {\n + \ \"required\": [\"item_assets\"],\n \"properties\": + {\n \"item_assets\": {\n \"$ref\": \"#/definitions/asset_contains\"\n + \ }\n }\n },\n {\n \"required\": + [\"summaries\"],\n \"properties\": {\n \"summaries\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n ]\n }\n }\n ],\n \"definitions\": + {\n \"require_properties\": {\n \"anyOf\": [\n {\n \"$ref\": + \"#/definitions/require_fields\"\n },\n {\n \"$ref\": + \"#/definitions/require_in_bands\"\n }\n ]\n },\n \"validate_bands\": + {\n \"type\": \"object\",\n \"properties\": {\n \"bands\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n \"validate_properties\": + {\n \"allOf\": [\n {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n {\n \"$ref\": \"#/definitions/validate_bands\"\n + \ }\n ]\n },\n \"require_assets\": {\n \"required\": + [\"assets\"],\n \"properties\": {\n \"assets\": {\n \"$ref\": + \"#/definitions/asset_contains\"\n }\n }\n },\n \"validate_assets\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n }\n },\n \"asset_contains\": {\n \"type\": \"object\",\n + \ \"not\": {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n },\n \"require_in_bands\": {\n \"required\": + [\"bands\"],\n \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"contains\": {\n \"$ref\": \"#/definitions/require_fields\"\n + \ }\n }\n }\n },\n \"require_fields\": {\n \"anyOf\": + [\n { \"required\": [\"eo:cloud_cover\"] },\n { \"required\": + [\"eo:snow_cover\"] },\n { \"required\": [\"eo:common_name\"] },\n + \ { \"required\": [\"eo:center_wavelength\"] },\n { \"required\": + [\"eo:full_width_half_max\"] },\n { \"required\": [\"eo:solar_illumination\"] + }\n ]\n },\n \"fields\": {\n \"type\": \"object\",\n \"properties\": + {\n \"eo:cloud_cover\": {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ },\n \"eo:snow_cover\": {\n \"$ref\": \"#/definitions/eo:snow_cover\"\n + \ },\n \"eo:common_name\": {\n \"$ref\": \"#/definitions/eo:common_name\"\n + \ },\n \"eo:center_wavelength\": {\n \"$ref\": \"#/definitions/eo:center_wavelength\"\n + \ },\n \"eo:full_width_half_max\": {\n \"$ref\": \"#/definitions/eo:full_width_half_max\"\n + \ },\n \"eo:solar_illumination\": {\n \"$ref\": \"#/definitions/eo:solar_illumination\"\n + \ }\n },\n \"patternProperties\": {\n \"^(?!eo:)\": + {}\n },\n \"additionalProperties\": false\n },\n \"eo:cloud_cover\": + {\n \"title\": \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:snow_cover\": {\n \"title\": + \"Snow and Ice Cover\",\n \"type\": \"number\",\n \"minimum\": 0,\n + \ \"maximum\": 100\n },\n \"eo:common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"pan\",\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"green05\",\n \"yellow\",\n \"red\",\n \"rededge\",\n + \ \"rededge071\",\n \"rededge075\",\n \"rededge078\",\n + \ \"nir\",\n \"nir08\",\n \"nir09\",\n \"cirrus\",\n + \ \"swir16\",\n \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"eo:center_wavelength\": {\n \"title\": + \"Center Wavelength\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:full_width_half_max\": {\n \"title\": \"Full Width + Half Max (FWHM)\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:solar_illumination\": {\n \"title\": \"Solar Illumination\",\n + \ \"type\": \"number\",\n \"minimum\": 0\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"$comment\": \"This schema succeeds if the proj: fields + are not used at all, please keep this in mind.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"properties\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"properties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n }\n ]\n },\n + \ {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"proj:code\":{\n \"title\":\"Projection + code\",\n \"type\":[\n \"string\",\n \"null\"\n + \ ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.7/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_from_dict[path14].yaml b/tests/cassettes/test_examples/test_example_from_dict[path14].yaml new file mode 100644 index 000000000..c6a6f459c --- /dev/null +++ b/tests/cassettes/test_examples/test_example_from_dict[path14].yaml @@ -0,0 +1,64 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_from_dict[path16].yaml b/tests/cassettes/test_examples/test_example_from_dict[path16].yaml new file mode 100644 index 000000000..7f15ee3df --- /dev/null +++ b/tests/cassettes/test_examples/test_example_from_dict[path16].yaml @@ -0,0 +1,422 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v2.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items and + STAC Collections.\",\n \"type\": \"object\",\n \"required\": [\"stac_extensions\", + \"type\"],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v2.0.0/schema.json\"\n + \ }\n }\n },\n \"allOf\": [\n {\n \"$comment\": \"Items\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/validate_assets\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"required\": + [\"properties\"],\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/require_assets\"\n + \ }\n ]\n }\n },\n {\n \"$comment\": \"Collections\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/validate_bands\"\n },\n + \ {\n \"$ref\": \"#/definitions/validate_assets\"\n },\n + \ {\n \"properties\": {\n \"item_assets\": + {\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/validate_properties\"\n }\n }\n + \ }\n },\n {\n \"properties\": {\n + \ \"summaries\": {\n \"type\": \"object\",\n \"$comment\": + \"We can't properly validate summary objects types (min/max or schemas) yet.\",\n + \ \"allOf\": [\n {\n \"$ref\": + \"#/definitions/validate_bands\"\n },\n {\n + \ \"properties\": {\n \"eo:cloud_cover\": + {\n \"type\": [\"array\", \"object\"],\n \"items\": + {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ }\n }\n }\n + \ },\n {\n \"properties\": + {\n \"eo:snow_cover\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:snow_cover\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:common_name\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:common_name\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:center_wavelength\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:center_wavelength\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:full_width_half_max\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:full_width_half_max\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:solar_illumination\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:solar_illumination\"\n }\n }\n + \ }\n }\n ]\n }\n + \ }\n }\n ],\n \"anyOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_in_bands\"\n },\n {\n + \ \"$ref\": \"#/definitions/require_assets\"\n },\n {\n + \ \"required\": [\"item_assets\"],\n \"properties\": + {\n \"item_assets\": {\n \"$ref\": \"#/definitions/asset_contains\"\n + \ }\n }\n },\n {\n \"required\": + [\"summaries\"],\n \"properties\": {\n \"summaries\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n ]\n }\n }\n ],\n \"definitions\": + {\n \"require_properties\": {\n \"anyOf\": [\n {\n \"$ref\": + \"#/definitions/require_fields\"\n },\n {\n \"$ref\": + \"#/definitions/require_in_bands\"\n }\n ]\n },\n \"validate_bands\": + {\n \"type\": \"object\",\n \"properties\": {\n \"bands\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n \"validate_properties\": + {\n \"allOf\": [\n {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n {\n \"$ref\": \"#/definitions/validate_bands\"\n + \ }\n ]\n },\n \"require_assets\": {\n \"required\": + [\"assets\"],\n \"properties\": {\n \"assets\": {\n \"$ref\": + \"#/definitions/asset_contains\"\n }\n }\n },\n \"validate_assets\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n }\n },\n \"asset_contains\": {\n \"type\": \"object\",\n + \ \"not\": {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n },\n \"require_in_bands\": {\n \"required\": + [\"bands\"],\n \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"contains\": {\n \"$ref\": \"#/definitions/require_fields\"\n + \ }\n }\n }\n },\n \"require_fields\": {\n \"anyOf\": + [\n { \"required\": [\"eo:cloud_cover\"] },\n { \"required\": + [\"eo:snow_cover\"] },\n { \"required\": [\"eo:common_name\"] },\n + \ { \"required\": [\"eo:center_wavelength\"] },\n { \"required\": + [\"eo:full_width_half_max\"] },\n { \"required\": [\"eo:solar_illumination\"] + }\n ]\n },\n \"fields\": {\n \"type\": \"object\",\n \"properties\": + {\n \"eo:cloud_cover\": {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ },\n \"eo:snow_cover\": {\n \"$ref\": \"#/definitions/eo:snow_cover\"\n + \ },\n \"eo:common_name\": {\n \"$ref\": \"#/definitions/eo:common_name\"\n + \ },\n \"eo:center_wavelength\": {\n \"$ref\": \"#/definitions/eo:center_wavelength\"\n + \ },\n \"eo:full_width_half_max\": {\n \"$ref\": \"#/definitions/eo:full_width_half_max\"\n + \ },\n \"eo:solar_illumination\": {\n \"$ref\": \"#/definitions/eo:solar_illumination\"\n + \ }\n },\n \"patternProperties\": {\n \"^(?!eo:)\": + {}\n },\n \"additionalProperties\": false\n },\n \"eo:cloud_cover\": + {\n \"title\": \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:snow_cover\": {\n \"title\": + \"Snow and Ice Cover\",\n \"type\": \"number\",\n \"minimum\": 0,\n + \ \"maximum\": 100\n },\n \"eo:common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"pan\",\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"green05\",\n \"yellow\",\n \"red\",\n \"rededge\",\n + \ \"rededge071\",\n \"rededge075\",\n \"rededge078\",\n + \ \"nir\",\n \"nir08\",\n \"nir09\",\n \"cirrus\",\n + \ \"swir16\",\n \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"eo:center_wavelength\": {\n \"title\": + \"Center Wavelength\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:full_width_half_max\": {\n \"title\": \"Full Width + Half Max (FWHM)\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:solar_illumination\": {\n \"title\": \"Solar Illumination\",\n + \ \"type\": \"number\",\n \"minimum\": 0\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"$comment\": \"This schema succeeds if the proj: fields + are not used at all, please keep this in mind.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"properties\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"properties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n }\n ]\n },\n + \ {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"proj:code\":{\n \"title\":\"Projection + code\",\n \"type\":[\n \"string\",\n \"null\"\n + \ ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.7/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/scientific/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/scientific/v1.0.0/schema.json#\",\n \"title\": + \"Scientific Citation Extension\",\n \"description\": \"Scientific Citation + Extension for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items. Remove this object + if this extension only applies to Collections.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/stac_extensions\"\n },\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"properties\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"properties\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"$comment\": \"This validates the fields in Item Assets, + but does not require them.\",\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n }\n ]\n },\n {\n \"$comment\": \"This + is the schema for STAC Collections.\",\n \"type\": \"object\",\n \"allOf\": + [\n {\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n {\n \"$ref\": + \"#/definitions/stac_extensions\"\n }\n ],\n \"anyOf\": [\n + \ {\n \"$comment\": \"This is the schema for the top-level + fields in a Collection. Remove this if this extension does not define top-level + fields for Collections.\",\n \"allOf\": [\n {\n \"$ref\": + \"#/definitions/require_any_field\"\n },\n {\n \"$ref\": + \"#/definitions/fields\"\n }\n ]\n },\n {\n + \ \"$comment\": \"This validates the fields in Collection Assets, + but does not require them.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"properties\": {\n \"assets\": {\n \"type\": + \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n }\n }\n + \ }\n }\n }\n },\n {\n \"$comment\": + \"This is the schema for the fields in Item Asset Definitions. It doesn't + require any fields.\",\n \"required\": [\n \"item_assets\"\n + \ ],\n \"properties\": {\n \"item_assets\": {\n + \ \"type\": \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n }\n }\n + \ }\n }\n }\n },\n {\n \"$comment\": + \"This is the schema for the fields in Summaries. By default, only checks + the existance of the properties, but not the schema of the summaries.\",\n + \ \"required\": [\n \"summaries\"\n ],\n \"properties\": + {\n \"summaries\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/scientific/v1.0.0/schema.json\"\n }\n + \ }\n }\n },\n \"require_any_field\": {\n \"$comment\": + \"Please list all fields here so that we can force the existance of one of + them in other parts of the schemas.\",\n \"anyOf\": [\n {\"required\": + [\"sci:doi\"]},\n {\"required\": [\"sci:citation\"]},\n {\"required\": + [\"sci:publications\"]}\n ]\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"sci:doi\": + {\n \"type\": \"string\",\n \"title\": \"Data DOI\",\n \"pattern\": + \"^10\\\\.[0-9a-zA-Z]{4,}/[^\\\\s]+$\"\n }, \n \"sci:citation\": + {\n \"type\": \"string\", \n \"title\": \"Proposed Data + Citation\"\n },\n \"sci:publications\": {\n \"type\": + \"array\",\n \"title\": \"Publications\",\n \"items\": {\n + \ \"type\": \"object\",\n \"properties\": {\n \"doi\": + {\n \"type\": \"string\",\n \"title\": \"Publication + DOI\",\n \"pattern\": \"^10\\\\.[0-9a-zA-Z]{4,}/[^\\\\s]+$\"\n + \ }, \n \"citation\": { \n \"type\": + \"string\", \n \"title\": \"Publication Citation\"\n }\n + \ }\n }\n }\n },\n \"patternProperties\": + {\n \"^(?!sci:)\": {}\n },\n \"additionalProperties\": false\n + \ }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/remote-data/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/remote-data/v1.0.0/schema.json\",\n \"title\": + \"Remote Data (Example) Extension\",\n \"description\": \"STAC Example Extension + for fictional vendor Remote Data\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Required fields here for item properties.\",\n \"required\": + [\n \"rd:type\",\n \"rd:product_level\",\n + \ \"rd:sat_id\"\n ]\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"rd:visibility\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$comment\": \"Remove this + and the following object if this is not an extension to a Collection.\",\n + \ \"$ref\": \"#/definitions/stac_extensions\"\n },\n {\n + \ \"$ref\": \"#/definitions/collection_fields\"\n }\n ]\n + \ }\n ],\n \"definitions\": {\n \"stac_extensions\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/remote-data/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"collection_fields\": {\n \"properties\": + {\n \"rd:visibility\": {\n \"type\": \"string\",\n \"enum\": + [\n \"public\",\n \"protected\",\n \"private\"\n + \ ]\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Remote Data fictional fields.\",\n \"type\": \"object\",\n \"properties\": + {\n \"rd:type\": {\n \"type\": \"string\",\n \"enum\": + [\n \"scene\"\n ]\n },\n \"rd:product_level\": + {\n \"type\": \"string\",\n \"enum\": [\n \"LV1A\",\n + \ \"LV1B\",\n \"LV2A\",\n \"LV2B\",\n \"LV3A\",\n + \ \"LV3B\"\n ]\n }, \n \"rd:runs\": {\n \"type\": + \"array\", \n \"items\": {\n \"type\": \"string\"\n }\n + \ },\n \"rd:parsecs\": {\n \"type\": \"array\", \n \"items\": + {\n \"type\": \"number\"\n }\n },\n \"rd:anomalous_pixels\": + {\n \"type\": \"number\"\n },\n \"rd:sat_id\": {\n + \ \"type\": \"string\"\n },\n \"rd:earth_sun_distance\": + {\n \"type\": \"number\"\n },\n \"rd:flux_capacitor\": + {\n \"type\": \"boolean\"\n }\n },\n \"patternProperties\": + {\n \"^(?!rd:)\": {\n \"$comment\": \"Disallow other fields + with rd: prefix\"\n }\n },\n \"additionalProperties\": false\n + \ }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_from_dict[path18].yaml b/tests/cassettes/test_examples/test_example_from_dict[path18].yaml new file mode 100644 index 000000000..41db0987c --- /dev/null +++ b/tests/cassettes/test_examples/test_example_from_dict[path18].yaml @@ -0,0 +1,754 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v2.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items and + STAC Collections.\",\n \"type\": \"object\",\n \"required\": [\"stac_extensions\", + \"type\"],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v2.0.0/schema.json\"\n + \ }\n }\n },\n \"allOf\": [\n {\n \"$comment\": \"Items\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/validate_assets\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"required\": + [\"properties\"],\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/require_assets\"\n + \ }\n ]\n }\n },\n {\n \"$comment\": \"Collections\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/validate_bands\"\n },\n + \ {\n \"$ref\": \"#/definitions/validate_assets\"\n },\n + \ {\n \"properties\": {\n \"item_assets\": + {\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/validate_properties\"\n }\n }\n + \ }\n },\n {\n \"properties\": {\n + \ \"summaries\": {\n \"type\": \"object\",\n \"$comment\": + \"We can't properly validate summary objects types (min/max or schemas) yet.\",\n + \ \"allOf\": [\n {\n \"$ref\": + \"#/definitions/validate_bands\"\n },\n {\n + \ \"properties\": {\n \"eo:cloud_cover\": + {\n \"type\": [\"array\", \"object\"],\n \"items\": + {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ }\n }\n }\n + \ },\n {\n \"properties\": + {\n \"eo:snow_cover\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:snow_cover\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:common_name\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:common_name\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:center_wavelength\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:center_wavelength\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:full_width_half_max\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:full_width_half_max\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:solar_illumination\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:solar_illumination\"\n }\n }\n + \ }\n }\n ]\n }\n + \ }\n }\n ],\n \"anyOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_in_bands\"\n },\n {\n + \ \"$ref\": \"#/definitions/require_assets\"\n },\n {\n + \ \"required\": [\"item_assets\"],\n \"properties\": + {\n \"item_assets\": {\n \"$ref\": \"#/definitions/asset_contains\"\n + \ }\n }\n },\n {\n \"required\": + [\"summaries\"],\n \"properties\": {\n \"summaries\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n ]\n }\n }\n ],\n \"definitions\": + {\n \"require_properties\": {\n \"anyOf\": [\n {\n \"$ref\": + \"#/definitions/require_fields\"\n },\n {\n \"$ref\": + \"#/definitions/require_in_bands\"\n }\n ]\n },\n \"validate_bands\": + {\n \"type\": \"object\",\n \"properties\": {\n \"bands\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n \"validate_properties\": + {\n \"allOf\": [\n {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n {\n \"$ref\": \"#/definitions/validate_bands\"\n + \ }\n ]\n },\n \"require_assets\": {\n \"required\": + [\"assets\"],\n \"properties\": {\n \"assets\": {\n \"$ref\": + \"#/definitions/asset_contains\"\n }\n }\n },\n \"validate_assets\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n }\n },\n \"asset_contains\": {\n \"type\": \"object\",\n + \ \"not\": {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n },\n \"require_in_bands\": {\n \"required\": + [\"bands\"],\n \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"contains\": {\n \"$ref\": \"#/definitions/require_fields\"\n + \ }\n }\n }\n },\n \"require_fields\": {\n \"anyOf\": + [\n { \"required\": [\"eo:cloud_cover\"] },\n { \"required\": + [\"eo:snow_cover\"] },\n { \"required\": [\"eo:common_name\"] },\n + \ { \"required\": [\"eo:center_wavelength\"] },\n { \"required\": + [\"eo:full_width_half_max\"] },\n { \"required\": [\"eo:solar_illumination\"] + }\n ]\n },\n \"fields\": {\n \"type\": \"object\",\n \"properties\": + {\n \"eo:cloud_cover\": {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ },\n \"eo:snow_cover\": {\n \"$ref\": \"#/definitions/eo:snow_cover\"\n + \ },\n \"eo:common_name\": {\n \"$ref\": \"#/definitions/eo:common_name\"\n + \ },\n \"eo:center_wavelength\": {\n \"$ref\": \"#/definitions/eo:center_wavelength\"\n + \ },\n \"eo:full_width_half_max\": {\n \"$ref\": \"#/definitions/eo:full_width_half_max\"\n + \ },\n \"eo:solar_illumination\": {\n \"$ref\": \"#/definitions/eo:solar_illumination\"\n + \ }\n },\n \"patternProperties\": {\n \"^(?!eo:)\": + {}\n },\n \"additionalProperties\": false\n },\n \"eo:cloud_cover\": + {\n \"title\": \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:snow_cover\": {\n \"title\": + \"Snow and Ice Cover\",\n \"type\": \"number\",\n \"minimum\": 0,\n + \ \"maximum\": 100\n },\n \"eo:common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"pan\",\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"green05\",\n \"yellow\",\n \"red\",\n \"rededge\",\n + \ \"rededge071\",\n \"rededge075\",\n \"rededge078\",\n + \ \"nir\",\n \"nir08\",\n \"nir09\",\n \"cirrus\",\n + \ \"swir16\",\n \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"eo:center_wavelength\": {\n \"title\": + \"Center Wavelength\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:full_width_half_max\": {\n \"title\": \"Full Width + Half Max (FWHM)\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:solar_illumination\": {\n \"title\": \"Solar Illumination\",\n + \ \"type\": \"number\",\n \"minimum\": 0\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"$comment\": \"This schema succeeds if the proj: fields + are not used at all, please keep this in mind.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"properties\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"properties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n }\n ]\n },\n + \ {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"proj:code\":{\n \"title\":\"Projection + code\",\n \"type\":[\n \"string\",\n \"null\"\n + \ ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.7/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - proj.org + method: GET + uri: https://proj.org/schemas/v0.7/projjson.schema.json + response: + body: + string: '' + headers: + Location: + - https://proj.org/en/latest/schemas/v0.7/projjson.schema.json + status: + code: 302 + message: Found +- request: + body: null + headers: + Connection: + - close + Host: + - proj.org + method: GET + uri: https://proj.org/en/latest/schemas/v0.7/projjson.schema.json + response: + body: + string: "{\n \"$id\": \"https://proj.org/schemas/v0.7/projjson.schema.json\",\n + \ \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"description\": + \"Schema for PROJJSON (v0.7)\",\n \"$comment\": \"This document is copyright + Even Rouault and PROJ contributors, 2019-2023, and subject to the MIT license. + This file exists both in data/ and in schemas/vXXX/. Keep both in sync. And + if changing the value of $id, change PROJJSON_DEFAULT_VERSION accordingly + in io.cpp\",\n\n \"oneOf\": [\n { \"$ref\": \"#/definitions/crs\" },\n + \ { \"$ref\": \"#/definitions/datum\" },\n { \"$ref\": \"#/definitions/datum_ensemble\" + },\n { \"$ref\": \"#/definitions/ellipsoid\" },\n { \"$ref\": \"#/definitions/prime_meridian\" + },\n { \"$ref\": \"#/definitions/single_operation\" },\n { \"$ref\": + \"#/definitions/concatenated_operation\" },\n { \"$ref\": \"#/definitions/coordinate_metadata\" + }\n ],\n\n \"definitions\": {\n\n \"abridged_transformation\": {\n \"type\": + \"object\",\n \"properties\": {\n \"$schema\" : { \"type\": \"string\" + },\n \"type\": { \"type\": \"string\", \"enum\": [\"AbridgedTransformation\"] + },\n \"name\": { \"type\": \"string\" },\n \"source_crs\": {\n + \ \"$ref\": \"#/definitions/crs\",\n \"$comment\": \"Only + present when the source_crs of the bound_crs does not match the source_crs + of the AbridgedTransformation. No equivalent in WKT\"\n },\n \"method\": + { \"$ref\": \"#/definitions/method\" },\n \"parameters\": {\n \"type\": + \"array\",\n \"items\": { \"$ref\": \"#/definitions/parameter_value\" + }\n },\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"ids\": + { \"$ref\": \"#/definitions/ids\" }\n },\n \"required\" : [ \"name\", + \"method\", \"parameters\" ],\n \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"axis\": + {\n \"type\": \"object\",\n \"properties\": {\n \"$schema\" + : { \"type\": \"string\" },\n \"type\": { \"type\": \"string\", \"enum\": + [\"Axis\"] },\n \"name\": { \"type\": \"string\" },\n \"abbreviation\": + { \"type\": \"string\" },\n \"direction\": { \"type\": \"string\",\n + \ \"enum\": [ \"north\",\n \"northNorthEast\",\n + \ \"northEast\",\n \"eastNorthEast\",\n + \ \"east\",\n \"eastSouthEast\",\n + \ \"southEast\",\n \"southSouthEast\",\n + \ \"south\",\n \"southSouthWest\",\n + \ \"southWest\",\n \"westSouthWest\",\n + \ \"west\",\n \"westNorthWest\",\n + \ \"northWest\",\n \"northNorthWest\",\n + \ \"up\",\n \"down\",\n + \ \"geocentricX\",\n \"geocentricY\",\n + \ \"geocentricZ\",\n \"columnPositive\",\n + \ \"columnNegative\",\n \"rowPositive\",\n + \ \"rowNegative\",\n \"displayRight\",\n + \ \"displayLeft\",\n \"displayUp\",\n + \ \"displayDown\",\n \"forward\",\n + \ \"aft\",\n \"port\",\n + \ \"starboard\",\n \"clockwise\",\n + \ \"counterClockwise\",\n \"towards\",\n + \ \"awayFrom\",\n \"future\",\n + \ \"past\",\n \"unspecified\" + ] },\n \"meridian\": { \"$ref\": \"#/definitions/meridian\" },\n \"unit\": + { \"$ref\": \"#/definitions/unit\" },\n \"minimum_value\": { \"type\": + \"number\" },\n \"maximum_value\": { \"type\": \"number\" },\n \"range_meaning\": + { \"type\": \"string\", \"enum\": [ \"exact\", \"wraparound\"] },\n \"id\": + { \"$ref\": \"#/definitions/id\" },\n \"ids\": { \"$ref\": \"#/definitions/ids\" + }\n },\n \"required\" : [ \"name\", \"abbreviation\", \"direction\" + ],\n \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"bbox\": + {\n \"type\": \"object\",\n \"properties\": {\n \"east_longitude\": + { \"type\": \"number\" },\n \"west_longitude\": { \"type\": \"number\" + },\n \"south_latitude\": { \"type\": \"number\" },\n \"north_latitude\": + { \"type\": \"number\" }\n },\n \"required\" : [ \"east_longitude\", + \"west_longitude\",\n \"south_latitude\", \"north_latitude\" + ],\n \"additionalProperties\": false\n },\n\n \"bound_crs\": {\n + \ \"type\": \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" + }],\n \"properties\": {\n \"$schema\" : { \"type\": \"string\" + },\n \"type\": { \"type\": \"string\", \"enum\": [\"BoundCRS\"] },\n + \ \"name\": { \"type\": \"string\" },\n \"source_crs\": { \"$ref\": + \"#/definitions/crs\" },\n \"target_crs\": { \"$ref\": \"#/definitions/crs\" + },\n \"transformation\": { \"$ref\": \"#/definitions/abridged_transformation\" + },\n \"scope\": {},\n \"area\": {},\n \"bbox\": {},\n + \ \"vertical_extent\": {},\n \"temporal_extent\": {},\n \"usages\": + {},\n \"remarks\": {},\n \"id\": {}, \"ids\": {}\n },\n + \ \"required\" : [ \"source_crs\", \"target_crs\", \"transformation\" ],\n + \ \"additionalProperties\": false\n },\n\n \"compound_crs\": {\n + \ \"type\": \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\", \"enum\": + [\"CompoundCRS\"] },\n \"name\": { \"type\": \"string\" },\n \"components\": + \ {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/definitions/crs\" + }\n },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": + {},\n \"bbox\": {},\n \"vertical_extent\": {},\n \"temporal_extent\": + {},\n \"usages\": {},\n \"remarks\": {},\n \"id\": {}, + \"ids\": {}\n },\n \"required\" : [ \"name\", \"components\" ],\n + \ \"additionalProperties\": false\n },\n\n \"concatenated_operation\": + {\n \"type\": \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\", \"enum\": + [\"ConcatenatedOperation\"] },\n \"name\": { \"type\": \"string\" },\n + \ \"source_crs\": { \"$ref\": \"#/definitions/crs\" },\n \"target_crs\": + { \"$ref\": \"#/definitions/crs\" },\n \"steps\": {\n \"type\": + \"array\",\n \"items\": { \"$ref\": \"#/definitions/single_operation\" + }\n },\n \"accuracy\": { \"type\": \"string\" },\n \"$schema\" + : {},\n \"scope\": {},\n \"area\": {},\n \"bbox\": {},\n + \ \"vertical_extent\": {},\n \"temporal_extent\": {},\n \"usages\": + {},\n \"remarks\": {},\n \"id\": {}, \"ids\": {}\n },\n + \ \"required\" : [ \"name\", \"source_crs\", \"target_crs\", \"steps\" + ],\n \"additionalProperties\": false\n },\n\n \"conversion\": {\n + \ \"type\": \"object\",\n \"properties\": {\n \"$schema\" + : { \"type\": \"string\" },\n \"type\": { \"type\": \"string\", \"enum\": + [\"Conversion\"] },\n \"name\": { \"type\": \"string\" },\n \"method\": + { \"$ref\": \"#/definitions/method\" },\n \"parameters\": {\n \"type\": + \"array\",\n \"items\": { \"$ref\": \"#/definitions/parameter_value\" + }\n },\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"ids\": + { \"$ref\": \"#/definitions/ids\" }\n },\n \"required\" : [ \"name\", + \"method\" ],\n \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"coordinate_metadata\": + {\n \"type\": \"object\",\n \"properties\": {\n \"$schema\" + : { \"type\": \"string\" },\n \"type\": { \"type\": \"string\", \"enum\": + [\"CoordinateMetadata\"] },\n \"crs\": { \"$ref\": \"#/definitions/crs\" + },\n \"coordinateEpoch\": { \"type\": \"number\" }\n },\n \"required\" + : [ \"crs\" ],\n \"additionalProperties\": false\n },\n\n \"coordinate_system\": + {\n \"type\": \"object\",\n \"properties\": {\n \"$schema\" + : { \"type\": \"string\" },\n \"type\": { \"type\": \"string\", \"enum\": + [\"CoordinateSystem\"] },\n \"name\": { \"type\": \"string\" },\n \"subtype\": + { \"type\": \"string\",\n \"enum\": [\"Cartesian\",\n + \ \"spherical\",\n \"ellipsoidal\",\n + \ \"vertical\",\n \"ordinal\",\n + \ \"parametric\",\n \"affine\",\n + \ \"TemporalDateTime\",\n \"TemporalCount\",\n + \ \"TemporalMeasure\"] },\n \"axis\": + {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/definitions/axis\" + }\n },\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"ids\": + { \"$ref\": \"#/definitions/ids\" }\n },\n \"required\" : [ \"subtype\", + \"axis\" ],\n \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"crs\": + {\n \"oneOf\": [\n { \"$ref\": \"#/definitions/bound_crs\" },\n + \ { \"$ref\": \"#/definitions/compound_crs\" },\n { \"$ref\": + \"#/definitions/derived_engineering_crs\" },\n { \"$ref\": \"#/definitions/derived_geodetic_crs\" + },\n { \"$ref\": \"#/definitions/derived_parametric_crs\" },\n { + \"$ref\": \"#/definitions/derived_projected_crs\" },\n { \"$ref\": + \"#/definitions/derived_temporal_crs\" },\n { \"$ref\": \"#/definitions/derived_vertical_crs\" + },\n { \"$ref\": \"#/definitions/engineering_crs\" },\n { \"$ref\": + \"#/definitions/geodetic_crs\" },\n { \"$ref\": \"#/definitions/parametric_crs\" + },\n { \"$ref\": \"#/definitions/projected_crs\" },\n { \"$ref\": + \"#/definitions/temporal_crs\" },\n { \"$ref\": \"#/definitions/vertical_crs\" + }\n ]\n },\n\n \"datum\": {\n \"oneOf\": [\n { \"$ref\": + \"#/definitions/geodetic_reference_frame\" },\n { \"$ref\": \"#/definitions/vertical_reference_frame\" + },\n { \"$ref\": \"#/definitions/dynamic_geodetic_reference_frame\" + },\n { \"$ref\": \"#/definitions/dynamic_vertical_reference_frame\" + },\n { \"$ref\": \"#/definitions/temporal_datum\" },\n { \"$ref\": + \"#/definitions/parametric_datum\" },\n { \"$ref\": \"#/definitions/engineering_datum\" + }\n ]\n },\n\n \"datum_ensemble\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"$schema\" : { \"type\": \"string\" },\n + \ \"type\": { \"type\": \"string\", \"enum\": [\"DatumEnsemble\"] },\n + \ \"name\": { \"type\": \"string\" },\n \"members\": {\n \"type\": + \"array\",\n \"items\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"name\": { \"type\": + \"string\" },\n \"id\": { \"$ref\": \"#/definitions/id\" + },\n \"ids\": { \"$ref\": \"#/definitions/ids\" }\n },\n + \ \"required\" : [ \"name\" ],\n \"allOf\": [\n + \ { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ],\n \"additionalProperties\": false\n }\n + \ },\n \"ellipsoid\": { \"$ref\": \"#/definitions/ellipsoid\" + },\n \"accuracy\": { \"type\": \"string\" },\n \"id\": { \"$ref\": + \"#/definitions/id\" },\n \"ids\": { \"$ref\": \"#/definitions/ids\" + }\n },\n \"required\" : [ \"name\", \"members\", \"accuracy\" ],\n + \ \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"deformation_model\": + {\n \"description\": \"Association to a PointMotionOperation\",\n \"type\": + \"object\",\n \"properties\": {\n \"name\": { \"type\": \"string\" + },\n \"id\": { \"$ref\": \"#/definitions/id\" }\n },\n \"required\" + : [ \"name\" ],\n \"additionalProperties\": false\n },\n\n \"derived_engineering_crs\": + {\n \"type\": \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\",\n + \ \"enum\": [\"DerivedEngineeringCRS\"] },\n \"name\": + { \"type\": \"string\" },\n \"base_crs\": { \"$ref\": \"#/definitions/engineering_crs\" + },\n \"conversion\": { \"$ref\": \"#/definitions/conversion\" },\n + \ \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"vertical_extent\": {},\n \"temporal_extent\": + {},\n \"usages\": {},\n \"remarks\": {},\n \"id\": {}, + \"ids\": {}\n },\n \"required\" : [ \"name\", \"base_crs\", \"conversion\", + \"coordinate_system\" ],\n \"additionalProperties\": false\n },\n\n + \ \"derived_geodetic_crs\": {\n \"type\": \"object\",\n \"allOf\": + [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": {\n + \ \"type\": { \"type\": \"string\",\n \"enum\": [\"DerivedGeodeticCRS\",\n + \ \"DerivedGeographicCRS\"] },\n \"name\": + { \"type\": \"string\" },\n \"base_crs\": { \"$ref\": \"#/definitions/geodetic_crs\" + },\n \"conversion\": { \"$ref\": \"#/definitions/conversion\" },\n + \ \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"vertical_extent\": {},\n \"temporal_extent\": + {},\n \"usages\": {},\n \"remarks\": {},\n \"id\": {}, + \"ids\": {}\n },\n \"required\" : [ \"name\", \"base_crs\", \"conversion\", + \"coordinate_system\" ],\n \"additionalProperties\": false\n },\n\n + \ \"derived_parametric_crs\": {\n \"type\": \"object\",\n \"allOf\": + [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": {\n + \ \"type\": { \"type\": \"string\",\n \"enum\": [\"DerivedParametricCRS\"] + },\n \"name\": { \"type\": \"string\" },\n \"base_crs\": { \"$ref\": + \"#/definitions/parametric_crs\" },\n \"conversion\": { \"$ref\": \"#/definitions/conversion\" + },\n \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"vertical_extent\": {},\n \"temporal_extent\": + {},\n \"usages\": {},\n \"remarks\": {},\n \"id\": {}, + \"ids\": {}\n },\n \"required\" : [ \"name\", \"base_crs\", \"conversion\", + \"coordinate_system\" ],\n \"additionalProperties\": false\n },\n\n + \ \"derived_projected_crs\": {\n \"type\": \"object\",\n \"allOf\": + [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": {\n + \ \"type\": { \"type\": \"string\",\n \"enum\": [\"DerivedProjectedCRS\"] + },\n \"name\": { \"type\": \"string\" },\n \"base_crs\": { \"$ref\": + \"#/definitions/projected_crs\" },\n \"conversion\": { \"$ref\": \"#/definitions/conversion\" + },\n \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"vertical_extent\": {},\n \"temporal_extent\": + {},\n \"usages\": {},\n \"remarks\": {},\n \"id\": {}, + \"ids\": {}\n },\n \"required\" : [ \"name\", \"base_crs\", \"conversion\", + \"coordinate_system\" ],\n \"additionalProperties\": false\n },\n\n + \ \"derived_temporal_crs\": {\n \"type\": \"object\",\n \"allOf\": + [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": {\n + \ \"type\": { \"type\": \"string\",\n \"enum\": [\"DerivedTemporalCRS\"] + },\n \"name\": { \"type\": \"string\" },\n \"base_crs\": { \"$ref\": + \"#/definitions/temporal_crs\" },\n \"conversion\": { \"$ref\": \"#/definitions/conversion\" + },\n \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"vertical_extent\": {},\n \"temporal_extent\": + {},\n \"usages\": {},\n \"remarks\": {},\n \"id\": {}, + \"ids\": {}\n },\n \"required\" : [ \"name\", \"base_crs\", \"conversion\", + \"coordinate_system\" ],\n \"additionalProperties\": false\n },\n\n + \ \"derived_vertical_crs\": {\n \"type\": \"object\",\n \"allOf\": + [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": {\n + \ \"type\": { \"type\": \"string\",\n \"enum\": [\"DerivedVerticalCRS\"] + },\n \"name\": { \"type\": \"string\" },\n \"base_crs\": { \"$ref\": + \"#/definitions/vertical_crs\" },\n \"conversion\": { \"$ref\": \"#/definitions/conversion\" + },\n \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"vertical_extent\": {},\n \"temporal_extent\": + {},\n \"usages\": {},\n \"remarks\": {},\n \"id\": {}, + \"ids\": {}\n },\n \"required\" : [ \"name\", \"base_crs\", \"conversion\", + \"coordinate_system\" ],\n \"additionalProperties\": false\n },\n\n + \ \"dynamic_geodetic_reference_frame\": {\n \"type\": \"object\",\n + \ \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": + {\n \"type\": { \"type\": \"string\", \"enum\": [\"DynamicGeodeticReferenceFrame\"] + },\n \"name\": {},\n \"anchor\": {},\n \"anchor_epoch\": + {},\n \"ellipsoid\": {},\n \"prime_meridian\": {},\n \"frame_reference_epoch\": + { \"type\": \"number\" },\n \"$schema\" : {},\n \"scope\": {},\n + \ \"area\": {},\n \"bbox\": {},\n \"vertical_extent\": + {},\n \"temporal_extent\": {},\n \"usages\": {},\n \"remarks\": + {},\n \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\", + \"ellipsoid\", \"frame_reference_epoch\" ],\n \"additionalProperties\": + false\n },\n\n \"dynamic_vertical_reference_frame\": {\n \"type\": + \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\", \"enum\": + [\"DynamicVerticalReferenceFrame\"] },\n \"name\": {},\n \"anchor\": + {},\n \"anchor_epoch\": {},\n \"frame_reference_epoch\": { \"type\": + \"number\" },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": + {},\n \"bbox\": {},\n \"vertical_extent\": {},\n \"temporal_extent\": + {},\n \"usages\": {},\n \"remarks\": {},\n \"id\": {}, + \"ids\": {}\n },\n \"required\" : [ \"name\", \"frame_reference_epoch\" + ],\n \"additionalProperties\": false\n },\n\n \"ellipsoid\": {\n + \ \"type\": \"object\",\n \"oneOf\":[\n {\n \"properties\": + {\n \"$schema\" : { \"type\": \"string\" },\n \"type\": + { \"type\": \"string\", \"enum\": [\"Ellipsoid\"] },\n \"name\": + { \"type\": \"string\" },\n \"semi_major_axis\": { \"$ref\": \"#/definitions/value_in_metre_or_value_and_unit\" + },\n \"semi_minor_axis\": { \"$ref\": \"#/definitions/value_in_metre_or_value_and_unit\" + },\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"ids\": + { \"$ref\": \"#/definitions/ids\" }\n },\n \"required\" + : [ \"name\", \"semi_major_axis\", \"semi_minor_axis\" ],\n \"additionalProperties\": + false\n },\n {\n \"properties\": {\n \"$schema\" + : { \"type\": \"string\" },\n \"type\": { \"type\": \"string\", + \"enum\": [\"Ellipsoid\"] },\n \"name\": { \"type\": \"string\" + },\n \"semi_major_axis\": { \"$ref\": \"#/definitions/value_in_metre_or_value_and_unit\" + },\n \"inverse_flattening\": { \"type\": \"number\" },\n \"id\": + { \"$ref\": \"#/definitions/id\" },\n \"ids\": { \"$ref\": \"#/definitions/ids\" + }\n },\n \"required\" : [ \"name\", \"semi_major_axis\", + \"inverse_flattening\" ],\n \"additionalProperties\": false\n },\n + \ {\n \"properties\": {\n \"$schema\" : { \"type\": + \"string\" },\n \"type\": { \"type\": \"string\", \"enum\": [\"Ellipsoid\"] + },\n \"name\": { \"type\": \"string\" },\n \"radius\": + { \"$ref\": \"#/definitions/value_in_metre_or_value_and_unit\" },\n \"id\": + { \"$ref\": \"#/definitions/id\" },\n \"ids\": { \"$ref\": \"#/definitions/ids\" + }\n },\n \"required\" : [ \"name\", \"radius\" ],\n \"additionalProperties\": + false\n }\n ],\n \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ]\n },\n\n \"engineering_crs\": {\n \"type\": \"object\",\n + \ \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": + {\n \"type\": { \"type\": \"string\", \"enum\": [\"EngineeringCRS\"] + },\n \"name\": { \"type\": \"string\" },\n \"datum\": { \"$ref\": + \"#/definitions/engineering_datum\" },\n \"coordinate_system\": { \"$ref\": + \"#/definitions/coordinate_system\" },\n \"$schema\" : {},\n \"scope\": + {},\n \"area\": {},\n \"bbox\": {},\n \"vertical_extent\": + {},\n \"temporal_extent\": {},\n \"usages\": {},\n \"remarks\": + {},\n \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\", + \"datum\" ],\n \"additionalProperties\": false\n },\n\n \"engineering_datum\": + {\n \"type\": \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\", \"enum\": + [\"EngineeringDatum\"] },\n \"name\": { \"type\": \"string\" },\n \"anchor\": + { \"type\": \"string\" },\n \"$schema\" : {},\n \"scope\": {},\n + \ \"area\": {},\n \"bbox\": {},\n \"vertical_extent\": + {},\n \"temporal_extent\": {},\n \"usages\": {},\n \"remarks\": + {},\n \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\" + ],\n \"additionalProperties\": false\n },\n\n \"geodetic_crs\": + {\n \"type\": \"object\",\n \"properties\": {\n \"type\": + { \"type\": \"string\", \"enum\": [\"GeodeticCRS\", \"GeographicCRS\"] },\n + \ \"name\": { \"type\": \"string\" },\n \"datum\": {\n \"oneOf\": + [\n { \"$ref\": \"#/definitions/geodetic_reference_frame\" + },\n { \"$ref\": \"#/definitions/dynamic_geodetic_reference_frame\" + }\n ]\n },\n \"datum_ensemble\": { \"$ref\": \"#/definitions/datum_ensemble\" + },\n \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"deformation_models\": {\n \"type\": \"array\",\n \"items\": + { \"$ref\": \"#/definitions/deformation_model\" }\n },\n \"$schema\" + : {},\n \"scope\": {},\n \"area\": {},\n \"bbox\": {},\n + \ \"vertical_extent\": {},\n \"temporal_extent\": {},\n \"usages\": + {},\n \"remarks\": {},\n \"id\": {}, \"ids\": {}\n },\n + \ \"required\" : [ \"name\" ],\n \"description\": \"One and only + one of datum and datum_ensemble must be provided\",\n \"allOf\": [\n + \ { \"$ref\": \"#/definitions/object_usage\" },\n { \"$ref\": + \"#/definitions/one_and_only_one_of_datum_or_datum_ensemble\" }\n ],\n + \ \"additionalProperties\": false\n },\n\n \"geodetic_reference_frame\": + {\n \"type\": \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\", \"enum\": + [\"GeodeticReferenceFrame\"] },\n \"name\": { \"type\": \"string\" + },\n \"anchor\": { \"type\": \"string\" },\n \"anchor_epoch\": + { \"type\": \"number\" },\n \"ellipsoid\": { \"$ref\": \"#/definitions/ellipsoid\" + },\n \"prime_meridian\": { \"$ref\": \"#/definitions/prime_meridian\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"vertical_extent\": {},\n \"temporal_extent\": + {},\n \"usages\": {},\n \"remarks\": {},\n \"id\": {}, + \"ids\": {}\n },\n \"required\" : [ \"name\", \"ellipsoid\" ],\n + \ \"additionalProperties\": false\n },\n\n \"geoid_model\": {\n + \ \"type\": \"object\",\n \"properties\": {\n \"name\": { + \"type\": \"string\" },\n \"interpolation_crs\": { \"$ref\": \"#/definitions/crs\" + },\n \"id\": { \"$ref\": \"#/definitions/id\" }\n },\n \"required\" + : [ \"name\" ],\n \"additionalProperties\": false\n },\n\n \"id\": + {\n \"type\": \"object\",\n \"properties\": {\n \"authority\": + { \"type\": \"string\" },\n \"code\": {\n \"oneOf\": [ { \"type\": + \"string\" }, { \"type\": \"integer\" } ]\n },\n \"version\": + {\n \"oneOf\": [ { \"type\": \"string\" }, { \"type\": \"number\" + } ]\n },\n \"authority_citation\": { \"type\": \"string\" },\n + \ \"uri\": { \"type\": \"string\" }\n },\n \"required\" : + [ \"authority\", \"code\" ],\n \"additionalProperties\": false\n },\n\n + \ \"ids\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/definitions/id\" + }\n },\n\n \"method\": {\n \"type\": \"object\",\n \"properties\": + {\n \"$schema\" : { \"type\": \"string\" },\n \"type\": { \"type\": + \"string\", \"enum\": [\"OperationMethod\"]},\n \"name\": { \"type\": + \"string\" },\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"ids\": + { \"$ref\": \"#/definitions/ids\" }\n },\n \"required\" : [ \"name\" + ],\n \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"id_ids_mutually_exclusive\": + {\n \"not\": {\n \"type\": \"object\",\n \"required\": + [ \"id\", \"ids\" ]\n }\n },\n\n \"one_and_only_one_of_datum_or_datum_ensemble\": + {\n \"allOf\": [\n {\n \"not\": {\n \"type\": + \"object\",\n \"required\": [ \"datum\", \"datum_ensemble\" + ]\n }\n },\n {\n \"oneOf\": [\n { + \"type\": \"object\", \"required\": [\"datum\"] },\n { \"type\": + \"object\", \"required\": [\"datum_ensemble\"] }\n ]\n }\n + \ ]\n },\n\n \"meridian\": {\n \"type\": \"object\",\n \"properties\": + {\n \"$schema\" : { \"type\": \"string\" },\n \"type\": { \"type\": + \"string\", \"enum\": [\"Meridian\"] },\n \"longitude\": { \"$ref\": + \"#/definitions/value_in_degree_or_value_and_unit\" },\n \"id\": { + \"$ref\": \"#/definitions/id\" },\n \"ids\": { \"$ref\": \"#/definitions/ids\" + }\n },\n \"required\" : [ \"longitude\" ],\n \"allOf\": [\n + \ { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" }\n ],\n + \ \"additionalProperties\": false\n },\n\n \"object_usage\": {\n + \ \"anyOf\": [\n {\n \"type\": \"object\",\n \"properties\": + {\n \"$schema\" : { \"type\": \"string\" },\n \"scope\": + { \"type\": \"string\" },\n \"area\": { \"type\": \"string\" },\n + \ \"bbox\": { \"$ref\": \"#/definitions/bbox\" },\n \"vertical_extent\": + { \"$ref\": \"#/definitions/vertical_extent\" },\n \"temporal_extent\": + { \"$ref\": \"#/definitions/temporal_extent\" },\n \"remarks\": + { \"type\": \"string\" },\n \"id\": { \"$ref\": \"#/definitions/id\" + },\n \"ids\": { \"$ref\": \"#/definitions/ids\" }\n },\n + \ \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ]\n },\n {\n \"type\": \"object\",\n \"properties\": + {\n \"$schema\" : { \"type\": \"string\" },\n \"usages\": + { \"$ref\": \"#/definitions/usages\" },\n \"remarks\": { \"type\": + \"string\" },\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"ids\": + { \"$ref\": \"#/definitions/ids\" }\n },\n \"allOf\": [\n { + \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" }\n ]\n }\n + \ ]\n },\n\n \"parameter_value\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"$schema\" : { \"type\": \"string\" },\n + \ \"type\": { \"type\": \"string\", \"enum\": [\"ParameterValue\"] },\n + \ \"name\": { \"type\": \"string\" },\n \"value\": {\n \"oneOf\": + [\n { \"type\": \"string\" },\n { \"type\": \"number\" + }\n ]\n },\n \"unit\": { \"$ref\": \"#/definitions/unit\" + },\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"ids\": + { \"$ref\": \"#/definitions/ids\" }\n },\n \"required\" : [ \"name\", + \"value\" ],\n \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"parametric_crs\": + {\n \"type\": \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\", \"enum\": + [\"ParametricCRS\"] },\n \"name\": { \"type\": \"string\" },\n \"datum\": + { \"$ref\": \"#/definitions/parametric_datum\" },\n \"coordinate_system\": + { \"$ref\": \"#/definitions/coordinate_system\" },\n \"$schema\" : + {},\n \"scope\": {},\n \"area\": {},\n \"bbox\": {},\n + \ \"vertical_extent\": {},\n \"temporal_extent\": {},\n \"usages\": + {},\n \"remarks\": {},\n \"id\": {}, \"ids\": {}\n },\n + \ \"required\" : [ \"name\", \"datum\" ],\n \"additionalProperties\": + false\n },\n\n \"parametric_datum\": {\n \"type\": \"object\",\n + \ \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": + {\n \"type\": { \"type\": \"string\", \"enum\": [\"ParametricDatum\"] + },\n \"name\": { \"type\": \"string\" },\n \"anchor\": { \"type\": + \"string\" },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": + {},\n \"bbox\": {},\n \"vertical_extent\": {},\n \"temporal_extent\": + {},\n \"usages\": {},\n \"remarks\": {},\n \"id\": {}, + \"ids\": {}\n },\n \"required\" : [ \"name\" ],\n \"additionalProperties\": + false\n },\n\n \"point_motion_operation\": {\n \"$comment\": \"Not + implemented in PROJ (at least as of PROJ 9.1)\",\n \"type\": \"object\",\n + \ \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": + {\n \"type\": { \"type\": \"string\", \"enum\": [\"PointMotionOperation\"] + },\n \"name\": { \"type\": \"string\" },\n \"source_crs\": { + \"$ref\": \"#/definitions/crs\" },\n \"method\": { \"$ref\": \"#/definitions/method\" + },\n \"parameters\": {\n \"type\": \"array\",\n \"items\": + { \"$ref\": \"#/definitions/parameter_value\" }\n },\n \"accuracy\": + { \"type\": \"string\" },\n \"$schema\" : {},\n \"scope\": {},\n + \ \"area\": {},\n \"bbox\": {},\n \"vertical_extent\": + {},\n \"temporal_extent\": {},\n \"usages\": {},\n \"remarks\": + {},\n \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\", + \"source_crs\", \"method\", \"parameters\" ],\n \"additionalProperties\": + false\n },\n\n \"prime_meridian\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"$schema\" : { \"type\": \"string\" },\n + \ \"type\": { \"type\": \"string\", \"enum\": [\"PrimeMeridian\"] },\n + \ \"name\": { \"type\": \"string\" },\n \"longitude\": { \"$ref\": + \"#/definitions/value_in_degree_or_value_and_unit\" },\n \"id\": { + \"$ref\": \"#/definitions/id\" },\n \"ids\": { \"$ref\": \"#/definitions/ids\" + }\n },\n \"required\" : [ \"name\" ],\n \"allOf\": [\n { + \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" }\n ],\n \"additionalProperties\": + false\n },\n\n \"single_operation\": {\n \"oneOf\": [\n { + \"$ref\": \"#/definitions/conversion\" },\n { \"$ref\": \"#/definitions/transformation\" + },\n { \"$ref\": \"#/definitions/point_motion_operation\" }\n ]\n + \ },\n\n \"projected_crs\": {\n \"type\": \"object\",\n \"allOf\": + [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": {\n + \ \"type\": { \"type\": \"string\",\n \"enum\": [\"ProjectedCRS\"] + },\n \"name\": { \"type\": \"string\" },\n \"base_crs\": { \"$ref\": + \"#/definitions/geodetic_crs\" },\n \"conversion\": { \"$ref\": \"#/definitions/conversion\" + },\n \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"vertical_extent\": {},\n \"temporal_extent\": + {},\n \"usages\": {},\n \"remarks\": {},\n \"id\": {}, + \"ids\": {}\n },\n \"required\" : [ \"name\", \"base_crs\", \"conversion\", + \"coordinate_system\" ],\n \"additionalProperties\": false\n },\n\n + \ \"temporal_crs\": {\n \"type\": \"object\",\n \"allOf\": [{ + \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": {\n \"type\": + { \"type\": \"string\", \"enum\": [\"TemporalCRS\"] },\n \"name\": + { \"type\": \"string\" },\n \"datum\": { \"$ref\": \"#/definitions/temporal_datum\" + },\n \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"vertical_extent\": {},\n \"temporal_extent\": + {},\n \"usages\": {},\n \"remarks\": {},\n \"id\": {}, + \"ids\": {}\n },\n \"required\" : [ \"name\", \"datum\" ],\n \"additionalProperties\": + false\n },\n\n \"temporal_datum\": {\n \"type\": \"object\",\n + \ \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": + {\n \"type\": { \"type\": \"string\", \"enum\": [\"TemporalDatum\"] + },\n \"name\": { \"type\": \"string\" },\n \"calendar\": { \"type\": + \"string\" },\n \"time_origin\": { \"type\": \"string\" },\n \"$schema\" + : {},\n \"scope\": {},\n \"area\": {},\n \"bbox\": {},\n + \ \"vertical_extent\": {},\n \"temporal_extent\": {},\n \"usages\": + {},\n \"remarks\": {},\n \"id\": {}, \"ids\": {}\n },\n + \ \"required\" : [ \"name\", \"calendar\" ],\n \"additionalProperties\": + false\n },\n\n \"temporal_extent\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"start\": { \"type\": \"string\" },\n \"end\": + { \"type\": \"string\" }\n },\n \"required\" : [ \"start\", \"end\" + ],\n \"additionalProperties\": false\n },\n\n \"transformation\": + {\n \"type\": \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\", \"enum\": + [\"Transformation\"] },\n \"name\": { \"type\": \"string\" },\n \"source_crs\": + { \"$ref\": \"#/definitions/crs\" },\n \"target_crs\": { \"$ref\": + \"#/definitions/crs\" },\n \"interpolation_crs\": { \"$ref\": \"#/definitions/crs\" + },\n \"method\": { \"$ref\": \"#/definitions/method\" },\n \"parameters\": + {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/definitions/parameter_value\" + }\n },\n \"accuracy\": { \"type\": \"string\" },\n \"$schema\" + : {},\n \"scope\": {},\n \"area\": {},\n \"bbox\": {},\n + \ \"vertical_extent\": {},\n \"temporal_extent\": {},\n \"usages\": + {},\n \"remarks\": {},\n \"id\": {}, \"ids\": {}\n },\n + \ \"required\" : [ \"name\", \"source_crs\", \"target_crs\", \"method\", + \"parameters\" ],\n \"additionalProperties\": false\n },\n\n \"unit\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\"metre\", \"degree\", \"unity\"]\n },\n {\n \"type\": \"object\",\n + \ \"properties\": {\n \"type\": { \"type\": \"string\",\n \"enum\": + [\"LinearUnit\", \"AngularUnit\", \"ScaleUnit\",\n \"TimeUnit\", + \"ParametricUnit\", \"Unit\"] },\n \"name\": { \"type\": \"string\" + },\n \"conversion_factor\": { \"type\": \"number\" },\n \"id\": + { \"$ref\": \"#/definitions/id\" },\n \"ids\": { \"$ref\": \"#/definitions/ids\" + }\n },\n \"required\" : [ \"type\", \"name\" ],\n \"allOf\": + [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" }\n + \ ],\n \"additionalProperties\": false\n }\n ]\n + \ },\n\n \"usages\": {\n \"type\": \"array\",\n \"items\": + {\n \"type\": \"object\",\n \"properties\": {\n \"scope\": + { \"type\": \"string\" },\n \"area\": { \"type\": \"string\" },\n + \ \"bbox\": { \"$ref\": \"#/definitions/bbox\" },\n \"vertical_extent\": + { \"$ref\": \"#/definitions/vertical_extent\" },\n \"temporal_extent\": + { \"$ref\": \"#/definitions/temporal_extent\" }\n },\n \"additionalProperties\": + false\n }\n },\n\n \"value_and_unit\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"value\": { \"type\": \"number\" },\n \"unit\": + { \"$ref\": \"#/definitions/unit\" }\n },\n \"required\" : [ \"value\", + \"unit\" ],\n \"additionalProperties\": false\n },\n\n \"value_in_degree_or_value_and_unit\": + {\n \"oneOf\": [\n { \"type\": \"number\" },\n { \"$ref\": + \"#/definitions/value_and_unit\" }\n ]\n },\n\n \"value_in_metre_or_value_and_unit\": + {\n \"oneOf\": [\n { \"type\": \"number\" },\n { \"$ref\": + \"#/definitions/value_and_unit\" }\n ]\n },\n\n \"vertical_crs\": + {\n \"type\": \"object\",\n \"properties\": {\n \"type\": + { \"type\": \"string\", \"enum\": [\"VerticalCRS\"] },\n \"name\": + { \"type\": \"string\" },\n \"datum\": {\n \"oneOf\": [\n + \ { \"$ref\": \"#/definitions/vertical_reference_frame\" },\n + \ { \"$ref\": \"#/definitions/dynamic_vertical_reference_frame\" + }\n ]\n },\n \"datum_ensemble\": { \"$ref\": \"#/definitions/datum_ensemble\" + },\n \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"geoid_model\": { \"$ref\": \"#/definitions/geoid_model\" },\n + \ \"geoid_models\": {\n \"type\": \"array\",\n \"items\": + { \"$ref\": \"#/definitions/geoid_model\" }\n },\n \"deformation_models\": + {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/definitions/deformation_model\" + }\n },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": + {},\n \"bbox\": {},\n \"vertical_extent\": {},\n \"temporal_extent\": + {},\n \"usages\": {},\n \"remarks\": {},\n \"id\": {}, + \"ids\": {}\n },\n \"required\" : [ \"name\"],\n \"description\": + \"One and only one of datum and datum_ensemble must be provided\",\n \"allOf\": + [\n { \"$ref\": \"#/definitions/object_usage\" },\n { \"$ref\": + \"#/definitions/one_and_only_one_of_datum_or_datum_ensemble\" },\n {\n + \ \"not\": {\n \"type\": \"object\",\n \"required\": + [ \"geoid_model\", \"geoid_models\" ]\n }\n }\n ],\n + \ \"additionalProperties\": false\n },\n\n \"vertical_extent\": + {\n \"type\": \"object\",\n \"properties\": {\n \"minimum\": + { \"type\": \"number\" },\n \"maximum\": { \"type\": \"number\" },\n + \ \"unit\": { \"$ref\": \"#/definitions/unit\" }\n },\n \"required\" + : [ \"minimum\", \"maximum\" ],\n \"additionalProperties\": false\n },\n\n + \ \"vertical_reference_frame\": {\n \"type\": \"object\",\n \"allOf\": + [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": {\n + \ \"type\": { \"type\": \"string\", \"enum\": [\"VerticalReferenceFrame\"] + },\n \"name\": { \"type\": \"string\" },\n \"anchor\": { \"type\": + \"string\" },\n \"anchor_epoch\": { \"type\": \"number\" },\n \"$schema\" + : {},\n \"scope\": {},\n \"area\": {},\n \"bbox\": {},\n + \ \"vertical_extent\": {},\n \"temporal_extent\": {},\n \"usages\": + {},\n \"remarks\": {},\n \"id\": {}, \"ids\": {}\n },\n + \ \"required\" : [ \"name\" ],\n \"additionalProperties\": false\n + \ }\n\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_from_dict[path1].yaml b/tests/cassettes/test_examples/test_example_from_dict[path1].yaml new file mode 100644 index 000000000..a0a42e384 --- /dev/null +++ b/tests/cassettes/test_examples/test_example_from_dict[path1].yaml @@ -0,0 +1,204 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v1.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Items.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"properties\": {\n \"$ref\": + \"#/definitions/fields\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"$comment\": + \"The if-then-else below checks whether the eo:bands is given in assets or + not. If yes, allows eo:bands in properties (else), otherwise, disallows eo:bands + in properties (then).\",\n \"if\": {\n \"required\": [\n + \ \"assets\"\n ],\n \"properties\": {\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"properties\": {\n \"eo:bands\": + false\n }\n }\n }\n }\n + \ },\n \"then\": {\n \"properties\": {\n \"properties\": + {\n \"properties\": {\n \"eo:bands\": false\n + \ }\n }\n }\n },\n \"else\": + {\n \"properties\": {\n \"properties\": {\n \"properties\": + {\n \"eo:bands\": {\n \"$ref\": \"#/definitions/bands\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"eo:cloud_cover\": {\n \"title\": + \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!eo:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"title\": \"Band\",\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"properties\": + {\n \"name\": {\n \"title\": \"Name of the band\",\n \"type\": + \"string\"\n },\n \"common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"red\",\n \"rededge\",\n \"yellow\",\n + \ \"pan\",\n \"nir\",\n \"nir08\",\n + \ \"nir09\",\n \"cirrus\",\n \"swir16\",\n + \ \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"center_wavelength\": + {\n \"title\": \"Center Wavelength\",\n \"type\": \"number\"\n + \ },\n \"full_width_half_max\": {\n \"title\": + \"Full Width Half Max (FWHM)\",\n \"type\": \"number\"\n }\n + \ }\n }\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/sat/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/sat/v1.0.0/schema.json\",\n \"title\": + \"Satellite Extension\",\n \"description\": \"STAC Sat Extension to a STAC + Item.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC Items.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\n \"required\": [\n \"sat:platform_international_designator\"\n + \ ]\n },\n {\n \"required\": + [\n \"sat:orbit_state\"\n ]\n + \ },\n {\n \"required\": + [\n \"sat:absolute_orbit\"\n ]\n + \ },\n {\n \"required\": + [\n \"sat:relative_orbit\"\n ]\n + \ },\n {\n \"required\": + [\n \"sat:anx_datetime\"\n ]\n + \ }\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/sat/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"sat:platform_international_designator\": + {\n \"type\": \"string\"\n },\n \"sat:orbit_state\": + {\n \"title\": \"Orbit State\",\n \"type\": \"string\",\n + \ \"enum\": [\n \"ascending\",\n \"descending\",\n + \ \"geostationary\"\n ]\n },\n \"sat:absolute_orbit\": + {\n \"type\": \"integer\",\n \"minimum\": 1\n },\n + \ \"sat:relative_orbit\": {\n \"type\": \"integer\",\n \"minimum\": + 1\n },\n \"sat:anx_datetime\": {\n \"type\": \"string\",\n + \ \"format\": \"date-time\"\n }\n },\n \"patternProperties\": + {\n \"^(?!sat:)\": {\n \"$comment\": \"Do not allow unspecified + fields prefixed with sat:\"\n }\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_from_dict[path2].yaml b/tests/cassettes/test_examples/test_example_from_dict[path2].yaml new file mode 100644 index 000000000..92b20ee3b --- /dev/null +++ b/tests/cassettes/test_examples/test_example_from_dict[path2].yaml @@ -0,0 +1,216 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v1.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Items.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"properties\": {\n \"$ref\": + \"#/definitions/fields\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"$comment\": + \"The if-then-else below checks whether the eo:bands is given in assets or + not. If yes, allows eo:bands in properties (else), otherwise, disallows eo:bands + in properties (then).\",\n \"if\": {\n \"required\": [\n + \ \"assets\"\n ],\n \"properties\": {\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"properties\": {\n \"eo:bands\": + false\n }\n }\n }\n }\n + \ },\n \"then\": {\n \"properties\": {\n \"properties\": + {\n \"properties\": {\n \"eo:bands\": false\n + \ }\n }\n }\n },\n \"else\": + {\n \"properties\": {\n \"properties\": {\n \"properties\": + {\n \"eo:bands\": {\n \"$ref\": \"#/definitions/bands\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"eo:cloud_cover\": {\n \"title\": + \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!eo:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"title\": \"Band\",\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"properties\": + {\n \"name\": {\n \"title\": \"Name of the band\",\n \"type\": + \"string\"\n },\n \"common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"red\",\n \"rededge\",\n \"yellow\",\n + \ \"pan\",\n \"nir\",\n \"nir08\",\n + \ \"nir09\",\n \"cirrus\",\n \"swir16\",\n + \ \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"center_wavelength\": + {\n \"title\": \"Center Wavelength\",\n \"type\": \"number\"\n + \ },\n \"full_width_half_max\": {\n \"title\": + \"Full Width Half Max (FWHM)\",\n \"type\": \"number\"\n }\n + \ }\n }\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the + schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Require fields here for item properties.\",\n \"required\": + [\n \"proj:epsg\"\n ]\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"proj:epsg\":{\n + \ \"title\":\"EPSG code\",\n \"type\":[\n \"integer\",\n + \ \"null\"\n ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.2/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_from_dict[path3].yaml b/tests/cassettes/test_examples/test_example_from_dict[path3].yaml new file mode 100644 index 000000000..92b20ee3b --- /dev/null +++ b/tests/cassettes/test_examples/test_example_from_dict[path3].yaml @@ -0,0 +1,216 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v1.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Items.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"properties\": {\n \"$ref\": + \"#/definitions/fields\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"$comment\": + \"The if-then-else below checks whether the eo:bands is given in assets or + not. If yes, allows eo:bands in properties (else), otherwise, disallows eo:bands + in properties (then).\",\n \"if\": {\n \"required\": [\n + \ \"assets\"\n ],\n \"properties\": {\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"properties\": {\n \"eo:bands\": + false\n }\n }\n }\n }\n + \ },\n \"then\": {\n \"properties\": {\n \"properties\": + {\n \"properties\": {\n \"eo:bands\": false\n + \ }\n }\n }\n },\n \"else\": + {\n \"properties\": {\n \"properties\": {\n \"properties\": + {\n \"eo:bands\": {\n \"$ref\": \"#/definitions/bands\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"eo:cloud_cover\": {\n \"title\": + \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!eo:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"title\": \"Band\",\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"properties\": + {\n \"name\": {\n \"title\": \"Name of the band\",\n \"type\": + \"string\"\n },\n \"common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"red\",\n \"rededge\",\n \"yellow\",\n + \ \"pan\",\n \"nir\",\n \"nir08\",\n + \ \"nir09\",\n \"cirrus\",\n \"swir16\",\n + \ \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"center_wavelength\": + {\n \"title\": \"Center Wavelength\",\n \"type\": \"number\"\n + \ },\n \"full_width_half_max\": {\n \"title\": + \"Full Width Half Max (FWHM)\",\n \"type\": \"number\"\n }\n + \ }\n }\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the + schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Require fields here for item properties.\",\n \"required\": + [\n \"proj:epsg\"\n ]\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"proj:epsg\":{\n + \ \"title\":\"EPSG code\",\n \"type\":[\n \"integer\",\n + \ \"null\"\n ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.2/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_from_dict[path4].yaml b/tests/cassettes/test_examples/test_example_from_dict[path4].yaml new file mode 100644 index 000000000..8a8c2e0e6 --- /dev/null +++ b/tests/cassettes/test_examples/test_example_from_dict[path4].yaml @@ -0,0 +1,140 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v1.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Items.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"properties\": {\n \"$ref\": + \"#/definitions/fields\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"$comment\": + \"The if-then-else below checks whether the eo:bands is given in assets or + not. If yes, allows eo:bands in properties (else), otherwise, disallows eo:bands + in properties (then).\",\n \"if\": {\n \"required\": [\n + \ \"assets\"\n ],\n \"properties\": {\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"properties\": {\n \"eo:bands\": + false\n }\n }\n }\n }\n + \ },\n \"then\": {\n \"properties\": {\n \"properties\": + {\n \"properties\": {\n \"eo:bands\": false\n + \ }\n }\n }\n },\n \"else\": + {\n \"properties\": {\n \"properties\": {\n \"properties\": + {\n \"eo:bands\": {\n \"$ref\": \"#/definitions/bands\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"eo:cloud_cover\": {\n \"title\": + \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!eo:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"title\": \"Band\",\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"properties\": + {\n \"name\": {\n \"title\": \"Name of the band\",\n \"type\": + \"string\"\n },\n \"common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"red\",\n \"rededge\",\n \"yellow\",\n + \ \"pan\",\n \"nir\",\n \"nir08\",\n + \ \"nir09\",\n \"cirrus\",\n \"swir16\",\n + \ \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"center_wavelength\": + {\n \"title\": \"Center Wavelength\",\n \"type\": \"number\"\n + \ },\n \"full_width_half_max\": {\n \"title\": + \"Full Width Half Max (FWHM)\",\n \"type\": \"number\"\n }\n + \ }\n }\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_from_dict[path6].yaml b/tests/cassettes/test_examples/test_example_from_dict[path6].yaml new file mode 100644 index 000000000..e799c741e --- /dev/null +++ b/tests/cassettes/test_examples/test_example_from_dict[path6].yaml @@ -0,0 +1,375 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v1.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Items.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"properties\": {\n \"$ref\": + \"#/definitions/fields\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"$comment\": + \"The if-then-else below checks whether the eo:bands is given in assets or + not. If yes, allows eo:bands in properties (else), otherwise, disallows eo:bands + in properties (then).\",\n \"if\": {\n \"required\": [\n + \ \"assets\"\n ],\n \"properties\": {\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"properties\": {\n \"eo:bands\": + false\n }\n }\n }\n }\n + \ },\n \"then\": {\n \"properties\": {\n \"properties\": + {\n \"properties\": {\n \"eo:bands\": false\n + \ }\n }\n }\n },\n \"else\": + {\n \"properties\": {\n \"properties\": {\n \"properties\": + {\n \"eo:bands\": {\n \"$ref\": \"#/definitions/bands\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"eo:cloud_cover\": {\n \"title\": + \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!eo:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"title\": \"Band\",\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"properties\": + {\n \"name\": {\n \"title\": \"Name of the band\",\n \"type\": + \"string\"\n },\n \"common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"red\",\n \"rededge\",\n \"yellow\",\n + \ \"pan\",\n \"nir\",\n \"nir08\",\n + \ \"nir09\",\n \"cirrus\",\n \"swir16\",\n + \ \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"center_wavelength\": + {\n \"title\": \"Center Wavelength\",\n \"type\": \"number\"\n + \ },\n \"full_width_half_max\": {\n \"title\": + \"Full Width Half Max (FWHM)\",\n \"type\": \"number\"\n }\n + \ }\n }\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the + schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Require fields here for item properties.\",\n \"required\": + [\n \"proj:epsg\"\n ]\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"proj:epsg\":{\n + \ \"title\":\"EPSG code\",\n \"type\":[\n \"integer\",\n + \ \"null\"\n ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.2/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/scientific/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/scientific/v1.0.0/schema.json#\",\n \"title\": + \"Scientific Citation Extension\",\n \"description\": \"Scientific Citation + Extension for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items. Remove this object + if this extension only applies to Collections.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/stac_extensions\"\n },\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"properties\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"properties\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"$comment\": \"This validates the fields in Item Assets, + but does not require them.\",\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n }\n ]\n },\n {\n \"$comment\": \"This + is the schema for STAC Collections.\",\n \"type\": \"object\",\n \"allOf\": + [\n {\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n {\n \"$ref\": + \"#/definitions/stac_extensions\"\n }\n ],\n \"anyOf\": [\n + \ {\n \"$comment\": \"This is the schema for the top-level + fields in a Collection. Remove this if this extension does not define top-level + fields for Collections.\",\n \"allOf\": [\n {\n \"$ref\": + \"#/definitions/require_any_field\"\n },\n {\n \"$ref\": + \"#/definitions/fields\"\n }\n ]\n },\n {\n + \ \"$comment\": \"This validates the fields in Collection Assets, + but does not require them.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"properties\": {\n \"assets\": {\n \"type\": + \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n }\n }\n + \ }\n }\n }\n },\n {\n \"$comment\": + \"This is the schema for the fields in Item Asset Definitions. It doesn't + require any fields.\",\n \"required\": [\n \"item_assets\"\n + \ ],\n \"properties\": {\n \"item_assets\": {\n + \ \"type\": \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n }\n }\n + \ }\n }\n }\n },\n {\n \"$comment\": + \"This is the schema for the fields in Summaries. By default, only checks + the existance of the properties, but not the schema of the summaries.\",\n + \ \"required\": [\n \"summaries\"\n ],\n \"properties\": + {\n \"summaries\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/scientific/v1.0.0/schema.json\"\n }\n + \ }\n }\n },\n \"require_any_field\": {\n \"$comment\": + \"Please list all fields here so that we can force the existance of one of + them in other parts of the schemas.\",\n \"anyOf\": [\n {\"required\": + [\"sci:doi\"]},\n {\"required\": [\"sci:citation\"]},\n {\"required\": + [\"sci:publications\"]}\n ]\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"sci:doi\": + {\n \"type\": \"string\",\n \"title\": \"Data DOI\",\n \"pattern\": + \"^10\\\\.[0-9a-zA-Z]{4,}/[^\\\\s]+$\"\n }, \n \"sci:citation\": + {\n \"type\": \"string\", \n \"title\": \"Proposed Data + Citation\"\n },\n \"sci:publications\": {\n \"type\": + \"array\",\n \"title\": \"Publications\",\n \"items\": {\n + \ \"type\": \"object\",\n \"properties\": {\n \"doi\": + {\n \"type\": \"string\",\n \"title\": \"Publication + DOI\",\n \"pattern\": \"^10\\\\.[0-9a-zA-Z]{4,}/[^\\\\s]+$\"\n + \ }, \n \"citation\": { \n \"type\": + \"string\", \n \"title\": \"Publication Citation\"\n }\n + \ }\n }\n }\n },\n \"patternProperties\": + {\n \"^(?!sci:)\": {}\n },\n \"additionalProperties\": false\n + \ }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/remote-data/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/remote-data/v1.0.0/schema.json\",\n \"title\": + \"Remote Data (Example) Extension\",\n \"description\": \"STAC Example Extension + for fictional vendor Remote Data\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Required fields here for item properties.\",\n \"required\": + [\n \"rd:type\",\n \"rd:product_level\",\n + \ \"rd:sat_id\"\n ]\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"rd:visibility\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$comment\": \"Remove this + and the following object if this is not an extension to a Collection.\",\n + \ \"$ref\": \"#/definitions/stac_extensions\"\n },\n {\n + \ \"$ref\": \"#/definitions/collection_fields\"\n }\n ]\n + \ }\n ],\n \"definitions\": {\n \"stac_extensions\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/remote-data/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"collection_fields\": {\n \"properties\": + {\n \"rd:visibility\": {\n \"type\": \"string\",\n \"enum\": + [\n \"public\",\n \"protected\",\n \"private\"\n + \ ]\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Remote Data fictional fields.\",\n \"type\": \"object\",\n \"properties\": + {\n \"rd:type\": {\n \"type\": \"string\",\n \"enum\": + [\n \"scene\"\n ]\n },\n \"rd:product_level\": + {\n \"type\": \"string\",\n \"enum\": [\n \"LV1A\",\n + \ \"LV1B\",\n \"LV2A\",\n \"LV2B\",\n \"LV3A\",\n + \ \"LV3B\"\n ]\n }, \n \"rd:runs\": {\n \"type\": + \"array\", \n \"items\": {\n \"type\": \"string\"\n }\n + \ },\n \"rd:parsecs\": {\n \"type\": \"array\", \n \"items\": + {\n \"type\": \"number\"\n }\n },\n \"rd:anomalous_pixels\": + {\n \"type\": \"number\"\n },\n \"rd:sat_id\": {\n + \ \"type\": \"string\"\n },\n \"rd:earth_sun_distance\": + {\n \"type\": \"number\"\n },\n \"rd:flux_capacitor\": + {\n \"type\": \"boolean\"\n }\n },\n \"patternProperties\": + {\n \"^(?!rd:)\": {\n \"$comment\": \"Disallow other fields + with rd: prefix\"\n }\n },\n \"additionalProperties\": false\n + \ }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_from_dict[path8].yaml b/tests/cassettes/test_examples/test_example_from_dict[path8].yaml new file mode 100644 index 000000000..5c30837a3 --- /dev/null +++ b/tests/cassettes/test_examples/test_example_from_dict[path8].yaml @@ -0,0 +1,626 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v1.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Items.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"properties\": {\n \"$ref\": + \"#/definitions/fields\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"$comment\": + \"The if-then-else below checks whether the eo:bands is given in assets or + not. If yes, allows eo:bands in properties (else), otherwise, disallows eo:bands + in properties (then).\",\n \"if\": {\n \"required\": [\n + \ \"assets\"\n ],\n \"properties\": {\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"properties\": {\n \"eo:bands\": + false\n }\n }\n }\n }\n + \ },\n \"then\": {\n \"properties\": {\n \"properties\": + {\n \"properties\": {\n \"eo:bands\": false\n + \ }\n }\n }\n },\n \"else\": + {\n \"properties\": {\n \"properties\": {\n \"properties\": + {\n \"eo:bands\": {\n \"$ref\": \"#/definitions/bands\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"eo:cloud_cover\": {\n \"title\": + \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!eo:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"title\": \"Band\",\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"properties\": + {\n \"name\": {\n \"title\": \"Name of the band\",\n \"type\": + \"string\"\n },\n \"common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"red\",\n \"rededge\",\n \"yellow\",\n + \ \"pan\",\n \"nir\",\n \"nir08\",\n + \ \"nir09\",\n \"cirrus\",\n \"swir16\",\n + \ \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"center_wavelength\": + {\n \"title\": \"Center Wavelength\",\n \"type\": \"number\"\n + \ },\n \"full_width_half_max\": {\n \"title\": + \"Full Width Half Max (FWHM)\",\n \"type\": \"number\"\n }\n + \ }\n }\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the + schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Require fields here for item properties.\",\n \"required\": + [\n \"proj:epsg\"\n ]\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"proj:epsg\":{\n + \ \"title\":\"EPSG code\",\n \"type\":[\n \"integer\",\n + \ \"null\"\n ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.2/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - proj.org + method: GET + uri: https://proj.org/schemas/v0.2/projjson.schema.json + response: + body: + string: '' + headers: + Location: + - https://proj.org/en/latest/schemas/v0.2/projjson.schema.json + status: + code: 302 + message: Found +- request: + body: null + headers: + Connection: + - close + Host: + - proj.org + method: GET + uri: https://proj.org/en/latest/schemas/v0.2/projjson.schema.json + response: + body: + string: "{\n \"$id\": \"https://proj.org/schemas/v0.2/projjson.schema.json\",\n + \ \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"description\": + \"Schema for PROJJSON (v0.2.1)\",\n \"$comment\": \"This document is copyright + Even Rouault and PROJ contributors, 2019-2020, and subject to the MIT license. + This file exists both in data/ and in schemas/vXXX/. Keep both in sync. And + if changing the value of $id, change PROJJSON_CURRENT_VERSION accordingly + in io.cpp\",\n\n \"oneOf\": [\n { \"$ref\": \"#/definitions/crs\" },\n + \ { \"$ref\": \"#/definitions/datum\" },\n { \"$ref\": \"#/definitions/datum_ensemble\" + },\n { \"$ref\": \"#/definitions/ellipsoid\" },\n { \"$ref\": \"#/definitions/prime_meridian\" + },\n { \"$ref\": \"#/definitions/single_operation\" },\n { \"$ref\": + \"#/definitions/concatenated_operation\" }\n ],\n\n \"definitions\": {\n\n + \ \"abridged_transformation\": {\n \"type\": \"object\",\n \"properties\": + {\n \"$schema\" : { \"type\": \"string\" },\n \"type\": { \"type\": + \"string\", \"enum\": [\"AbridgedTransformation\"] },\n \"name\": { + \"type\": \"string\" },\n \"method\": { \"$ref\": \"#/definitions/method\" + },\n \"parameters\": {\n \"type\": \"array\",\n \"items\": + { \"$ref\": \"#/definitions/parameter_value\" }\n },\n \"id\": + { \"$ref\": \"#/definitions/id\" },\n \"ids\": { \"$ref\": \"#/definitions/ids\" + }\n },\n \"required\" : [ \"name\", \"method\", \"parameters\" ],\n + \ \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"axis\": + {\n \"type\": \"object\",\n \"properties\": {\n \"$schema\" + : { \"type\": \"string\" },\n \"type\": { \"type\": \"string\", \"enum\": + [\"Axis\"] },\n \"name\": { \"type\": \"string\" },\n \"abbreviation\": + { \"type\": \"string\" },\n \"direction\": { \"type\": \"string\",\n + \ \"enum\": [ \"north\",\n \"northNorthEast\",\n + \ \"northEast\",\n \"eastNorthEast\",\n + \ \"east\",\n \"eastSouthEast\",\n + \ \"southEast\",\n \"southSouthEast\",\n + \ \"south\",\n \"southSouthWest\",\n + \ \"southWest\",\n \"westSouthWest\",\n + \ \"west\",\n \"westNorthWest\",\n + \ \"northWest\",\n \"northNorthWest\",\n + \ \"up\",\n \"down\",\n + \ \"geocentricX\",\n \"geocentricY\",\n + \ \"geocentricZ\",\n \"columnPositive\",\n + \ \"columnNegative\",\n \"rowPositive\",\n + \ \"rowNegative\",\n \"displayRight\",\n + \ \"displayLeft\",\n \"displayUp\",\n + \ \"displayDown\",\n \"forward\",\n + \ \"aft\",\n \"port\",\n + \ \"starboard\",\n \"clockwise\",\n + \ \"counterClockwise\",\n \"towards\",\n + \ \"awayFrom\",\n \"future\",\n + \ \"past\",\n \"unspecified\" + ] },\n \"unit\": { \"$ref\": \"#/definitions/unit\" },\n \"id\": + { \"$ref\": \"#/definitions/id\" },\n \"ids\": { \"$ref\": \"#/definitions/ids\" + }\n },\n \"required\" : [ \"name\", \"abbreviation\", \"direction\" + ],\n \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"bbox\": + {\n \"type\": \"object\",\n \"properties\": {\n \"east_longitude\": + { \"type\": \"number\" },\n \"west_longitude\": { \"type\": \"number\" + },\n \"south_latitude\": { \"type\": \"number\" },\n \"north_latitude\": + { \"type\": \"number\" }\n },\n \"required\" : [ \"east_longitude\", + \"west_longitude\",\n \"south_latitude\", \"north_latitude\" + ],\n \"additionalProperties\": false\n },\n\n \"bound_crs\": {\n + \ \"type\": \"object\",\n \"properties\": {\n \"$schema\" + : { \"type\": \"string\" },\n \"type\": { \"type\": \"string\", \"enum\": + [\"BoundCRS\"] },\n \"source_crs\": { \"$ref\": \"#/definitions/crs\" + },\n \"target_crs\": { \"$ref\": \"#/definitions/crs\" },\n \"transformation\": + { \"$ref\": \"#/definitions/abridged_transformation\" }\n },\n \"required\" + : [ \"source_crs\", \"target_crs\", \"transformation\" ],\n \"additionalProperties\": + false\n },\n\n \"compound_crs\": {\n \"type\": \"object\",\n \"allOf\": + [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": {\n + \ \"type\": { \"type\": \"string\", \"enum\": [\"CompoundCRS\"] },\n + \ \"name\": { \"type\": \"string\" },\n \"components\": {\n + \ \"type\": \"array\",\n \"items\": { \"$ref\": \"#/definitions/crs\" + }\n },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": + {},\n \"bbox\": {},\n \"usages\": {},\n \"remarks\": + {},\n \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\", + \"components\" ],\n \"additionalProperties\": false\n },\n\n \"concatenated_operation\": + {\n \"type\": \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\", \"enum\": + [\"ConcatenatedOperation\"] },\n \"name\": { \"type\": \"string\" },\n + \ \"source_crs\": { \"$ref\": \"#/definitions/crs\" },\n \"target_crs\": + { \"$ref\": \"#/definitions/crs\" },\n \"steps\": {\n \"type\": + \"array\",\n \"items\": { \"$ref\": \"#/definitions/single_operation\" + }\n },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": + {},\n \"bbox\": {},\n \"usages\": {},\n \"remarks\": + {},\n \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\", + \"source_crs\", \"target_crs\", \"steps\" ],\n \"additionalProperties\": + false\n },\n\n \"conversion\": {\n \"type\": \"object\",\n \"properties\": + {\n \"$schema\" : { \"type\": \"string\" },\n \"type\": { \"type\": + \"string\", \"enum\": [\"Conversion\"] },\n \"name\": { \"type\": \"string\" + },\n \"method\": { \"$ref\": \"#/definitions/method\" },\n \"parameters\": + {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/definitions/parameter_value\" + }\n },\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"ids\": + { \"$ref\": \"#/definitions/ids\" }\n },\n \"required\" : [ \"name\", + \"method\" ],\n \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"coordinate_system\": + {\n \"type\": \"object\",\n \"properties\": {\n \"$schema\" + : { \"type\": \"string\" },\n \"type\": { \"type\": \"string\", \"enum\": + [\"CoordinateSystem\"] },\n \"name\": { \"type\": \"string\" },\n \"subtype\": + { \"type\": \"string\",\n \"enum\": [\"Cartesian\",\n + \ \"spherical\",\n \"ellipsoidal\",\n + \ \"vertical\",\n \"ordinal\",\n + \ \"parametric\",\n \"TemporalDateTime\",\n + \ \"TemporalCount\",\n \"TemporalMeasure\"] + \ },\n \"axis\": {\n \"type\": \"array\",\n \"items\": + { \"$ref\": \"#/definitions/axis\" }\n },\n \"id\": { \"$ref\": + \"#/definitions/id\" },\n \"ids\": { \"$ref\": \"#/definitions/ids\" + }\n },\n \"required\" : [ \"subtype\", \"axis\" ],\n \"allOf\": + [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" }\n ],\n + \ \"additionalProperties\": false\n },\n\n \"crs\": {\n \"oneOf\": + [\n { \"$ref\": \"#/definitions/bound_crs\" },\n { \"$ref\": + \"#/definitions/compound_crs\" },\n { \"$ref\": \"#/definitions/derived_engineering_crs\" + },\n { \"$ref\": \"#/definitions/derived_geodetic_crs\" },\n { + \"$ref\": \"#/definitions/derived_parametric_crs\" },\n { \"$ref\": + \"#/definitions/derived_projected_crs\" },\n { \"$ref\": \"#/definitions/derived_temporal_crs\" + },\n { \"$ref\": \"#/definitions/derived_vertical_crs\" },\n { + \"$ref\": \"#/definitions/engineering_crs\" },\n { \"$ref\": \"#/definitions/geodetic_crs\" + },\n { \"$ref\": \"#/definitions/parametric_crs\" },\n { \"$ref\": + \"#/definitions/projected_crs\" },\n { \"$ref\": \"#/definitions/temporal_crs\" + },\n { \"$ref\": \"#/definitions/vertical_crs\" }\n ]\n },\n\n + \ \"datum\": {\n \"oneOf\": [\n { \"$ref\": \"#/definitions/geodetic_reference_frame\" + },\n { \"$ref\": \"#/definitions/vertical_reference_frame\" },\n { + \"$ref\": \"#/definitions/dynamic_geodetic_reference_frame\" },\n { + \"$ref\": \"#/definitions/dynamic_vertical_reference_frame\" },\n { + \"$ref\": \"#/definitions/temporal_datum\" },\n { \"$ref\": \"#/definitions/parametric_datum\" + },\n { \"$ref\": \"#/definitions/engineering_datum\" }\n ]\n },\n\n + \ \"datum_ensemble\": {\n \"type\": \"object\",\n \"properties\": + {\n \"$schema\" : { \"type\": \"string\" },\n \"type\": { \"type\": + \"string\", \"enum\": [\"DatumEnsemble\"] },\n \"name\": { \"type\": + \"string\" },\n \"members\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"type\": \"object\",\n \"properties\": + {\n \"name\": { \"type\": \"string\" },\n \"id\": + { \"$ref\": \"#/definitions/id\" },\n \"ids\": { \"$ref\": + \"#/definitions/ids\" }\n },\n \"required\" + : [ \"name\" ],\n \"allOf\": [\n { \"$ref\": + \"#/definitions/id_ids_mutually_exclusive\" }\n ],\n \"additionalProperties\": + false\n }\n },\n \"ellipsoid\": { \"$ref\": \"#/definitions/ellipsoid\" + },\n \"accuracy\": { \"type\": \"string\" },\n \"id\": { \"$ref\": + \"#/definitions/id\" },\n \"ids\": { \"$ref\": \"#/definitions/ids\" + }\n },\n \"required\" : [ \"name\", \"members\", \"accuracy\" ],\n + \ \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"derived_engineering_crs\": + {\n \"type\": \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\",\n + \ \"enum\": [\"DerivedEngineeringCRS\"] },\n \"name\": + { \"type\": \"string\" },\n \"base_crs\": { \"$ref\": \"#/definitions/engineering_crs\" + },\n \"conversion\": { \"$ref\": \"#/definitions/conversion\" },\n + \ \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"usages\": {},\n \"remarks\": {},\n + \ \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\", + \"base_crs\", \"conversion\", \"coordinate_system\" ],\n \"additionalProperties\": + false\n },\n\n \"derived_geodetic_crs\": {\n \"type\": \"object\",\n + \ \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": + {\n \"type\": { \"type\": \"string\",\n \"enum\": + [\"DerivedGeodeticCRS\",\n \"DerivedGeographicCRS\"] + },\n \"name\": { \"type\": \"string\" },\n \"base_crs\": { \"$ref\": + \"#/definitions/geodetic_crs\" },\n \"conversion\": { \"$ref\": \"#/definitions/conversion\" + },\n \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"usages\": {},\n \"remarks\": {},\n + \ \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\", + \"base_crs\", \"conversion\", \"coordinate_system\" ],\n \"additionalProperties\": + false\n },\n\n \"derived_parametric_crs\": {\n \"type\": \"object\",\n + \ \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": + {\n \"type\": { \"type\": \"string\",\n \"enum\": + [\"DerivedParametricCRS\"] },\n \"name\": { \"type\": \"string\" },\n + \ \"base_crs\": { \"$ref\": \"#/definitions/parametric_crs\" },\n \"conversion\": + { \"$ref\": \"#/definitions/conversion\" },\n \"coordinate_system\": + { \"$ref\": \"#/definitions/coordinate_system\" },\n \"$schema\" : + {},\n \"scope\": {},\n \"area\": {},\n \"bbox\": {},\n + \ \"usages\": {},\n \"remarks\": {},\n \"id\": {}, \"ids\": + {}\n },\n \"required\" : [ \"name\", \"base_crs\", \"conversion\", + \"coordinate_system\" ],\n \"additionalProperties\": false\n },\n\n + \ \"derived_projected_crs\": {\n \"type\": \"object\",\n \"allOf\": + [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": {\n + \ \"type\": { \"type\": \"string\",\n \"enum\": [\"DerivedProjectedCRS\"] + },\n \"name\": { \"type\": \"string\" },\n \"base_crs\": { \"$ref\": + \"#/definitions/projected_crs\" },\n \"conversion\": { \"$ref\": \"#/definitions/conversion\" + },\n \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"usages\": {},\n \"remarks\": {},\n + \ \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\", + \"base_crs\", \"conversion\", \"coordinate_system\" ],\n \"additionalProperties\": + false\n },\n\n \"derived_temporal_crs\": {\n \"type\": \"object\",\n + \ \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": + {\n \"type\": { \"type\": \"string\",\n \"enum\": + [\"DerivedTemporalCRS\"] },\n \"name\": { \"type\": \"string\" },\n + \ \"base_crs\": { \"$ref\": \"#/definitions/temporal_crs\" },\n \"conversion\": + { \"$ref\": \"#/definitions/conversion\" },\n \"coordinate_system\": + { \"$ref\": \"#/definitions/coordinate_system\" },\n \"$schema\" : + {},\n \"scope\": {},\n \"area\": {},\n \"bbox\": {},\n + \ \"usages\": {},\n \"remarks\": {},\n \"id\": {}, \"ids\": + {}\n },\n \"required\" : [ \"name\", \"base_crs\", \"conversion\", + \"coordinate_system\" ],\n \"additionalProperties\": false\n },\n\n + \ \"derived_vertical_crs\": {\n \"type\": \"object\",\n \"allOf\": + [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": {\n + \ \"type\": { \"type\": \"string\",\n \"enum\": [\"DerivedVerticalCRS\"] + },\n \"name\": { \"type\": \"string\" },\n \"base_crs\": { \"$ref\": + \"#/definitions/vertical_crs\" },\n \"conversion\": { \"$ref\": \"#/definitions/conversion\" + },\n \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"usages\": {},\n \"remarks\": {},\n + \ \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\", + \"base_crs\", \"conversion\", \"coordinate_system\" ],\n \"additionalProperties\": + false\n },\n\n \"dynamic_geodetic_reference_frame\": {\n \"type\": + \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/geodetic_reference_frame\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\", \"enum\": + [\"DynamicGeodeticReferenceFrame\"] },\n \"name\": {},\n \"anchor\": + {},\n \"ellipsoid\": {},\n \"prime_meridian\": {},\n \"frame_reference_epoch\": + { \"type\": \"number\" },\n \"deformation_model\": { \"type\": \"string\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"usages\": {},\n \"remarks\": {},\n + \ \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\", + \"ellipsoid\", \"frame_reference_epoch\" ],\n \"additionalProperties\": + false\n },\n\n \"dynamic_vertical_reference_frame\": {\n \"type\": + \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/vertical_reference_frame\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\", \"enum\": + [\"DynamicVerticalReferenceFrame\"] },\n \"name\": {},\n \"anchor\": + {},\n \"frame_reference_epoch\": { \"type\": \"number\" },\n \"deformation_model\": + { \"type\": \"string\" },\n \"$schema\" : {},\n \"scope\": {},\n + \ \"area\": {},\n \"bbox\": {},\n \"usages\": {},\n \"remarks\": + {},\n \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\", + \"frame_reference_epoch\" ],\n \"additionalProperties\": false\n },\n\n + \ \"ellipsoid\": {\n \"type\": \"object\",\n \"oneOf\":[\n {\n + \ \"properties\": {\n \"$schema\" : { \"type\": \"string\" + },\n \"type\": { \"type\": \"string\", \"enum\": [\"Ellipsoid\"] + },\n \"name\": { \"type\": \"string\" },\n \"semi_major_axis\": + { \"$ref\": \"#/definitions/value_in_metre_or_value_and_unit\" },\n \"semi_minor_axis\": + { \"$ref\": \"#/definitions/value_in_metre_or_value_and_unit\" },\n \"id\": + { \"$ref\": \"#/definitions/id\" },\n \"ids\": { \"$ref\": \"#/definitions/ids\" + }\n },\n \"required\" : [ \"name\", \"semi_major_axis\", + \"semi_minor_axis\" ],\n \"additionalProperties\": false\n },\n + \ {\n \"properties\": {\n \"$schema\" : { \"type\": + \"string\" },\n \"type\": { \"type\": \"string\", \"enum\": [\"Ellipsoid\"] + },\n \"name\": { \"type\": \"string\" },\n \"semi_major_axis\": + { \"$ref\": \"#/definitions/value_in_metre_or_value_and_unit\" },\n \"inverse_flattening\": + { \"type\": \"number\" },\n \"id\": { \"$ref\": \"#/definitions/id\" + },\n \"ids\": { \"$ref\": \"#/definitions/ids\" }\n },\n + \ \"required\" : [ \"name\", \"semi_major_axis\", \"inverse_flattening\" + ],\n \"additionalProperties\": false\n },\n {\n \"properties\": + {\n \"$schema\" : { \"type\": \"string\" },\n \"type\": + { \"type\": \"string\", \"enum\": [\"Ellipsoid\"] },\n \"name\": + { \"type\": \"string\" },\n \"radius\": { \"$ref\": \"#/definitions/value_in_metre_or_value_and_unit\" + },\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"ids\": + { \"$ref\": \"#/definitions/ids\" }\n },\n \"required\" + : [ \"name\", \"radius\" ],\n \"additionalProperties\": false\n }\n + \ ],\n \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ]\n },\n\n \"engineering_crs\": {\n \"type\": \"object\",\n + \ \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": + {\n \"type\": { \"type\": \"string\", \"enum\": [\"EngineeringCRS\"] + },\n \"name\": { \"type\": \"string\" },\n \"datum\": { \"$ref\": + \"#/definitions/engineering_datum\" },\n \"coordinate_system\": { \"$ref\": + \"#/definitions/coordinate_system\" },\n \"$schema\" : {},\n \"scope\": + {},\n \"area\": {},\n \"bbox\": {},\n \"usages\": {},\n + \ \"remarks\": {},\n \"id\": {}, \"ids\": {}\n },\n \"required\" + : [ \"name\", \"datum\" ],\n \"additionalProperties\": false\n },\n\n + \ \"engineering_datum\": {\n \"type\": \"object\",\n \"allOf\": + [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": {\n + \ \"type\": { \"type\": \"string\", \"enum\": [\"EngineeringDatum\"] + },\n \"name\": { \"type\": \"string\" },\n \"anchor\": { \"type\": + \"string\" },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": + {},\n \"bbox\": {},\n \"usages\": {},\n \"remarks\": + {},\n \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\" + ],\n \"additionalProperties\": false\n },\n\n \"geodetic_crs\": + {\n \"type\": \"object\",\n \"properties\": {\n \"type\": + { \"type\": \"string\", \"enum\": [\"GeodeticCRS\", \"GeographicCRS\"] },\n + \ \"name\": { \"type\": \"string\" },\n \"datum\": {\n \"oneOf\": + [\n { \"$ref\": \"#/definitions/geodetic_reference_frame\" + },\n { \"$ref\": \"#/definitions/dynamic_geodetic_reference_frame\" + }\n ]\n },\n \"datum_ensemble\": { \"$ref\": \"#/definitions/datum_ensemble\" + },\n \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": {},\n + \ \"bbox\": {},\n \"usages\": {},\n \"remarks\": {},\n + \ \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\" + ],\n \"description\": \"One and only one of datum and datum_ensemble + must be provided\",\n \"allOf\": [\n { \"$ref\": \"#/definitions/object_usage\" + },\n { \"$ref\": \"#/definitions/one_and_only_one_of_datum_or_datum_ensemble\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"geodetic_reference_frame\": + {\n \"type\": \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\", \"enum\": + [\"GeodeticReferenceFrame\"] },\n \"name\": { \"type\": \"string\" + },\n \"anchor\": { \"type\": \"string\" },\n \"ellipsoid\": + { \"$ref\": \"#/definitions/ellipsoid\" },\n \"prime_meridian\": { + \"$ref\": \"#/definitions/prime_meridian\" },\n \"$schema\" : {},\n + \ \"scope\": {},\n \"area\": {},\n \"bbox\": {},\n \"usages\": + {},\n \"remarks\": {},\n \"id\": {}, \"ids\": {}\n },\n + \ \"required\" : [ \"name\", \"ellipsoid\" ],\n \"additionalProperties\": + false\n },\n\n \"id\": {\n \"type\": \"object\",\n \"properties\": + {\n \"authority\": { \"type\": \"string\" },\n \"code\": {\n + \ \"oneOf\": [ { \"type\": \"string\" }, { \"type\": \"integer\" } + ]\n }\n },\n \"required\" : [ \"authority\", \"code\" ],\n + \ \"additionalProperties\": false\n },\n\n \"ids\": {\n \"type\": + \"array\",\n \"items\": { \"$ref\": \"#/definitions/id\" }\n },\n\n + \ \"method\": {\n \"type\": \"object\",\n \"properties\": {\n + \ \"$schema\" : { \"type\": \"string\" },\n \"type\": { \"type\": + \"string\", \"enum\": [\"OperationMethod\"]},\n \"name\": { \"type\": + \"string\" },\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"ids\": + { \"$ref\": \"#/definitions/ids\" }\n },\n \"required\" : [ \"name\" + ],\n \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"id_ids_mutually_exclusive\": + {\n \"not\": {\n \"type\": \"object\",\n \"required\": + [ \"id\", \"ids\" ]\n }\n },\n\n \"one_and_only_one_of_datum_or_datum_ensemble\": + {\n \"allOf\": [\n {\n \"not\": {\n \"type\": + \"object\",\n \"required\": [ \"datum\", \"datum_ensemble\" + ]\n }\n },\n {\n \"oneOf\": [\n { + \"type\": \"object\", \"required\": [\"datum\"] },\n { \"type\": + \"object\", \"required\": [\"datum_ensemble\"] }\n ]\n }\n + \ ]\n },\n\n \"object_usage\": {\n \"anyOf\": [\n {\n + \ \"type\": \"object\",\n \"properties\": {\n \"$schema\" + : { \"type\": \"string\" },\n \"scope\": { \"type\": \"string\" + },\n \"area\": { \"type\": \"string\" },\n \"bbox\": + { \"$ref\": \"#/definitions/bbox\" },\n \"remarks\": { \"type\": + \"string\" },\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"ids\": + { \"$ref\": \"#/definitions/ids\" }\n },\n \"allOf\": [\n { + \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" }\n ]\n },\n + \ {\n \"type\": \"object\",\n \"properties\": {\n \"$schema\" + : { \"type\": \"string\" },\n \"usages\": { \"$ref\": \"#/definitions/usages\" + },\n \"remarks\": { \"type\": \"string\" },\n \"id\": + { \"$ref\": \"#/definitions/id\" },\n \"ids\": { \"$ref\": \"#/definitions/ids\" + }\n },\n \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ]\n }\n ]\n },\n\n \"parameter_value\": {\n \"type\": + \"object\",\n \"properties\": {\n \"$schema\" : { \"type\": \"string\" + },\n \"type\": { \"type\": \"string\", \"enum\": [\"ParameterValue\"] + },\n \"name\": { \"type\": \"string\" },\n \"value\": {\n \"oneOf\": + [\n { \"type\": \"string\" },\n { \"type\": \"number\" + }\n ]\n },\n \"unit\": { \"$ref\": \"#/definitions/unit\" + },\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"ids\": + { \"$ref\": \"#/definitions/ids\" }\n },\n \"required\" : [ \"name\", + \"value\" ],\n \"allOf\": [\n { \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"parametric_crs\": + {\n \"type\": \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\", \"enum\": + [\"ParametricCRS\"] },\n \"name\": { \"type\": \"string\" },\n \"datum\": + { \"$ref\": \"#/definitions/parametric_datum\" },\n \"coordinate_system\": + { \"$ref\": \"#/definitions/coordinate_system\" },\n \"$schema\" : + {},\n \"scope\": {},\n \"area\": {},\n \"bbox\": {},\n + \ \"usages\": {},\n \"remarks\": {},\n \"id\": {}, \"ids\": + {}\n },\n \"required\" : [ \"name\", \"datum\" ],\n \"additionalProperties\": + false\n },\n\n \"parametric_datum\": {\n \"type\": \"object\",\n + \ \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": + {\n \"type\": { \"type\": \"string\", \"enum\": [\"ParametricDatum\"] + },\n \"name\": { \"type\": \"string\" },\n \"anchor\": { \"type\": + \"string\" },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": + {},\n \"bbox\": {},\n \"usages\": {},\n \"remarks\": + {},\n \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\" + ],\n \"additionalProperties\": false\n },\n\n \"prime_meridian\": + {\n \"type\": \"object\",\n \"properties\": {\n \"$schema\" + : { \"type\": \"string\" },\n \"type\": { \"type\": \"string\", \"enum\": + [\"PrimeMeridian\"] },\n \"name\": { \"type\": \"string\" },\n \"longitude\": + { \"$ref\": \"#/definitions/value_in_degree_or_value_and_unit\" },\n \"id\": + { \"$ref\": \"#/definitions/id\" },\n \"ids\": { \"$ref\": \"#/definitions/ids\" + }\n },\n \"required\" : [ \"name\" ],\n \"allOf\": [\n { + \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" }\n ],\n \"additionalProperties\": + false\n },\n\n \"single_operation\": {\n \"oneOf\": [\n { + \"$ref\": \"#/definitions/conversion\" },\n { \"$ref\": \"#/definitions/transformation\" + }\n ]\n },\n\n \"projected_crs\": {\n \"type\": \"object\",\n + \ \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": + {\n \"type\": { \"type\": \"string\",\n \"enum\": + [\"ProjectedCRS\"] },\n \"name\": { \"type\": \"string\" },\n \"base_crs\": + { \"$ref\": \"#/definitions/geodetic_crs\" },\n \"conversion\": { \"$ref\": + \"#/definitions/conversion\" },\n \"coordinate_system\": { \"$ref\": + \"#/definitions/coordinate_system\" },\n \"$schema\" : {},\n \"scope\": + {},\n \"area\": {},\n \"bbox\": {},\n \"usages\": {},\n + \ \"remarks\": {},\n \"id\": {}, \"ids\": {}\n },\n \"required\" + : [ \"name\", \"base_crs\", \"conversion\", \"coordinate_system\" ],\n \"additionalProperties\": + false\n },\n\n \"temporal_crs\": {\n \"type\": \"object\",\n \"allOf\": + [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": {\n + \ \"type\": { \"type\": \"string\", \"enum\": [\"TemporalCRS\"] },\n + \ \"name\": { \"type\": \"string\" },\n \"datum\": { \"$ref\": + \"#/definitions/temporal_datum\" },\n \"coordinate_system\": { \"$ref\": + \"#/definitions/coordinate_system\" },\n \"$schema\" : {},\n \"scope\": + {},\n \"area\": {},\n \"bbox\": {},\n \"usages\": {},\n + \ \"remarks\": {},\n \"id\": {}, \"ids\": {}\n },\n \"required\" + : [ \"name\", \"datum\" ],\n \"additionalProperties\": false\n },\n\n + \ \"temporal_datum\": {\n \"type\": \"object\",\n \"allOf\": [{ + \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": {\n \"type\": + { \"type\": \"string\", \"enum\": [\"TemporalDatum\"] },\n \"name\": + { \"type\": \"string\" },\n \"calendar\": { \"type\": \"string\" },\n + \ \"time_origin\": { \"type\": \"string\" },\n \"$schema\" : + {},\n \"scope\": {},\n \"area\": {},\n \"bbox\": {},\n + \ \"usages\": {},\n \"remarks\": {},\n \"id\": {}, \"ids\": + {}\n },\n \"required\" : [ \"name\", \"calendar\" ],\n \"additionalProperties\": + false\n },\n\n \"transformation\": {\n \"type\": \"object\",\n + \ \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" }],\n \"properties\": + {\n \"type\": { \"type\": \"string\", \"enum\": [\"Transformation\"] + },\n \"name\": { \"type\": \"string\" },\n \"source_crs\": { + \"$ref\": \"#/definitions/crs\" },\n \"target_crs\": { \"$ref\": \"#/definitions/crs\" + },\n \"interpolation_crs\": { \"$ref\": \"#/definitions/crs\" },\n + \ \"method\": { \"$ref\": \"#/definitions/method\" },\n \"parameters\": + {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/definitions/parameter_value\" + }\n },\n \"accuracy\": { \"type\": \"string\" },\n \"$schema\" + : {},\n \"scope\": {},\n \"area\": {},\n \"bbox\": {},\n + \ \"usages\": {},\n \"remarks\": {},\n \"id\": {}, \"ids\": + {}\n },\n \"required\" : [ \"name\", \"source_crs\", \"target_crs\", + \"method\", \"parameters\" ],\n \"additionalProperties\": false\n },\n\n + \ \"unit\": {\n \"oneOf\": [\n {\n \"type\": \"string\",\n + \ \"enum\": [\"metre\", \"degree\", \"unity\"]\n },\n {\n + \ \"type\": \"object\",\n \"properties\": {\n \"type\": + { \"type\": \"string\",\n \"enum\": [\"LinearUnit\", \"AngularUnit\", + \"ScaleUnit\",\n \"TimeUnit\", \"ParametricUnit\", + \"Unit\"] },\n \"name\": { \"type\": \"string\" },\n \"conversion_factor\": + { \"type\": \"number\" },\n \"id\": { \"$ref\": \"#/definitions/id\" + },\n \"ids\": { \"$ref\": \"#/definitions/ids\" }\n },\n + \ \"required\" : [ \"type\", \"name\" ],\n \"allOf\": [\n { + \"$ref\": \"#/definitions/id_ids_mutually_exclusive\" }\n ],\n \"additionalProperties\": + false\n }\n ]\n },\n\n \"usages\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"type\": \"object\",\n \"properties\": + {\n \"scope\": { \"type\": \"string\" },\n \"area\": + { \"type\": \"string\" },\n \"bbox\": { \"$ref\": \"#/definitions/bbox\" + }\n },\n \"additionalProperties\": false\n }\n },\n\n + \ \"value_and_unit\": {\n \"type\": \"object\",\n \"properties\": + {\n \"value\": { \"type\": \"number\" },\n \"unit\": { \"$ref\": + \"#/definitions/unit\" }\n },\n \"required\" : [ \"value\", \"unit\" + ],\n \"additionalProperties\": false\n },\n\n \"value_in_degree_or_value_and_unit\": + {\n \"oneOf\": [\n { \"type\": \"number\" },\n { \"$ref\": + \"#/definitions/value_and_unit\" }\n ]\n },\n\n \"value_in_metre_or_value_and_unit\": + {\n \"oneOf\": [\n { \"type\": \"number\" },\n { \"$ref\": + \"#/definitions/value_and_unit\" }\n ]\n },\n\n \"vertical_crs\": + {\n \"type\": \"object\",\n \"properties\": {\n \"type\": + { \"type\": \"string\", \"enum\": [\"VerticalCRS\"] },\n \"name\": + { \"type\": \"string\" },\n \"datum\": {\n \"oneOf\": [\n + \ { \"$ref\": \"#/definitions/vertical_reference_frame\" },\n + \ { \"$ref\": \"#/definitions/dynamic_vertical_reference_frame\" + }\n ]\n },\n \"datum_ensemble\": { \"$ref\": \"#/definitions/datum_ensemble\" + },\n \"coordinate_system\": { \"$ref\": \"#/definitions/coordinate_system\" + },\n \"geoid_model\": {\n \"type\": \"object\",\n \"properties\": + {\n \"name\": { \"type\": \"string\" },\n \"interpolation_crs\": + { \"$ref\": \"#/definitions/crs\" },\n \"id\": { \"$ref\": \"#/definitions/id\" + }\n },\n \"required\" : [ \"name\" ],\n \"additionalProperties\": + false\n },\n \"$schema\" : {},\n \"scope\": {},\n \"area\": + {},\n \"bbox\": {},\n \"usages\": {},\n \"remarks\": + {},\n \"id\": {}, \"ids\": {}\n },\n \"required\" : [ \"name\"],\n + \ \"description\": \"One and only one of datum and datum_ensemble must + be provided\",\n \"allOf\": [\n { \"$ref\": \"#/definitions/object_usage\" + },\n { \"$ref\": \"#/definitions/one_and_only_one_of_datum_or_datum_ensemble\" + }\n ],\n \"additionalProperties\": false\n },\n\n \"vertical_reference_frame\": + {\n \"type\": \"object\",\n \"allOf\": [{ \"$ref\": \"#/definitions/object_usage\" + }],\n \"properties\": {\n \"type\": { \"type\": \"string\", \"enum\": + [\"VerticalReferenceFrame\"] },\n \"name\": { \"type\": \"string\" + },\n \"anchor\": { \"type\": \"string\" },\n \"$schema\" : {},\n + \ \"scope\": {},\n \"area\": {},\n \"bbox\": {},\n \"usages\": + {},\n \"remarks\": {},\n \"id\": {}, \"ids\": {}\n },\n + \ \"required\" : [ \"name\" ],\n \"additionalProperties\": false\n + \ }\n\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_read_file[path11].yaml b/tests/cassettes/test_examples/test_example_read_file[path11].yaml new file mode 100644 index 000000000..5562e8a96 --- /dev/null +++ b/tests/cassettes/test_examples/test_example_read_file[path11].yaml @@ -0,0 +1,255 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v2.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items and + STAC Collections.\",\n \"type\": \"object\",\n \"required\": [\"stac_extensions\", + \"type\"],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v2.0.0/schema.json\"\n + \ }\n }\n },\n \"allOf\": [\n {\n \"$comment\": \"Items\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/validate_assets\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"required\": + [\"properties\"],\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/require_assets\"\n + \ }\n ]\n }\n },\n {\n \"$comment\": \"Collections\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/validate_bands\"\n },\n + \ {\n \"$ref\": \"#/definitions/validate_assets\"\n },\n + \ {\n \"properties\": {\n \"item_assets\": + {\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/validate_properties\"\n }\n }\n + \ }\n },\n {\n \"properties\": {\n + \ \"summaries\": {\n \"type\": \"object\",\n \"$comment\": + \"We can't properly validate summary objects types (min/max or schemas) yet.\",\n + \ \"allOf\": [\n {\n \"$ref\": + \"#/definitions/validate_bands\"\n },\n {\n + \ \"properties\": {\n \"eo:cloud_cover\": + {\n \"type\": [\"array\", \"object\"],\n \"items\": + {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ }\n }\n }\n + \ },\n {\n \"properties\": + {\n \"eo:snow_cover\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:snow_cover\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:common_name\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:common_name\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:center_wavelength\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:center_wavelength\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:full_width_half_max\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:full_width_half_max\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:solar_illumination\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:solar_illumination\"\n }\n }\n + \ }\n }\n ]\n }\n + \ }\n }\n ],\n \"anyOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_in_bands\"\n },\n {\n + \ \"$ref\": \"#/definitions/require_assets\"\n },\n {\n + \ \"required\": [\"item_assets\"],\n \"properties\": + {\n \"item_assets\": {\n \"$ref\": \"#/definitions/asset_contains\"\n + \ }\n }\n },\n {\n \"required\": + [\"summaries\"],\n \"properties\": {\n \"summaries\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n ]\n }\n }\n ],\n \"definitions\": + {\n \"require_properties\": {\n \"anyOf\": [\n {\n \"$ref\": + \"#/definitions/require_fields\"\n },\n {\n \"$ref\": + \"#/definitions/require_in_bands\"\n }\n ]\n },\n \"validate_bands\": + {\n \"type\": \"object\",\n \"properties\": {\n \"bands\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n \"validate_properties\": + {\n \"allOf\": [\n {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n {\n \"$ref\": \"#/definitions/validate_bands\"\n + \ }\n ]\n },\n \"require_assets\": {\n \"required\": + [\"assets\"],\n \"properties\": {\n \"assets\": {\n \"$ref\": + \"#/definitions/asset_contains\"\n }\n }\n },\n \"validate_assets\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n }\n },\n \"asset_contains\": {\n \"type\": \"object\",\n + \ \"not\": {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n },\n \"require_in_bands\": {\n \"required\": + [\"bands\"],\n \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"contains\": {\n \"$ref\": \"#/definitions/require_fields\"\n + \ }\n }\n }\n },\n \"require_fields\": {\n \"anyOf\": + [\n { \"required\": [\"eo:cloud_cover\"] },\n { \"required\": + [\"eo:snow_cover\"] },\n { \"required\": [\"eo:common_name\"] },\n + \ { \"required\": [\"eo:center_wavelength\"] },\n { \"required\": + [\"eo:full_width_half_max\"] },\n { \"required\": [\"eo:solar_illumination\"] + }\n ]\n },\n \"fields\": {\n \"type\": \"object\",\n \"properties\": + {\n \"eo:cloud_cover\": {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ },\n \"eo:snow_cover\": {\n \"$ref\": \"#/definitions/eo:snow_cover\"\n + \ },\n \"eo:common_name\": {\n \"$ref\": \"#/definitions/eo:common_name\"\n + \ },\n \"eo:center_wavelength\": {\n \"$ref\": \"#/definitions/eo:center_wavelength\"\n + \ },\n \"eo:full_width_half_max\": {\n \"$ref\": \"#/definitions/eo:full_width_half_max\"\n + \ },\n \"eo:solar_illumination\": {\n \"$ref\": \"#/definitions/eo:solar_illumination\"\n + \ }\n },\n \"patternProperties\": {\n \"^(?!eo:)\": + {}\n },\n \"additionalProperties\": false\n },\n \"eo:cloud_cover\": + {\n \"title\": \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:snow_cover\": {\n \"title\": + \"Snow and Ice Cover\",\n \"type\": \"number\",\n \"minimum\": 0,\n + \ \"maximum\": 100\n },\n \"eo:common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"pan\",\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"green05\",\n \"yellow\",\n \"red\",\n \"rededge\",\n + \ \"rededge071\",\n \"rededge075\",\n \"rededge078\",\n + \ \"nir\",\n \"nir08\",\n \"nir09\",\n \"cirrus\",\n + \ \"swir16\",\n \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"eo:center_wavelength\": {\n \"title\": + \"Center Wavelength\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:full_width_half_max\": {\n \"title\": \"Full Width + Half Max (FWHM)\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:solar_illumination\": {\n \"title\": \"Solar Illumination\",\n + \ \"type\": \"number\",\n \"minimum\": 0\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/sat/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/sat/v1.0.0/schema.json\",\n \"title\": + \"Satellite Extension\",\n \"description\": \"STAC Sat Extension to a STAC + Item.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC Items.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\n \"required\": [\n \"sat:platform_international_designator\"\n + \ ]\n },\n {\n \"required\": + [\n \"sat:orbit_state\"\n ]\n + \ },\n {\n \"required\": + [\n \"sat:absolute_orbit\"\n ]\n + \ },\n {\n \"required\": + [\n \"sat:relative_orbit\"\n ]\n + \ },\n {\n \"required\": + [\n \"sat:anx_datetime\"\n ]\n + \ }\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/sat/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"sat:platform_international_designator\": + {\n \"type\": \"string\"\n },\n \"sat:orbit_state\": + {\n \"title\": \"Orbit State\",\n \"type\": \"string\",\n + \ \"enum\": [\n \"ascending\",\n \"descending\",\n + \ \"geostationary\"\n ]\n },\n \"sat:absolute_orbit\": + {\n \"type\": \"integer\",\n \"minimum\": 1\n },\n + \ \"sat:relative_orbit\": {\n \"type\": \"integer\",\n \"minimum\": + 1\n },\n \"sat:anx_datetime\": {\n \"type\": \"string\",\n + \ \"format\": \"date-time\"\n }\n },\n \"patternProperties\": + {\n \"^(?!sat:)\": {\n \"$comment\": \"Do not allow unspecified + fields prefixed with sat:\"\n }\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_read_file[path12].yaml b/tests/cassettes/test_examples/test_example_read_file[path12].yaml new file mode 100644 index 000000000..b543e00c0 --- /dev/null +++ b/tests/cassettes/test_examples/test_example_read_file[path12].yaml @@ -0,0 +1,263 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v2.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items and + STAC Collections.\",\n \"type\": \"object\",\n \"required\": [\"stac_extensions\", + \"type\"],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v2.0.0/schema.json\"\n + \ }\n }\n },\n \"allOf\": [\n {\n \"$comment\": \"Items\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/validate_assets\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"required\": + [\"properties\"],\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/require_assets\"\n + \ }\n ]\n }\n },\n {\n \"$comment\": \"Collections\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/validate_bands\"\n },\n + \ {\n \"$ref\": \"#/definitions/validate_assets\"\n },\n + \ {\n \"properties\": {\n \"item_assets\": + {\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/validate_properties\"\n }\n }\n + \ }\n },\n {\n \"properties\": {\n + \ \"summaries\": {\n \"type\": \"object\",\n \"$comment\": + \"We can't properly validate summary objects types (min/max or schemas) yet.\",\n + \ \"allOf\": [\n {\n \"$ref\": + \"#/definitions/validate_bands\"\n },\n {\n + \ \"properties\": {\n \"eo:cloud_cover\": + {\n \"type\": [\"array\", \"object\"],\n \"items\": + {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ }\n }\n }\n + \ },\n {\n \"properties\": + {\n \"eo:snow_cover\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:snow_cover\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:common_name\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:common_name\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:center_wavelength\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:center_wavelength\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:full_width_half_max\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:full_width_half_max\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:solar_illumination\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:solar_illumination\"\n }\n }\n + \ }\n }\n ]\n }\n + \ }\n }\n ],\n \"anyOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_in_bands\"\n },\n {\n + \ \"$ref\": \"#/definitions/require_assets\"\n },\n {\n + \ \"required\": [\"item_assets\"],\n \"properties\": + {\n \"item_assets\": {\n \"$ref\": \"#/definitions/asset_contains\"\n + \ }\n }\n },\n {\n \"required\": + [\"summaries\"],\n \"properties\": {\n \"summaries\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n ]\n }\n }\n ],\n \"definitions\": + {\n \"require_properties\": {\n \"anyOf\": [\n {\n \"$ref\": + \"#/definitions/require_fields\"\n },\n {\n \"$ref\": + \"#/definitions/require_in_bands\"\n }\n ]\n },\n \"validate_bands\": + {\n \"type\": \"object\",\n \"properties\": {\n \"bands\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n \"validate_properties\": + {\n \"allOf\": [\n {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n {\n \"$ref\": \"#/definitions/validate_bands\"\n + \ }\n ]\n },\n \"require_assets\": {\n \"required\": + [\"assets\"],\n \"properties\": {\n \"assets\": {\n \"$ref\": + \"#/definitions/asset_contains\"\n }\n }\n },\n \"validate_assets\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n }\n },\n \"asset_contains\": {\n \"type\": \"object\",\n + \ \"not\": {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n },\n \"require_in_bands\": {\n \"required\": + [\"bands\"],\n \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"contains\": {\n \"$ref\": \"#/definitions/require_fields\"\n + \ }\n }\n }\n },\n \"require_fields\": {\n \"anyOf\": + [\n { \"required\": [\"eo:cloud_cover\"] },\n { \"required\": + [\"eo:snow_cover\"] },\n { \"required\": [\"eo:common_name\"] },\n + \ { \"required\": [\"eo:center_wavelength\"] },\n { \"required\": + [\"eo:full_width_half_max\"] },\n { \"required\": [\"eo:solar_illumination\"] + }\n ]\n },\n \"fields\": {\n \"type\": \"object\",\n \"properties\": + {\n \"eo:cloud_cover\": {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ },\n \"eo:snow_cover\": {\n \"$ref\": \"#/definitions/eo:snow_cover\"\n + \ },\n \"eo:common_name\": {\n \"$ref\": \"#/definitions/eo:common_name\"\n + \ },\n \"eo:center_wavelength\": {\n \"$ref\": \"#/definitions/eo:center_wavelength\"\n + \ },\n \"eo:full_width_half_max\": {\n \"$ref\": \"#/definitions/eo:full_width_half_max\"\n + \ },\n \"eo:solar_illumination\": {\n \"$ref\": \"#/definitions/eo:solar_illumination\"\n + \ }\n },\n \"patternProperties\": {\n \"^(?!eo:)\": + {}\n },\n \"additionalProperties\": false\n },\n \"eo:cloud_cover\": + {\n \"title\": \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:snow_cover\": {\n \"title\": + \"Snow and Ice Cover\",\n \"type\": \"number\",\n \"minimum\": 0,\n + \ \"maximum\": 100\n },\n \"eo:common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"pan\",\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"green05\",\n \"yellow\",\n \"red\",\n \"rededge\",\n + \ \"rededge071\",\n \"rededge075\",\n \"rededge078\",\n + \ \"nir\",\n \"nir08\",\n \"nir09\",\n \"cirrus\",\n + \ \"swir16\",\n \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"eo:center_wavelength\": {\n \"title\": + \"Center Wavelength\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:full_width_half_max\": {\n \"title\": \"Full Width + Half Max (FWHM)\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:solar_illumination\": {\n \"title\": \"Solar Illumination\",\n + \ \"type\": \"number\",\n \"minimum\": 0\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"$comment\": \"This schema succeeds if the proj: fields + are not used at all, please keep this in mind.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"properties\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"properties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n }\n ]\n },\n + \ {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"proj:code\":{\n \"title\":\"Projection + code\",\n \"type\":[\n \"string\",\n \"null\"\n + \ ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.7/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_read_file[path13].yaml b/tests/cassettes/test_examples/test_example_read_file[path13].yaml new file mode 100644 index 000000000..b543e00c0 --- /dev/null +++ b/tests/cassettes/test_examples/test_example_read_file[path13].yaml @@ -0,0 +1,263 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v2.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items and + STAC Collections.\",\n \"type\": \"object\",\n \"required\": [\"stac_extensions\", + \"type\"],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v2.0.0/schema.json\"\n + \ }\n }\n },\n \"allOf\": [\n {\n \"$comment\": \"Items\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/validate_assets\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"required\": + [\"properties\"],\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/require_assets\"\n + \ }\n ]\n }\n },\n {\n \"$comment\": \"Collections\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/validate_bands\"\n },\n + \ {\n \"$ref\": \"#/definitions/validate_assets\"\n },\n + \ {\n \"properties\": {\n \"item_assets\": + {\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/validate_properties\"\n }\n }\n + \ }\n },\n {\n \"properties\": {\n + \ \"summaries\": {\n \"type\": \"object\",\n \"$comment\": + \"We can't properly validate summary objects types (min/max or schemas) yet.\",\n + \ \"allOf\": [\n {\n \"$ref\": + \"#/definitions/validate_bands\"\n },\n {\n + \ \"properties\": {\n \"eo:cloud_cover\": + {\n \"type\": [\"array\", \"object\"],\n \"items\": + {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ }\n }\n }\n + \ },\n {\n \"properties\": + {\n \"eo:snow_cover\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:snow_cover\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:common_name\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:common_name\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:center_wavelength\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:center_wavelength\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:full_width_half_max\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:full_width_half_max\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:solar_illumination\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:solar_illumination\"\n }\n }\n + \ }\n }\n ]\n }\n + \ }\n }\n ],\n \"anyOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_in_bands\"\n },\n {\n + \ \"$ref\": \"#/definitions/require_assets\"\n },\n {\n + \ \"required\": [\"item_assets\"],\n \"properties\": + {\n \"item_assets\": {\n \"$ref\": \"#/definitions/asset_contains\"\n + \ }\n }\n },\n {\n \"required\": + [\"summaries\"],\n \"properties\": {\n \"summaries\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n ]\n }\n }\n ],\n \"definitions\": + {\n \"require_properties\": {\n \"anyOf\": [\n {\n \"$ref\": + \"#/definitions/require_fields\"\n },\n {\n \"$ref\": + \"#/definitions/require_in_bands\"\n }\n ]\n },\n \"validate_bands\": + {\n \"type\": \"object\",\n \"properties\": {\n \"bands\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n \"validate_properties\": + {\n \"allOf\": [\n {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n {\n \"$ref\": \"#/definitions/validate_bands\"\n + \ }\n ]\n },\n \"require_assets\": {\n \"required\": + [\"assets\"],\n \"properties\": {\n \"assets\": {\n \"$ref\": + \"#/definitions/asset_contains\"\n }\n }\n },\n \"validate_assets\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n }\n },\n \"asset_contains\": {\n \"type\": \"object\",\n + \ \"not\": {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n },\n \"require_in_bands\": {\n \"required\": + [\"bands\"],\n \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"contains\": {\n \"$ref\": \"#/definitions/require_fields\"\n + \ }\n }\n }\n },\n \"require_fields\": {\n \"anyOf\": + [\n { \"required\": [\"eo:cloud_cover\"] },\n { \"required\": + [\"eo:snow_cover\"] },\n { \"required\": [\"eo:common_name\"] },\n + \ { \"required\": [\"eo:center_wavelength\"] },\n { \"required\": + [\"eo:full_width_half_max\"] },\n { \"required\": [\"eo:solar_illumination\"] + }\n ]\n },\n \"fields\": {\n \"type\": \"object\",\n \"properties\": + {\n \"eo:cloud_cover\": {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ },\n \"eo:snow_cover\": {\n \"$ref\": \"#/definitions/eo:snow_cover\"\n + \ },\n \"eo:common_name\": {\n \"$ref\": \"#/definitions/eo:common_name\"\n + \ },\n \"eo:center_wavelength\": {\n \"$ref\": \"#/definitions/eo:center_wavelength\"\n + \ },\n \"eo:full_width_half_max\": {\n \"$ref\": \"#/definitions/eo:full_width_half_max\"\n + \ },\n \"eo:solar_illumination\": {\n \"$ref\": \"#/definitions/eo:solar_illumination\"\n + \ }\n },\n \"patternProperties\": {\n \"^(?!eo:)\": + {}\n },\n \"additionalProperties\": false\n },\n \"eo:cloud_cover\": + {\n \"title\": \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:snow_cover\": {\n \"title\": + \"Snow and Ice Cover\",\n \"type\": \"number\",\n \"minimum\": 0,\n + \ \"maximum\": 100\n },\n \"eo:common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"pan\",\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"green05\",\n \"yellow\",\n \"red\",\n \"rededge\",\n + \ \"rededge071\",\n \"rededge075\",\n \"rededge078\",\n + \ \"nir\",\n \"nir08\",\n \"nir09\",\n \"cirrus\",\n + \ \"swir16\",\n \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"eo:center_wavelength\": {\n \"title\": + \"Center Wavelength\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:full_width_half_max\": {\n \"title\": \"Full Width + Half Max (FWHM)\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:solar_illumination\": {\n \"title\": \"Solar Illumination\",\n + \ \"type\": \"number\",\n \"minimum\": 0\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"$comment\": \"This schema succeeds if the proj: fields + are not used at all, please keep this in mind.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"properties\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"properties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n }\n ]\n },\n + \ {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"proj:code\":{\n \"title\":\"Projection + code\",\n \"type\":[\n \"string\",\n \"null\"\n + \ ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.7/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_read_file[path14].yaml b/tests/cassettes/test_examples/test_example_read_file[path14].yaml new file mode 100644 index 000000000..c6a6f459c --- /dev/null +++ b/tests/cassettes/test_examples/test_example_read_file[path14].yaml @@ -0,0 +1,64 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_read_file[path16].yaml b/tests/cassettes/test_examples/test_example_read_file[path16].yaml new file mode 100644 index 000000000..7f15ee3df --- /dev/null +++ b/tests/cassettes/test_examples/test_example_read_file[path16].yaml @@ -0,0 +1,422 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v2.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items and + STAC Collections.\",\n \"type\": \"object\",\n \"required\": [\"stac_extensions\", + \"type\"],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v2.0.0/schema.json\"\n + \ }\n }\n },\n \"allOf\": [\n {\n \"$comment\": \"Items\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/validate_assets\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"required\": + [\"properties\"],\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/require_assets\"\n + \ }\n ]\n }\n },\n {\n \"$comment\": \"Collections\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/validate_bands\"\n },\n + \ {\n \"$ref\": \"#/definitions/validate_assets\"\n },\n + \ {\n \"properties\": {\n \"item_assets\": + {\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/validate_properties\"\n }\n }\n + \ }\n },\n {\n \"properties\": {\n + \ \"summaries\": {\n \"type\": \"object\",\n \"$comment\": + \"We can't properly validate summary objects types (min/max or schemas) yet.\",\n + \ \"allOf\": [\n {\n \"$ref\": + \"#/definitions/validate_bands\"\n },\n {\n + \ \"properties\": {\n \"eo:cloud_cover\": + {\n \"type\": [\"array\", \"object\"],\n \"items\": + {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ }\n }\n }\n + \ },\n {\n \"properties\": + {\n \"eo:snow_cover\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:snow_cover\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:common_name\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:common_name\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:center_wavelength\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:center_wavelength\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:full_width_half_max\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:full_width_half_max\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:solar_illumination\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:solar_illumination\"\n }\n }\n + \ }\n }\n ]\n }\n + \ }\n }\n ],\n \"anyOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_in_bands\"\n },\n {\n + \ \"$ref\": \"#/definitions/require_assets\"\n },\n {\n + \ \"required\": [\"item_assets\"],\n \"properties\": + {\n \"item_assets\": {\n \"$ref\": \"#/definitions/asset_contains\"\n + \ }\n }\n },\n {\n \"required\": + [\"summaries\"],\n \"properties\": {\n \"summaries\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n ]\n }\n }\n ],\n \"definitions\": + {\n \"require_properties\": {\n \"anyOf\": [\n {\n \"$ref\": + \"#/definitions/require_fields\"\n },\n {\n \"$ref\": + \"#/definitions/require_in_bands\"\n }\n ]\n },\n \"validate_bands\": + {\n \"type\": \"object\",\n \"properties\": {\n \"bands\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n \"validate_properties\": + {\n \"allOf\": [\n {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n {\n \"$ref\": \"#/definitions/validate_bands\"\n + \ }\n ]\n },\n \"require_assets\": {\n \"required\": + [\"assets\"],\n \"properties\": {\n \"assets\": {\n \"$ref\": + \"#/definitions/asset_contains\"\n }\n }\n },\n \"validate_assets\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n }\n },\n \"asset_contains\": {\n \"type\": \"object\",\n + \ \"not\": {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n },\n \"require_in_bands\": {\n \"required\": + [\"bands\"],\n \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"contains\": {\n \"$ref\": \"#/definitions/require_fields\"\n + \ }\n }\n }\n },\n \"require_fields\": {\n \"anyOf\": + [\n { \"required\": [\"eo:cloud_cover\"] },\n { \"required\": + [\"eo:snow_cover\"] },\n { \"required\": [\"eo:common_name\"] },\n + \ { \"required\": [\"eo:center_wavelength\"] },\n { \"required\": + [\"eo:full_width_half_max\"] },\n { \"required\": [\"eo:solar_illumination\"] + }\n ]\n },\n \"fields\": {\n \"type\": \"object\",\n \"properties\": + {\n \"eo:cloud_cover\": {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ },\n \"eo:snow_cover\": {\n \"$ref\": \"#/definitions/eo:snow_cover\"\n + \ },\n \"eo:common_name\": {\n \"$ref\": \"#/definitions/eo:common_name\"\n + \ },\n \"eo:center_wavelength\": {\n \"$ref\": \"#/definitions/eo:center_wavelength\"\n + \ },\n \"eo:full_width_half_max\": {\n \"$ref\": \"#/definitions/eo:full_width_half_max\"\n + \ },\n \"eo:solar_illumination\": {\n \"$ref\": \"#/definitions/eo:solar_illumination\"\n + \ }\n },\n \"patternProperties\": {\n \"^(?!eo:)\": + {}\n },\n \"additionalProperties\": false\n },\n \"eo:cloud_cover\": + {\n \"title\": \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:snow_cover\": {\n \"title\": + \"Snow and Ice Cover\",\n \"type\": \"number\",\n \"minimum\": 0,\n + \ \"maximum\": 100\n },\n \"eo:common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"pan\",\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"green05\",\n \"yellow\",\n \"red\",\n \"rededge\",\n + \ \"rededge071\",\n \"rededge075\",\n \"rededge078\",\n + \ \"nir\",\n \"nir08\",\n \"nir09\",\n \"cirrus\",\n + \ \"swir16\",\n \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"eo:center_wavelength\": {\n \"title\": + \"Center Wavelength\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:full_width_half_max\": {\n \"title\": \"Full Width + Half Max (FWHM)\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:solar_illumination\": {\n \"title\": \"Solar Illumination\",\n + \ \"type\": \"number\",\n \"minimum\": 0\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"$comment\": \"This schema succeeds if the proj: fields + are not used at all, please keep this in mind.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"properties\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"properties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n }\n ]\n },\n + \ {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"proj:code\":{\n \"title\":\"Projection + code\",\n \"type\":[\n \"string\",\n \"null\"\n + \ ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.7/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/scientific/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/scientific/v1.0.0/schema.json#\",\n \"title\": + \"Scientific Citation Extension\",\n \"description\": \"Scientific Citation + Extension for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items. Remove this object + if this extension only applies to Collections.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/stac_extensions\"\n },\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"properties\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"properties\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"$comment\": \"This validates the fields in Item Assets, + but does not require them.\",\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n }\n ]\n },\n {\n \"$comment\": \"This + is the schema for STAC Collections.\",\n \"type\": \"object\",\n \"allOf\": + [\n {\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n {\n \"$ref\": + \"#/definitions/stac_extensions\"\n }\n ],\n \"anyOf\": [\n + \ {\n \"$comment\": \"This is the schema for the top-level + fields in a Collection. Remove this if this extension does not define top-level + fields for Collections.\",\n \"allOf\": [\n {\n \"$ref\": + \"#/definitions/require_any_field\"\n },\n {\n \"$ref\": + \"#/definitions/fields\"\n }\n ]\n },\n {\n + \ \"$comment\": \"This validates the fields in Collection Assets, + but does not require them.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"properties\": {\n \"assets\": {\n \"type\": + \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n }\n }\n + \ }\n }\n }\n },\n {\n \"$comment\": + \"This is the schema for the fields in Item Asset Definitions. It doesn't + require any fields.\",\n \"required\": [\n \"item_assets\"\n + \ ],\n \"properties\": {\n \"item_assets\": {\n + \ \"type\": \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n }\n }\n + \ }\n }\n }\n },\n {\n \"$comment\": + \"This is the schema for the fields in Summaries. By default, only checks + the existance of the properties, but not the schema of the summaries.\",\n + \ \"required\": [\n \"summaries\"\n ],\n \"properties\": + {\n \"summaries\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/scientific/v1.0.0/schema.json\"\n }\n + \ }\n }\n },\n \"require_any_field\": {\n \"$comment\": + \"Please list all fields here so that we can force the existance of one of + them in other parts of the schemas.\",\n \"anyOf\": [\n {\"required\": + [\"sci:doi\"]},\n {\"required\": [\"sci:citation\"]},\n {\"required\": + [\"sci:publications\"]}\n ]\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"sci:doi\": + {\n \"type\": \"string\",\n \"title\": \"Data DOI\",\n \"pattern\": + \"^10\\\\.[0-9a-zA-Z]{4,}/[^\\\\s]+$\"\n }, \n \"sci:citation\": + {\n \"type\": \"string\", \n \"title\": \"Proposed Data + Citation\"\n },\n \"sci:publications\": {\n \"type\": + \"array\",\n \"title\": \"Publications\",\n \"items\": {\n + \ \"type\": \"object\",\n \"properties\": {\n \"doi\": + {\n \"type\": \"string\",\n \"title\": \"Publication + DOI\",\n \"pattern\": \"^10\\\\.[0-9a-zA-Z]{4,}/[^\\\\s]+$\"\n + \ }, \n \"citation\": { \n \"type\": + \"string\", \n \"title\": \"Publication Citation\"\n }\n + \ }\n }\n }\n },\n \"patternProperties\": + {\n \"^(?!sci:)\": {}\n },\n \"additionalProperties\": false\n + \ }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/remote-data/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/remote-data/v1.0.0/schema.json\",\n \"title\": + \"Remote Data (Example) Extension\",\n \"description\": \"STAC Example Extension + for fictional vendor Remote Data\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Required fields here for item properties.\",\n \"required\": + [\n \"rd:type\",\n \"rd:product_level\",\n + \ \"rd:sat_id\"\n ]\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"rd:visibility\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$comment\": \"Remove this + and the following object if this is not an extension to a Collection.\",\n + \ \"$ref\": \"#/definitions/stac_extensions\"\n },\n {\n + \ \"$ref\": \"#/definitions/collection_fields\"\n }\n ]\n + \ }\n ],\n \"definitions\": {\n \"stac_extensions\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/remote-data/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"collection_fields\": {\n \"properties\": + {\n \"rd:visibility\": {\n \"type\": \"string\",\n \"enum\": + [\n \"public\",\n \"protected\",\n \"private\"\n + \ ]\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Remote Data fictional fields.\",\n \"type\": \"object\",\n \"properties\": + {\n \"rd:type\": {\n \"type\": \"string\",\n \"enum\": + [\n \"scene\"\n ]\n },\n \"rd:product_level\": + {\n \"type\": \"string\",\n \"enum\": [\n \"LV1A\",\n + \ \"LV1B\",\n \"LV2A\",\n \"LV2B\",\n \"LV3A\",\n + \ \"LV3B\"\n ]\n }, \n \"rd:runs\": {\n \"type\": + \"array\", \n \"items\": {\n \"type\": \"string\"\n }\n + \ },\n \"rd:parsecs\": {\n \"type\": \"array\", \n \"items\": + {\n \"type\": \"number\"\n }\n },\n \"rd:anomalous_pixels\": + {\n \"type\": \"number\"\n },\n \"rd:sat_id\": {\n + \ \"type\": \"string\"\n },\n \"rd:earth_sun_distance\": + {\n \"type\": \"number\"\n },\n \"rd:flux_capacitor\": + {\n \"type\": \"boolean\"\n }\n },\n \"patternProperties\": + {\n \"^(?!rd:)\": {\n \"$comment\": \"Disallow other fields + with rd: prefix\"\n }\n },\n \"additionalProperties\": false\n + \ }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_read_file[path18].yaml b/tests/cassettes/test_examples/test_example_read_file[path18].yaml new file mode 100644 index 000000000..41e4e31cb --- /dev/null +++ b/tests/cassettes/test_examples/test_example_read_file[path18].yaml @@ -0,0 +1,201 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v2.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items and + STAC Collections.\",\n \"type\": \"object\",\n \"required\": [\"stac_extensions\", + \"type\"],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v2.0.0/schema.json\"\n + \ }\n }\n },\n \"allOf\": [\n {\n \"$comment\": \"Items\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/validate_assets\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"required\": + [\"properties\"],\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/require_assets\"\n + \ }\n ]\n }\n },\n {\n \"$comment\": \"Collections\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/validate_bands\"\n },\n + \ {\n \"$ref\": \"#/definitions/validate_assets\"\n },\n + \ {\n \"properties\": {\n \"item_assets\": + {\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/validate_properties\"\n }\n }\n + \ }\n },\n {\n \"properties\": {\n + \ \"summaries\": {\n \"type\": \"object\",\n \"$comment\": + \"We can't properly validate summary objects types (min/max or schemas) yet.\",\n + \ \"allOf\": [\n {\n \"$ref\": + \"#/definitions/validate_bands\"\n },\n {\n + \ \"properties\": {\n \"eo:cloud_cover\": + {\n \"type\": [\"array\", \"object\"],\n \"items\": + {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ }\n }\n }\n + \ },\n {\n \"properties\": + {\n \"eo:snow_cover\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:snow_cover\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:common_name\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:common_name\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:center_wavelength\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:center_wavelength\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:full_width_half_max\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:full_width_half_max\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:solar_illumination\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:solar_illumination\"\n }\n }\n + \ }\n }\n ]\n }\n + \ }\n }\n ],\n \"anyOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_in_bands\"\n },\n {\n + \ \"$ref\": \"#/definitions/require_assets\"\n },\n {\n + \ \"required\": [\"item_assets\"],\n \"properties\": + {\n \"item_assets\": {\n \"$ref\": \"#/definitions/asset_contains\"\n + \ }\n }\n },\n {\n \"required\": + [\"summaries\"],\n \"properties\": {\n \"summaries\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n ]\n }\n }\n ],\n \"definitions\": + {\n \"require_properties\": {\n \"anyOf\": [\n {\n \"$ref\": + \"#/definitions/require_fields\"\n },\n {\n \"$ref\": + \"#/definitions/require_in_bands\"\n }\n ]\n },\n \"validate_bands\": + {\n \"type\": \"object\",\n \"properties\": {\n \"bands\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n \"validate_properties\": + {\n \"allOf\": [\n {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n {\n \"$ref\": \"#/definitions/validate_bands\"\n + \ }\n ]\n },\n \"require_assets\": {\n \"required\": + [\"assets\"],\n \"properties\": {\n \"assets\": {\n \"$ref\": + \"#/definitions/asset_contains\"\n }\n }\n },\n \"validate_assets\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n }\n },\n \"asset_contains\": {\n \"type\": \"object\",\n + \ \"not\": {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n },\n \"require_in_bands\": {\n \"required\": + [\"bands\"],\n \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"contains\": {\n \"$ref\": \"#/definitions/require_fields\"\n + \ }\n }\n }\n },\n \"require_fields\": {\n \"anyOf\": + [\n { \"required\": [\"eo:cloud_cover\"] },\n { \"required\": + [\"eo:snow_cover\"] },\n { \"required\": [\"eo:common_name\"] },\n + \ { \"required\": [\"eo:center_wavelength\"] },\n { \"required\": + [\"eo:full_width_half_max\"] },\n { \"required\": [\"eo:solar_illumination\"] + }\n ]\n },\n \"fields\": {\n \"type\": \"object\",\n \"properties\": + {\n \"eo:cloud_cover\": {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ },\n \"eo:snow_cover\": {\n \"$ref\": \"#/definitions/eo:snow_cover\"\n + \ },\n \"eo:common_name\": {\n \"$ref\": \"#/definitions/eo:common_name\"\n + \ },\n \"eo:center_wavelength\": {\n \"$ref\": \"#/definitions/eo:center_wavelength\"\n + \ },\n \"eo:full_width_half_max\": {\n \"$ref\": \"#/definitions/eo:full_width_half_max\"\n + \ },\n \"eo:solar_illumination\": {\n \"$ref\": \"#/definitions/eo:solar_illumination\"\n + \ }\n },\n \"patternProperties\": {\n \"^(?!eo:)\": + {}\n },\n \"additionalProperties\": false\n },\n \"eo:cloud_cover\": + {\n \"title\": \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:snow_cover\": {\n \"title\": + \"Snow and Ice Cover\",\n \"type\": \"number\",\n \"minimum\": 0,\n + \ \"maximum\": 100\n },\n \"eo:common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"pan\",\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"green05\",\n \"yellow\",\n \"red\",\n \"rededge\",\n + \ \"rededge071\",\n \"rededge075\",\n \"rededge078\",\n + \ \"nir\",\n \"nir08\",\n \"nir09\",\n \"cirrus\",\n + \ \"swir16\",\n \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"eo:center_wavelength\": {\n \"title\": + \"Center Wavelength\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:full_width_half_max\": {\n \"title\": \"Full Width + Half Max (FWHM)\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:solar_illumination\": {\n \"title\": \"Solar Illumination\",\n + \ \"type\": \"number\",\n \"minimum\": 0\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"$comment\": \"This schema succeeds if the proj: fields + are not used at all, please keep this in mind.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"properties\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"properties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n }\n ]\n },\n + \ {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"proj:code\":{\n \"title\":\"Projection + code\",\n \"type\":[\n \"string\",\n \"null\"\n + \ ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.7/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_read_file[path1].yaml b/tests/cassettes/test_examples/test_example_read_file[path1].yaml new file mode 100644 index 000000000..a0a42e384 --- /dev/null +++ b/tests/cassettes/test_examples/test_example_read_file[path1].yaml @@ -0,0 +1,204 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v1.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Items.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"properties\": {\n \"$ref\": + \"#/definitions/fields\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"$comment\": + \"The if-then-else below checks whether the eo:bands is given in assets or + not. If yes, allows eo:bands in properties (else), otherwise, disallows eo:bands + in properties (then).\",\n \"if\": {\n \"required\": [\n + \ \"assets\"\n ],\n \"properties\": {\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"properties\": {\n \"eo:bands\": + false\n }\n }\n }\n }\n + \ },\n \"then\": {\n \"properties\": {\n \"properties\": + {\n \"properties\": {\n \"eo:bands\": false\n + \ }\n }\n }\n },\n \"else\": + {\n \"properties\": {\n \"properties\": {\n \"properties\": + {\n \"eo:bands\": {\n \"$ref\": \"#/definitions/bands\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"eo:cloud_cover\": {\n \"title\": + \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!eo:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"title\": \"Band\",\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"properties\": + {\n \"name\": {\n \"title\": \"Name of the band\",\n \"type\": + \"string\"\n },\n \"common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"red\",\n \"rededge\",\n \"yellow\",\n + \ \"pan\",\n \"nir\",\n \"nir08\",\n + \ \"nir09\",\n \"cirrus\",\n \"swir16\",\n + \ \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"center_wavelength\": + {\n \"title\": \"Center Wavelength\",\n \"type\": \"number\"\n + \ },\n \"full_width_half_max\": {\n \"title\": + \"Full Width Half Max (FWHM)\",\n \"type\": \"number\"\n }\n + \ }\n }\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/sat/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/sat/v1.0.0/schema.json\",\n \"title\": + \"Satellite Extension\",\n \"description\": \"STAC Sat Extension to a STAC + Item.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC Items.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\n \"required\": [\n \"sat:platform_international_designator\"\n + \ ]\n },\n {\n \"required\": + [\n \"sat:orbit_state\"\n ]\n + \ },\n {\n \"required\": + [\n \"sat:absolute_orbit\"\n ]\n + \ },\n {\n \"required\": + [\n \"sat:relative_orbit\"\n ]\n + \ },\n {\n \"required\": + [\n \"sat:anx_datetime\"\n ]\n + \ }\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/sat/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"sat:platform_international_designator\": + {\n \"type\": \"string\"\n },\n \"sat:orbit_state\": + {\n \"title\": \"Orbit State\",\n \"type\": \"string\",\n + \ \"enum\": [\n \"ascending\",\n \"descending\",\n + \ \"geostationary\"\n ]\n },\n \"sat:absolute_orbit\": + {\n \"type\": \"integer\",\n \"minimum\": 1\n },\n + \ \"sat:relative_orbit\": {\n \"type\": \"integer\",\n \"minimum\": + 1\n },\n \"sat:anx_datetime\": {\n \"type\": \"string\",\n + \ \"format\": \"date-time\"\n }\n },\n \"patternProperties\": + {\n \"^(?!sat:)\": {\n \"$comment\": \"Do not allow unspecified + fields prefixed with sat:\"\n }\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_read_file[path2].yaml b/tests/cassettes/test_examples/test_example_read_file[path2].yaml new file mode 100644 index 000000000..92b20ee3b --- /dev/null +++ b/tests/cassettes/test_examples/test_example_read_file[path2].yaml @@ -0,0 +1,216 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v1.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Items.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"properties\": {\n \"$ref\": + \"#/definitions/fields\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"$comment\": + \"The if-then-else below checks whether the eo:bands is given in assets or + not. If yes, allows eo:bands in properties (else), otherwise, disallows eo:bands + in properties (then).\",\n \"if\": {\n \"required\": [\n + \ \"assets\"\n ],\n \"properties\": {\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"properties\": {\n \"eo:bands\": + false\n }\n }\n }\n }\n + \ },\n \"then\": {\n \"properties\": {\n \"properties\": + {\n \"properties\": {\n \"eo:bands\": false\n + \ }\n }\n }\n },\n \"else\": + {\n \"properties\": {\n \"properties\": {\n \"properties\": + {\n \"eo:bands\": {\n \"$ref\": \"#/definitions/bands\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"eo:cloud_cover\": {\n \"title\": + \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!eo:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"title\": \"Band\",\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"properties\": + {\n \"name\": {\n \"title\": \"Name of the band\",\n \"type\": + \"string\"\n },\n \"common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"red\",\n \"rededge\",\n \"yellow\",\n + \ \"pan\",\n \"nir\",\n \"nir08\",\n + \ \"nir09\",\n \"cirrus\",\n \"swir16\",\n + \ \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"center_wavelength\": + {\n \"title\": \"Center Wavelength\",\n \"type\": \"number\"\n + \ },\n \"full_width_half_max\": {\n \"title\": + \"Full Width Half Max (FWHM)\",\n \"type\": \"number\"\n }\n + \ }\n }\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the + schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Require fields here for item properties.\",\n \"required\": + [\n \"proj:epsg\"\n ]\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"proj:epsg\":{\n + \ \"title\":\"EPSG code\",\n \"type\":[\n \"integer\",\n + \ \"null\"\n ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.2/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_read_file[path3].yaml b/tests/cassettes/test_examples/test_example_read_file[path3].yaml new file mode 100644 index 000000000..92b20ee3b --- /dev/null +++ b/tests/cassettes/test_examples/test_example_read_file[path3].yaml @@ -0,0 +1,216 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v1.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Items.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"properties\": {\n \"$ref\": + \"#/definitions/fields\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"$comment\": + \"The if-then-else below checks whether the eo:bands is given in assets or + not. If yes, allows eo:bands in properties (else), otherwise, disallows eo:bands + in properties (then).\",\n \"if\": {\n \"required\": [\n + \ \"assets\"\n ],\n \"properties\": {\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"properties\": {\n \"eo:bands\": + false\n }\n }\n }\n }\n + \ },\n \"then\": {\n \"properties\": {\n \"properties\": + {\n \"properties\": {\n \"eo:bands\": false\n + \ }\n }\n }\n },\n \"else\": + {\n \"properties\": {\n \"properties\": {\n \"properties\": + {\n \"eo:bands\": {\n \"$ref\": \"#/definitions/bands\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"eo:cloud_cover\": {\n \"title\": + \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!eo:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"title\": \"Band\",\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"properties\": + {\n \"name\": {\n \"title\": \"Name of the band\",\n \"type\": + \"string\"\n },\n \"common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"red\",\n \"rededge\",\n \"yellow\",\n + \ \"pan\",\n \"nir\",\n \"nir08\",\n + \ \"nir09\",\n \"cirrus\",\n \"swir16\",\n + \ \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"center_wavelength\": + {\n \"title\": \"Center Wavelength\",\n \"type\": \"number\"\n + \ },\n \"full_width_half_max\": {\n \"title\": + \"Full Width Half Max (FWHM)\",\n \"type\": \"number\"\n }\n + \ }\n }\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the + schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Require fields here for item properties.\",\n \"required\": + [\n \"proj:epsg\"\n ]\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"proj:epsg\":{\n + \ \"title\":\"EPSG code\",\n \"type\":[\n \"integer\",\n + \ \"null\"\n ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.2/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_read_file[path4].yaml b/tests/cassettes/test_examples/test_example_read_file[path4].yaml new file mode 100644 index 000000000..8a8c2e0e6 --- /dev/null +++ b/tests/cassettes/test_examples/test_example_read_file[path4].yaml @@ -0,0 +1,140 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v1.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Items.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"properties\": {\n \"$ref\": + \"#/definitions/fields\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"$comment\": + \"The if-then-else below checks whether the eo:bands is given in assets or + not. If yes, allows eo:bands in properties (else), otherwise, disallows eo:bands + in properties (then).\",\n \"if\": {\n \"required\": [\n + \ \"assets\"\n ],\n \"properties\": {\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"properties\": {\n \"eo:bands\": + false\n }\n }\n }\n }\n + \ },\n \"then\": {\n \"properties\": {\n \"properties\": + {\n \"properties\": {\n \"eo:bands\": false\n + \ }\n }\n }\n },\n \"else\": + {\n \"properties\": {\n \"properties\": {\n \"properties\": + {\n \"eo:bands\": {\n \"$ref\": \"#/definitions/bands\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"eo:cloud_cover\": {\n \"title\": + \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!eo:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"title\": \"Band\",\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"properties\": + {\n \"name\": {\n \"title\": \"Name of the band\",\n \"type\": + \"string\"\n },\n \"common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"red\",\n \"rededge\",\n \"yellow\",\n + \ \"pan\",\n \"nir\",\n \"nir08\",\n + \ \"nir09\",\n \"cirrus\",\n \"swir16\",\n + \ \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"center_wavelength\": + {\n \"title\": \"Center Wavelength\",\n \"type\": \"number\"\n + \ },\n \"full_width_half_max\": {\n \"title\": + \"Full Width Half Max (FWHM)\",\n \"type\": \"number\"\n }\n + \ }\n }\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_read_file[path6].yaml b/tests/cassettes/test_examples/test_example_read_file[path6].yaml new file mode 100644 index 000000000..e799c741e --- /dev/null +++ b/tests/cassettes/test_examples/test_example_read_file[path6].yaml @@ -0,0 +1,375 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v1.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Items.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"properties\": {\n \"$ref\": + \"#/definitions/fields\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"$comment\": + \"The if-then-else below checks whether the eo:bands is given in assets or + not. If yes, allows eo:bands in properties (else), otherwise, disallows eo:bands + in properties (then).\",\n \"if\": {\n \"required\": [\n + \ \"assets\"\n ],\n \"properties\": {\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"properties\": {\n \"eo:bands\": + false\n }\n }\n }\n }\n + \ },\n \"then\": {\n \"properties\": {\n \"properties\": + {\n \"properties\": {\n \"eo:bands\": false\n + \ }\n }\n }\n },\n \"else\": + {\n \"properties\": {\n \"properties\": {\n \"properties\": + {\n \"eo:bands\": {\n \"$ref\": \"#/definitions/bands\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"eo:cloud_cover\": {\n \"title\": + \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!eo:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"title\": \"Band\",\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"properties\": + {\n \"name\": {\n \"title\": \"Name of the band\",\n \"type\": + \"string\"\n },\n \"common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"red\",\n \"rededge\",\n \"yellow\",\n + \ \"pan\",\n \"nir\",\n \"nir08\",\n + \ \"nir09\",\n \"cirrus\",\n \"swir16\",\n + \ \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"center_wavelength\": + {\n \"title\": \"Center Wavelength\",\n \"type\": \"number\"\n + \ },\n \"full_width_half_max\": {\n \"title\": + \"Full Width Half Max (FWHM)\",\n \"type\": \"number\"\n }\n + \ }\n }\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the + schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Require fields here for item properties.\",\n \"required\": + [\n \"proj:epsg\"\n ]\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"proj:epsg\":{\n + \ \"title\":\"EPSG code\",\n \"type\":[\n \"integer\",\n + \ \"null\"\n ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.2/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/scientific/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/scientific/v1.0.0/schema.json#\",\n \"title\": + \"Scientific Citation Extension\",\n \"description\": \"Scientific Citation + Extension for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items. Remove this object + if this extension only applies to Collections.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/stac_extensions\"\n },\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"properties\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"properties\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"$comment\": \"This validates the fields in Item Assets, + but does not require them.\",\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n }\n ]\n },\n {\n \"$comment\": \"This + is the schema for STAC Collections.\",\n \"type\": \"object\",\n \"allOf\": + [\n {\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n {\n \"$ref\": + \"#/definitions/stac_extensions\"\n }\n ],\n \"anyOf\": [\n + \ {\n \"$comment\": \"This is the schema for the top-level + fields in a Collection. Remove this if this extension does not define top-level + fields for Collections.\",\n \"allOf\": [\n {\n \"$ref\": + \"#/definitions/require_any_field\"\n },\n {\n \"$ref\": + \"#/definitions/fields\"\n }\n ]\n },\n {\n + \ \"$comment\": \"This validates the fields in Collection Assets, + but does not require them.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"properties\": {\n \"assets\": {\n \"type\": + \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n }\n }\n + \ }\n }\n }\n },\n {\n \"$comment\": + \"This is the schema for the fields in Item Asset Definitions. It doesn't + require any fields.\",\n \"required\": [\n \"item_assets\"\n + \ ],\n \"properties\": {\n \"item_assets\": {\n + \ \"type\": \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n }\n }\n + \ }\n }\n }\n },\n {\n \"$comment\": + \"This is the schema for the fields in Summaries. By default, only checks + the existance of the properties, but not the schema of the summaries.\",\n + \ \"required\": [\n \"summaries\"\n ],\n \"properties\": + {\n \"summaries\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/scientific/v1.0.0/schema.json\"\n }\n + \ }\n }\n },\n \"require_any_field\": {\n \"$comment\": + \"Please list all fields here so that we can force the existance of one of + them in other parts of the schemas.\",\n \"anyOf\": [\n {\"required\": + [\"sci:doi\"]},\n {\"required\": [\"sci:citation\"]},\n {\"required\": + [\"sci:publications\"]}\n ]\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"sci:doi\": + {\n \"type\": \"string\",\n \"title\": \"Data DOI\",\n \"pattern\": + \"^10\\\\.[0-9a-zA-Z]{4,}/[^\\\\s]+$\"\n }, \n \"sci:citation\": + {\n \"type\": \"string\", \n \"title\": \"Proposed Data + Citation\"\n },\n \"sci:publications\": {\n \"type\": + \"array\",\n \"title\": \"Publications\",\n \"items\": {\n + \ \"type\": \"object\",\n \"properties\": {\n \"doi\": + {\n \"type\": \"string\",\n \"title\": \"Publication + DOI\",\n \"pattern\": \"^10\\\\.[0-9a-zA-Z]{4,}/[^\\\\s]+$\"\n + \ }, \n \"citation\": { \n \"type\": + \"string\", \n \"title\": \"Publication Citation\"\n }\n + \ }\n }\n }\n },\n \"patternProperties\": + {\n \"^(?!sci:)\": {}\n },\n \"additionalProperties\": false\n + \ }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/view/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json#\",\n \"title\": + \"View Geometry Extension\",\n \"description\": \"STAC View Geometry Extension + for STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"anyOf\": + [\n {\"required\": [\"view:off_nadir\"]},\n {\"required\": + [\"view:incidence_angle\"]},\n {\"required\": [\"view:azimuth\"]},\n + \ {\"required\": [\"view:sun_azimuth\"]},\n {\"required\": + [\"view:sun_elevation\"]}\n ]\n },\n {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n + \ {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Collection\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/view/v1.0.0/schema.json\"\n }\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"view:off_nadir\": + {\n \"title\": \"Off Nadir\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:incidence_angle\": + {\n \"title\": \"Center incidence angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 90\n },\n \"view:azimuth\": + {\n \"title\": \"Azimuth angle\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_azimuth\": + {\n \"title\": \"Sun Azimuth\",\n \"type\": \"number\",\n + \ \"minimum\": 0,\n \"maximum\": 360\n },\n \"view:sun_elevation\": + {\n \"title\": \"Sun Elevation\",\n \"type\": \"number\",\n + \ \"minimum\": -90,\n \"maximum\": 90\n }\n },\n + \ \"patternProperties\": {\n \"^(?!view:)\": {}\n },\n \"additionalProperties\": + false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/remote-data/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/remote-data/v1.0.0/schema.json\",\n \"title\": + \"Remote Data (Example) Extension\",\n \"description\": \"STAC Example Extension + for fictional vendor Remote Data\",\n \"oneOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Required fields here for item properties.\",\n \"required\": + [\n \"rd:type\",\n \"rd:product_level\",\n + \ \"rd:sat_id\"\n ]\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"rd:visibility\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$comment\": \"Remove this + and the following object if this is not an extension to a Collection.\",\n + \ \"$ref\": \"#/definitions/stac_extensions\"\n },\n {\n + \ \"$ref\": \"#/definitions/collection_fields\"\n }\n ]\n + \ }\n ],\n \"definitions\": {\n \"stac_extensions\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/remote-data/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"collection_fields\": {\n \"properties\": + {\n \"rd:visibility\": {\n \"type\": \"string\",\n \"enum\": + [\n \"public\",\n \"protected\",\n \"private\"\n + \ ]\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Remote Data fictional fields.\",\n \"type\": \"object\",\n \"properties\": + {\n \"rd:type\": {\n \"type\": \"string\",\n \"enum\": + [\n \"scene\"\n ]\n },\n \"rd:product_level\": + {\n \"type\": \"string\",\n \"enum\": [\n \"LV1A\",\n + \ \"LV1B\",\n \"LV2A\",\n \"LV2B\",\n \"LV3A\",\n + \ \"LV3B\"\n ]\n }, \n \"rd:runs\": {\n \"type\": + \"array\", \n \"items\": {\n \"type\": \"string\"\n }\n + \ },\n \"rd:parsecs\": {\n \"type\": \"array\", \n \"items\": + {\n \"type\": \"number\"\n }\n },\n \"rd:anomalous_pixels\": + {\n \"type\": \"number\"\n },\n \"rd:sat_id\": {\n + \ \"type\": \"string\"\n },\n \"rd:earth_sun_distance\": + {\n \"type\": \"number\"\n },\n \"rd:flux_capacitor\": + {\n \"type\": \"boolean\"\n }\n },\n \"patternProperties\": + {\n \"^(?!rd:)\": {\n \"$comment\": \"Disallow other fields + with rd: prefix\"\n }\n },\n \"additionalProperties\": false\n + \ }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_examples/test_example_read_file[path8].yaml b/tests/cassettes/test_examples/test_example_read_file[path8].yaml new file mode 100644 index 000000000..365856314 --- /dev/null +++ b/tests/cassettes/test_examples/test_example_read_file[path8].yaml @@ -0,0 +1,154 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v1.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Items.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"properties\": {\n \"$ref\": + \"#/definitions/fields\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"$comment\": + \"The if-then-else below checks whether the eo:bands is given in assets or + not. If yes, allows eo:bands in properties (else), otherwise, disallows eo:bands + in properties (then).\",\n \"if\": {\n \"required\": [\n + \ \"assets\"\n ],\n \"properties\": {\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"properties\": {\n \"eo:bands\": + false\n }\n }\n }\n }\n + \ },\n \"then\": {\n \"properties\": {\n \"properties\": + {\n \"properties\": {\n \"eo:bands\": false\n + \ }\n }\n }\n },\n \"else\": + {\n \"properties\": {\n \"properties\": {\n \"properties\": + {\n \"eo:bands\": {\n \"$ref\": \"#/definitions/bands\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"eo:cloud_cover\": {\n \"title\": + \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!eo:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"title\": \"Band\",\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"properties\": + {\n \"name\": {\n \"title\": \"Name of the band\",\n \"type\": + \"string\"\n },\n \"common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"red\",\n \"rededge\",\n \"yellow\",\n + \ \"pan\",\n \"nir\",\n \"nir08\",\n + \ \"nir09\",\n \"cirrus\",\n \"swir16\",\n + \ \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"center_wavelength\": + {\n \"title\": \"Center Wavelength\",\n \"type\": \"number\"\n + \ },\n \"full_width_half_max\": {\n \"title\": + \"Full Width Half Max (FWHM)\",\n \"type\": \"number\"\n }\n + \ }\n }\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the + schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Require fields here for item properties.\",\n \"required\": + [\n \"proj:epsg\"\n ]\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"proj:epsg\":{\n + \ \"title\":\"EPSG code\",\n \"type\":[\n \"integer\",\n + \ \"null\"\n ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.2/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_item/test_null_geometry.yaml b/tests/cassettes/test_item/test_null_geometry.yaml deleted file mode 100644 index 25da191a3..000000000 --- a/tests/cassettes/test_item/test_null_geometry.yaml +++ /dev/null @@ -1,215 +0,0 @@ -interactions: -- request: - body: null - headers: {} - method: GET - uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/item.json - response: - body: - string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": - \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/item.json#\",\n - \ \"title\": \"STAC Item\",\n \"type\": \"object\",\n \"description\": \"This - object represents the metadata for an item in a SpatioTemporal Asset Catalog.\",\n - \ \"allOf\": [\n {\n \"$ref\": \"#/definitions/core\"\n }\n ],\n - \ \"definitions\": {\n \"common_metadata\": {\n \"allOf\": [\n {\n - \ \"$ref\": \"basics.json\"\n },\n {\n \"$ref\": - \"datetime.json\"\n },\n {\n \"$ref\": \"instrument.json\"\n - \ },\n {\n \"$ref\": \"licensing.json\"\n },\n - \ {\n \"$ref\": \"provider.json\"\n }\n ]\n },\n - \ \"core\": {\n \"allOf\": [\n {\n \"$ref\": \"https://geojson.org/schema/Feature.json\"\n - \ },\n {\n \"oneOf\": [\n {\n \"type\": - \"object\",\n \"required\": [\n \"geometry\",\n - \ \"bbox\"\n ],\n \"properties\": - {\n \"geometry\": {\n \"$ref\": \"https://geojson.org/schema/Geometry.json\"\n - \ },\n \"bbox\": {\n \"type\": - \"array\",\n \"oneOf\": [\n {\n \"minItems\": - 4,\n \"maxItems\": 4\n },\n {\n - \ \"minItems\": 6,\n \"maxItems\": - 6\n }\n ],\n \"items\": - {\n \"type\": \"number\"\n }\n }\n - \ }\n },\n {\n \"type\": \"object\",\n - \ \"required\": [\n \"geometry\"\n ],\n - \ \"properties\": {\n \"geometry\": {\n \"type\": - \"null\"\n },\n \"bbox\": {\n \"not\": - {}\n }\n }\n }\n ]\n },\n - \ {\n \"type\": \"object\",\n \"required\": [\n \"stac_version\",\n - \ \"id\",\n \"links\",\n \"assets\",\n \"properties\"\n - \ ],\n \"properties\": {\n \"stac_version\": {\n - \ \"title\": \"STAC version\",\n \"type\": \"string\",\n - \ \"const\": \"1.0.0-beta.2\"\n },\n \"stac_extensions\": - {\n \"title\": \"STAC extensions\",\n \"type\": - \"array\",\n \"uniqueItems\": true,\n \"items\": - {\n \"anyOf\": [\n {\n \"title\": - \"Reference to a JSON Schema\",\n \"type\": \"string\",\n - \ \"format\": \"uri\"\n },\n {\n - \ \"title\": \"Reference to a core extension\",\n \"type\": - \"string\"\n }\n ]\n }\n },\n - \ \"id\": {\n \"title\": \"Provider ID\",\n \"description\": - \"Provider item ID\",\n \"type\": \"string\"\n },\n - \ \"links\": {\n \"title\": \"Item links\",\n \"description\": - \"Links to item relations\",\n \"type\": \"array\",\n \"items\": - {\n \"$ref\": \"#/definitions/link\"\n }\n },\n - \ \"assets\": {\n \"$ref\": \"#/definitions/assets\"\n - \ },\n \"properties\": {\n \"$ref\": \"#/definitions/common_metadata\"\n - \ },\n \"collection\": {\n \"title\": \"Collection - ID\",\n \"description\": \"The ID of the STAC Collection this - Item references to.\",\n \"type\": \"string\"\n }\n - \ }\n }\n ]\n },\n \"link\": {\n \"type\": - \"object\",\n \"required\": [\n \"rel\",\n \"href\"\n ],\n - \ \"properties\": {\n \"href\": {\n \"title\": \"Link - reference\",\n \"type\": \"string\"\n },\n \"rel\": - {\n \"title\": \"Link relation type\",\n \"type\": \"string\"\n - \ },\n \"type\": {\n \"title\": \"Link type\",\n \"type\": - \"string\"\n },\n \"title\": {\n \"title\": \"Link - title\",\n \"type\": \"string\"\n },\n \"created\": - {\n \"$ref\": \"datetime.json#/definitions/created\"\n },\n - \ \"updated\": {\n \"$ref\": \"datetime.json#/definitions/updated\"\n - \ }\n }\n },\n \"assets\": {\n \"title\": \"Asset links\",\n - \ \"description\": \"Links to assets\",\n \"type\": \"object\",\n - \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/asset\"\n - \ }\n },\n \"asset\": {\n \"type\": \"object\",\n \"required\": - [\n \"href\"\n ],\n \"properties\": {\n \"href\": - {\n \"title\": \"Asset reference\",\n \"type\": \"string\"\n - \ },\n \"title\": {\n \"title\": \"Asset title\",\n - \ \"type\": \"string\"\n },\n \"description\": {\n \"title\": - \"Asset description\",\n \"type\": \"string\"\n },\n \"type\": - {\n \"title\": \"Asset type\",\n \"type\": \"string\"\n - \ },\n \"roles\": {\n \"title\": \"Asset roles\",\n - \ \"type\": \"array\",\n \"items\": {\n \"type\": - \"string\"\n }\n }\n }\n }\n }\n}\n" - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/basics.json - response: - body: - string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": - \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/basics.json#\",\n - \ \"title\": \"Basic Descriptive Fields\",\n \"type\": \"object\",\n \"properties\": - {\n \"title\": {\n \"title\": \"Item Title\",\n \"description\": - \"A human-readable title describing the Item.\",\n \"type\": \"string\"\n - \ },\n \"description\": {\n \"title\": \"Item Description\",\n \"description\": - \"Detailed multi-line description to fully explain the Item.\",\n \"type\": - \"string\"\n }\n }\n}" - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/datetime.json - response: - body: - string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": - \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/datetime.json#\",\n - \ \"title\": \"Date and Time Fields\",\n \"type\": \"object\",\n \"allOf\": - [\n {\n \"properties\": {\n \"created\": {\n \"$ref\": - \"#/definitions/created\"\n },\n \"updated\": {\n \"$ref\": - \"#/definitions/updated\"\n }\n }\n },\n {\n \"anyOf\": - [\n {\n \"required\": [\n \"datetime\"\n ],\n - \ \"properties\": {\n \"datetime\": {\n \"$ref\": - \"#/definitions/datetime\"\n },\n \"start_datetime\": - {\n \"$ref\": \"#/definitions/start_datetime\"\n }, - \n \"end_datetime\": {\n \"$ref\": \"#/definitions/end_datetime\"\n - \ }\n },\n \"dependencies\": {\n \"start_datetime\": - {\n \"required\": [\n \"end_datetime\"\n ]\n - \ },\n \"end_datetime\": {\n \"required\": - [\n \"start_datetime\"\n ]\n }\n }\n - \ },\n {\n \"required\": [\n \"datetime\",\n - \ \"start_datetime\",\n \"end_datetime\"\n ],\n - \ \"properties\": {\n \"datetime\": {\n \"oneOf\": - [\n {\n \"$ref\": \"#/definitions/datetime\"\n - \ },\n {\n \"type\": [\"null\"],\n - \ \"const\": null\n }\n ]\n },\n - \ \"start_datetime\": {\n \"$ref\": \"#/definitions/start_datetime\"\n - \ }, \n \"end_datetime\": {\n \"$ref\": - \"#/definitions/end_datetime\"\n }\n }\n }\n ]\n - \ }\n ],\n \"definitions\": {\n \"datetime\": {\n \"title\": \"Date - and Time\",\n \"description\": \"The searchable date/time of the assets, - in UTC (Formatted in RFC 3339) \",\n \"type\": \"string\",\n \"format\": - \"date-time\"\n },\n \"start_datetime\": {\n \"title\": \"Start - Date and Time\",\n \"description\": \"The searchable start date/time - of the assets, in UTC (Formatted in RFC 3339) \",\n \"type\": \"string\",\n - \ \"format\": \"date-time\"\n }, \n \"end_datetime\": {\n \"title\": - \"End Date and Time\", \n \"description\": \"The searchable end date/time - of the assets, in UTC (Formatted in RFC 3339) \", \n \"type\": - \"string\",\n \"format\": \"date-time\"\n },\n \"created\": {\n - \ \"title\": \"Creation Time\",\n \"type\": \"string\",\n \"format\": - \"date-time\"\n },\n \"updated\": {\n \"title\": \"Last Update - Time\",\n \"type\": \"string\",\n \"format\": \"date-time\"\n }\n - \ }\n}" - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/instrument.json - response: - body: - string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": - \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/instrument.json#\",\n - \ \"title\": \"Instrument Fields\",\n \"type\": \"object\",\n \"properties\": - {\n \"platform\": {\n \"title\": \"Platform\",\n \"type\": \"string\"\n - \ },\n \"instruments\": {\n \"title\": \"Instruments\",\n \"type\": - \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n - \ \"constellation\": {\n \"title\": \"Constellation\",\n \"type\": - \"string\"\n },\n \"mission\": {\n \"title\": \"Mission\",\n \"type\": - \"string\"\n },\n \"gsd\": {\n \"title\": \"Ground Sample Distance\",\n - \ \"type\": \"number\"\n }\n }\n}" - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/licensing.json - response: - body: - string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": - \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/licensing.json#\",\n - \ \"title\": \"Licensing Fields\",\n \"type\": \"object\",\n \"properties\": - {\n \"license\": {\n \"type\": \"string\",\n \"pattern\": \"^[\\\\w\\\\-\\\\.\\\\+]+$\"\n - \ }\n }\n}" - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/provider.json - response: - body: - string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": - \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/provider.json#\",\n - \ \"title\": \"Provider Fields\",\n \"type\": \"object\",\n \"properties\": - {\n \"providers\": {\n \"title\": \"Providers\",\n \"type\": - \"array\",\n \"items\": {\n \"properties\": {\n \"name\": - {\n \"title\": \"Organization name\",\n \"type\": \"string\"\n - \ },\n \"description\": {\n \"title\": \"Organization - description\",\n \"type\": \"string\"\n },\n \"roles\": - {\n \"title\": \"Organization roles\",\n \"type\": \"array\",\n - \ \"items\": {\n \"type\": \"string\",\n \"enum\": - [\n \"producer\",\n \"licensor\",\n \"processor\",\n - \ \"host\"\n ]\n }\n },\n \"url\": - {\n \"title\": \"Organization homepage\",\n \"type\": - \"string\",\n \"format\": \"url\"\n }\n }\n }\n - \ }\n }\n}" - headers: {} - status: - code: 200 - message: OK -version: 1 diff --git a/tests/cassettes/test_item/test_validate_extension[v1.0.0].yaml b/tests/cassettes/test_item/test_validate_extension[v1.0.0].yaml new file mode 100644 index 000000000..365856314 --- /dev/null +++ b/tests/cassettes/test_item/test_validate_extension[v1.0.0].yaml @@ -0,0 +1,154 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v1.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Items.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"properties\": {\n \"$ref\": + \"#/definitions/fields\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"$comment\": + \"The if-then-else below checks whether the eo:bands is given in assets or + not. If yes, allows eo:bands in properties (else), otherwise, disallows eo:bands + in properties (then).\",\n \"if\": {\n \"required\": [\n + \ \"assets\"\n ],\n \"properties\": {\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"properties\": {\n \"eo:bands\": + false\n }\n }\n }\n }\n + \ },\n \"then\": {\n \"properties\": {\n \"properties\": + {\n \"properties\": {\n \"eo:bands\": false\n + \ }\n }\n }\n },\n \"else\": + {\n \"properties\": {\n \"properties\": {\n \"properties\": + {\n \"eo:bands\": {\n \"$ref\": \"#/definitions/bands\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"eo:cloud_cover\": {\n \"title\": + \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!eo:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"title\": \"Band\",\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"properties\": + {\n \"name\": {\n \"title\": \"Name of the band\",\n \"type\": + \"string\"\n },\n \"common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"red\",\n \"rededge\",\n \"yellow\",\n + \ \"pan\",\n \"nir\",\n \"nir08\",\n + \ \"nir09\",\n \"cirrus\",\n \"swir16\",\n + \ \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"center_wavelength\": + {\n \"title\": \"Center Wavelength\",\n \"type\": \"number\"\n + \ },\n \"full_width_half_max\": {\n \"title\": + \"Full Width Half Max (FWHM)\",\n \"type\": \"number\"\n }\n + \ }\n }\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the + schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Require fields here for item properties.\",\n \"required\": + [\n \"proj:epsg\"\n ]\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"proj:epsg\":{\n + \ \"title\":\"EPSG code\",\n \"type\":[\n \"integer\",\n + \ \"null\"\n ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.2/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_item/test_validate_extension[v1.1.0].yaml b/tests/cassettes/test_item/test_validate_extension[v1.1.0].yaml new file mode 100644 index 000000000..41e4e31cb --- /dev/null +++ b/tests/cassettes/test_item/test_validate_extension[v1.1.0].yaml @@ -0,0 +1,201 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/eo/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/eo/v2.0.0/schema.json#\",\n \"title\": + \"EO Extension\",\n \"description\": \"STAC EO Extension for STAC Items and + STAC Collections.\",\n \"type\": \"object\",\n \"required\": [\"stac_extensions\", + \"type\"],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/eo/v2.0.0/schema.json\"\n + \ }\n }\n },\n \"allOf\": [\n {\n \"$comment\": \"Items\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/validate_assets\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"required\": + [\"properties\"],\n \"properties\": {\n \"properties\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/require_assets\"\n + \ }\n ]\n }\n },\n {\n \"$comment\": \"Collections\",\n + \ \"if\": {\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n }\n }\n },\n \"then\": {\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/validate_bands\"\n },\n + \ {\n \"$ref\": \"#/definitions/validate_assets\"\n },\n + \ {\n \"properties\": {\n \"item_assets\": + {\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/validate_properties\"\n }\n }\n + \ }\n },\n {\n \"properties\": {\n + \ \"summaries\": {\n \"type\": \"object\",\n \"$comment\": + \"We can't properly validate summary objects types (min/max or schemas) yet.\",\n + \ \"allOf\": [\n {\n \"$ref\": + \"#/definitions/validate_bands\"\n },\n {\n + \ \"properties\": {\n \"eo:cloud_cover\": + {\n \"type\": [\"array\", \"object\"],\n \"items\": + {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ }\n }\n }\n + \ },\n {\n \"properties\": + {\n \"eo:snow_cover\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:snow_cover\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:common_name\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:common_name\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:center_wavelength\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:center_wavelength\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:full_width_half_max\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:full_width_half_max\"\n }\n }\n + \ }\n },\n {\n \"properties\": + {\n \"eo:solar_illumination\": {\n \"type\": + [\"array\", \"object\"],\n \"items\": {\n \"$ref\": + \"#/definitions/eo:solar_illumination\"\n }\n }\n + \ }\n }\n ]\n }\n + \ }\n }\n ],\n \"anyOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_in_bands\"\n },\n {\n + \ \"$ref\": \"#/definitions/require_assets\"\n },\n {\n + \ \"required\": [\"item_assets\"],\n \"properties\": + {\n \"item_assets\": {\n \"$ref\": \"#/definitions/asset_contains\"\n + \ }\n }\n },\n {\n \"required\": + [\"summaries\"],\n \"properties\": {\n \"summaries\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n ]\n }\n }\n ],\n \"definitions\": + {\n \"require_properties\": {\n \"anyOf\": [\n {\n \"$ref\": + \"#/definitions/require_fields\"\n },\n {\n \"$ref\": + \"#/definitions/require_in_bands\"\n }\n ]\n },\n \"validate_bands\": + {\n \"type\": \"object\",\n \"properties\": {\n \"bands\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n }\n },\n \"validate_properties\": + {\n \"allOf\": [\n {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n {\n \"$ref\": \"#/definitions/validate_bands\"\n + \ }\n ]\n },\n \"require_assets\": {\n \"required\": + [\"assets\"],\n \"properties\": {\n \"assets\": {\n \"$ref\": + \"#/definitions/asset_contains\"\n }\n }\n },\n \"validate_assets\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/validate_properties\"\n }\n + \ }\n }\n },\n \"asset_contains\": {\n \"type\": \"object\",\n + \ \"not\": {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_properties\"\n }\n + \ }\n }\n },\n \"require_in_bands\": {\n \"required\": + [\"bands\"],\n \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"contains\": {\n \"$ref\": \"#/definitions/require_fields\"\n + \ }\n }\n }\n },\n \"require_fields\": {\n \"anyOf\": + [\n { \"required\": [\"eo:cloud_cover\"] },\n { \"required\": + [\"eo:snow_cover\"] },\n { \"required\": [\"eo:common_name\"] },\n + \ { \"required\": [\"eo:center_wavelength\"] },\n { \"required\": + [\"eo:full_width_half_max\"] },\n { \"required\": [\"eo:solar_illumination\"] + }\n ]\n },\n \"fields\": {\n \"type\": \"object\",\n \"properties\": + {\n \"eo:cloud_cover\": {\n \"$ref\": \"#/definitions/eo:cloud_cover\"\n + \ },\n \"eo:snow_cover\": {\n \"$ref\": \"#/definitions/eo:snow_cover\"\n + \ },\n \"eo:common_name\": {\n \"$ref\": \"#/definitions/eo:common_name\"\n + \ },\n \"eo:center_wavelength\": {\n \"$ref\": \"#/definitions/eo:center_wavelength\"\n + \ },\n \"eo:full_width_half_max\": {\n \"$ref\": \"#/definitions/eo:full_width_half_max\"\n + \ },\n \"eo:solar_illumination\": {\n \"$ref\": \"#/definitions/eo:solar_illumination\"\n + \ }\n },\n \"patternProperties\": {\n \"^(?!eo:)\": + {}\n },\n \"additionalProperties\": false\n },\n \"eo:cloud_cover\": + {\n \"title\": \"Cloud Cover\",\n \"type\": \"number\",\n \"minimum\": + 0,\n \"maximum\": 100\n },\n \"eo:snow_cover\": {\n \"title\": + \"Snow and Ice Cover\",\n \"type\": \"number\",\n \"minimum\": 0,\n + \ \"maximum\": 100\n },\n \"eo:common_name\": {\n \"title\": + \"Common Name of the band\",\n \"type\": \"string\",\n \"enum\": + [\n \"pan\",\n \"coastal\",\n \"blue\",\n \"green\",\n + \ \"green05\",\n \"yellow\",\n \"red\",\n \"rededge\",\n + \ \"rededge071\",\n \"rededge075\",\n \"rededge078\",\n + \ \"nir\",\n \"nir08\",\n \"nir09\",\n \"cirrus\",\n + \ \"swir16\",\n \"swir22\",\n \"lwir\",\n \"lwir11\",\n + \ \"lwir12\"\n ]\n },\n \"eo:center_wavelength\": {\n \"title\": + \"Center Wavelength\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:full_width_half_max\": {\n \"title\": \"Full Width + Half Max (FWHM)\",\n \"type\": \"number\",\n \"minimumExclusive\": + 0\n },\n \"eo:solar_illumination\": {\n \"title\": \"Solar Illumination\",\n + \ \"type\": \"number\",\n \"minimum\": 0\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + method: GET + uri: https://stac-extensions.github.io/projection/v2.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\",\n \"title\": + \"Projection Extension\",\n \"description\": \"STAC Projection Extension + for STAC Items.\",\n \"$comment\": \"This schema succeeds if the proj: fields + are not used at all, please keep this in mind.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"properties\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"properties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n }\n ]\n },\n + \ {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/projection/v2.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"proj:code\":{\n \"title\":\"Projection + code\",\n \"type\":[\n \"string\",\n \"null\"\n + \ ]\n },\n \"proj:wkt2\":{\n \"title\":\"Coordinate + Reference System in WKT2 format\",\n \"type\":[\n \"string\",\n + \ \"null\"\n ]\n },\n \"proj:projjson\": + {\n \"title\":\"Coordinate Reference System in PROJJSON format\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://proj.org/schemas/v0.7/projjson.schema.json\"\n + \ },\n {\n \"type\": \"null\"\n }\n + \ ]\n },\n \"proj:geometry\":{\n \"$ref\": + \"https://geojson.org/schema/Geometry.json\"\n },\n \"proj:bbox\":{\n + \ \"title\":\"Extent\",\n \"type\":\"array\",\n \"oneOf\": + [\n {\n \"minItems\":4,\n \"maxItems\":4\n + \ },\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n },\n \"proj:centroid\":{\n \"title\":\"Centroid\",\n + \ \"type\":\"object\",\n \"required\": [\n \"lat\",\n + \ \"lon\"\n ],\n \"properties\": {\n \"lat\": + {\n \"type\": \"number\",\n \"minimum\": -90,\n + \ \"maximum\": 90\n },\n \"lon\": {\n \"type\": + \"number\",\n \"minimum\": -180,\n \"maximum\": + 180\n }\n }\n },\n \"proj:shape\":{\n \"title\":\"Shape\",\n + \ \"type\":\"array\",\n \"minItems\":2,\n \"maxItems\":2,\n + \ \"items\":{\n \"type\":\"integer\"\n }\n },\n + \ \"proj:transform\":{\n \"title\":\"Transform\",\n \"type\":\"array\",\n + \ \"oneOf\": [\n {\n \"minItems\":6,\n \"maxItems\":6\n + \ },\n {\n \"minItems\":9,\n \"maxItems\":9\n + \ }\n ],\n \"items\":{\n \"type\":\"number\"\n + \ }\n }\n },\n \"patternProperties\": {\n \"^(?!proj:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/conftest.py b/tests/conftest.py index bdada35c9..609f37085 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,20 +1,24 @@ -# 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 +from typing import Any, cast from urllib.request import Request import pytest +from pytest import FixtureRequest, Parser + +import pystac.jsonschema +from pystac import Catalog, Item +from pystac.jsonschema import JSONSchemaValidator -from pystac import Asset, Catalog, Collection, Item, ItemCollection, Link +V1_DIR = Path(__file__).parent / "v1" -from .utils import ARBITRARY_BBOX, ARBITRARY_EXTENT, ARBITRARY_GEOM, TestCases -HERE = Path(__file__).resolve().parent +def pytest_addoption(parser: Parser) -> None: + parser.addoption( + "--v1", + action="store_true", + default=False, + help="Run v1 tests (skipped by default)", + ) @pytest.fixture(scope="module") @@ -31,7 +35,7 @@ def scrub_response_headers(response: dict[str, Any]) -> dict[str, Any]: def scrub_request_headers(request: Request) -> Request: drop = ["User-Agent"] for header in drop: - request.headers.pop(header, None) + _ = request.headers.pop(header, None) return request @@ -41,100 +45,34 @@ def scrub_request_headers(request: Request) -> Request: } -@pytest.fixture -def catalog() -> Catalog: - return Catalog("test-catalog", "A test catalog") - - -@pytest.fixture -def collection() -> Catalog: - return Collection("test-collection", "A test collection", ARBITRARY_EXTENT) - - -@pytest.fixture -def multi_extent_collection() -> Collection: - # TODO this code is repeated many times; refactor to use this fixture - return Collection.from_file( - TestCases.get_path("data-files/collections/multi-extent.json") - ) - - -@pytest.fixture -def item() -> Item: - return Item("test-item", ARBITRARY_GEOM, ARBITRARY_BBOX, datetime.now(), {}) - - -@pytest.fixture -def asset(item: Item) -> Asset: - item.add_asset("foo", Asset("https://example.tif")) - return item.assets["foo"] - - -@pytest.fixture -def link(item: Item) -> Link: - item.add_link(Link(rel="child", target="https://example.tif")) - return item.links[0] - - -@pytest.fixture -def test_case_1_catalog() -> Catalog: - return TestCases.case_1() - - -@pytest.fixture -def test_case_8_collection() -> Collection: - return TestCases.case_8() +@pytest.fixture(autouse=True) +def reset_json_schema_validator() -> None: + # Needed to make sure each test records a cassette for validation requests + pystac.jsonschema.set_default_json_schema_validator(JSONSchemaValidator()) @pytest.fixture -def projection_landsat8_item() -> Item: - path = TestCases.get_path("data-files/projection/example-landsat8.json") - return Item.from_file(path) +def data_files_path() -> Path: + return Path(__file__).parent / "data-files" -def get_data_file(rel_path: str) -> str: - return str(HERE / "data-files" / rel_path) +@pytest.fixture(params=["v1.0.0", "v1.1.0"]) +def examples_path(request: FixtureRequest, data_files_path: Path) -> Path: + return data_files_path / "examples" / cast(str, request.param) @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 +def catalog(examples_path: Path) -> Catalog: + return Catalog.from_file(examples_path / "catalog.json") @pytest.fixture -def sample_item() -> Item: - return Item.from_file(TestCases.get_path("data-files/item/sample-item.json")) +def item(examples_path: Path) -> Item: + return Item.from_file(examples_path / "simple-item.json") @pytest.fixture -def sample_item_collection() -> ItemCollection: - return ItemCollection.from_file( - TestCases.get_path("data-files/item-collection/sample-item-collection.json") +def proj_example(examples_path: Path) -> Item: + return Item.from_file( + examples_path / "extensions-collection" / "proj-example" / "proj-example.json" ) - - -@pytest.fixture -def sample_items(sample_item_collection: ItemCollection) -> list[Item]: - return list(sample_item_collection) - - -@pytest.fixture(scope="function") -def tmp_asset(tmp_path: Path) -> Asset: - """Copy the entirety of test-case-2 to tmp and""" - src = get_data_file("catalogs/test-case-2") - dst = str(tmp_path / str(uuid.uuid4())) - shutil.copytree(src, dst) - - catalog = Catalog.from_file(f"{dst}/catalog.json") - item = next(catalog.get_items(recursive=True)) - return next(v for v in item.assets.values()) - - -@pytest.fixture(autouse=True) -def clear_validator() -> None: - from pystac.validation import RegisteredValidator - - RegisteredValidator._validator = None diff --git a/tests/data-files/item-assets/example-landsat8.json b/tests/data-files/collections/with-item-assets.json similarity index 100% rename from tests/data-files/item-assets/example-landsat8.json rename to tests/data-files/collections/with-item-assets.json diff --git a/tests/data-files/examples/v1.0.0/catalog.json b/tests/data-files/examples/v1.0.0/catalog.json new file mode 100644 index 000000000..b5910be3c --- /dev/null +++ b/tests/data-files/examples/v1.0.0/catalog.json @@ -0,0 +1,43 @@ +{ + "id": "examples", + "type": "Catalog", + "title": "Example Catalog", + "stac_version": "1.0.0", + "description": "This catalog is a simple demonstration of an example catalog that is used to organize a hierarchy of collections and their items.", + "links": [ + { + "rel": "root", + "href": "./catalog.json", + "type": "application/json" + }, + { + "rel": "child", + "href": "./extensions-collection/collection.json", + "type": "application/json", + "title": "Collection Demonstrating STAC Extensions" + }, + { + "rel": "child", + "href": "./collection-only/collection.json", + "type": "application/json", + "title": "Collection with no items (standalone)" + }, + { + "rel": "child", + "href": "./collection-only/collection-with-schemas.json", + "type": "application/json", + "title": "Collection with no items (standalone with JSON Schemas)" + }, + { + "rel": "item", + "href": "./collectionless-item.json", + "type": "application/json", + "title": "Collection with no items (standalone)" + }, + { + "rel": "self", + "href": "https://raw.githubusercontent.com/radiantearth/stac-spec/v1.0.0/examples/catalog.json", + "type": "application/json" + } + ] +} diff --git a/tests/data-files/examples/v1.0.0/collection-only/collection-with-schemas.json b/tests/data-files/examples/v1.0.0/collection-only/collection-with-schemas.json new file mode 100644 index 000000000..213e53fb6 --- /dev/null +++ b/tests/data-files/examples/v1.0.0/collection-only/collection-with-schemas.json @@ -0,0 +1,277 @@ +{ + "stac_version": "1.0.0", + "stac_extensions": [ + "https://stac-extensions.github.io/eo/v1.0.0/schema.json", + "https://stac-extensions.github.io/sat/v1.0.0/schema.json", + "https://stac-extensions.github.io/view/v1.0.0/schema.json" + ], + "id": "sentinel-2", + "type": "Collection", + "title": "Sentinel-2 MSI: MultiSpectral Instrument, Level-2A", + "description": "The SENTINEL-2 mission is a land monitoring constellation of two satellites each equipped with a MSI (Multispectral Imager) instrument covering 13 spectral bands providing high resolution optical imagery (i.e., 10m, 20m, 60 m) every 10 days with one satellite and 5 days with two satellites", + "license": "proprietary", + "extent": { + "spatial": { + "bbox": [ + [ + -180.0, + -82.852377834669, + 180.0, + 82.819463367711 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2017-04-12T02:57:21.459000Z", + "2021-04-22T11:30:12.767000Z" + ] + ] + } + }, + "links": [ + { + "rel": "parent", + "href": "../catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "root", + "href": "../catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "license", + "href": "https://scihub.copernicus.eu/twiki/pub/SciHubWebPortal/TermsConditions/Sentinel_Data_Terms_and_Conditions.pdf", + "title": "Legal notice on the use of Copernicus Sentinel Data and Service Information" + } + ], + "providers": [ + { + "name": "European Union/ESA/Copernicus", + "roles": [ + "producer", + "licensor" + ], + "url": "https://sentinel.esa.int/web/sentinel/user-guides/sentinel-2-msi" + }, + { + "name": "AWS", + "roles": [ + "host" + ], + "url": "https://registry.opendata.aws/sentinel-2/" + }, + { + "name": "jeobrowser", + "roles": [ + "processor" + ], + "url": "https://github.com/jjrom/resto" + } + ], + "summaries": { + "datetime": { + "minimum": "2017-04-12T02:57:21Z", + "maximum": "2021-04-22T11:30:12Z" + }, + "instruments": { + "type": "string", + "const": "msi", + "title": "Multispectral Intrument", + "count": 6613431 + }, + "resto:landcover": { + "type": "string", + "oneOf": [ + { + "const": "cultivated", + "title": "Cultivated", + "count": 490750 + }, + { + "const": "desert", + "title": "Desert", + "count": 543120 + }, + { + "const": "flooded", + "title": "Flooded", + "count": 5187 + }, + { + "const": "forest", + "title": "Forest", + "count": 767807 + }, + { + "const": "herbaceous", + "title": "Herbaceous", + "count": 674281 + }, + { + "const": "ice", + "title": "Ice", + "count": 231285 + }, + { + "const": "urban", + "title": "Urban", + "count": 1219 + }, + { + "const": "water", + "title": "Water", + "count": 2303314 + } + ] + }, + "resto:location": { + "type": "string", + "oneOf": [ + { + "const": "tropical", + "title": "Tropical", + "count": 1807474 + }, + { + "const": "southern", + "title": "Southern", + "count": 1671685 + }, + { + "const": "northern", + "title": "Northern", + "count": 4876669 + }, + { + "const": "equatorial", + "title": "Equatorial", + "count": 27302 + }, + { + "const": "coastal", + "title": "Coastal", + "count": 1495516 + } + ] + }, + "platform": { + "type": "string", + "oneOf": [ + { + "const": "sentinel-2b", + "title": "Sentinel 2B", + "count": 3495597 + }, + { + "const": "sentinel-2a", + "title": "Sentinel 2A", + "count": 3117831 + } + ] + }, + "resto:season": { + "type": "integer", + "oneOf": [ + { + "const": 0, + "title": "Winter", + "count": 1621108 + }, + { + "const": 2, + "title": "Summer", + "count": 2279472 + }, + { + "const": 1, + "title": "Spring", + "count": 1577067 + }, + { + "const": 3, + "title": "Autumn", + "count": 1098015 + } + ] + }, + "eo:bands": [ + { + "title": "B1", + "common_name": "coastal", + "center_wavelength": 4.439, + "gsd": 60 + }, + { + "title": "B2", + "common_name": "blue", + "center_wavelength": 4.966, + "gsd": 10 + }, + { + "title": "B3", + "common_name": "green", + "center_wavelength": 5.6, + "gsd": 10 + }, + { + "title": "B4", + "common_name": "red", + "center_wavelength": 6.645, + "gsd": 10 + }, + { + "title": "B5", + "center_wavelength": 7.039, + "gsd": 20 + }, + { + "title": "B6", + "center_wavelength": 7.402, + "gsd": 20 + }, + { + "title": "B7", + "center_wavelength": 7.825, + "gsd": 20 + }, + { + "title": "B8", + "common_name": "nir", + "center_wavelength": 8.351, + "gsd": 10 + }, + { + "title": "B8A", + "center_wavelength": 8.648, + "gsd": 20 + }, + { + "title": "B9", + "center_wavelength": 9.45, + "gsd": 60 + }, + { + "title": "B10", + "center_wavelength": 1.3735, + "gsd": 60 + }, + { + "title": "B11", + "common_name": "swir16", + "center_wavelength": 1.6137, + "gsd": 20 + }, + { + "title": "B12", + "common_name": "swir22", + "center_wavelength": 2.2024, + "gsd": 20 + } + ] + } +} diff --git a/tests/data-files/examples/v1.0.0/collection-only/collection.json b/tests/data-files/examples/v1.0.0/collection-only/collection.json new file mode 100644 index 000000000..64edf78dd --- /dev/null +++ b/tests/data-files/examples/v1.0.0/collection-only/collection.json @@ -0,0 +1,233 @@ +{ + "type": "Collection", + "stac_version": "1.0.0", + "stac_extensions": [ + "https://stac-extensions.github.io/eo/v1.0.0/schema.json", + "https://stac-extensions.github.io/projection/v1.0.0/schema.json", + "https://stac-extensions.github.io/view/v1.0.0/schema.json" + ], + "id": "sentinel-2", + "title": "Sentinel-2 MSI: MultiSpectral Instrument, Level-1C", + "description": "Sentinel-2 is a wide-swath, high-resolution, multi-spectral\nimaging mission supporting Copernicus Land Monitoring studies,\nincluding the monitoring of vegetation, soil and water cover,\nas well as observation of inland waterways and coastal areas.\n\nThe Sentinel-2 data contain 13 UINT16 spectral bands representing\nTOA reflectance scaled by 10000. See the [Sentinel-2 User Handbook](https://sentinel.esa.int/documents/247904/685211/Sentinel-2_User_Handbook)\nfor details. In addition, three QA bands are present where one\n(QA60) is a bitmask band with cloud mask information. For more\ndetails, [see the full explanation of how cloud masks are computed.](https://sentinel.esa.int/web/sentinel/technical-guides/sentinel-2-msi/level-1c/cloud-masks)\n\nEach Sentinel-2 product (zip archive) may contain multiple\ngranules. Each granule becomes a separate Earth Engine asset.\nEE asset ids for Sentinel-2 assets have the following format:\nCOPERNICUS/S2/20151128T002653_20151128T102149_T56MNN. Here the\nfirst numeric part represents the sensing date and time, the\nsecond numeric part represents the product generation date and\ntime, and the final 6-character string is a unique granule identifier\nindicating its UTM grid reference (see [MGRS](https://en.wikipedia.org/wiki/Military_Grid_Reference_System)).\n\nFor more details on Sentinel-2 radiometric resoltuon, [see this page](https://earth.esa.int/web/sentinel/user-guides/sentinel-2-msi/resolutions/radiometric).\n", + "license": "proprietary", + "keywords": [ + "copernicus", + "esa", + "eu", + "msi", + "radiance", + "sentinel" + ], + "providers": [ + { + "name": "European Union/ESA/Copernicus", + "roles": [ + "producer", + "licensor" + ], + "url": "https://sentinel.esa.int/web/sentinel/user-guides/sentinel-2-msi" + } + ], + "extent": { + "spatial": { + "bbox": [ + [ + -180.0, + -56.0, + 180.0, + 83.0 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2015-06-23T00:00:00Z", + null + ] + ] + } + }, + "assets": { + "metadata_iso_19139": { + "roles": [ + "metadata", + "iso-19139" + ], + "href": "https://storage.googleapis.com/open-cogs/stac-examples/sentinel-2-iso-19139.xml", + "title": "ISO 19139 metadata", + "type": "application/vnd.iso.19139+xml" + } + }, + "summaries": { + "datetime": { + "minimum": "2015-06-23T00:00:00Z", + "maximum": "2019-07-10T13:44:56Z" + }, + "platform": [ + "sentinel-2a", + "sentinel-2b" + ], + "constellation": [ + "sentinel-2" + ], + "instruments": [ + "msi" + ], + "view:off_nadir": { + "minimum": 0, + "maximum": 100 + }, + "view:sun_elevation": { + "minimum": 6.78, + "maximum": 89.9 + }, + "gsd": [ + 10, + 30, + 60 + ], + "proj:epsg": [ + 32601, + 32602, + 32603, + 32604, + 32605, + 32606, + 32607, + 32608, + 32609, + 32610, + 32611, + 32612, + 32613, + 32614, + 32615, + 32616, + 32617, + 32618, + 32619, + 32620, + 32621, + 32622, + 32623, + 32624, + 32625, + 32626, + 32627, + 32628, + 32629, + 32630, + 32631, + 32632, + 32633, + 32634, + 32635, + 32636, + 32637, + 32638, + 32639, + 32640, + 32641, + 32642, + 32643, + 32644, + 32645, + 32646, + 32647, + 32648, + 32649, + 32650, + 32651, + 32652, + 32653, + 32654, + 32655, + 32656, + 32657, + 32658, + 32659, + 32660 + ], + "eo:bands": [ + { + "name": "B1", + "common_name": "coastal", + "center_wavelength": 4.439 + }, + { + "name": "B2", + "common_name": "blue", + "center_wavelength": 4.966 + }, + { + "name": "B3", + "common_name": "green", + "center_wavelength": 5.6 + }, + { + "name": "B4", + "common_name": "red", + "center_wavelength": 6.645 + }, + { + "name": "B5", + "center_wavelength": 7.039 + }, + { + "name": "B6", + "center_wavelength": 7.402 + }, + { + "name": "B7", + "center_wavelength": 7.825 + }, + { + "name": "B8", + "common_name": "nir", + "center_wavelength": 8.351 + }, + { + "name": "B8A", + "center_wavelength": 8.648 + }, + { + "name": "B9", + "center_wavelength": 9.45 + }, + { + "name": "B10", + "center_wavelength": 1.3735 + }, + { + "name": "B11", + "common_name": "swir16", + "center_wavelength": 1.6137 + }, + { + "name": "B12", + "common_name": "swir22", + "center_wavelength": 2.2024 + } + ] + }, + "links": [ + { + "rel": "parent", + "href": "../catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "root", + "href": "../catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "license", + "href": "https://scihub.copernicus.eu/twiki/pub/SciHubWebPortal/TermsConditions/Sentinel_Data_Terms_and_Conditions.pdf", + "title": "Legal notice on the use of Copernicus Sentinel Data and Service Information" + } + ] +} diff --git a/tests/data-files/examples/v1.0.0/collection.json b/tests/data-files/examples/v1.0.0/collection.json new file mode 100644 index 000000000..07643b600 --- /dev/null +++ b/tests/data-files/examples/v1.0.0/collection.json @@ -0,0 +1,112 @@ +{ + "id": "simple-collection", + "type": "Collection", + "stac_extensions": [ + "https://stac-extensions.github.io/eo/v1.0.0/schema.json", + "https://stac-extensions.github.io/projection/v1.0.0/schema.json", + "https://stac-extensions.github.io/view/v1.0.0/schema.json" + ], + "stac_version": "1.0.0", + "description": "A simple collection demonstrating core catalog fields with links to a couple of items", + "title": "Simple Example Collection", + "providers": [ + { + "name": "Remote Data, Inc", + "description": "Producers of awesome spatiotemporal assets", + "roles": [ + "producer", + "processor" + ], + "url": "http://remotedata.io" + } + ], + "extent": { + "spatial": { + "bbox": [ + [ + 172.91173669923782, + 1.3438851951615003, + 172.95469614953714, + 1.3690476620161975 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2020-12-11T22:38:32.125Z", + "2020-12-14T18:02:31.437Z" + ] + ] + } + }, + "license": "CC-BY-4.0", + "summaries": { + "platform": [ + "cool_sat1", + "cool_sat2" + ], + "constellation": [ + "ion" + ], + "instruments": [ + "cool_sensor_v1", + "cool_sensor_v2" + ], + "gsd": { + "minimum": 0.512, + "maximum": 0.66 + }, + "eo:cloud_cover": { + "minimum": 1.2, + "maximum": 1.2 + }, + "proj:epsg": { + "minimum": 32659, + "maximum": 32659 + }, + "view:sun_elevation": { + "minimum": 54.9, + "maximum": 54.9 + }, + "view:off_nadir": { + "minimum": 3.8, + "maximum": 3.8 + }, + "view:sun_azimuth": { + "minimum": 135.7, + "maximum": 135.7 + } + }, + "links": [ + { + "rel": "root", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "item", + "href": "./simple-item.json", + "type": "application/geo+json", + "title": "Simple Item" + }, + { + "rel": "item", + "href": "./core-item.json", + "type": "application/geo+json", + "title": "Core Item" + }, + { + "rel": "item", + "href": "./extended-item.json", + "type": "application/geo+json", + "title": "Extended Item" + }, + { + "rel": "self", + "href": "https://raw.githubusercontent.com/radiantearth/stac-spec/v1.0.0/examples/collection.json", + "type": "application/json" + } + ] +} diff --git a/tests/data-files/examples/v1.0.0/collectionless-item.json b/tests/data-files/examples/v1.0.0/collectionless-item.json new file mode 100644 index 000000000..6e7beab93 --- /dev/null +++ b/tests/data-files/examples/v1.0.0/collectionless-item.json @@ -0,0 +1,144 @@ +{ + "stac_version": "1.0.0", + "stac_extensions": [ + "https://stac-extensions.github.io/eo/v1.0.0/schema.json", + "https://stac-extensions.github.io/view/v1.0.0/schema.json" + ], + "type": "Feature", + "id": "CS3-20160503_132131_08", + "bbox": [ + -122.59750209, + 37.48803556, + -122.2880486, + 37.613537207 + ], + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -122.308150179, + 37.488035566 + ], + [ + -122.597502109, + 37.538869539 + ], + [ + -122.576687533, + 37.613537207 + ], + [ + -122.2880486, + 37.562818007 + ], + [ + -122.308150179, + 37.488035566 + ] + ] + ] + }, + "properties": { + "title": "Full Item", + "description": "A sample STAC Item demonstrates an Item that does not have a collection, which is not recommended, but allowed by the spec.", + "datetime": null, + "start_datetime": "2016-05-03T13:22:30Z", + "end_datetime": "2016-05-03T13:27:30Z", + "created": "2016-05-04T00:00:01Z", + "updated": "2017-01-01T00:30:55Z", + "license": "various", + "providers": [ + { + "name": "Remote Data, Inc", + "description": "Producers of awesome spatiotemporal assets", + "roles": [ + "producer", + "processor" + ], + "url": "http://remotedata.it" + } + ], + "platform": "cool_sat2", + "instruments": [ + "cool_sensor_v1" + ], + "view:sun_elevation": 33.4, + "gsd": 0.512, + "cs:type": "scene", + "cs:anomalous_pixels": 0.14, + "cs:earth_sun_distance": 1.014156, + "cs:sat_id": "CS3", + "cs:product_level": "LV1B" + }, + "links": [ + { + "rel": "root", + "href": "./catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "parent", + "href": "./catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "alternate", + "type": "text/html", + "href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/CS3-20160503_132130_04.html", + "title": "HTML representation of this STAC Item" + }, + { + "rel": "license", + "type": "text/html", + "href": "http://remotedata.io/license.html", + "title": "Data License for Remote Data, Inc." + } + ], + "assets": { + "analytic": { + "href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/analytic.tif", + "title": "4-Band Analytic", + "eo:bands": [ + { + "name": "band1" + }, + { + "name": "band1" + }, + { + "name": "band2" + }, + { + "name": "band3" + } + ] + }, + "thumbnail": { + "href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/thumbnail.png", + "title": "Thumbnail", + "type": "image/png", + "roles": [ + "thumbnail" + ] + }, + "udm": { + "href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/UDM.tif", + "title": "Unusable Data Mask" + }, + "json-metadata": { + "href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/extended-metadata.json", + "title": "Extended Metadata", + "type": "application/json", + "roles": [ + "metadata" + ] + }, + "ephemeris": { + "href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/S3-20160503_132130_04.EPH", + "title": "Satellite Ephemeris Metadata" + } + } +} diff --git a/tests/data-files/examples/v1.0.0/core-item.json b/tests/data-files/examples/v1.0.0/core-item.json new file mode 100644 index 000000000..d07cc8df6 --- /dev/null +++ b/tests/data-files/examples/v1.0.0/core-item.json @@ -0,0 +1,125 @@ +{ + "stac_version": "1.0.0", + "stac_extensions": [], + "type": "Feature", + "id": "20201211_223832_CS2", + "bbox": [ + 172.91173669923782, + 1.3438851951615003, + 172.95469614953714, + 1.3690476620161975 + ], + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 172.91173669923782, + 1.3438851951615003 + ], + [ + 172.95469614953714, + 1.3438851951615003 + ], + [ + 172.95469614953714, + 1.3690476620161975 + ], + [ + 172.91173669923782, + 1.3690476620161975 + ], + [ + 172.91173669923782, + 1.3438851951615003 + ] + ] + ] + }, + "properties": { + "title": "Core Item", + "description": "A sample STAC Item that includes examples of all common metadata", + "datetime": null, + "start_datetime": "2020-12-11T22:38:32.125Z", + "end_datetime": "2020-12-11T22:38:32.327Z", + "created": "2020-12-12T01:48:13.725Z", + "updated": "2020-12-12T01:48:13.725Z", + "platform": "cool_sat1", + "instruments": [ + "cool_sensor_v1" + ], + "constellation": "ion", + "mission": "collection 5624", + "gsd": 0.512 + }, + "collection": "simple-collection", + "links": [ + { + "rel": "collection", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "root", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "parent", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "alternate", + "type": "text/html", + "href": "http://remotedata.io/catalog/20201211_223832_CS2/index.html", + "title": "HTML version of this STAC Item" + } + ], + "assets": { + "analytic": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2_analytic.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "4-Band Analytic", + "roles": [ + "data" + ] + }, + "thumbnail": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2.jpg", + "title": "Thumbnail", + "type": "image/png", + "roles": [ + "thumbnail" + ] + }, + "visual": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "3-Band Visual", + "roles": [ + "visual" + ] + }, + "udm": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2_analytic_udm.tif", + "title": "Unusable Data Mask", + "type": "image/tiff; application=geotiff;" + }, + "json-metadata": { + "href": "http://remotedata.io/catalog/20201211_223832_CS2/extended-metadata.json", + "title": "Extended Metadata", + "type": "application/json", + "roles": [ + "metadata" + ] + }, + "ephemeris": { + "href": "http://cool-sat.com/catalog/20201211_223832_CS2/20201211_223832_CS2.EPH", + "title": "Satellite Ephemeris Metadata" + } + } +} diff --git a/tests/data-files/examples/v1.0.0/extended-item.json b/tests/data-files/examples/v1.0.0/extended-item.json new file mode 100644 index 000000000..59f2de1f3 --- /dev/null +++ b/tests/data-files/examples/v1.0.0/extended-item.json @@ -0,0 +1,199 @@ +{ + "stac_version": "1.0.0", + "stac_extensions": [ + "https://stac-extensions.github.io/eo/v1.0.0/schema.json", + "https://stac-extensions.github.io/projection/v1.0.0/schema.json", + "https://stac-extensions.github.io/scientific/v1.0.0/schema.json", + "https://stac-extensions.github.io/view/v1.0.0/schema.json", + "https://stac-extensions.github.io/remote-data/v1.0.0/schema.json" + ], + "type": "Feature", + "id": "20201211_223832_CS2", + "bbox": [ + 172.91173669923782, + 1.3438851951615003, + 172.95469614953714, + 1.3690476620161975 + ], + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 172.91173669923782, + 1.3438851951615003 + ], + [ + 172.95469614953714, + 1.3438851951615003 + ], + [ + 172.95469614953714, + 1.3690476620161975 + ], + [ + 172.91173669923782, + 1.3690476620161975 + ], + [ + 172.91173669923782, + 1.3438851951615003 + ] + ] + ] + }, + "properties": { + "title": "Extended Item", + "description": "A sample STAC Item that includes a variety of examples from the stable extensions", + "datetime": "2020-12-14T18:02:31.437000Z", + "created": "2020-12-15T01:48:13.725Z", + "updated": "2020-12-15T01:48:13.725Z", + "platform": "cool_sat2", + "instruments": [ + "cool_sensor_v2" + ], + "gsd": 0.66, + "eo:cloud_cover": 1.2, + "proj:epsg": 32659, + "proj:shape": [ + 5558, + 9559 + ], + "proj:transform": [ + 0.5, + 0, + 712710, + 0, + -0.5, + 151406, + 0, + 0, + 1 + ], + "view:sun_elevation": 54.9, + "view:off_nadir": 3.8, + "view:sun_azimuth": 135.7, + "rd:type": "scene", + "rd:anomalous_pixels": 0.14, + "rd:earth_sun_distance": 1.014156, + "rd:sat_id": "cool_sat2", + "rd:product_level": "LV3A", + "sci:doi": "10.5061/dryad.s2v81.2/27.2" + }, + "collection": "simple-collection", + "links": [ + { + "rel": "collection", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "root", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "parent", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "alternate", + "type": "text/html", + "href": "http://remotedata.io/catalog/20201211_223832_CS2/index.html", + "title": "HTML version of this STAC Item" + } + ], + "assets": { + "analytic": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2_analytic.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "4-Band Analytic", + "roles": [ + "data" + ], + "eo:bands": [ + { + "name": "band1", + "common_name": "blue", + "center_wavelength": 470, + "full_width_half_max": 70 + }, + { + "name": "band2", + "common_name": "green", + "center_wavelength": 560, + "full_width_half_max": 80 + }, + { + "name": "band3", + "common_name": "red", + "center_wavelength": 645, + "full_width_half_max": 90 + }, + { + "name": "band4", + "common_name": "nir", + "center_wavelength": 800, + "full_width_half_max": 152 + } + ] + }, + "thumbnail": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2.jpg", + "title": "Thumbnail", + "type": "image/png", + "roles": [ + "thumbnail" + ] + }, + "visual": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "3-Band Visual", + "roles": [ + "visual" + ], + "eo:bands": [ + { + "name": "band3", + "common_name": "red", + "center_wavelength": 645, + "full_width_half_max": 90 + }, + { + "name": "band2", + "common_name": "green", + "center_wavelength": 560, + "full_width_half_max": 80 + }, + { + "name": "band1", + "common_name": "blue", + "center_wavelength": 470, + "full_width_half_max": 70 + } + ] + }, + "udm": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2_analytic_udm.tif", + "title": "Unusable Data Mask", + "type": "image/tiff; application=geotiff;" + }, + "json-metadata": { + "href": "http://remotedata.io/catalog/20201211_223832_CS2/extended-metadata.json", + "title": "Extended Metadata", + "type": "application/json", + "roles": [ + "metadata" + ] + }, + "ephemeris": { + "href": "http://cool-sat.com/catalog/20201211_223832_CS2/20201211_223832_CS2.EPH", + "title": "Satellite Ephemeris Metadata" + } + } +} diff --git a/tests/data-files/examples/v1.0.0/extensions-collection/collection.json b/tests/data-files/examples/v1.0.0/extensions-collection/collection.json new file mode 100644 index 000000000..2c6306acb --- /dev/null +++ b/tests/data-files/examples/v1.0.0/extensions-collection/collection.json @@ -0,0 +1,68 @@ +{ + "id": "extensions-collection", + "type": "Collection", + "stac_version": "1.0.0", + "description": "A heterogenous collection containing deeper examples of various extensions", + "links": [ + { + "rel": "parent", + "href": "../catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "root", + "href": "../catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "item", + "href": "./proj-example/proj-example.json", + "title": "Proj extension example" + }, + { + "rel": "license", + "href": "https://remotedata.io/license.html", + "title": "Remote Data License Terms" + } + ], + "stac_extensions": [], + "title": "Collection of Extension Items", + "keywords": [ + "examples", + "sar", + "projection" + ], + "providers": [ + { + "name": "Remote Data, Inc.", + "roles": [ + "producer", + "licensor" + ], + "url": "https://remotedata.io" + } + ], + "extent": { + "spatial": { + "bbox": [ + [ + -180.0, + -56.0, + 180.0, + 83.0 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2009-05-20T02:40:01.042784Z", + "2018-11-03T23:59:55.112875Z" + ] + ] + } + }, + "license": "PDDL-1.0" +} diff --git a/tests/data-files/examples/v1.0.0/extensions-collection/proj-example/proj-example.json b/tests/data-files/examples/v1.0.0/extensions-collection/proj-example/proj-example.json new file mode 100644 index 000000000..09b7986da --- /dev/null +++ b/tests/data-files/examples/v1.0.0/extensions-collection/proj-example/proj-example.json @@ -0,0 +1,285 @@ +{ + "type": "Feature", + "stac_version": "1.0.0", + "id": "proj-example", + "properties": { + "datetime": "2018-10-01T01:08:32.033000Z", + "proj:epsg": 32614, + "proj:wkt2": "PROJCS[\"WGS 84 / UTM zone 14N\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-99],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],AUTHORITY[\"EPSG\",\"32614\"],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH]]", + "proj:projjson": { + "$schema": "https://proj.org/schemas/v0.2/projjson.schema.json", + "type": "ProjectedCRS", + "name": "WGS 84 / UTM zone 14N", + "base_crs": { + "name": "WGS 84", + "datum": { + "type": "GeodeticReferenceFrame", + "name": "World Geodetic System 1984", + "ellipsoid": { + "name": "WGS 84", + "semi_major_axis": 6378137, + "inverse_flattening": 298.257223563 + } + }, + "coordinate_system": { + "subtype": "ellipsoidal", + "axis": [ + { + "name": "Geodetic latitude", + "abbreviation": "Lat", + "direction": "north", + "unit": "degree" + }, + { + "name": "Geodetic longitude", + "abbreviation": "Lon", + "direction": "east", + "unit": "degree" + } + ] + }, + "id": { + "authority": "EPSG", + "code": 4326 + } + }, + "conversion": { + "name": "UTM zone 14N", + "method": { + "name": "Transverse Mercator", + "id": { + "authority": "EPSG", + "code": 9807 + } + }, + "parameters": [ + { + "name": "Latitude of natural origin", + "value": 0, + "unit": "degree", + "id": { + "authority": "EPSG", + "code": 8801 + } + }, + { + "name": "Longitude of natural origin", + "value": -99, + "unit": "degree", + "id": { + "authority": "EPSG", + "code": 8802 + } + }, + { + "name": "Scale factor at natural origin", + "value": 0.9996, + "unit": "unity", + "id": { + "authority": "EPSG", + "code": 8805 + } + }, + { + "name": "False easting", + "value": 500000, + "unit": "metre", + "id": { + "authority": "EPSG", + "code": 8806 + } + }, + { + "name": "False northing", + "value": 0, + "unit": "metre", + "id": { + "authority": "EPSG", + "code": 8807 + } + } + ] + }, + "coordinate_system": { + "subtype": "Cartesian", + "axis": [ + { + "name": "Easting", + "abbreviation": "E", + "direction": "east", + "unit": "metre" + }, + { + "name": "Northing", + "abbreviation": "N", + "direction": "north", + "unit": "metre" + } + ] + }, + "area": "World - N hemisphere - 102°W to 96°W - by country", + "bbox": { + "south_latitude": 0, + "west_longitude": -102, + "north_latitude": 84, + "east_longitude": -96 + }, + "id": { + "authority": "EPSG", + "code": 32614 + } + }, + "proj:geometry": { + "coordinates": [ + [ + [ + 169200, + 3712800 + ], + [ + 403200, + 3712800 + ], + [ + 403200, + 3951000 + ], + [ + 169200, + 3951000 + ], + [ + 169200, + 3712800 + ] + ] + ], + "type": "Polygon" + }, + "proj:bbox": [ + 169200, + 3712800, + 403200, + 3951000 + ], + "proj:centroid": { + "lat": 34.595302781575604, + "lon": -101.34448382627504 + }, + "proj:shape": [ + 8391, + 8311 + ], + "proj:transform": [ + 30, + 0, + 224985, + 0, + -30, + 6790215, + 0, + 0, + 1 + ] + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 152.52758, + 60.63437 + ], + [ + 149.1755, + 61.19016 + ], + [ + 148.13933, + 59.51584 + ], + [ + 151.33786, + 58.97792 + ], + [ + 152.52758, + 60.63437 + ] + ] + ] + }, + "links": [ + { + "rel": "root", + "href": "../../catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "parent", + "href": "../collection.json", + "type": "application/json", + "title": "Collection of Extension Items" + }, + { + "rel": "collection", + "href": "../collection.json", + "type": "application/json", + "title": "Collection of Extension Items" + } + ], + "assets": { + "B1": { + "href": "https://landsat-pds.s3.amazonaws.com/c1/L8/107/018/LC08_L1TP_107018_20181001_20181001_01_RT/LC08_L1TP_107018_20181001_20181001_01_RT_B1.TIF", + "type": "image/tiff; application=geotiff", + "title": "Band 1 (coastal)", + "eo:bands": [ + { + "name": "B1", + "common_name": "coastal", + "center_wavelength": 0.44, + "full_width_half_max": 0.02 + } + ] + }, + "B8": { + "href": "https://landsat-pds.s3.amazonaws.com/c1/L8/107/018/LC08_L1TP_107018_20181001_20181001_01_RT/LC08_L1TP_107018_20181001_20181001_01_RT_B8.TIF", + "type": "image/tiff; application=geotiff", + "title": "Band 8 (panchromatic)", + "eo:bands": [ + { + "name": "B8", + "center_wavelength": 0.59, + "full_width_half_max": 0.18 + } + ], + "proj:shape": [ + 16781, + 16621 + ], + "proj:transform": [ + 15, + 0, + 224992.5, + 0, + -15, + 6790207.5, + 0, + 0, + 1 + ] + } + }, + "bbox": [ + 148.13933, + 59.51584, + 152.52758, + 60.63437 + ], + "stac_extensions": [ + "https://stac-extensions.github.io/eo/v1.0.0/schema.json", + "https://stac-extensions.github.io/projection/v1.0.0/schema.json" + ], + "collection": "landsat-8-l1" +} diff --git a/tests/data-files/examples/v1.0.0/simple-item.json b/tests/data-files/examples/v1.0.0/simple-item.json new file mode 100644 index 000000000..1e413c43d --- /dev/null +++ b/tests/data-files/examples/v1.0.0/simple-item.json @@ -0,0 +1,81 @@ +{ + "stac_version": "1.0.0", + "stac_extensions": [], + "type": "Feature", + "id": "20201211_223832_CS2", + "bbox": [ + 172.91173669923782, + 1.3438851951615003, + 172.95469614953714, + 1.3690476620161975 + ], + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 172.91173669923782, + 1.3438851951615003 + ], + [ + 172.95469614953714, + 1.3438851951615003 + ], + [ + 172.95469614953714, + 1.3690476620161975 + ], + [ + 172.91173669923782, + 1.3690476620161975 + ], + [ + 172.91173669923782, + 1.3438851951615003 + ] + ] + ] + }, + "properties": { + "datetime": "2020-12-11T22:38:32.125000Z" + }, + "collection": "simple-collection", + "links": [ + { + "rel": "collection", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "root", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "parent", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + } + ], + "assets": { + "visual": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "3-Band Visual", + "roles": [ + "visual" + ] + }, + "thumbnail": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2.jpg", + "title": "Thumbnail", + "type": "image/jpeg", + "roles": [ + "thumbnail" + ] + } + } +} diff --git a/tests/data-files/examples/1.1.0/README.md b/tests/data-files/examples/v1.1.0/README.md similarity index 100% rename from tests/data-files/examples/1.1.0/README.md rename to tests/data-files/examples/v1.1.0/README.md diff --git a/tests/data-files/examples/1.1.0/catalog.json b/tests/data-files/examples/v1.1.0/catalog.json similarity index 100% rename from tests/data-files/examples/1.1.0/catalog.json rename to tests/data-files/examples/v1.1.0/catalog.json diff --git a/tests/data-files/examples/v1.1.0/collection-only/collection-with-schemas.json b/tests/data-files/examples/v1.1.0/collection-only/collection-with-schemas.json new file mode 100644 index 000000000..eeebb779e --- /dev/null +++ b/tests/data-files/examples/v1.1.0/collection-only/collection-with-schemas.json @@ -0,0 +1,311 @@ +{ + "stac_version": "1.1.0", + "stac_extensions": [ + "https://stac-extensions.github.io/eo/v2.0.0/schema.json", + "https://stac-extensions.github.io/sat/v1.0.0/schema.json", + "https://stac-extensions.github.io/view/v1.0.0/schema.json" + ], + "id": "sentinel-2", + "type": "Collection", + "title": "Sentinel-2 MSI: MultiSpectral Instrument, Level-2A", + "description": "The SENTINEL-2 mission is a land monitoring constellation of two satellites each equipped with a MSI (Multispectral Imager) instrument covering 13 spectral bands providing high resolution optical imagery (i.e., 10m, 20m, 60 m) every 10 days with one satellite and 5 days with two satellites", + "license": "other", + "extent": { + "spatial": { + "bbox": [ + [ + -180, + -82.852377834669, + 180, + 82.819463367711 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2017-04-12T02:57:21.459000Z", + "2021-04-22T11:30:12.767000Z" + ] + ] + } + }, + "links": [ + { + "rel": "parent", + "href": "../catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "root", + "href": "../catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "license", + "href": "https://scihub.copernicus.eu/twiki/pub/SciHubWebPortal/TermsConditions/Sentinel_Data_Terms_and_Conditions.pdf", + "title": "Legal notice on the use of Copernicus Sentinel Data and Service Information" + } + ], + "providers": [ + { + "name": "European Union/ESA/Copernicus", + "roles": [ + "producer", + "licensor" + ], + "url": "https://sentinel.esa.int/web/sentinel/user-guides/sentinel-2-msi" + }, + { + "name": "AWS", + "roles": [ + "host" + ], + "url": "https://registry.opendata.aws/sentinel-2/" + }, + { + "name": "jeobrowser", + "roles": [ + "processor" + ], + "url": "https://github.com/jjrom/resto" + } + ], + "summaries": { + "datetime": { + "minimum": "2017-04-12T02:57:21Z", + "maximum": "2021-04-22T11:30:12Z" + }, + "instruments": { + "type": "string", + "const": "msi", + "title": "Multispectral Instrument", + "count": 6613431 + }, + "resto:landcover": { + "type": "string", + "oneOf": [ + { + "const": "cultivated", + "title": "Cultivated", + "count": 490750 + }, + { + "const": "desert", + "title": "Desert", + "count": 543120 + }, + { + "const": "flooded", + "title": "Flooded", + "count": 5187 + }, + { + "const": "forest", + "title": "Forest", + "count": 767807 + }, + { + "const": "herbaceous", + "title": "Herbaceous", + "count": 674281 + }, + { + "const": "ice", + "title": "Ice", + "count": 231285 + }, + { + "const": "urban", + "title": "Urban", + "count": 1219 + }, + { + "const": "water", + "title": "Water", + "count": 2303314 + } + ] + }, + "resto:location": { + "type": "string", + "oneOf": [ + { + "const": "tropical", + "title": "Tropical", + "count": 1807474 + }, + { + "const": "southern", + "title": "Southern", + "count": 1671685 + }, + { + "const": "northern", + "title": "Northern", + "count": 4876669 + }, + { + "const": "equatorial", + "title": "Equatorial", + "count": 27302 + }, + { + "const": "coastal", + "title": "Coastal", + "count": 1495516 + } + ] + }, + "platform": { + "type": "string", + "oneOf": [ + { + "const": "sentinel-2b", + "title": "Sentinel 2B", + "count": 3495597 + }, + { + "const": "sentinel-2a", + "title": "Sentinel 2A", + "count": 3117831 + } + ] + }, + "resto:season": { + "type": "integer", + "oneOf": [ + { + "const": 0, + "title": "Winter", + "count": 1621108 + }, + { + "const": 2, + "title": "Summer", + "count": 2279472 + }, + { + "const": 1, + "title": "Spring", + "count": 1577067 + }, + { + "const": 3, + "title": "Autumn", + "count": 1098015 + } + ] + }, + "bands": [ + { + "title": "B1", + "eo:common_name": "coastal", + "eo:center_wavelength": 0.4439, + "gsd": 60 + }, + { + "title": "B2", + "eo:common_name": "blue", + "eo:center_wavelength": 0.4966, + "gsd": 10 + }, + { + "title": "B3", + "eo:common_name": "green", + "eo:center_wavelength": 0.56, + "gsd": 10 + }, + { + "title": "B4", + "eo:common_name": "red", + "eo:center_wavelength": 0.6645, + "gsd": 10 + }, + { + "title": "B5", + "eo:center_wavelength": 0.7039, + "gsd": 20 + }, + { + "title": "B6", + "eo:center_wavelength": 0.7402, + "gsd": 20 + }, + { + "title": "B7", + "eo:center_wavelength": 0.7825, + "gsd": 20 + }, + { + "title": "B8", + "eo:common_name": "nir", + "eo:center_wavelength": 0.8351, + "gsd": 10 + }, + { + "title": "B8A", + "eo:center_wavelength": 0.8648, + "gsd": 20 + }, + { + "title": "B9", + "eo:center_wavelength": 0.945, + "gsd": 60 + }, + { + "title": "B10", + "eo:center_wavelength": 0.13735, + "gsd": 60 + }, + { + "title": "B11", + "eo:common_name": "swir16", + "eo:center_wavelength": 0.16137, + "gsd": 20 + }, + { + "title": "B12", + "eo:common_name": "swir22", + "eo:center_wavelength": 0.22024, + "gsd": 20 + } + ] + }, + "item_assets": { + "B1": { + "title": "Band 1 (coastal)", + "type": "image/tiff; application=geotiff", + "roles": [ + "data", + "visual" + ] + }, + "B2": { + "title": "Band 2 (blue)", + "type": "image/tiff; application=geotiff", + "roles": [ + "data", + "visual" + ] + }, + "B3": { + "title": "Band 3 (green)", + "type": "image/tiff; application=geotiff", + "roles": [ + "data", + "visual" + ] + }, + "B4": { + "title": "Band 4 (red)", + "type": "image/tiff; application=geotiff", + "roles": [ + "data", + "visual" + ] + } + } +} diff --git a/tests/data-files/examples/v1.1.0/collection-only/collection.json b/tests/data-files/examples/v1.1.0/collection-only/collection.json new file mode 100644 index 000000000..9fab89984 --- /dev/null +++ b/tests/data-files/examples/v1.1.0/collection-only/collection.json @@ -0,0 +1,233 @@ +{ + "type": "Collection", + "stac_version": "1.1.0", + "stac_extensions": [ + "https://stac-extensions.github.io/eo/v2.0.0/schema.json", + "https://stac-extensions.github.io/projection/v2.0.0/schema.json", + "https://stac-extensions.github.io/view/v1.0.0/schema.json" + ], + "id": "sentinel-2", + "title": "Sentinel-2 MSI: MultiSpectral Instrument, Level-1C", + "description": "Sentinel-2 is a wide-swath, high-resolution, multi-spectral\nimaging mission supporting Copernicus Land Monitoring studies,\nincluding the monitoring of vegetation, soil and water cover,\nas well as observation of inland waterways and coastal areas.\n\nThe Sentinel-2 data contain 13 UINT16 spectral bands representing\nTOA reflectance scaled by 10000. See the [Sentinel-2 User Handbook](https://sentinel.esa.int/documents/247904/685211/Sentinel-2_User_Handbook)\nfor details. In addition, three QA bands are present where one\n(QA60) is a bitmask band with cloud mask information. For more\ndetails, [see the full explanation of how cloud masks are computed.](https://sentinel.esa.int/web/sentinel/technical-guides/sentinel-2-msi/level-1c/cloud-masks)\n\nEach Sentinel-2 product (zip archive) may contain multiple\ngranules. Each granule becomes a separate Earth Engine asset.\nEE asset ids for Sentinel-2 assets have the following format:\nCOPERNICUS/S2/20151128T002653_20151128T102149_T56MNN. Here the\nfirst numeric part represents the sensing date and time, the\nsecond numeric part represents the product generation date and\ntime, and the final 6-character string is a unique granule identifier\nindicating its UTM grid reference (see [MGRS](https://en.wikipedia.org/wiki/Military_Grid_Reference_System)).\n\nFor more details on Sentinel-2 radiometric resoltuon, [see this page](https://earth.esa.int/web/sentinel/user-guides/sentinel-2-msi/resolutions/radiometric).\n", + "license": "other", + "keywords": [ + "copernicus", + "esa", + "eu", + "msi", + "radiance", + "sentinel" + ], + "providers": [ + { + "name": "European Union/ESA/Copernicus", + "roles": [ + "producer", + "licensor" + ], + "url": "https://sentinel.esa.int/web/sentinel/user-guides/sentinel-2-msi" + } + ], + "extent": { + "spatial": { + "bbox": [ + [ + -180, + -56, + 180, + 83 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2015-06-23T00:00:00Z", + null + ] + ] + } + }, + "assets": { + "metadata_iso_19139": { + "roles": [ + "metadata", + "iso-19139" + ], + "href": "https://storage.googleapis.com/open-cogs/stac-examples/sentinel-2-iso-19139.xml", + "title": "ISO 19139 metadata", + "type": "application/vnd.iso.19139+xml" + } + }, + "summaries": { + "datetime": { + "minimum": "2015-06-23T00:00:00Z", + "maximum": "2019-07-10T13:44:56Z" + }, + "platform": [ + "sentinel-2a", + "sentinel-2b" + ], + "constellation": [ + "sentinel-2" + ], + "instruments": [ + "msi" + ], + "view:off_nadir": { + "minimum": 0, + "maximum": 100 + }, + "view:sun_elevation": { + "minimum": 6.78, + "maximum": 89.9 + }, + "gsd": [ + 10, + 30, + 60 + ], + "proj:code": [ + "EPSG:32601", + "EPSG:32602", + "EPSG:32603", + "EPSG:32604", + "EPSG:32605", + "EPSG:32606", + "EPSG:32607", + "EPSG:32608", + "EPSG:32609", + "EPSG:32610", + "EPSG:32611", + "EPSG:32612", + "EPSG:32613", + "EPSG:32614", + "EPSG:32615", + "EPSG:32616", + "EPSG:32617", + "EPSG:32618", + "EPSG:32619", + "EPSG:32620", + "EPSG:32621", + "EPSG:32622", + "EPSG:32623", + "EPSG:32624", + "EPSG:32625", + "EPSG:32626", + "EPSG:32627", + "EPSG:32628", + "EPSG:32629", + "EPSG:32630", + "EPSG:32631", + "EPSG:32632", + "EPSG:32633", + "EPSG:32634", + "EPSG:32635", + "EPSG:32636", + "EPSG:32637", + "EPSG:32638", + "EPSG:32639", + "EPSG:32640", + "EPSG:32641", + "EPSG:32642", + "EPSG:32643", + "EPSG:32644", + "EPSG:32645", + "EPSG:32646", + "EPSG:32647", + "EPSG:32648", + "EPSG:32649", + "EPSG:32650", + "EPSG:32651", + "EPSG:32652", + "EPSG:32653", + "EPSG:32654", + "EPSG:32655", + "EPSG:32656", + "EPSG:32657", + "EPSG:32658", + "EPSG:32659", + "EPSG:32660" + ], + "bands": [ + { + "name": "B1", + "eo:common_name": "coastal", + "eo:center_wavelength": 0.4439 + }, + { + "name": "B2", + "eo:common_name": "blue", + "eo:center_wavelength": 0.4966 + }, + { + "name": "B3", + "eo:common_name": "green", + "eo:center_wavelength": 0.56 + }, + { + "name": "B4", + "eo:common_name": "red", + "eo:center_wavelength": 0.6645 + }, + { + "name": "B5", + "eo:center_wavelength": 0.7039 + }, + { + "name": "B6", + "eo:center_wavelength": 0.7402 + }, + { + "name": "B7", + "eo:center_wavelength": 0.7825 + }, + { + "name": "B8", + "eo:common_name": "nir", + "eo:center_wavelength": 0.8351 + }, + { + "name": "B8A", + "eo:center_wavelength": 0.8648 + }, + { + "name": "B9", + "eo:center_wavelength": 0.945 + }, + { + "name": "B10", + "eo:center_wavelength": 1.3735 + }, + { + "name": "B11", + "eo:common_name": "swir16", + "eo:center_wavelength": 1.6137 + }, + { + "name": "B12", + "eo:common_name": "swir22", + "eo:center_wavelength": 2.2024 + } + ] + }, + "links": [ + { + "rel": "parent", + "href": "../catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "root", + "href": "../catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "license", + "href": "https://scihub.copernicus.eu/twiki/pub/SciHubWebPortal/TermsConditions/Sentinel_Data_Terms_and_Conditions.pdf", + "title": "Legal notice on the use of Copernicus Sentinel Data and Service Information" + } + ] +} diff --git a/tests/data-files/examples/1.1.0/collection.json b/tests/data-files/examples/v1.1.0/collection.json similarity index 100% rename from tests/data-files/examples/1.1.0/collection.json rename to tests/data-files/examples/v1.1.0/collection.json diff --git a/tests/data-files/examples/1.1.0/collectionless-item.json b/tests/data-files/examples/v1.1.0/collectionless-item.json similarity index 100% rename from tests/data-files/examples/1.1.0/collectionless-item.json rename to tests/data-files/examples/v1.1.0/collectionless-item.json diff --git a/tests/data-files/examples/1.1.0/core-item.json b/tests/data-files/examples/v1.1.0/core-item.json similarity index 100% rename from tests/data-files/examples/1.1.0/core-item.json rename to tests/data-files/examples/v1.1.0/core-item.json diff --git a/tests/data-files/examples/v1.1.0/extended-item.json b/tests/data-files/examples/v1.1.0/extended-item.json new file mode 100644 index 000000000..c3e493200 --- /dev/null +++ b/tests/data-files/examples/v1.1.0/extended-item.json @@ -0,0 +1,210 @@ +{ + "stac_version": "1.1.0", + "stac_extensions": [ + "https://stac-extensions.github.io/eo/v2.0.0/schema.json", + "https://stac-extensions.github.io/projection/v2.0.0/schema.json", + "https://stac-extensions.github.io/scientific/v1.0.0/schema.json", + "https://stac-extensions.github.io/view/v1.0.0/schema.json", + "https://stac-extensions.github.io/remote-data/v1.0.0/schema.json" + ], + "type": "Feature", + "id": "20201211_223832_CS2", + "bbox": [ + 172.91173669923782, + 1.3438851951615003, + 172.95469614953714, + 1.3690476620161975 + ], + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 172.91173669923782, + 1.3438851951615003 + ], + [ + 172.95469614953714, + 1.3438851951615003 + ], + [ + 172.95469614953714, + 1.3690476620161975 + ], + [ + 172.91173669923782, + 1.3690476620161975 + ], + [ + 172.91173669923782, + 1.3438851951615003 + ] + ] + ] + }, + "properties": { + "title": "Extended Item", + "description": "A sample STAC Item that includes a variety of examples from the stable extensions", + "keywords": [ + "extended", + "example", + "item" + ], + "datetime": "2020-12-14T18:02:31.437000Z", + "created": "2020-12-15T01:48:13.725Z", + "updated": "2020-12-15T01:48:13.725Z", + "platform": "cool_sat2", + "instruments": [ + "cool_sensor_v2" + ], + "gsd": 0.66, + "eo:cloud_cover": 1.2, + "eo:snow_cover": 0, + "statistics": { + "vegetation": 12.57, + "water": 1.23, + "urban": 26.2 + }, + "proj:code": "EPSG:32659", + "proj:shape": [ + 5558, + 9559 + ], + "proj:transform": [ + 0.5, + 0, + 712710, + 0, + -0.5, + 151406, + 0, + 0, + 1 + ], + "view:sun_elevation": 54.9, + "view:off_nadir": 3.8, + "view:sun_azimuth": 135.7, + "rd:type": "scene", + "rd:anomalous_pixels": 0.14, + "rd:earth_sun_distance": 1.014156, + "rd:sat_id": "cool_sat2", + "rd:product_level": "LV3A", + "sci:doi": "10.5061/dryad.s2v81.2/27.2" + }, + "collection": "simple-collection", + "links": [ + { + "rel": "collection", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "root", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "parent", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "alternate", + "type": "text/html", + "href": "http://remotedata.io/catalog/20201211_223832_CS2/index.html", + "title": "HTML version of this STAC Item" + } + ], + "assets": { + "analytic": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2_analytic.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "4-Band Analytic", + "roles": [ + "data" + ], + "bands": [ + { + "name": "band1", + "eo:common_name": "blue", + "eo:center_wavelength": 0.47, + "eo:full_width_half_max": 70 + }, + { + "name": "band2", + "eo:common_name": "green", + "eo:center_wavelength": 0.56, + "eo:full_width_half_max": 80 + }, + { + "name": "band3", + "eo:common_name": "red", + "eo:center_wavelength": 0.645, + "eo:full_width_half_max": 90 + }, + { + "name": "band4", + "eo:common_name": "nir", + "eo:center_wavelength": 0.8, + "eo:full_width_half_max": 152 + } + ] + }, + "thumbnail": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2.jpg", + "title": "Thumbnail", + "type": "image/png", + "roles": [ + "thumbnail" + ] + }, + "visual": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "3-Band Visual", + "roles": [ + "visual" + ], + "bands": [ + { + "name": "band3", + "eo:common_name": "red", + "eo:center_wavelength": 0.645, + "eo:full_width_half_max": 90 + }, + { + "name": "band2", + "eo:common_name": "green", + "eo:center_wavelength": 0.56, + "eo:full_width_half_max": 80 + }, + { + "name": "band1", + "eo:common_name": "blue", + "eo:center_wavelength": 0.47, + "eo:full_width_half_max": 70 + } + ] + }, + "udm": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2_analytic_udm.tif", + "title": "Unusable Data Mask", + "type": "image/tiff; application=geotiff" + }, + "json-metadata": { + "href": "http://remotedata.io/catalog/20201211_223832_CS2/extended-metadata.json", + "title": "Extended Metadata", + "type": "application/json", + "roles": [ + "metadata" + ] + }, + "ephemeris": { + "href": "http://cool-sat.com/catalog/20201211_223832_CS2/20201211_223832_CS2.EPH", + "title": "Satellite Ephemeris Metadata" + } + } +} diff --git a/tests/data-files/examples/1.1.0/extensions-collection/collection.json b/tests/data-files/examples/v1.1.0/extensions-collection/collection.json similarity index 100% rename from tests/data-files/examples/1.1.0/extensions-collection/collection.json rename to tests/data-files/examples/v1.1.0/extensions-collection/collection.json diff --git a/tests/data-files/examples/1.1.0/extensions-collection/proj-example/proj-example.json b/tests/data-files/examples/v1.1.0/extensions-collection/proj-example/proj-example.json similarity index 100% rename from tests/data-files/examples/1.1.0/extensions-collection/proj-example/proj-example.json rename to tests/data-files/examples/v1.1.0/extensions-collection/proj-example/proj-example.json diff --git a/tests/data-files/examples/1.1.0/simple-item.json b/tests/data-files/examples/v1.1.0/simple-item.json similarity index 100% rename from tests/data-files/examples/1.1.0/simple-item.json rename to tests/data-files/examples/v1.1.0/simple-item.json diff --git a/tests/test_band.py b/tests/test_band.py new file mode 100644 index 000000000..e2f25b8b9 --- /dev/null +++ b/tests/test_band.py @@ -0,0 +1,94 @@ +import pytest + +from pystac import Asset, Band, Collection, Item + + +@pytest.mark.parametrize( + "data", [{"name": "B01"}, {}, {"name": "B01", "eo:common_name": "blue"}] +) +def test_roundtrip_to_dict(data: dict[str, str]) -> None: + band = Band(**data) + assert band.to_dict() == data + + +def test_get_band_fields() -> None: + band = Band(name="foo", field="one") + assert band["name"] == "foo" + assert band["field"] == "one" + assert "field" in band + assert band.extra_fields["field"] == "one" + + +def test_set_band_fields() -> None: + band = Band() + band["field"] = "two" + assert "field" in band + assert band["field"] == "two" + assert band.extra_fields["field"] == "two" + + +def test_set_band_name() -> None: + band = Band() + band.name = "B01" + assert band.name == "B01" + assert band["name"] == "B01" + + band["name"] = "B02" + assert band.name == "B02" + assert band["name"] == "B02" + + +def test_band_repr() -> None: + band = Band(name="foo", field="one") + assert str(band) == "Band(name='foo')" + + +def test_set_bands_on_asset() -> None: + asset = Asset(href="foo", bands=[Band(name="B01")]) + assert asset.bands == [Band(name="B01")] + + +def test_set_bands_on_asset_as_dict() -> None: + asset = Asset(href="foo", bands=[{"name": "B01"}]) + assert asset.bands == [Band(name="B01")] + + +def test_asset_to_dict_includes_bands() -> None: + asset = Asset(href="foo", bands=[Band("B01")]) + asset_dict = asset.to_dict() + assert "bands" in asset_dict + assert asset_dict["bands"] == [{"name": "B01"}] + + +def test_set_bands_on_item() -> None: + item = Item(id="foo", properties={"bands": [Band("B01")]}) + assert item.properties.bands == [Band("B01")] + + +def test_set_bands_on_item_as_dict() -> None: + item = Item(id="foo", properties={"bands": [{"name": "B01"}]}) + assert item.properties.bands == [Band("B01")] + + +def test_item_to_dict_includes_bands() -> None: + item = Item(id="foo", properties={"bands": [Band("B01")]}) + properties_dict = item.to_dict()["properties"] + assert "bands" in properties_dict + assert properties_dict["bands"] == [{"name": "B01"}] + + +def test_set_bands_on_collection() -> None: + collection = Collection(id="foo", description="foo", bands=[Band("B01")]) + assert collection.bands == [Band("B01")] + + +def test_set_bands_on_collection_as_dict() -> None: + collection = Collection(id="foo", description="foo", bands=[{"name": "B01"}]) + assert collection.bands == [Band("B01")] + + +def test_collection_to_dict_includes_bands() -> None: + collection = Collection(id="foo", description="foo", bands=[Band("B01")]) + collection_dict = collection.to_dict() + assert "bands" in collection_dict + assert collection_dict["bands"] == [{"name": "B01"}] diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 3f82b81ff..0c726a9be 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -1,2039 +1,17 @@ -from __future__ import annotations - import json -import os -import posixpath -import tempfile -from collections import defaultdict -from collections.abc import Iterator -from copy import deepcopy -from datetime import datetime, timezone from pathlib import Path -from typing import Any, cast - -import pytest - -import pystac -from pystac import ( - HIERARCHICAL_LINKS, - Asset, - Catalog, - CatalogType, - Collection, - Item, - Link, - MediaType, -) -from pystac.errors import STACError -from pystac.layout import ( - APILayoutStrategy, - BestPracticesLayoutStrategy, - HrefLayoutStrategy, - TemplateLayoutStrategy, -) -from pystac.utils import ( - is_absolute_href, - make_absolute_href, - make_posix_style, - make_relative_href, -) -from tests.utils import ( - ARBITRARY_BBOX, - ARBITRARY_EXTENT, - ARBITRARY_GEOM, - MockStacIO, - TestCases, -) - - -class TestCatalogType: - def test_determine_type_for_absolute_published(self) -> None: - cat = TestCases.case_1() - with tempfile.TemporaryDirectory() as tmp_dir: - cat.normalize_and_save(tmp_dir, catalog_type=CatalogType.ABSOLUTE_PUBLISHED) - cat_json = pystac.StacIO.default().read_json( - os.path.join(tmp_dir, "catalog.json") - ) - - catalog_type = CatalogType.determine_type(cat_json) - assert catalog_type == CatalogType.ABSOLUTE_PUBLISHED - - def test_determine_type_for_relative_published(self) -> None: - cat = TestCases.case_2() - with tempfile.TemporaryDirectory() as tmp_dir: - cat.normalize_and_save(tmp_dir, catalog_type=CatalogType.RELATIVE_PUBLISHED) - cat_json = pystac.StacIO.default().read_json( - os.path.join(tmp_dir, "catalog.json") - ) - - catalog_type = CatalogType.determine_type(cat_json) - assert catalog_type == CatalogType.RELATIVE_PUBLISHED - - def test_determine_type_for_self_contained(self) -> None: - cat_json = pystac.StacIO.default().read_json( - TestCases.get_path("data-files/catalogs/test-case-1/catalog.json") - ) - catalog_type = CatalogType.determine_type(cat_json) - assert catalog_type == CatalogType.SELF_CONTAINED - - def test_determine_type_for_unknown(self) -> None: - catalog = Catalog(id="test", description="test desc") - subcat = Catalog(id="subcat", description="subcat desc") - catalog.add_child(subcat) - catalog.normalize_hrefs("http://example.com") - d = catalog.to_dict(include_self_link=False) - - assert CatalogType.determine_type(d) is None - - -class TestCatalog: - def test_create_and_read(self) -> None: - with tempfile.TemporaryDirectory() as tmp_dir: - cat_dir = os.path.join(tmp_dir, "catalog") - catalog = TestCases.case_1() - - catalog.normalize_and_save( - cat_dir, catalog_type=CatalogType.ABSOLUTE_PUBLISHED - ) - - read_catalog = Catalog.from_file(f"{cat_dir}/catalog.json") - - collections = catalog.get_children() - assert len(list(collections)) == 2 - - items = read_catalog.get_items(recursive=True) - - assert len(list(items)) == 8 - - def test_from_dict_preserves_dict(self) -> None: - catalog_dict = TestCases.case_1().to_dict() - param_dict = deepcopy(catalog_dict) - - # test that the parameter is preserved - _ = Catalog.from_dict(param_dict) - assert param_dict == catalog_dict - - # assert that the parameter is not preserved with - # non-default parameter - _ = Catalog.from_dict(param_dict, preserve_dict=False, migrate=False) - assert param_dict != catalog_dict - - def test_from_file_bad_catalog(self) -> None: - with pytest.raises(pystac.errors.STACTypeError) as ctx: - _ = Catalog.from_file(TestCases.get_path(TestCases.bad_catalog_case)) - assert "(id = broken_cat) does not represent a STACObject" in ctx.value.args[0] - assert "is Catalog" in ctx.value.args[0] - - def test_from_dict_set_root(self) -> None: - path = TestCases.get_path("data-files/catalogs/test-case-1/catalog.json") - with open(path) as f: - cat_dict = json.load(f) - root_cat = pystac.Catalog(id="test", description="test desc") - collection = Catalog.from_dict(cat_dict, root=root_cat) - assert collection.get_root() is root_cat - - @pytest.mark.vcr() - def test_read_remote(self) -> None: - catalog_url = ( - "https://raw.githubusercontent.com/stac-extensions/label/main/" - "examples/multidataset/catalog.json" - ) - cat = Catalog.from_file(catalog_url) - - zanzibar = cat.get_child("zanzibar-collection") - assert zanzibar is not None - - assert len(list(zanzibar.get_items())) == 2 - - def test_clear_items_removes_from_cache(self) -> None: - catalog = Catalog(id="test", description="test") - subcat = Catalog(id="subcat", description="test") - catalog.add_child(subcat) - item = Item( - id="test-item", - geometry=ARBITRARY_GEOM, - bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), - properties={"key": "one"}, - ) - subcat.add_item(item) - - items = list(catalog.get_items(recursive=True)) - assert len(items) == 1 - assert items[0].properties["key"] == "one" - - subcat.clear_items() - item = Item( - id="test-item", - geometry=ARBITRARY_GEOM, - bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), - properties={"key": "two"}, - ) - subcat.add_item(item) - - items = list(catalog.get_items(recursive=True)) - assert len(items) == 1 - assert items[0].properties["key"] == "two" - - subcat.remove_item("test-item") - item = Item( - id="test-item", - geometry=ARBITRARY_GEOM, - bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), - properties={"key": "three"}, - ) - subcat.add_item(item) - - items = list(catalog.get_items(recursive=True)) - assert len(items) == 1 - assert items[0].properties["key"] == "three" - - def test_clear_children_removes_from_cache(self) -> None: - catalog = Catalog(id="test", description="test") - subcat = Catalog(id="subcat", description="test") - catalog.add_child(subcat) - - children = list(catalog.get_children()) - assert len(children) == 1 - assert children[0].description == "test" - - catalog.clear_children() - subcat = Catalog(id="subcat", description="test2") - catalog.add_child(subcat) - - children = list(catalog.get_children()) - assert len(children) == 1 - assert children[0].description == "test2" - - catalog.remove_child("subcat") - subcat = Catalog(id="subcat", description="test3") - catalog.add_child(subcat) - - children = list(catalog.get_children()) - assert len(children) == 1 - assert children[0].description == "test3" - - def test_clear_children_sets_parent_and_root_to_None(self) -> None: - catalog = Catalog(id="test", description="test") - subcat1 = Catalog(id="subcat", description="test") - subcat2 = Catalog(id="subcat2", description="test2") - catalog.add_children([subcat1, subcat2]) - - assert subcat1.get_parent() is not None - assert subcat2.get_parent() is not None - assert subcat1.get_root() is not None - assert subcat2.get_root() is not None - - children = list(catalog.get_children()) - assert len(children) == 2 - - catalog.clear_children() - - assert subcat1.get_parent() is None - assert subcat2.get_parent() is None - assert subcat1.get_root() is None - assert subcat2.get_root() is None - - def test_add_child_throws_if_item(self) -> None: - cat = TestCases.case_1() - item = next(cat.get_items(recursive=True)) - with pytest.raises(pystac.STACError): - cat.add_child(item) # type:ignore - - def test_add_child_returns_link(self) -> None: - parent = Catalog(id="parent", description="test") - child = Catalog(id="child", description="test") - link = parent.add_child(child) - assert isinstance(link, pystac.Link) - link.extra_fields["foo"] = "bar" - link2 = parent.get_single_link("child") - assert link2 is not None - assert link2.extra_fields["foo"] == "bar" - - def test_add_children_returns_links(self) -> None: - parent = Catalog(id="parent", description="test") - child1 = Catalog(id="child", description="test") - child2 = Catalog(id="child2", description="test") - links = parent.add_children([child1, child2]) - assert isinstance(links, list) - assert len(links) == 2 - assert isinstance(links[0], pystac.Link) - assert isinstance(links[1], pystac.Link) - links2 = parent.get_links("child") - assert links[0] in links2 - assert links[1] in links2 - - def test_add_child_override_parent(self) -> None: - parent1 = Catalog(id="parent1", description="test1") - parent2 = Catalog(id="parent2", description="test2") - child = Catalog(id="child", description="test3") - assert child.get_parent() is None - - parent1.add_child(child) - assert child.get_parent() is parent1 - - parent2.add_child(child) - assert child.get_parent() is parent2 - - def test_add_child_set_parent_false(self) -> None: - parent1 = Catalog(id="parent1", description="test1") - parent2 = Catalog(id="parent2", description="test2") - child = Catalog(id="child", description="test3") - assert child.get_parent() is None - - parent1.add_child(child, set_parent=False) - assert child.get_parent() is not parent1 - - parent1.add_child(child) - parent2.add_child(child, set_parent=False) - assert child.get_parent() is parent1 - - def test_add_item_override_parent(self) -> None: - parent1 = Catalog(id="parent1", description="test1") - parent2 = Catalog(id="parent2", description="test2") - child = Item( - id="child", - geometry=None, - bbox=None, - datetime=datetime.now(), - properties={}, - ) - assert child.get_parent() is None - - parent1.add_item(child) - assert child.get_parent() is parent1 - - parent2.add_item(child) - assert child.get_parent() is parent2 - - def test_add_item_set_parent_false(self) -> None: - parent1 = Catalog(id="parent1", description="test1") - parent2 = Catalog(id="parent2", description="test2") - child = Item( - id="child", - geometry=None, - bbox=None, - datetime=datetime.now(), - properties={}, - ) - assert child.get_parent() is None - - parent1.add_item(child, set_parent=False) - assert child.get_parent() is not parent1 - - parent1.add_item(child) - parent2.add_item(child, set_parent=False) - assert child.get_parent() is parent1 - - def test_add_item_throws_if_child(self) -> None: - cat = TestCases.case_1() - child = next(iter(cat.get_children())) - with pytest.raises(pystac.STACError): - cat.add_item(child) # type:ignore - - def test_add_item_returns_link(self) -> None: - parent = Catalog(id="parent", description="test") - child = Item( - id="child", - geometry=None, - bbox=None, - datetime=datetime.now(), - properties={}, - ) - link = parent.add_item(child) - assert isinstance(link, pystac.Link) - link.extra_fields["foo"] = "bar" - link2 = parent.get_single_link("item") - assert link2 is not None - assert link2.extra_fields["foo"] == "bar" - - def test_add_items_returns_links(self) -> None: - parent = Catalog(id="parent", description="test") - child1 = Item( - id="child1", - geometry=None, - bbox=None, - datetime=datetime.now(), - properties={}, - ) - child2 = Item( - id="child2", - geometry=None, - bbox=None, - datetime=datetime.now(), - properties={}, - ) - links = parent.add_items([child1, child2]) - assert isinstance(links, list) - assert len(links) == 2 - assert isinstance(links[0], pystac.Link) - assert isinstance(links[1], pystac.Link) - links2 = parent.get_links("item") - assert links[0] in links2 - assert links[1] in links2 - - def test_get_child_returns_none_if_not_found(self) -> None: - cat = TestCases.case_1() - child = cat.get_child("thisshouldnotbeachildid", recursive=True) - assert child is None - - def test_get_item_is_deprecated_but_still_works(self) -> None: - cat = TestCases.case_1() - with pytest.warns(DeprecationWarning): - item = cat.get_item("area-2-1-imagery", recursive=True) - assert item is not None - - def test_get_item_returns_none_if_not_found(self) -> None: - cat = TestCases.case_1() - with pytest.warns(DeprecationWarning): - item = cat.get_item("thisshouldnotbeanitemid", recursive=True) - assert item is None - - def test_get_all_items_is_deprecated_but_still_works(self) -> None: - cat = TestCases.case_1() - with pytest.warns(DeprecationWarning): - all_items = cat.get_all_items() - items = cat.get_items(recursive=True) - assert all(a == i for a, i in zip(all_items, items)) - - def test_get_items_returns_empty_generator_if_not_found(self) -> None: - cat = TestCases.case_1() - items = cat.get_items("thisshouldnotbeanitemid", recursive=True) - assert next(items, None) is None - - def test_sets_catalog_type(self) -> None: - cat = TestCases.case_1() - - assert cat.catalog_type == CatalogType.SELF_CONTAINED - - @pytest.mark.parametrize("catalog", TestCases.all_test_catalogs()) - def test_walk_iterates_correctly(self, catalog: Catalog) -> None: - def test_catalog(cat: Catalog) -> None: - expected_catalog_iterations = 1 - actual_catalog_iterations = 0 - for root, children, items in cat.walk(): - actual_catalog_iterations += 1 - expected_catalog_iterations += len(list(root.get_children())) - - assert {c.id for c in root.get_children()} == { - c.id for c in children - }, "Children unequal" - - assert {c.id for c in root.get_items()} == {c.id for c in items}, ( - "Items unequal" - ) - - assert actual_catalog_iterations == expected_catalog_iterations - - test_catalog(catalog) - - @pytest.mark.parametrize("catalog", TestCases.all_test_catalogs()) - def test_clone_generates_correct_links(self, catalog: Catalog) -> None: - expected_link_types_to_counts: Any = {} - actual_link_types_to_counts: Any = {} - - for root, _, items in catalog.walk(): - expected_link_types_to_counts[root.id] = defaultdict(int) - actual_link_types_to_counts[root.id] = defaultdict(int) - - for link in root.get_links(): - expected_link_types_to_counts[root.id][link.rel] += 1 - - for link in root.clone().get_links(): - actual_link_types_to_counts[root.id][link.rel] += 1 - - for item in items: - expected_link_types_to_counts[item.id] = defaultdict(int) - actual_link_types_to_counts[item.id] = defaultdict(int) - for link in item.get_links(): - expected_link_types_to_counts[item.id][link.rel] += 1 - for link in item.get_links(): - actual_link_types_to_counts[item.id][link.rel] += 1 - - assert set(expected_link_types_to_counts.keys()) == set( - actual_link_types_to_counts.keys() - ) - - for obj_id in actual_link_types_to_counts: - expected_counts = expected_link_types_to_counts[obj_id] - actual_counts = actual_link_types_to_counts[obj_id] - assert set(expected_counts.keys()) == set(actual_counts.keys()) - for rel in expected_counts: - assert actual_counts[rel] == expected_counts[rel], ( - "Clone of {} has {} {} links, original has {}".format( - obj_id, actual_counts[rel], rel, expected_counts[rel] - ) - ) - - def test_save_uses_previous_catalog_type(self) -> None: - catalog = TestCases.case_1() - assert catalog.catalog_type == CatalogType.SELF_CONTAINED - with tempfile.TemporaryDirectory() as tmp_dir: - catalog.normalize_hrefs(tmp_dir) - href = catalog.self_href - catalog.save() - - cat2 = pystac.Catalog.from_file(href) - assert cat2.catalog_type == CatalogType.SELF_CONTAINED - - def test_save_to_provided_href(self) -> None: - with tempfile.TemporaryDirectory() as tmp_dir: - catalog = TestCases.case_1() - href = "https://stac.test" - folder = os.path.join(tmp_dir, "cat") - catalog.normalize_hrefs(href) - catalog.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED, dest_href=folder) - - catalog_path = os.path.join(folder, "catalog.json") - assert os.path.exists(catalog_path) - result_cat = Catalog.from_file(catalog_path) - for link in result_cat.get_child_links(): - assert cast(str, link.target).startswith(href) - - def test_save_relative_published_no_self_links(self) -> None: - with tempfile.TemporaryDirectory() as tmp_dir: - catalog = TestCases.case_1() - href = "https://stac.test" - folder = os.path.join(tmp_dir, "cat") - catalog.normalize_hrefs(href) - catalog.save(catalog_type=CatalogType.RELATIVE_PUBLISHED, dest_href=folder) - - catalog_path = os.path.join(folder, "catalog.json") - assert os.path.exists(catalog_path) - result_cat = Catalog.from_file(catalog_path) - - # Check that Items do not have a self link - # Since Item.from_dict automatically adds a self link, we need to look at - # the JSON files themselves. - stac_io = pystac.StacIO.default() - - for current_cat, _, __ in result_cat.walk(): - for item_link in current_cat.get_item_links(): - item_dict = stac_io.read_json(item_link) - self_link = next( - ( - link - for link in item_dict.get("links", []) - if link["rel"] == "self" - ), - None, - ) - assert self_link is None - - def test_save_with_different_stac_io(self) -> None: - catalog = Catalog.from_file( - TestCases.get_path("data-files/catalogs/test-case-1/catalog.json") - ) - stac_io = MockStacIO() - with tempfile.TemporaryDirectory() as tmp_dir: - catalog.normalize_hrefs(tmp_dir) - catalog.save( - catalog_type=CatalogType.ABSOLUTE_PUBLISHED, - dest_href=tmp_dir, - stac_io=stac_io, - ) - - hrefs = [] - for root, _, items in catalog.walk(): - hrefs.append(root.get_self_href()) - for item in items: - hrefs.append(item.get_self_href()) - - assert len(hrefs) == stac_io.mock.write_text.call_count - for call_args_list in stac_io.mock.write_text.call_args_list: - assert call_args_list[0][0] in hrefs - - def test_subcatalogs_saved_to_correct_path(self) -> None: - with tempfile.TemporaryDirectory() as tmp_dir: - catalog = TestCases.case_1() - href = "https://stac.test" - - catalog.normalize_hrefs(href) - catalog.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED, dest_href=tmp_dir) - - # Check the root catalog path - expected_root_catalog_path = os.path.join(tmp_dir, "catalog.json") - assert os.path.exists(expected_root_catalog_path), ( - f"{expected_root_catalog_path} does not exist." - ) - assert os.path.isfile(expected_root_catalog_path), ( - f"{expected_root_catalog_path} is not a file." - ) - - # Check each child catalog - for child_catalog in catalog.get_children(): - relative_path = make_relative_href( - child_catalog.self_href, catalog.self_href, start_is_dir=False - ) - expected_child_path = make_absolute_href( - relative_path, - expected_root_catalog_path, - start_is_dir=False, - ) - assert os.path.exists(expected_child_path), ( - f"{expected_child_path} does not exist." - ) - assert os.path.isfile(expected_child_path), ( - f"{expected_child_path} is not a file." - ) - - # Check each item - for item in catalog.get_items(recursive=True): - relative_path = make_relative_href( - item.self_href, catalog.self_href, start_is_dir=False - ) - expected_item_path = make_absolute_href( - relative_path, - expected_root_catalog_path, - start_is_dir=False, - ) - assert os.path.exists(expected_item_path), ( - f"{expected_item_path} does not exist." - ) - assert os.path.isfile(expected_item_path), ( - f"{expected_item_path} is not a file." - ) - - def test_clone_uses_previous_catalog_type(self) -> None: - catalog = TestCases.case_1() - assert catalog.catalog_type == CatalogType.SELF_CONTAINED - clone = catalog.clone() - assert clone.catalog_type == CatalogType.SELF_CONTAINED - - def test_normalize_hrefs_sets_all_hrefs(self) -> None: - catalog = TestCases.case_1() - catalog.normalize_hrefs("http://example.com") - for root, _, items in catalog.walk(): - self_href = root.get_self_href() - assert self_href is not None - assert self_href.startswith("http://example.com") - for link in root.links: - if link.is_resolved(): - target_href = cast(pystac.STACObject, link.target).self_href - else: - target_href = link.absolute_href - assert "http://example.com" in target_href, ( - '[{}] {} does not contain "{}"'.format( - link.rel, target_href, "http://example.com" - ) - ) - for item in items: - assert "http://example.com" in item.self_href - - def test_normalize_hrefs_makes_absolute_href(self) -> None: - catalog = TestCases.case_1() - catalog.normalize_hrefs("./relativepath") - abspath = make_posix_style(os.path.abspath("./relativepath")) - self_href = catalog.get_self_href() - assert self_href is not None - assert self_href.startswith(abspath) - - def test_normalize_hrefs_skip_unresolved(self) -> None: - catalog = TestCases.case_1() - catalog.normalize_hrefs("http://example.com", skip_unresolved=True) - assert catalog.self_href.startswith("http://example.com") - for link in catalog.links: - if link.rel == "child" or link.rel == "item": - assert not link.is_resolved() - item = Item( - "an-id", - geometry=None, - bbox=None, - datetime=datetime.now(), - properties={}, - ) - catalog.add_item(item, title="This is the test item") - catalog.normalize_hrefs("http://example.com", skip_unresolved=True) - for link in catalog.links: - if link.title == "This is the test item": - assert link.is_resolved() - assert isinstance(link.target, pystac.STACObject) - assert link.target.self_href.startswith("http://example.com") - elif link.rel == "child" or link.rel == "item": - assert not link.is_resolved() - - def test_save_unresolved(self) -> None: - catalog = Catalog("an-id", "a description") - item = Item( - "an-id", - geometry=None, - bbox=None, - datetime=datetime.now(), - properties={}, - ) - catalog.add_item(item) - catalog.add_link(pystac.Link("child", "/not/a/real/path/catalog.json")) - with tempfile.TemporaryDirectory() as temporary_directory: - catalog.set_self_href(os.path.join(temporary_directory, "catalog.json")) - item.set_self_href(os.path.join(temporary_directory, "item.json")) - catalog.save() - assert len(os.listdir(temporary_directory)) == 2 - - with tempfile.TemporaryDirectory() as temporary_directory: - catalog.normalize_and_save(temporary_directory, skip_unresolved=True) - assert len(os.listdir(temporary_directory)) == 2 - - with tempfile.TemporaryDirectory() as temporary_directory: - with pytest.raises(STACError, match="does not resolve to a STAC object"): - catalog.normalize_and_save(temporary_directory, skip_unresolved=False) - - def test_generate_subcatalogs_works_with_custom_properties(self) -> None: - catalog = TestCases.case_8() - defaults = {"pl:item_type": "PlanetScope"} - catalog.generate_subcatalogs( - "${year}/${month}/${pl:item_type}", defaults=defaults - ) - - month_cat = catalog.get_child("8", recursive=True) - assert month_cat is not None - type_cats = {cat.id for cat in month_cat.get_children()} - - assert type_cats == {"PSScene4Band", "SkySatScene", "PlanetScope"} - - def test_generate_subcatalogs_does_not_change_item_count(self) -> None: - catalog = TestCases.case_7() - - item_counts = { - cat.id: len(list(cat.get_items(recursive=True))) - for cat in catalog.get_children() - } - - catalog.generate_subcatalogs("${year}/${day}") - - with tempfile.TemporaryDirectory() as tmp_dir: - catalog.normalize_hrefs(tmp_dir) - catalog.save(pystac.CatalogType.SELF_CONTAINED) - - cat2 = pystac.Catalog.from_file(os.path.join(tmp_dir, "catalog.json")) - for child in cat2.get_children(): - actual = len(list(child.get_items(recursive=True))) - expected = item_counts[child.id] - assert actual == expected, f" for child '{child.id}'" - - def test_generate_subcatalogs_merge_template_elements(self) -> None: - catalog = Catalog(id="test", description="Test") - item_properties = [ - dict(property1=p1, property2=p2) for p1 in ("A", "B") for p2 in (1, 2) - ] - for ni, properties in enumerate(item_properties): - catalog.add_item( - Item( - id=f"item{ni}", - geometry=ARBITRARY_GEOM, - bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), - properties=properties, - ) - ) - result = catalog.generate_subcatalogs("${property1}_${property2}") - - actual_subcats = {cat.id for cat in result} - expected_subcats = { - "{}_{}".format(d["property1"], d["property2"]) for d in item_properties - } - assert len(result) == len(expected_subcats) - assert actual_subcats == expected_subcats - - def test_generate_subcatalogs_can_be_applied_multiple_times(self) -> None: - catalog = TestCases.case_8() - - _ = catalog.generate_subcatalogs("${year}/${month}") - catalog.normalize_hrefs("/tmp") - expected_hrefs = { - item.id: item.get_self_href() for item in catalog.get_items(recursive=True) - } - - result = catalog.generate_subcatalogs("${year}/${month}") - assert len(result) == 0 - catalog.normalize_hrefs("/tmp") - for item in catalog.get_items(recursive=True): - assert item.get_self_href() == expected_hrefs[item.id], ( - f" for item '{item.id}'" - ) - - def test_generate_subcatalogs_works_after_adding_more_items(self) -> None: - catalog = Catalog(id="test", description="Test") - properties = dict(property1="A", property2=1) - catalog.add_item( - Item( - id="item1", - geometry=ARBITRARY_GEOM, - bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), - properties=properties, - ) - ) - catalog.generate_subcatalogs("${property1}/${property2}") - catalog.add_item( - Item( - id="item2", - geometry=ARBITRARY_GEOM, - bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), - properties=properties, - ) - ) - catalog.generate_subcatalogs("${property1}/${property2}") - - catalog.normalize_hrefs("/tmp") - item1 = next(catalog.get_items("item1", recursive=True)) - item1_parent = item1.get_parent() - assert item1_parent is not None - item2 = next(catalog.get_items("item2", recursive=True)) - item2_parent = item2.get_parent() - assert item2_parent is not None - assert item1_parent.get_self_href() == item2_parent.get_self_href() - - def test_generate_subcatalogs_works_for_branched_subcatalogs(self) -> None: - catalog = Catalog(id="test", description="Test") - item_properties = [ - dict(property1="A", property2=1, property3="i"), # add 3 subcats - dict(property1="A", property2=1, property3="j"), # add 1 more - dict(property1="A", property2=2, property3="i"), # add 2 more - dict(property1="B", property2=1, property3="i"), # add 3 more - ] - for ni, properties in enumerate(item_properties): - catalog.add_item( - Item( - id=f"item{ni}", - geometry=ARBITRARY_GEOM, - bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), - properties=properties, - ) - ) - result = catalog.generate_subcatalogs("${property1}/${property2}/${property3}") - assert len(result) == 9 - - actual_subcats = {cat.id for cat in result} - expected_subcats = {"A", "B", "1", "2", "i", "j"} - assert actual_subcats == expected_subcats - - def test_generate_subcatalogs_works_for_subcatalogs_with_same_ids(self) -> None: - catalog = Catalog(id="test", description="Test") - item_properties = [ - dict(property1=1, property2=1), # add 2 subcats - dict(property1=1, property2=2), # add 1 more - dict(property1=2, property2=1), # add 2 more - dict(property1=2, property2=2), # add 1 more - ] - for ni, properties in enumerate(item_properties): - catalog.add_item( - Item( - id=f"item{ni}", - geometry=ARBITRARY_GEOM, - bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), - properties=properties, - ) - ) - - result = catalog.generate_subcatalogs("${property1}/${property2}") - assert len(result) == 6 - - catalog.normalize_hrefs("/") - - for item in catalog.get_items(recursive=True): - item_parent = item.get_parent() - assert item_parent is not None - parent_href = item_parent.self_href - path_to_parent, _ = os.path.split(parent_href) - subcats = list( - Path(path_to_parent).parts[1:] - ) # Skip drive letter if present (Windows) - - assert len(subcats) == 2, f" for item '{item.id}'" - - def test_map_items(self) -> None: - def item_mapper(item: pystac.Item) -> pystac.Item: - item.properties["ITEM_MAPPER"] = "YEP" - return item - - with tempfile.TemporaryDirectory() as tmp_dir: - catalog = TestCases.case_1() - - new_cat = catalog.map_items(item_mapper) - - new_cat.normalize_hrefs(os.path.join(tmp_dir, "cat")) - new_cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) - - result_cat = Catalog.from_file(os.path.join(tmp_dir, "cat", "catalog.json")) - - for item in result_cat.get_items(recursive=True): - assert "ITEM_MAPPER" in item.properties - - for item in catalog.get_items(recursive=True): - assert "ITEM_MAPPER" not in item.properties - - def test_map_items_multiple(self) -> None: - def item_mapper(item: pystac.Item) -> list[pystac.Item]: - item2 = item.clone() - item2.id = item2.id + "_2" - item.properties["ITEM_MAPPER_1"] = "YEP" - item2.properties["ITEM_MAPPER_2"] = "YEP" - return [item, item2] - - with tempfile.TemporaryDirectory() as tmp_dir: - catalog = TestCases.case_1() - catalog_items = list(catalog.get_items(recursive=True)) - - new_cat = catalog.map_items(item_mapper) - - new_cat.normalize_hrefs(os.path.join(tmp_dir, "cat")) - new_cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) - - result_cat = Catalog.from_file(os.path.join(tmp_dir, "cat", "catalog.json")) - result_items = list(result_cat.get_items(recursive=True)) - - assert len(catalog_items) * 2 == len(result_items) - - ones, twos = 0, 0 - for item in result_items: - assert ("ITEM_MAPPER_1" in item.properties) or ( - "ITEM_MAPPER_2" in item.properties - ) - if "ITEM_MAPPER_1" in item.properties: - ones += 1 - - if "ITEM_MAPPER_2" in item.properties: - twos += 1 - - assert ones == twos - - for item in catalog.get_items(recursive=True): - assert ("ITEM_MAPPER_1" not in item.properties) and ( - "ITEM_MAPPER_2" not in item.properties - ) - - def test_map_items_multiple_2(self) -> None: - catalog = Catalog(id="test-1", description="Test1") - item1 = Item( - id="item1", - geometry=ARBITRARY_GEOM, - bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), - properties={}, - ) - item1.add_asset("ortho", Asset(href="/some/ortho.tif")) - catalog.add_item(item1) - kitten = Catalog(id="test-kitten", description="A cuter version of catalog") - catalog.add_child(kitten) - item2 = Item( - id="item2", - geometry=ARBITRARY_GEOM, - bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), - properties={}, - ) - item2.add_asset("ortho", Asset(href="/some/other/ortho.tif")) - kitten.add_item(item2) - - def modify_item_title(item: pystac.Item) -> pystac.Item: - item.properties["title"] = "Some title" - return item - - def duplicate_item(item: pystac.Item) -> list[pystac.Item]: - duplicated_item = item.clone() - duplicated_item.id += "-duplicated" - return [item, duplicated_item] - - c = catalog.map_items(modify_item_title) - c = c.map_items(duplicate_item) - new_catalog = c - - items = new_catalog.get_items(recursive=True) - assert len(list(items)) == 4 - - def test_map_assets_single(self) -> None: - changed_asset = "d43bead8-e3f8-4c51-95d6-e24e750a402b" - - def asset_mapper(key: str, asset: pystac.Asset) -> pystac.Asset: - if key == changed_asset: - asset.title = "NEW TITLE" - - return asset - - with tempfile.TemporaryDirectory() as tmp_dir: - catalog = TestCases.case_2() - - new_cat = catalog.map_assets(asset_mapper) - - new_cat.normalize_hrefs(os.path.join(tmp_dir, "cat")) - new_cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) - - result_cat = Catalog.from_file(os.path.join(tmp_dir, "cat", "catalog.json")) - - found = False - for item in result_cat.get_items(recursive=True): - for key, asset in item.assets.items(): - if key == changed_asset: - found = True - assert asset.title == "NEW TITLE" - else: - assert asset.title != "NEW TITLE" - assert found - - def test_map_assets_tup(self) -> None: - changed_assets: list[str] = [] - - def asset_mapper( - key: str, asset: pystac.Asset - ) -> pystac.Asset | tuple[str, pystac.Asset]: - if asset.media_type and "geotiff" in asset.media_type: - asset.title = "NEW TITLE" - changed_assets.append(key) - return (f"{key}-modified", asset) - else: - return asset - - with tempfile.TemporaryDirectory() as tmp_dir: - catalog = TestCases.case_2() - - new_cat = catalog.map_assets(asset_mapper) - - new_cat.normalize_hrefs(os.path.join(tmp_dir, "cat")) - new_cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) - - result_cat = Catalog.from_file(os.path.join(tmp_dir, "cat", "catalog.json")) - - found = False - not_found = False - for item in result_cat.get_items(recursive=True): - for key, asset in item.assets.items(): - if key.replace("-modified", "") in changed_assets: - found = True - assert asset.title == "NEW TITLE" - else: - not_found = True - assert asset.title != "NEW TITLE" - - assert found - assert not_found - - def test_map_assets_multi(self) -> None: - changed_assets = [] - - def asset_mapper( - key: str, asset: pystac.Asset - ) -> pystac.Asset | dict[str, pystac.Asset]: - if asset.media_type and "geotiff" in asset.media_type: - changed_assets.append(key) - mod1 = asset.clone() - mod1.title = "NEW TITLE 1" - mod2 = asset.clone() - mod2.title = "NEW TITLE 2" - return {f"{key}-mod-1": mod1, f"{key}-mod-2": mod2} - else: - return asset - - with tempfile.TemporaryDirectory() as tmp_dir: - catalog = TestCases.case_2() - - new_cat = catalog.map_assets(asset_mapper) - - new_cat.normalize_hrefs(os.path.join(tmp_dir, "cat")) - new_cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) - - result_cat = Catalog.from_file(os.path.join(tmp_dir, "cat", "catalog.json")) - - found1 = False - found2 = False - not_found = False - for item in result_cat.get_items(recursive=True): - for key, asset in item.assets.items(): - if key.replace("-mod-1", "") in changed_assets: - found1 = True - assert asset.title == "NEW TITLE 1" - elif key.replace("-mod-2", "") in changed_assets: - found2 = True - assert asset.title == "NEW TITLE 2" - else: - not_found = True - assert asset.title != "NEW TITLE" - - assert found1 - assert found2 - assert not_found - - def test_make_all_asset_hrefs_absolute(self) -> None: - cat = TestCases.case_2() - cat.make_all_asset_hrefs_absolute() - ID = "cf73ec1a-d790-4b59-b077-e101738571ed" - item = next(cat.get_items(ID, recursive=True)) - href = item.assets[ID].href - assert is_absolute_href(href) - - def test_make_all_asset_hrefs_relative(self) -> None: - cat = TestCases.case_2() - ID = "cf73ec1a-d790-4b59-b077-e101738571ed" - item = next(cat.get_items(ID, recursive=True)) - asset = item.assets[ID] - original_href = asset.href - cat.make_all_asset_hrefs_absolute() - - assert is_absolute_href(asset.href) - - cat.make_all_asset_hrefs_relative() - - assert not is_absolute_href(asset.href) - assert asset.href == original_href - - @pytest.mark.parametrize("catalog", TestCases.all_test_catalogs()) - def test_make_all_links_relative_or_absolute(self, catalog: Catalog) -> None: - def check_all_relative(cat: Catalog) -> None: - for root, catalogs, items in cat.walk(): - for link in root.links: - if link.rel in HIERARCHICAL_LINKS: - assert not is_absolute_href(link.href) - for item in items: - for link in item.links: - if link.rel in HIERARCHICAL_LINKS: - assert not is_absolute_href(link.href) - - def check_all_absolute(cat: Catalog) -> None: - for root, catalogs, items in cat.walk(): - for link in root.links: - assert is_absolute_href(link.href) - for item in items: - for link in item.links: - assert is_absolute_href(link.href) - - with tempfile.TemporaryDirectory() as tmp_dir: - c2 = catalog.full_copy() - c2.normalize_hrefs(tmp_dir) - c2.catalog_type = CatalogType.RELATIVE_PUBLISHED - check_all_relative(c2) - c2.catalog_type = CatalogType.ABSOLUTE_PUBLISHED - check_all_absolute(c2) - - @pytest.mark.block_network() - def test_self_contained_catalog_collection_item_links(self) -> None: - """See issue https://github.com/stac-utils/pystac/issues/657""" - with tempfile.TemporaryDirectory() as tmp_dir: - catalog = pystac.Catalog( - id="catalog-issue-657", description="catalog-issue-657" - ) - collection = pystac.Collection( - "collection-issue-657", - "collection-issue-657", - pystac.Extent( - spatial=pystac.SpatialExtent([[-180.0, -90.0, 180.0, 90.0]]), - temporal=pystac.TemporalExtent([[datetime(2021, 11, 1), None]]), - ), - license="other", - ) - - item = pystac.Item( - id="item-issue-657", - stac_extensions=[], - geometry=ARBITRARY_GEOM, - bbox=ARBITRARY_BBOX, - datetime=datetime(2021, 11, 1), - properties={}, - ) - - collection.add_item(item) - catalog.add_child(collection) - - catalog.normalize_hrefs(tmp_dir) - catalog.validate_all() - - catalog.save(catalog_type=CatalogType.SELF_CONTAINED) - with open( - f"{tmp_dir}/collection-issue-657/item-issue-657/item-issue-657.json" - ) as f: - item_json = json.load(f) - - for link in item_json["links"]: - # self links are always absolute - if link["rel"] == "self": - continue - - href = link["href"] - assert not is_absolute_href(href), ( - f"Link with rel={link['rel']} is absolute!" - ) - - def test_full_copy_and_normalize_works_with_created_stac(self) -> None: - cat = TestCases.case_3() - cat_copy = cat.full_copy() - cat_copy.normalize_hrefs("http://example.com") - for root, catalogs, items in cat_copy.walk(): - for link in root.links: - if link.rel != "self": - assert link.target is not None - for item in items: - for link in item.links: - if link.rel != "self": - assert link.get_href() is not None - - def test_extra_fields(self) -> None: - catalog = TestCases.case_1() - - catalog.extra_fields["custom_field"] = "Special content" - - with tempfile.TemporaryDirectory() as tmp_dir: - p = os.path.join(tmp_dir, "catalog.json") - catalog.save_object(include_self_link=False, dest_href=p) - with open(p) as f: - cat_json = json.load(f) - assert "custom_field" in cat_json - assert cat_json["custom_field"] == "Special content" - - read_cat = pystac.Catalog.from_file(p) - assert "custom_field" in read_cat.extra_fields - assert read_cat.extra_fields["custom_field"] == "Special content" - - @pytest.mark.parametrize("cat", TestCases.all_test_catalogs()) - @pytest.mark.vcr() - def test_validate_all(self, cat: Catalog) -> None: - # If hrefs are not set, it will fail validation. - if cat.get_self_href() is None: - cat.normalize_hrefs("/tmp") - cat.validate_all() - - @pytest.mark.vcr() - def test_validate_all_invalid(self) -> None: - # Make one invalid, write it off, read it in, ensure it throws - cat = TestCases.case_1() - item = next(cat.get_items("area-1-1-labels", recursive=True)) - item.geometry = {"type": "INVALID", "coordinates": "NONE"} - with tempfile.TemporaryDirectory() as tmp_dir: - cat.normalize_hrefs(tmp_dir) - cat.save(catalog_type=pystac.CatalogType.SELF_CONTAINED) - - cat2 = pystac.Catalog.from_file(os.path.join(tmp_dir, "catalog.json")) - - with pytest.raises(pystac.STACValidationError): - cat2.validate_all() - - def test_set_hrefs_manually(self) -> None: - catalog = TestCases.case_1() - - # Modify the datetimes - year = 2004 - month = 2 - for item in catalog.get_items(recursive=True): - assert item.datetime is not None - item.datetime = item.datetime.replace(year=year, month=month) - year += 1 - month += 1 - - with tempfile.TemporaryDirectory() as tmp_dir: - for root, _, items in catalog.walk(): - # Set root's HREF based off the parent - parent = root.get_parent() - if parent is None: - root_dir = tmp_dir - else: - d = os.path.dirname(parent.self_href) - root_dir = os.path.join(d, root.id) - root_href = os.path.join(root_dir, root.DEFAULT_FILE_NAME) - root.set_self_href(root_href) - - # Set each item's HREF based on it's datetime - for item in items: - assert item.datetime is not None - item_href = "{}/{}-{}/{}.json".format( - root_dir, item.datetime.year, item.datetime.month, item.id - ) - item.set_self_href(item_href) - - catalog.save(catalog_type=CatalogType.SELF_CONTAINED) - - read_catalog = Catalog.from_file(os.path.join(tmp_dir, "catalog.json")) - - for root, _, items in read_catalog.walk(): - parent = root.get_parent() - if parent is None: - assert root.get_self_href() == make_posix_style( - os.path.join(tmp_dir, "catalog.json") - ) - else: - d = os.path.dirname(parent.self_href) - assert root.get_self_href() == make_posix_style( - os.path.join(d, root.id, root.DEFAULT_FILE_NAME) - ) - for item in items: - assert item.datetime is not None - end = posixpath.join( - f"{item.datetime.year}-{item.datetime.month}", - f"{item.id}.json", - ) - self_href = item.get_self_href() - assert self_href is not None - assert self_href.endswith(end), "{} does not end with {}".format( - self_href, end - ) - - @pytest.mark.parametrize("cat", TestCases.all_test_catalogs()) - def test_collections_cache_correctly(self, cat: Catalog) -> None: - mock_io = MockStacIO() - cat._stac_io = mock_io - expected_collection_reads = set() - for root, _, items in cat.walk(): - if isinstance(root, Collection) and root != cat: - expected_collection_reads.add(root.get_self_href()) - - # Iterate over items to make sure they are read - assert list(items) is not None - - call_uris: list[Any] = [ - call[0][0] - for call in mock_io.mock.read_text.call_args_list - if call[0][0] in expected_collection_reads - ] - - for collection_uri in expected_collection_reads: - calls = len([x for x in call_uris if x == collection_uri]) - assert calls == 1, "{} was read {} times instead of once!".format( - collection_uri, calls - ) - - def test_reading_iterating_and_writing_works_as_expected(self) -> None: - """Test case to cover issue #88""" - stac_uri = TestCases.get_path("data-files/catalogs/test-case-6/catalog.json") - cat = Catalog.from_file(stac_uri) - - # Iterate over the items. This was causing failure in - # the later iterations as per issue #88 - for item in cat.get_items(recursive=True): - pass - - with tempfile.TemporaryDirectory() as tmp_dir: - new_stac_uri = os.path.join(tmp_dir, "test-case-6") - cat.normalize_hrefs(new_stac_uri) - cat.save(catalog_type=CatalogType.SELF_CONTAINED) - - # Open the local copy and iterate over it. - cat2 = Catalog.from_file(os.path.join(new_stac_uri, "catalog.json")) - - for item in cat2.get_items(recursive=True): - # Iterate again over the items. This would fail in #88 - pass - - def test_get_children_cbers(self) -> None: - cat = TestCases.case_6() - assert len(list(cat.get_children())) == 4 - - def test_resolve_planet(self) -> None: - """Test against a bug that caused infinite recursion during link resolution""" - cat = TestCases.case_8() - for root, _, items in cat.walk(): - for item in items: - item.resolve_links() - root.resolve_links() - - def test_handles_children_with_same_id(self) -> None: - # This catalog has the root and child collection share an ID. - cat = pystac.Catalog.from_file( - TestCases.get_path("data-files/invalid/shared-id/catalog.json") - ) - items = list(cat.get_items(recursive=True)) - - assert len(items) == 1 - - def test_catalog_with_href_caches_by_href(self) -> None: - cat = TestCases.case_1() - cache = cat._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 - - def test_from_invalid_dict_raises_exception(self) -> None: - stac_io = pystac.StacIO.default() - collection_dict = stac_io.read_json( - TestCases.get_path("data-files/collections/multi-extent.json") - ) - with pytest.raises(pystac.STACTypeError): - _ = pystac.Catalog.from_dict(collection_dict) - - def test_get_collections(self) -> None: - catalog = TestCases.case_2() - collections = list(catalog.get_collections()) - - assert len(collections) > 0 - assert all(isinstance(c, pystac.Collection) for c in collections) - - def test_get_all_collections(self) -> None: - catalog = TestCases.case_1() - all_collections = list(catalog.get_all_collections()) - - assert len(all_collections) == 4 - assert all(isinstance(c, pystac.Collection) for c in all_collections) - - def test_get_single_links_media_type(self) -> None: - catalog = TestCases.case_1() - - catalog.links.append( - pystac.Link(rel="search", target="./search.html", media_type="text/html") - ) - catalog.links.append( - pystac.Link( - rel="search", target="./search.json", media_type="application/geo+json" - ) - ) - - html_link = catalog.get_single_link(rel="search") - assert html_link is not None - assert html_link.href == "./search.html" - html_link = catalog.get_single_link(media_type="text/html") - assert html_link is not None - assert html_link.href == "./search.html" - json_link = catalog.get_single_link( - rel="search", media_type="application/geo+json" - ) - assert json_link is not None - assert json_link.href == "./search.json" - no_link = catalog.get_single_link(rel="via") - assert no_link is None - first_link = catalog.get_single_link() - assert first_link is not None - assert first_link.rel == "self" - - def test_get_links(self) -> None: - catalog = TestCases.case_1() - - catalog.links.append( - pystac.Link(rel="search", target="./search.html", media_type="text/html") - ) - catalog.links.append( - pystac.Link( - rel="search", target="./search.json", media_type="application/geo+json" - ) - ) - assert ( - len(catalog.get_links(rel="search", media_type="application/geo+json")) == 1 - ) - assert len(catalog.get_links(media_type="text/html")) == 1 - assert ( - len(catalog.get_links(media_type=["text/html", "application/geo+json"])) - == 2 - ) - assert len(catalog.get_links(rel="search")) == 2 - assert len(catalog.get_links(rel="via")) == 0 - assert len(catalog.get_links()) == 6 - - def test_to_dict_no_self_href(self) -> None: - catalog = Catalog(id="an-id", description="A test Catalog") - d = catalog.to_dict(include_self_link=False) - Catalog.from_dict(d) - - -class TestFullCopy: - def check_link(self, link: pystac.Link, tag: str) -> None: - if link.is_resolved(): - target_href: str = cast(pystac.STACObject, link.target).self_href - else: - target_href = str(link.target) - assert tag in target_href, '[{}] {} does not contain "{}"'.format( - link.rel, target_href, tag - ) - - def check_item(self, item: Item, tag: str) -> None: - for link in item.links: - self.check_link(link, tag) - - def check_catalog(self, c: Catalog, tag: str) -> None: - assert len(c.get_links("root")) == 1, f"Failure for catalog: {c}" - - for link in c.links: - self.check_link(link, tag) - - for child in c.get_children(): - self.check_catalog(child, tag) - - for item in c.get_items(): - self.check_item(item, tag) - - def test_full_copy_1(self) -> None: - with tempfile.TemporaryDirectory() as tmp_dir: - cat = Catalog(id="test", description="test catalog") - - item = Item( - id="test_item", - geometry=ARBITRARY_GEOM, - bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), - properties={}, - ) - - cat.add_item(item) - - cat.normalize_hrefs(os.path.join(tmp_dir, "catalog-full-copy-1-source")) - cat2 = cat.full_copy() - cat2.normalize_hrefs(os.path.join(tmp_dir, "catalog-full-copy-1-dest")) - - self.check_catalog(cat, "source") - self.check_catalog(cat2, "dest") - - def test_full_copy_2(self) -> None: - with tempfile.TemporaryDirectory() as tmp_dir: - cat = Catalog(id="test", description="test catalog") - image_item = Item( - id="Imagery", - geometry=ARBITRARY_GEOM, - bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), - properties={}, - ) - for key in ["ortho", "dsm"]: - image_item.add_asset( - key, - Asset(href=f"some/{key}.tif", media_type=MediaType.GEOTIFF), - ) - - label_item = Item( - id="Labels", - geometry=ARBITRARY_GEOM, - bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), - properties={}, - ) - cat.add_items([image_item, label_item]) - - cat.normalize_hrefs(os.path.join(tmp_dir, "catalog-full-copy-2-source")) - cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) - cat2 = cat.full_copy() - cat2.normalize_hrefs(os.path.join(tmp_dir, "catalog-full-copy-2-dest")) - cat2.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) - - self.check_catalog(cat, "source") - self.check_catalog(cat2, "dest") - - def test_full_copy_3(self) -> None: - with tempfile.TemporaryDirectory() as tmp_dir: - root_cat = TestCases.case_1() - root_cat.normalize_hrefs( - os.path.join(tmp_dir, "catalog-full-copy-3-source") - ) - root_cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) - cat2 = root_cat.full_copy() - cat2.normalize_hrefs(os.path.join(tmp_dir, "catalog-full-copy-3-dest")) - cat2.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) - - self.check_catalog(root_cat, "source") - self.check_catalog(cat2, "dest") - - def test_full_copy_4(self) -> None: - with tempfile.TemporaryDirectory() as tmp_dir: - root_cat = TestCases.case_2() - root_cat.normalize_hrefs( - os.path.join(tmp_dir, "catalog-full-copy-4-source") - ) - root_cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) - cat2 = root_cat.full_copy() - cat2.normalize_hrefs(os.path.join(tmp_dir, "catalog-full-copy-4-dest")) - cat2.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) - - self.check_catalog(root_cat, "source") - self.check_catalog(cat2, "dest") - - # Check that the relative asset link was saved correctly in the copy. - ID = "cf73ec1a-d790-4b59-b077-e101738571ed" - item = next(cat2.get_items(ID, recursive=True)) - href = item.assets[ID].get_absolute_href() - assert href is not None - assert os.path.exists(href) - - -class TestCatalogSubClass: - """This tests cases related to creating classes inheriting from pystac.Catalog to - ensure that inheritance, class methods, etc. function as expected.""" - - case_1 = TestCases.get_path("data-files/catalogs/test-case-1/catalog.json") - - class BasicCustomCatalog(pystac.Catalog): - def get_items(self) -> Iterator[Item]: # type: ignore - # This get_items does not have the `recursive` kwarg. This mimics - # the current state of pystac-client and is intended to test - # backwards compatibility of inherited classes - return super().get_items() - - def test_from_dict_returns_subclass(self) -> None: - self.stac_io = pystac.StacIO.default() - catalog_dict = self.stac_io.read_json(self.case_1) - custom_catalog = self.BasicCustomCatalog.from_dict(catalog_dict) - assert isinstance(custom_catalog, self.BasicCustomCatalog) - - def test_from_file_returns_subclass(self) -> None: - custom_catalog = self.BasicCustomCatalog.from_file(self.case_1) - assert isinstance(custom_catalog, self.BasicCustomCatalog) - - def test_clone(self) -> None: - custom_catalog = self.BasicCustomCatalog.from_file(self.case_1) - cloned_catalog = custom_catalog.clone() - assert isinstance(cloned_catalog, self.BasicCustomCatalog) - - def test_get_all_items_works(self) -> None: - custom_catalog = self.BasicCustomCatalog.from_file(self.case_1) - cloned_catalog = custom_catalog.clone() - with pytest.warns(DeprecationWarning): - cloned_catalog.get_all_items() - - def test_get_item_works(self) -> None: - custom_catalog = self.BasicCustomCatalog.from_file(self.case_1) - cloned_catalog = custom_catalog.clone() - with pytest.warns(DeprecationWarning): - cloned_catalog.get_item("area-1-1-imagery") - - -def test_custom_catalog_from_dict(catalog: Catalog) -> None: - # https://github.com/stac-utils/pystac/issues/862 - class CustomCatalog(Catalog): - @classmethod - def from_dict( - cls, - d: dict[str, Any], - href: str | None = None, - root: Catalog | None = None, - migrate: bool = True, - preserve_dict: bool = True, - ) -> CustomCatalog: - return super().from_dict(d) - - _ = CustomCatalog.from_dict(catalog.to_dict()) - - -@pytest.mark.parametrize("add_canonical", (True, False)) -def test_remove_hierarchical_links( - test_case_1_catalog: Catalog, add_canonical: bool -) -> None: - test_case_1_catalog.remove_hierarchical_links(add_canonical=add_canonical) - for link in test_case_1_catalog.links: - assert not link.is_hierarchical() - assert bool(test_case_1_catalog.get_single_link("canonical")) == add_canonical - - -def test_fully_resolve(tmp_path: Path, test_case_1_catalog: Catalog) -> None: - test_case_1_catalog.save(dest_href=str(tmp_path / "before")) - assert len(list((tmp_path / "before").glob("**/*.json"))) == 1 - test_case_1_catalog.fully_resolve() - test_case_1_catalog.save(dest_href=str(tmp_path / "after")) - assert len(list((tmp_path / "after").glob("**/*.json"))) == 15 - - -def test_get_items_with_multiple_ids(test_case_1_catalog: Catalog) -> None: - cat = test_case_1_catalog - items = cat.get_items("area-2-1-imagery", "area-1-1-labels", recursive=True) - assert len(list(items)) == 2 - - -@pytest.mark.vcr() -def test_validate_all_with_max_n(test_case_1_catalog: Catalog) -> None: - cat = test_case_1_catalog - assert cat.validate_all() == 8 - assert cat.validate_all(max_items=6) == 6 - assert cat.validate_all(max_items=1) == 1 - - -@pytest.mark.vcr() -def test_validate_all_with_recusive_off(test_case_1_catalog: Catalog) -> None: - cat = test_case_1_catalog - assert cat.validate_all() == 8 - assert cat.validate_all(recursive=False) == 0 - - -@pytest.fixture -def nested_catalog() -> pystac.Catalog: - """ - Structure: - - ├── catalog.json - ├── products - │ ├── catalog.json - │ └── product_a - │ └── catalog.json - └── variables - ├── catalog.json - └── variable_a - └── catalog.json - └── variable_a_1 - └── collection.json - """ - root = pystac.Catalog("root", "root") - variables = pystac.Catalog("variables", "variables") - products = pystac.Catalog("products", "products") - - root.add_child(variables) - root.add_child(products) - - variable_a = pystac.Catalog("variable_a", "variable_a") - product_a = pystac.Catalog("product_a", "product_a") - - variables.add_child(variable_a) - products.add_child(product_a) - - variable_a_1 = pystac.Collection( - "variable_a_1", "variable_a_1", extent=ARBITRARY_EXTENT - ) - variable_a.add_child(variable_a_1) - - return root - - -def test_get_all_collections_deeply_nested(nested_catalog: pystac.Catalog) -> None: - catalog = nested_catalog - all_collections = list(catalog.get_all_collections()) - - assert len(all_collections) == 1 - assert all(isinstance(c, pystac.Collection) for c in all_collections) - - -def test_set_parent_false_stores_in_proper_place_on_normalize_and_save( - nested_catalog: pystac.Catalog, tmp_path: Path -) -> None: - root = nested_catalog - product_a = next(root.get_child("products").get_children()) # type: ignore - variable_a = next(root.get_child("variables").get_children()) # type: ignore - - variable_a.add_child(product_a, set_parent=False) - - root.normalize_and_save( - root_href=str(tmp_path), catalog_type=pystac.CatalogType.ABSOLUTE_PUBLISHED - ) - assert (tmp_path / "products" / "product_a").exists() - assert not (tmp_path / "variables" / "variable_a" / "product_a").exists() - - -def test_set_parent_false_stores_in_proper_place_on_save( - nested_catalog: pystac.Catalog, tmp_path: Path -) -> None: - nested_catalog.normalize_and_save( - root_href=str(tmp_path), catalog_type=pystac.CatalogType.ABSOLUTE_PUBLISHED - ) - root = pystac.Catalog.from_file(tmp_path / "catalog.json") - product_a = next(root.get_child("products").get_children()) # type: ignore - variable_a = next(root.get_child("variables").get_children()) # type: ignore - - variable_a.add_child(product_a, set_parent=False) - - root.save(pystac.CatalogType.SELF_CONTAINED, dest_href=str(tmp_path)) - - assert (tmp_path / "products" / "product_a").exists() - assert not (tmp_path / "variables" / "variable_a" / "product_a").exists() - - -BEST_PRACTICE_CATALOG_TEMPLATE = "{id}" -BEST_PRACTICE_ITEM_TEMPLATE = "{id}" -TEST_CATALOG_TEMPLATE = "cat/${id}/${description}" -TEST_ITEM_TEMPLATE = "cat/items/${id}" -STRATEGY = TemplateLayoutStrategy( - catalog_template=TEST_CATALOG_TEMPLATE, item_template=TEST_ITEM_TEMPLATE -) - - -@pytest.mark.parametrize( - "root_strategy,sub_strategy,provided_root_strategy,provided_sub_strategy,root_template,sub_template", - [ - ( - None, - None, - None, - None, - BEST_PRACTICE_CATALOG_TEMPLATE, - BEST_PRACTICE_CATALOG_TEMPLATE, - ), - (STRATEGY, None, None, None, TEST_CATALOG_TEMPLATE, TEST_CATALOG_TEMPLATE), - ( - STRATEGY, - BestPracticesLayoutStrategy(), - None, - None, - TEST_CATALOG_TEMPLATE, - BEST_PRACTICE_CATALOG_TEMPLATE, - ), - ( - STRATEGY, - None, - BestPracticesLayoutStrategy(), - None, - BEST_PRACTICE_CATALOG_TEMPLATE, - TEST_CATALOG_TEMPLATE, - ), - ( - STRATEGY, - None, - None, - BestPracticesLayoutStrategy(), - TEST_CATALOG_TEMPLATE, - BEST_PRACTICE_CATALOG_TEMPLATE, - ), - ], -) -def test_add_child_layout_strategy( - root_strategy: HrefLayoutStrategy, - sub_strategy: HrefLayoutStrategy, - provided_root_strategy: HrefLayoutStrategy, - provided_sub_strategy: HrefLayoutStrategy, - root_template: str, - sub_template: str, -) -> None: - """Test for layout strategy when adding a child. - - If no layout strategy is specified, children HREF should - always follow BestPracticesLayoutStrategy. - If only root strategy is set, all children HREFs should - follow than strategy. - If root and child strategy are set, root and child children - may follow different strategies. - Strategy provided to `add_child` always overrides other settings. - """ - - base_url = "http://example.com" - catalog = Catalog( - id="test", - description="test desc", - href=f"{base_url}/catalog.json", - strategy=root_strategy, - ) - subcat = Catalog(id="subcat", description="subcat desc", strategy=sub_strategy) - subsubcat = Catalog(id="subsubcat", description="subsubcat desc") - - catalog.add_child(subcat, strategy=provided_root_strategy) - subcat.add_child(subsubcat, strategy=provided_sub_strategy) - - root_template = root_template.format( - id=subcat.id, description=subcat.description - ).replace("$", "") - sub_template = sub_template.format( - id=subsubcat.id, description=subsubcat.description - ).replace("$", "") - - assert subcat.self_href == f"{base_url}/{root_template}/catalog.json" - assert ( - subsubcat.self_href == f"{base_url}/{root_template}/{sub_template}/catalog.json" - ) - - -@pytest.mark.parametrize( - "root_strategy,sub_strategy,provided_root_strategy," - "root_template,sub_template,norm_template", - [ - ( - None, - None, - None, - BEST_PRACTICE_CATALOG_TEMPLATE, - BEST_PRACTICE_CATALOG_TEMPLATE, - BEST_PRACTICE_CATALOG_TEMPLATE, - ), - ( - STRATEGY, - None, - None, - TEST_CATALOG_TEMPLATE, - TEST_CATALOG_TEMPLATE, - TEST_CATALOG_TEMPLATE, - ), - ( - STRATEGY, - BestPracticesLayoutStrategy(), - None, - TEST_CATALOG_TEMPLATE, - BEST_PRACTICE_CATALOG_TEMPLATE, - TEST_CATALOG_TEMPLATE, - ), - ( - STRATEGY, - None, - BestPracticesLayoutStrategy(), - TEST_CATALOG_TEMPLATE, - TEST_CATALOG_TEMPLATE, - BEST_PRACTICE_CATALOG_TEMPLATE, - ), - ], -) -def test_add_child_layout_strategy_normalize( - root_strategy: HrefLayoutStrategy, - sub_strategy: HrefLayoutStrategy, - provided_root_strategy: HrefLayoutStrategy, - root_template: str, - sub_template: str, - norm_template: str, -) -> None: - """Test for layout strategy when adding a child and normalizing HREFs. - - If no layout strategy is specified, children HREF - should always follow BestPracticesLayoutStrategy. - If only root strategy is set, all children HREFs - should follow than strategy. - If root and child strategy are set, root strategy - overrides child strategy. - Strategy provided to `normalize_href` always - overrides other settings. - """ - - base_url = "http://example.com" - catalog = Catalog( - id="test", - description="test desc", - href=f"{base_url}/catalog.json", - strategy=root_strategy, - ) - subcat = Catalog(id="subcat", description="subcat desc", strategy=sub_strategy) - subsubcat = Catalog(id="subsubcat", description="subsubcat desc") - catalog.add_child(subcat) - subcat.add_child(subsubcat) - - _root_template = root_template.format( - id=subcat.id, description=subcat.description - ).replace("$", "") - _sub_template = sub_template.format( - id=subsubcat.id, description=subsubcat.description - ).replace("$", "") - - assert subcat.self_href == f"{base_url}/{_root_template}/catalog.json" - assert ( - subsubcat.self_href - == f"{base_url}/{_root_template}/{_sub_template}/catalog.json" - ) - - catalog.normalize_hrefs(base_url, strategy=provided_root_strategy) - - _root_template = norm_template.format( - id=subcat.id, description=subcat.description - ).replace("$", "") - _sub_template = norm_template.format( - id=subsubcat.id, description=subsubcat.description - ).replace("$", "") - - assert subcat.self_href == f"{base_url}/{_root_template}/catalog.json" - assert ( - subsubcat.self_href - == f"{base_url}/{_root_template}/{_sub_template}/catalog.json" - ) - - -@pytest.mark.parametrize( - "root_strategy,provided_strategy,template", - [ - (None, None, BEST_PRACTICE_ITEM_TEMPLATE), - (STRATEGY, None, TEST_ITEM_TEMPLATE), - (STRATEGY, BestPracticesLayoutStrategy(), BEST_PRACTICE_ITEM_TEMPLATE), - ], -) -def test_add_item_layout_strategy( - root_strategy: HrefLayoutStrategy, - provided_strategy: HrefLayoutStrategy, - template: str, -) -> None: - """Test for layout strategy when adding an item. - - If no layout strategy is specified, item HREF - should always follow BestPracticesLayoutStrategy. - If only root strategy is set, all item HREFs - should follow than strategy. - Strategy provided to `add_item` always overrides other settings. - """ - - base_url = "http://example.com" - item_id = "item_id" - catalog = Catalog( - id="test", - description="test desc", - href=f"{base_url}/catalog.json", - strategy=root_strategy, - ) - item = Item( - id=item_id, - geometry={ - "type": "Polygon", - "coordinates": [ - [ - [180.0, -90.0], - [180.0, 90.0], - [-180.0, 90.0], - [-180.0, -90.0], - [180.0, -90.0], - ] - ], - }, - bbox=[-180, -90, 180, 90], - datetime=datetime(2023, 1, 1), - properties={}, - assets={ - "data": Asset( - href="http://example.com/assets/data.tif", - roles=["data"], - title="DATA", - ) - }, - ) - - catalog.add_item(item, strategy=provided_strategy) - - template = template.format(id=item.id).replace("$", "") - - assert item.self_href == f"{base_url}/{template}/{item_id}.json" - - -def test_APILayoutStrategy_requires_root_to_be_url( - catalog: Catalog, collection: Collection, item: Item -) -> None: - collection.add_item(item) - catalog.add_child(collection) - with pytest.raises( - pystac.errors.STACError, - match="When using APILayoutStrategy the root_href must be a URL", - ): - catalog.normalize_hrefs(root_href="issues-1486", strategy=APILayoutStrategy()) - - -def test_get_child_links_cares_about_media_type(catalog: pystac.Catalog) -> None: - catalog.links.extend( - [ - pystac.Link( - rel="child", target="./child-1.json", media_type="application/json" - ), - pystac.Link( - rel="child", target="./child-2.json", media_type="application/geo+json" - ), - pystac.Link(rel="child", target="./child-3.json"), - # this one won't get counted since it's the wrong media_type - pystac.Link(rel="child", target="./child.html", media_type="text/html"), - ] - ) - - assert len(catalog.get_child_links()) == 3 - - -def test_get_item_links_cares_about_media_type(catalog: pystac.Catalog) -> None: - catalog.links.extend( - [ - pystac.Link( - rel="item", target="./item-1.json", media_type="application/json" - ), - pystac.Link( - rel="item", target="./item-2.json", media_type="application/geo+json" - ), - pystac.Link(rel="item", target="./item-3.json"), - # this one won't get counted since it's the wrong media_type - pystac.Link(rel="item", target="./item.html", media_type="text/html"), - ] - ) - - assert len(catalog.get_item_links()) == 3 - -def test_get_root_link_cares_about_media_type(catalog: pystac.Catalog) -> None: - catalog.links.insert( - 0, pystac.Link(rel="root", target="./self.json", media_type="text/html") - ) - root_link = catalog.get_root_link() - assert root_link and root_link.target != "./self.json" +from pystac import Catalog -def test_clone_extra_fields(catalog: Catalog) -> None: - catalog.extra_fields["foo"] = "bar" - cloned = catalog.clone() - assert cloned.extra_fields["foo"] == "bar" +def test_catalog() -> None: + catalog = Catalog(id="an-id", description="a description") + catalog.validate() -def test_warn_if_next_link_present(catalog: Catalog) -> None: - catalog.links.append(Link(rel="next", target="./next.json")) - with pytest.warns(UserWarning): - _ = list(catalog.get_children()) +def test_catalog_from_dict(examples_path: Path) -> None: + with open(examples_path / "catalog.json") as f: + data = json.load(f) + catalog = Catalog.from_dict(data) + catalog.validate() + assert catalog.to_dict() == data diff --git a/tests/test_collection.py b/tests/test_collection.py index d44f82f57..bc9f653eb 100644 --- a/tests/test_collection.py +++ b/tests/test_collection.py @@ -1,844 +1,20 @@ -from __future__ import annotations - import json -import os -import tempfile -from collections.abc import Iterator -from copy import deepcopy -from datetime import datetime -from typing import Any +from pathlib import Path import pytest -from dateutil import tz - -import pystac -from pystac import ( - Asset, - Catalog, - CatalogType, - Collection, - Extent, - Item, - ItemCollection, - Provider, - SpatialExtent, - TemporalExtent, -) -from pystac.extensions.eo import EOExtension -from pystac.utils import datetime_to_str, get_required, str_to_datetime -from pystac.validation import validate_dict -from tests.utils import ARBITRARY_BBOX, ARBITRARY_GEOM, TestCases - -TEST_DATETIME = datetime(2020, 3, 14, 16, 32) - - -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 +from pystac import Collection - # Since all of our STAC objects have HREFs, everything should be - # cached only by HREF - assert len(cache.id_keys_to_objects) == 0 - -@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) +def test_collection() -> None: + collection = Collection(id="an-id", description="a-description") 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" - ) - ) - instruments_schema = get_required( - collection.summaries.get_schema("instruments"), - collection.summaries, - "instruments", - ) - - assert isinstance(instruments_schema, dict) - - -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) - - -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() - ) - - cloned_collection = original_collection.clone() - - 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}" - ) - - -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]]) - - -@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) - - assert temporal_extent.intervals == [interval] - - -@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] - - -@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 - - -@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") - +@pytest.mark.vcr +def test_collection_from_dict(examples_path: Path) -> None: + with open(examples_path / "collection.json") as f: + data = json.load(f) + collection = Collection.from_dict(data) collection.validate() - - -@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) - ), - }, - ) - - 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 - - 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.""" - - MULTI_EXTENT = TestCases.get_path("data-files/collections/multi-extent.json") - - class BasicCustomCollection(pystac.Collection): - def get_items(self) -> Iterator[Item]: # type: ignore - # This get_items does not have the `recursive` kwarg. This mimics - # the current state of pystac-client and is intended to test - # backwards compatibility of inherited classes - return super().get_items() - - def test_from_dict_returns_subclass(self) -> None: - stac_io = pystac.StacIO.default() - collection_dict = stac_io.read_json(self.MULTI_EXTENT) - custom_collection = self.BasicCustomCollection.from_dict(collection_dict) - - assert isinstance(custom_collection, self.BasicCustomCollection) - - def test_from_file_returns_subclass(self) -> None: - custom_collection = self.BasicCustomCollection.from_file(self.MULTI_EXTENT) - - 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() - - assert isinstance(cloned_collection, self.BasicCustomCollection) - - def test_collection_get_item_works(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.warns(DeprecationWarning): - collection.get_item("area-1-1-imagery") - - -def test_collection_get_item_raises_type_error() -> None: - class BasicCustomCollection(pystac.Collection): - def get_items( # type: ignore - self, *, recursive: bool = False - ) -> Iterator[Item]: - # This get_items does not allow ids as args. - return super().get_items(recursive=recursive) - - 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: - # https://github.com/stac-utils/pystac/issues/862 - class CustomCollection(Collection): - @classmethod - def from_dict( - cls, - d: dict[str, Any], - href: str | None = None, - root: Catalog | None = None, - migrate: bool = True, - preserve_dict: bool = True, - ) -> CustomCollection: - return super().from_dict(d) - - _ = CustomCollection.from_dict(collection.to_dict()) - - -@pytest.mark.parametrize("add_canonical", (True, False)) -def test_remove_hierarchical_links( - test_case_1_catalog: Catalog, add_canonical: bool -) -> None: - collection = list(test_case_1_catalog.get_all_collections())[0] - collection.remove_hierarchical_links(add_canonical=add_canonical) - for link in collection.links: - assert not link.is_hierarchical() - assert bool(collection.get_single_link("canonical")) == add_canonical - - -@pytest.mark.parametrize("child", ["country-1", "country-2"]) -def test_get_child_checks_links_where_hrefs_contains_id_first( - test_case_1_catalog: Catalog, child: str -) -> None: - cat1 = test_case_1_catalog - country = cat1.get_child(child) - assert country is not None - child_links = [link for link in cat1.links if link.rel == pystac.RelType.CHILD] - for link in child_links: - if country.id not in link.href: - assert not link.is_resolved() - - -def test_get_child_sort_links_by_id_is_configurable( - test_case_1_catalog: Catalog, -) -> None: - cat1 = test_case_1_catalog - country = cat1.get_child("country-2", sort_links_by_id=False) - assert country is not None - child_links = [link for link in cat1.links if link.rel == pystac.RelType.CHILD] - for link in child_links: - assert link.is_resolved() - - -def test_get_item_returns_none_if_not_found( - test_case_8_collection: Collection, -) -> None: - col8 = test_case_8_collection - item = col8.get_item("notarealitem") - assert item is None - - -def test_get_item_is_not_recursive_by_default( - test_case_8_collection: Collection, -) -> None: - col8 = test_case_8_collection - item = col8.get_item("20170831_162740_ssc1d1") - assert item is None - - item = col8.get_item("20170831_162740_ssc1d1", recursive=True) - assert item is not None - - -def test_delete_asset(tmp_asset: Asset, collection: Collection) -> None: - asset = tmp_asset - href = asset.get_absolute_href() - item = asset.owner - name = "foo" - - assert href is not None - assert item is not None - - collection.add_asset(name, asset) - - # steal the href from the owner and use it as the collection's - collection.set_self_href(item.get_self_href()) - - collection.delete_asset(name) - assert name not in collection.assets - assert not os.path.exists(href) - - -def test_delete_asset_relative_no_self_link_fails( - tmp_asset: Asset, collection: Collection -) -> None: - asset = tmp_asset - href = asset.get_absolute_href() - name = "foo" - - assert href is not None - - collection.add_asset(name, asset) - - with pytest.raises(ValueError, match="Cannot delete file") as e: - collection.delete_asset(name) - - assert asset.href in str(e.value) - assert name in collection.assets - assert os.path.exists(href) - - -def test_permissive_temporal_extent_deserialization(collection: Collection) -> None: - # https://github.com/stac-utils/pystac/issues/1221 - collection_dict = collection.to_dict(include_self_link=False, transform_hrefs=False) - collection_dict["extent"]["temporal"]["interval"] = collection_dict["extent"][ - "temporal" - ]["interval"][0] - with pytest.warns(UserWarning): - Collection.from_dict(collection_dict) - - -@pytest.mark.parametrize("fixture_name", ("sample_item_collection", "sample_items")) -def test_from_items(fixture_name: str, request: pytest.FixtureRequest) -> None: - items = request.getfixturevalue(fixture_name) - collection = Collection.from_items(items) - - for item in items: - assert collection.id == item.collection_id - assert collection.extent.spatial.bboxes[0][0] <= item.bbox[0] - assert collection.extent.spatial.bboxes[0][1] <= item.bbox[1] - assert collection.extent.spatial.bboxes[0][2] >= item.bbox[2] - assert collection.extent.spatial.bboxes[0][3] >= item.bbox[3] - - start = collection.extent.temporal.intervals[0][0] - end = collection.extent.temporal.intervals[0][1] - assert start and start <= str_to_datetime(item.properties["start_datetime"]) - assert end and end >= str_to_datetime(item.properties["end_datetime"]) - - if isinstance(items, ItemCollection): - expected = {(link["rel"], link["href"]) for link in items.extra_fields["links"]} - actual = {(link.rel, link.href) for link in collection.links} - assert expected.issubset(actual) - - -def test_from_items_pulls_from_properties() -> 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), - collection="test-collection-1", - properties={"title": "Test Item", "description": "Extra words describing"}, - ) - collection = Collection.from_items([item1]) - assert collection.id == item1.collection_id - assert collection.title == item1.properties["title"] - assert collection.description == item1.properties["description"] - - -def test_from_items_without_collection_id() -> 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={}, - ) - with pytest.raises(ValueError, match="Collection id must be defined."): - Collection.from_items([item1]) - - collection = Collection.from_items([item1], id="test-collection") - assert collection.id == "test-collection" - - -def test_from_items_with_collection_ids() -> 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), - collection="test-collection-1", - properties={}, - ) - item2 = Item( - id="test-item-2", - geometry=ARBITRARY_GEOM, - bbox=[-15, -20, 0, -10], - datetime=datetime(2000, 2, 1, 13, 0, 0, 0, tzinfo=tz.UTC), - collection="test-collection-2", - properties={}, - ) - - with pytest.raises(ValueError, match="Collection id must be defined."): - Collection.from_items([item1, item2]) - - collection = Collection.from_items([item1, item2], id="test-collection") - assert collection.id == "test-collection" - - -def test_from_items_with_different_values() -> 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={"title": "Test Item 1"}, - ) - item2 = Item( - id="test-item-2", - geometry=ARBITRARY_GEOM, - bbox=[-15, -20, 0, -10], - datetime=datetime(2000, 2, 1, 13, 0, 0, 0, tzinfo=tz.UTC), - properties={"title": "Test Item 2"}, - ) - - collection = Collection.from_items([item1, item2], id="test_collection") - assert collection.title is None - - -def test_from_items_with_providers(sample_item_collection: ItemCollection) -> None: - sample_item_collection.extra_fields["providers"] = [{"name": "pystac"}] - - collection = Collection.from_items(sample_item_collection) - assert collection.providers and len(collection.providers) == 1 - - provider = collection.providers[0] - assert provider and provider.name == "pystac" - - -def test_from_dict_null_extent(collection: Collection) -> None: - # https://github.com/stac-utils/pystac/issues/1558 - # https://github.com/EOPF-Sample-Service/eopf-stac/issues/18 - d = collection.to_dict() - d["extent"] = None - with pytest.warns(UserWarning): - c = Collection.from_dict(d) - - assert c.extent.spatial.to_dict()["bbox"] == [[-90, -180, 90, 180]] - assert c.extent.temporal.to_dict()["interval"] == [[None, None]] - - -def test_from_dict_missing_extent(collection: Collection) -> None: - d = collection.to_dict() - del d["extent"] - with pytest.warns(UserWarning): - c = Collection.from_dict(d) - - assert c.extent.spatial.to_dict()["bbox"] == [[-90, -180, 90, 180]] - assert c.extent.temporal.to_dict()["interval"] == [[None, None]] + assert collection.to_dict() == data diff --git a/tests/test_common_metadata.py b/tests/test_common_metadata.py index 166e530b2..c4a9a40ca 100644 --- a/tests/test_common_metadata.py +++ b/tests/test_common_metadata.py @@ -1,562 +1,69 @@ -from datetime import datetime -from typing import Any +from typing import Any, final import pytest -from pystac import CommonMetadata, Item, Provider, ProviderRole, utils -from tests.utils import TestCases +from pystac import Asset +from pystac.common_metadata import DataValue -@pytest.fixture -def date_time_range_item() -> Item: - return Item.from_file( - TestCases.get_path( - "data-files/examples/1.0.0-beta.2/item-spec/examples/datetimerange.json" - ) - ) +@final +class Common(DataValue): + def __init__(self, **kwargs: Any): + self.extra_fields = kwargs -@pytest.fixture -def sample_full_item() -> Item: - return Item.from_file( - TestCases.get_path( - "data-files/examples/1.0.0-beta.2/item-spec/examples/sample-full.json" - ) - ) +def test_data_type_set_to_invalid_value() -> None: + common = Common() + with pytest.raises(ValueError, match="'bad' is not a valid DataType"): + common.data_type = "bad" -def test_datetimes(date_time_range_item: Item) -> None: - # save dict of original item to check that `common_metadata` - # method doesn't mutate self.item_1 - before = date_time_range_item.clone().to_dict() - start_datetime_str = date_time_range_item.properties["start_datetime"] - assert isinstance(start_datetime_str, str) +def test_data_type_repr() -> None: + common = Common() + common.data_type = "int8" + assert common.data_type.__repr__() == "int8" - cm = date_time_range_item.common_metadata - assert isinstance(cm, CommonMetadata) - assert isinstance(cm.start_datetime, datetime) - assert before == date_time_range_item.to_dict() - assert cm.providers is None +def test_setting_statistics_directly_raises() -> None: + common = Common() + with pytest.raises(AttributeError): + common.statistics = {} # pyright: ignore [reportAttributeAccessIssue] -def test_common_metadata_start_datetime(date_time_range_item: Item) -> None: - x = date_time_range_item.clone() - start_datetime_str = "2018-01-01T13:21:30Z" - start_datetime_dt = utils.str_to_datetime(start_datetime_str) - example_datetime_str = "2020-01-01T00:00:00Z" - example_datetime_dt = utils.str_to_datetime(example_datetime_str) - assert x.common_metadata.start_datetime == start_datetime_dt - assert x.properties["start_datetime"] == start_datetime_str - - x.common_metadata.start_datetime = example_datetime_dt - - assert x.common_metadata.start_datetime == example_datetime_dt - assert x.properties["start_datetime"] == example_datetime_str - - -def test_common_metadata_end_datetime(date_time_range_item: Item) -> None: - x = date_time_range_item.clone() - end_datetime_str = "2018-01-01T13:31:30Z" - end_datetime_dt = utils.str_to_datetime(end_datetime_str) - example_datetime_str = "2020-01-01T00:00:00Z" - example_datetime_dt = utils.str_to_datetime(example_datetime_str) - - assert x.common_metadata.end_datetime == end_datetime_dt - assert x.properties["end_datetime"] == end_datetime_str - - x.common_metadata.end_datetime = example_datetime_dt - - assert x.common_metadata.end_datetime == example_datetime_dt - assert x.properties["end_datetime"] == example_datetime_str - - -def test_common_metadata_created(sample_full_item: Item) -> None: - x = sample_full_item.clone() - created_str = "2016-05-04T00:00:01Z" - created_dt = utils.str_to_datetime(created_str) - example_datetime_str = "2020-01-01T00:00:00Z" - example_datetime_dt = utils.str_to_datetime(example_datetime_str) - - assert x.common_metadata.created == created_dt - assert x.properties["created"] == created_str - - x.common_metadata.created = example_datetime_dt - - assert x.common_metadata.created == example_datetime_dt - assert x.properties["created"] == example_datetime_str - - -def test_common_metadata_updated(sample_full_item: Item) -> None: - x = sample_full_item.clone() - updated_str = "2017-01-01T00:30:55Z" - updated_dt = utils.str_to_datetime(updated_str) - example_datetime_str = "2020-01-01T00:00:00Z" - example_datetime_dt = utils.str_to_datetime(example_datetime_str) - - assert x.common_metadata.updated == updated_dt - assert x.properties["updated"] == updated_str - - x.common_metadata.updated = example_datetime_dt - - assert x.common_metadata.updated == example_datetime_dt - assert x.properties["updated"] == example_datetime_str - - -def test_common_metadata_providers(sample_full_item: Item) -> None: - x = sample_full_item.clone() - - providers_dict_list: list[dict[str, Any]] = [ - { - "name": "CoolSat", - "roles": ["producer", "licensor"], - "url": "https://cool-sat.com/", - } - ] - providers_object_list = [Provider.from_dict(d) for d in providers_dict_list] - - example_providers_dict_list: list[dict[str, Any]] = [ - { - "name": "ExampleProvider_1", - "roles": ["example_role_1", "example_role_2"], - "url": "https://exampleprovider1.com/", +def test_get_data_value_common_fields_on_asset() -> None: + asset = Asset(href="foo", data_type="int8", statistics=dict(minimum=3, maximum=5)) + assert asset.data_type == "int8" + assert asset.statistics.minimum == 3 + assert asset.statistics.maximum == 5 + assert asset.extra_fields == { + "data_type": "int8", + "statistics": { + "minimum": 3, + "maximum": 5, }, - { - "name": "ExampleProvider_2", - "roles": ["example_role_1", "example_role_2"], - "url": "https://exampleprovider2.com/", + } + + +def test_set_data_value_common_fields_on_asset() -> None: + asset = Asset(href="foo") + asset.data_type = "int8" + asset.nodata = None + asset.statistics.minimum = 3 + asset.statistics.maximum = 5 + asset.statistics.count = None + + asset_dict = asset.to_dict() + assert asset_dict == { + "href": "foo", + "data_type": "int8", + "statistics": { + "minimum": 3, + "maximum": 5, }, - ] - example_providers_object_list = [ - Provider.from_dict(d) for d in example_providers_dict_list - ] - - for i in range(len(utils.get_opt(x.common_metadata.providers))): - p1 = utils.get_opt(x.common_metadata.providers)[i] - p2 = providers_object_list[i] - assert isinstance(p1, Provider) - assert isinstance(p2, Provider) - assert p1.to_dict() == p2.to_dict() - - pd1 = x.properties["providers"][i] - pd2 = providers_dict_list[i] - assert isinstance(pd1, dict) - assert isinstance(pd2, dict) - assert pd1 == pd2 - - x.common_metadata.providers = example_providers_object_list - - for i in range(len(x.common_metadata.providers)): - p1 = x.common_metadata.providers[i] - p2 = example_providers_object_list[i] - assert isinstance(p1, Provider) - assert isinstance(p2, Provider) - assert p1.to_dict() == p2.to_dict() - - pd1 = x.properties["providers"][i] - pd2 = example_providers_dict_list[i] - assert isinstance(pd1, dict) - assert isinstance(pd2, dict) - assert pd1 == pd2 - - -def test_common_metadata_basics(sample_full_item: Item) -> None: - x = sample_full_item.clone() - - # Title - title = "A CS3 item" - example_title = "example title" - assert x.common_metadata.title == title - x.common_metadata.title = example_title - assert x.common_metadata.title == example_title - assert x.properties["title"] == example_title - - # Description - example_description = "example description" - assert x.common_metadata.description is None - x.common_metadata.description = example_description - assert x.common_metadata.description == example_description - assert x.properties["description"] == example_description - with pytest.raises(ValueError): - x.common_metadata.description = "" - - # License - license = "PDDL-1.0" - example_license = "example license" - assert x.common_metadata.license == license - x.common_metadata.license = example_license - assert x.common_metadata.license == example_license - assert x.properties["license"] == example_license - - # Platform - platform = "coolsat2" - example_platform = "example_platform" - assert x.common_metadata.platform == platform - x.common_metadata.platform = example_platform - assert x.common_metadata.platform == example_platform - assert x.properties["platform"] == example_platform - - # Instruments - instruments = ["cool_sensor_v1"] - example_instruments = ["example instrument 1", "example instrument 2"] - assert (x.common_metadata.instruments or []) == instruments - x.common_metadata.instruments = example_instruments - assert x.common_metadata.instruments == example_instruments - assert x.properties["instruments"] == example_instruments - - # Constellation - example_constellation = "example constellation" - assert x.common_metadata.constellation is None - x.common_metadata.constellation = example_constellation - assert x.common_metadata.constellation == example_constellation - assert x.properties["constellation"] == example_constellation - - # Mission - example_mission = "example mission" - assert x.common_metadata.mission is None - x.common_metadata.mission = example_mission - assert x.common_metadata.mission == example_mission - assert x.properties["mission"] == example_mission - - # GSD - gsd = 0.512 - example_gsd = 0.75 - assert x.common_metadata.gsd == gsd - x.common_metadata.gsd = example_gsd - assert x.common_metadata.gsd == example_gsd - assert x.properties["gsd"] == example_gsd - - -class TestAssetCommonMetadata: - @pytest.fixture - def item(self) -> Item: - return Item.from_file( - TestCases.get_path("data-files/item/sample-item-asset-properties.json") - ) - - def test_title(self, item: Item) -> None: - cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = cm.title - a2_known_value = "Thumbnail" - - # Get - assert thumbnail_cm.title != item_value - assert thumbnail_cm.title == a2_known_value - - # Set - set_value = "Just Another Asset" - analytic_cm.title = set_value - - assert analytic_cm.title == set_value - assert analytic.to_dict()["title"] == set_value - - def test_description(self, item: Item) -> None: - cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = cm.description - a2_known_value = "Thumbnail of the item" - - # Get - assert thumbnail_cm.description != item_value - assert thumbnail_cm.description == a2_known_value - - # Set - set_value = "Yet another description." - analytic_cm.description = set_value - - assert analytic_cm.description == set_value - assert analytic.to_dict()["description"] == set_value - - def test_start_datetime(self, item: Item) -> None: - item_cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = item_cm.start_datetime - a2_known_value = utils.str_to_datetime("2017-05-01T13:22:30.040Z") - - # Get - assert thumbnail_cm.start_datetime != item_value - assert thumbnail_cm.start_datetime == a2_known_value - - # Set - set_value = utils.str_to_datetime("2014-05-01T13:22:30.040Z") - analytic_cm.start_datetime = set_value - - assert analytic_cm.start_datetime == set_value - assert analytic.to_dict()["start_datetime"] == utils.datetime_to_str(set_value) - - def test_end_datetime(self, item: Item) -> None: - cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = cm.end_datetime - a2_known_value = utils.str_to_datetime("2017-05-02T13:22:30.040Z") - - # Get - assert thumbnail_cm.end_datetime != item_value - assert thumbnail_cm.end_datetime == a2_known_value - - # Set - set_value = utils.str_to_datetime("2014-05-01T13:22:30.040Z") - analytic_cm.end_datetime = set_value - - assert analytic_cm.end_datetime == set_value - assert analytic.to_dict()["end_datetime"] == utils.datetime_to_str(set_value) - - def test_license(self, item: Item) -> None: - cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = cm.license - a2_known_value = "CC-BY-4.0" - - # Get - assert thumbnail_cm.license != item_value - assert thumbnail_cm.license == a2_known_value - - # Set - set_value = "various" - analytic_cm.license = set_value - - assert analytic_cm.license == set_value - assert analytic.to_dict()["license"] == set_value - - def test_providers(self, item: Item) -> None: - cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = cm.providers - a2_known_value = [ - Provider( - name="USGS", - url="https://landsat.usgs.gov/", - roles=[ProviderRole.PRODUCER, ProviderRole.LICENSOR], - ) - ] - - # Get - assert thumbnail_cm.providers != item_value - assert thumbnail_cm.providers == a2_known_value - - # Set - set_value = [ - Provider( - name="John Snow", - url="https://cholera.com/", - roles=[ProviderRole.PRODUCER], - ) - ] - analytic_cm.providers = set_value - - assert analytic_cm.providers == set_value - assert analytic.to_dict()["providers"] == [p.to_dict() for p in set_value] - - def test_platform(self, item: Item) -> None: - cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = cm.platform - a2_known_value = "shoes" - - # Get - assert thumbnail_cm.platform != item_value - assert thumbnail_cm.platform == a2_known_value - - # Set - set_value = "brick" - analytic_cm.platform = set_value - - assert analytic_cm.platform == set_value - assert analytic.to_dict()["platform"] == set_value - - def test_instruments(self, item: Item) -> None: - cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = cm.instruments - a2_known_value = ["caliper"] - - # Get - assert thumbnail_cm.instruments != item_value - assert thumbnail_cm.instruments == a2_known_value - - # Set - set_value = ["horns"] - analytic_cm.instruments = set_value - - assert analytic_cm.instruments == set_value - assert analytic.to_dict()["instruments"] == set_value - - def test_constellation(self, item: Item) -> None: - cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = cm.constellation - a2_known_value = "little dipper" - - # Get - assert thumbnail_cm.constellation != item_value - assert thumbnail_cm.constellation == a2_known_value - - # Set - set_value = "orion" - analytic_cm.constellation = set_value - - assert analytic_cm.constellation == set_value - assert analytic.to_dict()["constellation"] == set_value - - def test_mission(self, item: Item) -> None: - cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = cm.mission - a2_known_value = "possible" - - # Get - assert thumbnail_cm.mission != item_value - assert thumbnail_cm.mission == a2_known_value - - # Set - set_value = "critical" - analytic_cm.mission = set_value - - assert analytic_cm.mission == set_value - assert analytic.to_dict()["mission"] == set_value - - def test_gsd(self, item: Item) -> None: - cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = cm.gsd - a2_known_value = 40 - - # Get - assert thumbnail_cm.gsd != item_value - assert thumbnail_cm.gsd == a2_known_value - - # Set - set_value = 100 - analytic_cm.gsd = set_value - - assert analytic_cm.gsd == set_value - assert analytic.to_dict()["gsd"] == set_value - - def test_created(self, item: Item) -> None: - cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = cm.created - a2_known_value = utils.str_to_datetime("2017-05-17T13:22:30.040Z") - - # Get - assert thumbnail_cm.created != item_value - assert thumbnail_cm.created == a2_known_value - - # Set - set_value = utils.str_to_datetime("2014-05-17T13:22:30.040Z") - analytic_cm.created = set_value - - assert analytic_cm.created == set_value - assert analytic.to_dict()["created"] == utils.datetime_to_str(set_value) - - def test_updated(self, item: Item) -> None: - cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = cm.updated - a2_known_value = utils.str_to_datetime("2017-05-18T13:22:30.040Z") - - # Get - assert thumbnail_cm.updated != item_value - assert thumbnail_cm.updated == a2_known_value - - # Set - set_value = utils.str_to_datetime("2014-05-18T13:22:30.040Z") - analytic_cm.updated = set_value - - assert analytic_cm.updated == set_value - assert analytic.to_dict()["updated"] == utils.datetime_to_str(set_value) - - def test_keywords(self, item: Item) -> None: - cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = cm.keywords - a2_known_value = ["keyword_a"] - - # Get - assert thumbnail_cm.keywords != item_value - assert thumbnail_cm.keywords == a2_known_value - - # Set - set_value = ["keyword_b"] - analytic_cm.keywords = set_value - - assert analytic_cm.keywords == set_value - assert analytic.to_dict()["keywords"] == set_value - - def test_roles(self, item: Item) -> None: - cm = item.common_metadata - analytic = item.assets["analytic"] - analytic_cm = CommonMetadata(analytic) - thumbnail = item.assets["thumbnail"] - thumbnail_cm = CommonMetadata(thumbnail) - - item_value = cm.roles - a2_known_value = ["a_role"] - - # Get - assert thumbnail_cm.roles != item_value - assert thumbnail_cm.roles == a2_known_value + } - # Set - set_value = ["another_role"] - analytic_cm.roles = set_value - assert analytic_cm.roles == set_value - assert analytic.to_dict()["roles"] == set_value +def test_get_instrument_common_fields_on_asset() -> None: + asset = Asset(href="foo", gsd=60) + assert asset.gsd == 60 + assert asset.extra_fields == {"gsd": 60} diff --git a/tests/test_examples.py b/tests/test_examples.py new file mode 100644 index 000000000..0c6b858ce --- /dev/null +++ b/tests/test_examples.py @@ -0,0 +1,29 @@ +import json +from pathlib import Path + +import pytest + +import pystac +from pystac.reader import StandardLibraryReader + +EXAMPLE_FILES = sorted( + list( + (Path(__file__).parent / "data-files" / "examples").glob("**/*.json"), + ) +) + + +@pytest.mark.vcr +@pytest.mark.parametrize("path", EXAMPLE_FILES) +def test_example_from_dict(path: Path): + with open(path) as f: + data = json.load(f) + stac_object = pystac.from_dict(data) + stac_object.validate() + + +@pytest.mark.vcr +@pytest.mark.parametrize("path", EXAMPLE_FILES) +def test_example_read_file(path: Path): + stac_object = pystac.read_file(path, reader=StandardLibraryReader()) + stac_object.validate() diff --git a/tests/test_item.py b/tests/test_item.py index 6dee920c3..74d56c9dd 100644 --- a/tests/test_item.py +++ b/tests/test_item.py @@ -1,694 +1,31 @@ -from __future__ import annotations - -import copy +import datetime import json -import os -import pickle -import tempfile -from copy import deepcopy from pathlib import Path -from typing import Any, cast -import dateutil.relativedelta import pytest -import pystac -import pystac.serialization.common_properties -from pystac import Asset, Catalog, Collection, Item, Link, STACValidationError -from pystac.utils import ( - datetime_to_str, - get_opt, - is_absolute_href, - make_posix_style, - str_to_datetime, -) -from pystac.validation import validate_dict -from tests.utils import TestCases, assert_to_from_dict - - -def test_to_from_dict(sample_item_dict: dict[str, Any]) -> None: - param_dict = deepcopy(sample_item_dict) - - assert_to_from_dict(Item, param_dict) - item = Item.from_dict(param_dict) - assert item.id == "CS3-20160503_132131_05" - - # 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 - - -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 - - rel_asset = Asset("./data.geojson") - rel_asset.set_owner(item) - actual_href = rel_asset.get_absolute_href() - assert actual_href is None - - -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 - - -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 - - -@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={}, - ) - - 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() -> 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 == {} - - -@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 pytest.raises(pystac.STACError): - Item( - "test", - geometry=item.geometry, - bbox=item.bbox, - datetime=None, - start_datetime=item.datetime, - properties={}, - ) - 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() -> 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 - assert item.get_datetime(item.assets["thumbnail"]) == item.datetime - - # 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" - ) - - 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" - ) +from pystac import Item, utils -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 - - -@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) - - validate_dict(item_dict, pystac.STACObjectType.ITEM) - - item = Item.from_dict(item_dict) - assert isinstance(item, Item) +def test_item_init() -> None: + item = Item(id="an-id") item.validate() - item_dict = item.to_dict() - assert item_dict["geometry"] is None - assert "bbox" not in item_dict - - -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" - - did_merge = pystac.serialization.common_properties.merge_common_properties( - item_json - ) - assert not did_merge - - -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_item_from_dict(examples_path: Path) -> None: + with open(examples_path / "simple-item.json") as f: + data = json.load(f) -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") + # Normalize datetime string to get rid of spurious zeros + data["properties"]["datetime"] = utils.datetime_to_str( + datetime.datetime.fromisoformat(data["properties"]["datetime"]) ) - with pytest.raises(pystac.STACTypeError): - _ = pystac.Item.from_dict(catalog_dict) - -@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 = Item.from_dict(data) item.validate() + assert item.to_dict() == data -class TestItemSubClass: - """This tests cases related to creating classes inheriting from pystac.Catalog to - ensure that inheritance, class methods, etc. function as expected.""" - - SAMPLE_ITEM = TestCases.get_path("data-files/item/sample-item.json") - - class BasicCustomItem(pystac.Item): - pass - - def test_from_dict_returns_subclass(self) -> None: - stac_io = pystac.StacIO.default() - item_dict = stac_io.read_json(self.SAMPLE_ITEM) - custom_item = self.BasicCustomItem.from_dict(item_dict) - - assert isinstance(custom_item, self.BasicCustomItem) - - def test_from_file_returns_subclass(self) -> None: - custom_item = self.BasicCustomItem.from_file(self.SAMPLE_ITEM) - - 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() - - assert isinstance(cloned_item, self.BasicCustomItem) - - -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) - - cloned_asset = original_asset.clone() - - assert original_asset.to_dict() == asset_dict - assert cloned_asset.to_dict() == 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 - - -class TestAssetSubClass: - class CustomAsset(Asset): - pass - - 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"]) - - 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, asset_dict: AssetDict) -> None: - asset = self.CustomAsset.from_dict(asset_dict) - cloned_asset = asset.clone() - assert isinstance(cloned_asset, self.CustomAsset) - - -def test_custom_item_from_dict(item: Item) -> None: - # https://github.com/stac-utils/pystac/issues/862 - class CustomItem(Item): - @classmethod - def from_dict( - cls, - d: dict[str, Any], - href: str | None = None, - root: Catalog | None = None, - migrate: bool = True, - preserve_dict: bool = True, - ) -> CustomItem: - return super().from_dict(d) - - _ = CustomItem.from_dict(item.to_dict()) - - -def test_item_from_dict_raises_useful_error() -> None: - item_dict = {"type": "Feature", "stac_version": "1.1.0", "id": "lalalalala"} - with pytest.raises(pystac.STACError, match="Invalid Item: "): - Item.from_dict(item_dict) - - -def test_item_from_dict_with_missing_stac_version_raises_useful_error() -> None: - item_dict = {"type": "Feature", "id": "lalalalala"} - with pytest.raises(pystac.STACTypeError, match="'stac_version' is missing"): - Item.from_dict(item_dict, migrate=False) - - -def test_item_from_dict_with_missing_type_raises_useful_error() -> None: - item_dict = {"stac_version": "0.8.0", "id": "lalalalala"} - with pytest.raises(pystac.STACTypeError, match="'type' is missing"): - Item.from_dict(item_dict, migrate=False) - - -@pytest.mark.parametrize("add_canonical", (True, False)) -def test_remove_hierarchical_links( - test_case_1_catalog: Catalog, add_canonical: bool -) -> None: - item = next(test_case_1_catalog.get_items(recursive=True)) - item.remove_hierarchical_links(add_canonical=add_canonical) - for link in item.links: - assert not link.is_hierarchical() - assert bool(item.get_single_link("canonical")) == add_canonical - - -def test_geo_interface() -> None: - item = pystac.Item.from_file(TestCases.get_path("data-files/item/sample-item.json")) - assert ( - item.to_dict(include_self_link=False, transform_hrefs=False) - == item.__geo_interface__ - ) - - -def test_duplicate_self_links(tmp_path: Path, sample_item: pystac.Item) -> None: - # https://github.com/stac-utils/pystac/issues/1102 - assert len(sample_item.get_links(rel="self")) == 1 - path = tmp_path / "item.json" - sample_item.save_object(include_self_link=True, dest_href=str(path)) - sample_item = Item.from_file(path) - assert len(sample_item.get_links(rel="self")) == 1 - - -def test_get_derived_from_when_none_exists(test_case_1_catalog: Catalog) -> None: - item = next(test_case_1_catalog.get_items(recursive=True)) - assert item.get_derived_from() == [] - for link in item.links: - assert link.rel != pystac.RelType.DERIVED_FROM - assert item.get_single_link(pystac.RelType.DERIVED_FROM) is None - - -def test_add_derived_from(test_case_1_catalog: Catalog) -> None: - items = list(test_case_1_catalog.get_items(recursive=True)) - item_0 = items[0] - item_1 = items[1] - item_2 = items[2] - item_0.add_derived_from(item_1, item_2.self_href) - derived_from = item_0.get_derived_from() - assert len(derived_from) == 2 - assert derived_from[0].id == item_1.id - assert derived_from[1].id == item_2.id - filtered = [ - link for link in item_0.links if link.rel == pystac.RelType.DERIVED_FROM - ] - assert len(filtered) == 2 - assert filtered[0].to_dict(transform_href=False)["href"] == item_1.self_href - assert filtered[1].to_dict(transform_href=False)["href"] == item_2.self_href - - -def test_get_unresolvable_derived_from(test_case_1_catalog: Catalog) -> None: - item = next(test_case_1_catalog.get_items(recursive=True)) - item.add_derived_from("foo") - with pytest.raises( - pystac.STACError, match="Link failed to resolve. Use get_links instead" - ): - item.get_derived_from() - - links = item.get_links(pystac.RelType.DERIVED_FROM) - assert len(links) == 1 - - -def test_remove_unresolvable_derived_from(test_case_1_catalog: Catalog) -> None: - item = next(test_case_1_catalog.get_items(recursive=True)) - item.add_derived_from("foo") - with pytest.raises( - pystac.STACError, match="Link failed to resolve. Use remove_links instead" - ): - item.remove_derived_from("foo") - - item.remove_links(pystac.RelType.DERIVED_FROM) - assert item.get_derived_from() == [] - - -def test_remove_derived_from(test_case_1_catalog: Catalog) -> None: - items = list(test_case_1_catalog.get_items(recursive=True)) - item_0 = items[0] - item_1 = items[1] - item_0.add_derived_from(item_1) - item_0.remove_derived_from(item_1.id) - assert item_0.get_derived_from() == [] - for link in item_0.links: - assert link.rel != pystac.RelType.DERIVED_FROM - assert item_0.get_single_link(pystac.RelType.DERIVED_FROM) is None - - -def test_delete_asset(tmp_asset: Asset) -> None: - asset = tmp_asset - href = asset.get_absolute_href() - item = asset.owner - - assert href is not None - assert item is not None - - name = next(k for k in item.assets.keys() if item.assets[k] == asset) - item.delete_asset(name) - - assert name not in item.assets - assert not os.path.exists(href) - - -def test_delete_asset_relative_no_self_link_fails(tmp_asset: pystac.Asset) -> None: - asset = tmp_asset - href = asset.get_absolute_href() - item = asset.owner - - assert href is not None - assert item is not None - assert isinstance(item, pystac.Item) - - item.set_self_href(None) - - name = next(k for k in item.assets.keys() if item.assets[k] == asset) - with pytest.raises(ValueError, match="Cannot delete file") as e: - item.delete_asset(name) - - assert asset.href in str(e.value) - assert name in item.assets - assert os.path.exists(href) - - -def test_resolve_collection_with_root( - tmp_path: Path, item: Item, collection: Collection -) -> None: - # Motivated by https://github.com/stac-utils/pystac-client/issues/548 - catalog = Catalog("root", "the description") - item.set_root(catalog) - - collection_path = str(tmp_path / "collection.json") - collection.save_object( - include_self_link=False, - dest_href=collection_path, - ) - item.add_link(Link(rel="collection", target=collection_path)) - - read_collection = item.get_collection() - assert read_collection - root = read_collection.get_root() - assert root - assert root.id == "root" - - -@pytest.mark.vcr() -def test_non_hierarchical_relative_link() -> None: - root = pystac.Catalog("root", "root") - a = pystac.Catalog("a", "a") - b = pystac.Catalog("b", "b") - - root.add_child(a) - root.add_child(b) - a.add_link(pystac.Link("related", b)) - b.add_link( - pystac.Link("item", TestCases.get_path("data-files/item/sample-item.json")) - ) - - root.catalog_type = pystac.catalog.CatalogType.SELF_CONTAINED - root.normalize_hrefs("test_output") - related_href = [link for link in a.links if link.rel == "related"][0].get_href() - - assert related_href is not None and not is_absolute_href(related_href) - assert a.target_in_hierarchy(b) - assert root.target_in_hierarchy(next(b.get_items())) - assert root.target_in_hierarchy(root) - - -def test_pathlib() -> None: - # This works, but breaks mypy until we fix - # https://github.com/stac-utils/pystac/issues/1216 - Item.from_file(Path(TestCases.get_path("data-files/item/sample-item.json"))) - - -def test_invalid_error_message(item: Item) -> None: - item.extra_fields["collection"] = "can't have a collection" - with pytest.raises(STACValidationError) as error: - item.validate() - assert "can't have a collection" in str(error.value) - - -def test_pickle_with_no_links(item: Item) -> None: - roundtripped = pickle.loads(pickle.dumps(item)) - for attr in ["id", "geometry", "bbox", "datetime", "links"]: - assert getattr(roundtripped, attr) == getattr(item, attr) - - -def test_pickle_with_hrefless_links(item: Item) -> None: - root = pystac.Catalog("root", "root") - a = pystac.Catalog("a", "a") - - item.add_link(pystac.Link("related", a)) - item.add_link( - pystac.Link("item", TestCases.get_path("data-files/item/sample-item.json")) - ) - item.set_root(root) - - roundtripped = pickle.loads(pickle.dumps(item)) - for original, new in zip(item.links, roundtripped.links): - assert original.rel == new.rel - assert original.media_type == new.media_type - assert str(original.owner) == str(new.owner) - assert str(original.target) == str(new.target) - - -def test_pickle_with_only_href_links(item: Item) -> None: - item.add_link( - pystac.Link("item", TestCases.get_path("data-files/item/sample-item.json")) - ) - - roundtripped = pickle.loads(pickle.dumps(item)) - for original, new in zip(item.links, roundtripped.links): - assert original.rel == new.rel - assert original.media_type == new.media_type - assert str(original.owner) == str(new.owner) - assert str(original.target) == str(new.target) - - -def test_copy_with_unresolveable_root(item: Item) -> None: - item.add_link( - pystac.Link( - "root", "s3://naip-visualization/this-is-a-non-existent-catalog.json" - ) - ) - copy.deepcopy(item) - - -def test_no_collection(item: Item) -> None: - # https://github.com/stac-utils/stac-api-validator/issues/527 - assert item.collection is None - - -def test_migrate_by_default() -> None: - with open( - TestCases.get_path("data-files/projection/example-with-version-1.1.json") - ) as f: - data = json.load(f) - item = pystac.Item.from_dict(data) # default used to be migrate=False - assert item.ext.proj.code == "EPSG:32614" - - -def test_clone_extra_fields(item: Item) -> None: - item.extra_fields["foo"] = "bar" - cloned = item.clone() - assert cloned.extra_fields["foo"] == "bar" +@pytest.mark.vcr +def test_validate_extension(proj_example: Item) -> None: + proj_example.validate() diff --git a/tests/test_item_assets.py b/tests/test_item_assets.py index 167f1bd26..3cb31a884 100644 --- a/tests/test_item_assets.py +++ b/tests/test_item_assets.py @@ -1,27 +1,23 @@ +from pathlib import Path + import pytest from pystac import Collection -from pystac.errors import DeprecatedWarning -from pystac.extensions.item_assets import AssetDefinition, ItemAssetsExtension -from pystac.item_assets import ItemAssetDefinition -from tests.utils import TestCases - -CLASSIFICATION_COLLECTION_RASTER_URI = TestCases.get_path( - "data-files/classification/collection-item-assets-raster-bands.json" -) +from pystac.asset import ItemAsset @pytest.fixture -def landsat8_collection() -> Collection: +def landsat8_collection(data_files_path: Path) -> Collection: return Collection.from_file( - TestCases.get_path("data-files/item-assets/example-landsat8.json") + data_files_path / "collections" / "with-item-assets.json" ) def test_example(landsat8_collection: Collection) -> None: + assert landsat8_collection.item_assets assert len(landsat8_collection.item_assets) == 13 - assert landsat8_collection.item_assets["B1"] == ItemAssetDefinition( + assert landsat8_collection.item_assets["B1"] == ItemAsset.from_dict( { "type": "image/tiff; application=geotiff", "eo:bands": [ @@ -38,49 +34,30 @@ def test_example(landsat8_collection: Collection) -> None: ) -def test_set_using_dict(landsat8_collection: Collection) -> None: - assert len(landsat8_collection.item_assets) == 13 - - 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"] - ) - - -class TestAssetDefinition: +class TestItemAsset: def test_eq(self, landsat8_collection: Collection) -> None: - assert landsat8_collection.item_assets["B1"] != {"title": "Coastal Band (B1)"} + assert landsat8_collection.item_assets + assert landsat8_collection.item_assets["B1"].to_dict() != { + "title": "Coastal Band (B1)" + } def test_create(self) -> None: title = "Coastal Band (B1)" description = "Coastal Band Top Of the Atmosphere" media_type = "image/tiff; application=geotiff" roles = ["data"] - asset_defn = ItemAssetDefinition.create( - title=title, description=description, media_type=media_type, roles=roles + asset_defn = ItemAsset( + title=title, description=description, type=media_type, roles=roles ) assert ( asset_defn.title, asset_defn.description, - asset_defn.media_type, + asset_defn.type, asset_defn.roles, ) == (title, description, media_type, roles) def test_title(self) -> None: - asset_defn = ItemAssetDefinition({}) + asset_defn = ItemAsset() title = "Very Important Asset" asset_defn.title = title @@ -88,7 +65,7 @@ def test_title(self) -> None: assert asset_defn.title == asset_defn.to_dict()["title"] == title def test_description(self) -> None: - asset_defn = ItemAssetDefinition({}) + asset_defn = ItemAsset() description = "What an incredibly important asset this is!" asset_defn.description = description @@ -98,125 +75,17 @@ def test_description(self) -> None: ) def test_media_type(self) -> None: - asset_defn = ItemAssetDefinition({}) + asset_defn = ItemAsset() media_type = "application/json" - asset_defn.media_type = media_type + asset_defn.type = media_type - assert asset_defn.media_type == asset_defn.to_dict()["type"] == media_type + assert asset_defn.type == asset_defn.to_dict()["type"] == media_type def test_roles(self) -> None: - asset_defn = ItemAssetDefinition({}) + asset_defn = ItemAsset() roles = ["thumbnail"] asset_defn.roles = roles assert asset_defn.roles == asset_defn.to_dict()["roles"] == roles - - def test_set_owner(self, landsat8_collection: Collection) -> None: - asset_definition = 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", - } - ) - asset_definition.set_owner(landsat8_collection) - assert asset_definition.owner == landsat8_collection - - -def test_extra_fields(collection: Collection) -> None: - asset_definition = ItemAssetDefinition.create( - title=None, - description=None, - media_type=None, - roles=None, - extra_fields={"raster:bands": [{"nodata": 42}]}, - ) - - collection.item_assets = {"data": asset_definition} - assert collection.item_assets["data"].owner == collection - - collection_as_dict = collection.to_dict() - assert collection_as_dict["item_assets"]["data"]["raster:bands"] == [{"nodata": 42}] - asset = asset_definition.create_asset("asset.tif") - assert asset.extra_fields["raster:bands"] == [{"nodata": 42}] - - collection.item_assets["data"].ext.add("raster") - assert (bands := collection.item_assets["data"].ext.raster.bands) - assert bands[0].nodata == 42 - - assert collection.item_assets["data"].ext.has("raster") - assert collection.ext.has("raster") - - -def test_set_item_asset(collection: Collection) -> None: - asset_definition = ItemAssetDefinition.create( - title=None, - description=None, - media_type=None, - roles=None, - extra_fields={"raster:bands": [{"nodata": 42}]}, - ) - - collection.item_assets["data"] = asset_definition - assert collection.item_assets["data"].owner == collection - - -def test_item_assets_extension_is_deprecated() -> None: - collection = Collection.from_file(CLASSIFICATION_COLLECTION_RASTER_URI) - - assert ItemAssetsExtension.get_schema_uri() not in collection.stac_extensions - - with pytest.warns(DeprecatedWarning, match="top-level property of"): - item_asset_ext = ItemAssetsExtension.ext(collection, add_if_missing=True) - item_asset = item_asset_ext.item_assets["cloud-mask-raster"] - - assert collection.id in repr(item_asset_ext) - - assert item_asset.ext.has("eo") - - with pytest.warns(DeprecatedWarning, match="top-level property of"): - assert collection.ext.item_assets["cloud-mask-raster"].ext.has("eo") - - assert ItemAssetsExtension.get_schema_uri() in collection.stac_extensions - - with pytest.warns(DeprecationWarning): - asset_definition = AssetDefinition( - {"title": "Thumbnail image", "type": "image/jpeg"} - ) - item_asset_ext.item_assets["thumbnail"] = asset_definition - - -def test_item_assets_extension_asset_definition_is_deprecated() -> None: - with pytest.warns( - DeprecationWarning, match="Please use ``pystac.ItemAssetDefinition``" - ): - asset_definition = AssetDefinition( - { - "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", - } - ) - - assert asset_definition.title == "Coastal Band (B1)" - assert asset_definition.ext.eo.bands - assert asset_definition.ext.eo.bands[0].name == "B1" - assert asset_definition.owner is None diff --git a/tests/test_obstore.py b/tests/test_obstore.py new file mode 100644 index 000000000..eab3ef6b8 --- /dev/null +++ b/tests/test_obstore.py @@ -0,0 +1,20 @@ +from pathlib import Path + +import pytest +from pytest import Subtests + +import pystac + +obstore = pytest.importorskip("obstore") + + +def test_examples_read_file(examples_path: Path, subtests: Subtests) -> None: + from pystac.obstore import ObstoreReader + + store = obstore.store.LocalStore() + reader = ObstoreReader(store) + + for path in examples_path.glob("**/*.json"): + with subtests.test(path=path): + stac_object = pystac.read_file(path, reader=reader) + stac_object.validate() diff --git a/tests/__init__.py b/tests/v1/__init__.py similarity index 100% rename from tests/__init__.py rename to tests/v1/__init__.py diff --git a/tests/cassettes/test_catalog/TestCatalog.test_read_remote.yaml b/tests/v1/cassettes/test_catalog/TestCatalog.test_read_remote.yaml similarity index 100% rename from tests/cassettes/test_catalog/TestCatalog.test_read_remote.yaml rename to tests/v1/cassettes/test_catalog/TestCatalog.test_read_remote.yaml diff --git a/tests/cassettes/test_catalog/TestCatalog.test_validate_all[cat0].yaml b/tests/v1/cassettes/test_catalog/TestCatalog.test_validate_all[cat0].yaml similarity index 100% rename from tests/cassettes/test_catalog/TestCatalog.test_validate_all[cat0].yaml rename to tests/v1/cassettes/test_catalog/TestCatalog.test_validate_all[cat0].yaml diff --git a/tests/cassettes/test_catalog/TestCatalog.test_validate_all[cat1].yaml b/tests/v1/cassettes/test_catalog/TestCatalog.test_validate_all[cat1].yaml similarity index 100% rename from tests/cassettes/test_catalog/TestCatalog.test_validate_all[cat1].yaml rename to tests/v1/cassettes/test_catalog/TestCatalog.test_validate_all[cat1].yaml diff --git a/tests/cassettes/test_catalog/TestCatalog.test_validate_all[cat3].yaml b/tests/v1/cassettes/test_catalog/TestCatalog.test_validate_all[cat3].yaml similarity index 100% rename from tests/cassettes/test_catalog/TestCatalog.test_validate_all[cat3].yaml rename to tests/v1/cassettes/test_catalog/TestCatalog.test_validate_all[cat3].yaml diff --git a/tests/cassettes/test_catalog/TestCatalog.test_validate_all[cat4].yaml b/tests/v1/cassettes/test_catalog/TestCatalog.test_validate_all[cat4].yaml similarity index 100% rename from tests/cassettes/test_catalog/TestCatalog.test_validate_all[cat4].yaml rename to tests/v1/cassettes/test_catalog/TestCatalog.test_validate_all[cat4].yaml diff --git a/tests/cassettes/test_catalog/TestCatalog.test_validate_all[cat5].yaml b/tests/v1/cassettes/test_catalog/TestCatalog.test_validate_all[cat5].yaml similarity index 100% rename from tests/cassettes/test_catalog/TestCatalog.test_validate_all[cat5].yaml rename to tests/v1/cassettes/test_catalog/TestCatalog.test_validate_all[cat5].yaml diff --git a/tests/cassettes/test_catalog/TestCatalog.test_validate_all[cat6].yaml b/tests/v1/cassettes/test_catalog/TestCatalog.test_validate_all[cat6].yaml similarity index 100% rename from tests/cassettes/test_catalog/TestCatalog.test_validate_all[cat6].yaml rename to tests/v1/cassettes/test_catalog/TestCatalog.test_validate_all[cat6].yaml diff --git a/tests/cassettes/test_catalog/test_validate_all_with_max_n.yaml b/tests/v1/cassettes/test_catalog/test_validate_all_with_max_n.yaml similarity index 100% rename from tests/cassettes/test_catalog/test_validate_all_with_max_n.yaml rename to tests/v1/cassettes/test_catalog/test_validate_all_with_max_n.yaml diff --git a/tests/cassettes/test_catalog/test_validate_all_with_recusive_off.yaml b/tests/v1/cassettes/test_catalog/test_validate_all_with_recusive_off.yaml similarity index 100% rename from tests/cassettes/test_catalog/test_validate_all_with_recusive_off.yaml rename to tests/v1/cassettes/test_catalog/test_validate_all_with_recusive_off.yaml diff --git a/tests/cassettes/test_item/test_non_hierarchical_relative_link.yaml b/tests/v1/cassettes/test_item/test_non_hierarchical_relative_link.yaml similarity index 100% rename from tests/cassettes/test_item/test_non_hierarchical_relative_link.yaml rename to tests/v1/cassettes/test_item/test_non_hierarchical_relative_link.yaml diff --git a/tests/v1/cassettes/test_item/test_null_geometry.yaml b/tests/v1/cassettes/test_item/test_null_geometry.yaml new file mode 100644 index 000000000..5ef31d732 --- /dev/null +++ b/tests/v1/cassettes/test_item/test_null_geometry.yaml @@ -0,0 +1,452 @@ +interactions: +- request: + body: null + headers: {} + method: GET + uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/item.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/item.json#\",\n + \ \"title\": \"STAC Item\",\n \"type\": \"object\",\n \"description\": \"This + object represents the metadata for an item in a SpatioTemporal Asset Catalog.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/definitions/core\"\n }\n ],\n + \ \"definitions\": {\n \"common_metadata\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"basics.json\"\n },\n {\n \"$ref\": + \"datetime.json\"\n },\n {\n \"$ref\": \"instrument.json\"\n + \ },\n {\n \"$ref\": \"licensing.json\"\n },\n + \ {\n \"$ref\": \"provider.json\"\n }\n ]\n },\n + \ \"core\": {\n \"allOf\": [\n {\n \"$ref\": \"https://geojson.org/schema/Feature.json\"\n + \ },\n {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"geometry\",\n + \ \"bbox\"\n ],\n \"properties\": + {\n \"geometry\": {\n \"$ref\": \"https://geojson.org/schema/Geometry.json\"\n + \ },\n \"bbox\": {\n \"type\": + \"array\",\n \"oneOf\": [\n {\n \"minItems\": + 4,\n \"maxItems\": 4\n },\n {\n + \ \"minItems\": 6,\n \"maxItems\": + 6\n }\n ],\n \"items\": + {\n \"type\": \"number\"\n }\n }\n + \ }\n },\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"geometry\"\n ],\n + \ \"properties\": {\n \"geometry\": {\n \"type\": + \"null\"\n },\n \"bbox\": {\n \"not\": + {}\n }\n }\n }\n ]\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"stac_version\",\n + \ \"id\",\n \"links\",\n \"assets\",\n \"properties\"\n + \ ],\n \"properties\": {\n \"stac_version\": {\n + \ \"title\": \"STAC version\",\n \"type\": \"string\",\n + \ \"const\": \"1.0.0-beta.2\"\n },\n \"stac_extensions\": + {\n \"title\": \"STAC extensions\",\n \"type\": + \"array\",\n \"uniqueItems\": true,\n \"items\": + {\n \"anyOf\": [\n {\n \"title\": + \"Reference to a JSON Schema\",\n \"type\": \"string\",\n + \ \"format\": \"uri\"\n },\n {\n + \ \"title\": \"Reference to a core extension\",\n \"type\": + \"string\"\n }\n ]\n }\n },\n + \ \"id\": {\n \"title\": \"Provider ID\",\n \"description\": + \"Provider item ID\",\n \"type\": \"string\"\n },\n + \ \"links\": {\n \"title\": \"Item links\",\n \"description\": + \"Links to item relations\",\n \"type\": \"array\",\n \"items\": + {\n \"$ref\": \"#/definitions/link\"\n }\n },\n + \ \"assets\": {\n \"$ref\": \"#/definitions/assets\"\n + \ },\n \"properties\": {\n \"$ref\": \"#/definitions/common_metadata\"\n + \ },\n \"collection\": {\n \"title\": \"Collection + ID\",\n \"description\": \"The ID of the STAC Collection this + Item references to.\",\n \"type\": \"string\"\n }\n + \ }\n }\n ]\n },\n \"link\": {\n \"type\": + \"object\",\n \"required\": [\n \"rel\",\n \"href\"\n ],\n + \ \"properties\": {\n \"href\": {\n \"title\": \"Link + reference\",\n \"type\": \"string\"\n },\n \"rel\": + {\n \"title\": \"Link relation type\",\n \"type\": \"string\"\n + \ },\n \"type\": {\n \"title\": \"Link type\",\n \"type\": + \"string\"\n },\n \"title\": {\n \"title\": \"Link + title\",\n \"type\": \"string\"\n },\n \"created\": + {\n \"$ref\": \"datetime.json#/definitions/created\"\n },\n + \ \"updated\": {\n \"$ref\": \"datetime.json#/definitions/updated\"\n + \ }\n }\n },\n \"assets\": {\n \"title\": \"Asset links\",\n + \ \"description\": \"Links to assets\",\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/asset\"\n + \ }\n },\n \"asset\": {\n \"type\": \"object\",\n \"required\": + [\n \"href\"\n ],\n \"properties\": {\n \"href\": + {\n \"title\": \"Asset reference\",\n \"type\": \"string\"\n + \ },\n \"title\": {\n \"title\": \"Asset title\",\n + \ \"type\": \"string\"\n },\n \"description\": {\n \"title\": + \"Asset description\",\n \"type\": \"string\"\n },\n \"type\": + {\n \"title\": \"Asset type\",\n \"type\": \"string\"\n + \ },\n \"roles\": {\n \"title\": \"Asset roles\",\n + \ \"type\": \"array\",\n \"items\": {\n \"type\": + \"string\"\n }\n }\n }\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/basics.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/basics.json#\",\n + \ \"title\": \"Basic Descriptive Fields\",\n \"type\": \"object\",\n \"properties\": + {\n \"title\": {\n \"title\": \"Item Title\",\n \"description\": + \"A human-readable title describing the Item.\",\n \"type\": \"string\"\n + \ },\n \"description\": {\n \"title\": \"Item Description\",\n \"description\": + \"Detailed multi-line description to fully explain the Item.\",\n \"type\": + \"string\"\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/datetime.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/datetime.json#\",\n + \ \"title\": \"Date and Time Fields\",\n \"type\": \"object\",\n \"allOf\": + [\n {\n \"properties\": {\n \"created\": {\n \"$ref\": + \"#/definitions/created\"\n },\n \"updated\": {\n \"$ref\": + \"#/definitions/updated\"\n }\n }\n },\n {\n \"anyOf\": + [\n {\n \"required\": [\n \"datetime\"\n ],\n + \ \"properties\": {\n \"datetime\": {\n \"$ref\": + \"#/definitions/datetime\"\n },\n \"start_datetime\": + {\n \"$ref\": \"#/definitions/start_datetime\"\n }, + \n \"end_datetime\": {\n \"$ref\": \"#/definitions/end_datetime\"\n + \ }\n },\n \"dependencies\": {\n \"start_datetime\": + {\n \"required\": [\n \"end_datetime\"\n ]\n + \ },\n \"end_datetime\": {\n \"required\": + [\n \"start_datetime\"\n ]\n }\n }\n + \ },\n {\n \"required\": [\n \"datetime\",\n + \ \"start_datetime\",\n \"end_datetime\"\n ],\n + \ \"properties\": {\n \"datetime\": {\n \"oneOf\": + [\n {\n \"$ref\": \"#/definitions/datetime\"\n + \ },\n {\n \"type\": [\"null\"],\n + \ \"const\": null\n }\n ]\n },\n + \ \"start_datetime\": {\n \"$ref\": \"#/definitions/start_datetime\"\n + \ }, \n \"end_datetime\": {\n \"$ref\": + \"#/definitions/end_datetime\"\n }\n }\n }\n ]\n + \ }\n ],\n \"definitions\": {\n \"datetime\": {\n \"title\": \"Date + and Time\",\n \"description\": \"The searchable date/time of the assets, + in UTC (Formatted in RFC 3339) \",\n \"type\": \"string\",\n \"format\": + \"date-time\"\n },\n \"start_datetime\": {\n \"title\": \"Start + Date and Time\",\n \"description\": \"The searchable start date/time + of the assets, in UTC (Formatted in RFC 3339) \",\n \"type\": \"string\",\n + \ \"format\": \"date-time\"\n }, \n \"end_datetime\": {\n \"title\": + \"End Date and Time\", \n \"description\": \"The searchable end date/time + of the assets, in UTC (Formatted in RFC 3339) \", \n \"type\": + \"string\",\n \"format\": \"date-time\"\n },\n \"created\": {\n + \ \"title\": \"Creation Time\",\n \"type\": \"string\",\n \"format\": + \"date-time\"\n },\n \"updated\": {\n \"title\": \"Last Update + Time\",\n \"type\": \"string\",\n \"format\": \"date-time\"\n }\n + \ }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/instrument.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/instrument.json#\",\n + \ \"title\": \"Instrument Fields\",\n \"type\": \"object\",\n \"properties\": + {\n \"platform\": {\n \"title\": \"Platform\",\n \"type\": \"string\"\n + \ },\n \"instruments\": {\n \"title\": \"Instruments\",\n \"type\": + \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n + \ \"constellation\": {\n \"title\": \"Constellation\",\n \"type\": + \"string\"\n },\n \"mission\": {\n \"title\": \"Mission\",\n \"type\": + \"string\"\n },\n \"gsd\": {\n \"title\": \"Ground Sample Distance\",\n + \ \"type\": \"number\"\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/licensing.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/licensing.json#\",\n + \ \"title\": \"Licensing Fields\",\n \"type\": \"object\",\n \"properties\": + {\n \"license\": {\n \"type\": \"string\",\n \"pattern\": \"^[\\\\w\\\\-\\\\.\\\\+]+$\"\n + \ }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/provider.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/provider.json#\",\n + \ \"title\": \"Provider Fields\",\n \"type\": \"object\",\n \"properties\": + {\n \"providers\": {\n \"title\": \"Providers\",\n \"type\": + \"array\",\n \"items\": {\n \"properties\": {\n \"name\": + {\n \"title\": \"Organization name\",\n \"type\": \"string\"\n + \ },\n \"description\": {\n \"title\": \"Organization + description\",\n \"type\": \"string\"\n },\n \"roles\": + {\n \"title\": \"Organization roles\",\n \"type\": \"array\",\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": + [\n \"producer\",\n \"licensor\",\n \"processor\",\n + \ \"host\"\n ]\n }\n },\n \"url\": + {\n \"title\": \"Organization homepage\",\n \"type\": + \"string\",\n \"format\": \"url\"\n }\n }\n }\n + \ }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - schemas.stacspec.org + method: GET + uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/item.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/item.json#\",\n + \ \"title\": \"STAC Item\",\n \"type\": \"object\",\n \"description\": \"This + object represents the metadata for an item in a SpatioTemporal Asset Catalog.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/definitions/core\"\n }\n ],\n + \ \"definitions\": {\n \"common_metadata\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"basics.json\"\n },\n {\n \"$ref\": + \"datetime.json\"\n },\n {\n \"$ref\": \"instrument.json\"\n + \ },\n {\n \"$ref\": \"licensing.json\"\n },\n + \ {\n \"$ref\": \"provider.json\"\n }\n ]\n },\n + \ \"core\": {\n \"allOf\": [\n {\n \"$ref\": \"https://geojson.org/schema/Feature.json\"\n + \ },\n {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"geometry\",\n + \ \"bbox\"\n ],\n \"properties\": + {\n \"geometry\": {\n \"$ref\": \"https://geojson.org/schema/Geometry.json\"\n + \ },\n \"bbox\": {\n \"type\": + \"array\",\n \"oneOf\": [\n {\n \"minItems\": + 4,\n \"maxItems\": 4\n },\n {\n + \ \"minItems\": 6,\n \"maxItems\": + 6\n }\n ],\n \"items\": + {\n \"type\": \"number\"\n }\n }\n + \ }\n },\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"geometry\"\n ],\n + \ \"properties\": {\n \"geometry\": {\n \"type\": + \"null\"\n },\n \"bbox\": {\n \"not\": + {}\n }\n }\n }\n ]\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"stac_version\",\n + \ \"id\",\n \"links\",\n \"assets\",\n \"properties\"\n + \ ],\n \"properties\": {\n \"stac_version\": {\n + \ \"title\": \"STAC version\",\n \"type\": \"string\",\n + \ \"const\": \"1.0.0-beta.2\"\n },\n \"stac_extensions\": + {\n \"title\": \"STAC extensions\",\n \"type\": + \"array\",\n \"uniqueItems\": true,\n \"items\": + {\n \"anyOf\": [\n {\n \"title\": + \"Reference to a JSON Schema\",\n \"type\": \"string\",\n + \ \"format\": \"uri\"\n },\n {\n + \ \"title\": \"Reference to a core extension\",\n \"type\": + \"string\"\n }\n ]\n }\n },\n + \ \"id\": {\n \"title\": \"Provider ID\",\n \"description\": + \"Provider item ID\",\n \"type\": \"string\"\n },\n + \ \"links\": {\n \"title\": \"Item links\",\n \"description\": + \"Links to item relations\",\n \"type\": \"array\",\n \"items\": + {\n \"$ref\": \"#/definitions/link\"\n }\n },\n + \ \"assets\": {\n \"$ref\": \"#/definitions/assets\"\n + \ },\n \"properties\": {\n \"$ref\": \"#/definitions/common_metadata\"\n + \ },\n \"collection\": {\n \"title\": \"Collection + ID\",\n \"description\": \"The ID of the STAC Collection this + Item references to.\",\n \"type\": \"string\"\n }\n + \ }\n }\n ]\n },\n \"link\": {\n \"type\": + \"object\",\n \"required\": [\n \"rel\",\n \"href\"\n ],\n + \ \"properties\": {\n \"href\": {\n \"title\": \"Link + reference\",\n \"type\": \"string\"\n },\n \"rel\": + {\n \"title\": \"Link relation type\",\n \"type\": \"string\"\n + \ },\n \"type\": {\n \"title\": \"Link type\",\n \"type\": + \"string\"\n },\n \"title\": {\n \"title\": \"Link + title\",\n \"type\": \"string\"\n },\n \"created\": + {\n \"$ref\": \"datetime.json#/definitions/created\"\n },\n + \ \"updated\": {\n \"$ref\": \"datetime.json#/definitions/updated\"\n + \ }\n }\n },\n \"assets\": {\n \"title\": \"Asset links\",\n + \ \"description\": \"Links to assets\",\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/asset\"\n + \ }\n },\n \"asset\": {\n \"type\": \"object\",\n \"required\": + [\n \"href\"\n ],\n \"properties\": {\n \"href\": + {\n \"title\": \"Asset reference\",\n \"type\": \"string\"\n + \ },\n \"title\": {\n \"title\": \"Asset title\",\n + \ \"type\": \"string\"\n },\n \"description\": {\n \"title\": + \"Asset description\",\n \"type\": \"string\"\n },\n \"type\": + {\n \"title\": \"Asset type\",\n \"type\": \"string\"\n + \ },\n \"roles\": {\n \"title\": \"Asset roles\",\n + \ \"type\": \"array\",\n \"items\": {\n \"type\": + \"string\"\n }\n }\n }\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - schemas.stacspec.org + method: GET + uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/basics.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/basics.json#\",\n + \ \"title\": \"Basic Descriptive Fields\",\n \"type\": \"object\",\n \"properties\": + {\n \"title\": {\n \"title\": \"Item Title\",\n \"description\": + \"A human-readable title describing the Item.\",\n \"type\": \"string\"\n + \ },\n \"description\": {\n \"title\": \"Item Description\",\n \"description\": + \"Detailed multi-line description to fully explain the Item.\",\n \"type\": + \"string\"\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - schemas.stacspec.org + method: GET + uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/datetime.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/datetime.json#\",\n + \ \"title\": \"Date and Time Fields\",\n \"type\": \"object\",\n \"allOf\": + [\n {\n \"properties\": {\n \"created\": {\n \"$ref\": + \"#/definitions/created\"\n },\n \"updated\": {\n \"$ref\": + \"#/definitions/updated\"\n }\n }\n },\n {\n \"anyOf\": + [\n {\n \"required\": [\n \"datetime\"\n ],\n + \ \"properties\": {\n \"datetime\": {\n \"$ref\": + \"#/definitions/datetime\"\n },\n \"start_datetime\": + {\n \"$ref\": \"#/definitions/start_datetime\"\n }, + \n \"end_datetime\": {\n \"$ref\": \"#/definitions/end_datetime\"\n + \ }\n },\n \"dependencies\": {\n \"start_datetime\": + {\n \"required\": [\n \"end_datetime\"\n ]\n + \ },\n \"end_datetime\": {\n \"required\": + [\n \"start_datetime\"\n ]\n }\n }\n + \ },\n {\n \"required\": [\n \"datetime\",\n + \ \"start_datetime\",\n \"end_datetime\"\n ],\n + \ \"properties\": {\n \"datetime\": {\n \"oneOf\": + [\n {\n \"$ref\": \"#/definitions/datetime\"\n + \ },\n {\n \"type\": [\"null\"],\n + \ \"const\": null\n }\n ]\n },\n + \ \"start_datetime\": {\n \"$ref\": \"#/definitions/start_datetime\"\n + \ }, \n \"end_datetime\": {\n \"$ref\": + \"#/definitions/end_datetime\"\n }\n }\n }\n ]\n + \ }\n ],\n \"definitions\": {\n \"datetime\": {\n \"title\": \"Date + and Time\",\n \"description\": \"The searchable date/time of the assets, + in UTC (Formatted in RFC 3339) \",\n \"type\": \"string\",\n \"format\": + \"date-time\"\n },\n \"start_datetime\": {\n \"title\": \"Start + Date and Time\",\n \"description\": \"The searchable start date/time + of the assets, in UTC (Formatted in RFC 3339) \",\n \"type\": \"string\",\n + \ \"format\": \"date-time\"\n }, \n \"end_datetime\": {\n \"title\": + \"End Date and Time\", \n \"description\": \"The searchable end date/time + of the assets, in UTC (Formatted in RFC 3339) \", \n \"type\": + \"string\",\n \"format\": \"date-time\"\n },\n \"created\": {\n + \ \"title\": \"Creation Time\",\n \"type\": \"string\",\n \"format\": + \"date-time\"\n },\n \"updated\": {\n \"title\": \"Last Update + Time\",\n \"type\": \"string\",\n \"format\": \"date-time\"\n }\n + \ }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - schemas.stacspec.org + method: GET + uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/instrument.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/instrument.json#\",\n + \ \"title\": \"Instrument Fields\",\n \"type\": \"object\",\n \"properties\": + {\n \"platform\": {\n \"title\": \"Platform\",\n \"type\": \"string\"\n + \ },\n \"instruments\": {\n \"title\": \"Instruments\",\n \"type\": + \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n + \ \"constellation\": {\n \"title\": \"Constellation\",\n \"type\": + \"string\"\n },\n \"mission\": {\n \"title\": \"Mission\",\n \"type\": + \"string\"\n },\n \"gsd\": {\n \"title\": \"Ground Sample Distance\",\n + \ \"type\": \"number\"\n }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - schemas.stacspec.org + method: GET + uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/licensing.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/licensing.json#\",\n + \ \"title\": \"Licensing Fields\",\n \"type\": \"object\",\n \"properties\": + {\n \"license\": {\n \"type\": \"string\",\n \"pattern\": \"^[\\\\w\\\\-\\\\.\\\\+]+$\"\n + \ }\n }\n}" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - schemas.stacspec.org + method: GET + uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/provider.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/provider.json#\",\n + \ \"title\": \"Provider Fields\",\n \"type\": \"object\",\n \"properties\": + {\n \"providers\": {\n \"title\": \"Providers\",\n \"type\": + \"array\",\n \"items\": {\n \"properties\": {\n \"name\": + {\n \"title\": \"Organization name\",\n \"type\": \"string\"\n + \ },\n \"description\": {\n \"title\": \"Organization + description\",\n \"type\": \"string\"\n },\n \"roles\": + {\n \"title\": \"Organization roles\",\n \"type\": \"array\",\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": + [\n \"producer\",\n \"licensor\",\n \"processor\",\n + \ \"host\"\n ]\n }\n },\n \"url\": + {\n \"title\": \"Organization homepage\",\n \"type\": + \"string\",\n \"format\": \"url\"\n }\n }\n }\n + \ }\n }\n}" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_stac_io/test_proj_json_schema_is_readable.yaml b/tests/v1/cassettes/test_stac_io/test_proj_json_schema_is_readable.yaml similarity index 100% rename from tests/cassettes/test_stac_io/test_proj_json_schema_is_readable.yaml rename to tests/v1/cassettes/test_stac_io/test_proj_json_schema_is_readable.yaml diff --git a/tests/cassettes/test_stac_io/test_retry_stac_io.yaml b/tests/v1/cassettes/test_stac_io/test_retry_stac_io.yaml similarity index 100% rename from tests/cassettes/test_stac_io/test_retry_stac_io.yaml rename to tests/v1/cassettes/test_stac_io/test_retry_stac_io.yaml diff --git a/tests/cassettes/test_stac_io/test_retry_stac_io_404.yaml b/tests/v1/cassettes/test_stac_io/test_retry_stac_io_404.yaml similarity index 100% rename from tests/cassettes/test_stac_io/test_retry_stac_io_404.yaml rename to tests/v1/cassettes/test_stac_io/test_retry_stac_io_404.yaml diff --git a/tests/cassettes/test_stac_io/test_urls_with_non_ascii_characters.yaml b/tests/v1/cassettes/test_stac_io/test_urls_with_non_ascii_characters.yaml similarity index 100% rename from tests/cassettes/test_stac_io/test_urls_with_non_ascii_characters.yaml rename to tests/v1/cassettes/test_stac_io/test_urls_with_non_ascii_characters.yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog0].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog0].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog0].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog0].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog1].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog1].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog1].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog1].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog3].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog3].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog3].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog3].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog4].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog4].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog4].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog4].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog5].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog5].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog5].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog5].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog6].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog6].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog6].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[ABSOLUTE_PUBLISHED-catalog6].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog0].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog0].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog0].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog0].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog1].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog1].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog1].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog1].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog3].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog3].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog3].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog3].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog4].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog4].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog4].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog4].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog5].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog5].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog5].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog5].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog6].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog6].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog6].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[RELATIVE_PUBLISHED-catalog6].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog0].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog0].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog0].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog0].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog1].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog1].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog1].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog1].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog3].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog3].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog3].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog3].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog4].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog4].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog4].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog4].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog5].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog5].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog5].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog5].yaml diff --git a/tests/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog6].yaml b/tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog6].yaml similarity index 100% rename from tests/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog6].yaml rename to tests/v1/cassettes/test_writing/TestSTACWriting.test_testcases[SELF_CONTAINED-catalog6].yaml diff --git a/tests/v1/conftest.py b/tests/v1/conftest.py new file mode 100644 index 000000000..4d65e6240 --- /dev/null +++ b/tests/v1/conftest.py @@ -0,0 +1,132 @@ +# 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 + +from pystac import Asset, Catalog, Collection, Item, ItemCollection, Link + +from .utils import ARBITRARY_BBOX, ARBITRARY_EXTENT, ARBITRARY_GEOM, TestCases + +HERE = Path(__file__).resolve().parent + + +def pytest_collection_modifyitems(config, items): + """Skip all tests in v1 unless they are marked with `passing_v2` flag""" + if config.getoption("--v1"): + return + skip_v1 = pytest.mark.skip(reason="need --v1 option to run") + for item in items: + if item.parent.path.is_relative_to(HERE) and "passing_v2" not in item.keywords: + item.add_marker(skip_v1) + + +@pytest.fixture +def catalog() -> Catalog: + return Catalog("test-catalog", "A test catalog") + + +@pytest.fixture +def collection() -> Catalog: + return Collection("test-collection", "A test collection", ARBITRARY_EXTENT) + + +@pytest.fixture +def self_link_collection() -> Catalog: + c = Collection("test-collection", "A test collection", ARBITRARY_EXTENT) + c.set_self_href("file:///a/real/url.json") + return c + + +@pytest.fixture +def multi_extent_collection() -> Collection: + # TODO this code is repeated many times; refactor to use this fixture + return Collection.from_file( + TestCases.get_path("data-files/collections/multi-extent.json") + ) + + +@pytest.fixture +def item() -> Item: + return Item("test-item", ARBITRARY_GEOM, ARBITRARY_BBOX, datetime.now(), {}) + + +@pytest.fixture +def asset(item: Item) -> Asset: + item.add_asset("foo", Asset("https://example.tif")) + return item.assets["foo"] + + +@pytest.fixture +def link(item: Item) -> Link: + item.add_link(Link(rel="child", target="https://example.tif")) + return item.links[0] + + +@pytest.fixture +def test_case_1_catalog() -> Catalog: + return TestCases.case_1() + + +@pytest.fixture +def test_case_8_collection() -> Collection: + return TestCases.case_8() + + +@pytest.fixture +def projection_landsat8_item() -> Item: + path = TestCases.get_path("data-files/projection/example-landsat8.json") + return Item.from_file(path) + + +def get_data_file(rel_path: str) -> str: + return str(HERE / "data-files" / rel_path) + + +@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")) + + +@pytest.fixture +def sample_item_collection() -> ItemCollection: + return ItemCollection.from_file( + TestCases.get_path("data-files/item-collection/sample-item-collection.json") + ) + + +@pytest.fixture +def sample_items(sample_item_collection: ItemCollection) -> list[Item]: + return list(sample_item_collection) + + +@pytest.fixture(scope="function") +def tmp_asset(tmp_path: Path) -> Asset: + """Copy the entirety of test-case-2 to tmp and""" + src = get_data_file("catalogs/test-case-2") + dst = str(tmp_path / str(uuid.uuid4())) + shutil.copytree(src, dst) + + catalog = Catalog.from_file(f"{dst}/catalog.json") + item = next(catalog.get_items(recursive=True)) + return next(v for v in item.assets.values()) + + +@pytest.fixture(autouse=True) +def clear_validator() -> None: + from pystac.validation import RegisteredValidator + + RegisteredValidator._validator = None diff --git a/tests/data-files/catalogs/cbers-partial/CBERS4AWFI/collection.json b/tests/v1/data-files/catalogs/cbers-partial/CBERS4AWFI/collection.json similarity index 100% rename from tests/data-files/catalogs/cbers-partial/CBERS4AWFI/collection.json rename to tests/v1/data-files/catalogs/cbers-partial/CBERS4AWFI/collection.json diff --git a/tests/data-files/catalogs/cbers-partial/CBERS4MUX/collection.json b/tests/v1/data-files/catalogs/cbers-partial/CBERS4MUX/collection.json similarity index 100% rename from tests/data-files/catalogs/cbers-partial/CBERS4MUX/collection.json rename to tests/v1/data-files/catalogs/cbers-partial/CBERS4MUX/collection.json diff --git a/tests/data-files/catalogs/cbers-partial/CBERS4PAN10M/collection.json b/tests/v1/data-files/catalogs/cbers-partial/CBERS4PAN10M/collection.json similarity index 100% rename from tests/data-files/catalogs/cbers-partial/CBERS4PAN10M/collection.json rename to tests/v1/data-files/catalogs/cbers-partial/CBERS4PAN10M/collection.json diff --git a/tests/data-files/catalogs/cbers-partial/CBERS4PAN5M/collection.json b/tests/v1/data-files/catalogs/cbers-partial/CBERS4PAN5M/collection.json similarity index 100% rename from tests/data-files/catalogs/cbers-partial/CBERS4PAN5M/collection.json rename to tests/v1/data-files/catalogs/cbers-partial/CBERS4PAN5M/collection.json diff --git a/tests/data-files/catalogs/cbers-partial/catalog.json b/tests/v1/data-files/catalogs/cbers-partial/catalog.json similarity index 100% rename from tests/data-files/catalogs/cbers-partial/catalog.json rename to tests/v1/data-files/catalogs/cbers-partial/catalog.json diff --git a/tests/data-files/catalogs/invalid-catalog/catalog.json b/tests/v1/data-files/catalogs/invalid-catalog/catalog.json similarity index 100% rename from tests/data-files/catalogs/invalid-catalog/catalog.json rename to tests/v1/data-files/catalogs/invalid-catalog/catalog.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/acc/665946-labels/665946-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/665946-labels/665946-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/acc/665946-labels/665946-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/665946-labels/665946-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/acc/665946/665946.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/665946/665946.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/acc/665946/665946.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/665946/665946.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/acc/a42435-labels/a42435-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/a42435-labels/a42435-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/acc/a42435-labels/a42435-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/a42435-labels/a42435-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/acc/a42435/a42435.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/a42435/a42435.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/acc/a42435/a42435.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/a42435/a42435.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/acc/ca041a-labels/ca041a-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/ca041a-labels/ca041a-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/acc/ca041a-labels/ca041a-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/ca041a-labels/ca041a-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/acc/ca041a/ca041a.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/ca041a/ca041a.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/acc/ca041a/ca041a.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/ca041a/ca041a.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/acc/collection.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/collection.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/acc/collection.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/collection.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/acc/d41d81-labels/d41d81-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/d41d81-labels/d41d81-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/acc/d41d81-labels/d41d81-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/d41d81-labels/d41d81-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/acc/d41d81/d41d81.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/d41d81/d41d81.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/acc/d41d81/d41d81.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/acc/d41d81/d41d81.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/catalog.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/catalog.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/catalog.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/catalog.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/dar/0a4c40-labels/0a4c40-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/0a4c40-labels/0a4c40-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/dar/0a4c40-labels/0a4c40-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/0a4c40-labels/0a4c40-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/dar/0a4c40/0a4c40.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/0a4c40/0a4c40.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/dar/0a4c40/0a4c40.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/0a4c40/0a4c40.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/dar/353093-labels/353093-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/353093-labels/353093-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/dar/353093-labels/353093-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/353093-labels/353093-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/dar/353093/353093.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/353093/353093.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/dar/353093/353093.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/353093/353093.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/dar/42f235-labels/42f235-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/42f235-labels/42f235-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/dar/42f235-labels/42f235-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/42f235-labels/42f235-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/dar/42f235/42f235.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/42f235/42f235.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/dar/42f235/42f235.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/42f235/42f235.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/dar/a017f9-labels/a017f9-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/a017f9-labels/a017f9-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/dar/a017f9-labels/a017f9-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/a017f9-labels/a017f9-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/dar/a017f9/a017f9.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/a017f9/a017f9.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/dar/a017f9/a017f9.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/a017f9/a017f9.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/dar/b15fce-labels/b15fce-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/b15fce-labels/b15fce-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/dar/b15fce-labels/b15fce-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/b15fce-labels/b15fce-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/dar/b15fce/b15fce.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/b15fce/b15fce.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/dar/b15fce/b15fce.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/b15fce/b15fce.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/dar/collection.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/collection.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/dar/collection.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/collection.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/dar/f883a0-labels/f883a0-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/f883a0-labels/f883a0-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/dar/f883a0-labels/f883a0-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/f883a0-labels/f883a0-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/dar/f883a0/f883a0.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/f883a0/f883a0.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/dar/f883a0/f883a0.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/dar/f883a0/f883a0.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/kam/4e7c7f-labels/4e7c7f-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/kam/4e7c7f-labels/4e7c7f-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/kam/4e7c7f-labels/4e7c7f-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/kam/4e7c7f-labels/4e7c7f-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/kam/4e7c7f/4e7c7f.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/kam/4e7c7f/4e7c7f.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/kam/4e7c7f/4e7c7f.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/kam/4e7c7f/4e7c7f.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/kam/collection.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/kam/collection.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/kam/collection.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/kam/collection.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/mon/207cc7-labels/207cc7-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/207cc7-labels/207cc7-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/mon/207cc7-labels/207cc7-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/207cc7-labels/207cc7-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/mon/207cc7/207cc7.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/207cc7/207cc7.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/mon/207cc7/207cc7.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/207cc7/207cc7.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/mon/401175-labels/401175-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/401175-labels/401175-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/mon/401175-labels/401175-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/401175-labels/401175-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/mon/401175/401175.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/401175/401175.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/mon/401175/401175.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/401175/401175.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/mon/493701-labels/493701-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/493701-labels/493701-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/mon/493701-labels/493701-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/493701-labels/493701-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/mon/493701/493701.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/493701/493701.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/mon/493701/493701.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/493701/493701.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/mon/collection.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/collection.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/mon/collection.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/collection.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/mon/f15272-labels/f15272-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/f15272-labels/f15272-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/mon/f15272-labels/f15272-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/f15272-labels/f15272-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/mon/f15272/f15272.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/f15272/f15272.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/mon/f15272/f15272.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/mon/f15272/f15272.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/nia/825a50-labels/825a50-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/nia/825a50-labels/825a50-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/nia/825a50-labels/825a50-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/nia/825a50-labels/825a50-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/nia/825a50/825a50.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/nia/825a50/825a50.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/nia/825a50/825a50.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/nia/825a50/825a50.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/nia/collection.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/nia/collection.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/nia/collection.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/nia/collection.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/ptn/abe1a3-labels/abe1a3-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/ptn/abe1a3-labels/abe1a3-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/ptn/abe1a3-labels/abe1a3-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/ptn/abe1a3-labels/abe1a3-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/ptn/abe1a3/abe1a3.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/ptn/abe1a3/abe1a3.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/ptn/abe1a3/abe1a3.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/ptn/abe1a3/abe1a3.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/ptn/collection.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/ptn/collection.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/ptn/collection.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/ptn/collection.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/ptn/f49f31-labels/f49f31-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/ptn/f49f31-labels/f49f31-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/ptn/f49f31-labels/f49f31-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/ptn/f49f31-labels/f49f31-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/ptn/f49f31/f49f31.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/ptn/f49f31/f49f31.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/ptn/f49f31/f49f31.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/ptn/f49f31/f49f31.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/06f252-labels/06f252-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/06f252-labels/06f252-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/06f252-labels/06f252-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/06f252-labels/06f252-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/06f252/06f252.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/06f252/06f252.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/06f252/06f252.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/06f252/06f252.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/076995-labels/076995-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/076995-labels/076995-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/076995-labels/076995-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/076995-labels/076995-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/076995/076995.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/076995/076995.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/076995/076995.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/076995/076995.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/33cae6-labels/33cae6-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/33cae6-labels/33cae6-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/33cae6-labels/33cae6-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/33cae6-labels/33cae6-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/33cae6/33cae6.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/33cae6/33cae6.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/33cae6/33cae6.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/33cae6/33cae6.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/3b20d4-labels/3b20d4-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/3b20d4-labels/3b20d4-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/3b20d4-labels/3b20d4-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/3b20d4-labels/3b20d4-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/3b20d4/3b20d4.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/3b20d4/3b20d4.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/3b20d4/3b20d4.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/3b20d4/3b20d4.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/3f8360-labels/3f8360-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/3f8360-labels/3f8360-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/3f8360-labels/3f8360-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/3f8360-labels/3f8360-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/3f8360/3f8360.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/3f8360/3f8360.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/3f8360/3f8360.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/3f8360/3f8360.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/425403-labels/425403-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/425403-labels/425403-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/425403-labels/425403-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/425403-labels/425403-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/425403/425403.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/425403/425403.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/425403/425403.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/425403/425403.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/75cdfa-labels/75cdfa-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/75cdfa-labels/75cdfa-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/75cdfa-labels/75cdfa-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/75cdfa-labels/75cdfa-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/75cdfa/75cdfa.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/75cdfa/75cdfa.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/75cdfa/75cdfa.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/75cdfa/75cdfa.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/9b8638-labels/9b8638-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/9b8638-labels/9b8638-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/9b8638-labels/9b8638-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/9b8638-labels/9b8638-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/9b8638/9b8638.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/9b8638/9b8638.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/9b8638/9b8638.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/9b8638/9b8638.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/aee7fd-labels/aee7fd-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/aee7fd-labels/aee7fd-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/aee7fd-labels/aee7fd-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/aee7fd-labels/aee7fd-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/aee7fd/aee7fd.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/aee7fd/aee7fd.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/aee7fd/aee7fd.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/aee7fd/aee7fd.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/bc32f1-labels/bc32f1-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/bc32f1-labels/bc32f1-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/bc32f1-labels/bc32f1-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/bc32f1-labels/bc32f1-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/bc32f1/bc32f1.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/bc32f1/bc32f1.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/bc32f1/bc32f1.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/bc32f1/bc32f1.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/bd5c14-labels/bd5c14-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/bd5c14-labels/bd5c14-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/bd5c14-labels/bd5c14-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/bd5c14-labels/bd5c14-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/bd5c14/bd5c14.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/bd5c14/bd5c14.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/bd5c14/bd5c14.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/bd5c14/bd5c14.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/c7415c-labels/c7415c-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/c7415c-labels/c7415c-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/c7415c-labels/c7415c-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/c7415c-labels/c7415c-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/c7415c/c7415c.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/c7415c/c7415c.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/c7415c/c7415c.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/c7415c/c7415c.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/collection.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/collection.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/collection.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/collection.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/e52478-labels/e52478-labels.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/e52478-labels/e52478-labels.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/e52478-labels/e52478-labels.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/e52478-labels/e52478-labels.json diff --git a/tests/data-files/catalogs/label_catalog-v0.8.1/znz/e52478/e52478.json b/tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/e52478/e52478.json similarity index 100% rename from tests/data-files/catalogs/label_catalog-v0.8.1/znz/e52478/e52478.json rename to tests/v1/data-files/catalogs/label_catalog-v0.8.1/znz/e52478/e52478.json diff --git a/tests/data-files/catalogs/planet-example-v1.0.0-beta.2/collection.json b/tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/collection.json similarity index 100% rename from tests/data-files/catalogs/planet-example-v1.0.0-beta.2/collection.json rename to tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/collection.json diff --git a/tests/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/catalog.json b/tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/catalog.json similarity index 100% rename from tests/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/catalog.json rename to tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/catalog.json diff --git a/tests/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/20170831_162740_ssc1d1/20170831_162740_ssc1d1.json b/tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/20170831_162740_ssc1d1/20170831_162740_ssc1d1.json similarity index 100% rename from tests/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/20170831_162740_ssc1d1/20170831_162740_ssc1d1.json rename to tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/20170831_162740_ssc1d1/20170831_162740_ssc1d1.json diff --git a/tests/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/20170831_172754_101c/20170831_172754_101c.json b/tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/20170831_172754_101c/20170831_172754_101c.json similarity index 100% rename from tests/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/20170831_172754_101c/20170831_172754_101c.json rename to tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/20170831_172754_101c/20170831_172754_101c.json diff --git a/tests/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/20170831_195425_SS02/20170831_195425_SS02.json b/tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/20170831_195425_SS02/20170831_195425_SS02.json similarity index 100% rename from tests/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/20170831_195425_SS02/20170831_195425_SS02.json rename to tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/20170831_195425_SS02/20170831_195425_SS02.json diff --git a/tests/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/2017831_195552_SS02/2017831_195552_SS02.json b/tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/2017831_195552_SS02/2017831_195552_SS02.json similarity index 100% rename from tests/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/2017831_195552_SS02/2017831_195552_SS02.json rename to tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/2017831_195552_SS02/2017831_195552_SS02.json diff --git a/tests/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/Houston-East-20170831-103f-100d-0f4f-RGB/Houston-East-20170831-103f-100d-0f4f-RGB.json b/tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/Houston-East-20170831-103f-100d-0f4f-RGB/Houston-East-20170831-103f-100d-0f4f-RGB.json similarity index 100% rename from tests/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/Houston-East-20170831-103f-100d-0f4f-RGB/Houston-East-20170831-103f-100d-0f4f-RGB.json rename to tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/Houston-East-20170831-103f-100d-0f4f-RGB/Houston-East-20170831-103f-100d-0f4f-RGB.json diff --git a/tests/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/catalog.json b/tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/catalog.json similarity index 100% rename from tests/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/catalog.json rename to tests/v1/data-files/catalogs/planet-example-v1.0.0-beta.2/hurricane-harvey/hurricane-harvey-0831/catalog.json diff --git a/tests/data-files/catalogs/test-case-1/catalog.json b/tests/v1/data-files/catalogs/test-case-1/catalog.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/catalog.json rename to tests/v1/data-files/catalogs/test-case-1/catalog.json diff --git a/tests/data-files/catalogs/test-case-1/country-1/area-1-1/area-1-1-imagery/area-1-1-imagery.json b/tests/v1/data-files/catalogs/test-case-1/country-1/area-1-1/area-1-1-imagery/area-1-1-imagery.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/country-1/area-1-1/area-1-1-imagery/area-1-1-imagery.json rename to tests/v1/data-files/catalogs/test-case-1/country-1/area-1-1/area-1-1-imagery/area-1-1-imagery.json diff --git a/tests/data-files/catalogs/test-case-1/country-1/area-1-1/area-1-1-labels/area-1-1-labels.json b/tests/v1/data-files/catalogs/test-case-1/country-1/area-1-1/area-1-1-labels/area-1-1-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/country-1/area-1-1/area-1-1-labels/area-1-1-labels.json rename to tests/v1/data-files/catalogs/test-case-1/country-1/area-1-1/area-1-1-labels/area-1-1-labels.json diff --git a/tests/data-files/catalogs/test-case-1/country-1/area-1-1/collection.json b/tests/v1/data-files/catalogs/test-case-1/country-1/area-1-1/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/country-1/area-1-1/collection.json rename to tests/v1/data-files/catalogs/test-case-1/country-1/area-1-1/collection.json diff --git a/tests/data-files/catalogs/test-case-1/country-1/area-1-2/area-1-2-imagery/area-1-2-imagery.json b/tests/v1/data-files/catalogs/test-case-1/country-1/area-1-2/area-1-2-imagery/area-1-2-imagery.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/country-1/area-1-2/area-1-2-imagery/area-1-2-imagery.json rename to tests/v1/data-files/catalogs/test-case-1/country-1/area-1-2/area-1-2-imagery/area-1-2-imagery.json diff --git a/tests/data-files/catalogs/test-case-1/country-1/area-1-2/area-1-2-labels/area-1-2-labels.json b/tests/v1/data-files/catalogs/test-case-1/country-1/area-1-2/area-1-2-labels/area-1-2-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/country-1/area-1-2/area-1-2-labels/area-1-2-labels.json rename to tests/v1/data-files/catalogs/test-case-1/country-1/area-1-2/area-1-2-labels/area-1-2-labels.json diff --git a/tests/data-files/catalogs/test-case-1/country-1/area-1-2/collection.json b/tests/v1/data-files/catalogs/test-case-1/country-1/area-1-2/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/country-1/area-1-2/collection.json rename to tests/v1/data-files/catalogs/test-case-1/country-1/area-1-2/collection.json diff --git a/tests/data-files/catalogs/test-case-1/country-1/catalog.json b/tests/v1/data-files/catalogs/test-case-1/country-1/catalog.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/country-1/catalog.json rename to tests/v1/data-files/catalogs/test-case-1/country-1/catalog.json diff --git a/tests/data-files/catalogs/test-case-1/country-2/area-2-1/area-2-1-imagery/area-2-1-imagery.json b/tests/v1/data-files/catalogs/test-case-1/country-2/area-2-1/area-2-1-imagery/area-2-1-imagery.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/country-2/area-2-1/area-2-1-imagery/area-2-1-imagery.json rename to tests/v1/data-files/catalogs/test-case-1/country-2/area-2-1/area-2-1-imagery/area-2-1-imagery.json diff --git a/tests/data-files/catalogs/test-case-1/country-2/area-2-1/area-2-1-labels/area-2-1-labels.json b/tests/v1/data-files/catalogs/test-case-1/country-2/area-2-1/area-2-1-labels/area-2-1-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/country-2/area-2-1/area-2-1-labels/area-2-1-labels.json rename to tests/v1/data-files/catalogs/test-case-1/country-2/area-2-1/area-2-1-labels/area-2-1-labels.json diff --git a/tests/data-files/catalogs/test-case-1/country-2/area-2-1/collection.json b/tests/v1/data-files/catalogs/test-case-1/country-2/area-2-1/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/country-2/area-2-1/collection.json rename to tests/v1/data-files/catalogs/test-case-1/country-2/area-2-1/collection.json diff --git a/tests/data-files/catalogs/test-case-1/country-2/area-2-2/area-2-2-imagery/area-2-2-imagery.json b/tests/v1/data-files/catalogs/test-case-1/country-2/area-2-2/area-2-2-imagery/area-2-2-imagery.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/country-2/area-2-2/area-2-2-imagery/area-2-2-imagery.json rename to tests/v1/data-files/catalogs/test-case-1/country-2/area-2-2/area-2-2-imagery/area-2-2-imagery.json diff --git a/tests/data-files/catalogs/test-case-1/country-2/area-2-2/area-2-2-labels/area-2-2-labels.json b/tests/v1/data-files/catalogs/test-case-1/country-2/area-2-2/area-2-2-labels/area-2-2-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/country-2/area-2-2/area-2-2-labels/area-2-2-labels.json rename to tests/v1/data-files/catalogs/test-case-1/country-2/area-2-2/area-2-2-labels/area-2-2-labels.json diff --git a/tests/data-files/catalogs/test-case-1/country-2/area-2-2/collection.json b/tests/v1/data-files/catalogs/test-case-1/country-2/area-2-2/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/country-2/area-2-2/collection.json rename to tests/v1/data-files/catalogs/test-case-1/country-2/area-2-2/collection.json diff --git a/tests/data-files/catalogs/test-case-1/country-2/catalog.json b/tests/v1/data-files/catalogs/test-case-1/country-2/catalog.json similarity index 100% rename from tests/data-files/catalogs/test-case-1/country-2/catalog.json rename to tests/v1/data-files/catalogs/test-case-1/country-2/catalog.json diff --git a/tests/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/5b922d42-9a77-4f79-a672-86096f7f849e/cf73ec1a-d790-4b59-b077-e101738571ed/cf73ec1a-d790-4b59-b077-e101738571ed.json b/tests/v1/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/5b922d42-9a77-4f79-a672-86096f7f849e/cf73ec1a-d790-4b59-b077-e101738571ed/cf73ec1a-d790-4b59-b077-e101738571ed.json similarity index 100% rename from tests/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/5b922d42-9a77-4f79-a672-86096f7f849e/cf73ec1a-d790-4b59-b077-e101738571ed/cf73ec1a-d790-4b59-b077-e101738571ed.json rename to tests/v1/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/5b922d42-9a77-4f79-a672-86096f7f849e/cf73ec1a-d790-4b59-b077-e101738571ed/cf73ec1a-d790-4b59-b077-e101738571ed.json diff --git a/tests/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/5b922d42-9a77-4f79-a672-86096f7f849e/cf73ec1a-d790-4b59-b077-e101738571ed/data.geojson b/tests/v1/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/5b922d42-9a77-4f79-a672-86096f7f849e/cf73ec1a-d790-4b59-b077-e101738571ed/data.geojson similarity index 100% rename from tests/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/5b922d42-9a77-4f79-a672-86096f7f849e/cf73ec1a-d790-4b59-b077-e101738571ed/data.geojson rename to tests/v1/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/5b922d42-9a77-4f79-a672-86096f7f849e/cf73ec1a-d790-4b59-b077-e101738571ed/data.geojson diff --git a/tests/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/5b922d42-9a77-4f79-a672-86096f7f849e/collection.json b/tests/v1/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/5b922d42-9a77-4f79-a672-86096f7f849e/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/5b922d42-9a77-4f79-a672-86096f7f849e/collection.json rename to tests/v1/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/5b922d42-9a77-4f79-a672-86096f7f849e/collection.json diff --git a/tests/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/collection.json b/tests/v1/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/collection.json rename to tests/v1/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/collection.json diff --git a/tests/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/f433578c-f879-414d-8101-83142a0a13c3/collection.json b/tests/v1/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/f433578c-f879-414d-8101-83142a0a13c3/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/f433578c-f879-414d-8101-83142a0a13c3/collection.json rename to tests/v1/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/f433578c-f879-414d-8101-83142a0a13c3/collection.json diff --git a/tests/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/f433578c-f879-414d-8101-83142a0a13c3/d43bead8-e3f8-4c51-95d6-e24e750a402b/d43bead8-e3f8-4c51-95d6-e24e750a402b.json b/tests/v1/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/f433578c-f879-414d-8101-83142a0a13c3/d43bead8-e3f8-4c51-95d6-e24e750a402b/d43bead8-e3f8-4c51-95d6-e24e750a402b.json similarity index 100% rename from tests/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/f433578c-f879-414d-8101-83142a0a13c3/d43bead8-e3f8-4c51-95d6-e24e750a402b/d43bead8-e3f8-4c51-95d6-e24e750a402b.json rename to tests/v1/data-files/catalogs/test-case-2/1a8c1632-fa91-4a62-b33e-3a87c2ebdf16/f433578c-f879-414d-8101-83142a0a13c3/d43bead8-e3f8-4c51-95d6-e24e750a402b/d43bead8-e3f8-4c51-95d6-e24e750a402b.json diff --git a/tests/data-files/catalogs/test-case-2/catalog.json b/tests/v1/data-files/catalogs/test-case-2/catalog.json similarity index 100% rename from tests/data-files/catalogs/test-case-2/catalog.json rename to tests/v1/data-files/catalogs/test-case-2/catalog.json diff --git a/tests/data-files/catalogs/test-case-4/acc/665946-labels/665946-labels.json b/tests/v1/data-files/catalogs/test-case-4/acc/665946-labels/665946-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/acc/665946-labels/665946-labels.json rename to tests/v1/data-files/catalogs/test-case-4/acc/665946-labels/665946-labels.json diff --git a/tests/data-files/catalogs/test-case-4/acc/665946/665946.json b/tests/v1/data-files/catalogs/test-case-4/acc/665946/665946.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/acc/665946/665946.json rename to tests/v1/data-files/catalogs/test-case-4/acc/665946/665946.json diff --git a/tests/data-files/catalogs/test-case-4/acc/a42435-labels/a42435-labels.json b/tests/v1/data-files/catalogs/test-case-4/acc/a42435-labels/a42435-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/acc/a42435-labels/a42435-labels.json rename to tests/v1/data-files/catalogs/test-case-4/acc/a42435-labels/a42435-labels.json diff --git a/tests/data-files/catalogs/test-case-4/acc/a42435/a42435.json b/tests/v1/data-files/catalogs/test-case-4/acc/a42435/a42435.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/acc/a42435/a42435.json rename to tests/v1/data-files/catalogs/test-case-4/acc/a42435/a42435.json diff --git a/tests/data-files/catalogs/test-case-4/acc/ca041a-labels/ca041a-labels.json b/tests/v1/data-files/catalogs/test-case-4/acc/ca041a-labels/ca041a-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/acc/ca041a-labels/ca041a-labels.json rename to tests/v1/data-files/catalogs/test-case-4/acc/ca041a-labels/ca041a-labels.json diff --git a/tests/data-files/catalogs/test-case-4/acc/ca041a/ca041a.json b/tests/v1/data-files/catalogs/test-case-4/acc/ca041a/ca041a.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/acc/ca041a/ca041a.json rename to tests/v1/data-files/catalogs/test-case-4/acc/ca041a/ca041a.json diff --git a/tests/data-files/catalogs/test-case-4/acc/collection.json b/tests/v1/data-files/catalogs/test-case-4/acc/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/acc/collection.json rename to tests/v1/data-files/catalogs/test-case-4/acc/collection.json diff --git a/tests/data-files/catalogs/test-case-4/acc/d41d81-labels/d41d81-labels.json b/tests/v1/data-files/catalogs/test-case-4/acc/d41d81-labels/d41d81-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/acc/d41d81-labels/d41d81-labels.json rename to tests/v1/data-files/catalogs/test-case-4/acc/d41d81-labels/d41d81-labels.json diff --git a/tests/data-files/catalogs/test-case-4/acc/d41d81/d41d81.json b/tests/v1/data-files/catalogs/test-case-4/acc/d41d81/d41d81.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/acc/d41d81/d41d81.json rename to tests/v1/data-files/catalogs/test-case-4/acc/d41d81/d41d81.json diff --git a/tests/data-files/catalogs/test-case-4/catalog.json b/tests/v1/data-files/catalogs/test-case-4/catalog.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/catalog.json rename to tests/v1/data-files/catalogs/test-case-4/catalog.json diff --git a/tests/data-files/catalogs/test-case-4/dar/0a4c40-labels/0a4c40-labels.json b/tests/v1/data-files/catalogs/test-case-4/dar/0a4c40-labels/0a4c40-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/dar/0a4c40-labels/0a4c40-labels.json rename to tests/v1/data-files/catalogs/test-case-4/dar/0a4c40-labels/0a4c40-labels.json diff --git a/tests/data-files/catalogs/test-case-4/dar/0a4c40/0a4c40.json b/tests/v1/data-files/catalogs/test-case-4/dar/0a4c40/0a4c40.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/dar/0a4c40/0a4c40.json rename to tests/v1/data-files/catalogs/test-case-4/dar/0a4c40/0a4c40.json diff --git a/tests/data-files/catalogs/test-case-4/dar/353093-labels/353093-labels.json b/tests/v1/data-files/catalogs/test-case-4/dar/353093-labels/353093-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/dar/353093-labels/353093-labels.json rename to tests/v1/data-files/catalogs/test-case-4/dar/353093-labels/353093-labels.json diff --git a/tests/data-files/catalogs/test-case-4/dar/353093/353093.json b/tests/v1/data-files/catalogs/test-case-4/dar/353093/353093.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/dar/353093/353093.json rename to tests/v1/data-files/catalogs/test-case-4/dar/353093/353093.json diff --git a/tests/data-files/catalogs/test-case-4/dar/42f235-labels/42f235-labels.json b/tests/v1/data-files/catalogs/test-case-4/dar/42f235-labels/42f235-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/dar/42f235-labels/42f235-labels.json rename to tests/v1/data-files/catalogs/test-case-4/dar/42f235-labels/42f235-labels.json diff --git a/tests/data-files/catalogs/test-case-4/dar/42f235/42f235.json b/tests/v1/data-files/catalogs/test-case-4/dar/42f235/42f235.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/dar/42f235/42f235.json rename to tests/v1/data-files/catalogs/test-case-4/dar/42f235/42f235.json diff --git a/tests/data-files/catalogs/test-case-4/dar/a017f9-labels/a017f9-labels.json b/tests/v1/data-files/catalogs/test-case-4/dar/a017f9-labels/a017f9-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/dar/a017f9-labels/a017f9-labels.json rename to tests/v1/data-files/catalogs/test-case-4/dar/a017f9-labels/a017f9-labels.json diff --git a/tests/data-files/catalogs/test-case-4/dar/a017f9/a017f9.json b/tests/v1/data-files/catalogs/test-case-4/dar/a017f9/a017f9.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/dar/a017f9/a017f9.json rename to tests/v1/data-files/catalogs/test-case-4/dar/a017f9/a017f9.json diff --git a/tests/data-files/catalogs/test-case-4/dar/b15fce-labels/b15fce-labels.json b/tests/v1/data-files/catalogs/test-case-4/dar/b15fce-labels/b15fce-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/dar/b15fce-labels/b15fce-labels.json rename to tests/v1/data-files/catalogs/test-case-4/dar/b15fce-labels/b15fce-labels.json diff --git a/tests/data-files/catalogs/test-case-4/dar/b15fce/b15fce.json b/tests/v1/data-files/catalogs/test-case-4/dar/b15fce/b15fce.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/dar/b15fce/b15fce.json rename to tests/v1/data-files/catalogs/test-case-4/dar/b15fce/b15fce.json diff --git a/tests/data-files/catalogs/test-case-4/dar/collection.json b/tests/v1/data-files/catalogs/test-case-4/dar/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/dar/collection.json rename to tests/v1/data-files/catalogs/test-case-4/dar/collection.json diff --git a/tests/data-files/catalogs/test-case-4/dar/f883a0-labels/f883a0-labels.json b/tests/v1/data-files/catalogs/test-case-4/dar/f883a0-labels/f883a0-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/dar/f883a0-labels/f883a0-labels.json rename to tests/v1/data-files/catalogs/test-case-4/dar/f883a0-labels/f883a0-labels.json diff --git a/tests/data-files/catalogs/test-case-4/dar/f883a0/f883a0.json b/tests/v1/data-files/catalogs/test-case-4/dar/f883a0/f883a0.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/dar/f883a0/f883a0.json rename to tests/v1/data-files/catalogs/test-case-4/dar/f883a0/f883a0.json diff --git a/tests/data-files/catalogs/test-case-4/kam/4e7c7f-labels/4e7c7f-labels.json b/tests/v1/data-files/catalogs/test-case-4/kam/4e7c7f-labels/4e7c7f-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/kam/4e7c7f-labels/4e7c7f-labels.json rename to tests/v1/data-files/catalogs/test-case-4/kam/4e7c7f-labels/4e7c7f-labels.json diff --git a/tests/data-files/catalogs/test-case-4/kam/4e7c7f/4e7c7f.json b/tests/v1/data-files/catalogs/test-case-4/kam/4e7c7f/4e7c7f.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/kam/4e7c7f/4e7c7f.json rename to tests/v1/data-files/catalogs/test-case-4/kam/4e7c7f/4e7c7f.json diff --git a/tests/data-files/catalogs/test-case-4/kam/collection.json b/tests/v1/data-files/catalogs/test-case-4/kam/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/kam/collection.json rename to tests/v1/data-files/catalogs/test-case-4/kam/collection.json diff --git a/tests/data-files/catalogs/test-case-4/mon/207cc7-labels/207cc7-labels.json b/tests/v1/data-files/catalogs/test-case-4/mon/207cc7-labels/207cc7-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/mon/207cc7-labels/207cc7-labels.json rename to tests/v1/data-files/catalogs/test-case-4/mon/207cc7-labels/207cc7-labels.json diff --git a/tests/data-files/catalogs/test-case-4/mon/207cc7/207cc7.json b/tests/v1/data-files/catalogs/test-case-4/mon/207cc7/207cc7.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/mon/207cc7/207cc7.json rename to tests/v1/data-files/catalogs/test-case-4/mon/207cc7/207cc7.json diff --git a/tests/data-files/catalogs/test-case-4/mon/401175-labels/401175-labels.json b/tests/v1/data-files/catalogs/test-case-4/mon/401175-labels/401175-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/mon/401175-labels/401175-labels.json rename to tests/v1/data-files/catalogs/test-case-4/mon/401175-labels/401175-labels.json diff --git a/tests/data-files/catalogs/test-case-4/mon/401175/401175.json b/tests/v1/data-files/catalogs/test-case-4/mon/401175/401175.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/mon/401175/401175.json rename to tests/v1/data-files/catalogs/test-case-4/mon/401175/401175.json diff --git a/tests/data-files/catalogs/test-case-4/mon/493701-labels/493701-labels.json b/tests/v1/data-files/catalogs/test-case-4/mon/493701-labels/493701-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/mon/493701-labels/493701-labels.json rename to tests/v1/data-files/catalogs/test-case-4/mon/493701-labels/493701-labels.json diff --git a/tests/data-files/catalogs/test-case-4/mon/493701/493701.json b/tests/v1/data-files/catalogs/test-case-4/mon/493701/493701.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/mon/493701/493701.json rename to tests/v1/data-files/catalogs/test-case-4/mon/493701/493701.json diff --git a/tests/data-files/catalogs/test-case-4/mon/collection.json b/tests/v1/data-files/catalogs/test-case-4/mon/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/mon/collection.json rename to tests/v1/data-files/catalogs/test-case-4/mon/collection.json diff --git a/tests/data-files/catalogs/test-case-4/mon/f15272-labels/f15272-labels.json b/tests/v1/data-files/catalogs/test-case-4/mon/f15272-labels/f15272-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/mon/f15272-labels/f15272-labels.json rename to tests/v1/data-files/catalogs/test-case-4/mon/f15272-labels/f15272-labels.json diff --git a/tests/data-files/catalogs/test-case-4/mon/f15272/f15272.json b/tests/v1/data-files/catalogs/test-case-4/mon/f15272/f15272.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/mon/f15272/f15272.json rename to tests/v1/data-files/catalogs/test-case-4/mon/f15272/f15272.json diff --git a/tests/data-files/catalogs/test-case-4/nia/825a50-labels/825a50-labels.json b/tests/v1/data-files/catalogs/test-case-4/nia/825a50-labels/825a50-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/nia/825a50-labels/825a50-labels.json rename to tests/v1/data-files/catalogs/test-case-4/nia/825a50-labels/825a50-labels.json diff --git a/tests/data-files/catalogs/test-case-4/nia/825a50/825a50.json b/tests/v1/data-files/catalogs/test-case-4/nia/825a50/825a50.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/nia/825a50/825a50.json rename to tests/v1/data-files/catalogs/test-case-4/nia/825a50/825a50.json diff --git a/tests/data-files/catalogs/test-case-4/nia/collection.json b/tests/v1/data-files/catalogs/test-case-4/nia/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/nia/collection.json rename to tests/v1/data-files/catalogs/test-case-4/nia/collection.json diff --git a/tests/data-files/catalogs/test-case-4/ptn/abe1a3-labels/abe1a3-labels.json b/tests/v1/data-files/catalogs/test-case-4/ptn/abe1a3-labels/abe1a3-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/ptn/abe1a3-labels/abe1a3-labels.json rename to tests/v1/data-files/catalogs/test-case-4/ptn/abe1a3-labels/abe1a3-labels.json diff --git a/tests/data-files/catalogs/test-case-4/ptn/abe1a3/abe1a3.json b/tests/v1/data-files/catalogs/test-case-4/ptn/abe1a3/abe1a3.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/ptn/abe1a3/abe1a3.json rename to tests/v1/data-files/catalogs/test-case-4/ptn/abe1a3/abe1a3.json diff --git a/tests/data-files/catalogs/test-case-4/ptn/collection.json b/tests/v1/data-files/catalogs/test-case-4/ptn/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/ptn/collection.json rename to tests/v1/data-files/catalogs/test-case-4/ptn/collection.json diff --git a/tests/data-files/catalogs/test-case-4/ptn/f49f31-labels/f49f31-labels.json b/tests/v1/data-files/catalogs/test-case-4/ptn/f49f31-labels/f49f31-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/ptn/f49f31-labels/f49f31-labels.json rename to tests/v1/data-files/catalogs/test-case-4/ptn/f49f31-labels/f49f31-labels.json diff --git a/tests/data-files/catalogs/test-case-4/ptn/f49f31/f49f31.json b/tests/v1/data-files/catalogs/test-case-4/ptn/f49f31/f49f31.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/ptn/f49f31/f49f31.json rename to tests/v1/data-files/catalogs/test-case-4/ptn/f49f31/f49f31.json diff --git a/tests/data-files/catalogs/test-case-4/znz/06f252-labels/06f252-labels.json b/tests/v1/data-files/catalogs/test-case-4/znz/06f252-labels/06f252-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/06f252-labels/06f252-labels.json rename to tests/v1/data-files/catalogs/test-case-4/znz/06f252-labels/06f252-labels.json diff --git a/tests/data-files/catalogs/test-case-4/znz/06f252/06f252.json b/tests/v1/data-files/catalogs/test-case-4/znz/06f252/06f252.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/06f252/06f252.json rename to tests/v1/data-files/catalogs/test-case-4/znz/06f252/06f252.json diff --git a/tests/data-files/catalogs/test-case-4/znz/076995-labels/076995-labels.json b/tests/v1/data-files/catalogs/test-case-4/znz/076995-labels/076995-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/076995-labels/076995-labels.json rename to tests/v1/data-files/catalogs/test-case-4/znz/076995-labels/076995-labels.json diff --git a/tests/data-files/catalogs/test-case-4/znz/076995/076995.json b/tests/v1/data-files/catalogs/test-case-4/znz/076995/076995.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/076995/076995.json rename to tests/v1/data-files/catalogs/test-case-4/znz/076995/076995.json diff --git a/tests/data-files/catalogs/test-case-4/znz/33cae6-labels/33cae6-labels.json b/tests/v1/data-files/catalogs/test-case-4/znz/33cae6-labels/33cae6-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/33cae6-labels/33cae6-labels.json rename to tests/v1/data-files/catalogs/test-case-4/znz/33cae6-labels/33cae6-labels.json diff --git a/tests/data-files/catalogs/test-case-4/znz/33cae6/33cae6.json b/tests/v1/data-files/catalogs/test-case-4/znz/33cae6/33cae6.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/33cae6/33cae6.json rename to tests/v1/data-files/catalogs/test-case-4/znz/33cae6/33cae6.json diff --git a/tests/data-files/catalogs/test-case-4/znz/3b20d4-labels/3b20d4-labels.json b/tests/v1/data-files/catalogs/test-case-4/znz/3b20d4-labels/3b20d4-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/3b20d4-labels/3b20d4-labels.json rename to tests/v1/data-files/catalogs/test-case-4/znz/3b20d4-labels/3b20d4-labels.json diff --git a/tests/data-files/catalogs/test-case-4/znz/3b20d4/3b20d4.json b/tests/v1/data-files/catalogs/test-case-4/znz/3b20d4/3b20d4.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/3b20d4/3b20d4.json rename to tests/v1/data-files/catalogs/test-case-4/znz/3b20d4/3b20d4.json diff --git a/tests/data-files/catalogs/test-case-4/znz/3f8360-labels/3f8360-labels.json b/tests/v1/data-files/catalogs/test-case-4/znz/3f8360-labels/3f8360-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/3f8360-labels/3f8360-labels.json rename to tests/v1/data-files/catalogs/test-case-4/znz/3f8360-labels/3f8360-labels.json diff --git a/tests/data-files/catalogs/test-case-4/znz/3f8360/3f8360.json b/tests/v1/data-files/catalogs/test-case-4/znz/3f8360/3f8360.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/3f8360/3f8360.json rename to tests/v1/data-files/catalogs/test-case-4/znz/3f8360/3f8360.json diff --git a/tests/data-files/catalogs/test-case-4/znz/425403-labels/425403-labels.json b/tests/v1/data-files/catalogs/test-case-4/znz/425403-labels/425403-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/425403-labels/425403-labels.json rename to tests/v1/data-files/catalogs/test-case-4/znz/425403-labels/425403-labels.json diff --git a/tests/data-files/catalogs/test-case-4/znz/425403/425403.json b/tests/v1/data-files/catalogs/test-case-4/znz/425403/425403.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/425403/425403.json rename to tests/v1/data-files/catalogs/test-case-4/znz/425403/425403.json diff --git a/tests/data-files/catalogs/test-case-4/znz/75cdfa-labels/75cdfa-labels.json b/tests/v1/data-files/catalogs/test-case-4/znz/75cdfa-labels/75cdfa-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/75cdfa-labels/75cdfa-labels.json rename to tests/v1/data-files/catalogs/test-case-4/znz/75cdfa-labels/75cdfa-labels.json diff --git a/tests/data-files/catalogs/test-case-4/znz/75cdfa/75cdfa.json b/tests/v1/data-files/catalogs/test-case-4/znz/75cdfa/75cdfa.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/75cdfa/75cdfa.json rename to tests/v1/data-files/catalogs/test-case-4/znz/75cdfa/75cdfa.json diff --git a/tests/data-files/catalogs/test-case-4/znz/9b8638-labels/9b8638-labels.json b/tests/v1/data-files/catalogs/test-case-4/znz/9b8638-labels/9b8638-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/9b8638-labels/9b8638-labels.json rename to tests/v1/data-files/catalogs/test-case-4/znz/9b8638-labels/9b8638-labels.json diff --git a/tests/data-files/catalogs/test-case-4/znz/9b8638/9b8638.json b/tests/v1/data-files/catalogs/test-case-4/znz/9b8638/9b8638.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/9b8638/9b8638.json rename to tests/v1/data-files/catalogs/test-case-4/znz/9b8638/9b8638.json diff --git a/tests/data-files/catalogs/test-case-4/znz/aee7fd-labels/aee7fd-labels.json b/tests/v1/data-files/catalogs/test-case-4/znz/aee7fd-labels/aee7fd-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/aee7fd-labels/aee7fd-labels.json rename to tests/v1/data-files/catalogs/test-case-4/znz/aee7fd-labels/aee7fd-labels.json diff --git a/tests/data-files/catalogs/test-case-4/znz/aee7fd/aee7fd.json b/tests/v1/data-files/catalogs/test-case-4/znz/aee7fd/aee7fd.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/aee7fd/aee7fd.json rename to tests/v1/data-files/catalogs/test-case-4/znz/aee7fd/aee7fd.json diff --git a/tests/data-files/catalogs/test-case-4/znz/bc32f1-labels/bc32f1-labels.json b/tests/v1/data-files/catalogs/test-case-4/znz/bc32f1-labels/bc32f1-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/bc32f1-labels/bc32f1-labels.json rename to tests/v1/data-files/catalogs/test-case-4/znz/bc32f1-labels/bc32f1-labels.json diff --git a/tests/data-files/catalogs/test-case-4/znz/bc32f1/bc32f1.json b/tests/v1/data-files/catalogs/test-case-4/znz/bc32f1/bc32f1.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/bc32f1/bc32f1.json rename to tests/v1/data-files/catalogs/test-case-4/znz/bc32f1/bc32f1.json diff --git a/tests/data-files/catalogs/test-case-4/znz/bd5c14-labels/bd5c14-labels.json b/tests/v1/data-files/catalogs/test-case-4/znz/bd5c14-labels/bd5c14-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/bd5c14-labels/bd5c14-labels.json rename to tests/v1/data-files/catalogs/test-case-4/znz/bd5c14-labels/bd5c14-labels.json diff --git a/tests/data-files/catalogs/test-case-4/znz/bd5c14/bd5c14.json b/tests/v1/data-files/catalogs/test-case-4/znz/bd5c14/bd5c14.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/bd5c14/bd5c14.json rename to tests/v1/data-files/catalogs/test-case-4/znz/bd5c14/bd5c14.json diff --git a/tests/data-files/catalogs/test-case-4/znz/c7415c-labels/c7415c-labels.json b/tests/v1/data-files/catalogs/test-case-4/znz/c7415c-labels/c7415c-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/c7415c-labels/c7415c-labels.json rename to tests/v1/data-files/catalogs/test-case-4/znz/c7415c-labels/c7415c-labels.json diff --git a/tests/data-files/catalogs/test-case-4/znz/c7415c/c7415c.json b/tests/v1/data-files/catalogs/test-case-4/znz/c7415c/c7415c.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/c7415c/c7415c.json rename to tests/v1/data-files/catalogs/test-case-4/znz/c7415c/c7415c.json diff --git a/tests/data-files/catalogs/test-case-4/znz/collection.json b/tests/v1/data-files/catalogs/test-case-4/znz/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/collection.json rename to tests/v1/data-files/catalogs/test-case-4/znz/collection.json diff --git a/tests/data-files/catalogs/test-case-4/znz/e52478-labels/e52478-labels.json b/tests/v1/data-files/catalogs/test-case-4/znz/e52478-labels/e52478-labels.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/e52478-labels/e52478-labels.json rename to tests/v1/data-files/catalogs/test-case-4/znz/e52478-labels/e52478-labels.json diff --git a/tests/data-files/catalogs/test-case-4/znz/e52478/e52478.json b/tests/v1/data-files/catalogs/test-case-4/znz/e52478/e52478.json similarity index 100% rename from tests/data-files/catalogs/test-case-4/znz/e52478/e52478.json rename to tests/v1/data-files/catalogs/test-case-4/znz/e52478/e52478.json diff --git a/tests/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/CBERS4-MUX-027/069/CBERS_4_MUX_20190510_027_069_L2/CBERS_4_MUX_20190510_027_069_L2.json b/tests/v1/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/CBERS4-MUX-027/069/CBERS_4_MUX_20190510_027_069_L2/CBERS_4_MUX_20190510_027_069_L2.json similarity index 100% rename from tests/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/CBERS4-MUX-027/069/CBERS_4_MUX_20190510_027_069_L2/CBERS_4_MUX_20190510_027_069_L2.json rename to tests/v1/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/CBERS4-MUX-027/069/CBERS_4_MUX_20190510_027_069_L2/CBERS_4_MUX_20190510_027_069_L2.json diff --git a/tests/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/CBERS4-MUX-027/069/catalog.json b/tests/v1/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/CBERS4-MUX-027/069/catalog.json similarity index 100% rename from tests/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/CBERS4-MUX-027/069/catalog.json rename to tests/v1/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/CBERS4-MUX-027/069/catalog.json diff --git a/tests/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/CBERS4-MUX-027/collection.json b/tests/v1/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/CBERS4-MUX-027/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/CBERS4-MUX-027/collection.json rename to tests/v1/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/CBERS4-MUX-027/collection.json diff --git a/tests/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/catalog.json b/tests/v1/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/catalog.json similarity index 100% rename from tests/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/catalog.json rename to tests/v1/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/CBERS4-MUX-027/catalog.json diff --git a/tests/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/collection.json b/tests/v1/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/collection.json rename to tests/v1/data-files/catalogs/test-case-5/CBERS4/CBERS4MUX/collection.json diff --git a/tests/data-files/catalogs/test-case-5/CBERS4/catalog.json b/tests/v1/data-files/catalogs/test-case-5/CBERS4/catalog.json similarity index 100% rename from tests/data-files/catalogs/test-case-5/CBERS4/catalog.json rename to tests/v1/data-files/catalogs/test-case-5/CBERS4/catalog.json diff --git a/tests/data-files/catalogs/test-case-5/catalog.json b/tests/v1/data-files/catalogs/test-case-5/catalog.json similarity index 100% rename from tests/data-files/catalogs/test-case-5/catalog.json rename to tests/v1/data-files/catalogs/test-case-5/catalog.json diff --git a/tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/400c22e3-5b54-438b-b600-5e9bd6d0a498/3c67b59c-2e6f-47fb-ba3c-0dd106941096.json b/tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/400c22e3-5b54-438b-b600-5e9bd6d0a498/3c67b59c-2e6f-47fb-ba3c-0dd106941096.json similarity index 100% rename from tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/400c22e3-5b54-438b-b600-5e9bd6d0a498/3c67b59c-2e6f-47fb-ba3c-0dd106941096.json rename to tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/400c22e3-5b54-438b-b600-5e9bd6d0a498/3c67b59c-2e6f-47fb-ba3c-0dd106941096.json diff --git a/tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/400c22e3-5b54-438b-b600-5e9bd6d0a498/collection.json b/tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/400c22e3-5b54-438b-b600-5e9bd6d0a498/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/400c22e3-5b54-438b-b600-5e9bd6d0a498/collection.json rename to tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/400c22e3-5b54-438b-b600-5e9bd6d0a498/collection.json diff --git a/tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/400c22e3-5b54-438b-b600-5e9bd6d0a498/data.geojson b/tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/400c22e3-5b54-438b-b600-5e9bd6d0a498/data.geojson similarity index 100% rename from tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/400c22e3-5b54-438b-b600-5e9bd6d0a498/data.geojson rename to tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/400c22e3-5b54-438b-b600-5e9bd6d0a498/data.geojson diff --git a/tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/3bfb5bd3-5740-4ed0-92c9-968cc532dbc5.json b/tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/3bfb5bd3-5740-4ed0-92c9-968cc532dbc5.json similarity index 100% rename from tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/3bfb5bd3-5740-4ed0-92c9-968cc532dbc5.json rename to tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/3bfb5bd3-5740-4ed0-92c9-968cc532dbc5.json diff --git a/tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/41c57cea-50ba-495c-9ee7-17ddba837380.json b/tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/41c57cea-50ba-495c-9ee7-17ddba837380.json similarity index 100% rename from tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/41c57cea-50ba-495c-9ee7-17ddba837380.json rename to tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/41c57cea-50ba-495c-9ee7-17ddba837380.json diff --git a/tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/4514bbc6-cd17-4c14-816d-1709dfc61079.json b/tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/4514bbc6-cd17-4c14-816d-1709dfc61079.json similarity index 100% rename from tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/4514bbc6-cd17-4c14-816d-1709dfc61079.json rename to tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/4514bbc6-cd17-4c14-816d-1709dfc61079.json diff --git a/tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/52072b95-0275-4255-be5e-c567006f80cb.json b/tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/52072b95-0275-4255-be5e-c567006f80cb.json similarity index 100% rename from tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/52072b95-0275-4255-be5e-c567006f80cb.json rename to tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/52072b95-0275-4255-be5e-c567006f80cb.json diff --git a/tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/84fe42c0-9d23-404a-bd0f-1406112cca8c.json b/tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/84fe42c0-9d23-404a-bd0f-1406112cca8c.json similarity index 100% rename from tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/84fe42c0-9d23-404a-bd0f-1406112cca8c.json rename to tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/84fe42c0-9d23-404a-bd0f-1406112cca8c.json diff --git a/tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/884a939d-1d73-4351-9601-f9ee89a980b0.json b/tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/884a939d-1d73-4351-9601-f9ee89a980b0.json similarity index 100% rename from tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/884a939d-1d73-4351-9601-f9ee89a980b0.json rename to tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/884a939d-1d73-4351-9601-f9ee89a980b0.json diff --git a/tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/a6bd2ff9-3805-40ab-96fa-b7f112b18973.json b/tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/a6bd2ff9-3805-40ab-96fa-b7f112b18973.json similarity index 100% rename from tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/a6bd2ff9-3805-40ab-96fa-b7f112b18973.json rename to tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/a6bd2ff9-3805-40ab-96fa-b7f112b18973.json diff --git a/tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/collection.json b/tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/collection.json rename to tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/collection.json diff --git a/tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/e40bb30d-0295-42ed-ba04-6cf563b057f9.json b/tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/e40bb30d-0295-42ed-ba04-6cf563b057f9.json similarity index 100% rename from tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/e40bb30d-0295-42ed-ba04-6cf563b057f9.json rename to tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/e40bb30d-0295-42ed-ba04-6cf563b057f9.json diff --git a/tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/eb1124cc-3b45-4487-bc2c-4855ac877ad9.json b/tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/eb1124cc-3b45-4487-bc2c-4855ac877ad9.json similarity index 100% rename from tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/eb1124cc-3b45-4487-bc2c-4855ac877ad9.json rename to tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/9420e0fd-3731-40e8-93ff-5b6ad0067048/eb1124cc-3b45-4487-bc2c-4855ac877ad9.json diff --git a/tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/collection.json b/tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/collection.json similarity index 100% rename from tests/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/collection.json rename to tests/v1/data-files/catalogs/test-case-6/12cb17a8-ae08-469c-a2be-4d0619240014/collection.json diff --git a/tests/data-files/catalogs/test-case-6/catalog.json b/tests/v1/data-files/catalogs/test-case-6/catalog.json similarity index 100% rename from tests/data-files/catalogs/test-case-6/catalog.json rename to tests/v1/data-files/catalogs/test-case-6/catalog.json diff --git a/tests/data-files/change_stac_version.py b/tests/v1/data-files/change_stac_version.py similarity index 91% rename from tests/data-files/change_stac_version.py rename to tests/v1/data-files/change_stac_version.py index 5a5346657..d2971b393 100644 --- a/tests/data-files/change_stac_version.py +++ b/tests/v1/data-files/change_stac_version.py @@ -24,11 +24,7 @@ def migrate(path: str) -> None: if "stac_version" in stac_json: cur_ver = stac_json["stac_version"] if not cur_ver == TARGET_VERSION: - print( - " - Migrating {} from {} to {}...".format( - path, cur_ver, TARGET_VERSION - ) - ) + print(f" - Migrating {path} from {cur_ver} to {TARGET_VERSION}...") obj = pystac.read_dict(stac_json, href=path) migrated = obj.to_dict(include_self_link=False) with open(path, "w") as f: diff --git a/tests/data-files/classification/classification_landsat_example.json b/tests/v1/data-files/classification/classification_landsat_example.json similarity index 100% rename from tests/data-files/classification/classification_landsat_example.json rename to tests/v1/data-files/classification/classification_landsat_example.json diff --git a/tests/data-files/classification/collection-item-assets-raster-bands.json b/tests/v1/data-files/classification/collection-item-assets-raster-bands.json similarity index 100% rename from tests/data-files/classification/collection-item-assets-raster-bands.json rename to tests/v1/data-files/classification/collection-item-assets-raster-bands.json diff --git a/tests/data-files/collections/multi-extent.json b/tests/v1/data-files/collections/multi-extent.json similarity index 100% rename from tests/data-files/collections/multi-extent.json rename to tests/v1/data-files/collections/multi-extent.json diff --git a/tests/data-files/collections/with-assets.json b/tests/v1/data-files/collections/with-assets.json similarity index 100% rename from tests/data-files/collections/with-assets.json rename to tests/v1/data-files/collections/with-assets.json diff --git a/tests/data-files/datacube/item.json b/tests/v1/data-files/datacube/item.json similarity index 100% rename from tests/data-files/datacube/item.json rename to tests/v1/data-files/datacube/item.json diff --git a/tests/data-files/eo/eo-collection.json b/tests/v1/data-files/eo/eo-collection.json similarity index 100% rename from tests/data-files/eo/eo-collection.json rename to tests/v1/data-files/eo/eo-collection.json diff --git a/tests/data-files/eo/eo-landsat-example.json b/tests/v1/data-files/eo/eo-landsat-example.json similarity index 100% rename from tests/data-files/eo/eo-landsat-example.json rename to tests/v1/data-files/eo/eo-landsat-example.json diff --git a/tests/data-files/eo/eo-sentinel2-item.json b/tests/v1/data-files/eo/eo-sentinel2-item.json similarity index 100% rename from tests/data-files/eo/eo-sentinel2-item.json rename to tests/v1/data-files/eo/eo-sentinel2-item.json diff --git a/tests/data-files/eo/sample-bands-in-item-properties.json b/tests/v1/data-files/eo/sample-bands-in-item-properties.json similarity index 100% rename from tests/data-files/eo/sample-bands-in-item-properties.json rename to tests/v1/data-files/eo/sample-bands-in-item-properties.json diff --git a/tests/data-files/examples/0.8.1/catalog-spec/examples/catalog.json b/tests/v1/data-files/examples/0.8.1/catalog-spec/examples/catalog.json similarity index 100% rename from tests/data-files/examples/0.8.1/catalog-spec/examples/catalog.json rename to tests/v1/data-files/examples/0.8.1/catalog-spec/examples/catalog.json diff --git a/tests/data-files/examples/0.8.1/catalog-spec/examples/summaries-s2.json b/tests/v1/data-files/examples/0.8.1/catalog-spec/examples/summaries-s2.json similarity index 100% rename from tests/data-files/examples/0.8.1/catalog-spec/examples/summaries-s2.json rename to tests/v1/data-files/examples/0.8.1/catalog-spec/examples/summaries-s2.json diff --git a/tests/data-files/examples/0.8.1/collection-spec/examples/landsat-collection.json b/tests/v1/data-files/examples/0.8.1/collection-spec/examples/landsat-collection.json similarity index 100% rename from tests/data-files/examples/0.8.1/collection-spec/examples/landsat-collection.json rename to tests/v1/data-files/examples/0.8.1/collection-spec/examples/landsat-collection.json diff --git a/tests/data-files/examples/0.8.1/collection-spec/examples/landsat-item.json b/tests/v1/data-files/examples/0.8.1/collection-spec/examples/landsat-item.json similarity index 100% rename from tests/data-files/examples/0.8.1/collection-spec/examples/landsat-item.json rename to tests/v1/data-files/examples/0.8.1/collection-spec/examples/landsat-item.json diff --git a/tests/data-files/examples/0.8.1/collection-spec/examples/sentinel2.json b/tests/v1/data-files/examples/0.8.1/collection-spec/examples/sentinel2.json similarity index 100% rename from tests/data-files/examples/0.8.1/collection-spec/examples/sentinel2.json rename to tests/v1/data-files/examples/0.8.1/collection-spec/examples/sentinel2.json diff --git a/tests/data-files/examples/0.8.1/extensions/asset/examples/example-landsat8.json b/tests/v1/data-files/examples/0.8.1/extensions/asset/examples/example-landsat8.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/asset/examples/example-landsat8.json rename to tests/v1/data-files/examples/0.8.1/extensions/asset/examples/example-landsat8.json diff --git a/tests/data-files/examples/0.8.1/extensions/checksum/examples/example-sentinel1.json b/tests/v1/data-files/examples/0.8.1/extensions/checksum/examples/example-sentinel1.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/checksum/examples/example-sentinel1.json rename to tests/v1/data-files/examples/0.8.1/extensions/checksum/examples/example-sentinel1.json diff --git a/tests/data-files/examples/0.8.1/extensions/datacube/examples/example.json b/tests/v1/data-files/examples/0.8.1/extensions/datacube/examples/example.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/datacube/examples/example.json rename to tests/v1/data-files/examples/0.8.1/extensions/datacube/examples/example.json diff --git a/tests/data-files/examples/0.8.1/extensions/datetime-range/examples/example-video.json b/tests/v1/data-files/examples/0.8.1/extensions/datetime-range/examples/example-video.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/datetime-range/examples/example-video.json rename to tests/v1/data-files/examples/0.8.1/extensions/datetime-range/examples/example-video.json diff --git a/tests/data-files/examples/0.8.1/extensions/eo/examples/example-landsat8.json b/tests/v1/data-files/examples/0.8.1/extensions/eo/examples/example-landsat8.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/eo/examples/example-landsat8.json rename to tests/v1/data-files/examples/0.8.1/extensions/eo/examples/example-landsat8.json diff --git a/tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/catalog.json b/tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/catalog.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/catalog.json rename to tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/catalog.json diff --git a/tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/AOI_2_Vegas_img2636.json b/tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/AOI_2_Vegas_img2636.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/AOI_2_Vegas_img2636.json rename to tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/AOI_2_Vegas_img2636.json diff --git a/tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/AOI_3_Paris_img1648.json b/tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/AOI_3_Paris_img1648.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/AOI_3_Paris_img1648.json rename to tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/AOI_3_Paris_img1648.json diff --git a/tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/AOI_4_Shanghai_img3344.json b/tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/AOI_4_Shanghai_img3344.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/AOI_4_Shanghai_img3344.json rename to tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/AOI_4_Shanghai_img3344.json diff --git a/tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/collection.json b/tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/collection.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/collection.json rename to tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/spacenet-buildings/collection.json diff --git a/tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/zanzibar/collection.json b/tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/zanzibar/collection.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/zanzibar/collection.json rename to tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/zanzibar/collection.json diff --git a/tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/zanzibar/znz001.json b/tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/zanzibar/znz001.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/zanzibar/znz001.json rename to tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/zanzibar/znz001.json diff --git a/tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/zanzibar/znz029.json b/tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/zanzibar/znz029.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/label/examples/multidataset/zanzibar/znz029.json rename to tests/v1/data-files/examples/0.8.1/extensions/label/examples/multidataset/zanzibar/znz029.json diff --git a/tests/data-files/examples/0.8.1/extensions/label/examples/spacenet-roads/roads_collection.json b/tests/v1/data-files/examples/0.8.1/extensions/label/examples/spacenet-roads/roads_collection.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/label/examples/spacenet-roads/roads_collection.json rename to tests/v1/data-files/examples/0.8.1/extensions/label/examples/spacenet-roads/roads_collection.json diff --git a/tests/data-files/examples/0.8.1/extensions/label/examples/spacenet-roads/roads_item.json b/tests/v1/data-files/examples/0.8.1/extensions/label/examples/spacenet-roads/roads_item.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/label/examples/spacenet-roads/roads_item.json rename to tests/v1/data-files/examples/0.8.1/extensions/label/examples/spacenet-roads/roads_item.json diff --git a/tests/data-files/examples/0.8.1/extensions/label/examples/spacenet-roads/roads_source.json b/tests/v1/data-files/examples/0.8.1/extensions/label/examples/spacenet-roads/roads_source.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/label/examples/spacenet-roads/roads_source.json rename to tests/v1/data-files/examples/0.8.1/extensions/label/examples/spacenet-roads/roads_source.json diff --git a/tests/data-files/examples/0.8.1/extensions/pointcloud/examples/example-autzen.json b/tests/v1/data-files/examples/0.8.1/extensions/pointcloud/examples/example-autzen.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/pointcloud/examples/example-autzen.json rename to tests/v1/data-files/examples/0.8.1/extensions/pointcloud/examples/example-autzen.json diff --git a/tests/data-files/examples/0.8.1/extensions/sar/examples/envisat.json b/tests/v1/data-files/examples/0.8.1/extensions/sar/examples/envisat.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/sar/examples/envisat.json rename to tests/v1/data-files/examples/0.8.1/extensions/sar/examples/envisat.json diff --git a/tests/data-files/examples/0.8.1/extensions/sar/examples/sentinel1.json b/tests/v1/data-files/examples/0.8.1/extensions/sar/examples/sentinel1.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/sar/examples/sentinel1.json rename to tests/v1/data-files/examples/0.8.1/extensions/sar/examples/sentinel1.json diff --git a/tests/data-files/examples/0.8.1/extensions/scientific/examples/collection.json b/tests/v1/data-files/examples/0.8.1/extensions/scientific/examples/collection.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/scientific/examples/collection.json rename to tests/v1/data-files/examples/0.8.1/extensions/scientific/examples/collection.json diff --git a/tests/data-files/examples/0.8.1/extensions/scientific/examples/item.json b/tests/v1/data-files/examples/0.8.1/extensions/scientific/examples/item.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/scientific/examples/item.json rename to tests/v1/data-files/examples/0.8.1/extensions/scientific/examples/item.json diff --git a/tests/data-files/examples/0.8.1/extensions/single-file-stac/examples/example-search.json b/tests/v1/data-files/examples/0.8.1/extensions/single-file-stac/examples/example-search.json similarity index 100% rename from tests/data-files/examples/0.8.1/extensions/single-file-stac/examples/example-search.json rename to tests/v1/data-files/examples/0.8.1/extensions/single-file-stac/examples/example-search.json diff --git a/tests/data-files/examples/0.8.1/item-spec/examples/digitalglobe-sample.json b/tests/v1/data-files/examples/0.8.1/item-spec/examples/digitalglobe-sample.json similarity index 100% rename from tests/data-files/examples/0.8.1/item-spec/examples/digitalglobe-sample.json rename to tests/v1/data-files/examples/0.8.1/item-spec/examples/digitalglobe-sample.json diff --git a/tests/data-files/examples/0.8.1/item-spec/examples/itemcollection-sample-full.json b/tests/v1/data-files/examples/0.8.1/item-spec/examples/itemcollection-sample-full.json similarity index 100% rename from tests/data-files/examples/0.8.1/item-spec/examples/itemcollection-sample-full.json rename to tests/v1/data-files/examples/0.8.1/item-spec/examples/itemcollection-sample-full.json diff --git a/tests/data-files/examples/0.8.1/item-spec/examples/itemcollection-sample-minimal.json b/tests/v1/data-files/examples/0.8.1/item-spec/examples/itemcollection-sample-minimal.json similarity index 100% rename from tests/data-files/examples/0.8.1/item-spec/examples/itemcollection-sample-minimal.json rename to tests/v1/data-files/examples/0.8.1/item-spec/examples/itemcollection-sample-minimal.json diff --git a/tests/data-files/examples/0.8.1/item-spec/examples/landsat8-sample.json b/tests/v1/data-files/examples/0.8.1/item-spec/examples/landsat8-sample.json similarity index 100% rename from tests/data-files/examples/0.8.1/item-spec/examples/landsat8-sample.json rename to tests/v1/data-files/examples/0.8.1/item-spec/examples/landsat8-sample.json diff --git a/tests/data-files/examples/0.8.1/item-spec/examples/planet-sample.json b/tests/v1/data-files/examples/0.8.1/item-spec/examples/planet-sample.json similarity index 100% rename from tests/data-files/examples/0.8.1/item-spec/examples/planet-sample.json rename to tests/v1/data-files/examples/0.8.1/item-spec/examples/planet-sample.json diff --git a/tests/data-files/examples/0.8.1/item-spec/examples/sample-full.json b/tests/v1/data-files/examples/0.8.1/item-spec/examples/sample-full.json similarity index 100% rename from tests/data-files/examples/0.8.1/item-spec/examples/sample-full.json rename to tests/v1/data-files/examples/0.8.1/item-spec/examples/sample-full.json diff --git a/tests/data-files/examples/0.8.1/item-spec/examples/sample.json b/tests/v1/data-files/examples/0.8.1/item-spec/examples/sample.json similarity index 100% rename from tests/data-files/examples/0.8.1/item-spec/examples/sample.json rename to tests/v1/data-files/examples/0.8.1/item-spec/examples/sample.json diff --git a/tests/data-files/examples/0.8.1/item-spec/examples/sentinel-s2-l1c-collection.json b/tests/v1/data-files/examples/0.8.1/item-spec/examples/sentinel-s2-l1c-collection.json similarity index 100% rename from tests/data-files/examples/0.8.1/item-spec/examples/sentinel-s2-l1c-collection.json rename to tests/v1/data-files/examples/0.8.1/item-spec/examples/sentinel-s2-l1c-collection.json diff --git a/tests/data-files/examples/0.8.1/item-spec/examples/sentinel2-sample.json b/tests/v1/data-files/examples/0.8.1/item-spec/examples/sentinel2-sample.json similarity index 100% rename from tests/data-files/examples/0.8.1/item-spec/examples/sentinel2-sample.json rename to tests/v1/data-files/examples/0.8.1/item-spec/examples/sentinel2-sample.json diff --git a/tests/data-files/examples/0.9.0/catalog-spec/examples/catalog.json b/tests/v1/data-files/examples/0.9.0/catalog-spec/examples/catalog.json similarity index 100% rename from tests/data-files/examples/0.9.0/catalog-spec/examples/catalog.json rename to tests/v1/data-files/examples/0.9.0/catalog-spec/examples/catalog.json diff --git a/tests/data-files/examples/0.9.0/collection-spec/examples/landsat-collection.json b/tests/v1/data-files/examples/0.9.0/collection-spec/examples/landsat-collection.json similarity index 100% rename from tests/data-files/examples/0.9.0/collection-spec/examples/landsat-collection.json rename to tests/v1/data-files/examples/0.9.0/collection-spec/examples/landsat-collection.json diff --git a/tests/data-files/examples/0.9.0/collection-spec/examples/landsat-item.json b/tests/v1/data-files/examples/0.9.0/collection-spec/examples/landsat-item.json similarity index 100% rename from tests/data-files/examples/0.9.0/collection-spec/examples/landsat-item.json rename to tests/v1/data-files/examples/0.9.0/collection-spec/examples/landsat-item.json diff --git a/tests/data-files/examples/0.9.0/collection-spec/examples/sentinel2.json b/tests/v1/data-files/examples/0.9.0/collection-spec/examples/sentinel2.json similarity index 100% rename from tests/data-files/examples/0.9.0/collection-spec/examples/sentinel2.json rename to tests/v1/data-files/examples/0.9.0/collection-spec/examples/sentinel2.json diff --git a/tests/data-files/examples/0.9.0/extensions/asset/examples/example-landsat8.json b/tests/v1/data-files/examples/0.9.0/extensions/asset/examples/example-landsat8.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/asset/examples/example-landsat8.json rename to tests/v1/data-files/examples/0.9.0/extensions/asset/examples/example-landsat8.json diff --git a/tests/data-files/examples/0.9.0/extensions/checksum/examples/sentinel1.json b/tests/v1/data-files/examples/0.9.0/extensions/checksum/examples/sentinel1.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/checksum/examples/sentinel1.json rename to tests/v1/data-files/examples/0.9.0/extensions/checksum/examples/sentinel1.json diff --git a/tests/data-files/examples/0.9.0/extensions/commons/examples/landsat-collection.json b/tests/v1/data-files/examples/0.9.0/extensions/commons/examples/landsat-collection.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/commons/examples/landsat-collection.json rename to tests/v1/data-files/examples/0.9.0/extensions/commons/examples/landsat-collection.json diff --git a/tests/data-files/examples/0.9.0/extensions/commons/examples/landsat-item.json b/tests/v1/data-files/examples/0.9.0/extensions/commons/examples/landsat-item.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/commons/examples/landsat-item.json rename to tests/v1/data-files/examples/0.9.0/extensions/commons/examples/landsat-item.json diff --git a/tests/data-files/examples/0.9.0/extensions/datacube/examples/example-collection.json b/tests/v1/data-files/examples/0.9.0/extensions/datacube/examples/example-collection.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/datacube/examples/example-collection.json rename to tests/v1/data-files/examples/0.9.0/extensions/datacube/examples/example-collection.json diff --git a/tests/data-files/examples/0.9.0/extensions/datacube/examples/example-item.json b/tests/v1/data-files/examples/0.9.0/extensions/datacube/examples/example-item.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/datacube/examples/example-item.json rename to tests/v1/data-files/examples/0.9.0/extensions/datacube/examples/example-item.json diff --git a/tests/data-files/examples/0.9.0/extensions/eo/examples/example-landsat8.json b/tests/v1/data-files/examples/0.9.0/extensions/eo/examples/example-landsat8.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/eo/examples/example-landsat8.json rename to tests/v1/data-files/examples/0.9.0/extensions/eo/examples/example-landsat8.json diff --git a/tests/data-files/examples/0.9.0/extensions/label/examples/multidataset/catalog.json b/tests/v1/data-files/examples/0.9.0/extensions/label/examples/multidataset/catalog.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/label/examples/multidataset/catalog.json rename to tests/v1/data-files/examples/0.9.0/extensions/label/examples/multidataset/catalog.json diff --git a/tests/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/AOI_2_Vegas_img2636.json b/tests/v1/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/AOI_2_Vegas_img2636.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/AOI_2_Vegas_img2636.json rename to tests/v1/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/AOI_2_Vegas_img2636.json diff --git a/tests/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/AOI_3_Paris_img1648.json b/tests/v1/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/AOI_3_Paris_img1648.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/AOI_3_Paris_img1648.json rename to tests/v1/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/AOI_3_Paris_img1648.json diff --git a/tests/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/AOI_4_Shanghai_img3344.json b/tests/v1/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/AOI_4_Shanghai_img3344.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/AOI_4_Shanghai_img3344.json rename to tests/v1/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/AOI_4_Shanghai_img3344.json diff --git a/tests/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/collection.json b/tests/v1/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/collection.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/collection.json rename to tests/v1/data-files/examples/0.9.0/extensions/label/examples/multidataset/spacenet-buildings/collection.json diff --git a/tests/data-files/examples/0.9.0/extensions/label/examples/multidataset/zanzibar/collection.json b/tests/v1/data-files/examples/0.9.0/extensions/label/examples/multidataset/zanzibar/collection.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/label/examples/multidataset/zanzibar/collection.json rename to tests/v1/data-files/examples/0.9.0/extensions/label/examples/multidataset/zanzibar/collection.json diff --git a/tests/data-files/examples/0.9.0/extensions/label/examples/multidataset/zanzibar/znz001.json b/tests/v1/data-files/examples/0.9.0/extensions/label/examples/multidataset/zanzibar/znz001.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/label/examples/multidataset/zanzibar/znz001.json rename to tests/v1/data-files/examples/0.9.0/extensions/label/examples/multidataset/zanzibar/znz001.json diff --git a/tests/data-files/examples/0.9.0/extensions/label/examples/spacenet-roads/roads_collection.json b/tests/v1/data-files/examples/0.9.0/extensions/label/examples/spacenet-roads/roads_collection.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/label/examples/spacenet-roads/roads_collection.json rename to tests/v1/data-files/examples/0.9.0/extensions/label/examples/spacenet-roads/roads_collection.json diff --git a/tests/data-files/examples/0.9.0/extensions/label/examples/spacenet-roads/roads_item.json b/tests/v1/data-files/examples/0.9.0/extensions/label/examples/spacenet-roads/roads_item.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/label/examples/spacenet-roads/roads_item.json rename to tests/v1/data-files/examples/0.9.0/extensions/label/examples/spacenet-roads/roads_item.json diff --git a/tests/data-files/examples/0.9.0/extensions/label/examples/spacenet-roads/roads_source.json b/tests/v1/data-files/examples/0.9.0/extensions/label/examples/spacenet-roads/roads_source.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/label/examples/spacenet-roads/roads_source.json rename to tests/v1/data-files/examples/0.9.0/extensions/label/examples/spacenet-roads/roads_source.json diff --git a/tests/data-files/examples/0.9.0/extensions/pointcloud/examples/example-autzen.json b/tests/v1/data-files/examples/0.9.0/extensions/pointcloud/examples/example-autzen.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/pointcloud/examples/example-autzen.json rename to tests/v1/data-files/examples/0.9.0/extensions/pointcloud/examples/example-autzen.json diff --git a/tests/data-files/examples/0.9.0/extensions/projection/examples/example-landsat8.json b/tests/v1/data-files/examples/0.9.0/extensions/projection/examples/example-landsat8.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/projection/examples/example-landsat8.json rename to tests/v1/data-files/examples/0.9.0/extensions/projection/examples/example-landsat8.json diff --git a/tests/data-files/examples/0.9.0/extensions/sar/examples/envisat.json b/tests/v1/data-files/examples/0.9.0/extensions/sar/examples/envisat.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/sar/examples/envisat.json rename to tests/v1/data-files/examples/0.9.0/extensions/sar/examples/envisat.json diff --git a/tests/data-files/examples/0.9.0/extensions/sar/examples/sentinel1.json b/tests/v1/data-files/examples/0.9.0/extensions/sar/examples/sentinel1.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/sar/examples/sentinel1.json rename to tests/v1/data-files/examples/0.9.0/extensions/sar/examples/sentinel1.json diff --git a/tests/data-files/examples/0.9.0/extensions/sat/examples/example-landsat8.json b/tests/v1/data-files/examples/0.9.0/extensions/sat/examples/example-landsat8.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/sat/examples/example-landsat8.json rename to tests/v1/data-files/examples/0.9.0/extensions/sat/examples/example-landsat8.json diff --git a/tests/data-files/examples/0.9.0/extensions/scientific/examples/collection.json b/tests/v1/data-files/examples/0.9.0/extensions/scientific/examples/collection.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/scientific/examples/collection.json rename to tests/v1/data-files/examples/0.9.0/extensions/scientific/examples/collection.json diff --git a/tests/data-files/examples/0.9.0/extensions/scientific/examples/item.json b/tests/v1/data-files/examples/0.9.0/extensions/scientific/examples/item.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/scientific/examples/item.json rename to tests/v1/data-files/examples/0.9.0/extensions/scientific/examples/item.json diff --git a/tests/data-files/examples/0.9.0/extensions/version/examples/collection.json b/tests/v1/data-files/examples/0.9.0/extensions/version/examples/collection.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/version/examples/collection.json rename to tests/v1/data-files/examples/0.9.0/extensions/version/examples/collection.json diff --git a/tests/data-files/examples/0.9.0/extensions/version/examples/item.json b/tests/v1/data-files/examples/0.9.0/extensions/version/examples/item.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/version/examples/item.json rename to tests/v1/data-files/examples/0.9.0/extensions/version/examples/item.json diff --git a/tests/data-files/examples/0.9.0/extensions/view/examples/example-landsat8.json b/tests/v1/data-files/examples/0.9.0/extensions/view/examples/example-landsat8.json similarity index 100% rename from tests/data-files/examples/0.9.0/extensions/view/examples/example-landsat8.json rename to tests/v1/data-files/examples/0.9.0/extensions/view/examples/example-landsat8.json diff --git a/tests/data-files/examples/0.9.0/item-spec/examples/datetimerange.json b/tests/v1/data-files/examples/0.9.0/item-spec/examples/datetimerange.json similarity index 100% rename from tests/data-files/examples/0.9.0/item-spec/examples/datetimerange.json rename to tests/v1/data-files/examples/0.9.0/item-spec/examples/datetimerange.json diff --git a/tests/data-files/examples/0.9.0/item-spec/examples/digitalglobe-sample.json b/tests/v1/data-files/examples/0.9.0/item-spec/examples/digitalglobe-sample.json similarity index 100% rename from tests/data-files/examples/0.9.0/item-spec/examples/digitalglobe-sample.json rename to tests/v1/data-files/examples/0.9.0/item-spec/examples/digitalglobe-sample.json diff --git a/tests/data-files/examples/0.9.0/item-spec/examples/itemcollection-sample-full.json b/tests/v1/data-files/examples/0.9.0/item-spec/examples/itemcollection-sample-full.json similarity index 100% rename from tests/data-files/examples/0.9.0/item-spec/examples/itemcollection-sample-full.json rename to tests/v1/data-files/examples/0.9.0/item-spec/examples/itemcollection-sample-full.json diff --git a/tests/data-files/examples/0.9.0/item-spec/examples/itemcollection-sample-minimal.json b/tests/v1/data-files/examples/0.9.0/item-spec/examples/itemcollection-sample-minimal.json similarity index 100% rename from tests/data-files/examples/0.9.0/item-spec/examples/itemcollection-sample-minimal.json rename to tests/v1/data-files/examples/0.9.0/item-spec/examples/itemcollection-sample-minimal.json diff --git a/tests/data-files/examples/0.9.0/item-spec/examples/landsat8-sample.json b/tests/v1/data-files/examples/0.9.0/item-spec/examples/landsat8-sample.json similarity index 100% rename from tests/data-files/examples/0.9.0/item-spec/examples/landsat8-sample.json rename to tests/v1/data-files/examples/0.9.0/item-spec/examples/landsat8-sample.json diff --git a/tests/data-files/examples/0.9.0/item-spec/examples/planet-sample.json b/tests/v1/data-files/examples/0.9.0/item-spec/examples/planet-sample.json similarity index 100% rename from tests/data-files/examples/0.9.0/item-spec/examples/planet-sample.json rename to tests/v1/data-files/examples/0.9.0/item-spec/examples/planet-sample.json diff --git a/tests/data-files/examples/0.9.0/item-spec/examples/sample-full.json b/tests/v1/data-files/examples/0.9.0/item-spec/examples/sample-full.json similarity index 100% rename from tests/data-files/examples/0.9.0/item-spec/examples/sample-full.json rename to tests/v1/data-files/examples/0.9.0/item-spec/examples/sample-full.json diff --git a/tests/data-files/examples/0.9.0/item-spec/examples/sample.json b/tests/v1/data-files/examples/0.9.0/item-spec/examples/sample.json similarity index 100% rename from tests/data-files/examples/0.9.0/item-spec/examples/sample.json rename to tests/v1/data-files/examples/0.9.0/item-spec/examples/sample.json diff --git a/tests/data-files/examples/0.9.0/item-spec/examples/sentinel-s2-l2a-collection.json b/tests/v1/data-files/examples/0.9.0/item-spec/examples/sentinel-s2-l2a-collection.json similarity index 100% rename from tests/data-files/examples/0.9.0/item-spec/examples/sentinel-s2-l2a-collection.json rename to tests/v1/data-files/examples/0.9.0/item-spec/examples/sentinel-s2-l2a-collection.json diff --git a/tests/data-files/examples/0.9.0/item-spec/examples/sentinel2-sample.json b/tests/v1/data-files/examples/0.9.0/item-spec/examples/sentinel2-sample.json similarity index 100% rename from tests/data-files/examples/0.9.0/item-spec/examples/sentinel2-sample.json rename to tests/v1/data-files/examples/0.9.0/item-spec/examples/sentinel2-sample.json diff --git a/tests/data-files/examples/1.0.0-RC1/catalog.json b/tests/v1/data-files/examples/1.0.0-RC1/catalog.json similarity index 100% rename from tests/data-files/examples/1.0.0-RC1/catalog.json rename to tests/v1/data-files/examples/1.0.0-RC1/catalog.json diff --git a/tests/data-files/examples/1.0.0-RC1/collection-only/collection.json b/tests/v1/data-files/examples/1.0.0-RC1/collection-only/collection.json similarity index 100% rename from tests/data-files/examples/1.0.0-RC1/collection-only/collection.json rename to tests/v1/data-files/examples/1.0.0-RC1/collection-only/collection.json diff --git a/tests/data-files/examples/1.0.0-RC1/collection.json b/tests/v1/data-files/examples/1.0.0-RC1/collection.json similarity index 100% rename from tests/data-files/examples/1.0.0-RC1/collection.json rename to tests/v1/data-files/examples/1.0.0-RC1/collection.json diff --git a/tests/data-files/examples/1.0.0-RC1/collectionless-item.json b/tests/v1/data-files/examples/1.0.0-RC1/collectionless-item.json similarity index 100% rename from tests/data-files/examples/1.0.0-RC1/collectionless-item.json rename to tests/v1/data-files/examples/1.0.0-RC1/collectionless-item.json diff --git a/tests/data-files/examples/1.0.0-RC1/core-item.json b/tests/v1/data-files/examples/1.0.0-RC1/core-item.json similarity index 100% rename from tests/data-files/examples/1.0.0-RC1/core-item.json rename to tests/v1/data-files/examples/1.0.0-RC1/core-item.json diff --git a/tests/data-files/examples/1.0.0-RC1/extended-item.json b/tests/v1/data-files/examples/1.0.0-RC1/extended-item.json similarity index 100% rename from tests/data-files/examples/1.0.0-RC1/extended-item.json rename to tests/v1/data-files/examples/1.0.0-RC1/extended-item.json diff --git a/tests/data-files/examples/1.0.0-RC1/extensions-collection/collection.json b/tests/v1/data-files/examples/1.0.0-RC1/extensions-collection/collection.json similarity index 100% rename from tests/data-files/examples/1.0.0-RC1/extensions-collection/collection.json rename to tests/v1/data-files/examples/1.0.0-RC1/extensions-collection/collection.json diff --git a/tests/data-files/examples/1.0.0-RC1/extensions-collection/proj-example/proj-example.json b/tests/v1/data-files/examples/1.0.0-RC1/extensions-collection/proj-example/proj-example.json similarity index 100% rename from tests/data-files/examples/1.0.0-RC1/extensions-collection/proj-example/proj-example.json rename to tests/v1/data-files/examples/1.0.0-RC1/extensions-collection/proj-example/proj-example.json diff --git a/tests/data-files/examples/1.0.0-RC1/simple-item.json b/tests/v1/data-files/examples/1.0.0-RC1/simple-item.json similarity index 100% rename from tests/data-files/examples/1.0.0-RC1/simple-item.json rename to tests/v1/data-files/examples/1.0.0-RC1/simple-item.json diff --git a/tests/data-files/examples/1.0.0-RC2/extended-item.json b/tests/v1/data-files/examples/1.0.0-RC2/extended-item.json similarity index 100% rename from tests/data-files/examples/1.0.0-RC2/extended-item.json rename to tests/v1/data-files/examples/1.0.0-RC2/extended-item.json diff --git a/tests/data-files/examples/1.0.0-beta.2/catalog-spec/examples/catalog-items.json b/tests/v1/data-files/examples/1.0.0-beta.2/catalog-spec/examples/catalog-items.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/catalog-spec/examples/catalog-items.json rename to tests/v1/data-files/examples/1.0.0-beta.2/catalog-spec/examples/catalog-items.json diff --git a/tests/data-files/examples/1.0.0-beta.2/catalog-spec/examples/catalog.json b/tests/v1/data-files/examples/1.0.0-beta.2/catalog-spec/examples/catalog.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/catalog-spec/examples/catalog.json rename to tests/v1/data-files/examples/1.0.0-beta.2/catalog-spec/examples/catalog.json diff --git a/tests/data-files/examples/1.0.0-beta.2/collection-spec/examples/landsat-collection.json b/tests/v1/data-files/examples/1.0.0-beta.2/collection-spec/examples/landsat-collection.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/collection-spec/examples/landsat-collection.json rename to tests/v1/data-files/examples/1.0.0-beta.2/collection-spec/examples/landsat-collection.json diff --git a/tests/data-files/examples/1.0.0-beta.2/collection-spec/examples/sentinel2.json b/tests/v1/data-files/examples/1.0.0-beta.2/collection-spec/examples/sentinel2.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/collection-spec/examples/sentinel2.json rename to tests/v1/data-files/examples/1.0.0-beta.2/collection-spec/examples/sentinel2.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/checksum/examples/sentinel1.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/checksum/examples/sentinel1.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/checksum/examples/sentinel1.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/checksum/examples/sentinel1.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/collection-assets/examples/example-esm.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/collection-assets/examples/example-esm.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/collection-assets/examples/example-esm.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/collection-assets/examples/example-esm.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/datacube/examples/example-collection.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/datacube/examples/example-collection.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/datacube/examples/example-collection.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/datacube/examples/example-collection.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/datacube/examples/example-item.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/datacube/examples/example-item.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/datacube/examples/example-item.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/datacube/examples/example-item.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/eo/examples/example-landsat8.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/eo/examples/example-landsat8.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/eo/examples/example-landsat8.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/eo/examples/example-landsat8.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/item-assets/examples/example-landsat8.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/item-assets/examples/example-landsat8.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/item-assets/examples/example-landsat8.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/item-assets/examples/example-landsat8.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/catalog.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/catalog.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/catalog.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/catalog.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/AOI_2_Vegas_img2636.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/AOI_2_Vegas_img2636.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/AOI_2_Vegas_img2636.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/AOI_2_Vegas_img2636.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/AOI_3_Paris_img1648.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/AOI_3_Paris_img1648.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/AOI_3_Paris_img1648.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/AOI_3_Paris_img1648.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/AOI_4_Shanghai_img3344.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/AOI_4_Shanghai_img3344.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/AOI_4_Shanghai_img3344.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/AOI_4_Shanghai_img3344.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/collection.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/collection.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/collection.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/spacenet-buildings/collection.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/zanzibar/collection.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/zanzibar/collection.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/zanzibar/collection.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/zanzibar/collection.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/zanzibar/znz001.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/zanzibar/znz001.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/zanzibar/znz001.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/zanzibar/znz001.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/zanzibar/znz029.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/zanzibar/znz029.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/zanzibar/znz029.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/multidataset/zanzibar/znz029.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/spacenet-roads/roads_collection.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/spacenet-roads/roads_collection.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/spacenet-roads/roads_collection.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/spacenet-roads/roads_collection.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/spacenet-roads/roads_item.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/spacenet-roads/roads_item.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/spacenet-roads/roads_item.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/spacenet-roads/roads_item.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/spacenet-roads/roads_source.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/spacenet-roads/roads_source.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/label/examples/spacenet-roads/roads_source.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/label/examples/spacenet-roads/roads_source.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/pointcloud/examples/example-autzen.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/pointcloud/examples/example-autzen.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/pointcloud/examples/example-autzen.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/pointcloud/examples/example-autzen.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/projection/examples/example-landsat8.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/projection/examples/example-landsat8.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/projection/examples/example-landsat8.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/projection/examples/example-landsat8.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/sar/examples/envisat.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/sar/examples/envisat.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/sar/examples/envisat.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/sar/examples/envisat.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/sar/examples/sentinel1.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/sar/examples/sentinel1.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/sar/examples/sentinel1.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/sar/examples/sentinel1.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/sat/examples/example-landsat8.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/sat/examples/example-landsat8.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/sat/examples/example-landsat8.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/sat/examples/example-landsat8.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/scientific/examples/collection.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/scientific/examples/collection.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/scientific/examples/collection.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/scientific/examples/collection.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/scientific/examples/item.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/scientific/examples/item.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/scientific/examples/item.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/scientific/examples/item.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/single-file-stac/examples/example-search.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/single-file-stac/examples/example-search.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/single-file-stac/examples/example-search.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/single-file-stac/examples/example-search.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/tiled-assets/examples/example-dimension.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/tiled-assets/examples/example-dimension.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/tiled-assets/examples/example-dimension.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/tiled-assets/examples/example-dimension.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/tiled-assets/examples/example-tiled.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/tiled-assets/examples/example-tiled.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/tiled-assets/examples/example-tiled.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/tiled-assets/examples/example-tiled.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/timestamps/examples/example-landsat8.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/timestamps/examples/example-landsat8.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/timestamps/examples/example-landsat8.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/timestamps/examples/example-landsat8.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/version/examples/collection.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/version/examples/collection.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/version/examples/collection.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/version/examples/collection.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/version/examples/item.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/version/examples/item.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/version/examples/item.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/version/examples/item.json diff --git a/tests/data-files/examples/1.0.0-beta.2/extensions/view/examples/example-landsat8.json b/tests/v1/data-files/examples/1.0.0-beta.2/extensions/view/examples/example-landsat8.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/extensions/view/examples/example-landsat8.json rename to tests/v1/data-files/examples/1.0.0-beta.2/extensions/view/examples/example-landsat8.json diff --git a/tests/data-files/examples/1.0.0-beta.2/item-spec/examples/CBERS_4_MUX_20181029_177_106_L4.json b/tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/CBERS_4_MUX_20181029_177_106_L4.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/item-spec/examples/CBERS_4_MUX_20181029_177_106_L4.json rename to tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/CBERS_4_MUX_20181029_177_106_L4.json diff --git a/tests/data-files/examples/1.0.0-beta.2/item-spec/examples/datetimerange.json b/tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/datetimerange.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/item-spec/examples/datetimerange.json rename to tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/datetimerange.json diff --git a/tests/data-files/examples/1.0.0-beta.2/item-spec/examples/digitalglobe-sample.json b/tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/digitalglobe-sample.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/item-spec/examples/digitalglobe-sample.json rename to tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/digitalglobe-sample.json diff --git a/tests/data-files/examples/1.0.0-beta.2/item-spec/examples/landsat8-sample.json b/tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/landsat8-sample.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/item-spec/examples/landsat8-sample.json rename to tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/landsat8-sample.json diff --git a/tests/data-files/examples/1.0.0-beta.2/item-spec/examples/null-geom-item.json b/tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/null-geom-item.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/item-spec/examples/null-geom-item.json rename to tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/null-geom-item.json diff --git a/tests/data-files/examples/1.0.0-beta.2/item-spec/examples/planet-sample.json b/tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/planet-sample.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/item-spec/examples/planet-sample.json rename to tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/planet-sample.json diff --git a/tests/data-files/examples/1.0.0-beta.2/item-spec/examples/sample-full.json b/tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/sample-full.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/item-spec/examples/sample-full.json rename to tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/sample-full.json diff --git a/tests/data-files/examples/1.0.0-beta.2/item-spec/examples/sample.json b/tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/sample.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/item-spec/examples/sample.json rename to tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/sample.json diff --git a/tests/data-files/examples/1.0.0-beta.2/item-spec/examples/sentinel2-sample.json b/tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/sentinel2-sample.json similarity index 100% rename from tests/data-files/examples/1.0.0-beta.2/item-spec/examples/sentinel2-sample.json rename to tests/v1/data-files/examples/1.0.0-beta.2/item-spec/examples/sentinel2-sample.json diff --git a/tests/data-files/examples/1.0.0/README.md b/tests/v1/data-files/examples/1.0.0/README.md similarity index 100% rename from tests/data-files/examples/1.0.0/README.md rename to tests/v1/data-files/examples/1.0.0/README.md diff --git a/tests/data-files/examples/1.0.0/catalog.json b/tests/v1/data-files/examples/1.0.0/catalog.json similarity index 100% rename from tests/data-files/examples/1.0.0/catalog.json rename to tests/v1/data-files/examples/1.0.0/catalog.json diff --git a/tests/data-files/examples/1.0.0/collection-only/collection-with-schemas.json b/tests/v1/data-files/examples/1.0.0/collection-only/collection-with-schemas.json similarity index 100% rename from tests/data-files/examples/1.0.0/collection-only/collection-with-schemas.json rename to tests/v1/data-files/examples/1.0.0/collection-only/collection-with-schemas.json diff --git a/tests/data-files/examples/1.0.0/collection-only/collection.json b/tests/v1/data-files/examples/1.0.0/collection-only/collection.json similarity index 100% rename from tests/data-files/examples/1.0.0/collection-only/collection.json rename to tests/v1/data-files/examples/1.0.0/collection-only/collection.json diff --git a/tests/data-files/examples/1.0.0/collection.json b/tests/v1/data-files/examples/1.0.0/collection.json similarity index 100% rename from tests/data-files/examples/1.0.0/collection.json rename to tests/v1/data-files/examples/1.0.0/collection.json diff --git a/tests/data-files/examples/1.0.0/collectionless-item.json b/tests/v1/data-files/examples/1.0.0/collectionless-item.json similarity index 100% rename from tests/data-files/examples/1.0.0/collectionless-item.json rename to tests/v1/data-files/examples/1.0.0/collectionless-item.json diff --git a/tests/data-files/examples/1.0.0/core-item.json b/tests/v1/data-files/examples/1.0.0/core-item.json similarity index 100% rename from tests/data-files/examples/1.0.0/core-item.json rename to tests/v1/data-files/examples/1.0.0/core-item.json diff --git a/tests/data-files/examples/1.0.0/example-sentinel2.json b/tests/v1/data-files/examples/1.0.0/example-sentinel2.json similarity index 100% rename from tests/data-files/examples/1.0.0/example-sentinel2.json rename to tests/v1/data-files/examples/1.0.0/example-sentinel2.json diff --git a/tests/data-files/examples/1.0.0/extended-item.json b/tests/v1/data-files/examples/1.0.0/extended-item.json similarity index 100% rename from tests/data-files/examples/1.0.0/extended-item.json rename to tests/v1/data-files/examples/1.0.0/extended-item.json diff --git a/tests/data-files/examples/1.0.0/extensions-collection/collection.json b/tests/v1/data-files/examples/1.0.0/extensions-collection/collection.json similarity index 100% rename from tests/data-files/examples/1.0.0/extensions-collection/collection.json rename to tests/v1/data-files/examples/1.0.0/extensions-collection/collection.json diff --git a/tests/data-files/examples/1.0.0/extensions-collection/proj-example/proj-example.json b/tests/v1/data-files/examples/1.0.0/extensions-collection/proj-example/proj-example.json similarity index 100% rename from tests/data-files/examples/1.0.0/extensions-collection/proj-example/proj-example.json rename to tests/v1/data-files/examples/1.0.0/extensions-collection/proj-example/proj-example.json diff --git a/tests/data-files/examples/1.0.0/simple-item.json b/tests/v1/data-files/examples/1.0.0/simple-item.json similarity index 100% rename from tests/data-files/examples/1.0.0/simple-item.json rename to tests/v1/data-files/examples/1.0.0/simple-item.json diff --git a/tests/v1/data-files/examples/1.1.0/README.md b/tests/v1/data-files/examples/1.1.0/README.md new file mode 100644 index 000000000..a5f8d3f62 --- /dev/null +++ b/tests/v1/data-files/examples/1.1.0/README.md @@ -0,0 +1,91 @@ +# STAC Examples + +This directory contains various examples for all parts of the STAC specification. +It is structured to be two valid STACs, meaning both [catalog.json](catalog.json) and [collection.json](collection.json) +should successfully load in various tools. They do not follow *all* the [best practices](../best-practices.md) for STAC, mostly +due to the fact that they contrive examples to show the spec and we are hosting in GitHub. But we note below where they differ from an ideal catalog. + +The various fields are mostly fictional, to be able to demonstrate the various aspects of the spec as tersely as possible. To get a sense +of real world STAC implementations we recommend exploring the [stac-examples](http://github.com/stac-utils/stac-examples) repo, which +gathers in one place copies of STAC [Items](../item-spec/item-spec.md) and [Collection](../collection-spec/collection-spec.md) +from a number of different production catalogs that all follow good STAC practices. And you should also explore the various catalogs +listed on [STAC Index](http://stacindex.org), to see full catalogs in production. + +## Organization + +This directory contains two STAC implementations, both valid, but simplified a bit to be illustrative of the key concepts, so +they do not quite follow all the best practices. + +### Simple Collection + +This STAC implementation consists of three files, all contained at the root of the examples directory + +**[collection.json](collection.json)** is a minimal 'simple collection', that links to three items. + +**[simple-item.json](simple-item.json)** is the most minimal possible compliant Item record. Most all data will +include additional fields, as STAC is designed to be a minimal common subset. But it is useful for showing exactly what is +required. + +**[core-item.json](core-item.json)** is a more realistic example, for a hypothetical analytic image +acquisition from a satellite company called 'Remote Data'. It includes additional fields covering the [common +metadata](../commons/common-metadata.md). It also links to a variety of assets that is typical for +satellite imagery, as most providers include a number of complementary files. + +**[extended-item.json](extended-item.json)** is arguably an even more realistic example, as it includes a number of the +[extensions](../extensions/) that are commonly used, to demonstrate how implementations tend to start with the core, and add in +a number of the core extensions. + +**[collectionless-item.json](collectionless-item.json)** demonstrates the common metadata that is only used when an Item does not have +a collection. It is recommended to organize items in collections, but we wanted to show how this works. This is not technically in the +'simple collection' of this section, but it follows the same pattern, so is included here. + +### Nested Catalog + +This STAC implementation shows a common pattern, starting with a catalog that links to a number of distinct collections, which may +link down to a number of items. + +**[catalog.json](catalog.json)** is a minimal catalog implementation, linking to two other collections. + +**[collection-only/collection.json](collection-only/collection.json)** is a collection that does not link to any items. This +demonstrates how is is possible to make use of STAC Collections without needing items, to serve as nice summarizing metadata for +tools that work with full layers / collections. This example collection is based on real Sentinel-2 values, so is not quite fictional, +but should be taken as just an example. + +**[extensions-collection/collection.json](extensions-collection/collection.json)** contains a small number of items, that demonstrate +more functionality available in STAC [extensions](../extensions/). These are linked to directly from the individual extensions. These +items follow the recommendations for [Catalog Layout Best Practices](../best-practices.md#catalog-layout). + +## In Depth + +As mentioned above, the files in this examples directory form valid STAC implementations. They are all based on a +fictional remote sensing company called 'Remote Data', with a URL at remotedata.io. This domain has not been set up, so those links +will not work, but any valid data provider should provide valid links to their homepage. + +The examples use the `rd:` prefix to show how providers can use custom fields when there are not set fields. In the examples these +do not link to a schema which is completely valid, but it is recommended that providers do write a JSON schema that can validate +their custom fields (we will work to add an example schema for the `rd:` fields in the future). + +### Catalog Type + +One of the most important STAC Best Practices is to [use links consistently](../best-practices.md#use-of-links), following one of the +described 'catalog types'. The catalogs described here are [Relative Published Catalogs](../best-practices.md#relative-published-catalog), +that use absolute URL's to refer to their assets (so would be an example of a [Self-contained Metadata +Only](../best-practices.md#self-contained-metadata-only) catalog that is published). + +### Differences with STAC Best Practices + +One of the most important documents in this repository is the one about [best practices](../best-practices.md). It describes a number +of practical recommendations gained by people actually implementing STAC. The core spec is designed to be as flexible as possible, so +that it is not too rigid and unable to handle unanticipated needs. But we recommend following as many of the best practices as is +feasible, as it will help ensure various STAC tools work much better. The examples in this folder don't align with all the best +practices, mostly because they are meant to demonstrate things as tersely as possible, and also because they live directly inside +a github repository. As many people will look at these examples and take them as 'how things should be' we felt its important to +highlight where things here differ from the actual best practices. + +#### Catalog Layout + +Another important recommendations concerns the [layout of STAC catalogs](../best-practices.md#catalog-layout). This is important +for tools to be able to expect a certain layout, and most tools will follow the described layout. The simple collection that consists +of the collection.json and its 3 linked items violates this. This is done to be able to show item examples directly in the root of +the 'examples' folder, so people don't have to dig deep into folders to get a quick example. But a proper catalog layout would +put the items in sub-directories, along with their assets. diff --git a/tests/v1/data-files/examples/1.1.0/catalog.json b/tests/v1/data-files/examples/1.1.0/catalog.json new file mode 100644 index 000000000..a9fe3ef8e --- /dev/null +++ b/tests/v1/data-files/examples/1.1.0/catalog.json @@ -0,0 +1,43 @@ +{ + "id": "examples", + "type": "Catalog", + "title": "Example Catalog", + "stac_version": "1.1.0", + "description": "This catalog is a simple demonstration of an example catalog that is used to organize a hierarchy of collections and their items.", + "links": [ + { + "rel": "root", + "href": "./catalog.json", + "type": "application/json" + }, + { + "rel": "child", + "href": "./extensions-collection/collection.json", + "type": "application/json", + "title": "Collection Demonstrating STAC Extensions" + }, + { + "rel": "child", + "href": "./collection-only/collection.json", + "type": "application/json", + "title": "Collection with no items (standalone)" + }, + { + "rel": "child", + "href": "./collection-only/collection-with-schemas.json", + "type": "application/json", + "title": "Collection with no items (standalone with JSON Schemas)" + }, + { + "rel": "item", + "href": "./collectionless-item.json", + "type": "application/json", + "title": "Item that does not have a collection (not recommended, but allowed by the spec)" + }, + { + "rel": "self", + "href": "https://raw.githubusercontent.com/radiantearth/stac-spec/v1.1.0/examples/catalog.json", + "type": "application/json" + } + ] +} diff --git a/tests/data-files/examples/1.1.0/collection-only/collection-with-schemas.json b/tests/v1/data-files/examples/1.1.0/collection-only/collection-with-schemas.json similarity index 100% rename from tests/data-files/examples/1.1.0/collection-only/collection-with-schemas.json rename to tests/v1/data-files/examples/1.1.0/collection-only/collection-with-schemas.json diff --git a/tests/data-files/examples/1.1.0/collection-only/collection.json b/tests/v1/data-files/examples/1.1.0/collection-only/collection.json similarity index 100% rename from tests/data-files/examples/1.1.0/collection-only/collection.json rename to tests/v1/data-files/examples/1.1.0/collection-only/collection.json diff --git a/tests/v1/data-files/examples/1.1.0/collection.json b/tests/v1/data-files/examples/1.1.0/collection.json new file mode 100644 index 000000000..9de9cbeae --- /dev/null +++ b/tests/v1/data-files/examples/1.1.0/collection.json @@ -0,0 +1,136 @@ +{ + "id": "simple-collection", + "type": "Collection", + "stac_extensions": [ + "https://stac-extensions.github.io/eo/v2.0.0/schema.json", + "https://stac-extensions.github.io/projection/v2.0.0/schema.json", + "https://stac-extensions.github.io/view/v1.0.0/schema.json" + ], + "stac_version": "1.1.0", + "description": "A simple collection demonstrating core catalog fields with links to a couple of items", + "title": "Simple Example Collection", + "keywords": [ + "simple", + "example", + "collection" + ], + "providers": [ + { + "name": "Remote Data, Inc", + "description": "Producers of awesome spatiotemporal assets", + "roles": [ + "producer", + "processor" + ], + "url": "http://remotedata.io" + } + ], + "extent": { + "spatial": { + "bbox": [ + [ + 172.91173669923782, + 1.3438851951615003, + 172.95469614953714, + 1.3690476620161975 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2020-12-11T22:38:32.125Z", + "2020-12-14T18:02:31.437Z" + ] + ] + } + }, + "license": "CC-BY-4.0", + "summaries": { + "platform": [ + "cool_sat1", + "cool_sat2" + ], + "constellation": [ + "ion" + ], + "instruments": [ + "cool_sensor_v1", + "cool_sensor_v2" + ], + "gsd": { + "minimum": 0.512, + "maximum": 0.66 + }, + "eo:cloud_cover": { + "minimum": 1.2, + "maximum": 1.2 + }, + "proj:cpde": [ + "EPSG:32659" + ], + "view:sun_elevation": { + "minimum": 54.9, + "maximum": 54.9 + }, + "view:off_nadir": { + "minimum": 3.8, + "maximum": 3.8 + }, + "view:sun_azimuth": { + "minimum": 135.7, + "maximum": 135.7 + }, + "statistics": { + "type": "object", + "properties": { + "vegetation": { + "description": "Percentage of pixels that are detected as vegetation, e.g. forests, grasslands, etc.", + "minimum": 0, + "maximum": 100 + }, + "water": { + "description": "Percentage of pixels that are detected as water, e.g. rivers, oceans and ponds.", + "minimum": 0, + "maximum": 100 + }, + "urban": { + "description": "Percentage of pixels that detected as urban, e.g. roads and buildings.", + "minimum": 0, + "maximum": 100 + } + } + } + }, + "links": [ + { + "rel": "root", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "item", + "href": "./simple-item.json", + "type": "application/geo+json", + "title": "Simple Item" + }, + { + "rel": "item", + "href": "./core-item.json", + "type": "application/geo+json", + "title": "Core Item" + }, + { + "rel": "item", + "href": "./extended-item.json", + "type": "application/geo+json", + "title": "Extended Item" + }, + { + "rel": "self", + "href": "https://raw.githubusercontent.com/radiantearth/stac-spec/v1.1.0/examples/collection.json", + "type": "application/json" + } + ] +} diff --git a/tests/v1/data-files/examples/1.1.0/collectionless-item.json b/tests/v1/data-files/examples/1.1.0/collectionless-item.json new file mode 100644 index 000000000..3562d6652 --- /dev/null +++ b/tests/v1/data-files/examples/1.1.0/collectionless-item.json @@ -0,0 +1,143 @@ +{ + "stac_version": "1.1.0", + "stac_extensions": [ + "https://stac-extensions.github.io/view/v1.0.0/schema.json" + ], + "type": "Feature", + "id": "CS3-20160503_132131_08", + "bbox": [ + -122.59750209, + 37.48803556, + -122.2880486, + 37.613537207 + ], + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -122.308150179, + 37.488035566 + ], + [ + -122.597502109, + 37.538869539 + ], + [ + -122.576687533, + 37.613537207 + ], + [ + -122.2880486, + 37.562818007 + ], + [ + -122.308150179, + 37.488035566 + ] + ] + ] + }, + "properties": { + "title": "Full Item", + "description": "A sample STAC Item demonstrates an Item that does not have a collection, which is not recommended, but allowed by the spec.", + "datetime": null, + "start_datetime": "2016-05-03T13:22:30Z", + "end_datetime": "2016-05-03T13:27:30Z", + "created": "2016-05-04T00:00:01Z", + "updated": "2017-01-01T00:30:55Z", + "license": "other", + "providers": [ + { + "name": "Remote Data, Inc", + "description": "Producers of awesome spatiotemporal assets", + "roles": [ + "producer", + "processor" + ], + "url": "http://remotedata.it" + } + ], + "platform": "cool_sat2", + "instruments": [ + "cool_sensor_v1" + ], + "view:sun_elevation": 33.4, + "gsd": 0.512, + "cs:type": "scene", + "cs:anomalous_pixels": 0.14, + "cs:earth_sun_distance": 1.014156, + "cs:sat_id": "CS3", + "cs:product_level": "LV1B" + }, + "links": [ + { + "rel": "root", + "href": "./catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "parent", + "href": "./catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "alternate", + "type": "text/html", + "href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/CS3-20160503_132130_04.html", + "title": "HTML representation of this STAC Item" + }, + { + "rel": "license", + "type": "text/html", + "href": "http://remotedata.io/license.html", + "title": "Data License for Remote Data, Inc." + } + ], + "assets": { + "analytic": { + "href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/analytic.tif", + "title": "4-Band Analytic", + "bands": [ + { + "name": "band1" + }, + { + "name": "band1" + }, + { + "name": "band2" + }, + { + "name": "band3" + } + ] + }, + "thumbnail": { + "href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/thumbnail.png", + "title": "Thumbnail", + "type": "image/png", + "roles": [ + "thumbnail" + ] + }, + "udm": { + "href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/UDM.tif", + "title": "Unusable Data Mask" + }, + "json-metadata": { + "href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/extended-metadata.json", + "title": "Extended Metadata", + "type": "application/json", + "roles": [ + "metadata" + ] + }, + "ephemeris": { + "href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/S3-20160503_132130_04.EPH", + "title": "Satellite Ephemeris Metadata" + } + } +} diff --git a/tests/v1/data-files/examples/1.1.0/core-item.json b/tests/v1/data-files/examples/1.1.0/core-item.json new file mode 100644 index 000000000..e151b1353 --- /dev/null +++ b/tests/v1/data-files/examples/1.1.0/core-item.json @@ -0,0 +1,125 @@ +{ + "stac_version": "1.1.0", + "stac_extensions": [], + "type": "Feature", + "id": "20201211_223832_CS2", + "bbox": [ + 172.91173669923782, + 1.3438851951615003, + 172.95469614953714, + 1.3690476620161975 + ], + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 172.91173669923782, + 1.3438851951615003 + ], + [ + 172.95469614953714, + 1.3438851951615003 + ], + [ + 172.95469614953714, + 1.3690476620161975 + ], + [ + 172.91173669923782, + 1.3690476620161975 + ], + [ + 172.91173669923782, + 1.3438851951615003 + ] + ] + ] + }, + "properties": { + "title": "Core Item", + "description": "A sample STAC Item that includes examples of all common metadata", + "datetime": null, + "start_datetime": "2020-12-11T22:38:32.125Z", + "end_datetime": "2020-12-11T22:38:32.327Z", + "created": "2020-12-12T01:48:13.725Z", + "updated": "2020-12-12T01:48:13.725Z", + "platform": "cool_sat1", + "instruments": [ + "cool_sensor_v1" + ], + "constellation": "ion", + "mission": "collection 5624", + "gsd": 0.512 + }, + "collection": "simple-collection", + "links": [ + { + "rel": "collection", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "root", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "parent", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "alternate", + "type": "text/html", + "href": "http://remotedata.io/catalog/20201211_223832_CS2/index.html", + "title": "HTML version of this STAC Item" + } + ], + "assets": { + "analytic": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2_analytic.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "4-Band Analytic", + "roles": [ + "data" + ] + }, + "thumbnail": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2.jpg", + "title": "Thumbnail", + "type": "image/png", + "roles": [ + "thumbnail" + ] + }, + "visual": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "3-Band Visual", + "roles": [ + "visual" + ] + }, + "udm": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2_analytic_udm.tif", + "title": "Unusable Data Mask", + "type": "image/tiff; application=geotiff" + }, + "json-metadata": { + "href": "http://remotedata.io/catalog/20201211_223832_CS2/extended-metadata.json", + "title": "Extended Metadata", + "type": "application/json", + "roles": [ + "metadata" + ] + }, + "ephemeris": { + "href": "http://cool-sat.com/catalog/20201211_223832_CS2/20201211_223832_CS2.EPH", + "title": "Satellite Ephemeris Metadata" + } + } +} diff --git a/tests/data-files/examples/1.1.0/extended-item.json b/tests/v1/data-files/examples/1.1.0/extended-item.json similarity index 100% rename from tests/data-files/examples/1.1.0/extended-item.json rename to tests/v1/data-files/examples/1.1.0/extended-item.json diff --git a/tests/v1/data-files/examples/1.1.0/extensions-collection/collection.json b/tests/v1/data-files/examples/1.1.0/extensions-collection/collection.json new file mode 100644 index 000000000..825f76acb --- /dev/null +++ b/tests/v1/data-files/examples/1.1.0/extensions-collection/collection.json @@ -0,0 +1,68 @@ +{ + "id": "extensions-collection", + "type": "Collection", + "stac_version": "1.1.0", + "description": "A heterogeneous collection containing deeper examples of various extensions", + "links": [ + { + "rel": "parent", + "href": "../catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "root", + "href": "../catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "item", + "href": "./proj-example/proj-example.json", + "title": "Proj extension example" + }, + { + "rel": "license", + "href": "https://remotedata.io/license.html", + "title": "Remote Data License Terms" + } + ], + "stac_extensions": [], + "title": "Collection of Extension Items", + "keywords": [ + "examples", + "sar", + "projection" + ], + "providers": [ + { + "name": "Remote Data, Inc.", + "roles": [ + "producer", + "licensor" + ], + "url": "https://remotedata.io" + } + ], + "extent": { + "spatial": { + "bbox": [ + [ + -180, + -56, + 180, + 83 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2009-05-20T02:40:01.042784Z", + "2018-11-03T23:59:55.112875Z" + ] + ] + } + }, + "license": "other" +} diff --git a/tests/v1/data-files/examples/1.1.0/extensions-collection/proj-example/proj-example.json b/tests/v1/data-files/examples/1.1.0/extensions-collection/proj-example/proj-example.json new file mode 100644 index 000000000..2884e3882 --- /dev/null +++ b/tests/v1/data-files/examples/1.1.0/extensions-collection/proj-example/proj-example.json @@ -0,0 +1,285 @@ +{ + "type": "Feature", + "stac_version": "1.1.0", + "id": "proj-example", + "properties": { + "datetime": "2018-10-01T01:08:32.033000Z", + "proj:code": "EPSG:32614", + "proj:wkt2": "PROJCS[\"WGS 84 / UTM zone 14N\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-99],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],AUTHORITY[\"EPSG\",\"32614\"],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH]]", + "proj:projjson": { + "$schema": "https://proj.org/schemas/v0.2/projjson.schema.json", + "type": "ProjectedCRS", + "name": "WGS 84 / UTM zone 14N", + "base_crs": { + "name": "WGS 84", + "datum": { + "type": "GeodeticReferenceFrame", + "name": "World Geodetic System 1984", + "ellipsoid": { + "name": "WGS 84", + "semi_major_axis": 6378137, + "inverse_flattening": 298.257223563 + } + }, + "coordinate_system": { + "subtype": "ellipsoidal", + "axis": [ + { + "name": "Geodetic latitude", + "abbreviation": "Lat", + "direction": "north", + "unit": "degree" + }, + { + "name": "Geodetic longitude", + "abbreviation": "Lon", + "direction": "east", + "unit": "degree" + } + ] + }, + "id": { + "authority": "EPSG", + "code": 4326 + } + }, + "conversion": { + "name": "UTM zone 14N", + "method": { + "name": "Transverse Mercator", + "id": { + "authority": "EPSG", + "code": 9807 + } + }, + "parameters": [ + { + "name": "Latitude of natural origin", + "value": 0, + "unit": "degree", + "id": { + "authority": "EPSG", + "code": 8801 + } + }, + { + "name": "Longitude of natural origin", + "value": -99, + "unit": "degree", + "id": { + "authority": "EPSG", + "code": 8802 + } + }, + { + "name": "Scale factor at natural origin", + "value": 0.9996, + "unit": "unity", + "id": { + "authority": "EPSG", + "code": 8805 + } + }, + { + "name": "False easting", + "value": 500000, + "unit": "metre", + "id": { + "authority": "EPSG", + "code": 8806 + } + }, + { + "name": "False northing", + "value": 0, + "unit": "metre", + "id": { + "authority": "EPSG", + "code": 8807 + } + } + ] + }, + "coordinate_system": { + "subtype": "Cartesian", + "axis": [ + { + "name": "Easting", + "abbreviation": "E", + "direction": "east", + "unit": "metre" + }, + { + "name": "Northing", + "abbreviation": "N", + "direction": "north", + "unit": "metre" + } + ] + }, + "area": "World - N hemisphere - 102°W to 96°W - by country", + "bbox": { + "south_latitude": 0, + "west_longitude": -102, + "north_latitude": 84, + "east_longitude": -96 + }, + "id": { + "authority": "EPSG", + "code": 32614 + } + }, + "proj:geometry": { + "coordinates": [ + [ + [ + 169200, + 3712800 + ], + [ + 403200, + 3712800 + ], + [ + 403200, + 3951000 + ], + [ + 169200, + 3951000 + ], + [ + 169200, + 3712800 + ] + ] + ], + "type": "Polygon" + }, + "proj:bbox": [ + 169200, + 3712800, + 403200, + 3951000 + ], + "proj:centroid": { + "lat": 34.595302781575604, + "lon": -101.34448382627504 + }, + "proj:shape": [ + 8391, + 8311 + ], + "proj:transform": [ + 30, + 0, + 224985, + 0, + -30, + 6790215, + 0, + 0, + 1 + ] + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 152.52758, + 60.63437 + ], + [ + 149.1755, + 61.19016 + ], + [ + 148.13933, + 59.51584 + ], + [ + 151.33786, + 58.97792 + ], + [ + 152.52758, + 60.63437 + ] + ] + ] + }, + "links": [ + { + "rel": "root", + "href": "../../catalog.json", + "type": "application/json", + "title": "Example Catalog" + }, + { + "rel": "parent", + "href": "../collection.json", + "type": "application/json", + "title": "Collection of Extension Items" + }, + { + "rel": "collection", + "href": "../collection.json", + "type": "application/json", + "title": "Collection of Extension Items" + } + ], + "assets": { + "B1": { + "href": "https://landsat-pds.s3.amazonaws.com/c1/L8/107/018/LC08_L1TP_107018_20181001_20181001_01_RT/LC08_L1TP_107018_20181001_20181001_01_RT_B1.TIF", + "type": "image/tiff; application=geotiff", + "title": "Band 1 (coastal)", + "bands": [ + { + "name": "B1", + "eo:common_name": "coastal", + "eo:center_wavelength": 0.44, + "eo:full_width_half_max": 0.02 + } + ] + }, + "B8": { + "href": "https://landsat-pds.s3.amazonaws.com/c1/L8/107/018/LC08_L1TP_107018_20181001_20181001_01_RT/LC08_L1TP_107018_20181001_20181001_01_RT_B8.TIF", + "type": "image/tiff; application=geotiff", + "title": "Band 8 (panchromatic)", + "bands": [ + { + "name": "B8", + "eo:center_wavelength": 0.59, + "eo:full_width_half_max": 0.18 + } + ], + "proj:shape": [ + 16781, + 16621 + ], + "proj:transform": [ + 15, + 0, + 224992.5, + 0, + -15, + 6790207.5, + 0, + 0, + 1 + ] + } + }, + "bbox": [ + 148.13933, + 59.51584, + 152.52758, + 60.63437 + ], + "stac_extensions": [ + "https://stac-extensions.github.io/eo/v2.0.0/schema.json", + "https://stac-extensions.github.io/projection/v2.0.0/schema.json" + ], + "collection": "landsat-8-l1" +} diff --git a/tests/v1/data-files/examples/1.1.0/simple-item.json b/tests/v1/data-files/examples/1.1.0/simple-item.json new file mode 100644 index 000000000..449352edf --- /dev/null +++ b/tests/v1/data-files/examples/1.1.0/simple-item.json @@ -0,0 +1,81 @@ +{ + "stac_version": "1.1.0", + "stac_extensions": [], + "type": "Feature", + "id": "20201211_223832_CS2", + "bbox": [ + 172.91173669923782, + 1.3438851951615003, + 172.95469614953714, + 1.3690476620161975 + ], + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 172.91173669923782, + 1.3438851951615003 + ], + [ + 172.95469614953714, + 1.3438851951615003 + ], + [ + 172.95469614953714, + 1.3690476620161975 + ], + [ + 172.91173669923782, + 1.3690476620161975 + ], + [ + 172.91173669923782, + 1.3438851951615003 + ] + ] + ] + }, + "properties": { + "datetime": "2020-12-11T22:38:32.125000Z" + }, + "collection": "simple-collection", + "links": [ + { + "rel": "collection", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "root", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + }, + { + "rel": "parent", + "href": "./collection.json", + "type": "application/json", + "title": "Simple Example Collection" + } + ], + "assets": { + "visual": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "3-Band Visual", + "roles": [ + "visual" + ] + }, + "thumbnail": { + "href": "https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2.jpg", + "title": "Thumbnail", + "type": "image/jpeg", + "roles": [ + "thumbnail" + ] + } + } +} diff --git a/tests/data-files/examples/example-info.csv b/tests/v1/data-files/examples/example-info.csv similarity index 100% rename from tests/data-files/examples/example-info.csv rename to tests/v1/data-files/examples/example-info.csv diff --git a/tests/data-files/examples/hand-0.8.1/010100/010100.json b/tests/v1/data-files/examples/hand-0.8.1/010100/010100.json similarity index 100% rename from tests/data-files/examples/hand-0.8.1/010100/010100.json rename to tests/v1/data-files/examples/hand-0.8.1/010100/010100.json diff --git a/tests/data-files/examples/hand-0.8.1/collection.json b/tests/v1/data-files/examples/hand-0.8.1/collection.json similarity index 100% rename from tests/data-files/examples/hand-0.8.1/collection.json rename to tests/v1/data-files/examples/hand-0.8.1/collection.json diff --git a/tests/data-files/examples/hand-0.9.0/010100/010100.json b/tests/v1/data-files/examples/hand-0.9.0/010100/010100.json similarity index 100% rename from tests/data-files/examples/hand-0.9.0/010100/010100.json rename to tests/v1/data-files/examples/hand-0.9.0/010100/010100.json diff --git a/tests/data-files/examples/hand-0.9.0/collection.json b/tests/v1/data-files/examples/hand-0.9.0/collection.json similarity index 100% rename from tests/data-files/examples/hand-0.9.0/collection.json rename to tests/v1/data-files/examples/hand-0.9.0/collection.json diff --git a/tests/data-files/file/catalog.json b/tests/v1/data-files/file/catalog.json similarity index 100% rename from tests/data-files/file/catalog.json rename to tests/v1/data-files/file/catalog.json diff --git a/tests/data-files/file/collection.json b/tests/v1/data-files/file/collection.json similarity index 100% rename from tests/data-files/file/collection.json rename to tests/v1/data-files/file/collection.json diff --git a/tests/data-files/file/item.json b/tests/v1/data-files/file/item.json similarity index 100% rename from tests/data-files/file/item.json rename to tests/v1/data-files/file/item.json diff --git a/tests/data-files/geojson/sample.geojson b/tests/v1/data-files/geojson/sample.geojson similarity index 100% rename from tests/data-files/geojson/sample.geojson rename to tests/v1/data-files/geojson/sample.geojson diff --git a/tests/data-files/get_examples.py b/tests/v1/data-files/get_examples.py similarity index 100% rename from tests/data-files/get_examples.py rename to tests/v1/data-files/get_examples.py diff --git a/tests/data-files/grid/example-landsat.json b/tests/v1/data-files/grid/example-landsat.json similarity index 100% rename from tests/data-files/grid/example-landsat.json rename to tests/v1/data-files/grid/example-landsat.json diff --git a/tests/data-files/grid/example-sentinel2.json b/tests/v1/data-files/grid/example-sentinel2.json similarity index 100% rename from tests/data-files/grid/example-sentinel2.json rename to tests/v1/data-files/grid/example-sentinel2.json diff --git a/tests/data-files/invalid/shared-id/catalog.json b/tests/v1/data-files/invalid/shared-id/catalog.json similarity index 100% rename from tests/data-files/invalid/shared-id/catalog.json rename to tests/v1/data-files/invalid/shared-id/catalog.json diff --git a/tests/data-files/invalid/shared-id/test/collection.json b/tests/v1/data-files/invalid/shared-id/test/collection.json similarity index 100% rename from tests/data-files/invalid/shared-id/test/collection.json rename to tests/v1/data-files/invalid/shared-id/test/collection.json diff --git a/tests/data-files/invalid/shared-id/test/test-item-1/test-item-1.json b/tests/v1/data-files/invalid/shared-id/test/test-item-1/test-item-1.json similarity index 100% rename from tests/data-files/invalid/shared-id/test/test-item-1/test-item-1.json rename to tests/v1/data-files/invalid/shared-id/test/test-item-1/test-item-1.json diff --git a/tests/v1/data-files/item-assets/example-landsat8.json b/tests/v1/data-files/item-assets/example-landsat8.json new file mode 100644 index 000000000..237f3bb92 --- /dev/null +++ b/tests/v1/data-files/item-assets/example-landsat8.json @@ -0,0 +1,311 @@ +{ + "stac_version": "1.1.0", + "stac_extensions": [ + "eo" + ], + "type": "Collection", + "id": "landsat-8-l1", + "title": "Landsat 8 L1", + "description": "Landsat 8 imagery radiometrically calibrated and orthorectified using ground points and Digital Elevation Model (DEM) data to correct relief displacement.", + "keywords": [ + "landsat", + "earth observation", + "usgs" + ], + "extent": { + "spatial": { + "bbox": [ + [ + -180, + -90, + 180, + 90 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2013-06-01T00:00:00Z", + null + ] + ] + } + }, + "providers": [ + { + "name": "USGS", + "roles": [ + "producer" + ], + "url": "https://landsat.usgs.gov/" + }, + { + "name": "Planet Labs", + "roles": [ + "processor" + ], + "url": "https://github.com/landsat-pds/landsat_ingestor" + }, + { + "name": "AWS", + "roles": [ + "host" + ], + "url": "https://landsatonaws.com/" + }, + { + "name": "Development Seed", + "roles": [ + "processor" + ], + "url": "https://github.com/sat-utils/sat-api" + } + ], + "license": "PDDL-1.0", + "summaries": { + "gsd": [ + 15 + ], + "platform": [ + "landsat-8" + ], + "instruments": [ + "oli", + "tirs" + ], + "view:off_nadir": [ + 0 + ], + "eo:bands": [ + { + "name": "B1", + "common_name": "coastal", + "center_wavelength": 0.44, + "full_width_half_max": 0.02 + }, + { + "name": "B2", + "common_name": "blue", + "center_wavelength": 0.48, + "full_width_half_max": 0.06 + }, + { + "name": "B3", + "common_name": "green", + "center_wavelength": 0.56, + "full_width_half_max": 0.06 + }, + { + "name": "B4", + "common_name": "red", + "center_wavelength": 0.65, + "full_width_half_max": 0.04 + }, + { + "name": "B5", + "common_name": "nir", + "center_wavelength": 0.86, + "full_width_half_max": 0.03 + }, + { + "name": "B6", + "common_name": "swir16", + "center_wavelength": 1.6, + "full_width_half_max": 0.08 + }, + { + "name": "B7", + "common_name": "swir22", + "center_wavelength": 2.2, + "full_width_half_max": 0.2 + }, + { + "name": "B8", + "common_name": "pan", + "center_wavelength": 0.59, + "full_width_half_max": 0.18 + }, + { + "name": "B9", + "common_name": "cirrus", + "center_wavelength": 1.37, + "full_width_half_max": 0.02 + }, + { + "name": "B10", + "common_name": "lwir11", + "center_wavelength": 10.9, + "full_width_half_max": 0.8 + }, + { + "name": "B11", + "common_name": "lwir12", + "center_wavelength": 12, + "full_width_half_max": 1 + } + ] + }, + "item_assets": { + "thumbnail": { + "type": "image/jpeg", + "title": "Thumbnail", + "description": "A medium sized thumbnail", + "roles": [ + "thumbnail" + ] + }, + "metadata": { + "type": "mtl", + "roles": [ + "metadata" + ], + "title": "Original Metadata", + "description": "The original MTL metadata file provided for each Landsat scene" + }, + "B1": { + "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" + }, + "B2": { + "type": "image/tiff; application=geotiff", + "eo:bands": [ + { + "name": "B2", + "common_name": "blue", + "center_wavelength": 0.48, + "full_width_half_max": 0.06 + } + ], + "title": "Blue Band (B2)", + "description": "Blue Band Top Of the Atmosphere" + }, + "B3": { + "type": "image/tiff; application=geotiff", + "eo:bands": [ + { + "name": "B3", + "common_name": "green", + "center_wavelength": 0.56, + "full_width_half_max": 0.06 + } + ], + "title": "Green Band (B3)", + "description": "Green Band (B3) Top Of the Atmosphere" + }, + "B4": { + "type": "image/tiff; application=geotiff", + "eo:bands": [ + { + "name": "B4", + "common_name": "red", + "center_wavelength": 0.65, + "full_width_half_max": 0.04 + } + ], + "title": "Red Band (B4)", + "description": "Red Band (B4) Top Of the Atmosphere" + }, + "B5": { + "type": "image/tiff; application=geotiff", + "eo:bands": [ + { + "name": "B5", + "common_name": "nir", + "center_wavelength": 0.86, + "full_width_half_max": 0.03 + } + ], + "title": "NIR Band (B5)", + "description": "NIR Band (B5) Top Of the Atmosphere" + }, + "B6": { + "type": "image/tiff; application=geotiff", + "eo:bands": [ + { + "name": "B6", + "common_name": "swir16", + "center_wavelength": 1.6, + "full_width_half_max": 0.08 + } + ], + "title": "SWIR Band (B6)", + "description": "SWIR Band at 1.6um (B6) Top Of the Atmosphere" + }, + "B7": { + "type": "image/tiff; application=geotiff", + "eo:bands": [ + { + "name": "B7", + "common_name": "swir22", + "center_wavelength": 2.2, + "full_width_half_max": 0.2 + } + ], + "title": "SWIR Band (B7)", + "description": "SWIR Band at 2.2um (B7) Top Of the Atmosphere" + }, + "B8": { + "type": "image/tiff; application=geotiff", + "eo:bands": [ + { + "name": "B8", + "common_name": "pan", + "center_wavelength": 0.59, + "full_width_half_max": 0.18 + } + ], + "title": "Panchromatic Band (B8)", + "description": "Panchromatic Band (B8) Top Of the Atmosphere" + }, + "B9": { + "type": "image/tiff; application=geotiff", + "eo:bands": [ + { + "name": "B9", + "common_name": "cirrus", + "center_wavelength": 1.37, + "full_width_half_max": 0.02 + } + ], + "title": "Cirrus Band (B9)", + "description": "Cirrus Band (B9) Top Of the Atmosphere - for cirrus cloud detection" + }, + "B10": { + "type": "image/tiff; application=geotiff", + "eo:bands": [ + { + "name": "B10", + "common_name": "lwir11", + "center_wavelength": 10.9, + "full_width_half_max": 0.8 + } + ], + "title": "LWIR Band (B10)", + "description": "Long-wave IR Band at 11um (B10) Top Of the Atmosphere" + }, + "B11": { + "type": "image/tiff; application=geotiff", + "eo:bands": [ + { + "name": "B11", + "common_name": "lwir12", + "center_wavelength": 12, + "full_width_half_max": 1 + } + ], + "title": "LWIR Band (B11)", + "description": "Long-wave IR Band at 12um (B11) Top Of the Atmosphere" + } + }, + "links": [] +} \ No newline at end of file diff --git a/tests/data-files/item-collection/sample-item-collection.json b/tests/v1/data-files/item-collection/sample-item-collection.json similarity index 100% rename from tests/data-files/item-collection/sample-item-collection.json rename to tests/v1/data-files/item-collection/sample-item-collection.json diff --git a/tests/data-files/item/sample-item-asset-properties.json b/tests/v1/data-files/item/sample-item-asset-properties.json similarity index 100% rename from tests/data-files/item/sample-item-asset-properties.json rename to tests/v1/data-files/item/sample-item-asset-properties.json diff --git a/tests/data-files/item/sample-item-with-relative-extension-path.json b/tests/v1/data-files/item/sample-item-with-relative-extension-path.json similarity index 100% rename from tests/data-files/item/sample-item-with-relative-extension-path.json rename to tests/v1/data-files/item/sample-item-with-relative-extension-path.json diff --git a/tests/data-files/item/sample-item.json b/tests/v1/data-files/item/sample-item.json similarity index 100% rename from tests/data-files/item/sample-item.json rename to tests/v1/data-files/item/sample-item.json diff --git a/tests/data-files/mgrs/item.json b/tests/v1/data-files/mgrs/item.json similarity index 100% rename from tests/data-files/mgrs/item.json rename to tests/v1/data-files/mgrs/item.json diff --git a/tests/data-files/mlm/collection.json b/tests/v1/data-files/mlm/collection.json similarity index 100% rename from tests/data-files/mlm/collection.json rename to tests/v1/data-files/mlm/collection.json diff --git a/tests/data-files/mlm/item_basic.json b/tests/v1/data-files/mlm/item_basic.json similarity index 100% rename from tests/data-files/mlm/item_basic.json rename to tests/v1/data-files/mlm/item_basic.json diff --git a/tests/data-files/pointcloud/example-laz-no-statistics.json b/tests/v1/data-files/pointcloud/example-laz-no-statistics.json similarity index 100% rename from tests/data-files/pointcloud/example-laz-no-statistics.json rename to tests/v1/data-files/pointcloud/example-laz-no-statistics.json diff --git a/tests/data-files/pointcloud/example-laz.json b/tests/v1/data-files/pointcloud/example-laz.json similarity index 100% rename from tests/data-files/pointcloud/example-laz.json rename to tests/v1/data-files/pointcloud/example-laz.json diff --git a/tests/data-files/projection/another-1.1.json b/tests/v1/data-files/projection/another-1.1.json similarity index 100% rename from tests/data-files/projection/another-1.1.json rename to tests/v1/data-files/projection/another-1.1.json diff --git a/tests/data-files/projection/collection-with-summaries.json b/tests/v1/data-files/projection/collection-with-summaries.json similarity index 100% rename from tests/data-files/projection/collection-with-summaries.json rename to tests/v1/data-files/projection/collection-with-summaries.json diff --git a/tests/data-files/projection/example-landsat8.json b/tests/v1/data-files/projection/example-landsat8.json similarity index 100% rename from tests/data-files/projection/example-landsat8.json rename to tests/v1/data-files/projection/example-landsat8.json diff --git a/tests/data-files/projection/example-with-version-1.1.json b/tests/v1/data-files/projection/example-with-version-1.1.json similarity index 100% rename from tests/data-files/projection/example-with-version-1.1.json rename to tests/v1/data-files/projection/example-with-version-1.1.json diff --git a/tests/data-files/projection/example-with-version-1.2.json b/tests/v1/data-files/projection/example-with-version-1.2.json similarity index 100% rename from tests/data-files/projection/example-with-version-1.2.json rename to tests/v1/data-files/projection/example-with-version-1.2.json diff --git a/tests/data-files/projection/optional-epsg.json b/tests/v1/data-files/projection/optional-epsg.json similarity index 100% rename from tests/data-files/projection/optional-epsg.json rename to tests/v1/data-files/projection/optional-epsg.json diff --git a/tests/data-files/raster/gdalinfo.json b/tests/v1/data-files/raster/gdalinfo.json similarity index 100% rename from tests/data-files/raster/gdalinfo.json rename to tests/v1/data-files/raster/gdalinfo.json diff --git a/tests/data-files/raster/landsat-collection-example.json b/tests/v1/data-files/raster/landsat-collection-example.json similarity index 100% rename from tests/data-files/raster/landsat-collection-example.json rename to tests/v1/data-files/raster/landsat-collection-example.json diff --git a/tests/data-files/raster/raster-planet-example.json b/tests/v1/data-files/raster/raster-planet-example.json similarity index 100% rename from tests/data-files/raster/raster-planet-example.json rename to tests/v1/data-files/raster/raster-planet-example.json diff --git a/tests/data-files/raster/raster-sentinel2-example.json b/tests/v1/data-files/raster/raster-sentinel2-example.json similarity index 100% rename from tests/data-files/raster/raster-sentinel2-example.json rename to tests/v1/data-files/raster/raster-sentinel2-example.json diff --git a/tests/data-files/render/collection.json b/tests/v1/data-files/render/collection.json similarity index 100% rename from tests/data-files/render/collection.json rename to tests/v1/data-files/render/collection.json diff --git a/tests/data-files/render/item.json b/tests/v1/data-files/render/item.json similarity index 100% rename from tests/data-files/render/item.json rename to tests/v1/data-files/render/item.json diff --git a/tests/data-files/sar/sentinel-1.json b/tests/v1/data-files/sar/sentinel-1.json similarity index 100% rename from tests/data-files/sar/sentinel-1.json rename to tests/v1/data-files/sar/sentinel-1.json diff --git a/tests/data-files/sat/sentinel-1.json b/tests/v1/data-files/sat/sentinel-1.json similarity index 100% rename from tests/data-files/sat/sentinel-1.json rename to tests/v1/data-files/sat/sentinel-1.json diff --git a/tests/data-files/schemas/v1.0.0-projection.json b/tests/v1/data-files/schemas/v1.0.0-projection.json similarity index 100% rename from tests/data-files/schemas/v1.0.0-projection.json rename to tests/v1/data-files/schemas/v1.0.0-projection.json diff --git a/tests/data-files/scientific/collection.json b/tests/v1/data-files/scientific/collection.json similarity index 100% rename from tests/data-files/scientific/collection.json rename to tests/v1/data-files/scientific/collection.json diff --git a/tests/data-files/scientific/item.json b/tests/v1/data-files/scientific/item.json similarity index 100% rename from tests/data-files/scientific/item.json rename to tests/v1/data-files/scientific/item.json diff --git a/tests/data-files/storage/collection-naip.json b/tests/v1/data-files/storage/collection-naip.json similarity index 100% rename from tests/data-files/storage/collection-naip.json rename to tests/v1/data-files/storage/collection-naip.json diff --git a/tests/data-files/storage/item-naip.json b/tests/v1/data-files/storage/item-naip.json similarity index 100% rename from tests/data-files/storage/item-naip.json rename to tests/v1/data-files/storage/item-naip.json diff --git a/tests/data-files/storage/item-v1.0.0.json b/tests/v1/data-files/storage/item-v1.0.0.json similarity index 100% rename from tests/data-files/storage/item-v1.0.0.json rename to tests/v1/data-files/storage/item-v1.0.0.json diff --git a/tests/data-files/summaries/fields_no_bands.json b/tests/v1/data-files/summaries/fields_no_bands.json similarity index 100% rename from tests/data-files/summaries/fields_no_bands.json rename to tests/v1/data-files/summaries/fields_no_bands.json diff --git a/tests/data-files/table/README.md b/tests/v1/data-files/table/README.md similarity index 100% rename from tests/data-files/table/README.md rename to tests/v1/data-files/table/README.md diff --git a/tests/data-files/table/collection-2.json b/tests/v1/data-files/table/collection-2.json similarity index 100% rename from tests/data-files/table/collection-2.json rename to tests/v1/data-files/table/collection-2.json diff --git a/tests/data-files/table/collection.json b/tests/v1/data-files/table/collection.json similarity index 100% rename from tests/data-files/table/collection.json rename to tests/v1/data-files/table/collection.json diff --git a/tests/data-files/table/item.json b/tests/v1/data-files/table/item.json similarity index 100% rename from tests/data-files/table/item.json rename to tests/v1/data-files/table/item.json diff --git a/tests/data-files/table/table-collection.json b/tests/v1/data-files/table/table-collection.json similarity index 100% rename from tests/data-files/table/table-collection.json rename to tests/v1/data-files/table/table-collection.json diff --git a/tests/data-files/timestamps/example-landsat8.json b/tests/v1/data-files/timestamps/example-landsat8.json similarity index 100% rename from tests/data-files/timestamps/example-landsat8.json rename to tests/v1/data-files/timestamps/example-landsat8.json diff --git a/tests/data-files/version/collection.json b/tests/v1/data-files/version/collection.json similarity index 100% rename from tests/data-files/version/collection.json rename to tests/v1/data-files/version/collection.json diff --git a/tests/data-files/version/item.json b/tests/v1/data-files/version/item.json similarity index 100% rename from tests/data-files/version/item.json rename to tests/v1/data-files/version/item.json diff --git a/tests/data-files/view/collection-with-summaries.json b/tests/v1/data-files/view/collection-with-summaries.json similarity index 100% rename from tests/data-files/view/collection-with-summaries.json rename to tests/v1/data-files/view/collection-with-summaries.json diff --git a/tests/data-files/view/example-landsat8.json b/tests/v1/data-files/view/example-landsat8.json similarity index 100% rename from tests/data-files/view/example-landsat8.json rename to tests/v1/data-files/view/example-landsat8.json diff --git a/tests/data-files/windows_hrefs/catalog.json b/tests/v1/data-files/windows_hrefs/catalog.json similarity index 100% rename from tests/data-files/windows_hrefs/catalog.json rename to tests/v1/data-files/windows_hrefs/catalog.json diff --git a/tests/data-files/windows_hrefs/test-collection/collection.json b/tests/v1/data-files/windows_hrefs/test-collection/collection.json similarity index 100% rename from tests/data-files/windows_hrefs/test-collection/collection.json rename to tests/v1/data-files/windows_hrefs/test-collection/collection.json diff --git a/tests/data-files/windows_hrefs/test-collection/test-item/test-asset.txt b/tests/v1/data-files/windows_hrefs/test-collection/test-item/test-asset.txt similarity index 100% rename from tests/data-files/windows_hrefs/test-collection/test-item/test-asset.txt rename to tests/v1/data-files/windows_hrefs/test-collection/test-item/test-asset.txt diff --git a/tests/data-files/windows_hrefs/test-collection/test-item/test-item.json b/tests/v1/data-files/windows_hrefs/test-collection/test-item/test-item.json similarity index 100% rename from tests/data-files/windows_hrefs/test-collection/test-item/test-item.json rename to tests/v1/data-files/windows_hrefs/test-collection/test-item/test-item.json diff --git a/tests/data-files/xarray-assets/collection.json b/tests/v1/data-files/xarray-assets/collection.json similarity index 100% rename from tests/data-files/xarray-assets/collection.json rename to tests/v1/data-files/xarray-assets/collection.json diff --git a/tests/data-files/xarray-assets/item.json b/tests/v1/data-files/xarray-assets/item.json similarity index 100% rename from tests/data-files/xarray-assets/item.json rename to tests/v1/data-files/xarray-assets/item.json diff --git a/tests/extensions/__init__.py b/tests/v1/extensions/__init__.py similarity index 100% rename from tests/extensions/__init__.py rename to tests/v1/extensions/__init__.py diff --git a/tests/extensions/cassettes/test_classification/test_apply_bitfields.yaml b/tests/v1/extensions/cassettes/test_classification/test_apply_bitfields.yaml similarity index 100% rename from tests/extensions/cassettes/test_classification/test_apply_bitfields.yaml rename to tests/v1/extensions/cassettes/test_classification/test_apply_bitfields.yaml diff --git a/tests/extensions/cassettes/test_classification/test_apply_classes.yaml b/tests/v1/extensions/cassettes/test_classification/test_apply_classes.yaml similarity index 100% rename from tests/extensions/cassettes/test_classification/test_apply_classes.yaml rename to tests/v1/extensions/cassettes/test_classification/test_apply_classes.yaml diff --git a/tests/extensions/cassettes/test_classification/test_validate_classification.yaml b/tests/v1/extensions/cassettes/test_classification/test_validate_classification.yaml similarity index 100% rename from tests/extensions/cassettes/test_classification/test_validate_classification.yaml rename to tests/v1/extensions/cassettes/test_classification/test_validate_classification.yaml diff --git a/tests/extensions/cassettes/test_datacube/test_set_dimensions.yaml b/tests/v1/extensions/cassettes/test_datacube/test_set_dimensions.yaml similarity index 100% rename from tests/extensions/cassettes/test_datacube/test_set_dimensions.yaml rename to tests/v1/extensions/cassettes/test_datacube/test_set_dimensions.yaml diff --git a/tests/extensions/cassettes/test_datacube/test_set_variables.yaml b/tests/v1/extensions/cassettes/test_datacube/test_set_variables.yaml similarity index 100% rename from tests/extensions/cassettes/test_datacube/test_set_variables.yaml rename to tests/v1/extensions/cassettes/test_datacube/test_set_variables.yaml diff --git a/tests/extensions/cassettes/test_datacube/test_validate.yaml b/tests/v1/extensions/cassettes/test_datacube/test_validate.yaml similarity index 100% rename from tests/extensions/cassettes/test_datacube/test_validate.yaml rename to tests/v1/extensions/cassettes/test_datacube/test_validate.yaml diff --git a/tests/extensions/cassettes/test_eo/test_asset_bands.yaml b/tests/v1/extensions/cassettes/test_eo/test_asset_bands.yaml similarity index 100% rename from tests/extensions/cassettes/test_eo/test_asset_bands.yaml rename to tests/v1/extensions/cassettes/test_eo/test_asset_bands.yaml diff --git a/tests/extensions/cassettes/test_eo/test_bands.yaml b/tests/v1/extensions/cassettes/test_eo/test_bands.yaml similarity index 100% rename from tests/extensions/cassettes/test_eo/test_bands.yaml rename to tests/v1/extensions/cassettes/test_eo/test_bands.yaml diff --git a/tests/extensions/cassettes/test_eo/test_cloud_cover.yaml b/tests/v1/extensions/cassettes/test_eo/test_cloud_cover.yaml similarity index 100% rename from tests/extensions/cassettes/test_eo/test_cloud_cover.yaml rename to tests/v1/extensions/cassettes/test_eo/test_cloud_cover.yaml diff --git a/tests/extensions/cassettes/test_eo/test_set_field[cloud_cover-7.8].yaml b/tests/v1/extensions/cassettes/test_eo/test_set_field[cloud_cover-7.8].yaml similarity index 100% rename from tests/extensions/cassettes/test_eo/test_set_field[cloud_cover-7.8].yaml rename to tests/v1/extensions/cassettes/test_eo/test_set_field[cloud_cover-7.8].yaml diff --git a/tests/extensions/cassettes/test_eo/test_set_field[snow_cover-99].yaml b/tests/v1/extensions/cassettes/test_eo/test_set_field[snow_cover-99].yaml similarity index 100% rename from tests/extensions/cassettes/test_eo/test_set_field[snow_cover-99].yaml rename to tests/v1/extensions/cassettes/test_eo/test_set_field[snow_cover-99].yaml diff --git a/tests/extensions/cassettes/test_eo/test_validate_eo.yaml b/tests/v1/extensions/cassettes/test_eo/test_validate_eo.yaml similarity index 100% rename from tests/extensions/cassettes/test_eo/test_validate_eo.yaml rename to tests/v1/extensions/cassettes/test_eo/test_validate_eo.yaml diff --git a/tests/extensions/cassettes/test_file/test_migrate_from_v1_0_0.yaml b/tests/v1/extensions/cassettes/test_file/test_migrate_from_v1_0_0.yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_migrate_from_v1_0_0.yaml rename to tests/v1/extensions/cassettes/test_file/test_migrate_from_v1_0_0.yaml diff --git a/tests/extensions/cassettes/test_file/test_migrate_from_v2_0_0.yaml b/tests/v1/extensions/cassettes/test_file/test_migrate_from_v2_0_0.yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_migrate_from_v2_0_0.yaml rename to tests/v1/extensions/cassettes/test_file/test_migrate_from_v2_0_0.yaml diff --git a/tests/extensions/cassettes/test_file/test_set_field_on_asset[calibrations-local_path-different-file.xml].yaml b/tests/v1/extensions/cassettes/test_file/test_set_field_on_asset[calibrations-local_path-different-file.xml].yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_set_field_on_asset[calibrations-local_path-different-file.xml].yaml rename to tests/v1/extensions/cassettes/test_file/test_set_field_on_asset[calibrations-local_path-different-file.xml].yaml diff --git a/tests/extensions/cassettes/test_file/test_set_field_on_asset[measurement-header_size-8192].yaml b/tests/v1/extensions/cassettes/test_file/test_set_field_on_asset[measurement-header_size-8192].yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_set_field_on_asset[measurement-header_size-8192].yaml rename to tests/v1/extensions/cassettes/test_file/test_set_field_on_asset[measurement-header_size-8192].yaml diff --git a/tests/extensions/cassettes/test_file/test_set_field_on_asset[thumbnail-byte_order-little-endian].yaml b/tests/v1/extensions/cassettes/test_file/test_set_field_on_asset[thumbnail-byte_order-little-endian].yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_set_field_on_asset[thumbnail-byte_order-little-endian].yaml rename to tests/v1/extensions/cassettes/test_file/test_set_field_on_asset[thumbnail-byte_order-little-endian].yaml diff --git a/tests/extensions/cassettes/test_file/test_set_field_on_asset[thumbnail-checksum-90e40210163700a8a6501eccd00b6d3b44ddaed0].yaml b/tests/v1/extensions/cassettes/test_file/test_set_field_on_asset[thumbnail-checksum-90e40210163700a8a6501eccd00b6d3b44ddaed0].yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_set_field_on_asset[thumbnail-checksum-90e40210163700a8a6501eccd00b6d3b44ddaed0].yaml rename to tests/v1/extensions/cassettes/test_file/test_set_field_on_asset[thumbnail-checksum-90e40210163700a8a6501eccd00b6d3b44ddaed0].yaml diff --git a/tests/extensions/cassettes/test_file/test_set_field_on_asset[thumbnail-size-1].yaml b/tests/v1/extensions/cassettes/test_file/test_set_field_on_asset[thumbnail-size-1].yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_set_field_on_asset[thumbnail-size-1].yaml rename to tests/v1/extensions/cassettes/test_file/test_set_field_on_asset[thumbnail-size-1].yaml diff --git a/tests/extensions/cassettes/test_file/test_set_field_on_link[about-byte_order-big-endian].yaml b/tests/v1/extensions/cassettes/test_file/test_set_field_on_link[about-byte_order-big-endian].yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_set_field_on_link[about-byte_order-big-endian].yaml rename to tests/v1/extensions/cassettes/test_file/test_set_field_on_link[about-byte_order-big-endian].yaml diff --git a/tests/extensions/cassettes/test_file/test_set_field_on_link[about-checksum-90e40210163700a8a6501eccd00b6d3b44ddaedb].yaml b/tests/v1/extensions/cassettes/test_file/test_set_field_on_link[about-checksum-90e40210163700a8a6501eccd00b6d3b44ddaedb].yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_set_field_on_link[about-checksum-90e40210163700a8a6501eccd00b6d3b44ddaedb].yaml rename to tests/v1/extensions/cassettes/test_file/test_set_field_on_link[about-checksum-90e40210163700a8a6501eccd00b6d3b44ddaedb].yaml diff --git a/tests/extensions/cassettes/test_file/test_set_field_on_link[about-header_size-4092].yaml b/tests/v1/extensions/cassettes/test_file/test_set_field_on_link[about-header_size-4092].yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_set_field_on_link[about-header_size-4092].yaml rename to tests/v1/extensions/cassettes/test_file/test_set_field_on_link[about-header_size-4092].yaml diff --git a/tests/extensions/cassettes/test_file/test_set_field_on_link[about-local_path-a-path].yaml b/tests/v1/extensions/cassettes/test_file/test_set_field_on_link[about-local_path-a-path].yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_set_field_on_link[about-local_path-a-path].yaml rename to tests/v1/extensions/cassettes/test_file/test_set_field_on_link[about-local_path-a-path].yaml diff --git a/tests/extensions/cassettes/test_file/test_set_field_on_link[about-size-129302].yaml b/tests/v1/extensions/cassettes/test_file/test_set_field_on_link[about-size-129302].yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_set_field_on_link[about-size-129302].yaml rename to tests/v1/extensions/cassettes/test_file/test_set_field_on_link[about-size-129302].yaml diff --git a/tests/extensions/cassettes/test_file/test_validate_catalog.yaml b/tests/v1/extensions/cassettes/test_file/test_validate_catalog.yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_validate_catalog.yaml rename to tests/v1/extensions/cassettes/test_file/test_validate_catalog.yaml diff --git a/tests/extensions/cassettes/test_file/test_validate_collection.yaml b/tests/v1/extensions/cassettes/test_file/test_validate_collection.yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_validate_collection.yaml rename to tests/v1/extensions/cassettes/test_file/test_validate_collection.yaml diff --git a/tests/extensions/cassettes/test_file/test_validate_item.yaml b/tests/v1/extensions/cassettes/test_file/test_validate_item.yaml similarity index 100% rename from tests/extensions/cassettes/test_file/test_validate_item.yaml rename to tests/v1/extensions/cassettes/test_file/test_validate_item.yaml diff --git a/tests/extensions/cassettes/test_grid/test_attributes.yaml b/tests/v1/extensions/cassettes/test_grid/test_attributes.yaml similarity index 100% rename from tests/extensions/cassettes/test_grid/test_attributes.yaml rename to tests/v1/extensions/cassettes/test_grid/test_attributes.yaml diff --git a/tests/extensions/cassettes/test_grid/test_modify.yaml b/tests/v1/extensions/cassettes/test_grid/test_modify.yaml similarity index 100% rename from tests/extensions/cassettes/test_grid/test_modify.yaml rename to tests/v1/extensions/cassettes/test_grid/test_modify.yaml diff --git a/tests/extensions/cassettes/test_mgrs/test_set_field[grid_square-ZA].yaml b/tests/v1/extensions/cassettes/test_mgrs/test_set_field[grid_square-ZA].yaml similarity index 100% rename from tests/extensions/cassettes/test_mgrs/test_set_field[grid_square-ZA].yaml rename to tests/v1/extensions/cassettes/test_mgrs/test_set_field[grid_square-ZA].yaml diff --git a/tests/extensions/cassettes/test_mgrs/test_set_field[latitude_band-C].yaml b/tests/v1/extensions/cassettes/test_mgrs/test_set_field[latitude_band-C].yaml similarity index 100% rename from tests/extensions/cassettes/test_mgrs/test_set_field[latitude_band-C].yaml rename to tests/v1/extensions/cassettes/test_mgrs/test_set_field[latitude_band-C].yaml diff --git a/tests/extensions/cassettes/test_mgrs/test_set_field[utm_zone-59].yaml b/tests/v1/extensions/cassettes/test_mgrs/test_set_field[utm_zone-59].yaml similarity index 100% rename from tests/extensions/cassettes/test_mgrs/test_set_field[utm_zone-59].yaml rename to tests/v1/extensions/cassettes/test_mgrs/test_set_field[utm_zone-59].yaml diff --git a/tests/extensions/cassettes/test_mgrs/test_validate.yaml b/tests/v1/extensions/cassettes/test_mgrs/test_validate.yaml similarity index 100% rename from tests/extensions/cassettes/test_mgrs/test_validate.yaml rename to tests/v1/extensions/cassettes/test_mgrs/test_validate.yaml diff --git a/tests/extensions/cassettes/test_mlm/test_apply.yaml b/tests/v1/extensions/cassettes/test_mlm/test_apply.yaml similarity index 100% rename from tests/extensions/cassettes/test_mlm/test_apply.yaml rename to tests/v1/extensions/cassettes/test_mlm/test_apply.yaml diff --git a/tests/extensions/cassettes/test_mlm/test_validate_mlm.yaml b/tests/v1/extensions/cassettes/test_mlm/test_validate_mlm.yaml similarity index 100% rename from tests/extensions/cassettes/test_mlm/test_validate_mlm.yaml rename to tests/v1/extensions/cassettes/test_mlm/test_validate_mlm.yaml diff --git a/tests/extensions/cassettes/test_pointcloud/test_count.yaml b/tests/v1/extensions/cassettes/test_pointcloud/test_count.yaml similarity index 100% rename from tests/extensions/cassettes/test_pointcloud/test_count.yaml rename to tests/v1/extensions/cassettes/test_pointcloud/test_count.yaml diff --git a/tests/extensions/cassettes/test_pointcloud/test_density.yaml b/tests/v1/extensions/cassettes/test_pointcloud/test_density.yaml similarity index 100% rename from tests/extensions/cassettes/test_pointcloud/test_density.yaml rename to tests/v1/extensions/cassettes/test_pointcloud/test_density.yaml diff --git a/tests/extensions/cassettes/test_pointcloud/test_encoding.yaml b/tests/v1/extensions/cassettes/test_pointcloud/test_encoding.yaml similarity index 100% rename from tests/extensions/cassettes/test_pointcloud/test_encoding.yaml rename to tests/v1/extensions/cassettes/test_pointcloud/test_encoding.yaml diff --git a/tests/extensions/cassettes/test_pointcloud/test_schemas.yaml b/tests/v1/extensions/cassettes/test_pointcloud/test_schemas.yaml similarity index 100% rename from tests/extensions/cassettes/test_pointcloud/test_schemas.yaml rename to tests/v1/extensions/cassettes/test_pointcloud/test_schemas.yaml diff --git a/tests/extensions/cassettes/test_pointcloud/test_statistics.yaml b/tests/v1/extensions/cassettes/test_pointcloud/test_statistics.yaml similarity index 100% rename from tests/extensions/cassettes/test_pointcloud/test_statistics.yaml rename to tests/v1/extensions/cassettes/test_pointcloud/test_statistics.yaml diff --git a/tests/extensions/cassettes/test_pointcloud/test_type.yaml b/tests/v1/extensions/cassettes/test_pointcloud/test_type.yaml similarity index 100% rename from tests/extensions/cassettes/test_pointcloud/test_type.yaml rename to tests/v1/extensions/cassettes/test_pointcloud/test_type.yaml diff --git a/tests/extensions/cassettes/test_pointcloud/test_validate_pointcloud.yaml b/tests/v1/extensions/cassettes/test_pointcloud/test_validate_pointcloud.yaml similarity index 100% rename from tests/extensions/cassettes/test_pointcloud/test_validate_pointcloud.yaml rename to tests/v1/extensions/cassettes/test_pointcloud/test_validate_pointcloud.yaml diff --git a/tests/extensions/cassettes/test_projection/test_bbox.yaml b/tests/v1/extensions/cassettes/test_projection/test_bbox.yaml similarity index 100% rename from tests/extensions/cassettes/test_projection/test_bbox.yaml rename to tests/v1/extensions/cassettes/test_projection/test_bbox.yaml diff --git a/tests/extensions/cassettes/test_projection/test_centroid.yaml b/tests/v1/extensions/cassettes/test_projection/test_centroid.yaml similarity index 100% rename from tests/extensions/cassettes/test_projection/test_centroid.yaml rename to tests/v1/extensions/cassettes/test_projection/test_centroid.yaml diff --git a/tests/extensions/cassettes/test_projection/test_epsg.yaml b/tests/v1/extensions/cassettes/test_projection/test_epsg.yaml similarity index 100% rename from tests/extensions/cassettes/test_projection/test_epsg.yaml rename to tests/v1/extensions/cassettes/test_projection/test_epsg.yaml diff --git a/tests/extensions/cassettes/test_projection/test_geometry.yaml b/tests/v1/extensions/cassettes/test_projection/test_geometry.yaml similarity index 100% rename from tests/extensions/cassettes/test_projection/test_geometry.yaml rename to tests/v1/extensions/cassettes/test_projection/test_geometry.yaml diff --git a/tests/extensions/cassettes/test_projection/test_partial_apply.yaml b/tests/v1/extensions/cassettes/test_projection/test_partial_apply.yaml similarity index 100% rename from tests/extensions/cassettes/test_projection/test_partial_apply.yaml rename to tests/v1/extensions/cassettes/test_projection/test_partial_apply.yaml diff --git a/tests/extensions/cassettes/test_projection/test_projjson.yaml b/tests/v1/extensions/cassettes/test_projection/test_projjson.yaml similarity index 100% rename from tests/extensions/cassettes/test_projection/test_projjson.yaml rename to tests/v1/extensions/cassettes/test_projection/test_projjson.yaml diff --git a/tests/extensions/cassettes/test_projection/test_shape.yaml b/tests/v1/extensions/cassettes/test_projection/test_shape.yaml similarity index 100% rename from tests/extensions/cassettes/test_projection/test_shape.yaml rename to tests/v1/extensions/cassettes/test_projection/test_shape.yaml diff --git a/tests/extensions/cassettes/test_projection/test_transform.yaml b/tests/v1/extensions/cassettes/test_projection/test_transform.yaml similarity index 100% rename from tests/extensions/cassettes/test_projection/test_transform.yaml rename to tests/v1/extensions/cassettes/test_projection/test_transform.yaml diff --git a/tests/extensions/cassettes/test_projection/test_validate_proj.yaml b/tests/v1/extensions/cassettes/test_projection/test_validate_proj.yaml similarity index 100% rename from tests/extensions/cassettes/test_projection/test_validate_proj.yaml rename to tests/v1/extensions/cassettes/test_projection/test_validate_proj.yaml diff --git a/tests/extensions/cassettes/test_projection/test_wkt2.yaml b/tests/v1/extensions/cassettes/test_projection/test_wkt2.yaml similarity index 100% rename from tests/extensions/cassettes/test_projection/test_wkt2.yaml rename to tests/v1/extensions/cassettes/test_projection/test_wkt2.yaml diff --git a/tests/extensions/cassettes/test_raster/test_asset_bands.yaml b/tests/v1/extensions/cassettes/test_raster/test_asset_bands.yaml similarity index 100% rename from tests/extensions/cassettes/test_raster/test_asset_bands.yaml rename to tests/v1/extensions/cassettes/test_raster/test_asset_bands.yaml diff --git a/tests/extensions/cassettes/test_raster/test_validate_raster.yaml b/tests/v1/extensions/cassettes/test_raster/test_validate_raster.yaml similarity index 100% rename from tests/extensions/cassettes/test_raster/test_validate_raster.yaml rename to tests/v1/extensions/cassettes/test_raster/test_validate_raster.yaml diff --git a/tests/extensions/cassettes/test_render/test_collection_validate.yaml b/tests/v1/extensions/cassettes/test_render/test_collection_validate.yaml similarity index 100% rename from tests/extensions/cassettes/test_render/test_collection_validate.yaml rename to tests/v1/extensions/cassettes/test_render/test_collection_validate.yaml diff --git a/tests/extensions/cassettes/test_render/test_item_validate.yaml b/tests/v1/extensions/cassettes/test_render/test_item_validate.yaml similarity index 100% rename from tests/extensions/cassettes/test_render/test_item_validate.yaml rename to tests/v1/extensions/cassettes/test_render/test_item_validate.yaml diff --git a/tests/extensions/cassettes/test_sar/test_all.yaml b/tests/v1/extensions/cassettes/test_sar/test_all.yaml similarity index 100% rename from tests/extensions/cassettes/test_sar/test_all.yaml rename to tests/v1/extensions/cassettes/test_sar/test_all.yaml diff --git a/tests/extensions/cassettes/test_sar/test_required.yaml b/tests/v1/extensions/cassettes/test_sar/test_required.yaml similarity index 100% rename from tests/extensions/cassettes/test_sar/test_required.yaml rename to tests/v1/extensions/cassettes/test_sar/test_required.yaml diff --git a/tests/extensions/cassettes/test_sat/test_absolute_orbit.yaml b/tests/v1/extensions/cassettes/test_sat/test_absolute_orbit.yaml similarity index 100% rename from tests/extensions/cassettes/test_sat/test_absolute_orbit.yaml rename to tests/v1/extensions/cassettes/test_sat/test_absolute_orbit.yaml diff --git a/tests/extensions/cassettes/test_sat/test_anx_datetime.yaml b/tests/v1/extensions/cassettes/test_sat/test_anx_datetime.yaml similarity index 100% rename from tests/extensions/cassettes/test_sat/test_anx_datetime.yaml rename to tests/v1/extensions/cassettes/test_sat/test_anx_datetime.yaml diff --git a/tests/extensions/cassettes/test_sat/test_both.yaml b/tests/v1/extensions/cassettes/test_sat/test_both.yaml similarity index 100% rename from tests/extensions/cassettes/test_sat/test_both.yaml rename to tests/v1/extensions/cassettes/test_sat/test_both.yaml diff --git a/tests/extensions/cassettes/test_sat/test_clear_orbit_state.yaml b/tests/v1/extensions/cassettes/test_sat/test_clear_orbit_state.yaml similarity index 100% rename from tests/extensions/cassettes/test_sat/test_clear_orbit_state.yaml rename to tests/v1/extensions/cassettes/test_sat/test_clear_orbit_state.yaml diff --git a/tests/extensions/cassettes/test_sat/test_clear_relative_orbit.yaml b/tests/v1/extensions/cassettes/test_sat/test_clear_relative_orbit.yaml similarity index 100% rename from tests/extensions/cassettes/test_sat/test_clear_relative_orbit.yaml rename to tests/v1/extensions/cassettes/test_sat/test_clear_relative_orbit.yaml diff --git a/tests/extensions/cassettes/test_sat/test_modify.yaml b/tests/v1/extensions/cassettes/test_sat/test_modify.yaml similarity index 100% rename from tests/extensions/cassettes/test_sat/test_modify.yaml rename to tests/v1/extensions/cassettes/test_sat/test_modify.yaml diff --git a/tests/extensions/cassettes/test_sat/test_no_args_fails.yaml b/tests/v1/extensions/cassettes/test_sat/test_no_args_fails.yaml similarity index 100% rename from tests/extensions/cassettes/test_sat/test_no_args_fails.yaml rename to tests/v1/extensions/cassettes/test_sat/test_no_args_fails.yaml diff --git a/tests/extensions/cassettes/test_sat/test_orbit_state.yaml b/tests/v1/extensions/cassettes/test_sat/test_orbit_state.yaml similarity index 100% rename from tests/extensions/cassettes/test_sat/test_orbit_state.yaml rename to tests/v1/extensions/cassettes/test_sat/test_orbit_state.yaml diff --git a/tests/extensions/cassettes/test_sat/test_platform_international_designator.yaml b/tests/v1/extensions/cassettes/test_sat/test_platform_international_designator.yaml similarity index 100% rename from tests/extensions/cassettes/test_sat/test_platform_international_designator.yaml rename to tests/v1/extensions/cassettes/test_sat/test_platform_international_designator.yaml diff --git a/tests/extensions/cassettes/test_sat/test_relative_orbit.yaml b/tests/v1/extensions/cassettes/test_sat/test_relative_orbit.yaml similarity index 100% rename from tests/extensions/cassettes/test_sat/test_relative_orbit.yaml rename to tests/v1/extensions/cassettes/test_sat/test_relative_orbit.yaml diff --git a/tests/extensions/cassettes/test_sat/test_relative_orbit_no_negative.yaml b/tests/v1/extensions/cassettes/test_sat/test_relative_orbit_no_negative.yaml similarity index 100% rename from tests/extensions/cassettes/test_sat/test_relative_orbit_no_negative.yaml rename to tests/v1/extensions/cassettes/test_sat/test_relative_orbit_no_negative.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_citation.yaml b/tests/v1/extensions/cassettes/test_scientific/test_citation.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_citation.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_citation.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_collection_citation.yaml b/tests/v1/extensions/cassettes/test_scientific/test_collection_citation.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_collection_citation.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_collection_citation.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_collection_doi.yaml b/tests/v1/extensions/cassettes/test_scientific/test_collection_doi.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_collection_doi.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_collection_doi.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_collection_publications.yaml b/tests/v1/extensions/cassettes/test_scientific/test_collection_publications.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_collection_publications.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_collection_publications.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_collection_publications_one.yaml b/tests/v1/extensions/cassettes/test_scientific/test_collection_publications_one.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_collection_publications_one.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_collection_publications_one.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_collection_remove_all_publications_one.yaml b/tests/v1/extensions/cassettes/test_scientific/test_collection_remove_all_publications_one.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_collection_remove_all_publications_one.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_collection_remove_all_publications_one.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_collection_remove_all_publications_with_none.yaml b/tests/v1/extensions/cassettes/test_scientific/test_collection_remove_all_publications_with_none.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_collection_remove_all_publications_with_none.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_collection_remove_all_publications_with_none.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_collection_remove_all_publications_with_some.yaml b/tests/v1/extensions/cassettes/test_scientific/test_collection_remove_all_publications_with_some.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_collection_remove_all_publications_with_some.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_collection_remove_all_publications_with_some.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_collection_remove_publication_forward.yaml b/tests/v1/extensions/cassettes/test_scientific/test_collection_remove_publication_forward.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_collection_remove_publication_forward.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_collection_remove_publication_forward.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_collection_remove_publication_one.yaml b/tests/v1/extensions/cassettes/test_scientific/test_collection_remove_publication_one.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_collection_remove_publication_one.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_collection_remove_publication_one.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_collection_remove_publication_reverse.yaml b/tests/v1/extensions/cassettes/test_scientific/test_collection_remove_publication_reverse.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_collection_remove_publication_reverse.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_collection_remove_publication_reverse.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_doi.yaml b/tests/v1/extensions/cassettes/test_scientific/test_doi.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_doi.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_doi.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_publications.yaml b/tests/v1/extensions/cassettes/test_scientific/test_publications.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_publications.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_publications.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_publications_one.yaml b/tests/v1/extensions/cassettes/test_scientific/test_publications_one.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_publications_one.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_publications_one.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_remove_all_publications_one.yaml b/tests/v1/extensions/cassettes/test_scientific/test_remove_all_publications_one.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_remove_all_publications_one.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_remove_all_publications_one.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_remove_all_publications_with_none.yaml b/tests/v1/extensions/cassettes/test_scientific/test_remove_all_publications_with_none.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_remove_all_publications_with_none.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_remove_all_publications_with_none.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_remove_all_publications_with_some.yaml b/tests/v1/extensions/cassettes/test_scientific/test_remove_all_publications_with_some.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_remove_all_publications_with_some.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_remove_all_publications_with_some.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_remove_publication_forward.yaml b/tests/v1/extensions/cassettes/test_scientific/test_remove_publication_forward.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_remove_publication_forward.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_remove_publication_forward.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_remove_publication_one.yaml b/tests/v1/extensions/cassettes/test_scientific/test_remove_publication_one.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_remove_publication_one.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_remove_publication_one.yaml diff --git a/tests/extensions/cassettes/test_scientific/test_remove_publication_reverse.yaml b/tests/v1/extensions/cassettes/test_scientific/test_remove_publication_reverse.yaml similarity index 100% rename from tests/extensions/cassettes/test_scientific/test_remove_publication_reverse.yaml rename to tests/v1/extensions/cassettes/test_scientific/test_remove_publication_reverse.yaml diff --git a/tests/v1/extensions/cassettes/test_storage/test_asset_platform.yaml b/tests/v1/extensions/cassettes/test_storage/test_asset_platform.yaml new file mode 100644 index 000000000..df56e24ae --- /dev/null +++ b/tests/v1/extensions/cassettes/test_storage/test_asset_platform.yaml @@ -0,0 +1,52 @@ +interactions: +- request: + body: null + headers: {} + method: GET + uri: https://stac-extensions.github.io/storage/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/storage/v1.0.0/schema.json\",\n \"title\": + \"Storage Extension\",\n \"description\": \"STAC Storage Extension to a STAC + Item and STAC Assets.\",\n \"oneOf\": [\n {\n \"$comment\": \"This + is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Require fields here for item properties.\",\n \"required\": + []\n },\n {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ], \n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/storage/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"storage:platform\": {\n \"title\": + \"Platform\",\n \"type\": \"string\",\n \"enum\": [\n \"OTHER\",\n + \ \"AWS\",\n \"GCP\",\n \"AZURE\",\n \"IBM\",\n + \ \"ALIBABA\",\n \"ORACLE\"\n ],\n \"default\": + \"OTHER\"\n },\n \"storage:region\": {\n \"title\": + \"Region\",\n \"type\": \"string\"\n },\n \"storage:requester_pays\": + {\n \"type\": \"boolean\",\n \"title\": \"Requester pays\",\n + \ \"default\": false\n },\n \"storage:tier\": {\n \"title\": + \"Tier\",\n \"type\": \"string\"\n }\n }\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/v1/extensions/cassettes/test_storage/test_asset_region.yaml b/tests/v1/extensions/cassettes/test_storage/test_asset_region.yaml new file mode 100644 index 000000000..df56e24ae --- /dev/null +++ b/tests/v1/extensions/cassettes/test_storage/test_asset_region.yaml @@ -0,0 +1,52 @@ +interactions: +- request: + body: null + headers: {} + method: GET + uri: https://stac-extensions.github.io/storage/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/storage/v1.0.0/schema.json\",\n \"title\": + \"Storage Extension\",\n \"description\": \"STAC Storage Extension to a STAC + Item and STAC Assets.\",\n \"oneOf\": [\n {\n \"$comment\": \"This + is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Require fields here for item properties.\",\n \"required\": + []\n },\n {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ], \n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/storage/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"storage:platform\": {\n \"title\": + \"Platform\",\n \"type\": \"string\",\n \"enum\": [\n \"OTHER\",\n + \ \"AWS\",\n \"GCP\",\n \"AZURE\",\n \"IBM\",\n + \ \"ALIBABA\",\n \"ORACLE\"\n ],\n \"default\": + \"OTHER\"\n },\n \"storage:region\": {\n \"title\": + \"Region\",\n \"type\": \"string\"\n },\n \"storage:requester_pays\": + {\n \"type\": \"boolean\",\n \"title\": \"Requester pays\",\n + \ \"default\": false\n },\n \"storage:tier\": {\n \"title\": + \"Tier\",\n \"type\": \"string\"\n }\n }\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/v1/extensions/cassettes/test_storage/test_asset_requester_pays.yaml b/tests/v1/extensions/cassettes/test_storage/test_asset_requester_pays.yaml new file mode 100644 index 000000000..df56e24ae --- /dev/null +++ b/tests/v1/extensions/cassettes/test_storage/test_asset_requester_pays.yaml @@ -0,0 +1,52 @@ +interactions: +- request: + body: null + headers: {} + method: GET + uri: https://stac-extensions.github.io/storage/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/storage/v1.0.0/schema.json\",\n \"title\": + \"Storage Extension\",\n \"description\": \"STAC Storage Extension to a STAC + Item and STAC Assets.\",\n \"oneOf\": [\n {\n \"$comment\": \"This + is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Require fields here for item properties.\",\n \"required\": + []\n },\n {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ], \n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/storage/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"storage:platform\": {\n \"title\": + \"Platform\",\n \"type\": \"string\",\n \"enum\": [\n \"OTHER\",\n + \ \"AWS\",\n \"GCP\",\n \"AZURE\",\n \"IBM\",\n + \ \"ALIBABA\",\n \"ORACLE\"\n ],\n \"default\": + \"OTHER\"\n },\n \"storage:region\": {\n \"title\": + \"Region\",\n \"type\": \"string\"\n },\n \"storage:requester_pays\": + {\n \"type\": \"boolean\",\n \"title\": \"Requester pays\",\n + \ \"default\": false\n },\n \"storage:tier\": {\n \"title\": + \"Tier\",\n \"type\": \"string\"\n }\n }\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/v1/extensions/cassettes/test_storage/test_asset_tier.yaml b/tests/v1/extensions/cassettes/test_storage/test_asset_tier.yaml new file mode 100644 index 000000000..df56e24ae --- /dev/null +++ b/tests/v1/extensions/cassettes/test_storage/test_asset_tier.yaml @@ -0,0 +1,52 @@ +interactions: +- request: + body: null + headers: {} + method: GET + uri: https://stac-extensions.github.io/storage/v1.0.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/storage/v1.0.0/schema.json\",\n \"title\": + \"Storage Extension\",\n \"description\": \"STAC Storage Extension to a STAC + Item and STAC Assets.\",\n \"oneOf\": [\n {\n \"$comment\": \"This + is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$comment\": + \"Require fields here for item properties.\",\n \"required\": + []\n },\n {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n ]\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ], \n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/storage/v1.0.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"storage:platform\": {\n \"title\": + \"Platform\",\n \"type\": \"string\",\n \"enum\": [\n \"OTHER\",\n + \ \"AWS\",\n \"GCP\",\n \"AZURE\",\n \"IBM\",\n + \ \"ALIBABA\",\n \"ORACLE\"\n ],\n \"default\": + \"OTHER\"\n },\n \"storage:region\": {\n \"title\": + \"Region\",\n \"type\": \"string\"\n },\n \"storage:requester_pays\": + {\n \"type\": \"boolean\",\n \"title\": \"Requester pays\",\n + \ \"default\": false\n },\n \"storage:tier\": {\n \"title\": + \"Tier\",\n \"type\": \"string\"\n }\n }\n }\n }\n}\n" + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_storage/test_validate_storage.yaml b/tests/v1/extensions/cassettes/test_storage/test_validate_storage.yaml similarity index 100% rename from tests/extensions/cassettes/test_storage/test_validate_storage.yaml rename to tests/v1/extensions/cassettes/test_storage/test_validate_storage.yaml diff --git a/tests/extensions/cassettes/test_table/test_validate.yaml b/tests/v1/extensions/cassettes/test_table/test_validate.yaml similarity index 100% rename from tests/extensions/cassettes/test_table/test_validate.yaml rename to tests/v1/extensions/cassettes/test_table/test_validate.yaml diff --git a/tests/extensions/cassettes/test_timestamps/test_expires.yaml b/tests/v1/extensions/cassettes/test_timestamps/test_expires.yaml similarity index 100% rename from tests/extensions/cassettes/test_timestamps/test_expires.yaml rename to tests/v1/extensions/cassettes/test_timestamps/test_expires.yaml diff --git a/tests/extensions/cassettes/test_timestamps/test_published.yaml b/tests/v1/extensions/cassettes/test_timestamps/test_published.yaml similarity index 100% rename from tests/extensions/cassettes/test_timestamps/test_published.yaml rename to tests/v1/extensions/cassettes/test_timestamps/test_published.yaml diff --git a/tests/extensions/cassettes/test_timestamps/test_unpublished.yaml b/tests/v1/extensions/cassettes/test_timestamps/test_unpublished.yaml similarity index 100% rename from tests/extensions/cassettes/test_timestamps/test_unpublished.yaml rename to tests/v1/extensions/cassettes/test_timestamps/test_unpublished.yaml diff --git a/tests/extensions/cassettes/test_timestamps/test_validate_timestamps.yaml b/tests/v1/extensions/cassettes/test_timestamps/test_validate_timestamps.yaml similarity index 100% rename from tests/extensions/cassettes/test_timestamps/test_validate_timestamps.yaml rename to tests/v1/extensions/cassettes/test_timestamps/test_validate_timestamps.yaml diff --git a/tests/extensions/cassettes/test_version/test_add_deprecated_version.yaml b/tests/v1/extensions/cassettes/test_version/test_add_deprecated_version.yaml similarity index 100% rename from tests/extensions/cassettes/test_version/test_add_deprecated_version.yaml rename to tests/v1/extensions/cassettes/test_version/test_add_deprecated_version.yaml diff --git a/tests/extensions/cassettes/test_version/test_add_not_deprecated_version.yaml b/tests/v1/extensions/cassettes/test_version/test_add_not_deprecated_version.yaml similarity index 100% rename from tests/extensions/cassettes/test_version/test_add_not_deprecated_version.yaml rename to tests/v1/extensions/cassettes/test_version/test_add_not_deprecated_version.yaml diff --git a/tests/extensions/cassettes/test_version/test_add_version.yaml b/tests/v1/extensions/cassettes/test_version/test_add_version.yaml similarity index 100% rename from tests/extensions/cassettes/test_version/test_add_version.yaml rename to tests/v1/extensions/cassettes/test_version/test_add_version.yaml diff --git a/tests/extensions/cassettes/test_version/test_all_links.yaml b/tests/v1/extensions/cassettes/test_version/test_all_links.yaml similarity index 100% rename from tests/extensions/cassettes/test_version/test_all_links.yaml rename to tests/v1/extensions/cassettes/test_version/test_all_links.yaml diff --git a/tests/extensions/cassettes/test_version/test_assets.yaml b/tests/v1/extensions/cassettes/test_version/test_assets.yaml similarity index 100% rename from tests/extensions/cassettes/test_version/test_assets.yaml rename to tests/v1/extensions/cassettes/test_version/test_assets.yaml diff --git a/tests/extensions/cassettes/test_version/test_catalog_add_version.yaml b/tests/v1/extensions/cassettes/test_version/test_catalog_add_version.yaml similarity index 100% rename from tests/extensions/cassettes/test_version/test_catalog_add_version.yaml rename to tests/v1/extensions/cassettes/test_version/test_catalog_add_version.yaml diff --git a/tests/extensions/cassettes/test_version/test_catalog_validate_all.yaml b/tests/v1/extensions/cassettes/test_version/test_catalog_validate_all.yaml similarity index 100% rename from tests/extensions/cassettes/test_version/test_catalog_validate_all.yaml rename to tests/v1/extensions/cassettes/test_version/test_catalog_validate_all.yaml diff --git a/tests/extensions/cassettes/test_version/test_collection_add_version.yaml b/tests/v1/extensions/cassettes/test_version/test_collection_add_version.yaml similarity index 100% rename from tests/extensions/cassettes/test_version/test_collection_add_version.yaml rename to tests/v1/extensions/cassettes/test_version/test_collection_add_version.yaml diff --git a/tests/extensions/cassettes/test_version/test_collection_validate_all.yaml b/tests/v1/extensions/cassettes/test_version/test_collection_validate_all.yaml similarity index 100% rename from tests/extensions/cassettes/test_version/test_collection_validate_all.yaml rename to tests/v1/extensions/cassettes/test_version/test_collection_validate_all.yaml diff --git a/tests/extensions/cassettes/test_version/test_latest.yaml b/tests/v1/extensions/cassettes/test_version/test_latest.yaml similarity index 100% rename from tests/extensions/cassettes/test_version/test_latest.yaml rename to tests/v1/extensions/cassettes/test_version/test_latest.yaml diff --git a/tests/extensions/cassettes/test_version/test_optional_version.yaml b/tests/v1/extensions/cassettes/test_version/test_optional_version.yaml similarity index 100% rename from tests/extensions/cassettes/test_version/test_optional_version.yaml rename to tests/v1/extensions/cassettes/test_version/test_optional_version.yaml diff --git a/tests/extensions/cassettes/test_version/test_predecessor.yaml b/tests/v1/extensions/cassettes/test_version/test_predecessor.yaml similarity index 100% rename from tests/extensions/cassettes/test_version/test_predecessor.yaml rename to tests/v1/extensions/cassettes/test_version/test_predecessor.yaml diff --git a/tests/extensions/cassettes/test_version/test_successor.yaml b/tests/v1/extensions/cassettes/test_version/test_successor.yaml similarity index 100% rename from tests/extensions/cassettes/test_version/test_successor.yaml rename to tests/v1/extensions/cassettes/test_version/test_successor.yaml diff --git a/tests/extensions/cassettes/test_version/test_version_in_properties.yaml b/tests/v1/extensions/cassettes/test_version/test_version_in_properties.yaml similarity index 100% rename from tests/extensions/cassettes/test_version/test_version_in_properties.yaml rename to tests/v1/extensions/cassettes/test_version/test_version_in_properties.yaml diff --git a/tests/extensions/cassettes/test_view/test_azimuth.yaml b/tests/v1/extensions/cassettes/test_view/test_azimuth.yaml similarity index 100% rename from tests/extensions/cassettes/test_view/test_azimuth.yaml rename to tests/v1/extensions/cassettes/test_view/test_azimuth.yaml diff --git a/tests/extensions/cassettes/test_view/test_incidence_angle.yaml b/tests/v1/extensions/cassettes/test_view/test_incidence_angle.yaml similarity index 100% rename from tests/extensions/cassettes/test_view/test_incidence_angle.yaml rename to tests/v1/extensions/cassettes/test_view/test_incidence_angle.yaml diff --git a/tests/extensions/cassettes/test_view/test_off_nadir.yaml b/tests/v1/extensions/cassettes/test_view/test_off_nadir.yaml similarity index 100% rename from tests/extensions/cassettes/test_view/test_off_nadir.yaml rename to tests/v1/extensions/cassettes/test_view/test_off_nadir.yaml diff --git a/tests/extensions/cassettes/test_view/test_sun_azimuth.yaml b/tests/v1/extensions/cassettes/test_view/test_sun_azimuth.yaml similarity index 100% rename from tests/extensions/cassettes/test_view/test_sun_azimuth.yaml rename to tests/v1/extensions/cassettes/test_view/test_sun_azimuth.yaml diff --git a/tests/extensions/cassettes/test_view/test_sun_elevation.yaml b/tests/v1/extensions/cassettes/test_view/test_sun_elevation.yaml similarity index 100% rename from tests/extensions/cassettes/test_view/test_sun_elevation.yaml rename to tests/v1/extensions/cassettes/test_view/test_sun_elevation.yaml diff --git a/tests/extensions/cassettes/test_view/test_validate_view.yaml b/tests/v1/extensions/cassettes/test_view/test_validate_view.yaml similarity index 100% rename from tests/extensions/cassettes/test_view/test_validate_view.yaml rename to tests/v1/extensions/cassettes/test_view/test_validate_view.yaml diff --git a/tests/extensions/cassettes/test_xarray_assets/test_collection_validate.yaml b/tests/v1/extensions/cassettes/test_xarray_assets/test_collection_validate.yaml similarity index 100% rename from tests/extensions/cassettes/test_xarray_assets/test_collection_validate.yaml rename to tests/v1/extensions/cassettes/test_xarray_assets/test_collection_validate.yaml diff --git a/tests/extensions/cassettes/test_xarray_assets/test_item_validate.yaml b/tests/v1/extensions/cassettes/test_xarray_assets/test_item_validate.yaml similarity index 100% rename from tests/extensions/cassettes/test_xarray_assets/test_item_validate.yaml rename to tests/v1/extensions/cassettes/test_xarray_assets/test_item_validate.yaml diff --git a/tests/extensions/cassettes/test_xarray_assets/test_set_field[open_kwargs-value1].yaml b/tests/v1/extensions/cassettes/test_xarray_assets/test_set_field[open_kwargs-value1].yaml similarity index 100% rename from tests/extensions/cassettes/test_xarray_assets/test_set_field[open_kwargs-value1].yaml rename to tests/v1/extensions/cassettes/test_xarray_assets/test_set_field[open_kwargs-value1].yaml diff --git a/tests/extensions/cassettes/test_xarray_assets/test_set_field[storage_options-value0].yaml b/tests/v1/extensions/cassettes/test_xarray_assets/test_set_field[storage_options-value0].yaml similarity index 100% rename from tests/extensions/cassettes/test_xarray_assets/test_set_field[storage_options-value0].yaml rename to tests/v1/extensions/cassettes/test_xarray_assets/test_set_field[storage_options-value0].yaml diff --git a/tests/extensions/test_classification.py b/tests/v1/extensions/test_classification.py similarity index 99% rename from tests/extensions/test_classification.py rename to tests/v1/extensions/test_classification.py index d74838d67..61530cf53 100644 --- a/tests/extensions/test_classification.py +++ b/tests/v1/extensions/test_classification.py @@ -19,7 +19,8 @@ ClassificationExtension, ) from pystac.extensions.raster import RasterBand, RasterExtension -from tests.utils import TestCases + +from ..utils import TestCases logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger() diff --git a/tests/extensions/test_custom.py b/tests/v1/extensions/test_custom.py similarity index 100% rename from tests/extensions/test_custom.py rename to tests/v1/extensions/test_custom.py diff --git a/tests/extensions/test_datacube.py b/tests/v1/extensions/test_datacube.py similarity index 99% rename from tests/extensions/test_datacube.py rename to tests/v1/extensions/test_datacube.py index 1393b6c85..66407b987 100644 --- a/tests/extensions/test_datacube.py +++ b/tests/v1/extensions/test_datacube.py @@ -9,7 +9,8 @@ import pystac.extensions.datacube as dc from pystac import Asset, Item from pystac.errors import ExtensionTypeError -from tests.conftest import get_data_file + +from ..conftest import get_data_file @pytest.fixture diff --git a/tests/extensions/test_eo.py b/tests/v1/extensions/test_eo.py similarity index 99% rename from tests/extensions/test_eo.py rename to tests/v1/extensions/test_eo.py index 89d99990e..abc1c149d 100644 --- a/tests/extensions/test_eo.py +++ b/tests/v1/extensions/test_eo.py @@ -9,8 +9,9 @@ from pystac.extensions.projection import ProjectionExtension from pystac.summaries import RangeSummary from pystac.utils import get_opt -from tests.conftest import get_data_file -from tests.utils import TestCases, assert_to_from_dict + +from ..conftest import get_data_file +from ..utils import TestCases, assert_to_from_dict def test_band_create() -> None: diff --git a/tests/extensions/test_ext.py b/tests/v1/extensions/test_ext.py similarity index 99% rename from tests/extensions/test_ext.py rename to tests/v1/extensions/test_ext.py index 8b9584a56..179a39b26 100644 --- a/tests/extensions/test_ext.py +++ b/tests/v1/extensions/test_ext.py @@ -13,7 +13,8 @@ ItemExt, LinkExt, ) -from tests.conftest import get_data_file + +from ..conftest import get_data_file logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger() diff --git a/tests/extensions/test_file.py b/tests/v1/extensions/test_file.py similarity index 99% rename from tests/extensions/test_file.py rename to tests/v1/extensions/test_file.py index 282af9063..f37951917 100644 --- a/tests/extensions/test_file.py +++ b/tests/v1/extensions/test_file.py @@ -12,7 +12,8 @@ Item, ) from pystac.extensions.file import ByteOrder, FileExtension, MappingObject -from tests.utils import TestCases, assert_to_from_dict + +from ..utils import TestCases, assert_to_from_dict FILE_ITEM_EXAMPLE_URI = TestCases.get_path("data-files/file/item.json") FILE_COLLECTION_EXAMPLE_URI = TestCases.get_path("data-files/file/collection.json") diff --git a/tests/extensions/test_grid.py b/tests/v1/extensions/test_grid.py similarity index 98% rename from tests/extensions/test_grid.py rename to tests/v1/extensions/test_grid.py index 32d3cfdb4..80a59282c 100644 --- a/tests/extensions/test_grid.py +++ b/tests/v1/extensions/test_grid.py @@ -12,8 +12,9 @@ from pystac import ExtensionTypeError from pystac.extensions import grid from pystac.extensions.grid import GridExtension -from tests.conftest import get_data_file -from tests.utils import TestCases + +from ..conftest import get_data_file +from ..utils import TestCases code = "MGRS-4CFJ" diff --git a/tests/extensions/test_mgrs.py b/tests/v1/extensions/test_mgrs.py similarity index 99% rename from tests/extensions/test_mgrs.py rename to tests/v1/extensions/test_mgrs.py index 29ddbf5fd..59f558241 100644 --- a/tests/extensions/test_mgrs.py +++ b/tests/v1/extensions/test_mgrs.py @@ -6,7 +6,8 @@ import pystac from pystac.extensions.mgrs import MgrsExtension -from tests.conftest import get_data_file + +from ..conftest import get_data_file @pytest.fixture diff --git a/tests/extensions/test_mlm.py b/tests/v1/extensions/test_mlm.py similarity index 99% rename from tests/extensions/test_mlm.py rename to tests/v1/extensions/test_mlm.py index cb7090fe7..342b099c2 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/v1/extensions/test_mlm.py @@ -31,7 +31,8 @@ ValueScalingType, ) from pystac.extensions.raster import DataType -from tests.utils import TestCases + +from ..utils import TestCases logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger() diff --git a/tests/extensions/test_pointcloud.py b/tests/v1/extensions/test_pointcloud.py similarity index 99% rename from tests/extensions/test_pointcloud.py rename to tests/v1/extensions/test_pointcloud.py index 2145ea3ec..06e3ae0d7 100644 --- a/tests/extensions/test_pointcloud.py +++ b/tests/v1/extensions/test_pointcloud.py @@ -15,7 +15,8 @@ Statistic, ) from pystac.summaries import RangeSummary -from tests.utils import TestCases, assert_to_from_dict + +from ..utils import TestCases, assert_to_from_dict # from copy import deepcopy diff --git a/tests/extensions/test_projection.py b/tests/v1/extensions/test_projection.py similarity index 99% rename from tests/extensions/test_projection.py rename to tests/v1/extensions/test_projection.py index 44b28a3cb..95afd95a2 100644 --- a/tests/extensions/test_projection.py +++ b/tests/v1/extensions/test_projection.py @@ -9,7 +9,8 @@ from pystac.errors import ExtensionNotImplemented from pystac.extensions.projection import ProjectionExtension from pystac.utils import get_opt -from tests.utils import TestCases, assert_to_from_dict + +from ..utils import TestCases, assert_to_from_dict WKT2 = """ GEOGCS["WGS 84", diff --git a/tests/extensions/test_raster.py b/tests/v1/extensions/test_raster.py similarity index 98% rename from tests/extensions/test_raster.py rename to tests/v1/extensions/test_raster.py index 9214ab010..b02d8bb79 100644 --- a/tests/extensions/test_raster.py +++ b/tests/v1/extensions/test_raster.py @@ -14,8 +14,9 @@ Statistics, ) from pystac.utils import get_opt -from tests.conftest import get_data_file -from tests.utils import TestCases, assert_to_from_dict + +from ..conftest import get_data_file +from ..utils import TestCases, assert_to_from_dict PLANET_EXAMPLE_URI = get_data_file("raster/raster-planet-example.json") diff --git a/tests/extensions/test_render.py b/tests/v1/extensions/test_render.py similarity index 99% rename from tests/extensions/test_render.py rename to tests/v1/extensions/test_render.py index 15b90f092..f24c99516 100644 --- a/tests/extensions/test_render.py +++ b/tests/v1/extensions/test_render.py @@ -7,7 +7,8 @@ import pystac import pystac.errors from pystac.extensions.render import Render, RenderExtension -from tests.conftest import get_data_file + +from ..conftest import get_data_file @pytest.fixture diff --git a/tests/extensions/test_sar.py b/tests/v1/extensions/test_sar.py similarity index 99% rename from tests/extensions/test_sar.py rename to tests/v1/extensions/test_sar.py index 49b287f74..ca5716dfb 100644 --- a/tests/extensions/test_sar.py +++ b/tests/v1/extensions/test_sar.py @@ -16,7 +16,8 @@ SarExtension, ) from pystac.summaries import RangeSummary -from tests.utils import TestCases + +from ..utils import TestCases @pytest.fixture diff --git a/tests/extensions/test_sat.py b/tests/v1/extensions/test_sat.py similarity index 99% rename from tests/extensions/test_sat.py rename to tests/v1/extensions/test_sat.py index 0a090ce3f..8bd3be627 100644 --- a/tests/extensions/test_sat.py +++ b/tests/v1/extensions/test_sat.py @@ -11,7 +11,8 @@ from pystac.extensions.sat import OrbitState, SatExtension, SummariesSatExtension from pystac.summaries import RangeSummary from pystac.utils import datetime_to_str, str_to_datetime -from tests.utils import TestCases + +from ..utils import TestCases @pytest.fixture diff --git a/tests/extensions/test_scientific.py b/tests/v1/extensions/test_scientific.py similarity index 99% rename from tests/extensions/test_scientific.py rename to tests/v1/extensions/test_scientific.py index 790bcdd2b..58f7283b9 100644 --- a/tests/extensions/test_scientific.py +++ b/tests/v1/extensions/test_scientific.py @@ -16,7 +16,8 @@ ) from pystac.link import Link from pystac.summaries import Summaries -from tests.utils import TestCases + +from ..utils import TestCases URL_TEMPLATE = "http://example.com/catalog/%s.json" diff --git a/tests/extensions/test_storage.py b/tests/v1/extensions/test_storage.py similarity index 99% rename from tests/extensions/test_storage.py rename to tests/v1/extensions/test_storage.py index 70edf8afa..3e6666bbd 100644 --- a/tests/extensions/test_storage.py +++ b/tests/v1/extensions/test_storage.py @@ -14,7 +14,7 @@ StorageScheme, StorageSchemeType, ) -from tests.utils import TestCases, assert_to_from_dict +from ..utils import TestCases, assert_to_from_dict NAIP_EXAMPLE_URI = TestCases.get_path("data-files/storage/item-naip.json") NAIP_COLLECTION_URI = TestCases.get_path("data-files/storage/collection-naip.json") diff --git a/tests/extensions/test_table.py b/tests/v1/extensions/test_table.py similarity index 98% rename from tests/extensions/test_table.py rename to tests/v1/extensions/test_table.py index efeae38e2..9022c6d94 100644 --- a/tests/extensions/test_table.py +++ b/tests/v1/extensions/test_table.py @@ -5,7 +5,8 @@ import pystac from pystac import ExtensionTypeError, Item from pystac.extensions.table import Column, TableExtension -from tests.utils import TestCases + +from ..utils import TestCases @pytest.fixture diff --git a/tests/extensions/test_timestamps.py b/tests/v1/extensions/test_timestamps.py similarity index 99% rename from tests/extensions/test_timestamps.py rename to tests/v1/extensions/test_timestamps.py index 5bf01e971..0ba41dda9 100644 --- a/tests/extensions/test_timestamps.py +++ b/tests/v1/extensions/test_timestamps.py @@ -8,7 +8,8 @@ from pystac.extensions.timestamps import TimestampsExtension from pystac.summaries import RangeSummary from pystac.utils import datetime_to_str, get_opt, str_to_datetime -from tests.utils import TestCases, assert_to_from_dict + +from ..utils import TestCases, assert_to_from_dict EXAMPLE_URI = TestCases.get_path("data-files/timestamps/example-landsat8.json") SAMPLE_DATETIME_STR = "2020-01-01T00:00:00Z" diff --git a/tests/extensions/test_version.py b/tests/v1/extensions/test_version.py similarity index 99% rename from tests/extensions/test_version.py rename to tests/v1/extensions/test_version.py index e0e2fc794..efa11ef3e 100644 --- a/tests/extensions/test_version.py +++ b/tests/v1/extensions/test_version.py @@ -23,7 +23,8 @@ VersionRelType, ignore_deprecated, ) -from tests.utils import TestCases + +from ..utils import TestCases URL_TEMPLATE: str = "http://example.com/catalog/%s.json" diff --git a/tests/extensions/test_view.py b/tests/v1/extensions/test_view.py similarity index 99% rename from tests/extensions/test_view.py rename to tests/v1/extensions/test_view.py index ae96e2ad5..3df492f4e 100644 --- a/tests/extensions/test_view.py +++ b/tests/v1/extensions/test_view.py @@ -7,7 +7,8 @@ from pystac.collection import Collection from pystac.extensions.view import ViewExtension from pystac.summaries import RangeSummary -from tests.utils import TestCases, assert_to_from_dict + +from ..utils import TestCases, assert_to_from_dict EXAMPLE_URI = TestCases.get_path("data-files/view/example-landsat8.json") diff --git a/tests/extensions/test_xarray_assets.py b/tests/v1/extensions/test_xarray_assets.py similarity index 99% rename from tests/extensions/test_xarray_assets.py rename to tests/v1/extensions/test_xarray_assets.py index 804e27a87..edfdcf577 100644 --- a/tests/extensions/test_xarray_assets.py +++ b/tests/v1/extensions/test_xarray_assets.py @@ -6,7 +6,8 @@ import pystac from pystac.extensions.xarray_assets import XarrayAssetsExtension -from tests.conftest import get_data_file + +from ..conftest import get_data_file @pytest.fixture diff --git a/tests/html/__init__.py b/tests/v1/html/__init__.py similarity index 100% rename from tests/html/__init__.py rename to tests/v1/html/__init__.py diff --git a/tests/html/test_html.py b/tests/v1/html/test_html.py similarity index 98% rename from tests/html/test_html.py rename to tests/v1/html/test_html.py index cac5fca3e..e2fe8997a 100644 --- a/tests/html/test_html.py +++ b/tests/v1/html/test_html.py @@ -7,7 +7,8 @@ import pystac from pystac.html.jinja_env import get_jinja_env -from tests.utils import TestCases + +from ..utils import TestCases def parse_html(stac_html: str) -> None: diff --git a/tests/posix_paths/__init__.py b/tests/v1/posix_paths/__init__.py similarity index 100% rename from tests/posix_paths/__init__.py rename to tests/v1/posix_paths/__init__.py diff --git a/tests/posix_paths/test_posix_paths.py b/tests/v1/posix_paths/test_posix_paths.py similarity index 86% rename from tests/posix_paths/test_posix_paths.py rename to tests/v1/posix_paths/test_posix_paths.py index bf0218296..9cc8b41d0 100644 --- a/tests/posix_paths/test_posix_paths.py +++ b/tests/v1/posix_paths/test_posix_paths.py @@ -6,17 +6,23 @@ import pytest import pystac -from tests.conftest import get_data_file -from tests.utils import ARBITRARY_BBOX, ARBITRARY_EXTENT, ARBITRARY_GEOM +from ..conftest import get_data_file +from ..utils import ARBITRARY_BBOX, ARBITRARY_EXTENT, ARBITRARY_GEOM -def check_link(link: pystac.Link | None) -> None: - assert link is not None - href = link.get_target_str() +pytestmark = pytest.mark.passing_v2 + + +def check_href(href: str | None) -> None: assert href is not None assert "\\" not in href +def check_link(link: pystac.Link | None) -> None: + assert link is not None + check_href(link.get_href()) + + def test_create_item_from_absolute_posix_href() -> None: absolute_href = get_data_file("item/sample-item.json") absolute_posix_href = absolute_href.replace("\\", "/") @@ -65,6 +71,16 @@ def test_create_item_containing_posix_hrefs(tmp_path: Path) -> None: pystac.read_file(collection_href) +def test_posix_self_link_added_from_file(tmp_path: Path) -> None: + href = get_data_file("item/sample-item.json") + item = pystac.Item.from_file(href) + item.links.append(pystac.Link(rel="self", href=href)) + check_link(item.get_single_link(rel="self")) + item2 = pystac.read_file(href) + item2.links.append(pystac.Link(rel="self", href=href)) + check_link(item2.get_single_link(rel="self")) + + @pytest.mark.skipif(os.name != "nt", reason="windows only test") def test_posix_self_link_from_absolute_href(tmp_path: Path) -> None: # Check that we convert to a windows style (\\) absolute href to posix style @@ -72,48 +88,48 @@ def test_posix_self_link_from_absolute_href(tmp_path: Path) -> None: absolute_windows_href = get_data_file("item/sample-item.json") assert "\\" in absolute_windows_href item = pystac.Item.from_file(absolute_windows_href) - check_link(item.get_single_link(rel="self")) + check_href(item.get_self_href()) item2 = pystac.read_file(absolute_windows_href) - check_link(item2.get_single_link(rel="self")) + check_href(item2.get_self_href()) absolute_windows_href = get_data_file("collections/multi-extent.json") assert "\\" in absolute_windows_href collection = pystac.Collection.from_file(absolute_windows_href) - check_link(collection.get_single_link(rel="self")) + check_href(collection.get_self_href()) collection2 = pystac.read_file(absolute_windows_href) - check_link(collection2.get_single_link(rel="self")) + check_href(collection2.get_self_href()) absolute_windows_href = get_data_file("catalogs/test-case-1/catalog.json") assert "\\" in absolute_windows_href catalog = pystac.Catalog.from_file(absolute_windows_href) - check_link(catalog.get_single_link(rel="self")) + check_href(catalog.get_self_href()) catalog2 = pystac.read_file(absolute_windows_href) - check_link(catalog2.get_single_link(rel="self")) + check_href(catalog2.get_self_href()) # check that we retain a posix style (/) absolute href in the self link absolute_windows_href = get_data_file("item/sample-item.json") assert "\\" in absolute_windows_href absolute_posix_href = absolute_windows_href.replace("\\", "/") item = pystac.Item.from_file(absolute_posix_href) - check_link(item.get_single_link(rel="self")) + check_href(item.get_self_href()) item2 = pystac.read_file(absolute_posix_href) - check_link(item2.get_single_link(rel="self")) + check_href(item2.get_self_href()) absolute_windows_href = get_data_file("collections/multi-extent.json") assert "\\" in absolute_windows_href absolute_posix_href = absolute_windows_href.replace("\\", "/") collection = pystac.Collection.from_file(absolute_posix_href) - check_link(collection.get_single_link(rel="self")) + check_href(collection.get_self_href()) collection2 = pystac.read_file(absolute_posix_href) - check_link(collection2.get_single_link(rel="self")) + check_href(collection2.get_self_href()) absolute_windows_href = get_data_file("collections/multi-extent.json") assert "\\" in absolute_windows_href absolute_posix_href = absolute_windows_href.replace("\\", "/") catalog = pystac.Collection.from_file(absolute_posix_href) - check_link(catalog.get_single_link(rel="self")) + check_href(catalog.get_self_href()) catalog2 = pystac.read_file(absolute_posix_href) - check_link(catalog2.get_single_link(rel="self")) + check_href(catalog2.get_self_href()) @pytest.mark.skipif(os.name != "nt", reason="windows only test") @@ -132,9 +148,9 @@ def test_posix_self_link_from_relative_href( monkeypatch.chdir(tmp_path) item = pystac.Item.from_file("subdirectory\\test-item.json") - check_link(item.get_single_link(rel="self")) + check_href(item.get_self_href()) item2 = pystac.read_file("subdirectory\\test-item.json") - check_link(item2.get_single_link(rel="self")) + check_href(item2.get_self_href()) def test_all_generated_links_have_posix_hrefs(tmp_path: Path) -> None: diff --git a/tests/serialization/__init__.py b/tests/v1/serialization/__init__.py similarity index 100% rename from tests/serialization/__init__.py rename to tests/v1/serialization/__init__.py diff --git a/tests/serialization/test_identify.py b/tests/v1/serialization/test_identify.py similarity index 98% rename from tests/serialization/test_identify.py rename to tests/v1/serialization/test_identify.py index babc170f4..de156290d 100644 --- a/tests/serialization/test_identify.py +++ b/tests/v1/serialization/test_identify.py @@ -8,8 +8,9 @@ merge_common_properties, ) from pystac.serialization.identify import STACVersionID, STACVersionRange -from tests.utils import TestCases -from tests.utils.test_cases import ExampleInfo + +from ..utils import TestCases +from ..utils.test_cases import ExampleInfo class TestIdentify: diff --git a/tests/serialization/test_migrate.py b/tests/v1/serialization/test_migrate.py similarity index 98% rename from tests/serialization/test_migrate.py rename to tests/v1/serialization/test_migrate.py index ed1aae43b..d2d3d1be8 100644 --- a/tests/serialization/test_migrate.py +++ b/tests/v1/serialization/test_migrate.py @@ -12,8 +12,9 @@ migrate_to_latest, ) from pystac.utils import get_required, str_to_datetime -from tests.utils import TestCases -from tests.utils.test_cases import ExampleInfo + +from ..utils import TestCases +from ..utils.test_cases import ExampleInfo class TestMigrate: diff --git a/tests/test_asset.py b/tests/v1/test_asset.py similarity index 90% rename from tests/test_asset.py rename to tests/v1/test_asset.py index b38cdb36f..9c38d6bfa 100644 --- a/tests/test_asset.py +++ b/tests/v1/test_asset.py @@ -6,6 +6,10 @@ import pystac +pytestmark = pytest.mark.passing_v2 + + +@pytest.mark.xfail(reason="Asset copy/move/delete are removed in pystac v2") @pytest.mark.parametrize("action", ["copy", "move"]) def test_alter_asset_absolute_path( action: str, tmp_asset: pystac.Asset, tmp_path: Path @@ -26,6 +30,7 @@ def test_alter_asset_absolute_path( assert os.path.exists(old_href) +@pytest.mark.xfail(reason="Asset copy/move/delete are removed in pystac v2") @pytest.mark.parametrize("action", ["copy", "move"]) def test_alter_asset_relative_path(action: str, tmp_asset: pystac.Asset) -> None: asset = tmp_asset @@ -45,6 +50,7 @@ def test_alter_asset_relative_path(action: str, tmp_asset: pystac.Asset) -> None assert os.path.exists(old_href) +@pytest.mark.xfail(reason="Asset copy/move/delete are removed in pystac v2") @pytest.mark.parametrize("action", ["copy", "move"]) def test_alter_asset_relative_src_no_owner_fails( action: str, tmp_asset: pystac.Asset @@ -55,10 +61,12 @@ def test_alter_asset_relative_src_no_owner_fails( with pytest.raises(ValueError, match=f"Cannot {action} file") as e: getattr(asset, action)(new_href) - assert new_href not in str(e.value) + assert asset.href in str(e.value) + assert new_href in str(e.value) assert asset.href != new_href +@pytest.mark.xfail(reason="Asset copy/move/delete are removed in pystac v2") @pytest.mark.parametrize("action", ["copy", "move"]) def test_alter_asset_relative_dst_no_owner_fails( action: str, tmp_asset: pystac.Asset @@ -74,10 +82,12 @@ def test_alter_asset_relative_dst_no_owner_fails( with pytest.raises(ValueError, match=f"Cannot {action} file") as e: getattr(asset, action)(new_href) + assert asset.href in str(e.value) assert new_href in str(e.value) assert asset.href != new_href +@pytest.mark.xfail(reason="Asset copy/move/delete are removed in pystac v2") def test_delete_asset(tmp_asset: pystac.Asset) -> None: asset = tmp_asset href = asset.get_absolute_href() @@ -89,6 +99,7 @@ def test_delete_asset(tmp_asset: pystac.Asset) -> None: assert not os.path.exists(href) +@pytest.mark.xfail(reason="Asset copy/move/delete are removed in pystac v2") def test_delete_asset_relative_no_owner_fails(tmp_asset: pystac.Asset) -> None: asset = tmp_asset href = asset.get_absolute_href() diff --git a/tests/test_cache.py b/tests/v1/test_cache.py similarity index 98% rename from tests/test_cache.py rename to tests/v1/test_cache.py index 2d92554d3..64c61345a 100644 --- a/tests/test_cache.py +++ b/tests/v1/test_cache.py @@ -3,7 +3,8 @@ import pystac from pystac.cache import ResolvedObjectCache, ResolvedObjectCollectionCache from pystac.utils import get_opt -from tests.utils import TestCases + +from .utils import TestCases def create_catalog(suffix: Any, include_href: bool = True) -> pystac.Catalog: diff --git a/tests/v1/test_catalog.py b/tests/v1/test_catalog.py new file mode 100644 index 000000000..18a3daf70 --- /dev/null +++ b/tests/v1/test_catalog.py @@ -0,0 +1,2047 @@ +from __future__ import annotations + +import json +import os +import posixpath +import tempfile +from collections import defaultdict +from collections.abc import Iterator +from copy import deepcopy +from datetime import UTC, datetime +from pathlib import Path +from typing import Any, cast + +import pytest + +import pystac +from pystac import ( + HIERARCHICAL_LINKS, + Asset, + Catalog, + CatalogType, + Collection, + Item, + Link, + MediaType, +) +from pystac.errors import STACError +from pystac.layout import ( + APILayoutStrategy, + BestPracticesLayoutStrategy, + HrefLayoutStrategy, + TemplateLayoutStrategy, +) +from pystac.utils import ( + is_absolute_href, + make_absolute_href, + make_posix_style, + make_relative_href, +) + +from .utils import ( + ARBITRARY_BBOX, + ARBITRARY_EXTENT, + ARBITRARY_GEOM, + MockStacIO, + TestCases, +) + + +class TestCatalogType: + def test_determine_type_for_absolute_published(self) -> None: + cat = TestCases.case_1() + with tempfile.TemporaryDirectory() as tmp_dir: + cat.normalize_and_save(tmp_dir, catalog_type=CatalogType.ABSOLUTE_PUBLISHED) + cat_json = pystac.StacIO.default().read_json( + os.path.join(tmp_dir, "catalog.json") + ) + + catalog_type = CatalogType.determine_type(cat_json) + assert catalog_type == CatalogType.ABSOLUTE_PUBLISHED + + def test_determine_type_for_relative_published(self) -> None: + cat = TestCases.case_2() + with tempfile.TemporaryDirectory() as tmp_dir: + cat.normalize_and_save(tmp_dir, catalog_type=CatalogType.RELATIVE_PUBLISHED) + cat_json = pystac.StacIO.default().read_json( + os.path.join(tmp_dir, "catalog.json") + ) + + catalog_type = CatalogType.determine_type(cat_json) + assert catalog_type == CatalogType.RELATIVE_PUBLISHED + + def test_determine_type_for_self_contained(self) -> None: + cat_json = pystac.StacIO.default().read_json( + TestCases.get_path("data-files/catalogs/test-case-1/catalog.json") + ) + catalog_type = CatalogType.determine_type(cat_json) + assert catalog_type == CatalogType.SELF_CONTAINED + + @pytest.mark.xfail(reason="CatalogType is deprecated in v2, so it's not worth fixing this test") + def test_determine_type_for_unknown(self) -> None: + catalog = Catalog(id="test", description="test desc") + subcat = Catalog(id="subcat", description="subcat desc") + catalog.add_child(subcat) + catalog.normalize_hrefs("http://example.com") + d = catalog.to_dict(include_self_link=False) + + assert CatalogType.determine_type(d) is None + + +class TestCatalog: + def test_create_and_read(self) -> None: + with tempfile.TemporaryDirectory() as tmp_dir: + cat_dir = os.path.join(tmp_dir, "catalog") + catalog = TestCases.case_1() + + catalog.normalize_and_save( + cat_dir, catalog_type=CatalogType.ABSOLUTE_PUBLISHED + ) + + read_catalog = Catalog.from_file(f"{cat_dir}/catalog.json") + + collections = catalog.get_children() + assert len(list(collections)) == 2 + + items = read_catalog.get_items(recursive=True) + + assert len(list(items)) == 8 + + @pytest.mark.xfail(reason="We no longer migrate by default in v2, so this test should fail") + def test_from_dict_preserves_dict(self) -> None: + catalog_dict = TestCases.case_1().to_dict() + param_dict = deepcopy(catalog_dict) + + # test that the parameter is preserved + _ = Catalog.from_dict(param_dict) + assert param_dict == catalog_dict + + # assert that the parameter is not preserved with + # non-default parameter + _ = Catalog.from_dict(param_dict, preserve_dict=False, migrate=False) + assert param_dict != catalog_dict + + @pytest.mark.xfail(reason="We deserialize more permissively in v2") + def test_from_file_bad_catalog(self) -> None: + with pytest.raises(pystac.errors.STACTypeError) as ctx: + _ = Catalog.from_file(TestCases.get_path(TestCases.bad_catalog_case)) + assert "(id = broken_cat) does not represent a STACObject" in ctx.value.args[0] + assert "is Catalog" in ctx.value.args[0] + + def test_from_dict_set_root(self) -> None: + path = TestCases.get_path("data-files/catalogs/test-case-1/catalog.json") + with open(path) as f: + cat_dict = json.load(f) + root_cat = pystac.Catalog(id="test", description="test desc") + collection = Catalog.from_dict(cat_dict, root=root_cat) + assert collection.get_root() is root_cat + + @pytest.mark.vcr() + def test_read_remote(self) -> None: + catalog_url = ( + "https://raw.githubusercontent.com/stac-extensions/label/main/" + "examples/multidataset/catalog.json" + ) + cat = Catalog.from_file(catalog_url) + + zanzibar = cat.get_child("zanzibar-collection") + assert zanzibar is not None + + assert len(list(zanzibar.get_items())) == 2 + + def test_clear_items_removes_from_cache(self) -> None: + catalog = Catalog(id="test", description="test") + subcat = Catalog(id="subcat", description="test") + catalog.add_child(subcat) + item = Item( + id="test-item", + geometry=ARBITRARY_GEOM, + bbox=ARBITRARY_BBOX, + datetime=datetime.now(UTC), + properties={"key": "one"}, + ) + subcat.add_item(item) + + items = list(catalog.get_items(recursive=True)) + assert len(items) == 1 + assert items[0].properties["key"] == "one" + + subcat.clear_items() + item = Item( + id="test-item", + geometry=ARBITRARY_GEOM, + bbox=ARBITRARY_BBOX, + datetime=datetime.now(UTC), + properties={"key": "two"}, + ) + subcat.add_item(item) + + items = list(catalog.get_items(recursive=True)) + assert len(items) == 1 + assert items[0].properties["key"] == "two" + + subcat.remove_item("test-item") + item = Item( + id="test-item", + geometry=ARBITRARY_GEOM, + bbox=ARBITRARY_BBOX, + datetime=datetime.now(UTC), + properties={"key": "three"}, + ) + subcat.add_item(item) + + items = list(catalog.get_items(recursive=True)) + assert len(items) == 1 + assert items[0].properties["key"] == "three" + + def test_clear_children_removes_from_cache(self) -> None: + catalog = Catalog(id="test", description="test") + subcat = Catalog(id="subcat", description="test") + catalog.add_child(subcat) + + children = list(catalog.get_children()) + assert len(children) == 1 + assert children[0].description == "test" + + catalog.clear_children() + subcat = Catalog(id="subcat", description="test2") + catalog.add_child(subcat) + + children = list(catalog.get_children()) + assert len(children) == 1 + assert children[0].description == "test2" + + catalog.remove_child("subcat") + subcat = Catalog(id="subcat", description="test3") + catalog.add_child(subcat) + + children = list(catalog.get_children()) + assert len(children) == 1 + assert children[0].description == "test3" + + def test_clear_children_sets_parent_and_root_to_None(self) -> None: + catalog = Catalog(id="test", description="test") + subcat1 = Catalog(id="subcat", description="test") + subcat2 = Catalog(id="subcat2", description="test2") + catalog.add_children([subcat1, subcat2]) + + assert subcat1.get_parent() is not None + assert subcat2.get_parent() is not None + assert subcat1.get_root() is not None + assert subcat2.get_root() is not None + + children = list(catalog.get_children()) + assert len(children) == 2 + + catalog.clear_children() + + assert subcat1.get_parent() is None + assert subcat2.get_parent() is None + assert subcat1.get_root() is None + assert subcat2.get_root() is None + + def test_add_child_throws_if_item(self) -> None: + cat = TestCases.case_1() + item = next(cat.get_items(recursive=True)) + with pytest.raises(pystac.STACError): + cat.add_child(item) # type:ignore + + def test_add_child_returns_link(self) -> None: + parent = Catalog(id="parent", description="test") + child = Catalog(id="child", description="test") + link = parent.add_child(child) + assert isinstance(link, pystac.Link) + link.extra_fields["foo"] = "bar" + link2 = parent.get_single_link("child") + assert link2 is not None + assert link2.extra_fields["foo"] == "bar" + + def test_add_children_returns_links(self) -> None: + parent = Catalog(id="parent", description="test") + child1 = Catalog(id="child", description="test") + child2 = Catalog(id="child2", description="test") + links = parent.add_children([child1, child2]) + assert isinstance(links, list) + assert len(links) == 2 + assert isinstance(links[0], pystac.Link) + assert isinstance(links[1], pystac.Link) + links2 = parent.get_links("child") + assert links[0] in links2 + assert links[1] in links2 + + def test_add_child_override_parent(self) -> None: + parent1 = Catalog(id="parent1", description="test1") + parent2 = Catalog(id="parent2", description="test2") + child = Catalog(id="child", description="test3") + assert child.get_parent() is None + + parent1.add_child(child) + assert child.get_parent() is parent1 + + parent2.add_child(child) + assert child.get_parent() is parent2 + + def test_add_child_set_parent_false(self) -> None: + parent1 = Catalog(id="parent1", description="test1") + parent2 = Catalog(id="parent2", description="test2") + child = Catalog(id="child", description="test3") + assert child.get_parent() is None + + parent1.add_child(child, set_parent=False) + assert child.get_parent() is not parent1 + + parent1.add_child(child) + parent2.add_child(child, set_parent=False) + assert child.get_parent() is parent1 + + def test_add_item_override_parent(self) -> None: + parent1 = Catalog(id="parent1", description="test1") + parent2 = Catalog(id="parent2", description="test2") + child = Item( + id="child", + geometry=None, + bbox=None, + datetime=datetime.now(), + properties={}, + ) + assert child.get_parent() is None + + parent1.add_item(child) + assert child.get_parent() is parent1 + + parent2.add_item(child) + assert child.get_parent() is parent2 + + def test_add_item_set_parent_false(self) -> None: + parent1 = Catalog(id="parent1", description="test1") + parent2 = Catalog(id="parent2", description="test2") + child = Item( + id="child", + geometry=None, + bbox=None, + datetime=datetime.now(), + properties={}, + ) + assert child.get_parent() is None + + parent1.add_item(child, set_parent=False) + assert child.get_parent() is not parent1 + + parent1.add_item(child) + parent2.add_item(child, set_parent=False) + assert child.get_parent() is parent1 + + def test_add_item_throws_if_child(self) -> None: + cat = TestCases.case_1() + child = next(iter(cat.get_children())) + with pytest.raises(pystac.STACError): + cat.add_item(child) # type:ignore + + def test_add_item_returns_link(self) -> None: + parent = Catalog(id="parent", description="test") + child = Item( + id="child", + geometry=None, + bbox=None, + datetime=datetime.now(), + properties={}, + ) + link = parent.add_item(child) + assert isinstance(link, pystac.Link) + link.extra_fields["foo"] = "bar" + link2 = parent.get_single_link("item") + assert link2 is not None + assert link2.extra_fields["foo"] == "bar" + + def test_add_items_returns_links(self) -> None: + parent = Catalog(id="parent", description="test") + child1 = Item( + id="child1", + geometry=None, + bbox=None, + datetime=datetime.now(), + properties={}, + ) + child2 = Item( + id="child2", + geometry=None, + bbox=None, + datetime=datetime.now(), + properties={}, + ) + links = parent.add_items([child1, child2]) + assert isinstance(links, list) + assert len(links) == 2 + assert isinstance(links[0], pystac.Link) + assert isinstance(links[1], pystac.Link) + links2 = parent.get_links("item") + assert links[0] in links2 + assert links[1] in links2 + + def test_get_child_returns_none_if_not_found(self) -> None: + cat = TestCases.case_1() + child = cat.get_child("thisshouldnotbeachildid", recursive=True) + assert child is None + + @pytest.mark.xfail(reason="We've advertised that get_item will be removed in v2") + def test_get_item_is_deprecated_but_still_works(self) -> None: + cat = TestCases.case_1() + with pytest.warns(DeprecationWarning): + item = cat.get_item("area-2-1-imagery", recursive=True) + assert item is not None + + @pytest.mark.xfail(reason="We've advertised that get_item will be removed in v2") + def test_get_item_returns_none_if_not_found(self) -> None: + cat = TestCases.case_1() + with pytest.warns(DeprecationWarning): + item = cat.get_item("thisshouldnotbeanitemid", recursive=True) + assert item is None + + @pytest.mark.xfail(reason="We've advertised that get_all_items will be removed in v2") + def test_get_all_items_is_deprecated_but_still_works(self) -> None: + cat = TestCases.case_1() + with pytest.warns(DeprecationWarning): + all_items = cat.get_all_items() + items = cat.get_items(recursive=True) + assert all(a == i for a, i in zip(all_items, items)) + + def test_get_items_returns_empty_generator_if_not_found(self) -> None: + cat = TestCases.case_1() + items = cat.get_items("thisshouldnotbeanitemid", recursive=True) + assert next(items, None) is None + + @pytest.mark.xfail(reason="CatalogType is deprecated, and we don't care about fixing this test") + def test_sets_catalog_type(self) -> None: + cat = TestCases.case_1() + + assert cat.catalog_type == CatalogType.SELF_CONTAINED + + @pytest.mark.parametrize("catalog", TestCases.all_test_catalogs()) + def test_walk_iterates_correctly(self, catalog: Catalog) -> None: + def test_catalog(cat: Catalog) -> None: + expected_catalog_iterations = 1 + actual_catalog_iterations = 0 + for root, children, items in cat.walk(): + actual_catalog_iterations += 1 + expected_catalog_iterations += len(list(root.get_children())) + + assert {c.id for c in root.get_children()} == { + c.id for c in children + }, "Children unequal" + + assert {c.id for c in root.get_items()} == {c.id for c in items}, ( + "Items unequal" + ) + + assert actual_catalog_iterations == expected_catalog_iterations + + test_catalog(catalog) + + @pytest.mark.parametrize("catalog", TestCases.all_test_catalogs()) + def test_clone_generates_correct_links(self, catalog: Catalog) -> None: + expected_link_types_to_counts: Any = {} + actual_link_types_to_counts: Any = {} + + for root, _, items in catalog.walk(): + expected_link_types_to_counts[root.id] = defaultdict(int) + actual_link_types_to_counts[root.id] = defaultdict(int) + + for link in root.get_links(): + expected_link_types_to_counts[root.id][link.rel] += 1 + + for link in root.clone().get_links(): + actual_link_types_to_counts[root.id][link.rel] += 1 + + for item in items: + expected_link_types_to_counts[item.id] = defaultdict(int) + actual_link_types_to_counts[item.id] = defaultdict(int) + for link in item.get_links(): + expected_link_types_to_counts[item.id][link.rel] += 1 + for link in item.get_links(): + actual_link_types_to_counts[item.id][link.rel] += 1 + + assert set(expected_link_types_to_counts.keys()) == set( + actual_link_types_to_counts.keys() + ) + + for obj_id in actual_link_types_to_counts: + expected_counts = expected_link_types_to_counts[obj_id] + actual_counts = actual_link_types_to_counts[obj_id] + assert set(expected_counts.keys()) == set(actual_counts.keys()) + for rel in expected_counts: + assert actual_counts[rel] == expected_counts[rel], ( + f"Clone of {obj_id} has {actual_counts[rel]} {rel} links, original has {expected_counts[rel]}" + ) + + @pytest.mark.xfail(reason="v2 doesn't care about catalog type") + def test_save_uses_previous_catalog_type(self) -> None: + catalog = TestCases.case_1() + assert catalog.catalog_type == CatalogType.SELF_CONTAINED + with tempfile.TemporaryDirectory() as tmp_dir: + catalog.normalize_hrefs(tmp_dir) + href = catalog.self_href + catalog.save() + + cat2 = pystac.Catalog.from_file(href) + assert cat2.catalog_type == CatalogType.SELF_CONTAINED + + def test_save_to_provided_href(self) -> None: + with tempfile.TemporaryDirectory() as tmp_dir: + catalog = TestCases.case_1() + href = "https://stac.test" + folder = os.path.join(tmp_dir, "cat") + catalog.normalize_hrefs(href) + catalog.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED, dest_href=folder) + + catalog_path = os.path.join(folder, "catalog.json") + assert os.path.exists(catalog_path) + result_cat = Catalog.from_file(catalog_path) + for link in result_cat.get_child_links(): + assert cast(str, link.target).startswith(href) + + def test_save_relative_published_no_self_links(self) -> None: + with tempfile.TemporaryDirectory() as tmp_dir: + catalog = TestCases.case_1() + href = "https://stac.test" + folder = os.path.join(tmp_dir, "cat") + catalog.normalize_hrefs(href) + catalog.save(catalog_type=CatalogType.RELATIVE_PUBLISHED, dest_href=folder) + + catalog_path = os.path.join(folder, "catalog.json") + assert os.path.exists(catalog_path) + result_cat = Catalog.from_file(catalog_path) + + # Check that Items do not have a self link + # Since Item.from_dict automatically adds a self link, we need to look at + # the JSON files themselves. + stac_io = pystac.StacIO.default() + + for current_cat, _, __ in result_cat.walk(): + for item_link in current_cat.get_item_links(): + item_dict = stac_io.read_json(item_link) + self_link = next( + ( + link + for link in item_dict.get("links", []) + if link["rel"] == "self" + ), + None, + ) + assert self_link is None + + def test_save_with_different_stac_io(self) -> None: + catalog = Catalog.from_file( + TestCases.get_path("data-files/catalogs/test-case-1/catalog.json") + ) + stac_io = MockStacIO() + with tempfile.TemporaryDirectory() as tmp_dir: + catalog.normalize_hrefs(tmp_dir) + catalog.save( + catalog_type=CatalogType.ABSOLUTE_PUBLISHED, + dest_href=tmp_dir, + stac_io=stac_io, + ) + + hrefs = [] + for root, _, items in catalog.walk(): + hrefs.append(root.get_self_href()) + for item in items: + hrefs.append(item.get_self_href()) + + assert len(hrefs) == stac_io.mock.write_text.call_count + for call_args_list in stac_io.mock.write_text.call_args_list: + assert call_args_list[0][0] in hrefs + + def test_subcatalogs_saved_to_correct_path(self) -> None: + with tempfile.TemporaryDirectory() as tmp_dir: + catalog = TestCases.case_1() + href = "https://stac.test" + + catalog.normalize_hrefs(href) + catalog.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED, dest_href=tmp_dir) + + # Check the root catalog path + expected_root_catalog_path = os.path.join(tmp_dir, "catalog.json") + assert os.path.exists(expected_root_catalog_path), ( + f"{expected_root_catalog_path} does not exist." + ) + assert os.path.isfile(expected_root_catalog_path), ( + f"{expected_root_catalog_path} is not a file." + ) + + # Check each child catalog + for child_catalog in catalog.get_children(): + relative_path = make_relative_href( + child_catalog.self_href, catalog.self_href, start_is_dir=False + ) + expected_child_path = make_absolute_href( + relative_path, + expected_root_catalog_path, + start_is_dir=False, + ) + assert os.path.exists(expected_child_path), ( + f"{expected_child_path} does not exist." + ) + assert os.path.isfile(expected_child_path), ( + f"{expected_child_path} is not a file." + ) + + # Check each item + for item in catalog.get_items(recursive=True): + relative_path = make_relative_href( + item.self_href, catalog.self_href, start_is_dir=False + ) + expected_item_path = make_absolute_href( + relative_path, + expected_root_catalog_path, + start_is_dir=False, + ) + assert os.path.exists(expected_item_path), ( + f"{expected_item_path} does not exist." + ) + assert os.path.isfile(expected_item_path), ( + f"{expected_item_path} is not a file." + ) + + @pytest.mark.xfail(reason="CatalogType is deprecated in pystac v2, so we don't care about fixing this test") + def test_clone_uses_previous_catalog_type(self) -> None: + catalog = TestCases.case_1() + assert catalog.catalog_type == CatalogType.SELF_CONTAINED + clone = catalog.clone() + assert clone.catalog_type == CatalogType.SELF_CONTAINED + + def test_normalize_hrefs_sets_all_hrefs(self) -> None: + catalog = TestCases.case_1() + catalog.normalize_hrefs("http://example.com") + for root, _, items in catalog.walk(): + self_href = root.get_self_href() + assert self_href is not None + assert self_href.startswith("http://example.com") + for link in root.links: + if link.is_resolved(): + target_href = cast(pystac.STACObject, link.target).self_href + else: + target_href = link.absolute_href + assert "http://example.com" in target_href, ( + '[{}] {} does not contain "{}"'.format( + link.rel, target_href, "http://example.com" + ) + ) + for item in items: + assert "http://example.com" in item.self_href + + def test_normalize_hrefs_makes_absolute_href(self) -> None: + catalog = TestCases.case_1() + catalog.normalize_hrefs("./relativepath") + abspath = make_posix_style(os.path.abspath("./relativepath")) + self_href = catalog.get_self_href() + assert self_href is not None + assert self_href.startswith(abspath) + + def test_normalize_hrefs_skip_unresolved(self) -> None: + catalog = TestCases.case_1() + catalog.normalize_hrefs("http://example.com", skip_unresolved=True) + assert catalog.self_href.startswith("http://example.com") + for link in catalog.links: + if link.rel == "child" or link.rel == "item": + assert not link.is_resolved() + item = Item( + "an-id", + geometry=None, + bbox=None, + datetime=datetime.now(), + properties={}, + ) + catalog.add_item(item, title="This is the test item") + catalog.normalize_hrefs("http://example.com", skip_unresolved=True) + for link in catalog.links: + if link.title == "This is the test item": + assert link.is_resolved() + assert isinstance(link.target, pystac.STACObject) + assert link.target.self_href.startswith("http://example.com") + elif link.rel == "child" or link.rel == "item": + assert not link.is_resolved() + + def test_save_unresolved(self) -> None: + catalog = Catalog("an-id", "a description") + item = Item( + "an-id", + geometry=None, + bbox=None, + datetime=datetime.now(), + properties={}, + ) + catalog.add_item(item) + catalog.add_link(pystac.Link("child", "/not/a/real/path/catalog.json")) + with tempfile.TemporaryDirectory() as temporary_directory: + catalog.set_self_href(os.path.join(temporary_directory, "catalog.json")) + item.set_self_href(os.path.join(temporary_directory, "item.json")) + catalog.save() + assert len(os.listdir(temporary_directory)) == 2 + + with tempfile.TemporaryDirectory() as temporary_directory: + catalog.normalize_and_save(temporary_directory, skip_unresolved=True) + assert len(os.listdir(temporary_directory)) == 2 + + with tempfile.TemporaryDirectory() as temporary_directory: + # We remove the error matching string for pystac v2 + # with pytest.raises(STACError, match="does not resolve to a STAC object"): + with pytest.raises(STACError): + catalog.normalize_and_save(temporary_directory, skip_unresolved=False) + + def test_generate_subcatalogs_works_with_custom_properties(self) -> None: + catalog = TestCases.case_8() + defaults = {"pl:item_type": "PlanetScope"} + catalog.generate_subcatalogs( + "${year}/${month}/${pl:item_type}", defaults=defaults + ) + + month_cat = catalog.get_child("8", recursive=True) + assert month_cat is not None + type_cats = {cat.id for cat in month_cat.get_children()} + + assert type_cats == {"PSScene4Band", "SkySatScene", "PlanetScope"} + + def test_generate_subcatalogs_does_not_change_item_count(self) -> None: + catalog = TestCases.case_7() + + item_counts = { + cat.id: len(list(cat.get_items(recursive=True))) + for cat in catalog.get_children() + } + + catalog.generate_subcatalogs("${year}/${day}") + + with tempfile.TemporaryDirectory() as tmp_dir: + catalog.normalize_hrefs(tmp_dir) + catalog.save(pystac.CatalogType.SELF_CONTAINED) + + cat2 = pystac.Catalog.from_file(os.path.join(tmp_dir, "catalog.json")) + for child in cat2.get_children(): + actual = len(list(child.get_items(recursive=True))) + expected = item_counts[child.id] + assert actual == expected, f" for child '{child.id}'" + + def test_generate_subcatalogs_merge_template_elements(self) -> None: + catalog = Catalog(id="test", description="Test") + item_properties = [ + dict(property1=p1, property2=p2) for p1 in ("A", "B") for p2 in (1, 2) + ] + for ni, properties in enumerate(item_properties): + catalog.add_item( + Item( + id=f"item{ni}", + geometry=ARBITRARY_GEOM, + bbox=ARBITRARY_BBOX, + datetime=datetime.now(UTC), + properties=properties, + ) + ) + result = catalog.generate_subcatalogs("${property1}_${property2}") + + actual_subcats = {cat.id for cat in result} + expected_subcats = { + "{}_{}".format(d["property1"], d["property2"]) for d in item_properties + } + assert len(result) == len(expected_subcats) + assert actual_subcats == expected_subcats + + def test_generate_subcatalogs_can_be_applied_multiple_times(self) -> None: + catalog = TestCases.case_8() + + _ = catalog.generate_subcatalogs("${year}/${month}") + catalog.normalize_hrefs("/tmp") + expected_hrefs = { + item.id: item.get_self_href() for item in catalog.get_items(recursive=True) + } + + result = catalog.generate_subcatalogs("${year}/${month}") + assert len(result) == 0 + catalog.normalize_hrefs("/tmp") + for item in catalog.get_items(recursive=True): + assert item.get_self_href() == expected_hrefs[item.id], ( + f" for item '{item.id}'" + ) + + def test_generate_subcatalogs_works_after_adding_more_items(self) -> None: + catalog = Catalog(id="test", description="Test") + properties = dict(property1="A", property2=1) + catalog.add_item( + Item( + id="item1", + geometry=ARBITRARY_GEOM, + bbox=ARBITRARY_BBOX, + datetime=datetime.now(UTC), + properties=properties, + ) + ) + catalog.generate_subcatalogs("${property1}/${property2}") + catalog.add_item( + Item( + id="item2", + geometry=ARBITRARY_GEOM, + bbox=ARBITRARY_BBOX, + datetime=datetime.now(UTC), + properties=properties, + ) + ) + catalog.generate_subcatalogs("${property1}/${property2}") + + catalog.normalize_hrefs("/tmp") + item1 = next(catalog.get_items("item1", recursive=True)) + item1_parent = item1.get_parent() + assert item1_parent is not None + item2 = next(catalog.get_items("item2", recursive=True)) + item2_parent = item2.get_parent() + assert item2_parent is not None + assert item1_parent.get_self_href() == item2_parent.get_self_href() + + def test_generate_subcatalogs_works_for_branched_subcatalogs(self) -> None: + catalog = Catalog(id="test", description="Test") + item_properties = [ + dict(property1="A", property2=1, property3="i"), # add 3 subcats + dict(property1="A", property2=1, property3="j"), # add 1 more + dict(property1="A", property2=2, property3="i"), # add 2 more + dict(property1="B", property2=1, property3="i"), # add 3 more + ] + for ni, properties in enumerate(item_properties): + catalog.add_item( + Item( + id=f"item{ni}", + geometry=ARBITRARY_GEOM, + bbox=ARBITRARY_BBOX, + datetime=datetime.now(UTC), + properties=properties, + ) + ) + result = catalog.generate_subcatalogs("${property1}/${property2}/${property3}") + assert len(result) == 9 + + actual_subcats = {cat.id for cat in result} + expected_subcats = {"A", "B", "1", "2", "i", "j"} + assert actual_subcats == expected_subcats + + def test_generate_subcatalogs_works_for_subcatalogs_with_same_ids(self) -> None: + catalog = Catalog(id="test", description="Test") + item_properties = [ + dict(property1=1, property2=1), # add 2 subcats + dict(property1=1, property2=2), # add 1 more + dict(property1=2, property2=1), # add 2 more + dict(property1=2, property2=2), # add 1 more + ] + for ni, properties in enumerate(item_properties): + catalog.add_item( + Item( + id=f"item{ni}", + geometry=ARBITRARY_GEOM, + bbox=ARBITRARY_BBOX, + datetime=datetime.now(UTC), + properties=properties, + ) + ) + + result = catalog.generate_subcatalogs("${property1}/${property2}") + assert len(result) == 6 + + catalog.normalize_hrefs("/") + + for item in catalog.get_items(recursive=True): + item_parent = item.get_parent() + assert item_parent is not None + parent_href = item_parent.self_href + path_to_parent, _ = os.path.split(parent_href) + subcats = list( + Path(path_to_parent).parts[1:] + ) # Skip drive letter if present (Windows) + + assert len(subcats) == 2, f" for item '{item.id}'" + + def test_map_items(self) -> None: + def item_mapper(item: pystac.Item) -> pystac.Item: + item.properties["ITEM_MAPPER"] = "YEP" + return item + + with tempfile.TemporaryDirectory() as tmp_dir: + catalog = TestCases.case_1() + + new_cat = catalog.map_items(item_mapper) + + new_cat.normalize_hrefs(os.path.join(tmp_dir, "cat")) + new_cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) + + result_cat = Catalog.from_file(os.path.join(tmp_dir, "cat", "catalog.json")) + + for item in result_cat.get_items(recursive=True): + assert "ITEM_MAPPER" in item.properties + + for item in catalog.get_items(recursive=True): + assert "ITEM_MAPPER" not in item.properties + + def test_map_items_multiple(self) -> None: + def item_mapper(item: pystac.Item) -> list[pystac.Item]: + item2 = item.clone() + item2.id = item2.id + "_2" + item.properties["ITEM_MAPPER_1"] = "YEP" + item2.properties["ITEM_MAPPER_2"] = "YEP" + return [item, item2] + + with tempfile.TemporaryDirectory() as tmp_dir: + catalog = TestCases.case_1() + catalog_items = list(catalog.get_items(recursive=True)) + + new_cat = catalog.map_items(item_mapper) + + new_cat.normalize_hrefs(os.path.join(tmp_dir, "cat")) + new_cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) + + result_cat = Catalog.from_file(os.path.join(tmp_dir, "cat", "catalog.json")) + result_items = list(result_cat.get_items(recursive=True)) + + assert len(catalog_items) * 2 == len(result_items) + + ones, twos = 0, 0 + for item in result_items: + assert ("ITEM_MAPPER_1" in item.properties) or ( + "ITEM_MAPPER_2" in item.properties + ) + if "ITEM_MAPPER_1" in item.properties: + ones += 1 + + if "ITEM_MAPPER_2" in item.properties: + twos += 1 + + assert ones == twos + + for item in catalog.get_items(recursive=True): + assert ("ITEM_MAPPER_1" not in item.properties) and ( + "ITEM_MAPPER_2" not in item.properties + ) + + def test_map_items_multiple_2(self) -> None: + catalog = Catalog(id="test-1", description="Test1") + item1 = Item( + id="item1", + geometry=ARBITRARY_GEOM, + bbox=ARBITRARY_BBOX, + datetime=datetime.now(UTC), + properties={}, + ) + item1.add_asset("ortho", Asset(href="/some/ortho.tif")) + catalog.add_item(item1) + kitten = Catalog(id="test-kitten", description="A cuter version of catalog") + catalog.add_child(kitten) + item2 = Item( + id="item2", + geometry=ARBITRARY_GEOM, + bbox=ARBITRARY_BBOX, + datetime=datetime.now(UTC), + properties={}, + ) + item2.add_asset("ortho", Asset(href="/some/other/ortho.tif")) + kitten.add_item(item2) + + def modify_item_title(item: pystac.Item) -> pystac.Item: + item.properties["title"] = "Some title" + return item + + def duplicate_item(item: pystac.Item) -> list[pystac.Item]: + duplicated_item = item.clone() + duplicated_item.id += "-duplicated" + return [item, duplicated_item] + + c = catalog.map_items(modify_item_title) + c = c.map_items(duplicate_item) + new_catalog = c + + items = new_catalog.get_items(recursive=True) + assert len(list(items)) == 4 + + def test_map_assets_single(self) -> None: + changed_asset = "d43bead8-e3f8-4c51-95d6-e24e750a402b" + + def asset_mapper(key: str, asset: pystac.Asset) -> pystac.Asset: + if key == changed_asset: + asset.title = "NEW TITLE" + + return asset + + with tempfile.TemporaryDirectory() as tmp_dir: + catalog = TestCases.case_2() + + new_cat = catalog.map_assets(asset_mapper) + + new_cat.normalize_hrefs(os.path.join(tmp_dir, "cat")) + new_cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) + + result_cat = Catalog.from_file(os.path.join(tmp_dir, "cat", "catalog.json")) + + found = False + for item in result_cat.get_items(recursive=True): + for key, asset in item.assets.items(): + if key == changed_asset: + found = True + assert asset.title == "NEW TITLE" + else: + assert asset.title != "NEW TITLE" + assert found + + def test_map_assets_tup(self) -> None: + changed_assets: list[str] = [] + + def asset_mapper( + key: str, asset: pystac.Asset + ) -> pystac.Asset | tuple[str, pystac.Asset]: + if asset.media_type and "geotiff" in asset.media_type: + asset.title = "NEW TITLE" + changed_assets.append(key) + return (f"{key}-modified", asset) + else: + return asset + + with tempfile.TemporaryDirectory() as tmp_dir: + catalog = TestCases.case_2() + + new_cat = catalog.map_assets(asset_mapper) + + new_cat.normalize_hrefs(os.path.join(tmp_dir, "cat")) + new_cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) + + result_cat = Catalog.from_file(os.path.join(tmp_dir, "cat", "catalog.json")) + + found = False + not_found = False + for item in result_cat.get_items(recursive=True): + for key, asset in item.assets.items(): + if key.replace("-modified", "") in changed_assets: + found = True + assert asset.title == "NEW TITLE" + else: + not_found = True + assert asset.title != "NEW TITLE" + + assert found + assert not_found + + def test_map_assets_multi(self) -> None: + changed_assets = [] + + def asset_mapper( + key: str, asset: pystac.Asset + ) -> pystac.Asset | dict[str, pystac.Asset]: + if asset.media_type and "geotiff" in asset.media_type: + changed_assets.append(key) + mod1 = asset.clone() + mod1.title = "NEW TITLE 1" + mod2 = asset.clone() + mod2.title = "NEW TITLE 2" + return {f"{key}-mod-1": mod1, f"{key}-mod-2": mod2} + else: + return asset + + with tempfile.TemporaryDirectory() as tmp_dir: + catalog = TestCases.case_2() + + new_cat = catalog.map_assets(asset_mapper) + + new_cat.normalize_hrefs(os.path.join(tmp_dir, "cat")) + new_cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) + + result_cat = Catalog.from_file(os.path.join(tmp_dir, "cat", "catalog.json")) + + found1 = False + found2 = False + not_found = False + for item in result_cat.get_items(recursive=True): + for key, asset in item.assets.items(): + if key.replace("-mod-1", "") in changed_assets: + found1 = True + assert asset.title == "NEW TITLE 1" + elif key.replace("-mod-2", "") in changed_assets: + found2 = True + assert asset.title == "NEW TITLE 2" + else: + not_found = True + assert asset.title != "NEW TITLE" + + assert found1 + assert found2 + assert not_found + + def test_make_all_asset_hrefs_absolute(self) -> None: + cat = TestCases.case_2() + cat.make_all_asset_hrefs_absolute() + ID = "cf73ec1a-d790-4b59-b077-e101738571ed" + item = next(cat.get_items(ID, recursive=True)) + href = item.assets[ID].href + assert is_absolute_href(href) + + def test_make_all_asset_hrefs_relative(self) -> None: + cat = TestCases.case_2() + ID = "cf73ec1a-d790-4b59-b077-e101738571ed" + item = next(cat.get_items(ID, recursive=True)) + asset = item.assets[ID] + original_href = asset.href + cat.make_all_asset_hrefs_absolute() + + assert is_absolute_href(asset.href) + + cat.make_all_asset_hrefs_relative() + + assert not is_absolute_href(asset.href) + assert asset.href == original_href + + @pytest.mark.parametrize("catalog", TestCases.all_test_catalogs()) + def test_make_all_links_relative_or_absolute(self, catalog: Catalog) -> None: + def check_all_relative(cat: Catalog) -> None: + for root, catalogs, items in cat.walk(): + for link in root.links: + if link.rel in HIERARCHICAL_LINKS: + assert not is_absolute_href(link.href) + for item in items: + for link in item.links: + if link.rel in HIERARCHICAL_LINKS: + assert not is_absolute_href(link.href) + + def check_all_absolute(cat: Catalog) -> None: + for root, catalogs, items in cat.walk(): + for link in root.links: + assert is_absolute_href(link.href) + for item in items: + for link in item.links: + assert is_absolute_href(link.href) + + with tempfile.TemporaryDirectory() as tmp_dir: + c2 = catalog.full_copy() + c2.normalize_hrefs(tmp_dir) + c2.catalog_type = CatalogType.RELATIVE_PUBLISHED + check_all_relative(c2) + c2.catalog_type = CatalogType.ABSOLUTE_PUBLISHED + check_all_absolute(c2) + + @pytest.mark.block_network() + def test_self_contained_catalog_collection_item_links(self) -> None: + """See issue https://github.com/stac-utils/pystac/issues/657""" + with tempfile.TemporaryDirectory() as tmp_dir: + catalog = pystac.Catalog( + id="catalog-issue-657", description="catalog-issue-657" + ) + collection = pystac.Collection( + "collection-issue-657", + "collection-issue-657", + pystac.Extent( + spatial=pystac.SpatialExtent([[-180.0, -90.0, 180.0, 90.0]]), + temporal=pystac.TemporalExtent([[datetime(2021, 11, 1), None]]), + ), + license="other", + ) + + item = pystac.Item( + id="item-issue-657", + stac_extensions=[], + geometry=ARBITRARY_GEOM, + bbox=ARBITRARY_BBOX, + datetime=datetime(2021, 11, 1), + properties={}, + ) + + collection.add_item(item) + catalog.add_child(collection) + + catalog.normalize_hrefs(tmp_dir) + catalog.validate_all() + + catalog.save(catalog_type=CatalogType.SELF_CONTAINED) + with open( + f"{tmp_dir}/collection-issue-657/item-issue-657/item-issue-657.json" + ) as f: + item_json = json.load(f) + + for link in item_json["links"]: + # self links are always absolute + if link["rel"] == "self": + continue + + href = link["href"] + assert not is_absolute_href(href), ( + f"Link with rel={link['rel']} is absolute!" + ) + + def test_full_copy_and_normalize_works_with_created_stac(self) -> None: + cat = TestCases.case_3() + cat_copy = cat.full_copy() + cat_copy.normalize_hrefs("http://example.com") + for root, catalogs, items in cat_copy.walk(): + for link in root.links: + if link.rel != "self": + assert link.target is not None + for item in items: + for link in item.links: + if link.rel != "self": + assert link.get_href() is not None + + def test_extra_fields(self) -> None: + catalog = TestCases.case_1() + + catalog.extra_fields["custom_field"] = "Special content" + + with tempfile.TemporaryDirectory() as tmp_dir: + p = os.path.join(tmp_dir, "catalog.json") + catalog.save_object(include_self_link=False, dest_href=p) + with open(p) as f: + cat_json = json.load(f) + assert "custom_field" in cat_json + assert cat_json["custom_field"] == "Special content" + + read_cat = pystac.Catalog.from_file(p) + assert "custom_field" in read_cat.extra_fields + assert read_cat.extra_fields["custom_field"] == "Special content" + + @pytest.mark.parametrize("cat", TestCases.all_test_catalogs()) + @pytest.mark.vcr() + def test_validate_all(self, cat: Catalog) -> None: + # If hrefs are not set, it will fail validation. + if cat.get_self_href() is None: + cat.normalize_hrefs("/tmp") + cat.validate_all() + + @pytest.mark.vcr() + def test_validate_all_invalid(self) -> None: + # Make one invalid, write it off, read it in, ensure it throws + cat = TestCases.case_1() + item = next(cat.get_items("area-1-1-labels", recursive=True)) + item.geometry = {"type": "INVALID", "coordinates": "NONE"} + with tempfile.TemporaryDirectory() as tmp_dir: + cat.normalize_hrefs(tmp_dir) + cat.save(catalog_type=pystac.CatalogType.SELF_CONTAINED) + + cat2 = pystac.Catalog.from_file(os.path.join(tmp_dir, "catalog.json")) + + with pytest.raises(pystac.STACValidationError): + cat2.validate_all() + + def test_set_hrefs_manually(self) -> None: + catalog = TestCases.case_1() + + # Modify the datetimes + year = 2004 + month = 2 + for item in catalog.get_items(recursive=True): + assert item.datetime is not None + item.datetime = item.datetime.replace(year=year, month=month) + year += 1 + month += 1 + + with tempfile.TemporaryDirectory() as tmp_dir: + for root, _, items in catalog.walk(): + # Set root's HREF based off the parent + parent = root.get_parent() + if parent is None: + root_dir = tmp_dir + else: + d = os.path.dirname(parent.self_href) + root_dir = os.path.join(d, root.id) + root_href = os.path.join(root_dir, root.DEFAULT_FILE_NAME) + root.set_self_href(root_href) + + # Set each item's HREF based on it's datetime + for item in items: + assert item.datetime is not None + item_href = f"{root_dir}/{item.datetime.year}-{item.datetime.month}/{item.id}.json" + item.set_self_href(item_href) + + catalog.save(catalog_type=CatalogType.SELF_CONTAINED) + + read_catalog = Catalog.from_file(os.path.join(tmp_dir, "catalog.json")) + + for root, _, items in read_catalog.walk(): + parent = root.get_parent() + if parent is None: + assert root.get_self_href() == make_posix_style( + os.path.join(tmp_dir, "catalog.json") + ) + else: + d = os.path.dirname(parent.self_href) + assert root.get_self_href() == make_posix_style( + os.path.join(d, root.id, root.DEFAULT_FILE_NAME) + ) + for item in items: + assert item.datetime is not None + end = posixpath.join( + f"{item.datetime.year}-{item.datetime.month}", + f"{item.id}.json", + ) + self_href = item.get_self_href() + assert self_href is not None + assert self_href.endswith(end), ( + f"{self_href} does not end with {end}" + ) + + @pytest.mark.parametrize("cat", TestCases.all_test_catalogs()) + def test_collections_cache_correctly(self, cat: Catalog) -> None: + mock_io = MockStacIO() + cat._stac_io = mock_io + expected_collection_reads = set() + for root, _, items in cat.walk(): + if isinstance(root, Collection) and root != cat: + expected_collection_reads.add(root.get_self_href()) + + # Iterate over items to make sure they are read + assert list(items) is not None + + call_uris: list[Any] = [ + call[0][0] + for call in mock_io.mock.read_text.call_args_list + if call[0][0] in expected_collection_reads + ] + + for collection_uri in expected_collection_reads: + calls = len([x for x in call_uris if x == collection_uri]) + assert calls == 1, ( + f"{collection_uri} was read {calls} times instead of once!" + ) + + def test_reading_iterating_and_writing_works_as_expected(self) -> None: + """Test case to cover issue #88""" + stac_uri = TestCases.get_path("data-files/catalogs/test-case-6/catalog.json") + cat = Catalog.from_file(stac_uri) + + # Iterate over the items. This was causing failure in + # the later iterations as per issue #88 + for item in cat.get_items(recursive=True): + pass + + with tempfile.TemporaryDirectory() as tmp_dir: + new_stac_uri = os.path.join(tmp_dir, "test-case-6") + cat.normalize_hrefs(new_stac_uri) + cat.save(catalog_type=CatalogType.SELF_CONTAINED) + + # Open the local copy and iterate over it. + cat2 = Catalog.from_file(os.path.join(new_stac_uri, "catalog.json")) + + for item in cat2.get_items(recursive=True): + # Iterate again over the items. This would fail in #88 + pass + + def test_get_children_cbers(self) -> None: + cat = TestCases.case_6() + assert len(list(cat.get_children())) == 4 + + def test_resolve_planet(self) -> None: + """Test against a bug that caused infinite recursion during link resolution""" + cat = TestCases.case_8() + for root, _, items in cat.walk(): + for item in items: + item.resolve_links() + root.resolve_links() + + def test_handles_children_with_same_id(self) -> None: + # This catalog has the root and child collection share an ID. + cat = pystac.Catalog.from_file( + TestCases.get_path("data-files/invalid/shared-id/catalog.json") + ) + items = list(cat.get_items(recursive=True)) + + assert len(items) == 1 + + def test_catalog_with_href_caches_by_href(self) -> None: + cat = TestCases.case_1() + cache = cat._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 + + def test_from_invalid_dict_raises_exception(self) -> None: + stac_io = pystac.StacIO.default() + collection_dict = stac_io.read_json( + TestCases.get_path("data-files/collections/multi-extent.json") + ) + with pytest.raises(pystac.STACTypeError): + _ = pystac.Catalog.from_dict(collection_dict) + + def test_get_collections(self) -> None: + catalog = TestCases.case_2() + collections = list(catalog.get_collections()) + + assert len(collections) > 0 + assert all(isinstance(c, pystac.Collection) for c in collections) + + def test_get_all_collections(self) -> None: + catalog = TestCases.case_1() + all_collections = list(catalog.get_all_collections()) + + assert len(all_collections) == 4 + assert all(isinstance(c, pystac.Collection) for c in all_collections) + + def test_get_single_links_media_type(self) -> None: + catalog = TestCases.case_1() + + catalog.links.append( + pystac.Link(rel="search", target="./search.html", media_type="text/html") + ) + catalog.links.append( + pystac.Link( + rel="search", target="./search.json", media_type="application/geo+json" + ) + ) + + html_link = catalog.get_single_link(rel="search") + assert html_link is not None + assert html_link.href == "./search.html" + html_link = catalog.get_single_link(media_type="text/html") + assert html_link is not None + assert html_link.href == "./search.html" + json_link = catalog.get_single_link( + rel="search", media_type="application/geo+json" + ) + assert json_link is not None + assert json_link.href == "./search.json" + no_link = catalog.get_single_link(rel="via") + assert no_link is None + first_link = catalog.get_single_link() + assert first_link is not None + assert first_link.rel == "self" + + def test_get_links(self) -> None: + catalog = TestCases.case_1() + + catalog.links.append( + pystac.Link(rel="search", target="./search.html", media_type="text/html") + ) + catalog.links.append( + pystac.Link( + rel="search", target="./search.json", media_type="application/geo+json" + ) + ) + assert ( + len(catalog.get_links(rel="search", media_type="application/geo+json")) == 1 + ) + assert len(catalog.get_links(media_type="text/html")) == 1 + assert ( + len(catalog.get_links(media_type=["text/html", "application/geo+json"])) + == 2 + ) + assert len(catalog.get_links(rel="search")) == 2 + assert len(catalog.get_links(rel="via")) == 0 + assert len(catalog.get_links()) == 6 + + def test_to_dict_no_self_href(self) -> None: + catalog = Catalog(id="an-id", description="A test Catalog") + d = catalog.to_dict(include_self_link=False) + Catalog.from_dict(d) + + +class TestFullCopy: + def check_link(self, link: pystac.Link, tag: str) -> None: + if link.is_resolved(): + target_href: str = cast(pystac.STACObject, link.target).self_href + else: + target_href = str(link.target) + assert tag in target_href, ( + f'[{link.rel}] {target_href} does not contain "{tag}"' + ) + + def check_item(self, item: Item, tag: str) -> None: + for link in item.links: + self.check_link(link, tag) + + def check_catalog(self, c: Catalog, tag: str) -> None: + assert len(c.get_links("root")) == 1, f"Failure for catalog: {c}" + + for link in c.links: + self.check_link(link, tag) + + for child in c.get_children(): + self.check_catalog(child, tag) + + for item in c.get_items(): + self.check_item(item, tag) + + def test_full_copy_1(self) -> None: + with tempfile.TemporaryDirectory() as tmp_dir: + cat = Catalog(id="test", description="test catalog") + + item = Item( + id="test_item", + geometry=ARBITRARY_GEOM, + bbox=ARBITRARY_BBOX, + datetime=datetime.now(UTC), + properties={}, + ) + + cat.add_item(item) + + cat.normalize_hrefs(os.path.join(tmp_dir, "catalog-full-copy-1-source")) + cat2 = cat.full_copy() + cat2.normalize_hrefs(os.path.join(tmp_dir, "catalog-full-copy-1-dest")) + + self.check_catalog(cat, "source") + self.check_catalog(cat2, "dest") + + def test_full_copy_2(self) -> None: + with tempfile.TemporaryDirectory() as tmp_dir: + cat = Catalog(id="test", description="test catalog") + image_item = Item( + id="Imagery", + geometry=ARBITRARY_GEOM, + bbox=ARBITRARY_BBOX, + datetime=datetime.now(UTC), + properties={}, + ) + for key in ["ortho", "dsm"]: + image_item.add_asset( + key, + Asset(href=f"some/{key}.tif", media_type=MediaType.GEOTIFF), + ) + + label_item = Item( + id="Labels", + geometry=ARBITRARY_GEOM, + bbox=ARBITRARY_BBOX, + datetime=datetime.now(UTC), + properties={}, + ) + cat.add_items([image_item, label_item]) + + cat.normalize_hrefs(os.path.join(tmp_dir, "catalog-full-copy-2-source")) + cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) + cat2 = cat.full_copy() + cat2.normalize_hrefs(os.path.join(tmp_dir, "catalog-full-copy-2-dest")) + cat2.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) + + self.check_catalog(cat, "source") + self.check_catalog(cat2, "dest") + + def test_full_copy_3(self) -> None: + with tempfile.TemporaryDirectory() as tmp_dir: + root_cat = TestCases.case_1() + root_cat.normalize_hrefs( + os.path.join(tmp_dir, "catalog-full-copy-3-source") + ) + root_cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) + cat2 = root_cat.full_copy() + cat2.normalize_hrefs(os.path.join(tmp_dir, "catalog-full-copy-3-dest")) + cat2.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) + + self.check_catalog(root_cat, "source") + self.check_catalog(cat2, "dest") + + def test_full_copy_4(self) -> None: + with tempfile.TemporaryDirectory() as tmp_dir: + root_cat = TestCases.case_2() + root_cat.normalize_hrefs( + os.path.join(tmp_dir, "catalog-full-copy-4-source") + ) + root_cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) + cat2 = root_cat.full_copy() + cat2.normalize_hrefs(os.path.join(tmp_dir, "catalog-full-copy-4-dest")) + cat2.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) + + self.check_catalog(root_cat, "source") + self.check_catalog(cat2, "dest") + + # Check that the relative asset link was saved correctly in the copy. + ID = "cf73ec1a-d790-4b59-b077-e101738571ed" + item = next(cat2.get_items(ID, recursive=True)) + href = item.assets[ID].get_absolute_href() + assert href is not None + assert os.path.exists(href) + + +class TestCatalogSubClass: + """This tests cases related to creating classes inheriting from pystac.Catalog to + ensure that inheritance, class methods, etc. function as expected.""" + + case_1 = TestCases.get_path("data-files/catalogs/test-case-1/catalog.json") + + class BasicCustomCatalog(pystac.Catalog): + def get_items(self) -> Iterator[Item]: # type: ignore + # This get_items does not have the `recursive` kwarg. This mimics + # the current state of pystac-client and is intended to test + # backwards compatibility of inherited classes + return super().get_items() + + def test_from_dict_returns_subclass(self) -> None: + self.stac_io = pystac.StacIO.default() + catalog_dict = self.stac_io.read_json(self.case_1) + custom_catalog = self.BasicCustomCatalog.from_dict(catalog_dict) + assert isinstance(custom_catalog, self.BasicCustomCatalog) + + def test_from_file_returns_subclass(self) -> None: + custom_catalog = self.BasicCustomCatalog.from_file(self.case_1) + assert isinstance(custom_catalog, self.BasicCustomCatalog) + + def test_clone(self) -> None: + custom_catalog = self.BasicCustomCatalog.from_file(self.case_1) + cloned_catalog = custom_catalog.clone() + assert isinstance(cloned_catalog, self.BasicCustomCatalog) + + def test_get_all_items_works(self) -> None: + custom_catalog = self.BasicCustomCatalog.from_file(self.case_1) + cloned_catalog = custom_catalog.clone() + with pytest.warns(DeprecationWarning): + cloned_catalog.get_all_items() + + def test_get_item_works(self) -> None: + custom_catalog = self.BasicCustomCatalog.from_file(self.case_1) + cloned_catalog = custom_catalog.clone() + with pytest.warns(DeprecationWarning): + cloned_catalog.get_item("area-1-1-imagery") + + +def test_custom_catalog_from_dict(catalog: Catalog) -> None: + # https://github.com/stac-utils/pystac/issues/862 + class CustomCatalog(Catalog): + @classmethod + def from_dict( + cls, + d: dict[str, Any], + href: str | None = None, + root: Catalog | None = None, + migrate: bool = True, + preserve_dict: bool = True, + ) -> CustomCatalog: + return super().from_dict(d) + + _ = CustomCatalog.from_dict(catalog.to_dict()) + + +@pytest.mark.parametrize("add_canonical", (True, False)) +def test_remove_hierarchical_links( + test_case_1_catalog: Catalog, add_canonical: bool +) -> None: + test_case_1_catalog.remove_hierarchical_links(add_canonical=add_canonical) + for link in test_case_1_catalog.links: + assert not link.is_hierarchical() + assert bool(test_case_1_catalog.get_single_link("canonical")) == add_canonical + + +def test_fully_resolve(tmp_path: Path, test_case_1_catalog: Catalog) -> None: + test_case_1_catalog.save(dest_href=str(tmp_path / "before")) + assert len(list((tmp_path / "before").glob("**/*.json"))) == 1 + test_case_1_catalog.fully_resolve() + test_case_1_catalog.save(dest_href=str(tmp_path / "after")) + assert len(list((tmp_path / "after").glob("**/*.json"))) == 15 + + +def test_get_items_with_multiple_ids(test_case_1_catalog: Catalog) -> None: + cat = test_case_1_catalog + items = cat.get_items("area-2-1-imagery", "area-1-1-labels", recursive=True) + assert len(list(items)) == 2 + + +@pytest.mark.vcr() +def test_validate_all_with_max_n(test_case_1_catalog: Catalog) -> None: + cat = test_case_1_catalog + assert cat.validate_all() == 8 + assert cat.validate_all(max_items=6) == 6 + assert cat.validate_all(max_items=1) == 1 + + +@pytest.mark.vcr() +def test_validate_all_with_recusive_off(test_case_1_catalog: Catalog) -> None: + cat = test_case_1_catalog + assert cat.validate_all() == 8 + assert cat.validate_all(recursive=False) == 0 + + +@pytest.fixture +def nested_catalog() -> pystac.Catalog: + """ + Structure: + + ├── catalog.json + ├── products + │ ├── catalog.json + │ └── product_a + │ └── catalog.json + └── variables + ├── catalog.json + └── variable_a + └── catalog.json + └── variable_a_1 + └── collection.json + """ + root = pystac.Catalog("root", "root") + variables = pystac.Catalog("variables", "variables") + products = pystac.Catalog("products", "products") + + root.add_child(variables) + root.add_child(products) + + variable_a = pystac.Catalog("variable_a", "variable_a") + product_a = pystac.Catalog("product_a", "product_a") + + variables.add_child(variable_a) + products.add_child(product_a) + + variable_a_1 = pystac.Collection( + "variable_a_1", "variable_a_1", extent=ARBITRARY_EXTENT + ) + variable_a.add_child(variable_a_1) + + return root + + +def test_get_all_collections_deeply_nested(nested_catalog: pystac.Catalog) -> None: + catalog = nested_catalog + all_collections = list(catalog.get_all_collections()) + + assert len(all_collections) == 1 + assert all(isinstance(c, pystac.Collection) for c in all_collections) + + +def test_set_parent_false_stores_in_proper_place_on_normalize_and_save( + nested_catalog: pystac.Catalog, tmp_path: Path +) -> None: + root = nested_catalog + product_a = next(root.get_child("products").get_children()) # type: ignore + variable_a = next(root.get_child("variables").get_children()) # type: ignore + + variable_a.add_child(product_a, set_parent=False) + + root.normalize_and_save( + root_href=str(tmp_path), catalog_type=pystac.CatalogType.ABSOLUTE_PUBLISHED + ) + assert (tmp_path / "products" / "product_a").exists() + assert not (tmp_path / "variables" / "variable_a" / "product_a").exists() + + +def test_set_parent_false_stores_in_proper_place_on_save( + nested_catalog: pystac.Catalog, tmp_path: Path +) -> None: + nested_catalog.normalize_and_save( + root_href=str(tmp_path), catalog_type=pystac.CatalogType.ABSOLUTE_PUBLISHED + ) + root = pystac.Catalog.from_file(tmp_path / "catalog.json") + product_a = next(root.get_child("products").get_children()) # type: ignore + variable_a = next(root.get_child("variables").get_children()) # type: ignore + + variable_a.add_child(product_a, set_parent=False) + + root.save(pystac.CatalogType.SELF_CONTAINED, dest_href=str(tmp_path)) + + assert (tmp_path / "products" / "product_a").exists() + assert not (tmp_path / "variables" / "variable_a" / "product_a").exists() + + +BEST_PRACTICE_CATALOG_TEMPLATE = "{id}" +BEST_PRACTICE_ITEM_TEMPLATE = "{id}" +TEST_CATALOG_TEMPLATE = "cat/${id}/${description}" +TEST_ITEM_TEMPLATE = "cat/items/${id}" +STRATEGY = TemplateLayoutStrategy( + catalog_template=TEST_CATALOG_TEMPLATE, item_template=TEST_ITEM_TEMPLATE +) + + +@pytest.mark.parametrize( + "root_strategy,sub_strategy,provided_root_strategy,provided_sub_strategy,root_template,sub_template", + [ + ( + None, + None, + None, + None, + BEST_PRACTICE_CATALOG_TEMPLATE, + BEST_PRACTICE_CATALOG_TEMPLATE, + ), + (STRATEGY, None, None, None, TEST_CATALOG_TEMPLATE, TEST_CATALOG_TEMPLATE), + ( + STRATEGY, + BestPracticesLayoutStrategy(), + None, + None, + TEST_CATALOG_TEMPLATE, + BEST_PRACTICE_CATALOG_TEMPLATE, + ), + ( + STRATEGY, + None, + BestPracticesLayoutStrategy(), + None, + BEST_PRACTICE_CATALOG_TEMPLATE, + TEST_CATALOG_TEMPLATE, + ), + ( + STRATEGY, + None, + None, + BestPracticesLayoutStrategy(), + TEST_CATALOG_TEMPLATE, + BEST_PRACTICE_CATALOG_TEMPLATE, + ), + ], +) +def test_add_child_layout_strategy( + root_strategy: HrefLayoutStrategy, + sub_strategy: HrefLayoutStrategy, + provided_root_strategy: HrefLayoutStrategy, + provided_sub_strategy: HrefLayoutStrategy, + root_template: str, + sub_template: str, +) -> None: + """Test for layout strategy when adding a child. + + If no layout strategy is specified, children HREF should + always follow BestPracticesLayoutStrategy. + If only root strategy is set, all children HREFs should + follow than strategy. + If root and child strategy are set, root and child children + may follow different strategies. + Strategy provided to `add_child` always overrides other settings. + """ + + base_url = "http://example.com" + catalog = Catalog( + id="test", + description="test desc", + href=f"{base_url}/catalog.json", + strategy=root_strategy, + ) + subcat = Catalog(id="subcat", description="subcat desc", strategy=sub_strategy) + subsubcat = Catalog(id="subsubcat", description="subsubcat desc") + + catalog.add_child(subcat, strategy=provided_root_strategy) + subcat.add_child(subsubcat, strategy=provided_sub_strategy) + + root_template = root_template.format( + id=subcat.id, description=subcat.description + ).replace("$", "") + sub_template = sub_template.format( + id=subsubcat.id, description=subsubcat.description + ).replace("$", "") + + assert subcat.self_href == f"{base_url}/{root_template}/catalog.json" + assert ( + subsubcat.self_href == f"{base_url}/{root_template}/{sub_template}/catalog.json" + ) + + +@pytest.mark.parametrize( + "root_strategy,sub_strategy,provided_root_strategy," + "root_template,sub_template,norm_template", + [ + ( + None, + None, + None, + BEST_PRACTICE_CATALOG_TEMPLATE, + BEST_PRACTICE_CATALOG_TEMPLATE, + BEST_PRACTICE_CATALOG_TEMPLATE, + ), + ( + STRATEGY, + None, + None, + TEST_CATALOG_TEMPLATE, + TEST_CATALOG_TEMPLATE, + TEST_CATALOG_TEMPLATE, + ), + ( + STRATEGY, + BestPracticesLayoutStrategy(), + None, + TEST_CATALOG_TEMPLATE, + BEST_PRACTICE_CATALOG_TEMPLATE, + TEST_CATALOG_TEMPLATE, + ), + ( + STRATEGY, + None, + BestPracticesLayoutStrategy(), + TEST_CATALOG_TEMPLATE, + TEST_CATALOG_TEMPLATE, + BEST_PRACTICE_CATALOG_TEMPLATE, + ), + ], +) +def test_add_child_layout_strategy_normalize( + root_strategy: HrefLayoutStrategy, + sub_strategy: HrefLayoutStrategy, + provided_root_strategy: HrefLayoutStrategy, + root_template: str, + sub_template: str, + norm_template: str, +) -> None: + """Test for layout strategy when adding a child and normalizing HREFs. + + If no layout strategy is specified, children HREF + should always follow BestPracticesLayoutStrategy. + If only root strategy is set, all children HREFs + should follow than strategy. + If root and child strategy are set, root strategy + overrides child strategy. + Strategy provided to `normalize_href` always + overrides other settings. + """ + + base_url = "http://example.com" + catalog = Catalog( + id="test", + description="test desc", + href=f"{base_url}/catalog.json", + strategy=root_strategy, + ) + subcat = Catalog(id="subcat", description="subcat desc", strategy=sub_strategy) + subsubcat = Catalog(id="subsubcat", description="subsubcat desc") + catalog.add_child(subcat) + subcat.add_child(subsubcat) + + _root_template = root_template.format( + id=subcat.id, description=subcat.description + ).replace("$", "") + _sub_template = sub_template.format( + id=subsubcat.id, description=subsubcat.description + ).replace("$", "") + + assert subcat.self_href == f"{base_url}/{_root_template}/catalog.json" + assert ( + subsubcat.self_href + == f"{base_url}/{_root_template}/{_sub_template}/catalog.json" + ) + + catalog.normalize_hrefs(base_url, strategy=provided_root_strategy) + + _root_template = norm_template.format( + id=subcat.id, description=subcat.description + ).replace("$", "") + _sub_template = norm_template.format( + id=subsubcat.id, description=subsubcat.description + ).replace("$", "") + + assert subcat.self_href == f"{base_url}/{_root_template}/catalog.json" + assert ( + subsubcat.self_href + == f"{base_url}/{_root_template}/{_sub_template}/catalog.json" + ) + + +@pytest.mark.parametrize( + "root_strategy,provided_strategy,template", + [ + (None, None, BEST_PRACTICE_ITEM_TEMPLATE), + (STRATEGY, None, TEST_ITEM_TEMPLATE), + (STRATEGY, BestPracticesLayoutStrategy(), BEST_PRACTICE_ITEM_TEMPLATE), + ], +) +def test_add_item_layout_strategy( + root_strategy: HrefLayoutStrategy, + provided_strategy: HrefLayoutStrategy, + template: str, +) -> None: + """Test for layout strategy when adding an item. + + If no layout strategy is specified, item HREF + should always follow BestPracticesLayoutStrategy. + If only root strategy is set, all item HREFs + should follow than strategy. + Strategy provided to `add_item` always overrides other settings. + """ + + base_url = "http://example.com" + item_id = "item_id" + catalog = Catalog( + id="test", + description="test desc", + href=f"{base_url}/catalog.json", + strategy=root_strategy, + ) + item = Item( + id=item_id, + geometry={ + "type": "Polygon", + "coordinates": [ + [ + [180.0, -90.0], + [180.0, 90.0], + [-180.0, 90.0], + [-180.0, -90.0], + [180.0, -90.0], + ] + ], + }, + bbox=[-180, -90, 180, 90], + datetime=datetime(2023, 1, 1), + properties={}, + assets={ + "data": Asset( + href="http://example.com/assets/data.tif", + roles=["data"], + title="DATA", + ) + }, + ) + + catalog.add_item(item, strategy=provided_strategy) + + template = template.format(id=item.id).replace("$", "") + + assert item.self_href == f"{base_url}/{template}/{item_id}.json" + + +def test_APILayoutStrategy_requires_root_to_be_url( + catalog: Catalog, collection: Collection, item: Item +) -> None: + collection.add_item(item) + catalog.add_child(collection) + with pytest.raises( + pystac.errors.STACError, + match="When using APILayoutStrategy the root_href must be a URL", + ): + catalog.normalize_hrefs(root_href="issues-1486", strategy=APILayoutStrategy()) + + +def test_get_child_links_cares_about_media_type(catalog: pystac.Catalog) -> None: + catalog.links.extend( + [ + pystac.Link( + rel="child", target="./child-1.json", media_type="application/json" + ), + pystac.Link( + rel="child", target="./child-2.json", media_type="application/geo+json" + ), + pystac.Link(rel="child", target="./child-3.json"), + # this one won't get counted since it's the wrong media_type + pystac.Link(rel="child", target="./child.html", media_type="text/html"), + ] + ) + + assert len(catalog.get_child_links()) == 3 + + +def test_get_item_links_cares_about_media_type(catalog: pystac.Catalog) -> None: + catalog.links.extend( + [ + pystac.Link( + rel="item", target="./item-1.json", media_type="application/json" + ), + pystac.Link( + rel="item", target="./item-2.json", media_type="application/geo+json" + ), + pystac.Link(rel="item", target="./item-3.json"), + # this one won't get counted since it's the wrong media_type + pystac.Link(rel="item", target="./item.html", media_type="text/html"), + ] + ) + + assert len(catalog.get_item_links()) == 3 + + +def test_get_root_link_cares_about_media_type(catalog: pystac.Catalog) -> None: + catalog.links.insert( + 0, pystac.Link(rel="root", target="./self.json", media_type="text/html") + ) + root_link = catalog.get_root_link() + assert root_link and root_link.target != "./self.json" + + +def test_clone_extra_fields(catalog: Catalog) -> None: + catalog.extra_fields["foo"] = "bar" + cloned = catalog.clone() + assert cloned.extra_fields["foo"] == "bar" + + +def test_warn_if_next_link_present(catalog: Catalog) -> None: + catalog.links.append(Link(rel="next", target="./next.json")) + with pytest.warns(UserWarning): + _ = list(catalog.get_children()) diff --git a/tests/v1/test_collection.py b/tests/v1/test_collection.py new file mode 100644 index 000000000..901631bbd --- /dev/null +++ b/tests/v1/test_collection.py @@ -0,0 +1,875 @@ +from __future__ import annotations + +import json +import os +import tempfile +from collections.abc import Iterator +from copy import deepcopy +from datetime import datetime +from typing import Any + +import pytest +from dateutil import tz + +import pystac +from pystac import ( + Asset, + Catalog, + CatalogType, + Collection, + Extent, + Item, + ItemCollection, + Provider, + SpatialExtent, + TemporalExtent, +) +from pystac.extensions.eo import EOExtension +from pystac.utils import datetime_to_str, get_required, str_to_datetime +from pystac.validation import validate_dict + +from .utils import ARBITRARY_BBOX, ARBITRARY_GEOM, TestCases + + +pytestmark = pytest.mark.passing_v2 + + +TEST_DATETIME = datetime(2020, 3, 14, 16, 32) + + +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(), + ) + + +@pytest.mark.xfail(reason="SpatialExtent.from_coordinates is not supported in pystac v2") +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) + + +@pytest.mark.xfail(reason="Collection does not have a catalog type in pystac v2") +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 + + +@pytest.mark.xfail(reason="Collection does not have a catalog type in pystac v2") +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 [ + [ + datetime_to_str(item2.common_metadata.start_datetime), + base_extent.temporal.intervals[0][1], + ] + ] == collection.extent.temporal.intervals + + +@pytest.mark.xfail(reason="Supplying href in init is not supported in pystac v2") +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 + + +@pytest.mark.xfail(reason="Caching is not implemented in pystac v2") +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 + + +@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 is not None + 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", + "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 + + +@pytest.mark.xfail(reason="Dict is never mutated in pystac v2") +def test_from_dict_with_preserve_dict_False_does_not_preserve_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) + # 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 + + +@pytest.mark.xfail(reason="Collection.from_dict does not set self href in pystac v2") +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" + ) + ) + instruments_schema = get_required( + collection.summaries["instruments"], + collection.summaries, + "instruments", + ) + + assert isinstance(instruments_schema, dict) + + +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) + + +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() + ) + + cloned_collection = original_collection.clone() + + 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}" + ) + + +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]]) + + +@pytest.mark.xfail(reason="pystac v2 uses strings to represent interval") +@pytest.mark.block_network() +def test_temporal_extent_represents_interval_as_datetimes() -> 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) + + assert temporal_extent.intervals == [interval] + + +@pytest.mark.block_network() +def test_temporal_extent_allows_single_interval() -> None: + start = "2022-01-01T00:00:00Z" + end = "2022-01-31T23:59:59Z" + start_datetime = str_to_datetime(start) + end_datetime = str_to_datetime(end) + + interval = [start_datetime, end_datetime] + temporal_extent = TemporalExtent(intervals=interval) + + assert temporal_extent.intervals == [[start, end]] + + +@pytest.mark.block_network() +def test_temporal_extent_allows_single_interval_open_start() -> None: + end = "2022-01-31T23:59:59Z" + end_datetime = str_to_datetime(end) + + interval = [None, end_datetime] + temporal_extent = TemporalExtent(intervals=interval) + + assert temporal_extent.intervals == [[None, end]] + + +@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 + + +@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() + + +@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) + ), + }, + ) + + 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 + + interval = extent.temporal.intervals[0] + assert interval[0] == datetime_to_str(datetime(2000, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC)) + assert interval[1] == datetime_to_str(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.""" + + MULTI_EXTENT = TestCases.get_path("data-files/collections/multi-extent.json") + + class BasicCustomCollection(pystac.Collection): + def get_items(self) -> Iterator[Item]: # type: ignore + # This get_items does not have the `recursive` kwarg. This mimics + # the current state of pystac-client and is intended to test + # backwards compatibility of inherited classes + return super().get_items() + + def test_from_dict_returns_subclass(self) -> None: + stac_io = pystac.StacIO.default() + collection_dict = stac_io.read_json(self.MULTI_EXTENT) + custom_collection = self.BasicCustomCollection.from_dict(collection_dict) + + assert isinstance(custom_collection, self.BasicCustomCollection) + + def test_from_file_returns_subclass(self) -> None: + custom_collection = self.BasicCustomCollection.from_file(self.MULTI_EXTENT) + + 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() + + assert isinstance(cloned_collection, self.BasicCustomCollection) + + @pytest.mark.xfail(reason="Pystac v2 requires that custom classes match method signatures") + def test_collection_get_item_works(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() + collection.get_item("area-1-1-imagery") + + +def test_collection_get_item_raises_type_error() -> None: + class BasicCustomCollection(pystac.Collection): + def get_items( # type: ignore + self, *, recursive: bool = False + ) -> Iterator[Item]: + # This get_items does not allow ids as args. + return super().get_items(recursive=recursive) + + 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: + # https://github.com/stac-utils/pystac/issues/862 + class CustomCollection(Collection): + @classmethod + def from_dict( + cls, + d: dict[str, Any], + href: str | None = None, + root: Catalog | None = None, + migrate: bool = True, + preserve_dict: bool = True, + ) -> CustomCollection: + return super().from_dict(d) + + _ = CustomCollection.from_dict(collection.to_dict()) + + +@pytest.mark.parametrize("add_canonical", (True, False)) +def test_remove_hierarchical_links( + test_case_1_catalog: Catalog, add_canonical: bool +) -> None: + collection = list(test_case_1_catalog.get_all_collections())[0] + collection.remove_hierarchical_links(add_canonical=add_canonical) + for link in collection.links: + assert not link.is_hierarchical() + assert bool(collection.get_single_link("canonical")) == add_canonical + + +@pytest.mark.parametrize("child", ["country-1", "country-2"]) +def test_get_child_checks_links_where_hrefs_contains_id_first( + test_case_1_catalog: Catalog, child: str +) -> None: + cat1 = test_case_1_catalog + country = cat1.get_child(child) + assert country is not None + child_links = [link for link in cat1.links if link.rel == pystac.RelType.CHILD] + for link in child_links: + if country.id not in link.href: + assert not link.is_resolved() + + +def test_get_child_sort_links_by_id_is_configurable( + test_case_1_catalog: Catalog, +) -> None: + cat1 = test_case_1_catalog + country = cat1.get_child("country-2", sort_links_by_id=False) + assert country is not None + child_links = [link for link in cat1.links if link.rel == pystac.RelType.CHILD] + for link in child_links: + assert link.is_resolved() + + +def test_get_item_returns_none_if_not_found( + test_case_8_collection: Collection, +) -> None: + col8 = test_case_8_collection + item = col8.get_item("notarealitem") + assert item is None + + +def test_get_item_is_not_recursive_by_default( + test_case_8_collection: Collection, +) -> None: + col8 = test_case_8_collection + item = col8.get_item("20170831_162740_ssc1d1") + assert item is None + + item = col8.get_item("20170831_162740_ssc1d1", recursive=True) + assert item is not None + + +def test_delete_asset(tmp_asset: Asset, collection: Collection) -> None: + asset = tmp_asset + href = asset.get_absolute_href() + item = asset.owner + name = "foo" + + assert href is not None + assert item is not None + + collection.add_asset(name, asset) + + # steal the href from the owner and use it as the collection's + collection.set_self_href(item.get_self_href()) + + collection.delete_asset(name) + assert name not in collection.assets + assert not os.path.exists(href) + + +def test_delete_asset_relative_no_self_link_fails( + tmp_asset: Asset, collection: Collection +) -> None: + asset = tmp_asset + href = asset.get_absolute_href() + name = "foo" + + assert href is not None + + collection.add_asset(name, asset) + + with pytest.raises(ValueError, match="Cannot delete file") as e: + collection.delete_asset(name) + + assert asset.href in str(e.value) + assert name in collection.assets + assert os.path.exists(href) + + +def test_permissive_temporal_extent_deserialization(collection: Collection) -> None: + # https://github.com/stac-utils/pystac/issues/1221 + collection_dict = collection.to_dict(include_self_link=False, transform_hrefs=False) + collection_dict["extent"]["temporal"]["interval"] = collection_dict["extent"][ + "temporal" + ]["interval"][0] + Collection.from_dict(collection_dict) + + +@pytest.mark.parametrize("fixture_name", ("sample_item_collection", "sample_items")) +def test_from_items(fixture_name: str, request: pytest.FixtureRequest) -> None: + items = request.getfixturevalue(fixture_name) + collection = Collection.from_items(items) + + for item in items: + assert collection.id == item.collection_id + assert collection.extent.spatial.bboxes[0][0] <= item.bbox[0] + assert collection.extent.spatial.bboxes[0][1] <= item.bbox[1] + assert collection.extent.spatial.bboxes[0][2] >= item.bbox[2] + assert collection.extent.spatial.bboxes[0][3] >= item.bbox[3] + + start = collection.extent.temporal.intervals[0][0] + end = collection.extent.temporal.intervals[0][1] + assert start and str_to_datetime(start) <= item.properties["start_datetime"] + assert end and str_to_datetime(end) >= item.properties["end_datetime"] + + if isinstance(items, ItemCollection): + expected = {(link["rel"], link["href"]) for link in items.extra_fields["links"]} + actual = {(link.rel, link.href) for link in collection.links} + assert expected.issubset(actual) + + +def test_from_items_pulls_from_properties() -> 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), + collection="test-collection-1", + properties={"title": "Test Item", "description": "Extra words describing"}, + ) + collection = Collection.from_items([item1]) + assert collection.id == item1.collection_id + assert collection.title == item1.properties["title"] + assert collection.description == item1.properties["description"] + + +def test_from_items_without_collection_id() -> 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={}, + ) + with pytest.raises(ValueError, match="Collection id must be defined."): + Collection.from_items([item1]) + + collection = Collection.from_items([item1], id="test-collection") + assert collection.id == "test-collection" + + +def test_from_items_with_collection_ids() -> 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), + collection="test-collection-1", + properties={}, + ) + item2 = Item( + id="test-item-2", + geometry=ARBITRARY_GEOM, + bbox=[-15, -20, 0, -10], + datetime=datetime(2000, 2, 1, 13, 0, 0, 0, tzinfo=tz.UTC), + collection="test-collection-2", + properties={}, + ) + + with pytest.raises(ValueError, match="Collection id must be defined."): + Collection.from_items([item1, item2]) + + collection = Collection.from_items([item1, item2], id="test-collection") + assert collection.id == "test-collection" + + +def test_from_items_with_different_values() -> 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={"title": "Test Item 1"}, + ) + item2 = Item( + id="test-item-2", + geometry=ARBITRARY_GEOM, + bbox=[-15, -20, 0, -10], + datetime=datetime(2000, 2, 1, 13, 0, 0, 0, tzinfo=tz.UTC), + properties={"title": "Test Item 2"}, + ) + + collection = Collection.from_items([item1, item2], id="test_collection") + assert collection.title is None + + +def test_from_items_with_providers(sample_item_collection: ItemCollection) -> None: + sample_item_collection.extra_fields["providers"] = [{"name": "pystac"}] + + collection = Collection.from_items(sample_item_collection) + assert collection.providers and len(collection.providers) == 1 + + provider = collection.providers[0] + assert provider and provider.name == "pystac" + + +def test_from_dict_null_extent(collection: Collection) -> None: + # https://github.com/stac-utils/pystac/issues/1558 + # https://github.com/EOPF-Sample-Service/eopf-stac/issues/18 + d = collection.to_dict() + d["extent"] = None + with pytest.warns(UserWarning): + c = Collection.from_dict(d) + + assert c.extent.spatial.to_dict()["bbox"] == [[-180, -90, 180, 90]] + assert c.extent.temporal.to_dict()["interval"] == [[None, None]] + + +def test_from_dict_missing_extent(collection: Collection) -> None: + d = collection.to_dict() + del d["extent"] + with pytest.warns(UserWarning): + c = Collection.from_dict(d) + + assert c.extent.spatial.to_dict()["bbox"] == [[-180, -90, 180, 90]] + assert c.extent.temporal.to_dict()["interval"] == [[None, None]] diff --git a/tests/v1/test_common_metadata.py b/tests/v1/test_common_metadata.py new file mode 100644 index 000000000..74bb54f70 --- /dev/null +++ b/tests/v1/test_common_metadata.py @@ -0,0 +1,573 @@ +from datetime import datetime +from typing import Any + +import pytest + +from pystac import CommonMetadata, Item, Provider, ProviderRole, utils + +from .utils import TestCases + + +pytestmark = pytest.mark.passing_v2 + + +@pytest.fixture +def date_time_range_item() -> Item: + return Item.from_file( + TestCases.get_path( + "data-files/examples/1.0.0-beta.2/item-spec/examples/datetimerange.json" + ) + ) + + +@pytest.fixture +def sample_full_item() -> Item: + return Item.from_file( + TestCases.get_path( + "data-files/examples/1.0.0-beta.2/item-spec/examples/sample-full.json" + ) + ) + + +def test_datetimes(date_time_range_item: Item) -> None: + # save dict of original item to check that `common_metadata` + # method doesn't mutate self.item_1 + before = date_time_range_item.clone().to_dict() + start_datetime = date_time_range_item.properties["start_datetime"] + assert isinstance(start_datetime, datetime) + + cm = date_time_range_item.common_metadata + assert isinstance(cm, CommonMetadata) + assert isinstance(cm.start_datetime, datetime) + assert before == date_time_range_item.to_dict() + assert cm.providers is None + + +@pytest.mark.xfail(reason="pystac v2 purposefully converts datetimes to datetime objects") +def test_common_metadata_start_datetime(date_time_range_item: Item) -> None: + x = date_time_range_item.clone() + start_datetime_str = "2018-01-01T13:21:30Z" + start_datetime_dt = utils.str_to_datetime(start_datetime_str) + example_datetime_str = "2020-01-01T00:00:00Z" + example_datetime_dt = utils.str_to_datetime(example_datetime_str) + + assert x.common_metadata.start_datetime == start_datetime_dt + assert x.properties["start_datetime"] == start_datetime_str + + x.common_metadata.start_datetime = example_datetime_dt + + assert x.common_metadata.start_datetime == example_datetime_dt + assert x.properties["start_datetime"] == example_datetime_str + + +@pytest.mark.xfail(reason="pystac v2 purposefully converts datetimes to datetime objects") +def test_common_metadata_end_datetime(date_time_range_item: Item) -> None: + x = date_time_range_item.clone() + end_datetime_str = "2018-01-01T13:31:30Z" + end_datetime_dt = utils.str_to_datetime(end_datetime_str) + example_datetime_str = "2020-01-01T00:00:00Z" + example_datetime_dt = utils.str_to_datetime(example_datetime_str) + + assert x.common_metadata.end_datetime == end_datetime_dt + assert x.properties["end_datetime"] == end_datetime_str + + x.common_metadata.end_datetime = example_datetime_dt + + assert x.common_metadata.end_datetime == example_datetime_dt + assert x.properties["end_datetime"] == example_datetime_str + + +@pytest.mark.xfail(reason="pystac v2 purposefully converts datetimes to datetime objects") +def test_common_metadata_created(sample_full_item: Item) -> None: + x = sample_full_item.clone() + created_str = "2016-05-04T00:00:01Z" + created_dt = utils.str_to_datetime(created_str) + example_datetime_str = "2020-01-01T00:00:00Z" + example_datetime_dt = utils.str_to_datetime(example_datetime_str) + + assert x.common_metadata.created == created_dt + assert x.properties["created"] == created_str + + x.common_metadata.created = example_datetime_dt + + assert x.common_metadata.created == example_datetime_dt + assert x.properties["created"] == example_datetime_str + + +@pytest.mark.xfail(reason="pystac v2 purposefully converts datetimes to datetime objects") +def test_common_metadata_updated(sample_full_item: Item) -> None: + x = sample_full_item.clone() + updated_str = "2017-01-01T00:30:55Z" + updated_dt = utils.str_to_datetime(updated_str) + example_datetime_str = "2020-01-01T00:00:00Z" + example_datetime_dt = utils.str_to_datetime(example_datetime_str) + + assert x.common_metadata.updated == updated_dt + assert x.properties["updated"] == updated_str + + x.common_metadata.updated = example_datetime_dt + + assert x.common_metadata.updated == example_datetime_dt + assert x.properties["updated"] == example_datetime_str + + +def test_common_metadata_providers(sample_full_item: Item) -> None: + x = sample_full_item.clone() + + providers_dict_list: list[dict[str, Any]] = [ + { + "name": "CoolSat", + "roles": ["producer", "licensor"], + "url": "https://cool-sat.com/", + } + ] + providers_object_list = [Provider.from_dict(d) for d in providers_dict_list] + + example_providers_dict_list: list[dict[str, Any]] = [ + { + "name": "ExampleProvider_1", + "roles": ["example_role_1", "example_role_2"], + "url": "https://exampleprovider1.com/", + }, + { + "name": "ExampleProvider_2", + "roles": ["example_role_1", "example_role_2"], + "url": "https://exampleprovider2.com/", + }, + ] + example_providers_object_list = [ + Provider.from_dict(d) for d in example_providers_dict_list + ] + + for i in range(len(utils.get_opt(x.common_metadata.providers))): + p1 = utils.get_opt(x.common_metadata.providers)[i] + p2 = providers_object_list[i] + assert isinstance(p1, Provider) + assert isinstance(p2, Provider) + assert p1.to_dict() == p2.to_dict() + + pd1 = x.properties["providers"][i] + pd2 = providers_dict_list[i] + assert isinstance(pd1, dict) + assert isinstance(pd2, dict) + assert pd1 == pd2 + + x.common_metadata.providers = example_providers_object_list + + for i in range(len(x.common_metadata.providers)): + p1 = x.common_metadata.providers[i] + p2 = example_providers_object_list[i] + assert isinstance(p1, Provider) + assert isinstance(p2, Provider) + assert p1.to_dict() == p2.to_dict() + + pd1 = x.properties["providers"][i] + pd2 = example_providers_dict_list[i] + assert isinstance(pd1, dict) + assert isinstance(pd2, dict) + assert pd1 == pd2 + + +def test_common_metadata_basics(sample_full_item: Item) -> None: + x = sample_full_item.clone() + + # Title + title = "A CS3 item" + example_title = "example title" + assert x.common_metadata.title == title + x.common_metadata.title = example_title + assert x.common_metadata.title == example_title + assert x.properties["title"] == example_title + + # Description + example_description = "example description" + assert x.common_metadata.description is None + x.common_metadata.description = example_description + assert x.common_metadata.description == example_description + assert x.properties["description"] == example_description + with pytest.raises(ValueError): + x.common_metadata.description = "" + + # License + license = "PDDL-1.0" + example_license = "example license" + assert x.common_metadata.license == license + x.common_metadata.license = example_license + assert x.common_metadata.license == example_license + assert x.properties["license"] == example_license + + # Platform + platform = "coolsat2" + example_platform = "example_platform" + assert x.common_metadata.platform == platform + x.common_metadata.platform = example_platform + assert x.common_metadata.platform == example_platform + assert x.properties["platform"] == example_platform + + # Instruments + instruments = ["cool_sensor_v1"] + example_instruments = ["example instrument 1", "example instrument 2"] + assert (x.common_metadata.instruments or []) == instruments + x.common_metadata.instruments = example_instruments + assert x.common_metadata.instruments == example_instruments + assert x.properties["instruments"] == example_instruments + + # Constellation + example_constellation = "example constellation" + assert x.common_metadata.constellation is None + x.common_metadata.constellation = example_constellation + assert x.common_metadata.constellation == example_constellation + assert x.properties["constellation"] == example_constellation + + # Mission + example_mission = "example mission" + assert x.common_metadata.mission is None + x.common_metadata.mission = example_mission + assert x.common_metadata.mission == example_mission + assert x.properties["mission"] == example_mission + + # GSD + gsd = 0.512 + example_gsd = 0.75 + assert x.common_metadata.gsd == gsd + x.common_metadata.gsd = example_gsd + assert x.common_metadata.gsd == example_gsd + assert x.properties["gsd"] == example_gsd + + +class TestAssetCommonMetadata: + @pytest.fixture + def item(self) -> Item: + return Item.from_file( + TestCases.get_path("data-files/item/sample-item-asset-properties.json") + ) + + @pytest.mark.xfail(reason="title not part of common metadata for assets in pystac v2") + def test_title(self, item: Item) -> None: + cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = cm.title + a2_known_value = "Thumbnail" + + # Get + assert thumbnail_cm.title != item_value + assert thumbnail_cm.title == a2_known_value + + # Set + set_value = "Just Another Asset" + analytic_cm.title = set_value + + assert analytic_cm.title == set_value + assert analytic.to_dict()["title"] == set_value + + @pytest.mark.xfail(reason="description not part of common metadata for assets in pystac v2") + def test_description(self, item: Item) -> None: + cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = cm.description + a2_known_value = "Thumbnail of the item" + + # Get + assert thumbnail_cm.description != item_value + assert thumbnail_cm.description == a2_known_value + + # Set + set_value = "Yet another description." + analytic_cm.description = set_value + + assert analytic_cm.description == set_value + assert analytic.to_dict()["description"] == set_value + + def test_start_datetime(self, item: Item) -> None: + item_cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = item_cm.start_datetime + a2_known_value = utils.str_to_datetime("2017-05-01T13:22:30.040Z") + + # Get + assert thumbnail_cm.start_datetime != item_value + assert thumbnail_cm.start_datetime == a2_known_value + + # Set + set_value = utils.str_to_datetime("2014-05-01T13:22:30.040Z") + analytic_cm.start_datetime = set_value + + assert analytic_cm.start_datetime == set_value + assert analytic.to_dict()["start_datetime"] == utils.datetime_to_str(set_value) + + def test_end_datetime(self, item: Item) -> None: + cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = cm.end_datetime + a2_known_value = utils.str_to_datetime("2017-05-02T13:22:30.040Z") + + # Get + assert thumbnail_cm.end_datetime != item_value + assert thumbnail_cm.end_datetime == a2_known_value + + # Set + set_value = utils.str_to_datetime("2014-05-01T13:22:30.040Z") + analytic_cm.end_datetime = set_value + + assert analytic_cm.end_datetime == set_value + assert analytic.to_dict()["end_datetime"] == utils.datetime_to_str(set_value) + + def test_license(self, item: Item) -> None: + cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = cm.license + a2_known_value = "CC-BY-4.0" + + # Get + assert thumbnail_cm.license != item_value + assert thumbnail_cm.license == a2_known_value + + # Set + set_value = "various" + analytic_cm.license = set_value + + assert analytic_cm.license == set_value + assert analytic.to_dict()["license"] == set_value + + def test_providers(self, item: Item) -> None: + cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = cm.providers + a2_known_value = [ + Provider( + name="USGS", + url="https://landsat.usgs.gov/", + roles=[ProviderRole.PRODUCER, ProviderRole.LICENSOR], + ) + ] + + # Get + assert thumbnail_cm.providers != item_value + assert thumbnail_cm.providers == a2_known_value + + # Set + set_value = [ + Provider( + name="John Snow", + url="https://cholera.com/", + roles=[ProviderRole.PRODUCER], + ) + ] + analytic_cm.providers = set_value + + assert analytic_cm.providers == set_value + assert analytic.to_dict()["providers"] == [p.to_dict() for p in set_value] + + def test_platform(self, item: Item) -> None: + cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = cm.platform + a2_known_value = "shoes" + + # Get + assert thumbnail_cm.platform != item_value + assert thumbnail_cm.platform == a2_known_value + + # Set + set_value = "brick" + analytic_cm.platform = set_value + + assert analytic_cm.platform == set_value + assert analytic.to_dict()["platform"] == set_value + + def test_instruments(self, item: Item) -> None: + cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = cm.instruments + a2_known_value = ["caliper"] + + # Get + assert thumbnail_cm.instruments != item_value + assert thumbnail_cm.instruments == a2_known_value + + # Set + set_value = ["horns"] + analytic_cm.instruments = set_value + + assert analytic_cm.instruments == set_value + assert analytic.to_dict()["instruments"] == set_value + + def test_constellation(self, item: Item) -> None: + cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = cm.constellation + a2_known_value = "little dipper" + + # Get + assert thumbnail_cm.constellation != item_value + assert thumbnail_cm.constellation == a2_known_value + + # Set + set_value = "orion" + analytic_cm.constellation = set_value + + assert analytic_cm.constellation == set_value + assert analytic.to_dict()["constellation"] == set_value + + def test_mission(self, item: Item) -> None: + cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = cm.mission + a2_known_value = "possible" + + # Get + assert thumbnail_cm.mission != item_value + assert thumbnail_cm.mission == a2_known_value + + # Set + set_value = "critical" + analytic_cm.mission = set_value + + assert analytic_cm.mission == set_value + assert analytic.to_dict()["mission"] == set_value + + def test_gsd(self, item: Item) -> None: + cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = cm.gsd + a2_known_value = 40 + + # Get + assert thumbnail_cm.gsd != item_value + assert thumbnail_cm.gsd == a2_known_value + + # Set + set_value = 100 + analytic_cm.gsd = set_value + + assert analytic_cm.gsd == set_value + assert analytic.to_dict()["gsd"] == set_value + + def test_created(self, item: Item) -> None: + cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = cm.created + a2_known_value = utils.str_to_datetime("2017-05-17T13:22:30.040Z") + + # Get + assert thumbnail_cm.created != item_value + assert thumbnail_cm.created == a2_known_value + + # Set + set_value = utils.str_to_datetime("2014-05-17T13:22:30.040Z") + analytic_cm.created = set_value + + assert analytic_cm.created == set_value + assert analytic.to_dict()["created"] == utils.datetime_to_str(set_value) + + def test_updated(self, item: Item) -> None: + cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = cm.updated + a2_known_value = utils.str_to_datetime("2017-05-18T13:22:30.040Z") + + # Get + assert thumbnail_cm.updated != item_value + assert thumbnail_cm.updated == a2_known_value + + # Set + set_value = utils.str_to_datetime("2014-05-18T13:22:30.040Z") + analytic_cm.updated = set_value + + assert analytic_cm.updated == set_value + assert analytic.to_dict()["updated"] == utils.datetime_to_str(set_value) + + def test_keywords(self, item: Item) -> None: + cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = cm.keywords + a2_known_value = ["keyword_a"] + + # Get + assert thumbnail_cm.keywords != item_value + assert thumbnail_cm.keywords == a2_known_value + + # Set + set_value = ["keyword_b"] + analytic_cm.keywords = set_value + + assert analytic_cm.keywords == set_value + assert analytic.to_dict()["keywords"] == set_value + + @pytest.mark.xfail(reason="roles not part of common metadata for assets in pystac v2") + def test_roles(self, item: Item) -> None: + cm = item.common_metadata + analytic = item.assets["analytic"] + analytic_cm = CommonMetadata(analytic) + thumbnail = item.assets["thumbnail"] + thumbnail_cm = CommonMetadata(thumbnail) + + item_value = cm.roles + a2_known_value = ["a_role"] + + # Get + assert thumbnail_cm.roles != item_value + assert thumbnail_cm.roles == a2_known_value + + # Set + set_value = ["another_role"] + analytic_cm.roles = set_value + + assert analytic_cm.roles == set_value + assert analytic.to_dict()["roles"] == set_value diff --git a/tests/v1/test_item.py b/tests/v1/test_item.py new file mode 100644 index 000000000..bf48d854b --- /dev/null +++ b/tests/v1/test_item.py @@ -0,0 +1,710 @@ +from __future__ import annotations + +import copy +import json +import os +import pickle +import tempfile +from copy import deepcopy +from pathlib import Path +from typing import Any, cast + +import dateutil.relativedelta +import pytest + +import pystac +import pystac.serialization.common_properties +from pystac import Asset, Catalog, Collection, Item, Link, STACValidationError +from pystac.utils import ( + datetime_to_str, + get_opt, + is_absolute_href, + make_posix_style, + str_to_datetime, +) +from pystac.validation import validate_dict + +from .utils import TestCases, assert_to_from_dict + + +pytestmark = pytest.mark.passing_v2 + + +def test_to_from_dict(sample_item_dict: dict[str, Any]) -> None: + param_dict = deepcopy(sample_item_dict) + + assert_to_from_dict(Item, param_dict) + item = Item.from_dict(param_dict) + assert item.id == "CS3-20160503_132131_05" + + # 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 + + +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 + + rel_asset = Asset("./data.geojson") + rel_asset.set_owner(item) + actual_href = rel_asset.get_absolute_href() + assert actual_href is None + + +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 + + +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 + + +@pytest.mark.vcr() +@pytest.mark.xfail(reason="Null datetimes are ok in pystac v2") +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={}, + ) + + 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() -> 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 == {} + + +@pytest.mark.vcr() +@pytest.mark.xfail(reason="Null datetimes are ok in pystac v2") +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 pytest.raises(pystac.STACError): + Item( + "test", + geometry=item.geometry, + bbox=item.bbox, + datetime=None, + start_datetime=item.datetime, + properties={}, + ) + 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() -> 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 + assert item.get_datetime(item.assets["thumbnail"]) == item.datetime + + # 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" + ) + + 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" + ) + + +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 + + +@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) + + validate_dict(item_dict, pystac.STACObjectType.ITEM) + + item = Item.from_dict(item_dict) + assert isinstance(item, Item) + item.validate() + + item_dict = item.to_dict() + assert item_dict["geometry"] is None + assert "bbox" not in item_dict + + +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" + + did_merge = pystac.serialization.common_properties.merge_common_properties( + item_json + ) + assert not did_merge + + +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) + + +@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() + + +class TestItemSubClass: + """This tests cases related to creating classes inheriting from pystac.Catalog to + ensure that inheritance, class methods, etc. function as expected.""" + + SAMPLE_ITEM = TestCases.get_path("data-files/item/sample-item.json") + + class BasicCustomItem(pystac.Item): + pass + + def test_from_dict_returns_subclass(self) -> None: + stac_io = pystac.StacIO.default() + item_dict = stac_io.read_json(self.SAMPLE_ITEM) + custom_item = self.BasicCustomItem.from_dict(item_dict) + + assert isinstance(custom_item, self.BasicCustomItem) + + def test_from_file_returns_subclass(self) -> None: + custom_item = self.BasicCustomItem.from_file(self.SAMPLE_ITEM) + + 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() + + assert isinstance(cloned_item, self.BasicCustomItem) + + +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) + + cloned_asset = original_asset.clone() + + assert original_asset.to_dict() == asset_dict + assert cloned_asset.to_dict() == 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 + + +class TestAssetSubClass: + class CustomAsset(Asset): + pass + + 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"]) + + 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, asset_dict: AssetDict) -> None: + asset = self.CustomAsset.from_dict(asset_dict) + cloned_asset = asset.clone() + assert isinstance(cloned_asset, self.CustomAsset) + + +def test_custom_item_from_dict(item: Item) -> None: + # https://github.com/stac-utils/pystac/issues/862 + class CustomItem(Item): + @classmethod + def from_dict( + cls, + d: dict[str, Any], + href: str | None = None, + root: Catalog | None = None, + migrate: bool = True, + preserve_dict: bool = True, + ) -> CustomItem: + return super().from_dict(d) + + _ = CustomItem.from_dict(item.to_dict()) + + +@pytest.mark.xfail(reason="pystac v2 is more permissive on input") +def test_item_from_dict_raises_useful_error() -> None: + item_dict = {"type": "Feature", "stac_version": "1.1.0", "id": "lalalalala"} + with pytest.raises(pystac.STACError, match="Invalid Item: "): + Item.from_dict(item_dict) + + +@pytest.mark.xfail(reason="pystac v2 is more permissive on input") +def test_item_from_dict_with_missing_stac_version_raises_useful_error() -> None: + item_dict = {"type": "Feature", "id": "lalalalala"} + with pytest.raises(pystac.STACTypeError, match="'stac_version' is missing"): + Item.from_dict(item_dict, migrate=False) + + +@pytest.mark.xfail(reason="pystac v2 is more permissive on input") +def test_item_from_dict_with_missing_type_raises_useful_error() -> None: + item_dict = {"stac_version": "0.8.0", "id": "lalalalala"} + with pytest.raises(pystac.STACTypeError, match="'type' is missing"): + Item.from_dict(item_dict, migrate=False) + + +@pytest.mark.parametrize("add_canonical", (True, False)) +def test_remove_hierarchical_links( + test_case_1_catalog: Catalog, add_canonical: bool +) -> None: + item = next(test_case_1_catalog.get_items(recursive=True)) + item.remove_hierarchical_links(add_canonical=add_canonical) + for link in item.links: + assert not link.is_hierarchical() + assert bool(item.get_single_link("canonical")) == add_canonical + + +def test_geo_interface() -> None: + item = pystac.Item.from_file(TestCases.get_path("data-files/item/sample-item.json")) + assert ( + item.to_dict(include_self_link=False, transform_hrefs=False) + == item.__geo_interface__ + ) + + +def test_duplicate_self_links(tmp_path: Path, sample_item: pystac.Item) -> None: + # https://github.com/stac-utils/pystac/issues/1102 + sample_item.links.append( + Link( + rel=pystac.RelType.SELF, + href=sample_item.get_self_href(), + ) + ) + assert len(sample_item.get_links(rel="self")) == 1 + path = tmp_path / "item.json" + sample_item.save_object(include_self_link=True, dest_href=str(path)) + sample_item = Item.from_file(path) + assert len(sample_item.get_links(rel="self")) == 1 + + +def test_get_derived_from_when_none_exists(test_case_1_catalog: Catalog) -> None: + item = next(test_case_1_catalog.get_items(recursive=True)) + assert item.get_derived_from() == [] + for link in item.links: + assert link.rel != pystac.RelType.DERIVED_FROM + assert item.get_single_link(pystac.RelType.DERIVED_FROM) is None + + +def test_add_derived_from(test_case_1_catalog: Catalog) -> None: + items = list(test_case_1_catalog.get_items(recursive=True)) + item_0 = items[0] + item_1 = items[1] + item_2 = items[2] + item_0.add_derived_from(item_1, item_2.self_href) + derived_from = item_0.get_derived_from() + assert len(derived_from) == 2 + assert derived_from[0].id == item_1.id + assert derived_from[1].id == item_2.id + filtered = [ + link for link in item_0.links if link.rel == pystac.RelType.DERIVED_FROM + ] + assert len(filtered) == 2 + assert filtered[0].to_dict(transform_href=False)["href"] == item_1.self_href + assert filtered[1].to_dict(transform_href=False)["href"] == item_2.self_href + + +def test_get_unresolvable_derived_from(test_case_1_catalog: Catalog) -> None: + item = next(test_case_1_catalog.get_items(recursive=True)) + item.add_derived_from("foo") + with pytest.raises( + pystac.STACError # , match="Link failed to resolve. Use get_links instead" + ): + item.get_derived_from() + + links = item.get_links(pystac.RelType.DERIVED_FROM) + assert len(links) == 1 + + +def test_remove_unresolvable_derived_from(test_case_1_catalog: Catalog) -> None: + item = next(test_case_1_catalog.get_items(recursive=True)) + item.add_derived_from("foo") + with pytest.raises( + pystac.STACError # , match="Link failed to resolve. Use remove_links instead" + ): + item.remove_derived_from("foo") + + item.remove_links(pystac.RelType.DERIVED_FROM) + assert item.get_derived_from() == [] + + +def test_remove_derived_from(test_case_1_catalog: Catalog) -> None: + items = list(test_case_1_catalog.get_items(recursive=True)) + item_0 = items[0] + item_1 = items[1] + item_0.add_derived_from(item_1) + item_0.remove_derived_from(item_1.id) + assert item_0.get_derived_from() == [] + for link in item_0.links: + assert link.rel != pystac.RelType.DERIVED_FROM + assert item_0.get_single_link(pystac.RelType.DERIVED_FROM) is None + + +def test_delete_asset(tmp_asset: Asset) -> None: + asset = tmp_asset + href = asset.get_absolute_href() + item = asset.owner + + assert href is not None + assert item is not None + + name = next(k for k in item.assets.keys() if item.assets[k] == asset) + item.delete_asset(name) + + assert name not in item.assets + assert not os.path.exists(href) + + +def test_delete_asset_relative_no_self_link_fails(tmp_asset: pystac.Asset) -> None: + asset = tmp_asset + href = asset.get_absolute_href() + item = asset.owner + + assert href is not None + assert item is not None + assert isinstance(item, pystac.Item) + + item.set_self_href(None) + + name = next(k for k in item.assets.keys() if item.assets[k] == asset) + with pytest.raises(ValueError, match="Cannot delete file") as e: + item.delete_asset(name) + + assert asset.href in str(e.value) + assert name in item.assets + assert os.path.exists(href) + + +def test_resolve_collection_with_root( + tmp_path: Path, item: Item, collection: Collection +) -> None: + # Motivated by https://github.com/stac-utils/pystac-client/issues/548 + catalog = Catalog("root", "the description") + item.set_root(catalog) + + collection_path = str(tmp_path / "collection.json") + collection.save_object( + include_self_link=False, + dest_href=collection_path, + ) + item.add_link(Link(rel="collection", target=collection_path)) + + read_collection = item.get_collection() + assert read_collection + root = read_collection.get_root() + assert root + assert root.id == "root" + + +@pytest.mark.xfail(reason="Setting the catalog type doesn't change behavior") +def test_non_hierarchical_relative_link() -> None: + root = pystac.Catalog("root", "root") + a = pystac.Catalog("a", "a") + b = pystac.Catalog("b", "b") + + root.add_child(a) + root.add_child(b) + a.add_link(pystac.Link("related", b)) + b.add_link( + pystac.Link("item", TestCases.get_path("data-files/item/sample-item.json")) + ) + + root.catalog_type = pystac.catalog.CatalogType.SELF_CONTAINED + root.normalize_hrefs("test_output") + related_href = [link for link in a.links if link.rel == "related"][0].get_href() + + assert related_href is not None and not is_absolute_href(related_href) + assert a.target_in_hierarchy(b) + assert root.target_in_hierarchy(next(b.get_items())) + assert root.target_in_hierarchy(root) + + +def test_pathlib() -> None: + # This works, but breaks mypy until we fix + # https://github.com/stac-utils/pystac/issues/1216 + Item.from_file(Path(TestCases.get_path("data-files/item/sample-item.json"))) + + +def test_invalid_error_message(item: Item) -> None: + item.extra_fields["collection"] = "can't have a collection" + with pytest.raises(STACValidationError) as error: + item.validate() + assert "can't have a collection" in str(error.value) + + +def test_pickle_with_no_links(item: Item) -> None: + roundtripped = pickle.loads(pickle.dumps(item)) + for attr in ["id", "geometry", "bbox", "datetime", "links"]: + assert getattr(roundtripped, attr) == getattr(item, attr) + + +def test_pickle_with_hrefless_links(item: Item) -> None: + root = pystac.Catalog("root", "root") + a = pystac.Catalog("a", "a") + + item.add_link(pystac.Link("related", a)) + item.add_link( + pystac.Link("item", TestCases.get_path("data-files/item/sample-item.json")) + ) + item.set_root(root) + + roundtripped = pickle.loads(pickle.dumps(item)) + for original, new in zip(item.links, roundtripped.links): + assert original.rel == new.rel + assert original.media_type == new.media_type + assert str(original.owner) == str(new.owner) + assert str(original.target) == str(new.target) + + +def test_pickle_with_only_href_links(item: Item) -> None: + item.add_link( + pystac.Link("item", TestCases.get_path("data-files/item/sample-item.json")) + ) + + roundtripped = pickle.loads(pickle.dumps(item)) + for original, new in zip(item.links, roundtripped.links): + assert original.rel == new.rel + assert original.media_type == new.media_type + assert str(original.owner) == str(new.owner) + assert str(original.target) == str(new.target) + + +def test_copy_with_unresolveable_root(item: Item) -> None: + item.add_link( + pystac.Link( + "root", "s3://naip-visualization/this-is-a-non-existent-catalog.json" + ) + ) + copy.deepcopy(item) + + +def test_no_collection(item: Item) -> None: + # https://github.com/stac-utils/stac-api-validator/issues/527 + assert item.collection is None + + +@pytest.mark.xfail(reason=".ext is not supported for pystac v2") +def test_migrate_by_default() -> None: + with open( + TestCases.get_path("data-files/projection/example-with-version-1.1.json") + ) as f: + data = json.load(f) + item = pystac.Item.from_dict(data) # default used to be migrate=False + assert item.ext.proj.code == "EPSG:32614" + + +def test_clone_extra_fields(item: Item) -> None: + item.extra_fields["foo"] = "bar" + cloned = item.clone() + assert cloned.extra_fields["foo"] == "bar" diff --git a/tests/v1/test_item_assets.py b/tests/v1/test_item_assets.py new file mode 100644 index 000000000..425302b53 --- /dev/null +++ b/tests/v1/test_item_assets.py @@ -0,0 +1,233 @@ +import pytest + +from pystac import Collection +from pystac.errors import DeprecatedWarning +from pystac.extensions.item_assets import AssetDefinition, ItemAssetsExtension +from pystac.item_assets import ItemAssetDefinition + +from .utils import TestCases + + +pytestmark = pytest.mark.passing_v2 + + +CLASSIFICATION_COLLECTION_RASTER_URI = TestCases.get_path( + "data-files/classification/collection-item-assets-raster-bands.json" +) + + +@pytest.fixture +def landsat8_collection() -> Collection: + return Collection.from_file( + TestCases.get_path("data-files/item-assets/example-landsat8.json") + ) + + +def test_example(landsat8_collection: Collection) -> None: + assert len(landsat8_collection.item_assets) == 13 + + assert landsat8_collection.item_assets["B1"].to_dict() == 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", + } + ).to_dict() + + +@pytest.mark.xfail(reason="setting item_assets using a dict is no longer supported") +def test_set_using_dict(landsat8_collection: Collection) -> None: + assert len(landsat8_collection.item_assets) == 13 + + 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"] + ) + + +class TestAssetDefinition: + @pytest.mark.xfail(reason="Item assets don't pretend to be dict in pystac v2") + 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)" + description = "Coastal Band Top Of the Atmosphere" + media_type = "image/tiff; application=geotiff" + roles = ["data"] + asset_defn = ItemAssetDefinition.create( + title=title, description=description, media_type=media_type, 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({}) + title = "Very Important Asset" + + asset_defn.title = title + + assert asset_defn.title == asset_defn.to_dict()["title"] == title + + def test_description(self) -> None: + asset_defn = ItemAssetDefinition({}) + description = "What an incredibly important asset this is!" + + asset_defn.description = description + + assert ( + asset_defn.description == asset_defn.to_dict()["description"] == description + ) + + def test_media_type(self) -> None: + asset_defn = ItemAssetDefinition({}) + media_type = "application/json" + + asset_defn.media_type = media_type + + assert asset_defn.media_type == asset_defn.to_dict()["type"] == media_type + + def test_roles(self) -> None: + asset_defn = ItemAssetDefinition({}) + roles = ["thumbnail"] + + asset_defn.roles = roles + + assert asset_defn.roles == asset_defn.to_dict()["roles"] == roles + + def test_set_owner(self, landsat8_collection: Collection) -> None: + asset_definition = 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", + } + ) + asset_definition.set_owner(landsat8_collection) + assert asset_definition.owner == landsat8_collection + + +@pytest.mark.xfail(reason="ItemAssets have no owner in pystac v2") +def test_extra_fields(collection: Collection) -> None: + asset_definition = ItemAssetDefinition.create( + title=None, + description=None, + media_type=None, + roles=None, + extra_fields={"raster:bands": [{"nodata": 42}]}, + ) + + collection.item_assets = {"data": asset_definition} + assert collection.item_assets["data"].owner == collection + + collection_as_dict = collection.to_dict() + assert collection_as_dict["item_assets"]["data"]["raster:bands"] == [{"nodata": 42}] + asset = asset_definition.create_asset("asset.tif") + assert asset.extra_fields["raster:bands"] == [{"nodata": 42}] + + collection.item_assets["data"].ext.add("raster") + assert (bands := collection.item_assets["data"].ext.raster.bands) + assert bands[0].nodata == 42 + + assert collection.item_assets["data"].ext.has("raster") + assert collection.ext.has("raster") + + +@pytest.mark.xfail(reason="ItemAssets have no owner in pystac v2") +def test_set_item_asset(collection: Collection) -> None: + asset_definition = ItemAssetDefinition.create( + title=None, + description=None, + media_type=None, + roles=None, + extra_fields={"raster:bands": [{"nodata": 42}]}, + ) + + collection.item_assets["data"] = asset_definition + assert collection.item_assets["data"].owner == collection + + +@pytest.mark.xfail(reason="ItemAssets Extension deprecation cycle is complete") +def test_item_assets_extension_is_deprecated() -> None: + collection = Collection.from_file(CLASSIFICATION_COLLECTION_RASTER_URI) + + assert ItemAssetsExtension.get_schema_uri() not in collection.stac_extensions + + with pytest.warns(DeprecatedWarning, match="top-level property of"): + item_asset_ext = ItemAssetsExtension.ext(collection, add_if_missing=True) + item_asset = item_asset_ext.item_assets["cloud-mask-raster"] + + assert collection.id in repr(item_asset_ext) + + assert item_asset.ext.has("eo") + + with pytest.warns(DeprecatedWarning, match="top-level property of"): + assert collection.ext.item_assets["cloud-mask-raster"].ext.has("eo") + + assert ItemAssetsExtension.get_schema_uri() in collection.stac_extensions + + with pytest.warns(DeprecationWarning): + asset_definition = AssetDefinition( + {"title": "Thumbnail image", "type": "image/jpeg"} + ) + item_asset_ext.item_assets["thumbnail"] = asset_definition + + +@pytest.mark.xfail(reason="ItemAssets Extension deprecation cycle is complete") +def test_item_assets_extension_asset_definition_is_deprecated() -> None: + with pytest.warns( + DeprecationWarning, match="Please use ``pystac.ItemAssetDefinition``" + ): + asset_definition = AssetDefinition( + { + "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", + } + ) + + assert asset_definition.title == "Coastal Band (B1)" + assert asset_definition.ext.eo.bands + assert asset_definition.ext.eo.bands[0].name == "B1" + assert asset_definition.owner is None diff --git a/tests/test_item_collection.py b/tests/v1/test_item_collection.py similarity index 91% rename from tests/test_item_collection.py rename to tests/v1/test_item_collection.py index e4c0758ee..4b4c73bcb 100644 --- a/tests/test_item_collection.py +++ b/tests/v1/test_item_collection.py @@ -4,12 +4,17 @@ from typing import Any, cast import pytest +from unittest.mock import Mock 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 +from pystac.reader import DEFAULT_READER + +from .utils import TestCases + + +pytestmark = pytest.mark.passing_v2 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") @@ -19,6 +24,16 @@ "data-files/item-collection/sample-item-collection.json" ) +class MockReader: + mock: Mock + + def __init__(self) -> None: + self.mock = Mock() + + def get_json(self, href: str) -> dict[str, Any]: + self.mock.read_json(href) + return DEFAULT_READER.get_json(href) + @pytest.fixture def item_collection_dict() -> dict[str, Any]: @@ -198,9 +213,8 @@ def test_from_dict_sets_root(item_collection_dict: dict[str, Any]) -> None: 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() + mock_reader = MockReader() + item_collection = ItemCollection.from_file(ITEM_COLLECTION, reader=mock_reader) + item_collection.to_dict() - assert mock_stac_io.mock.read_text.call_count == 1 + assert mock_reader.mock.read_json.call_count == 1 diff --git a/tests/test_layout.py b/tests/v1/test_layout.py similarity index 98% rename from tests/test_layout.py rename to tests/v1/test_layout.py index b92d9dee7..7f3544c45 100644 --- a/tests/test_layout.py +++ b/tests/v1/test_layout.py @@ -14,7 +14,8 @@ LayoutTemplate, TemplateLayoutStrategy, ) -from tests.utils import ( + +from .utils import ( ARBITRARY_BBOX, ARBITRARY_GEOM, TestCases, @@ -311,8 +312,9 @@ def test_produces_layout_for_collection(self) -> None: ) collection = self._get_collection() href = strategy.get_href(collection, parent_dir="http://example.com") - assert href == "http://example.com/col/{}/{}/collection.json".format( - collection.id, collection.license + assert ( + href + == f"http://example.com/col/{collection.id}/{collection.license}/collection.json" ) def test_produces_layout_for_collection_with_filename(self) -> None: @@ -320,8 +322,9 @@ def test_produces_layout_for_collection_with_filename(self) -> None: strategy = TemplateLayoutStrategy(collection_template=template) collection = self._get_collection() href = strategy.get_href(collection, parent_dir="http://example.com") - assert href == "http://example.com/col/{}/{}/col.json".format( - collection.id, collection.license + assert ( + href + == f"http://example.com/col/{collection.id}/{collection.license}/col.json" ) def test_produces_fallback_layout_for_collection(self) -> None: diff --git a/tests/test_link.py b/tests/v1/test_link.py similarity index 75% rename from tests/test_link.py rename to tests/v1/test_link.py index 4379d1f80..c735b2763 100644 --- a/tests/test_link.py +++ b/tests/v1/test_link.py @@ -1,9 +1,10 @@ import json import os +import re from datetime import datetime from pathlib import Path from tempfile import TemporaryDirectory -from typing import Any, cast +from typing import Any import pytest @@ -12,7 +13,10 @@ from pystac.errors import STACError from pystac.link import HIERARCHICAL_LINKS from pystac.utils import make_posix_style -from tests.utils.test_cases import ARBITRARY_EXTENT + +from .utils.test_cases import ARBITRARY_EXTENT + +pytestmark = pytest.mark.passing_v2 TEST_DATETIME: datetime = datetime(2020, 3, 14, 16, 32) @@ -31,7 +35,7 @@ def test_minimal(item: pystac.Item) -> None: assert target == link.get_href() assert target == link.get_absolute_href() - expected_repr = f"" + expected_repr = f"Link(rel={rel}, href={target})" assert expected_repr == link.__repr__() assert not link.is_resolved() @@ -50,20 +54,14 @@ def test_minimal(item: pystac.Item) -> None: assert expected_dict == clone.to_dict() - # Try the modification methods. - assert link.owner is None - link.set_owner(None) - assert link.owner is None - - link.set_owner(item) - assert item == link.owner - def test_relative() -> None: rel = "my rel" target = "../elsewhere" mime_type = "example/stac_thing" - link = pystac.Link(rel, target, mime_type, "a title", extra_fields={"a": "b"}) + link = pystac.Link( + rel, target, media_type=mime_type, title="a title", extra_fields={"a": "b"} + ) expected_dict = { "rel": rel, "href": target, @@ -114,34 +112,45 @@ def test_resolved_self_href() -> None: def test_target_getter_setter(item: pystac.Item) -> None: link = pystac.Link("my rel", target="./foo/bar.json") assert link.target == "./foo/bar.json" - assert link.get_target_str() == "./foo/bar.json" + assert link.get_href() == "./foo/bar.json" + item.set_self_href("file:///bad/beef.json") link.target = item assert link.target == item - assert link.get_target_str() == item.get_self_href() - - link.target = "./bar/foo.json" - assert link.target == "./bar/foo.json" + # get_target_str is gone, so replaced with get_href + assert link.get_href() == item.get_self_href() + + # target property + link.set_href("./dead/d011.json") + assert link.target == item # target remains unchanged + assert link.get_href() == "./dead/d011.json" # href is updated + + expected_pattern_ltsh = r"file://([A-Z]:)?/bad/beef.json" + actual_ltsh = link.target.get_self_href() + # windows compatible absolute url regular expression. + # set_href doesn't update target, but self_href is mutated to be absolute. + assert actual_ltsh and re.match(expected_pattern_ltsh, actual_ltsh), ( + f"{actual_ltsh} does not match {expected_pattern_ltsh}" + ) def test_get_target_str_no_href(item: pystac.Item) -> None: item.remove_links("self") link = pystac.Link("self", target=item) item.add_link(link) - assert link.get_target_str() is None + assert link.get_href() is None def test_relative_self_href(item: pystac.Item) -> None: with TemporaryDirectory() as temporary_directory: - pystac.write_file( - item, + item.save_object( include_self_link=False, dest_href=os.path.join(temporary_directory, "item.json"), ) previous = os.getcwd() try: os.chdir(temporary_directory) - item = cast(pystac.Item, pystac.read_file("item.json")) + item = pystac.read_file("item.json") href = item.get_self_href() assert href assert os.path.isabs(href), f"Not an absolute path: {href}" @@ -182,8 +191,9 @@ def test_auto_title_is_serialized(item: pystac.Item) -> None: extent=extent, title="Collection Title", ) + collection.set_self_href("file:///dev/null") + ## link.to_dict needs to extract href from `target` or have it passed in link = pystac.Link("my rel", target=collection) - assert link.to_dict().get("title") == collection.title @@ -202,7 +212,14 @@ def test_title_as_init_argument(item: pystac.Item) -> None: extent=extent, title="Collection Title", ) - link = pystac.Link("my rel", title=link_title, target=collection) + + ## link.to_dict needs to extract href from `target` or have it passed in + link = pystac.Link( + "my rel", + title=link_title, + target=collection, + href="file:///dev/null", + ) assert link.title == link_title assert link.to_dict().get("title") == link_title @@ -211,7 +228,14 @@ def test_title_as_init_argument(item: pystac.Item) -> None: def test_serialize_link() -> None: href = "https://some-domain/path/to/item.json" title = "A Test Link" - link = pystac.Link(pystac.RelType.SELF, href, pystac.MediaType.JSON, title) + # Positional is lame + # link = Link(RelType.SELF, None, href, MediaType.JSON, title) + link = pystac.Link( + rel=pystac.RelType.SELF, + href=href, + media_type=pystac.MediaType.JSON, + title=title, + ) link_dict = link.to_dict() assert link_dict["rel"] == "self" @@ -222,7 +246,6 @@ def test_serialize_link() -> None: def test_static_from_dict_round_trip() -> None: test_cases: list[dict[str, Any]] = [ - {"rel": "", "href": ""}, # Not valid, but works. {"rel": "r", "href": "t"}, {"rel": "r", "href": "/t"}, {"rel": "r", "href": "t", "type": "a/b", "title": "t", "c": "d", "1": 2}, @@ -231,38 +254,55 @@ def test_static_from_dict_round_trip() -> None: d2 = pystac.Link.from_dict(d).to_dict() assert d == d2 d = {"rel": "self", "href": "t"} - d2 = {"rel": "self", "href": make_posix_style(os.path.join(os.getcwd(), "t"))} - assert pystac.Link.from_dict(d).to_dict() == d2 + d2 = {"rel": "self", "href": "t"} + assert Link.from_dict(d).to_dict() == d2 def test_static_from_dict_failures() -> None: dicts: list[dict[str, Any]] = [{}, {"href": "t"}, {"rel": "r"}] for d in dicts: - with pytest.raises(KeyError): - pystac.Link.from_dict(d) + with pytest.raises((TypeError, ValueError)): + Link.from_dict(d) -def test_static_collection(collection: pystac.Collection) -> None: - link = pystac.Link.collection(collection) - expected = {"rel": "collection", "href": None, "type": "application/json"} +def test_static_collection(self_link_collection: Collection) -> None: + link = pystac.Link.collection(self_link_collection) + expected = { + "rel": "collection", + "href": self_link_collection.get_self_href(), + "type": "application/json", + } assert expected == link.to_dict() -def test_static_child(collection: pystac.Collection) -> None: - link = pystac.Link.child(collection) - expected = {"rel": "child", "href": None, "type": "application/json"} +def test_static_child(self_link_collection: Collection) -> None: + link = pystac.Link.child(self_link_collection) + expected = { + "rel": "child", + "href": self_link_collection.get_self_href(), + "type": "application/json", + } assert expected == link.to_dict() def test_static_canonical_item(item: pystac.Item) -> None: + item.set_self_href("file:///bad/beef.json") link = pystac.Link.canonical(item) - expected = {"rel": "canonical", "href": None, "type": "application/json"} + expected = { + "rel": "canonical", + "href": item.get_self_href(), + "type": "application/json", + } assert expected == link.to_dict() -def test_static_canonical_collection(collection: pystac.Collection) -> None: - link = pystac.Link.canonical(collection) - expected = {"rel": "canonical", "href": None, "type": "application/json"} +def test_static_canonical_collection(self_link_collection: Collection) -> None: + link = pystac.Link.canonical(self_link_collection) + expected = { + "rel": "canonical", + "href": self_link_collection.get_self_href(), + "type": "application/json", + } assert expected == link.to_dict() diff --git a/tests/test_pystac_client.py b/tests/v1/test_pystac_client.py similarity index 100% rename from tests/test_pystac_client.py rename to tests/v1/test_pystac_client.py diff --git a/tests/test_stac_io.py b/tests/v1/test_stac_io.py similarity index 99% rename from tests/test_stac_io.py rename to tests/v1/test_stac_io.py index 7a826feb9..f7a201fa7 100644 --- a/tests/test_stac_io.py +++ b/tests/v1/test_stac_io.py @@ -9,7 +9,8 @@ import pystac import pystac.errors from pystac.stac_io import DefaultStacIO, DuplicateKeyReportingMixin, StacIO -from tests.utils import TestCases + +from .utils import TestCases def test_read_write_collection() -> None: diff --git a/tests/test_summaries.py b/tests/v1/test_summaries.py similarity index 97% rename from tests/test_summaries.py rename to tests/v1/test_summaries.py index 90b8704f1..8f57664a3 100644 --- a/tests/test_summaries.py +++ b/tests/v1/test_summaries.py @@ -4,7 +4,11 @@ import pytest from pystac.summaries import RangeSummary, Summaries, Summarizer, SummaryStrategy -from tests.utils import TestCases + +from .utils import TestCases + + +pytestmark = pytest.mark.passing_v2 def test_summary() -> None: diff --git a/tests/test_utils.py b/tests/v1/test_utils.py similarity index 98% rename from tests/test_utils.py rename to tests/v1/test_utils.py index 51a8dcb70..f9ecf4a37 100644 --- a/tests/test_utils.py +++ b/tests/v1/test_utils.py @@ -1,7 +1,7 @@ import json import os import time -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta, timezone import pytest from dateutil import tz @@ -20,7 +20,8 @@ safe_urlparse, str_to_datetime, ) -from tests.utils import TestCases, path_includes_drive_letter + +from .utils import TestCases, path_includes_drive_letter @pytest.mark.parametrize( @@ -261,7 +262,7 @@ def test_datetime_to_str() -> None: ), ( "timezone aware, utc", - datetime(2000, 1, 1, tzinfo=timezone.utc), + datetime(2000, 1, 1, tzinfo=UTC), "2000-01-01T00:00:00Z", ), ( @@ -285,7 +286,7 @@ def test_datetime_to_str_with_microseconds_timespec() -> None: ), ( "timezone aware, utc", - datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=timezone.utc), + datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=UTC), "2000-01-01T00:00:00.000000Z", ), ( @@ -417,7 +418,7 @@ def test_now_functions() -> None: now2 = now_in_utc() assert now1 < now2 - assert now1.tzinfo == timezone.utc + assert now1.tzinfo == UTC assert str_to_datetime(now_to_rfc3339_str()) diff --git a/tests/test_version.py b/tests/v1/test_version.py similarity index 96% rename from tests/test_version.py rename to tests/v1/test_version.py index 58b0e6cd2..cb15f0371 100644 --- a/tests/test_version.py +++ b/tests/v1/test_version.py @@ -5,7 +5,8 @@ import pytest import pystac -from tests.utils import TestCases + +from .utils import TestCases def test_override_stac_version_with_environ() -> None: diff --git a/tests/test_writing.py b/tests/v1/test_writing.py similarity index 99% rename from tests/test_writing.py rename to tests/v1/test_writing.py index 90a7b4f72..05d01aee3 100644 --- a/tests/test_writing.py +++ b/tests/v1/test_writing.py @@ -7,7 +7,8 @@ from pystac import HIERARCHICAL_LINKS, Catalog, CatalogType, Collection from pystac.utils import is_absolute_href, make_absolute_href from pystac.validation import validate_dict -from tests.utils import TestCases + +from .utils import TestCases CTYPES = [ CatalogType.ABSOLUTE_PUBLISHED, diff --git a/tests/utils/__init__.py b/tests/v1/utils/__init__.py similarity index 88% rename from tests/utils/__init__.py rename to tests/v1/utils/__init__.py index 03e5fbfd6..8d62846f9 100644 --- a/tests/utils/__init__.py +++ b/tests/v1/utils/__init__.py @@ -13,9 +13,10 @@ from dateutil.parser import parse import pystac -from tests.utils.os_utils import path_includes_drive_letter -from tests.utils.stac_io_mock import MockStacIO -from tests.utils.test_cases import ( + +from .os_utils import path_includes_drive_letter +from .stac_io_mock import MockStacIO +from .test_cases import ( ARBITRARY_BBOX, ARBITRARY_EXTENT, ARBITRARY_GEOM, diff --git a/tests/utils/os_utils.py b/tests/v1/utils/os_utils.py similarity index 100% rename from tests/utils/os_utils.py rename to tests/v1/utils/os_utils.py diff --git a/tests/utils/stac_io_mock.py b/tests/v1/utils/stac_io_mock.py similarity index 100% rename from tests/utils/stac_io_mock.py rename to tests/v1/utils/stac_io_mock.py diff --git a/tests/utils/test_cases.py b/tests/v1/utils/test_cases.py similarity index 96% rename from tests/utils/test_cases.py rename to tests/v1/utils/test_cases.py index 97184d836..f2bc08ad2 100644 --- a/tests/utils/test_cases.py +++ b/tests/v1/utils/test_cases.py @@ -1,6 +1,6 @@ import csv import os -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import Any import pystac @@ -63,7 +63,7 @@ ] ARBITRARY_EXTENT = Extent( - spatial=SpatialExtent.from_coordinates(ARBITRARY_GEOM["coordinates"]), + spatial=SpatialExtent(ARBITRARY_BBOX), temporal=TemporalExtent.from_now(), ) @@ -156,7 +156,7 @@ def case_3() -> Catalog: id="imagery-item", geometry=ARBITRARY_GEOM, bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), + datetime=datetime.now(UTC), properties={}, ) @@ -168,7 +168,7 @@ def case_3() -> Catalog: id="label-items", geometry=ARBITRARY_GEOM, bbox=ARBITRARY_BBOX, - datetime=datetime.now(timezone.utc), + datetime=datetime.now(UTC), properties={}, ) diff --git a/tests/validation/__init__.py b/tests/v1/validation/__init__.py similarity index 100% rename from tests/validation/__init__.py rename to tests/v1/validation/__init__.py diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_all.yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all.yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_all.yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all.yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_all_deprecated_dict_arg.yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_deprecated_dict_arg.yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_all_deprecated_dict_arg.yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_deprecated_dict_arg.yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case0].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case0].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case0].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case0].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case1].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case1].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case1].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case1].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case2].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case2].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case2].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case2].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case3].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case3].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case3].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case3].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case4].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case4].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case4].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case4].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case5].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case5].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case5].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case5].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case6].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case6].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case6].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_all_dict[test_case6].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_custom_validator.yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_custom_validator.yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_custom_validator.yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_custom_validator.yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example0].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example0].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example0].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example0].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example100].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example100].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example100].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example100].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example101].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example101].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example101].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example101].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example102].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example102].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example102].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example102].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example103].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example103].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example103].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example103].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example104].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example104].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example104].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example104].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example105].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example105].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example105].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example105].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example106].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example106].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example106].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example106].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example107].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example107].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example107].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example107].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example108].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example108].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example108].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example108].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example109].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example109].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example109].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example109].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example10].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example10].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example10].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example10].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example110].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example110].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example110].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example110].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example111].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example111].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example111].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example111].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example112].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example112].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example112].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example112].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example113].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example113].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example113].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example113].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example114].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example114].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example114].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example114].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example115].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example115].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example115].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example115].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example116].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example116].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example116].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example116].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example117].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example117].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example117].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example117].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example118].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example118].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example118].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example118].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example119].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example119].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example119].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example119].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example11].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example11].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example11].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example11].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example120].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example120].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example120].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example120].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example121].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example121].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example121].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example121].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example122].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example122].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example122].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example122].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example123].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example123].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example123].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example123].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example124].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example124].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example124].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example124].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example12].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example12].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example12].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example12].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example13].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example13].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example13].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example13].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example14].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example14].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example14].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example14].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example15].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example15].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example15].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example15].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example16].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example16].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example16].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example16].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example17].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example17].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example17].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example17].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example18].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example18].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example18].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example18].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example19].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example19].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example19].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example19].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example1].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example1].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example1].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example1].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example20].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example20].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example20].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example20].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example21].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example21].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example21].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example21].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example22].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example22].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example22].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example22].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example23].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example23].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example23].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example23].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example24].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example24].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example24].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example24].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example25].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example25].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example25].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example25].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example26].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example26].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example26].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example26].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example27].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example27].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example27].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example27].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example28].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example28].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example28].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example28].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example29].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example29].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example29].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example29].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example2].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example2].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example2].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example2].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example30].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example30].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example30].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example30].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example31].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example31].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example31].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example31].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example32].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example32].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example32].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example32].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example33].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example33].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example33].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example33].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example34].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example34].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example34].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example34].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example35].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example35].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example35].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example35].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example36].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example36].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example36].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example36].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example37].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example37].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example37].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example37].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example38].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example38].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example38].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example38].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example39].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example39].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example39].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example39].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example3].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example3].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example3].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example3].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example40].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example40].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example40].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example40].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example41].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example41].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example41].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example41].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example42].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example42].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example42].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example42].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example43].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example43].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example43].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example43].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example44].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example44].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example44].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example44].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example45].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example45].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example45].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example45].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example46].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example46].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example46].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example46].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example47].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example47].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example47].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example47].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example48].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example48].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example48].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example48].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example49].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example49].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example49].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example49].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example4].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example4].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example4].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example4].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example50].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example50].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example50].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example50].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example51].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example51].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example51].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example51].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example52].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example52].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example52].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example52].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example53].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example53].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example53].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example53].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example54].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example54].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example54].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example54].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example55].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example55].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example55].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example55].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example56].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example56].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example56].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example56].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example57].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example57].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example57].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example57].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example58].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example58].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example58].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example58].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example59].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example59].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example59].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example59].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example5].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example5].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example5].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example5].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example60].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example60].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example60].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example60].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example61].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example61].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example61].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example61].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example62].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example62].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example62].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example62].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example63].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example63].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example63].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example63].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example64].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example64].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example64].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example64].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example65].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example65].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example65].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example65].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example66].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example66].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example66].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example66].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example67].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example67].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example67].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example67].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example68].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example68].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example68].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example68].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example69].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example69].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example69].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example69].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example6].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example6].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example6].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example6].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example70].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example70].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example70].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example70].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example71].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example71].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example71].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example71].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example72].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example72].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example72].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example72].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example73].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example73].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example73].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example73].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example74].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example74].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example74].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example74].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example75].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example75].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example75].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example75].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example76].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example76].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example76].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example76].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example77].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example77].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example77].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example77].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example78].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example78].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example78].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example78].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example79].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example79].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example79].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example79].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example7].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example7].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example7].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example7].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example80].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example80].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example80].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example80].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example81].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example81].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example81].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example81].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example82].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example82].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example82].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example82].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example83].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example83].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example83].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example83].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example84].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example84].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example84].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example84].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example85].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example85].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example85].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example85].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example86].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example86].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example86].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example86].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example87].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example87].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example87].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example87].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example88].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example88].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example88].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example88].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example89].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example89].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example89].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example89].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example8].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example8].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example8].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example8].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example90].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example90].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example90].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example90].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example91].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example91].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example91].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example91].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example92].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example92].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example92].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example92].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example93].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example93].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example93].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example93].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example94].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example94].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example94].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example94].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example95].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example95].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example95].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example95].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example96].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example96].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example96].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example96].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example97].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example97].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example97].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example97].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example98].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example98].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example98].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example98].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example99].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example99].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example99].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example99].yaml diff --git a/tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example9].yaml b/tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example9].yaml similarity index 100% rename from tests/validation/cassettes/test_validate/TestValidate.test_validate_examples[example9].yaml rename to tests/v1/validation/cassettes/test_validate/TestValidate.test_validate_examples[example9].yaml diff --git a/tests/validation/test_schema_uri_map.py b/tests/v1/validation/test_schema_uri_map.py similarity index 100% rename from tests/validation/test_schema_uri_map.py rename to tests/v1/validation/test_schema_uri_map.py diff --git a/tests/validation/test_validate.py b/tests/v1/validation/test_validate.py similarity index 98% rename from tests/validation/test_validate.py rename to tests/v1/validation/test_validate.py index 2c42c6750..0d443293e 100644 --- a/tests/validation/test_validate.py +++ b/tests/v1/validation/test_validate.py @@ -2,7 +2,7 @@ import os import shutil import tempfile -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import Any, cast import jsonschema @@ -15,8 +15,9 @@ from pystac.serialization.common_properties import merge_common_properties from pystac.utils import get_opt from pystac.validation import GetSchemaError, JsonSchemaSTACValidator -from tests.utils import TestCases -from tests.utils.test_cases import ExampleInfo + +from ..utils import TestCases +from ..utils.test_cases import ExampleInfo class TestValidate: @@ -174,7 +175,7 @@ def test_validates_geojson_with_tuple_coordinates(self) -> None: id="test-item", geometry=geom, bbox=[-115.308, 36.126, -115.305, 36.129], - datetime=datetime.now(timezone.utc), + datetime=datetime.now(UTC), properties={}, ) diff --git a/uv.lock b/uv.lock index b5e1d5b78..d2dad6842 100644 --- a/uv.lock +++ b/uv.lock @@ -1,4437 +1,1124 @@ version = 1 -revision = 3 -requires-python = ">=3.10" -resolution-markers = [ - "python_full_version >= '3.12' and platform_python_implementation == 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", - "python_full_version >= '3.12' and platform_python_implementation != 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", - "python_full_version < '3.11' and platform_python_implementation == 'PyPy'", - "python_full_version < '3.11' and platform_python_implementation != 'PyPy'", -] +revision = 1 +requires-python = ">=3.12" [[package]] -name = "accessible-pygments" -version = "0.0.5" +name = "asv" +version = "0.6.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pygments" }, + { name = "asv-runner" }, + { name = "build" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "importlib-metadata" }, + { name = "json5" }, + { name = "packaging" }, + { name = "pympler", marker = "platform_python_implementation != 'PyPy'" }, + { name = "pyyaml", marker = "platform_python_implementation != 'PyPy'" }, + { name = "tabulate" }, + { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899, upload-time = "2024-05-10T11:23:10.216Z" } +sdist = { url = "https://files.pythonhosted.org/packages/88/50/f762be4ee8632d88aff4ba9e62e7c156a0684ef52db629c22bae24fda449/asv-0.6.5.tar.gz", hash = "sha256:a8eeb7c5037cd78c146bd727d27203132438d4d62f36e669eb0cd5d63da0cf39", size = 402650 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903, upload-time = "2024-05-10T11:23:08.421Z" }, + { url = "https://files.pythonhosted.org/packages/e7/58/3c94d6043f2d815480b873f302ecda3b9debb0f202556fd822b118e87415/asv-0.6.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:375da7109fa160d41e4b86a5de7783e8c9bc9f1c930a1c02c29b652b15d46835", size = 180231 }, + { url = "https://files.pythonhosted.org/packages/f2/75/2a23346aabb19698e97b7ee8f57eb905dabf7460b226a2070c6cdd3e8c17/asv-0.6.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:980cb8e9c3be5350621c85201bfb25f70c26695f69bd4e91b19f1b3c97f00ff3", size = 180531 }, + { url = "https://files.pythonhosted.org/packages/27/d3/22bc22619266bced0a53ff07ee94a41bb09fed2bbe505e07a146fc4c1a58/asv-0.6.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13f503d45077ba275d357a9712fe1506b98e507eb276ca01e981c9e5baf30b43", size = 254851 }, + { url = "https://files.pythonhosted.org/packages/de/70/9991371db795a84b4fb0774ff050ed48730c14568953f9f59ca35170541b/asv-0.6.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:440aca773d254f6590f7a459bdc388441027bc2745eae644675665acc3809c2e", size = 256342 }, + { url = "https://files.pythonhosted.org/packages/8a/1d/7cb7f02091908201c45c609d29ced5bbc1101a057edc0e1fb153f7592b4e/asv-0.6.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bf63f7ee3d35ec8191543d82ca3a3be3a3c0cde8eb2d45a672f09f32ba5fbb37", size = 807867 }, + { url = "https://files.pythonhosted.org/packages/ff/02/946c53292fe551b3bb977260f29b5bfa9f284bf17088ccef5c8ddf28f06c/asv-0.6.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:58a0d2de09ebc67642b661d904d2e686e0f32bb5e8b4867523a15ff7561f061a", size = 1384841 }, + { url = "https://files.pythonhosted.org/packages/cc/f7/b43efa62f20af7e9035f6fd6dadb7109feddfb4e0b1e2ff791656ae55606/asv-0.6.5-cp312-cp312-win_amd64.whl", hash = "sha256:8df71cd3c656680051e0d0b2834521f7ab6da3d4804c48354c0e5ca341a0a39e", size = 182804 }, + { url = "https://files.pythonhosted.org/packages/25/70/a00673c98d30de3377b036cd85247274330ade931c0e4909c3e2d269c0c6/asv-0.6.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bedfc7f0138ab136ccd67f42575dd4b2471c811239ad8c7b7aabc83f5eba79c3", size = 180236 }, + { url = "https://files.pythonhosted.org/packages/2f/fd/d2f5c43dcb614cfedd1f339feb2f711b0a69e7c2ad96d68b547d44227c80/asv-0.6.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2f062e0658e568b98154afe11d91b5fb631f884678b7ad4a00ebcc0d6aa6b41f", size = 180537 }, + { url = "https://files.pythonhosted.org/packages/6d/00/a8dba759f554e6aedc5a7caf2a8efd00e5c69536560c670b1f41e7b53d83/asv-0.6.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6caedcca3ec60602b907caeab54d1706abb12e088ce96650862c6fc117831cc0", size = 254761 }, + { url = "https://files.pythonhosted.org/packages/3e/bd/89c4483e1e781e39957547d7bda0a7a7af338911ea159cd66a9415cc811d/asv-0.6.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:861ae69bfdd8659f95a058891c98757d3a5f857bf0ddc5d810e0dabf3405042e", size = 256267 }, + { url = "https://files.pythonhosted.org/packages/35/f1/752a51a07be0c153281f04795433524c76e6926063da005b78e2053117c5/asv-0.6.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7344c0020c1ab1cb33af9626cf24e05c346b4fd521d4f866f35a7a9a276f8e16", size = 806346 }, + { url = "https://files.pythonhosted.org/packages/3e/ce/c6d9f2dc42462db7897178e21e38c925d8efb75813dde8e7a0a2e5126387/asv-0.6.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1279a92cd8a601d2be5430afe3dd9f942ab0e6003f33ff1914dd9f638b595a3d", size = 1384779 }, + { url = "https://files.pythonhosted.org/packages/f0/4f/48af74f756f1c67b71074c99f265d382927de742288cf416e212c1808c7d/asv-0.6.5-cp313-cp313-win_amd64.whl", hash = "sha256:dbc3269464ec27d025d3b25e0e1f3d616035e05e01bcfceb2f4e965278d72197", size = 182807 }, ] [[package]] -name = "adlfs" -version = "2024.12.0" +name = "asv-runner" +version = "0.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiohttp" }, - { name = "azure-core" }, - { name = "azure-datalake-store" }, - { name = "azure-identity" }, - { name = "azure-storage-blob" }, - { name = "fsspec" }, + { name = "importlib-metadata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/82/e30891af574fb358449fb9436aac53569814452cb88b0cba4f488171b8dc/adlfs-2024.12.0.tar.gz", hash = "sha256:04582bf7461a57365766d01a295a0a88b2b8c42c4fea06e2d673f62675cac5c6", size = 49189, upload-time = "2024-12-15T19:06:30.939Z" } +sdist = { url = "https://files.pythonhosted.org/packages/32/4b/da5ae9c35e0b9f793d07d4939ad99e1d2ba7c9c502fd6074af5ff4554b03/asv_runner-0.2.1.tar.gz", hash = "sha256:945dd301a06fa9102f221b1e9ddd048f5ecd863796d4c8cd487f5577fe0db66d", size = 39518 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/ed/d1bf75c089857d38332cf45416e419b47382b345ba5dfc4fae69397830d9/adlfs-2024.12.0-py3-none-any.whl", hash = "sha256:00aab061ddec0413b2039487e656b62e01ece8ef1ca0493f76034a596cf069e3", size = 41792, upload-time = "2024-12-15T19:06:27.718Z" }, + { url = "https://files.pythonhosted.org/packages/54/9a/6872af94fc8e8072723946651e65f66e16a0ca0efec7806bce8c2e2483d1/asv_runner-0.2.1-py3-none-any.whl", hash = "sha256:655d466208ce311768071f5003a61611481b24b3ad5ac41fb8a6374197e647e9", size = 47660 }, ] [[package]] -name = "affine" -version = "2.4.0" +name = "attrs" +version = "25.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/98/d2f0bb06385069e799fc7d2870d9e078cfa0fa396dc8a2b81227d0da08b9/affine-2.4.0.tar.gz", hash = "sha256:a24d818d6a836c131976d22f8c27b8d3ca32d0af64c1d8d29deb7bafa4da1eea", size = 17132, upload-time = "2023-01-19T23:44:30.696Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/f7/85273299ab57117850cc0a936c64151171fac4da49bc6fba0dad984a7c5f/affine-2.4.0-py3-none-any.whl", hash = "sha256:8a3df80e2b2378aef598a83c1392efd47967afec4242021a0b06b4c7cbc61a92", size = 15662, upload-time = "2023-01-19T23:44:28.833Z" }, + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615 }, ] [[package]] -name = "aiohappyeyeballs" -version = "2.6.1" +name = "babel" +version = "2.18.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, + { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845 }, ] [[package]] -name = "aiohttp" -version = "3.13.3" +name = "backrefs" +version = "6.1" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohappyeyeballs" }, - { name = "aiosignal" }, - { name = "async-timeout", marker = "python_full_version < '3.11'" }, - { name = "attrs" }, - { name = "frozenlist" }, - { name = "multidict" }, - { name = "propcache" }, - { name = "yarl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } +sdist = { url = "https://files.pythonhosted.org/packages/86/e3/bb3a439d5cb255c4774724810ad8073830fac9c9dee123555820c1bcc806/backrefs-6.1.tar.gz", hash = "sha256:3bba1749aafe1db9b915f00e0dd166cba613b6f788ffd63060ac3485dc9be231", size = 7011962 } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/d6/5aec9313ee6ea9c7cde8b891b69f4ff4001416867104580670a31daeba5b/aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", size = 738950, upload-time = "2026-01-03T17:29:13.002Z" }, - { url = "https://files.pythonhosted.org/packages/68/03/8fa90a7e6d11ff20a18837a8e2b5dd23db01aabc475aa9271c8ad33299f5/aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", size = 496099, upload-time = "2026-01-03T17:29:15.268Z" }, - { url = "https://files.pythonhosted.org/packages/d2/23/b81f744d402510a8366b74eb420fc0cc1170d0c43daca12d10814df85f10/aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", size = 491072, upload-time = "2026-01-03T17:29:16.922Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e1/56d1d1c0dd334cd203dd97706ce004c1aa24b34a813b0b8daf3383039706/aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", size = 1671588, upload-time = "2026-01-03T17:29:18.539Z" }, - { url = "https://files.pythonhosted.org/packages/5f/34/8d7f962604f4bc2b4e39eb1220dac7d4e4cba91fb9ba0474b4ecd67db165/aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940", size = 1640334, upload-time = "2026-01-03T17:29:21.028Z" }, - { url = "https://files.pythonhosted.org/packages/94/1d/fcccf2c668d87337ddeef9881537baee13c58d8f01f12ba8a24215f2b804/aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", size = 1722656, upload-time = "2026-01-03T17:29:22.531Z" }, - { url = "https://files.pythonhosted.org/packages/aa/98/c6f3b081c4c606bc1e5f2ec102e87d6411c73a9ef3616fea6f2d5c98c062/aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", size = 1817625, upload-time = "2026-01-03T17:29:24.276Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c0/cfcc3d2e11b477f86e1af2863f3858c8850d751ce8dc39c4058a072c9e54/aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", size = 1672604, upload-time = "2026-01-03T17:29:26.099Z" }, - { url = "https://files.pythonhosted.org/packages/1e/77/6b4ffcbcac4c6a5d041343a756f34a6dd26174ae07f977a64fe028dda5b0/aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", size = 1554370, upload-time = "2026-01-03T17:29:28.121Z" }, - { url = "https://files.pythonhosted.org/packages/f2/f0/e3ddfa93f17d689dbe014ba048f18e0c9f9b456033b70e94349a2e9048be/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", size = 1642023, upload-time = "2026-01-03T17:29:30.002Z" }, - { url = "https://files.pythonhosted.org/packages/eb/45/c14019c9ec60a8e243d06d601b33dcc4fd92379424bde3021725859d7f99/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", size = 1649680, upload-time = "2026-01-03T17:29:31.782Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fd/09c9451dae5aa5c5ed756df95ff9ef549d45d4be663bafd1e4954fd836f0/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", size = 1692407, upload-time = "2026-01-03T17:29:33.392Z" }, - { url = "https://files.pythonhosted.org/packages/a6/81/938bc2ec33c10efd6637ccb3d22f9f3160d08e8f3aa2587a2c2d5ab578eb/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", size = 1543047, upload-time = "2026-01-03T17:29:34.855Z" }, - { url = "https://files.pythonhosted.org/packages/f7/23/80488ee21c8d567c83045e412e1d9b7077d27171591a4eb7822586e8c06a/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", size = 1715264, upload-time = "2026-01-03T17:29:36.389Z" }, - { url = "https://files.pythonhosted.org/packages/e2/83/259a8da6683182768200b368120ab3deff5370bed93880fb9a3a86299f34/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", size = 1657275, upload-time = "2026-01-03T17:29:38.162Z" }, - { url = "https://files.pythonhosted.org/packages/3f/4f/2c41f800a0b560785c10fb316216ac058c105f9be50bdc6a285de88db625/aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", size = 434053, upload-time = "2026-01-03T17:29:40.074Z" }, - { url = "https://files.pythonhosted.org/packages/80/df/29cd63c7ecfdb65ccc12f7d808cac4fa2a19544660c06c61a4a48462de0c/aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", size = 456687, upload-time = "2026-01-03T17:29:41.819Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4c/a164164834f03924d9a29dc3acd9e7ee58f95857e0b467f6d04298594ebb/aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b", size = 746051, upload-time = "2026-01-03T17:29:43.287Z" }, - { url = "https://files.pythonhosted.org/packages/82/71/d5c31390d18d4f58115037c432b7e0348c60f6f53b727cad33172144a112/aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64", size = 499234, upload-time = "2026-01-03T17:29:44.822Z" }, - { url = "https://files.pythonhosted.org/packages/0e/c9/741f8ac91e14b1d2e7100690425a5b2b919a87a5075406582991fb7de920/aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea", size = 494979, upload-time = "2026-01-03T17:29:46.405Z" }, - { url = "https://files.pythonhosted.org/packages/75/b5/31d4d2e802dfd59f74ed47eba48869c1c21552c586d5e81a9d0d5c2ad640/aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a", size = 1748297, upload-time = "2026-01-03T17:29:48.083Z" }, - { url = "https://files.pythonhosted.org/packages/1a/3e/eefad0ad42959f226bb79664826883f2687d602a9ae2941a18e0484a74d3/aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540", size = 1707172, upload-time = "2026-01-03T17:29:49.648Z" }, - { url = "https://files.pythonhosted.org/packages/c5/3a/54a64299fac2891c346cdcf2aa6803f994a2e4beeaf2e5a09dcc54acc842/aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b", size = 1805405, upload-time = "2026-01-03T17:29:51.244Z" }, - { url = "https://files.pythonhosted.org/packages/6c/70/ddc1b7169cf64075e864f64595a14b147a895a868394a48f6a8031979038/aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3", size = 1899449, upload-time = "2026-01-03T17:29:53.938Z" }, - { url = "https://files.pythonhosted.org/packages/a1/7e/6815aab7d3a56610891c76ef79095677b8b5be6646aaf00f69b221765021/aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1", size = 1748444, upload-time = "2026-01-03T17:29:55.484Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f2/073b145c4100da5511f457dc0f7558e99b2987cf72600d42b559db856fbc/aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3", size = 1606038, upload-time = "2026-01-03T17:29:57.179Z" }, - { url = "https://files.pythonhosted.org/packages/0a/c1/778d011920cae03ae01424ec202c513dc69243cf2db303965615b81deeea/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440", size = 1724156, upload-time = "2026-01-03T17:29:58.914Z" }, - { url = "https://files.pythonhosted.org/packages/0e/cb/3419eabf4ec1e9ec6f242c32b689248365a1cf621891f6f0386632525494/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7", size = 1722340, upload-time = "2026-01-03T17:30:01.962Z" }, - { url = "https://files.pythonhosted.org/packages/7a/e5/76cf77bdbc435bf233c1f114edad39ed4177ccbfab7c329482b179cff4f4/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c", size = 1783041, upload-time = "2026-01-03T17:30:03.609Z" }, - { url = "https://files.pythonhosted.org/packages/9d/d4/dd1ca234c794fd29c057ce8c0566b8ef7fd6a51069de5f06fa84b9a1971c/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51", size = 1596024, upload-time = "2026-01-03T17:30:05.132Z" }, - { url = "https://files.pythonhosted.org/packages/55/58/4345b5f26661a6180afa686c473620c30a66afdf120ed3dd545bbc809e85/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4", size = 1804590, upload-time = "2026-01-03T17:30:07.135Z" }, - { url = "https://files.pythonhosted.org/packages/7b/06/05950619af6c2df7e0a431d889ba2813c9f0129cec76f663e547a5ad56f2/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29", size = 1740355, upload-time = "2026-01-03T17:30:09.083Z" }, - { url = "https://files.pythonhosted.org/packages/3e/80/958f16de79ba0422d7c1e284b2abd0c84bc03394fbe631d0a39ffa10e1eb/aiohttp-3.13.3-cp311-cp311-win32.whl", hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239", size = 433701, upload-time = "2026-01-03T17:30:10.869Z" }, - { url = "https://files.pythonhosted.org/packages/dc/f2/27cdf04c9851712d6c1b99df6821a6623c3c9e55956d4b1e318c337b5a48/aiohttp-3.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f", size = 457678, upload-time = "2026-01-03T17:30:12.719Z" }, - { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" }, - { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" }, - { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, - { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, - { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, - { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, - { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, - { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, - { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, - { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253, upload-time = "2026-01-03T17:30:42.644Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407, upload-time = "2026-01-03T17:30:44.195Z" }, - { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190, upload-time = "2026-01-03T17:30:45.832Z" }, - { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783, upload-time = "2026-01-03T17:30:47.466Z" }, - { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704, upload-time = "2026-01-03T17:30:49.373Z" }, - { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652, upload-time = "2026-01-03T17:30:50.974Z" }, - { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014, upload-time = "2026-01-03T17:30:52.729Z" }, - { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777, upload-time = "2026-01-03T17:30:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276, upload-time = "2026-01-03T17:30:56.512Z" }, - { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131, upload-time = "2026-01-03T17:30:58.256Z" }, - { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863, upload-time = "2026-01-03T17:31:00.445Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793, upload-time = "2026-01-03T17:31:03.024Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676, upload-time = "2026-01-03T17:31:04.842Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217, upload-time = "2026-01-03T17:31:06.868Z" }, - { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303, upload-time = "2026-01-03T17:31:08.958Z" }, - { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673, upload-time = "2026-01-03T17:31:10.676Z" }, - { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120, upload-time = "2026-01-03T17:31:12.575Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383, upload-time = "2026-01-03T17:31:14.382Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899, upload-time = "2026-01-03T17:31:15.958Z" }, - { url = "https://files.pythonhosted.org/packages/99/36/5b6514a9f5d66f4e2597e40dea2e3db271e023eb7a5d22defe96ba560996/aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", size = 737238, upload-time = "2026-01-03T17:31:17.909Z" }, - { url = "https://files.pythonhosted.org/packages/f7/49/459327f0d5bcd8c6c9ca69e60fdeebc3622861e696490d8674a6d0cb90a6/aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", size = 492292, upload-time = "2026-01-03T17:31:19.919Z" }, - { url = "https://files.pythonhosted.org/packages/e8/0b/b97660c5fd05d3495b4eb27f2d0ef18dc1dc4eff7511a9bf371397ff0264/aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", size = 493021, upload-time = "2026-01-03T17:31:21.636Z" }, - { url = "https://files.pythonhosted.org/packages/54/d4/438efabdf74e30aeceb890c3290bbaa449780583b1270b00661126b8aae4/aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", size = 1717263, upload-time = "2026-01-03T17:31:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/71/f2/7bddc7fd612367d1459c5bcf598a9e8f7092d6580d98de0e057eb42697ad/aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", size = 1669107, upload-time = "2026-01-03T17:31:25.334Z" }, - { url = "https://files.pythonhosted.org/packages/00/5a/1aeaecca40e22560f97610a329e0e5efef5e0b5afdf9f857f0d93839ab2e/aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", size = 1760196, upload-time = "2026-01-03T17:31:27.394Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f8/0ff6992bea7bd560fc510ea1c815f87eedd745fe035589c71ce05612a19a/aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", size = 1843591, upload-time = "2026-01-03T17:31:29.238Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d1/e30e537a15f53485b61f5be525f2157da719819e8377298502aebac45536/aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", size = 1720277, upload-time = "2026-01-03T17:31:31.053Z" }, - { url = "https://files.pythonhosted.org/packages/84/45/23f4c451d8192f553d38d838831ebbc156907ea6e05557f39563101b7717/aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", size = 1548575, upload-time = "2026-01-03T17:31:32.87Z" }, - { url = "https://files.pythonhosted.org/packages/6a/ed/0a42b127a43712eda7807e7892c083eadfaf8429ca8fb619662a530a3aab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", size = 1679455, upload-time = "2026-01-03T17:31:34.76Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b5/c05f0c2b4b4fe2c9d55e73b6d3ed4fd6c9dc2684b1d81cbdf77e7fad9adb/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", size = 1687417, upload-time = "2026-01-03T17:31:36.699Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6b/915bc5dad66aef602b9e459b5a973529304d4e89ca86999d9d75d80cbd0b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", size = 1729968, upload-time = "2026-01-03T17:31:38.622Z" }, - { url = "https://files.pythonhosted.org/packages/11/3b/e84581290a9520024a08640b63d07673057aec5ca548177a82026187ba73/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", size = 1545690, upload-time = "2026-01-03T17:31:40.57Z" }, - { url = "https://files.pythonhosted.org/packages/f5/04/0c3655a566c43fd647c81b895dfe361b9f9ad6d58c19309d45cff52d6c3b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", size = 1746390, upload-time = "2026-01-03T17:31:42.857Z" }, - { url = "https://files.pythonhosted.org/packages/1f/53/71165b26978f719c3419381514c9690bd5980e764a09440a10bb816ea4ab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", size = 1702188, upload-time = "2026-01-03T17:31:44.984Z" }, - { url = "https://files.pythonhosted.org/packages/29/a7/cbe6c9e8e136314fa1980da388a59d2f35f35395948a08b6747baebb6aa6/aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", size = 433126, upload-time = "2026-01-03T17:31:47.463Z" }, - { url = "https://files.pythonhosted.org/packages/de/56/982704adea7d3b16614fc5936014e9af85c0e34b58f9046655817f04306e/aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", size = 459128, upload-time = "2026-01-03T17:31:49.2Z" }, - { url = "https://files.pythonhosted.org/packages/6c/2a/3c79b638a9c3d4658d345339d22070241ea341ed4e07b5ac60fb0f418003/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", size = 769512, upload-time = "2026-01-03T17:31:51.134Z" }, - { url = "https://files.pythonhosted.org/packages/29/b9/3e5014d46c0ab0db8707e0ac2711ed28c4da0218c358a4e7c17bae0d8722/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", size = 506444, upload-time = "2026-01-03T17:31:52.85Z" }, - { url = "https://files.pythonhosted.org/packages/90/03/c1d4ef9a054e151cd7839cdc497f2638f00b93cbe8043983986630d7a80c/aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", size = 510798, upload-time = "2026-01-03T17:31:54.91Z" }, - { url = "https://files.pythonhosted.org/packages/ea/76/8c1e5abbfe8e127c893fe7ead569148a4d5a799f7cf958d8c09f3eedf097/aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", size = 1868835, upload-time = "2026-01-03T17:31:56.733Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ac/984c5a6f74c363b01ff97adc96a3976d9c98940b8969a1881575b279ac5d/aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", size = 1720486, upload-time = "2026-01-03T17:31:58.65Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9a/b7039c5f099c4eb632138728828b33428585031a1e658d693d41d07d89d1/aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", size = 1847951, upload-time = "2026-01-03T17:32:00.989Z" }, - { url = "https://files.pythonhosted.org/packages/3c/02/3bec2b9a1ba3c19ff89a43a19324202b8eb187ca1e928d8bdac9bbdddebd/aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", size = 1941001, upload-time = "2026-01-03T17:32:03.122Z" }, - { url = "https://files.pythonhosted.org/packages/37/df/d879401cedeef27ac4717f6426c8c36c3091c6e9f08a9178cc87549c537f/aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", size = 1797246, upload-time = "2026-01-03T17:32:05.255Z" }, - { url = "https://files.pythonhosted.org/packages/8d/15/be122de1f67e6953add23335c8ece6d314ab67c8bebb3f181063010795a7/aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", size = 1627131, upload-time = "2026-01-03T17:32:07.607Z" }, - { url = "https://files.pythonhosted.org/packages/12/12/70eedcac9134cfa3219ab7af31ea56bc877395b1ac30d65b1bc4b27d0438/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", size = 1795196, upload-time = "2026-01-03T17:32:09.59Z" }, - { url = "https://files.pythonhosted.org/packages/32/11/b30e1b1cd1f3054af86ebe60df96989c6a414dd87e27ad16950eee420bea/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", size = 1782841, upload-time = "2026-01-03T17:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/88/0d/d98a9367b38912384a17e287850f5695c528cff0f14f791ce8ee2e4f7796/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", size = 1795193, upload-time = "2026-01-03T17:32:13.705Z" }, - { url = "https://files.pythonhosted.org/packages/43/a5/a2dfd1f5ff5581632c7f6a30e1744deda03808974f94f6534241ef60c751/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", size = 1621979, upload-time = "2026-01-03T17:32:15.965Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f0/12973c382ae7c1cccbc4417e129c5bf54c374dfb85af70893646e1f0e749/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", size = 1822193, upload-time = "2026-01-03T17:32:18.219Z" }, - { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801, upload-time = "2026-01-03T17:32:20.25Z" }, - { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523, upload-time = "2026-01-03T17:32:22.215Z" }, - { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ee/c216d52f58ea75b5e1841022bbae24438b19834a29b163cb32aa3a2a7c6e/backrefs-6.1-py310-none-any.whl", hash = "sha256:2a2ccb96302337ce61ee4717ceacfbf26ba4efb1d55af86564b8bbaeda39cac1", size = 381059 }, + { url = "https://files.pythonhosted.org/packages/e6/9a/8da246d988ded941da96c7ed945d63e94a445637eaad985a0ed88787cb89/backrefs-6.1-py311-none-any.whl", hash = "sha256:e82bba3875ee4430f4de4b6db19429a27275d95a5f3773c57e9e18abc23fd2b7", size = 392854 }, + { url = "https://files.pythonhosted.org/packages/37/c9/fd117a6f9300c62bbc33bc337fd2b3c6bfe28b6e9701de336b52d7a797ad/backrefs-6.1-py312-none-any.whl", hash = "sha256:c64698c8d2269343d88947c0735cb4b78745bd3ba590e10313fbf3f78c34da5a", size = 398770 }, + { url = "https://files.pythonhosted.org/packages/eb/95/7118e935b0b0bd3f94dfec2d852fd4e4f4f9757bdb49850519acd245cd3a/backrefs-6.1-py313-none-any.whl", hash = "sha256:4c9d3dc1e2e558965202c012304f33d4e0e477e1c103663fd2c3cc9bb18b0d05", size = 400726 }, + { url = "https://files.pythonhosted.org/packages/1d/72/6296bad135bfafd3254ae3648cd152980a424bd6fed64a101af00cc7ba31/backrefs-6.1-py314-none-any.whl", hash = "sha256:13eafbc9ccd5222e9c1f0bec563e6d2a6d21514962f11e7fc79872fd56cbc853", size = 412584 }, + { url = "https://files.pythonhosted.org/packages/02/e3/a4fa1946722c4c7b063cc25043a12d9ce9b4323777f89643be74cef2993c/backrefs-6.1-py39-none-any.whl", hash = "sha256:a9e99b8a4867852cad177a6430e31b0f6e495d65f8c6c134b68c14c3c95bf4b0", size = 381058 }, ] [[package]] -name = "aiosignal" -version = "1.4.0" +name = "basedpyright" +version = "1.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "nodejs-wheel-binaries" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/d4/4ac6eeba6cfe2ad8586dcf87fdb9e8b045aa467b559bc2e24e91e84f58b2/basedpyright-1.38.0.tar.gz", hash = "sha256:7a9cf631d7eaf5859022a4352b51ed0e78ce115435a8599402239804000d0cdf", size = 25257385 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, + { url = "https://files.pythonhosted.org/packages/d5/90/1883cec16d667d944b08e8d8909b9b2f46cc1d2b9731e855e3c71f9b0450/basedpyright-1.38.0-py3-none-any.whl", hash = "sha256:a6c11a343fd12a2152a0d721b0e92f54f2e2e3322ee2562197e27dad952f1a61", size = 12303557 }, ] [[package]] -name = "alabaster" -version = "1.0.0" +name = "build" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, +dependencies = [ + { name = "colorama", marker = "os_name == 'nt'" }, + { name = "packaging" }, + { name = "pyproject-hooks" }, ] - -[[package]] -name = "annotated-types" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/18/94eaffda7b329535d91f00fe605ab1f1e5cd68b2074d03f255c7d250687d/build-1.4.0.tar.gz", hash = "sha256:f1b91b925aa322be454f8330c6fb48b465da993d1e7e7e6fa35027ec49f3c936", size = 50054 } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, + { url = "https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl", hash = "sha256:6a07c1b8eb6f2b311b96fcbdbce5dab5fe637ffda0fd83c9cac622e927501596", size = 24141 }, ] [[package]] -name = "anyio" -version = "4.9.0" +name = "certifi" +version = "2026.1.4" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "idna" }, - { name = "sniffio" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900 }, ] [[package]] -name = "appnope" -version = "0.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, +name = "charset-normalizer" +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425 }, + { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162 }, + { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558 }, + { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497 }, + { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240 }, + { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471 }, + { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864 }, + { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647 }, + { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110 }, + { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839 }, + { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667 }, + { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535 }, + { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816 }, + { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694 }, + { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131 }, + { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390 }, + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091 }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936 }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180 }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346 }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874 }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076 }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601 }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376 }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825 }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583 }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366 }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300 }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465 }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404 }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092 }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408 }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746 }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889 }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641 }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779 }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035 }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542 }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524 }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395 }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680 }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045 }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687 }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014 }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044 }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940 }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104 }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743 }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402 }, ] [[package]] -name = "argon2-cffi" -version = "25.1.0" +name = "click" +version = "8.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "argon2-cffi-bindings" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274 }, ] [[package]] -name = "argon2-cffi-bindings" -version = "21.2.0" +name = "colorama" +version = "0.4.6" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation == 'PyPy'" }, - { name = "cffi", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/e9/184b8ccce6683b0aa2fbb7ba5683ea4b9c5763f1356347f1312c32e3c66e/argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3", size = 1779911, upload-time = "2021-12-01T08:52:55.68Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/13/838ce2620025e9666aa8f686431f67a29052241692a3dd1ae9d3692a89d3/argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367", size = 29658, upload-time = "2021-12-01T09:09:17.016Z" }, - { url = "https://files.pythonhosted.org/packages/b3/02/f7f7bb6b6af6031edb11037639c697b912e1dea2db94d436e681aea2f495/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d", size = 80583, upload-time = "2021-12-01T09:09:19.546Z" }, - { url = "https://files.pythonhosted.org/packages/ec/f7/378254e6dd7ae6f31fe40c8649eea7d4832a42243acaf0f1fff9083b2bed/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae", size = 86168, upload-time = "2021-12-01T09:09:21.445Z" }, - { url = "https://files.pythonhosted.org/packages/74/f6/4a34a37a98311ed73bb80efe422fed95f2ac25a4cacc5ae1d7ae6a144505/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c", size = 82709, upload-time = "2021-12-01T09:09:18.182Z" }, - { url = "https://files.pythonhosted.org/packages/74/2b/73d767bfdaab25484f7e7901379d5f8793cccbb86c6e0cbc4c1b96f63896/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86", size = 83613, upload-time = "2021-12-01T09:09:22.741Z" }, - { url = "https://files.pythonhosted.org/packages/4f/fd/37f86deef67ff57c76f137a67181949c2d408077e2e3dd70c6c42912c9bf/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f", size = 84583, upload-time = "2021-12-01T09:09:24.177Z" }, - { url = "https://files.pythonhosted.org/packages/6f/52/5a60085a3dae8fded8327a4f564223029f5f54b0cb0455a31131b5363a01/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e", size = 88475, upload-time = "2021-12-01T09:09:26.673Z" }, - { url = "https://files.pythonhosted.org/packages/8b/95/143cd64feb24a15fa4b189a3e1e7efbaeeb00f39a51e99b26fc62fbacabd/argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082", size = 27698, upload-time = "2021-12-01T09:09:27.87Z" }, - { url = "https://files.pythonhosted.org/packages/37/2c/e34e47c7dee97ba6f01a6203e0383e15b60fb85d78ac9a15cd066f6fe28b/argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f", size = 30817, upload-time = "2021-12-01T09:09:30.267Z" }, - { url = "https://files.pythonhosted.org/packages/5a/e4/bf8034d25edaa495da3c8a3405627d2e35758e44ff6eaa7948092646fdcc/argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93", size = 53104, upload-time = "2021-12-01T09:09:31.335Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] [[package]] -name = "arrow" -version = "1.3.0" +name = "distlib" +version = "0.4.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil" }, - { name = "types-python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2e/00/0f6e8fcdb23ea632c866620cc872729ff43ed91d284c866b515c6342b173/arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85", size = 131960, upload-time = "2023-09-30T22:11:18.25Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80", size = 66419, upload-time = "2023-09-30T22:11:16.072Z" }, + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 }, ] [[package]] -name = "asciitree" -version = "0.3.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/6a/885bc91484e1aa8f618f6f0228d76d0e67000b0fdd6090673b777e311913/asciitree-0.3.3.tar.gz", hash = "sha256:4aa4b9b649f85e3fcb343363d97564aa1fb62e249677f2e18a96765145cc0f6e", size = 3951, upload-time = "2016-09-05T19:10:42.681Z" } - -[[package]] -name = "asttokens" -version = "3.0.0" +name = "filelock" +version = "3.20.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978, upload-time = "2024-11-30T04:30:14.439Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485 } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918, upload-time = "2024-11-30T04:30:10.946Z" }, + { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701 }, ] [[package]] -name = "asv" -version = "0.6.4" +name = "ghp-import" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "asv-runner" }, - { name = "build" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "json5" }, - { name = "pympler", marker = "platform_python_implementation != 'PyPy'" }, - { name = "pyyaml", marker = "platform_python_implementation != 'PyPy'" }, - { name = "tabulate" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "virtualenv" }, + { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/0c/31fe4135b378ee17131a804b11380a1ec1406c3925cb24ecdff5b86e673c/asv-0.6.4.tar.gz", hash = "sha256:1d124184171cfe106e3e57ac04e3221b8d4571c9bd6ca2c6498a8c7407339df1", size = 389611, upload-time = "2024-08-12T23:00:14.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943 } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/54/de33292ea7ce15613bdd9a6e51fac47878ce80f293157c9116c23387a1e5/asv-0.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e32b4cc435bdb6f2ef83d8092e977962f6fa20471542d6341e596324d350cbea", size = 185448, upload-time = "2024-08-12T22:58:12.49Z" }, - { url = "https://files.pythonhosted.org/packages/a9/14/fc30a590333a1021b28f9f2aaab009d3bb7c277cc1decadfcc86b74108de/asv-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fdfb9090623fc45cbeb77ab40b394779794083c155128e3d320fa06af2e0fdf5", size = 185245, upload-time = "2024-08-12T22:58:15.386Z" }, - { url = "https://files.pythonhosted.org/packages/c3/72/87296f54fbf876fd8e0ad744e0c9322dee6f6f631a8d9f14b9b48008b51c/asv-0.6.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dfee8a415f4b5da0be4bedf4c9cb3b041c2148d28d2327cf3b54f9cb565cefd", size = 259330, upload-time = "2024-08-12T22:58:17.403Z" }, - { url = "https://files.pythonhosted.org/packages/bf/77/26cb95b1f1751706352e9924388732dc9fe4cc9e60153098407974d72b89/asv-0.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abc13331bb8bb1880dbc33e75175ae90bca439038a1f7e246528481ecebd15dd", size = 261036, upload-time = "2024-08-12T22:58:19.447Z" }, - { url = "https://files.pythonhosted.org/packages/ca/c6/c6f40902933b87f41e1ded3065eae526d29a40fbb84a69e2e61e151b217e/asv-0.6.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b67eec004f8218bba25dcdbdda2e6676dd6c4ac3e97a80b691b27dcfbfbda38d", size = 866515, upload-time = "2024-08-12T22:58:22.934Z" }, - { url = "https://files.pythonhosted.org/packages/ae/00/7fa5a7c695ee49e11765956123407f868cefbf481b7c4177feba6e0646e9/asv-0.6.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aef14496a34552308d054db71181bfb1ca45d7ef29028747d388be9f00a5b45c", size = 812157, upload-time = "2024-08-12T22:58:25.684Z" }, - { url = "https://files.pythonhosted.org/packages/4f/97/f09925683128f9bce9a7bfbcecb22334cec988fdb139a9959c2d22f39f19/asv-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:0c8931e7a8aeda75f90b3ac422cbb7c46a5ce50d8c0a8e821cdf3e4d0705dd76", size = 188022, upload-time = "2024-08-12T22:58:27.774Z" }, - { url = "https://files.pythonhosted.org/packages/b0/39/8532a88cde13dca8d9dcfeb2ba48d92beaef8919fbfc2d428cbfa5a1bbb9/asv-0.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74666c5896b4aec92b4a12cf9aa7494dec3398bb9ea602a9f8dc1656b53e8e10", size = 185462, upload-time = "2024-08-12T22:58:30.299Z" }, - { url = "https://files.pythonhosted.org/packages/9b/a6/b0133d083ac4c979f16e9bd887427c141306e3779505941ccf25341c0384/asv-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26166a7bd7fe05b5a8507247d1a7ab1dfc4256414b0505d124a7b9d46a618a1c", size = 185242, upload-time = "2024-08-12T22:58:32.138Z" }, - { url = "https://files.pythonhosted.org/packages/ba/5c/0726b4925163c12e842e306267c0e702fb694b85f34b62240f687208091c/asv-0.6.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe6161c5616f5aed936947866b6376e09c937d628aa81115b3c72e90a151c1f9", size = 259959, upload-time = "2024-08-12T22:58:33.877Z" }, - { url = "https://files.pythonhosted.org/packages/e0/6d/944b4fc935b6c6874a17413159aa19701b80052d29c80efe6a9afbcec3b5/asv-0.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4d6122b5e86bf9071b9ff7136672d50da0d460dfc958f43429843f7a3cd3e86a", size = 261620, upload-time = "2024-08-12T22:58:36.129Z" }, - { url = "https://files.pythonhosted.org/packages/eb/24/b6169229108e1a1d6506fbd2bfad5ecede34df5e03020b16c627e44411b8/asv-0.6.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:79554f125033ecbcb599cd704b4b5b525d254e5e05b1dd24bab3bbd83ae5502e", size = 867864, upload-time = "2024-08-12T22:58:38.865Z" }, - { url = "https://files.pythonhosted.org/packages/4a/a2/b9e5be144fd0c7072637f50c98bdf615d83165244849473e2b9f262ea24b/asv-0.6.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2e80f39501628fd4cac972f08fa4c9b8e211a86fc43dd6e58c95d106cbaf54e7", size = 813539, upload-time = "2024-08-12T22:58:41.184Z" }, - { url = "https://files.pythonhosted.org/packages/2a/d7/ea92fc7155a5cd7aa1903dd74096a91a8851355423bf8f09d6e3a96ccc21/asv-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:363dfdee98cc072e6a1468137eed640985e48ccbb11c175d04ee420f05459872", size = 188025, upload-time = "2024-08-12T22:58:42.72Z" }, - { url = "https://files.pythonhosted.org/packages/42/5f/f6ac7c787cde901d694b9355d9cd9227b907b313b3f1ed2b006668104e5f/asv-0.6.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:244b71778f91aa6672e1f16feb9eecac78ef7cee95228ef8f0315a2e2deecfed", size = 185442, upload-time = "2024-08-12T22:58:44.839Z" }, - { url = "https://files.pythonhosted.org/packages/b2/c0/339bf20ad132b85376aa4ddc6d17e0ee704846a856a84eb59609df6ca4ef/asv-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3e798b275de2889748d43d42305bfce68c015a3e38ae935d231835cb836fef73", size = 185225, upload-time = "2024-08-12T22:58:48.309Z" }, - { url = "https://files.pythonhosted.org/packages/3d/3e/1019cb8cb54bc6cf28799c49b86a67b4713082d1e857eac8ec9e34d8ed83/asv-0.6.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d064c5ac1ab18efc62467f65ed4989a2e2ac1a4d21886119fa0ef0f91d548438", size = 260431, upload-time = "2024-08-12T22:58:49.663Z" }, - { url = "https://files.pythonhosted.org/packages/a5/80/fd70dba7f524a95a2d9e8f976cfc891d570f3ccdd9201c1f0c3bc2dfea74/asv-0.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c51e5862bdac0f1fe11886bdd40b30a9691a65cb7feac40f0676fe9206d5bb43", size = 261970, upload-time = "2024-08-12T22:58:51.499Z" }, - { url = "https://files.pythonhosted.org/packages/6a/bf/5af517bef47eb92eca0016b33334f77760a4f4f7acd3022b1d00dfe576fa/asv-0.6.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:46a7ca838e8c49109c43b1cda0eb64abc5e0a045538da718abe981d115ed47aa", size = 867717, upload-time = "2024-08-12T22:58:54.508Z" }, - { url = "https://files.pythonhosted.org/packages/98/71/f205a3122f8aa7889a4691384c6b01bea4805b5982d47ccdaa189296d53a/asv-0.6.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f5f722178c7e36b797f764c837fc03c462f68c8f2cba5145b2e64119e46231ff", size = 813322, upload-time = "2024-08-12T22:58:57.074Z" }, - { url = "https://files.pythonhosted.org/packages/1f/7b/a2001b35bc1fbaa7cbf763f8cff4ad64af849fd59ef26a3f4e32ee211e63/asv-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:f972ca71316d46a0242eb69e53dadfeab1e4d0546773b0f722462f97b3e5fbd9", size = 188022, upload-time = "2024-08-12T22:58:58.824Z" }, - { url = "https://files.pythonhosted.org/packages/67/c2/0808a237f90189f8fcfd6be4b77a8e1f19d0b8813d947a816f2bf9514809/asv-0.6.4-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0305e9eee21f71c3d3f8b046beb35e571f6dd7ed2fcd0e8405f8a208bcd3228a", size = 184577, upload-time = "2024-08-12T22:59:45.167Z" }, - { url = "https://files.pythonhosted.org/packages/7b/c7/171341cc046f570b32fc4da70e80f800470301df3d67301e71b9c6f68a43/asv-0.6.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6cd23fa20edf8cb30354fda3388a8835a15158e21559c86f0d997e5d30dbf91", size = 185004, upload-time = "2024-08-12T22:59:46.378Z" }, - { url = "https://files.pythonhosted.org/packages/8f/c6/df037423144a902cd2cbcdb9cbcdee567f7ba35be4f470f2a09ffba1e2fd/asv-0.6.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7424d2dbfcb98aa3c099311100ceb9aabfd83fed0b41420f70f142852ed392a", size = 185767, upload-time = "2024-08-12T22:59:48.423Z" }, - { url = "https://files.pythonhosted.org/packages/36/09/8ec4eb06432ccd13a346fb4db1e9ee67589c65731bc7a5c59c347eab5581/asv-0.6.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7f4b95583cf379015d35b747a1bb4df99c05dd4107d6081b2cf4a577f4caeca", size = 188103, upload-time = "2024-08-12T22:59:50.475Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 }, ] [[package]] -name = "asv-runner" -version = "0.2.1" +name = "griffe" +version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "importlib-metadata" }, + { name = "griffecli" }, + { name = "griffelib" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/32/4b/da5ae9c35e0b9f793d07d4939ad99e1d2ba7c9c502fd6074af5ff4554b03/asv_runner-0.2.1.tar.gz", hash = "sha256:945dd301a06fa9102f221b1e9ddd048f5ecd863796d4c8cd487f5577fe0db66d", size = 39518, upload-time = "2024-02-17T14:11:48.104Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/9a/6872af94fc8e8072723946651e65f66e16a0ca0efec7806bce8c2e2483d1/asv_runner-0.2.1-py3-none-any.whl", hash = "sha256:655d466208ce311768071f5003a61611481b24b3ad5ac41fb8a6374197e647e9", size = 47660, upload-time = "2024-02-11T21:50:07.026Z" }, + { url = "https://files.pythonhosted.org/packages/8b/94/ee21d41e7eb4f823b94603b9d40f86d3c7fde80eacc2c3c71845476dddaa/griffe-2.0.0-py3-none-any.whl", hash = "sha256:5418081135a391c3e6e757a7f3f156f1a1a746cc7b4023868ff7d5e2f9a980aa", size = 5214 }, ] [[package]] -name = "async-lru" -version = "2.0.5" +name = "griffecli" +version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "colorama" }, + { name = "griffelib" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b2/4d/71ec4d3939dc755264f680f6c2b4906423a304c3d18e96853f0a595dfe97/async_lru-2.0.5.tar.gz", hash = "sha256:481d52ccdd27275f42c43a928b4a50c3bfb2d67af4e78b170e3e0bb39c66e5bb", size = 10380, upload-time = "2025-03-16T17:25:36.919Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-py3-none-any.whl", hash = "sha256:ab95404d8d2605310d345932697371a5f40def0487c03d6d0ad9138de52c9943", size = 6069, upload-time = "2025-03-16T17:25:35.422Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ed/d93f7a447bbf7a935d8868e9617cbe1cadf9ee9ee6bd275d3040fbf93d60/griffecli-2.0.0-py3-none-any.whl", hash = "sha256:9f7cd9ee9b21d55e91689358978d2385ae65c22f307a63fb3269acf3f21e643d", size = 9345 }, ] [[package]] -name = "async-timeout" -version = "5.0.1" +name = "griffelib" +version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, + { url = "https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl", hash = "sha256:01284878c966508b6d6f1dbff9b6fa607bc062d8261c5c7253cb285b06422a7f", size = 142004 }, ] [[package]] -name = "attrs" -version = "25.3.0" +name = "html5lib" +version = "1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } +dependencies = [ + { name = "six" }, + { name = "webencodings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/b6/b55c3f49042f1df3dcd422b7f224f939892ee94f22abcf503a9b7339eaf2/html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f", size = 272215 } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, + { url = "https://files.pythonhosted.org/packages/6c/dd/a834df6482147d48e225a49515aabc28974ad5a4ca3215c18a882565b028/html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d", size = 112173 }, ] [[package]] -name = "azure-core" -version = "1.38.0" +name = "idna" +version = "3.11" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dc/1b/e503e08e755ea94e7d3419c9242315f888fc664211c90d032e40479022bf/azure_core-1.38.0.tar.gz", hash = "sha256:8194d2682245a3e4e3151a667c686464c3786fed7918b394d035bdcd61bb5993", size = 363033, upload-time = "2026-01-12T17:03:05.535Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/d8/b8fcba9464f02b121f39de2db2bf57f0b216fe11d014513d666e8634380d/azure_core-1.38.0-py3-none-any.whl", hash = "sha256:ab0c9b2cd71fecb1842d52c965c95285d3cfb38902f6766e4a471f1cd8905335", size = 217825, upload-time = "2026-01-12T17:03:07.291Z" }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 }, ] [[package]] -name = "azure-datalake-store" -version = "0.0.53" +name = "importlib-metadata" +version = "8.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation == 'PyPy'" }, - { name = "cffi", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, - { name = "msal" }, - { name = "requests" }, + { name = "zipp" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/ff/61369d06422b5ac48067215ff404841342651b14a89b46c8d8e1507c8f17/azure-datalake-store-0.0.53.tar.gz", hash = "sha256:05b6de62ee3f2a0a6e6941e6933b792b800c3e7f6ffce2fc324bc19875757393", size = 71430, upload-time = "2023-05-10T21:17:05.665Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107 } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/2a/75f56b14f115189155cf12e46b366ad1fe3357af5a1a7c09f7446662d617/azure_datalake_store-0.0.53-py2.py3-none-any.whl", hash = "sha256:a30c902a6e360aa47d7f69f086b426729784e71c536f330b691647a51dc42b2b", size = 55308, upload-time = "2023-05-10T21:17:02.629Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865 }, ] [[package]] -name = "azure-identity" -version = "1.24.0" +name = "iniconfig" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "azure-core" }, - { name = "cryptography" }, - { name = "msal" }, - { name = "msal-extensions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b5/44/f3ee20bacb220b6b4a2b0a6cf7e742eecb383a5ccf604dd79ec27c286b7e/azure_identity-1.24.0.tar.gz", hash = "sha256:6c3a40b2a70af831e920b89e6421e8dcd4af78a0cb38b9642d86c67643d4930c", size = 271630, upload-time = "2025-08-07T22:27:36.258Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/74/17428cb429e8d52f6d0d69ed685f4760a545cb0156594963a9337b53b6c9/azure_identity-1.24.0-py3-none-any.whl", hash = "sha256:9e04997cde0ab02ed66422c74748548e620b7b29361c72ce622acab0267ff7c4", size = 187890, upload-time = "2025-08-07T22:27:38.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484 }, ] [[package]] -name = "azure-storage-blob" -version = "12.26.0" +name = "jinja2" +version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-core" }, - { name = "cryptography" }, - { name = "isodate" }, - { name = "typing-extensions" }, + { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/95/3e3414491ce45025a1cde107b6ae72bf72049e6021597c201cd6a3029b9a/azure_storage_blob-12.26.0.tar.gz", hash = "sha256:5dd7d7824224f7de00bfeb032753601c982655173061e242f13be6e26d78d71f", size = 583332, upload-time = "2025-07-16T21:34:07.644Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/64/63dbfdd83b31200ac58820a7951ddfdeed1fbee9285b0f3eae12d1357155/azure_storage_blob-12.26.0-py3-none-any.whl", hash = "sha256:8c5631b8b22b4f53ec5fff2f3bededf34cfef111e2af613ad42c9e6de00a77fe", size = 412907, upload-time = "2025-07-16T21:34:09.367Z" }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, ] [[package]] -name = "babel" -version = "2.17.0" +name = "json5" +version = "0.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +sdist = { url = "https://files.pythonhosted.org/packages/77/e8/a3f261a66e4663f22700bc8a17c08cb83e91fbf086726e7a228398968981/json5-0.13.0.tar.gz", hash = "sha256:b1edf8d487721c0bf64d83c28e91280781f6e21f4a797d3261c7c828d4c165bf", size = 52441 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, + { url = "https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl", hash = "sha256:9a08e1dd65f6a4d4c6fa82d216cf2477349ec2346a38fd70cc11d2557499fbcc", size = 36163 }, ] [[package]] -name = "beautifulsoup4" -version = "4.13.4" +name = "jsonschema" +version = "4.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "soupsieve" }, - { name = "typing-extensions" }, + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067, upload-time = "2025-04-15T17:05:13.836Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583 } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285, upload-time = "2025-04-15T17:05:12.221Z" }, + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630 }, ] [[package]] -name = "bleach" -version = "6.2.0" +name = "jsonschema-specifications" +version = "2025.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "webencodings" }, + { name = "referencing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/9a/0e33f5054c54d349ea62c277191c020c2d6ef1d65ab2cb1993f91ec846d1/bleach-6.2.0.tar.gz", hash = "sha256:123e894118b8a599fd80d3ec1a6d4cc7ce4e5882b1317a7e1ba69b56e95f991f", size = 203083, upload-time = "2024-10-29T18:30:40.477Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/55/96142937f66150805c25c4d0f31ee4132fd33497753400734f9dfdcbdc66/bleach-6.2.0-py3-none-any.whl", hash = "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e", size = 163406, upload-time = "2024-10-29T18:30:38.186Z" }, + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, ] -[package.optional-dependencies] -css = [ - { name = "tinycss2" }, +[[package]] +name = "markdown" +version = "3.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180 }, ] [[package]] -name = "boto3" -version = "1.39.15" +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615 }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020 }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332 }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947 }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962 }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760 }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529 }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015 }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540 }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105 }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906 }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622 }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029 }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374 }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980 }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990 }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784 }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588 }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041 }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658 }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066 }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639 }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569 }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284 }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801 }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769 }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642 }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619 }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029 }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408 }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005 }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048 }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821 }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606 }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043 }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747 }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341 }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073 }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661 }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069 }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670 }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598 }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261 }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835 }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733 }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672 }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819 }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426 }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 }, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354 }, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "botocore" }, - { name = "jmespath" }, - { name = "s3transfer" }, + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ghp-import" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mergedeep" }, + { name = "mkdocs-get-deps" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/65/ddd4f52d138e52c1345c2d2421281a98449a6e4365290477befe06fa649a/boto3-1.39.15.tar.gz", hash = "sha256:b4483625f0d8c35045254dee46cd3c851bbc0450814f20b9b25bee1b5c0d8409", size = 111856, upload-time = "2025-07-28T19:56:49.504Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159 } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/c5/27f50a31317041dc3ad79d62f37d5fcfb3f349c2fba8ea3e81de169db870/boto3-1.39.15-py3-none-any.whl", hash = "sha256:38fc54576b925af0075636752de9974e172c8a2cf7133400e3e09b150d20fb6a", size = 139901, upload-time = "2025-07-28T19:56:47.381Z" }, + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451 }, ] [[package]] -name = "botocore" -version = "1.39.15" +name = "mkdocs-autorefs" +version = "1.4.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jmespath" }, - { name = "python-dateutil" }, - { name = "urllib3" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mkdocs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2f/e2/8cd9560e7e44cf977dc0cc2e48da7634e78b7104ae6e47f4e1dfc1093965/botocore-1.39.15.tar.gz", hash = "sha256:2aa29a717f14f8c7ca058c2e297aaed0aa10ecea24b91514eee802814d1b7600", size = 14237556, upload-time = "2025-07-28T19:56:39.397Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/c0/f641843de3f612a6b48253f39244165acff36657a91cc903633d456ae1ac/mkdocs_autorefs-1.4.4.tar.gz", hash = "sha256:d54a284f27a7346b9c38f1f852177940c222da508e66edc816a0fa55fc6da197", size = 56588 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/6e/f25b8633e7ab2008de4c27466c9bc39e32dc73816619ffebbea12936135a/botocore-1.39.15-py3-none-any.whl", hash = "sha256:eb9cfe918ebfbfb8654e1b153b29f0c129d586d2c0d7fb4032731d49baf04cff", size = 13894884, upload-time = "2025-07-28T19:56:33.715Z" }, + { url = "https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl", hash = "sha256:834ef5408d827071ad1bc69e0f39704fa34c7fc05bc8e1c72b227dfdc5c76089", size = 25530 }, ] [[package]] -name = "build" -version = "1.2.2.post1" +name = "mkdocs-get-deps" +version = "0.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "os_name == 'nt'" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10.2'" }, - { name = "packaging" }, - { name = "pyproject-hooks" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "mergedeep" }, + { name = "platformdirs" }, + { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/46/aeab111f8e06793e4f0e421fcad593d547fb8313b50990f31681ee2fb1ad/build-1.2.2.post1.tar.gz", hash = "sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7", size = 46701, upload-time = "2024-10-06T17:22:25.251Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239 } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/c2/80633736cd183ee4a62107413def345f7e6e3c01563dbca1417363cf957e/build-1.2.2.post1-py3-none-any.whl", hash = "sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5", size = 22950, upload-time = "2024-10-06T17:22:23.299Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521 }, ] [[package]] -name = "cachetools" -version = "6.1.0" +name = "mkdocs-material" +version = "9.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/89/817ad5d0411f136c484d535952aef74af9b25e0d99e90cdffbe121e6d628/cachetools-6.1.0.tar.gz", hash = "sha256:b4c4f404392848db3ce7aac34950d17be4d864da4b8b66911008e430bc544587", size = 30714, upload-time = "2025-06-16T18:51:03.07Z" } +dependencies = [ + { name = "babel" }, + { name = "backrefs" }, + { name = "colorama" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "mkdocs" }, + { name = "mkdocs-material-extensions" }, + { name = "paginate" }, + { name = "pygments" }, + { name = "pymdown-extensions" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/27/e2/2ffc356cd72f1473d07c7719d82a8f2cbd261666828614ecb95b12169f41/mkdocs_material-9.7.1.tar.gz", hash = "sha256:89601b8f2c3e6c6ee0a918cc3566cb201d40bf37c3cd3c2067e26fadb8cce2b8", size = 4094392 } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/f0/2ef431fe4141f5e334759d73e81120492b23b2824336883a91ac04ba710b/cachetools-6.1.0-py3-none-any.whl", hash = "sha256:1c7bb3cf9193deaf3508b7c5f2a79986c13ea38965c5adcff1f84519cf39163e", size = 11189, upload-time = "2025-06-16T18:51:01.514Z" }, + { url = "https://files.pythonhosted.org/packages/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-py3-none-any.whl", hash = "sha256:3f6100937d7d731f87f1e3e3b021c97f7239666b9ba1151ab476cabb96c60d5c", size = 9297166 }, ] [[package]] -name = "certifi" -version = "2025.7.14" +name = "mkdocs-material-extensions" +version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981, upload-time = "2025-07-14T03:29:28.449Z" } +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722, upload-time = "2025-07-14T03:29:26.863Z" }, + { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728 }, ] [[package]] -name = "cffi" -version = "1.17.1" +name = "mkdocstrings" +version = "1.0.3" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12' and platform_python_implementation == 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", - "python_full_version < '3.11' and platform_python_implementation == 'PyPy'", -] dependencies = [ - { name = "pycparser", marker = "platform_python_implementation == 'PyPy'" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mkdocs" }, + { name = "mkdocs-autorefs" }, + { name = "pymdown-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/62/0dfc5719514115bf1781f44b1d7f2a0923fcc01e9c5d7990e48a05c9ae5d/mkdocstrings-1.0.3.tar.gz", hash = "sha256:ab670f55040722b49bb45865b2e93b824450fb4aef638b00d7acb493a9020434", size = 100946 } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191, upload-time = "2024-09-04T20:43:30.027Z" }, - { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592, upload-time = "2024-09-04T20:43:32.108Z" }, - { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024, upload-time = "2024-09-04T20:43:34.186Z" }, - { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188, upload-time = "2024-09-04T20:43:36.286Z" }, - { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571, upload-time = "2024-09-04T20:43:38.586Z" }, - { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687, upload-time = "2024-09-04T20:43:40.084Z" }, - { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211, upload-time = "2024-09-04T20:43:41.526Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325, upload-time = "2024-09-04T20:43:43.117Z" }, - { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784, upload-time = "2024-09-04T20:43:45.256Z" }, - { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564, upload-time = "2024-09-04T20:43:46.779Z" }, - { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804, upload-time = "2024-09-04T20:43:48.186Z" }, - { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299, upload-time = "2024-09-04T20:43:49.812Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload-time = "2024-09-04T20:43:51.124Z" }, - { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload-time = "2024-09-04T20:43:52.872Z" }, - { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload-time = "2024-09-04T20:43:56.123Z" }, - { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload-time = "2024-09-04T20:43:57.891Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload-time = "2024-09-04T20:44:00.18Z" }, - { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload-time = "2024-09-04T20:44:01.585Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload-time = "2024-09-04T20:44:03.467Z" }, - { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload-time = "2024-09-04T20:44:05.023Z" }, - { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload-time = "2024-09-04T20:44:06.444Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload-time = "2024-09-04T20:44:08.206Z" }, - { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload-time = "2024-09-04T20:44:09.481Z" }, - { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload-time = "2024-09-04T20:44:10.873Z" }, - { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" }, - { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, - { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, - { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, - { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, - { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" }, - { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989, upload-time = "2024-09-04T20:44:28.956Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802, upload-time = "2024-09-04T20:44:30.289Z" }, - { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload-time = "2024-09-04T20:44:32.01Z" }, - { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload-time = "2024-09-04T20:44:33.606Z" }, - { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload-time = "2024-09-04T20:44:35.191Z" }, - { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload-time = "2024-09-04T20:44:36.743Z" }, - { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload-time = "2024-09-04T20:44:38.492Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload-time = "2024-09-04T20:44:40.046Z" }, - { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload-time = "2024-09-04T20:44:41.616Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload-time = "2024-09-04T20:44:43.733Z" }, - { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" }, + { url = "https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl", hash = "sha256:0d66d18430c2201dc7fe85134277382baaa15e6b30979f3f3bdbabd6dbdb6046", size = 35523 }, ] [[package]] -name = "cffi" -version = "2.0.0" +name = "mkdocstrings-python" +version = "2.0.2" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12' and platform_python_implementation != 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", - "python_full_version < '3.11' and platform_python_implementation != 'PyPy'", -] dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy' and platform_python_implementation != 'PyPy'" }, + { name = "griffe" }, + { name = "mkdocs-autorefs" }, + { name = "mkdocstrings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/84/78243847ad9d5c21d30a2842720425b17e880d99dfe824dee11d6b2149b4/mkdocstrings_python-2.0.2.tar.gz", hash = "sha256:4a32ccfc4b8d29639864698e81cfeb04137bce76bb9f3c251040f55d4b6e1ad8", size = 199124 } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, - { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, - { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, - { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, - { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, - { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, - { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, - { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, - { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, - { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, - { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, - { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, - { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, - { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, - { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, - { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, - { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, - { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, - { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, - { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, - { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, - { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, + { url = "https://files.pythonhosted.org/packages/f3/31/7ee938abbde2322e553a2cb5f604cdd1e4728e08bba39c7ee6fae9af840b/mkdocstrings_python-2.0.2-py3-none-any.whl", hash = "sha256:31241c0f43d85a69306d704d5725786015510ea3f3c4bdfdb5a5731d83cdc2b0", size = 104900 }, ] [[package]] -name = "cfgv" -version = "3.4.0" +name = "nodejs-wheel-binaries" +version = "24.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/f1/73182280e2c05f49a7c2c8dbd46144efe3f74f03f798fb90da67b4a93bbf/nodejs_wheel_binaries-24.13.0.tar.gz", hash = "sha256:766aed076e900061b83d3e76ad48bfec32a035ef0d41bd09c55e832eb93ef7a4", size = 8056 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, + { url = "https://files.pythonhosted.org/packages/c4/dc/4d7548aa74a5b446d093f03aff4fb236b570959d793f21c9c42ab6ad870a/nodejs_wheel_binaries-24.13.0-py2.py3-none-macosx_13_0_arm64.whl", hash = "sha256:356654baa37bfd894e447e7e00268db403ea1d223863963459a0fbcaaa1d9d48", size = 55133268 }, + { url = "https://files.pythonhosted.org/packages/24/8a/8a4454d28339487240dd2232f42f1090e4a58544c581792d427f6239798c/nodejs_wheel_binaries-24.13.0-py2.py3-none-macosx_13_0_x86_64.whl", hash = "sha256:92fdef7376120e575f8b397789bafcb13bbd22a1b4d21b060d200b14910f22a5", size = 55314800 }, + { url = "https://files.pythonhosted.org/packages/e7/fb/46c600fcc748bd13bc536a735f11532a003b14f5c4dfd6865f5911672175/nodejs_wheel_binaries-24.13.0-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:3f619ac140e039ecd25f2f71d6e83ad1414017a24608531851b7c31dc140cdfd", size = 59666320 }, + { url = "https://files.pythonhosted.org/packages/85/47/d48f11fc5d1541ace5d806c62a45738a1db9ce33e85a06fe4cd3d9ce83f6/nodejs_wheel_binaries-24.13.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:dfb31ebc2c129538192ddb5bedd3d63d6de5d271437cd39ea26bf3fe229ba430", size = 60162447 }, + { url = "https://files.pythonhosted.org/packages/b1/74/d285c579ae8157c925b577dde429543963b845e69cd006549e062d1cf5b6/nodejs_wheel_binaries-24.13.0-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fdd720d7b378d5bb9b2710457bbc880d4c4d1270a94f13fbe257198ac707f358", size = 61659994 }, + { url = "https://files.pythonhosted.org/packages/ba/97/88b4254a2ff93ed2eaed725f77b7d3d2d8d7973bf134359ce786db894faf/nodejs_wheel_binaries-24.13.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9ad6383613f3485a75b054647a09f1cd56d12380d7459184eebcf4a5d403f35c", size = 62244373 }, + { url = "https://files.pythonhosted.org/packages/4e/c3/0e13a3da78f08cb58650971a6957ac7bfef84164b405176e53ab1e3584e2/nodejs_wheel_binaries-24.13.0-py2.py3-none-win_amd64.whl", hash = "sha256:605be4763e3ef427a3385a55da5a1bcf0a659aa2716eebbf23f332926d7e5f23", size = 41345528 }, + { url = "https://files.pythonhosted.org/packages/a3/f1/0578d65b4e3dc572967fd702221ea1f42e1e60accfb6b0dd8d8f15410139/nodejs_wheel_binaries-24.13.0-py2.py3-none-win_arm64.whl", hash = "sha256:2e3431d869d6b2dbeef1d469ad0090babbdcc8baaa72c01dd3cc2c6121c96af5", size = 39054688 }, ] [[package]] -name = "charset-normalizer" -version = "3.4.2" +name = "obspec" +version = "0.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/94/7a9ad6927cac6ec7680e11772fb692145a05a93bafd80b84f6f0ef12f4e7/obspec-0.1.0.tar.gz", hash = "sha256:b189781a53f82ef8d6abf0c9e77fd4c46ac9f244d5a91eb35ee61c2e2b204a4a", size = 117254 } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, - { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, - { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, - { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, - { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, - { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, - { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, - { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, - { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, - { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, - { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, - { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, - { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, - { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, - { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, - { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, - { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, - { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, - { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, - { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, - { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, - { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, - { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, - { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, - { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, - { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, - { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, - { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, - { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, - { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, - { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, - { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, - { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, - { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, - { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, - { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, - { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, - { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, - { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, - { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, - { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, - { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/bb/69/96feeac84ce0b871567225c78515f3b557c023e72ed9b4f1833f3662bd6b/obspec-0.1.0-py3-none-any.whl", hash = "sha256:307f0fa2c2998b324ecf0eed6a2a89049a4c40c9b1fa2b5e1af28f0ee72136b3", size = 15231 }, ] [[package]] -name = "click" -version = "8.2.1" +name = "obstore" +version = "0.8.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/a3/8c/9ec984edd0f3b72226adfaa19b1c61b15823b35b52f311ca4af36d009d15/obstore-0.8.2.tar.gz", hash = "sha256:a467bc4e97169e2ba749981b4fd0936015428d9b8f3fb83a5528536b1b6f377f", size = 168852 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/dc/60fefbb5736e69eab56657bca04ca64dc07fdeccb3814164a31b62ad066b/obstore-0.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:bb70ce297a47392b1d9a3e310f18d59cd5ebbb9453428210fef02ed60e4d75d1", size = 3612955 }, + { url = "https://files.pythonhosted.org/packages/d2/8b/844e8f382e5a12b8a3796a05d76a03e12c7aedc13d6900419e39207d7868/obstore-0.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1619bf618428abf1f607e0b219b2e230a966dcf697b717deccfa0983dd91f646", size = 3346564 }, + { url = "https://files.pythonhosted.org/packages/89/73/8537f99e09a38a54a6a15ede907aa25d4da089f767a808f0b2edd9c03cec/obstore-0.8.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a4605c3ed7c9515aeb4c619b5f7f2c9986ed4a79fe6045e536b5e59b804b1476", size = 3460809 }, + { url = "https://files.pythonhosted.org/packages/b4/99/7714dec721e43f521d6325a82303a002cddad089437640f92542b84e9cc8/obstore-0.8.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce42670417876dd8668cbb8659e860e9725e5f26bbc86449fd259970e2dd9d18", size = 3692081 }, + { url = "https://files.pythonhosted.org/packages/ec/bd/4ac4175fe95a24c220a96021c25c432bcc0c0212f618be0737184eebbaad/obstore-0.8.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a3e893b2a06585f651c541c1972fe1e3bf999ae2a5fda052ee55eb7e6516f5", size = 3957466 }, + { url = "https://files.pythonhosted.org/packages/4e/04/caa288fb735484fc5cb019bdf3d896eaccfae0ac4622e520d05692c46790/obstore-0.8.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08462b32f95a9948ed56ed63e88406e2e5a4cae1fde198f9682e0fb8487100ed", size = 3951293 }, + { url = "https://files.pythonhosted.org/packages/44/2f/d380239da2d6a1fda82e17df5dae600a404e8a93a065784518ff8325d5f6/obstore-0.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a0bf7763292a8fc47d01cd66e6f19002c5c6ad4b3ed4e6b2729f5e190fa8a0d", size = 3766199 }, + { url = "https://files.pythonhosted.org/packages/28/41/d391be069d3da82969b54266948b2582aeca5dd735abeda4d63dba36e07b/obstore-0.8.2-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:bcd47f8126cb192cbe86942b8f73b1c45a651ce7e14c9a82c5641dfbf8be7603", size = 3529678 }, + { url = "https://files.pythonhosted.org/packages/b9/4c/4862fdd1a3abde459ee8eea699b1797df638a460af235b18ca82c8fffb72/obstore-0.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:57eda9fd8c757c3b4fe36cf3918d7e589cc1286591295cc10b34122fa36dd3fd", size = 3698079 }, + { url = "https://files.pythonhosted.org/packages/68/ca/014e747bc53b570059c27e3565b2316fbe5c107d4134551f4cd3e24aa667/obstore-0.8.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ea44442aad8992166baa69f5069750979e4c5d9ffce772e61565945eea5774b9", size = 3687154 }, + { url = "https://files.pythonhosted.org/packages/6f/89/6db5f8edd93028e5b8bfbeee15e6bd3e56f72106107d31cb208b57659de4/obstore-0.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:41496a3ab8527402db4142aaaf0d42df9d7d354b13ba10d9c33e0e48dd49dd96", size = 3773444 }, + { url = "https://files.pythonhosted.org/packages/26/e5/c9e2cc540689c873beb61246e1615d6e38301e6a34dec424f5a5c63c1afd/obstore-0.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:43da209803f052df96c7c3cbec512d310982efd2407e4a435632841a51143170", size = 3939315 }, + { url = "https://files.pythonhosted.org/packages/4d/c9/bb53280ca50103c1ffda373cdc9b0f835431060039c2897cbc87ddd92e42/obstore-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:1836f5dcd49f9f2950c75889ab5c51fb290d3ea93cdc39a514541e0be3af016e", size = 3978234 }, + { url = "https://files.pythonhosted.org/packages/f0/5d/8c3316cc958d386d5e6ab03e9db9ddc27f8e2141cee4a6777ae5b92f3aac/obstore-0.8.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:212f033e53fe6e53d64957923c5c88949a400e9027f7038c705ec2e9038be563", size = 3612027 }, + { url = "https://files.pythonhosted.org/packages/ea/4d/699359774ce6330130536d008bfc32827fab0c25a00238d015a5974a3d1d/obstore-0.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bee21fa4ba148d08fa90e47a96df11161661ed31e09c056a373cb2154b0f2852", size = 3344686 }, + { url = "https://files.pythonhosted.org/packages/82/37/55437341f10512906e02fd9fa69a8a95ad3f2f6a916d3233fda01763d110/obstore-0.8.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4c66594b59832ff1ced4c72575d9beb8b5f9b4e404ac1150a42bfb226617fd50", size = 3459860 }, + { url = "https://files.pythonhosted.org/packages/7a/51/4245a616c94ee4851965e33f7a563ab4090cc81f52cc73227ff9ceca2e46/obstore-0.8.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:089f33af5c2fe132d00214a0c1f40601b28f23a38e24ef9f79fb0576f2730b74", size = 3691648 }, + { url = "https://files.pythonhosted.org/packages/4e/f1/4e2fb24171e3ca3641a4653f006be826e7e17634b11688a5190553b00b83/obstore-0.8.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d87f658dfd340d5d9ea2d86a7c90d44da77a0db9e00c034367dca335735110cf", size = 3956867 }, + { url = "https://files.pythonhosted.org/packages/42/f5/b703115361c798c9c1744e1e700d5908d904a8c2e2bd38bec759c9ffb469/obstore-0.8.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e2e4fa92828c4fbc2d487f3da2d3588701a1b67d9f6ca3c97cc2afc912e9c63", size = 3950599 }, + { url = "https://files.pythonhosted.org/packages/53/20/08c6dc0f20c1394e2324b9344838e4e7af770cdcb52c30757a475f50daeb/obstore-0.8.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab440e89c5c37a8ec230857dd65147d4b923e0cada33297135d05e0f937d696a", size = 3765865 }, + { url = "https://files.pythonhosted.org/packages/77/20/77907765e29b2eba6bd8821872284d91170d7084f670855b2dfcb249ea14/obstore-0.8.2-cp313-cp313-manylinux_2_24_aarch64.whl", hash = "sha256:b9beed107c5c9cd995d4a73263861fcfbc414d58773ed65c14f80eb18258a932", size = 3529807 }, + { url = "https://files.pythonhosted.org/packages/a5/f5/f629d39cc30d050f52b1bf927e4d65c1cc7d7ffbb8a635cd546b5c5219a0/obstore-0.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b75b4e7746292c785e31edcd5aadc8b758238372a19d4c5e394db5c305d7d175", size = 3693629 }, + { url = "https://files.pythonhosted.org/packages/30/ff/106763fd10f2a1cb47f2ef1162293c78ad52f4e73223d8d43fc6b755445d/obstore-0.8.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:f33e6c366869d05ab0b7f12efe63269e631c5450d95d6b4ba4c5faf63f69de70", size = 3686176 }, + { url = "https://files.pythonhosted.org/packages/ce/0c/d2ccb6f32feeca906d5a7c4255340df5262af8838441ca06c9e4e37b67d5/obstore-0.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:12c885a9ce5ceb09d13cc186586c0c10b62597eff21b985f6ce8ff9dab963ad3", size = 3773081 }, + { url = "https://files.pythonhosted.org/packages/fa/79/40d1cc504cefc89c9b3dd8874287f3fddc7d963a8748d6dffc5880222013/obstore-0.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4accc883b93349a81c9931e15dd318cc703b02bbef2805d964724c73d006d00e", size = 3938589 }, + { url = "https://files.pythonhosted.org/packages/14/dd/916c6777222db3271e9fb3cf9a97ed92b3a9b3e465bdeec96de9ab809d53/obstore-0.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:ec850adf9980e5788a826ccfd5819989724e2a2f712bfa3258e85966c8d9981e", size = 3977768 }, + { url = "https://files.pythonhosted.org/packages/f1/61/66f8dc98bbf5613bbfe5bf21747b4c8091442977f4bd897945895ab7325c/obstore-0.8.2-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:1431e40e9bb4773a261e51b192ea6489d0799b9d4d7dbdf175cdf813eb8c0503", size = 3623364 }, + { url = "https://files.pythonhosted.org/packages/1a/66/6d527b3027e42f625c8fc816ac7d19b0d6228f95bfe7666e4d6b081d2348/obstore-0.8.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ddb39d4da303f50b959da000aa42734f6da7ac0cc0be2d5a7838b62c97055bb9", size = 3347764 }, + { url = "https://files.pythonhosted.org/packages/0d/79/c00103302b620192ea447a948921ad3fed031ce3d19e989f038e1183f607/obstore-0.8.2-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e01f4e13783db453e17e005a4a3ceff09c41c262e44649ba169d253098c775e8", size = 3460981 }, + { url = "https://files.pythonhosted.org/packages/3d/d9/bfe4ed4b1aebc45b56644dd5b943cf8e1673505cccb352e66878a457e807/obstore-0.8.2-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df0fc2d0bc17caff9b538564ddc26d7616f7e8b7c65b1a3c90b5048a8ad2e797", size = 3692711 }, + { url = "https://files.pythonhosted.org/packages/13/47/cd6c2cbb18e1f40c77e7957a4a03d2d83f1859a2e876a408f1ece81cad4c/obstore-0.8.2-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e439d06c99a140348f046c9f598ee349cc2dcd9105c15540a4b231f9cc48bbae", size = 3958362 }, + { url = "https://files.pythonhosted.org/packages/3d/ea/5ee82bf23abd71c7d6a3f2d008197ae8f8f569d41314c26a8f75318245be/obstore-0.8.2-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e37d9046669fcc59522d0faf1d105fcbfd09c84cccaaa1e809227d8e030f32c", size = 3957082 }, + { url = "https://files.pythonhosted.org/packages/cb/ee/46650405e50fdaa8d95f30375491f9c91fac9517980e8a28a4a6af66927f/obstore-0.8.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2646fdcc4bbe92dc2bb5bcdff15574da1211f5806c002b66d514cee2a23c7cb8", size = 3775539 }, + { url = "https://files.pythonhosted.org/packages/35/d6/348a7ebebe2ca3d94dfc75344ea19675ae45472823e372c1852844078307/obstore-0.8.2-cp314-cp314-manylinux_2_24_aarch64.whl", hash = "sha256:e31a7d37675056d93dfc244605089dee67f5bba30f37c88436623c8c5ad9ba9d", size = 3535048 }, + { url = "https://files.pythonhosted.org/packages/41/07/b7a16cc0da91a4b902d47880ad24016abfe7880c63f7cdafda45d89a2f91/obstore-0.8.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:656313dd8170dde0f0cd471433283337a63912e8e790a121f7cc7639c83e3816", size = 3699035 }, + { url = "https://files.pythonhosted.org/packages/7f/74/3269a3a58347e0b019742d888612c4b765293c9c75efa44e144b1e884c0d/obstore-0.8.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:329038c9645d6d1741e77fe1a53e28a14b1a5c1461cfe4086082ad39ebabf981", size = 3687307 }, + { url = "https://files.pythonhosted.org/packages/01/f9/4fd4819ad6a49d2f462a45be453561f4caebded0dc40112deeffc34b89b1/obstore-0.8.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:1e4df99b369790c97c752d126b286dc86484ea49bff5782843a265221406566f", size = 3776076 }, + { url = "https://files.pythonhosted.org/packages/14/dd/7c4f958fa0b9fc4778fb3d232e38b37db8c6b260f641022fbba48b049d7e/obstore-0.8.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9e1c65c65e20cc990414a8a9af88209b1bbc0dd9521b5f6b0293c60e19439bb7", size = 3947445 }, ] [[package]] -name = "click-plugins" -version = "1.1.1.2" +name = "packaging" +version = "26.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/a4/34847b59150da33690a36da3681d6bbc2ec14ee9a846bc30a6746e5984e4/click_plugins-1.1.1.2.tar.gz", hash = "sha256:d7af3984a99d243c131aa1a828331e7630f4a88a9741fd05c927b204bcf92261", size = 8343, upload-time = "2025-06-25T00:47:37.555Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/9a/2abecb28ae875e39c8cad711eb1186d8d14eab564705325e77e4e6ab9ae5/click_plugins-1.1.1.2-py2.py3-none-any.whl", hash = "sha256:008d65743833ffc1f5417bf0e78e8d2c23aab04d9745ba817bd3e71b0feb6aa6", size = 11051, upload-time = "2025-06-25T00:47:36.731Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366 }, ] [[package]] -name = "cligj" -version = "0.7.2" +name = "paginate" +version = "0.5.7" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ea/0d/837dbd5d8430fd0f01ed72c4cfb2f548180f4c68c635df84ce87956cff32/cligj-0.7.2.tar.gz", hash = "sha256:a4bc13d623356b373c2c27c53dbd9c68cae5d526270bfa71f6c6fa69669c6b27", size = 9803, upload-time = "2021-05-28T21:23:27.935Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252 } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/86/43fa9f15c5b9fb6e82620428827cd3c284aa933431405d1bcf5231ae3d3e/cligj-0.7.2-py3-none-any.whl", hash = "sha256:c1ca117dbce1fe20a5809dc96f01e1c2840f6dcc939b3ddbb1111bf330ba82df", size = 7069, upload-time = "2021-05-28T21:23:26.877Z" }, + { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746 }, ] [[package]] -name = "cloudpickle" -version = "3.1.1" +name = "pathspec" +version = "1.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/39/069100b84d7418bc358d81669d5748efb14b9cceacd2f9c75f550424132f/cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64", size = 22113, upload-time = "2025-01-14T17:02:05.085Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/e8/64c37fadfc2816a7701fa8a6ed8d87327c7d54eacfbfb6edab14a2f2be75/cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e", size = 20992, upload-time = "2025-01-14T17:02:02.417Z" }, + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206 }, ] [[package]] -name = "codespell" -version = "2.2.6" +name = "platformdirs" +version = "4.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e1/97/df3e00b4d795c96233e35d269c211131c5572503d2270afb6fed7d859cc2/codespell-2.2.6.tar.gz", hash = "sha256:a8c65d8eb3faa03deabab6b3bbe798bea72e1799c7e9e955d57eca4096abcff9", size = 300968, upload-time = "2023-10-02T19:35:46.513Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/474d0a8508029286b905622e6929470fb84337cfa08f9d09fbb624515249/platformdirs-4.6.0.tar.gz", hash = "sha256:4a13c2db1071e5846c3b3e04e5b095c0de36b2a24be9a3bc0145ca66fce4e328", size = 23433 } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/e0/5437cc96b74467c4df6e13b7128cc482c48bb43146fb4c11cf2bcd604e1f/codespell-2.2.6-py3-none-any.whl", hash = "sha256:9ee9a3e5df0990604013ac2a9f22fa8e57669c827124a2e961fe8a1da4cacc07", size = 301382, upload-time = "2023-10-02T19:35:44.262Z" }, + { url = "https://files.pythonhosted.org/packages/da/10/1b0dcf51427326f70e50d98df21b18c228117a743a1fc515a42f8dc7d342/platformdirs-4.6.0-py3-none-any.whl", hash = "sha256:dd7f808d828e1764a22ebff09e60f175ee3c41876606a6132a688d809c7c9c73", size = 19549 }, ] [[package]] -name = "colorama" -version = "0.4.6" +name = "pluggy" +version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, ] [[package]] -name = "comm" -version = "0.2.3" +name = "prek" +version = "0.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/f5/ee52def928dd1355c20bcfcf765e1e61434635c33f3075e848e7b83a157b/prek-0.3.2.tar.gz", hash = "sha256:dce0074ff1a21290748ca567b4bda7553ee305a8c7b14d737e6c58364a499364", size = 334229 } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, + { url = "https://files.pythonhosted.org/packages/76/69/70a5fc881290a63910494df2677c0fb241d27cfaa435bbcd0de5cd2e2443/prek-0.3.2-py3-none-linux_armv6l.whl", hash = "sha256:4f352f9c3fc98aeed4c8b2ec4dbf16fc386e45eea163c44d67e5571489bd8e6f", size = 4614960 }, + { url = "https://files.pythonhosted.org/packages/c0/15/a82d5d32a2207ccae5d86ea9e44f2b93531ed000faf83a253e8d1108e026/prek-0.3.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4a000cfbc3a6ec7d424f8be3c3e69ccd595448197f92daac8652382d0acc2593", size = 4622889 }, + { url = "https://files.pythonhosted.org/packages/89/75/ea833b58a12741397017baef9b66a6e443bfa8286ecbd645d14111446280/prek-0.3.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5436bdc2702cbd7bcf9e355564ae66f8131211e65fefae54665a94a07c3d450a", size = 4239653 }, + { url = "https://files.pythonhosted.org/packages/10/b4/d9c3885987afac6e20df4cb7db14e3b0d5a08a77ae4916488254ebac4d0b/prek-0.3.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:0161b5f584f9e7f416d6cf40a17b98f17953050ff8d8350ec60f20fe966b86b6", size = 4595101 }, + { url = "https://files.pythonhosted.org/packages/21/a6/1a06473ed83dbc898de22838abdb13954e2583ce229f857f61828384634c/prek-0.3.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e641e8533bca38797eebb49aa89ed0e8db0e61225943b27008c257e3af4d631", size = 4521978 }, + { url = "https://files.pythonhosted.org/packages/0c/5e/c38390d5612e6d86b32151c1d2fdab74a57913473193591f0eb00c894c21/prek-0.3.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfca1810d49d3f9ef37599c958c4e716bc19a1d78a7e88cbdcb332e0b008994f", size = 4829108 }, + { url = "https://files.pythonhosted.org/packages/80/a6/cecce2ab623747ff65ed990bb0d95fa38449ee19b348234862acf9392fff/prek-0.3.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d69d754299a95a85dc20196f633232f306bee7e7c8cba61791f49ce70404ec", size = 5357520 }, + { url = "https://files.pythonhosted.org/packages/a5/18/d6bcb29501514023c76d55d5cd03bdbc037737c8de8b6bc41cdebfb1682c/prek-0.3.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:539dcb90ad9b20837968539855df6a29493b328a1ae87641560768eed4f313b0", size = 4852635 }, + { url = "https://files.pythonhosted.org/packages/1b/0a/ae46f34ba27ba87aea5c9ad4ac9cd3e07e014fd5079ae079c84198f62118/prek-0.3.2-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:1998db3d0cbe243984736c82232be51318f9192e2433919a6b1c5790f600b5fd", size = 4599484 }, + { url = "https://files.pythonhosted.org/packages/1a/a9/73bfb5b3f7c3583f9b0d431924873928705cdef6abb3d0461c37254a681b/prek-0.3.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:07ab237a5415a3e8c0db54de9d63899bcd947624bdd8820d26f12e65f8d19eb7", size = 4657694 }, + { url = "https://files.pythonhosted.org/packages/a7/bc/0994bc176e1a80110fad3babce2c98b0ac4007630774c9e18fc200a34781/prek-0.3.2-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:0ced19701d69c14a08125f14a5dd03945982edf59e793c73a95caf4697a7ac30", size = 4509337 }, + { url = "https://files.pythonhosted.org/packages/f9/13/e73f85f65ba8f626468e5d1694ab3763111513da08e0074517f40238c061/prek-0.3.2-py3-none-musllinux_1_1_i686.whl", hash = "sha256:ffb28189f976fa111e770ee94e4f298add307714568fb7d610c8a7095cb1ce59", size = 4697350 }, + { url = "https://files.pythonhosted.org/packages/14/47/98c46dcd580305b9960252a4eb966f1a7b1035c55c363f378d85662ba400/prek-0.3.2-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f63134b3eea14421789a7335d86f99aee277cb520427196f2923b9260c60e5c5", size = 4955860 }, + { url = "https://files.pythonhosted.org/packages/73/42/1bb4bba3ff47897df11e9dfd774027cdfa135482c961a54e079af0faf45a/prek-0.3.2-py3-none-win32.whl", hash = "sha256:58c806bd1344becd480ef5a5ba348846cc000af0e1fbe854fef91181a2e06461", size = 4267619 }, + { url = "https://files.pythonhosted.org/packages/97/11/6665f47a7c350d83de17403c90bbf7a762ef50876ece456a86f64f46fbfb/prek-0.3.2-py3-none-win_amd64.whl", hash = "sha256:70114b48e9eb8048b2c11b4c7715ce618529c6af71acc84dd8877871a2ef71a6", size = 4624324 }, + { url = "https://files.pythonhosted.org/packages/22/e7/740997ca82574d03426f897fd88afe3fc8a7306b8c7ea342a8bc1c538488/prek-0.3.2-py3-none-win_arm64.whl", hash = "sha256:9144d176d0daa2469a25c303ef6f6fa95a8df015eb275232f5cb53551ecefef0", size = 4336008 }, ] [[package]] -name = "coverage" -version = "7.10.1" +name = "pygments" +version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/87/0e/66dbd4c6a7f0758a8d18044c048779ba21fb94856e1edcf764bd5403e710/coverage-7.10.1.tar.gz", hash = "sha256:ae2b4856f29ddfe827106794f3589949a57da6f0d38ab01e24ec35107979ba57", size = 819938, upload-time = "2025-07-27T14:13:39.045Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/e7/0f4e35a15361337529df88151bddcac8e8f6d6fd01da94a4b7588901c2fe/coverage-7.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1c86eb388bbd609d15560e7cc0eb936c102b6f43f31cf3e58b4fd9afe28e1372", size = 214627, upload-time = "2025-07-27T14:11:01.211Z" }, - { url = "https://files.pythonhosted.org/packages/e0/fd/17872e762c408362072c936dbf3ca28c67c609a1f5af434b1355edcb7e12/coverage-7.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6b4ba0f488c1bdb6bd9ba81da50715a372119785458831c73428a8566253b86b", size = 215015, upload-time = "2025-07-27T14:11:03.988Z" }, - { url = "https://files.pythonhosted.org/packages/54/50/c9d445ba38ee5f685f03876c0f8223469e2e46c5d3599594dca972b470c8/coverage-7.10.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:083442ecf97d434f0cb3b3e3676584443182653da08b42e965326ba12d6b5f2a", size = 241995, upload-time = "2025-07-27T14:11:05.983Z" }, - { url = "https://files.pythonhosted.org/packages/cc/83/4ae6e0f60376af33de543368394d21b9ac370dc86434039062ef171eebf8/coverage-7.10.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c1a40c486041006b135759f59189385da7c66d239bad897c994e18fd1d0c128f", size = 243253, upload-time = "2025-07-27T14:11:07.424Z" }, - { url = "https://files.pythonhosted.org/packages/49/90/17a4d9ac7171be364ce8c0bb2b6da05e618ebfe1f11238ad4f26c99f5467/coverage-7.10.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3beb76e20b28046989300c4ea81bf690df84ee98ade4dc0bbbf774a28eb98440", size = 245110, upload-time = "2025-07-27T14:11:09.152Z" }, - { url = "https://files.pythonhosted.org/packages/e1/f7/edc3f485d536ed417f3af2b4969582bcb5fab456241721825fa09354161e/coverage-7.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc265a7945e8d08da28999ad02b544963f813a00f3ed0a7a0ce4165fd77629f8", size = 243056, upload-time = "2025-07-27T14:11:10.586Z" }, - { url = "https://files.pythonhosted.org/packages/58/2c/c4c316a57718556b8d0cc8304437741c31b54a62934e7c8c551a7915c2f4/coverage-7.10.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:47c91f32ba4ac46f1e224a7ebf3f98b4b24335bad16137737fe71a5961a0665c", size = 241731, upload-time = "2025-07-27T14:11:12.145Z" }, - { url = "https://files.pythonhosted.org/packages/f7/93/c78e144c6f086043d0d7d9237c5b880e71ac672ed2712c6f8cca5544481f/coverage-7.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1a108dd78ed185020f66f131c60078f3fae3f61646c28c8bb4edd3fa121fc7fc", size = 242023, upload-time = "2025-07-27T14:11:13.573Z" }, - { url = "https://files.pythonhosted.org/packages/8f/e1/34e8505ca81fc144a612e1cc79fadd4a78f42e96723875f4e9f1f470437e/coverage-7.10.1-cp310-cp310-win32.whl", hash = "sha256:7092cc82382e634075cc0255b0b69cb7cada7c1f249070ace6a95cb0f13548ef", size = 217130, upload-time = "2025-07-27T14:11:15.11Z" }, - { url = "https://files.pythonhosted.org/packages/75/2b/82adfce6edffc13d804aee414e64c0469044234af9296e75f6d13f92f6a2/coverage-7.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:ac0c5bba938879c2fc0bc6c1b47311b5ad1212a9dcb8b40fe2c8110239b7faed", size = 218015, upload-time = "2025-07-27T14:11:16.836Z" }, - { url = "https://files.pythonhosted.org/packages/20/8e/ef088112bd1b26e2aa931ee186992b3e42c222c64f33e381432c8ee52aae/coverage-7.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b45e2f9d5b0b5c1977cb4feb5f594be60eb121106f8900348e29331f553a726f", size = 214747, upload-time = "2025-07-27T14:11:18.217Z" }, - { url = "https://files.pythonhosted.org/packages/2d/76/a1e46f3c6e0897758eb43af88bb3c763cb005f4950769f7b553e22aa5f89/coverage-7.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a7a4d74cb0f5e3334f9aa26af7016ddb94fb4bfa11b4a573d8e98ecba8c34f1", size = 215128, upload-time = "2025-07-27T14:11:19.706Z" }, - { url = "https://files.pythonhosted.org/packages/78/4d/903bafb371a8c887826ecc30d3977b65dfad0e1e66aa61b7e173de0828b0/coverage-7.10.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d4b0aab55ad60ead26159ff12b538c85fbab731a5e3411c642b46c3525863437", size = 245140, upload-time = "2025-07-27T14:11:21.261Z" }, - { url = "https://files.pythonhosted.org/packages/55/f1/1f8f09536f38394a8698dd08a0e9608a512eacee1d3b771e2d06397f77bf/coverage-7.10.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:dcc93488c9ebd229be6ee1f0d9aad90da97b33ad7e2912f5495804d78a3cd6b7", size = 246977, upload-time = "2025-07-27T14:11:23.15Z" }, - { url = "https://files.pythonhosted.org/packages/57/cc/ed6bbc5a3bdb36ae1bca900bbbfdcb23b260ef2767a7b2dab38b92f61adf/coverage-7.10.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa309df995d020f3438407081b51ff527171cca6772b33cf8f85344b8b4b8770", size = 249140, upload-time = "2025-07-27T14:11:24.743Z" }, - { url = "https://files.pythonhosted.org/packages/10/f5/e881ade2d8e291b60fa1d93d6d736107e940144d80d21a0d4999cff3642f/coverage-7.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cfb8b9d8855c8608f9747602a48ab525b1d320ecf0113994f6df23160af68262", size = 246869, upload-time = "2025-07-27T14:11:26.156Z" }, - { url = "https://files.pythonhosted.org/packages/53/b9/6a5665cb8996e3cd341d184bb11e2a8edf01d8dadcf44eb1e742186cf243/coverage-7.10.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:320d86da829b012982b414c7cdda65f5d358d63f764e0e4e54b33097646f39a3", size = 244899, upload-time = "2025-07-27T14:11:27.622Z" }, - { url = "https://files.pythonhosted.org/packages/27/11/24156776709c4e25bf8a33d6bb2ece9a9067186ddac19990f6560a7f8130/coverage-7.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dc60ddd483c556590da1d9482a4518292eec36dd0e1e8496966759a1f282bcd0", size = 245507, upload-time = "2025-07-27T14:11:29.544Z" }, - { url = "https://files.pythonhosted.org/packages/43/db/a6f0340b7d6802a79928659c9a32bc778ea420e87a61b568d68ac36d45a8/coverage-7.10.1-cp311-cp311-win32.whl", hash = "sha256:4fcfe294f95b44e4754da5b58be750396f2b1caca8f9a0e78588e3ef85f8b8be", size = 217167, upload-time = "2025-07-27T14:11:31.349Z" }, - { url = "https://files.pythonhosted.org/packages/f5/6f/1990eb4fd05cea4cfabdf1d587a997ac5f9a8bee883443a1d519a2a848c9/coverage-7.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:efa23166da3fe2915f8ab452dde40319ac84dc357f635737174a08dbd912980c", size = 218054, upload-time = "2025-07-27T14:11:33.202Z" }, - { url = "https://files.pythonhosted.org/packages/b4/4d/5e061d6020251b20e9b4303bb0b7900083a1a384ec4e5db326336c1c4abd/coverage-7.10.1-cp311-cp311-win_arm64.whl", hash = "sha256:d12b15a8c3759e2bb580ffa423ae54be4f184cf23beffcbd641f4fe6e1584293", size = 216483, upload-time = "2025-07-27T14:11:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/a5/3f/b051feeb292400bd22d071fdf933b3ad389a8cef5c80c7866ed0c7414b9e/coverage-7.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6b7dc7f0a75a7eaa4584e5843c873c561b12602439d2351ee28c7478186c4da4", size = 214934, upload-time = "2025-07-27T14:11:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e4/a61b27d5c4c2d185bdfb0bfe9d15ab4ac4f0073032665544507429ae60eb/coverage-7.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:607f82389f0ecafc565813aa201a5cade04f897603750028dd660fb01797265e", size = 215173, upload-time = "2025-07-27T14:11:38.005Z" }, - { url = "https://files.pythonhosted.org/packages/8a/01/40a6ee05b60d02d0bc53742ad4966e39dccd450aafb48c535a64390a3552/coverage-7.10.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f7da31a1ba31f1c1d4d5044b7c5813878adae1f3af8f4052d679cc493c7328f4", size = 246190, upload-time = "2025-07-27T14:11:39.887Z" }, - { url = "https://files.pythonhosted.org/packages/11/ef/a28d64d702eb583c377255047281305dc5a5cfbfb0ee36e721f78255adb6/coverage-7.10.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:51fe93f3fe4f5d8483d51072fddc65e717a175490804e1942c975a68e04bf97a", size = 248618, upload-time = "2025-07-27T14:11:41.841Z" }, - { url = "https://files.pythonhosted.org/packages/6a/ad/73d018bb0c8317725370c79d69b5c6e0257df84a3b9b781bda27a438a3be/coverage-7.10.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e59d00830da411a1feef6ac828b90bbf74c9b6a8e87b8ca37964925bba76dbe", size = 250081, upload-time = "2025-07-27T14:11:43.705Z" }, - { url = "https://files.pythonhosted.org/packages/2d/dd/496adfbbb4503ebca5d5b2de8bed5ec00c0a76558ffc5b834fd404166bc9/coverage-7.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:924563481c27941229cb4e16eefacc35da28563e80791b3ddc5597b062a5c386", size = 247990, upload-time = "2025-07-27T14:11:45.244Z" }, - { url = "https://files.pythonhosted.org/packages/18/3c/a9331a7982facfac0d98a4a87b36ae666fe4257d0f00961a3a9ef73e015d/coverage-7.10.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ca79146ee421b259f8131f153102220b84d1a5e6fb9c8aed13b3badfd1796de6", size = 246191, upload-time = "2025-07-27T14:11:47.093Z" }, - { url = "https://files.pythonhosted.org/packages/62/0c/75345895013b83f7afe92ec595e15a9a525ede17491677ceebb2ba5c3d85/coverage-7.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2b225a06d227f23f386fdc0eab471506d9e644be699424814acc7d114595495f", size = 247400, upload-time = "2025-07-27T14:11:48.643Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a9/98b268cfc5619ef9df1d5d34fee408ecb1542d9fd43d467e5c2f28668cd4/coverage-7.10.1-cp312-cp312-win32.whl", hash = "sha256:5ba9a8770effec5baaaab1567be916c87d8eea0c9ad11253722d86874d885eca", size = 217338, upload-time = "2025-07-27T14:11:50.258Z" }, - { url = "https://files.pythonhosted.org/packages/fe/31/22a5440e4d1451f253c5cd69fdcead65e92ef08cd4ec237b8756dc0b20a7/coverage-7.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:9eb245a8d8dd0ad73b4062135a251ec55086fbc2c42e0eb9725a9b553fba18a3", size = 218125, upload-time = "2025-07-27T14:11:52.034Z" }, - { url = "https://files.pythonhosted.org/packages/d6/2b/40d9f0ce7ee839f08a43c5bfc9d05cec28aaa7c9785837247f96cbe490b9/coverage-7.10.1-cp312-cp312-win_arm64.whl", hash = "sha256:7718060dd4434cc719803a5e526838a5d66e4efa5dc46d2b25c21965a9c6fcc4", size = 216523, upload-time = "2025-07-27T14:11:53.965Z" }, - { url = "https://files.pythonhosted.org/packages/ef/72/135ff5fef09b1ffe78dbe6fcf1e16b2e564cd35faeacf3d63d60d887f12d/coverage-7.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ebb08d0867c5a25dffa4823377292a0ffd7aaafb218b5d4e2e106378b1061e39", size = 214960, upload-time = "2025-07-27T14:11:55.959Z" }, - { url = "https://files.pythonhosted.org/packages/b1/aa/73a5d1a6fc08ca709a8177825616aa95ee6bf34d522517c2595484a3e6c9/coverage-7.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f32a95a83c2e17422f67af922a89422cd24c6fa94041f083dd0bb4f6057d0bc7", size = 215220, upload-time = "2025-07-27T14:11:57.899Z" }, - { url = "https://files.pythonhosted.org/packages/8d/40/3124fdd45ed3772a42fc73ca41c091699b38a2c3bd4f9cb564162378e8b6/coverage-7.10.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c4c746d11c8aba4b9f58ca8bfc6fbfd0da4efe7960ae5540d1a1b13655ee8892", size = 245772, upload-time = "2025-07-27T14:12:00.422Z" }, - { url = "https://files.pythonhosted.org/packages/42/62/a77b254822efa8c12ad59e8039f2bc3df56dc162ebda55e1943e35ba31a5/coverage-7.10.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7f39edd52c23e5c7ed94e0e4bf088928029edf86ef10b95413e5ea670c5e92d7", size = 248116, upload-time = "2025-07-27T14:12:03.099Z" }, - { url = "https://files.pythonhosted.org/packages/1d/01/8101f062f472a3a6205b458d18ef0444a63ae5d36a8a5ed5dd0f6167f4db/coverage-7.10.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab6e19b684981d0cd968906e293d5628e89faacb27977c92f3600b201926b994", size = 249554, upload-time = "2025-07-27T14:12:04.668Z" }, - { url = "https://files.pythonhosted.org/packages/8f/7b/e51bc61573e71ff7275a4f167aecbd16cb010aefdf54bcd8b0a133391263/coverage-7.10.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5121d8cf0eacb16133501455d216bb5f99899ae2f52d394fe45d59229e6611d0", size = 247766, upload-time = "2025-07-27T14:12:06.234Z" }, - { url = "https://files.pythonhosted.org/packages/4b/71/1c96d66a51d4204a9d6d12df53c4071d87e110941a2a1fe94693192262f5/coverage-7.10.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:df1c742ca6f46a6f6cbcaef9ac694dc2cb1260d30a6a2f5c68c5f5bcfee1cfd7", size = 245735, upload-time = "2025-07-27T14:12:08.305Z" }, - { url = "https://files.pythonhosted.org/packages/13/d5/efbc2ac4d35ae2f22ef6df2ca084c60e13bd9378be68655e3268c80349ab/coverage-7.10.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:40f9a38676f9c073bf4b9194707aa1eb97dca0e22cc3766d83879d72500132c7", size = 247118, upload-time = "2025-07-27T14:12:09.903Z" }, - { url = "https://files.pythonhosted.org/packages/d1/22/073848352bec28ca65f2b6816b892fcf9a31abbef07b868487ad15dd55f1/coverage-7.10.1-cp313-cp313-win32.whl", hash = "sha256:2348631f049e884839553b9974f0821d39241c6ffb01a418efce434f7eba0fe7", size = 217381, upload-time = "2025-07-27T14:12:11.535Z" }, - { url = "https://files.pythonhosted.org/packages/b7/df/df6a0ff33b042f000089bd11b6bb034bab073e2ab64a56e78ed882cba55d/coverage-7.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:4072b31361b0d6d23f750c524f694e1a417c1220a30d3ef02741eed28520c48e", size = 218152, upload-time = "2025-07-27T14:12:13.182Z" }, - { url = "https://files.pythonhosted.org/packages/30/e3/5085ca849a40ed6b47cdb8f65471c2f754e19390b5a12fa8abd25cbfaa8f/coverage-7.10.1-cp313-cp313-win_arm64.whl", hash = "sha256:3e31dfb8271937cab9425f19259b1b1d1f556790e98eb266009e7a61d337b6d4", size = 216559, upload-time = "2025-07-27T14:12:14.807Z" }, - { url = "https://files.pythonhosted.org/packages/cc/93/58714efbfdeb547909feaabe1d67b2bdd59f0597060271b9c548d5efb529/coverage-7.10.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1c4f679c6b573a5257af6012f167a45be4c749c9925fd44d5178fd641ad8bf72", size = 215677, upload-time = "2025-07-27T14:12:16.68Z" }, - { url = "https://files.pythonhosted.org/packages/c0/0c/18eaa5897e7e8cb3f8c45e563e23e8a85686b4585e29d53cacb6bc9cb340/coverage-7.10.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:871ebe8143da284bd77b84a9136200bd638be253618765d21a1fce71006d94af", size = 215899, upload-time = "2025-07-27T14:12:18.758Z" }, - { url = "https://files.pythonhosted.org/packages/84/c1/9d1affacc3c75b5a184c140377701bbf14fc94619367f07a269cd9e4fed6/coverage-7.10.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:998c4751dabf7d29b30594af416e4bf5091f11f92a8d88eb1512c7ba136d1ed7", size = 257140, upload-time = "2025-07-27T14:12:20.357Z" }, - { url = "https://files.pythonhosted.org/packages/3d/0f/339bc6b8fa968c346df346068cca1f24bdea2ddfa93bb3dc2e7749730962/coverage-7.10.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:780f750a25e7749d0af6b3631759c2c14f45de209f3faaa2398312d1c7a22759", size = 259005, upload-time = "2025-07-27T14:12:22.007Z" }, - { url = "https://files.pythonhosted.org/packages/c8/22/89390864b92ea7c909079939b71baba7e5b42a76bf327c1d615bd829ba57/coverage-7.10.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:590bdba9445df4763bdbebc928d8182f094c1f3947a8dc0fc82ef014dbdd8324", size = 261143, upload-time = "2025-07-27T14:12:23.746Z" }, - { url = "https://files.pythonhosted.org/packages/2c/56/3d04d89017c0c41c7a71bd69b29699d919b6bbf2649b8b2091240b97dd6a/coverage-7.10.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b2df80cb6a2af86d300e70acb82e9b79dab2c1e6971e44b78dbfc1a1e736b53", size = 258735, upload-time = "2025-07-27T14:12:25.73Z" }, - { url = "https://files.pythonhosted.org/packages/cb/40/312252c8afa5ca781063a09d931f4b9409dc91526cd0b5a2b84143ffafa2/coverage-7.10.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d6a558c2725bfb6337bf57c1cd366c13798bfd3bfc9e3dd1f4a6f6fc95a4605f", size = 256871, upload-time = "2025-07-27T14:12:27.767Z" }, - { url = "https://files.pythonhosted.org/packages/1f/2b/564947d5dede068215aaddb9e05638aeac079685101462218229ddea9113/coverage-7.10.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e6150d167f32f2a54690e572e0a4c90296fb000a18e9b26ab81a6489e24e78dd", size = 257692, upload-time = "2025-07-27T14:12:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/93/1b/c8a867ade85cb26d802aea2209b9c2c80613b9c122baa8c8ecea6799648f/coverage-7.10.1-cp313-cp313t-win32.whl", hash = "sha256:d946a0c067aa88be4a593aad1236493313bafaa27e2a2080bfe88db827972f3c", size = 218059, upload-time = "2025-07-27T14:12:31.076Z" }, - { url = "https://files.pythonhosted.org/packages/a1/fe/cd4ab40570ae83a516bf5e754ea4388aeedd48e660e40c50b7713ed4f930/coverage-7.10.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e37c72eaccdd5ed1130c67a92ad38f5b2af66eeff7b0abe29534225db2ef7b18", size = 219150, upload-time = "2025-07-27T14:12:32.746Z" }, - { url = "https://files.pythonhosted.org/packages/8d/16/6e5ed5854be6d70d0c39e9cb9dd2449f2c8c34455534c32c1a508c7dbdb5/coverage-7.10.1-cp313-cp313t-win_arm64.whl", hash = "sha256:89ec0ffc215c590c732918c95cd02b55c7d0f569d76b90bb1a5e78aa340618e4", size = 217014, upload-time = "2025-07-27T14:12:34.406Z" }, - { url = "https://files.pythonhosted.org/packages/54/8e/6d0bfe9c3d7121cf936c5f8b03e8c3da1484fb801703127dba20fb8bd3c7/coverage-7.10.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:166d89c57e877e93d8827dac32cedae6b0277ca684c6511497311249f35a280c", size = 214951, upload-time = "2025-07-27T14:12:36.069Z" }, - { url = "https://files.pythonhosted.org/packages/f2/29/e3e51a8c653cf2174c60532aafeb5065cea0911403fa144c9abe39790308/coverage-7.10.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bed4a2341b33cd1a7d9ffc47df4a78ee61d3416d43b4adc9e18b7d266650b83e", size = 215229, upload-time = "2025-07-27T14:12:37.759Z" }, - { url = "https://files.pythonhosted.org/packages/e0/59/3c972080b2fa18b6c4510201f6d4dc87159d450627d062cd9ad051134062/coverage-7.10.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ddca1e4f5f4c67980533df01430184c19b5359900e080248bbf4ed6789584d8b", size = 245738, upload-time = "2025-07-27T14:12:39.453Z" }, - { url = "https://files.pythonhosted.org/packages/2e/04/fc0d99d3f809452654e958e1788454f6e27b34e43f8f8598191c8ad13537/coverage-7.10.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:37b69226001d8b7de7126cad7366b0778d36777e4d788c66991455ba817c5b41", size = 248045, upload-time = "2025-07-27T14:12:41.387Z" }, - { url = "https://files.pythonhosted.org/packages/5e/2e/afcbf599e77e0dfbf4c97197747250d13d397d27e185b93987d9eaac053d/coverage-7.10.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2f22102197bcb1722691296f9e589f02b616f874e54a209284dd7b9294b0b7f", size = 249666, upload-time = "2025-07-27T14:12:43.056Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ae/bc47f7f8ecb7a06cbae2bf86a6fa20f479dd902bc80f57cff7730438059d/coverage-7.10.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1e0c768b0f9ac5839dac5cf88992a4bb459e488ee8a1f8489af4cb33b1af00f1", size = 247692, upload-time = "2025-07-27T14:12:44.83Z" }, - { url = "https://files.pythonhosted.org/packages/b6/26/cbfa3092d31ccba8ba7647e4d25753263e818b4547eba446b113d7d1efdf/coverage-7.10.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:991196702d5e0b120a8fef2664e1b9c333a81d36d5f6bcf6b225c0cf8b0451a2", size = 245536, upload-time = "2025-07-27T14:12:46.527Z" }, - { url = "https://files.pythonhosted.org/packages/56/77/9c68e92500e6a1c83d024a70eadcc9a173f21aadd73c4675fe64c9c43fdf/coverage-7.10.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae8e59e5f4fd85d6ad34c2bb9d74037b5b11be072b8b7e9986beb11f957573d4", size = 246954, upload-time = "2025-07-27T14:12:49.279Z" }, - { url = "https://files.pythonhosted.org/packages/7f/a5/ba96671c5a669672aacd9877a5987c8551501b602827b4e84256da2a30a7/coverage-7.10.1-cp314-cp314-win32.whl", hash = "sha256:042125c89cf74a074984002e165d61fe0e31c7bd40ebb4bbebf07939b5924613", size = 217616, upload-time = "2025-07-27T14:12:51.214Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3c/e1e1eb95fc1585f15a410208c4795db24a948e04d9bde818fe4eb893bc85/coverage-7.10.1-cp314-cp314-win_amd64.whl", hash = "sha256:a22c3bfe09f7a530e2c94c87ff7af867259c91bef87ed2089cd69b783af7b84e", size = 218412, upload-time = "2025-07-27T14:12:53.429Z" }, - { url = "https://files.pythonhosted.org/packages/b0/85/7e1e5be2cb966cba95566ba702b13a572ca744fbb3779df9888213762d67/coverage-7.10.1-cp314-cp314-win_arm64.whl", hash = "sha256:ee6be07af68d9c4fca4027c70cea0c31a0f1bc9cb464ff3c84a1f916bf82e652", size = 216776, upload-time = "2025-07-27T14:12:55.482Z" }, - { url = "https://files.pythonhosted.org/packages/62/0f/5bb8f29923141cca8560fe2217679caf4e0db643872c1945ac7d8748c2a7/coverage-7.10.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d24fb3c0c8ff0d517c5ca5de7cf3994a4cd559cde0315201511dbfa7ab528894", size = 215698, upload-time = "2025-07-27T14:12:57.225Z" }, - { url = "https://files.pythonhosted.org/packages/80/29/547038ffa4e8e4d9e82f7dfc6d152f75fcdc0af146913f0ba03875211f03/coverage-7.10.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1217a54cfd79be20512a67ca81c7da3f2163f51bbfd188aab91054df012154f5", size = 215902, upload-time = "2025-07-27T14:12:59.071Z" }, - { url = "https://files.pythonhosted.org/packages/e1/8a/7aaa8fbfaed900147987a424e112af2e7790e1ac9cd92601e5bd4e1ba60a/coverage-7.10.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:51f30da7a52c009667e02f125737229d7d8044ad84b79db454308033a7808ab2", size = 257230, upload-time = "2025-07-27T14:13:01.248Z" }, - { url = "https://files.pythonhosted.org/packages/e5/1d/c252b5ffac44294e23a0d79dd5acf51749b39795ccc898faeabf7bee903f/coverage-7.10.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ed3718c757c82d920f1c94089066225ca2ad7f00bb904cb72b1c39ebdd906ccb", size = 259194, upload-time = "2025-07-27T14:13:03.247Z" }, - { url = "https://files.pythonhosted.org/packages/16/ad/6c8d9f83d08f3bac2e7507534d0c48d1a4f52c18e6f94919d364edbdfa8f/coverage-7.10.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc452481e124a819ced0c25412ea2e144269ef2f2534b862d9f6a9dae4bda17b", size = 261316, upload-time = "2025-07-27T14:13:04.957Z" }, - { url = "https://files.pythonhosted.org/packages/d6/4e/f9bbf3a36c061e2e0e0f78369c006d66416561a33d2bee63345aee8ee65e/coverage-7.10.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9d6f494c307e5cb9b1e052ec1a471060f1dea092c8116e642e7a23e79d9388ea", size = 258794, upload-time = "2025-07-27T14:13:06.715Z" }, - { url = "https://files.pythonhosted.org/packages/87/82/e600bbe78eb2cb0541751d03cef9314bcd0897e8eea156219c39b685f869/coverage-7.10.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fc0e46d86905ddd16b85991f1f4919028092b4e511689bbdaff0876bd8aab3dd", size = 256869, upload-time = "2025-07-27T14:13:08.933Z" }, - { url = "https://files.pythonhosted.org/packages/ce/5d/2fc9a9236c5268f68ac011d97cd3a5ad16cc420535369bedbda659fdd9b7/coverage-7.10.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:80b9ccd82e30038b61fc9a692a8dc4801504689651b281ed9109f10cc9fe8b4d", size = 257765, upload-time = "2025-07-27T14:13:10.778Z" }, - { url = "https://files.pythonhosted.org/packages/8a/05/b4e00b2bd48a2dc8e1c7d2aea7455f40af2e36484ab2ef06deb85883e9fe/coverage-7.10.1-cp314-cp314t-win32.whl", hash = "sha256:e58991a2b213417285ec866d3cd32db17a6a88061a985dbb7e8e8f13af429c47", size = 218420, upload-time = "2025-07-27T14:13:12.882Z" }, - { url = "https://files.pythonhosted.org/packages/77/fb/d21d05f33ea27ece327422240e69654b5932b0b29e7fbc40fbab3cf199bf/coverage-7.10.1-cp314-cp314t-win_amd64.whl", hash = "sha256:e88dd71e4ecbc49d9d57d064117462c43f40a21a1383507811cf834a4a620651", size = 219536, upload-time = "2025-07-27T14:13:14.718Z" }, - { url = "https://files.pythonhosted.org/packages/a6/68/7fea94b141281ed8be3d1d5c4319a97f2befc3e487ce33657fc64db2c45e/coverage-7.10.1-cp314-cp314t-win_arm64.whl", hash = "sha256:1aadfb06a30c62c2eb82322171fe1f7c288c80ca4156d46af0ca039052814bab", size = 217190, upload-time = "2025-07-27T14:13:16.85Z" }, - { url = "https://files.pythonhosted.org/packages/0f/64/922899cff2c0fd3496be83fa8b81230f5a8d82a2ad30f98370b133c2c83b/coverage-7.10.1-py3-none-any.whl", hash = "sha256:fa2a258aa6bf188eb9a8948f7102a83da7c430a0dce918dbd8b60ef8fcb772d7", size = 206597, upload-time = "2025-07-27T14:13:37.221Z" }, -] - -[package.optional-dependencies] -toml = [ - { name = "tomli", marker = "python_full_version <= '3.11'" }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, ] [[package]] -name = "crc32c" -version = "2.7.1" +name = "pymdown-extensions" +version = "10.20.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7f/4c/4e40cc26347ac8254d3f25b9f94710b8e8df24ee4dddc1ba41907a88a94d/crc32c-2.7.1.tar.gz", hash = "sha256:f91b144a21eef834d64178e01982bb9179c354b3e9e5f4c803b0e5096384968c", size = 45712, upload-time = "2024-09-24T06:20:17.553Z" } +dependencies = [ + { name = "markdown" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1e/6c/9e370934bfa30e889d12e61d0dae009991294f40055c238980066a7fbd83/pymdown_extensions-10.20.1.tar.gz", hash = "sha256:e7e39c865727338d434b55f1dd8da51febcffcaebd6e1a0b9c836243f660740a", size = 852860 } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/f8/2c5cc5b8d16c76a66548283d74d1f4979c8970c2a274e63f76fbfaa0cf4e/crc32c-2.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1fd1f9c6b50d7357736676278a1b8c8986737b8a1c76d7eab4baa71d0b6af67f", size = 49668, upload-time = "2024-09-24T06:18:02.204Z" }, - { url = "https://files.pythonhosted.org/packages/35/52/cdebceaed37a5657ee4864881da0f29f4036867dfb79bb058d38d4d737f3/crc32c-2.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:805c2be1bc0e251c48439a62b0422385899c15289483692bc70e78473c1039f1", size = 37151, upload-time = "2024-09-24T06:18:04.173Z" }, - { url = "https://files.pythonhosted.org/packages/1e/33/6476918b4cac85a18e32dc754e9d653b0dcd96518d661cbbf91bf8aec8cc/crc32c-2.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f4333e62b7844dfde112dbb8489fd2970358eddc3310db21e943a9f6994df749", size = 35370, upload-time = "2024-09-24T06:18:05.468Z" }, - { url = "https://files.pythonhosted.org/packages/ad/fd/8972a70d7c39f37240f554c348fd9e15a4d8d0a548b1bc3139cd4e1cfb66/crc32c-2.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f0fadc741e79dc705e2d9ee967473e8a061d26b04310ed739f1ee292f33674f", size = 54110, upload-time = "2024-09-24T06:18:06.758Z" }, - { url = "https://files.pythonhosted.org/packages/35/be/0b045f84c7acc36312a91211190bf84e73a0bbd30f21cbaf3670c4dba9b2/crc32c-2.7.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91ced31055d26d59385d708bbd36689e1a1d604d4b0ceb26767eb5a83156f85d", size = 51792, upload-time = "2024-09-24T06:18:07.767Z" }, - { url = "https://files.pythonhosted.org/packages/8c/e2/acaabbc172b7c45ec62f273cd2e214f626e2b4324eca9152dea6095a26f4/crc32c-2.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36ffa999b72e3c17f6a066ae9e970b40f8c65f38716e436c39a33b809bc6ed9f", size = 52884, upload-time = "2024-09-24T06:18:09.449Z" }, - { url = "https://files.pythonhosted.org/packages/60/40/963ba3d2ec0d8e4a2ceaf90e8f9cb10911a926fe75d4329e013a51343122/crc32c-2.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e80114dd7f462297e54d5da1b9ff472e5249c5a2b406aa51c371bb0edcbf76bd", size = 53888, upload-time = "2024-09-24T06:18:11.039Z" }, - { url = "https://files.pythonhosted.org/packages/f2/b8/8a093b9dc1792b2cec9805e1428e97be0338a45ac9fae2fd5911613eacb1/crc32c-2.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:676f5b46da268b5190f9fb91b3f037a00d114b411313664438525db876adc71f", size = 52098, upload-time = "2024-09-24T06:18:12.522Z" }, - { url = "https://files.pythonhosted.org/packages/26/76/a254ddb4ae83b545f6e08af384d62268a99d00f5c58a468754f8416468ce/crc32c-2.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8d0e660c9ed269e90692993a4457a932fc22c9cc96caf79dd1f1a84da85bb312", size = 52716, upload-time = "2024-09-24T06:18:14.118Z" }, - { url = "https://files.pythonhosted.org/packages/b6/cb/6062806e5b6cb8d9af3c62945a5a07fa22c3b4dc59084d2fa2e533f9aaa1/crc32c-2.7.1-cp310-cp310-win32.whl", hash = "sha256:17a2c3f8c6d85b04b5511af827b5dbbda4e672d188c0b9f20a8156e93a1aa7b6", size = 38363, upload-time = "2024-09-24T06:18:15.603Z" }, - { url = "https://files.pythonhosted.org/packages/7f/a9/dc935e26c8d7bd4722bc1312ed88f443e6e36816b46835b4464baa3f7c6d/crc32c-2.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:3208764c29688f91a35392073229975dd7687b6cb9f76b919dae442cabcd5126", size = 39795, upload-time = "2024-09-24T06:18:17.182Z" }, - { url = "https://files.pythonhosted.org/packages/45/8e/2f37f46368bbfd50edfc11b96f0aa135699034b1b020966c70ebaff3463b/crc32c-2.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:19e03a50545a3ef400bd41667d5525f71030488629c57d819e2dd45064f16192", size = 49672, upload-time = "2024-09-24T06:18:18.032Z" }, - { url = "https://files.pythonhosted.org/packages/ed/b8/e52f7c4b045b871c2984d70f37c31d4861b533a8082912dfd107a96cf7c1/crc32c-2.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c03286b1e5ce9bed7090084f206aacd87c5146b4b10de56fe9e86cbbbf851cf", size = 37155, upload-time = "2024-09-24T06:18:19.373Z" }, - { url = "https://files.pythonhosted.org/packages/25/ee/0cfa82a68736697f3c7e435ba658c2ef8c997f42b89f6ab4545efe1b2649/crc32c-2.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:80ebbf144a1a56a532b353e81fa0f3edca4f4baa1bf92b1dde2c663a32bb6a15", size = 35372, upload-time = "2024-09-24T06:18:20.983Z" }, - { url = "https://files.pythonhosted.org/packages/aa/92/c878aaba81c431fcd93a059e9f6c90db397c585742793f0bf6e0c531cc67/crc32c-2.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96b794fd11945298fdd5eb1290a812efb497c14bc42592c5c992ca077458eeba", size = 54879, upload-time = "2024-09-24T06:18:23.085Z" }, - { url = "https://files.pythonhosted.org/packages/5b/f5/ab828ab3907095e06b18918408748950a9f726ee2b37be1b0839fb925ee1/crc32c-2.7.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9df7194dd3c0efb5a21f5d70595b7a8b4fd9921fbbd597d6d8e7a11eca3e2d27", size = 52588, upload-time = "2024-09-24T06:18:24.463Z" }, - { url = "https://files.pythonhosted.org/packages/6a/2b/9e29e9ac4c4213d60491db09487125db358cd9263490fbadbd55e48fbe03/crc32c-2.7.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d698eec444b18e296a104d0b9bb6c596c38bdcb79d24eba49604636e9d747305", size = 53674, upload-time = "2024-09-24T06:18:25.624Z" }, - { url = "https://files.pythonhosted.org/packages/79/ed/df3c4c14bf1b29f5c9b52d51fb6793e39efcffd80b2941d994e8f7f5f688/crc32c-2.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e07cf10ef852d219d179333fd706d1c415626f1f05e60bd75acf0143a4d8b225", size = 54691, upload-time = "2024-09-24T06:18:26.578Z" }, - { url = "https://files.pythonhosted.org/packages/0c/47/4917af3c9c1df2fff28bbfa6492673c9adeae5599dcc207bbe209847489c/crc32c-2.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d2a051f296e6e92e13efee3b41db388931cdb4a2800656cd1ed1d9fe4f13a086", size = 52896, upload-time = "2024-09-24T06:18:28.174Z" }, - { url = "https://files.pythonhosted.org/packages/1b/6f/26fc3dda5835cda8f6cd9d856afe62bdeae428de4c34fea200b0888e8835/crc32c-2.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1738259802978cdf428f74156175da6a5fdfb7256f647fdc0c9de1bc6cd7173", size = 53554, upload-time = "2024-09-24T06:18:29.104Z" }, - { url = "https://files.pythonhosted.org/packages/56/3e/6f39127f7027c75d130c0ba348d86a6150dff23761fbc6a5f71659f4521e/crc32c-2.7.1-cp311-cp311-win32.whl", hash = "sha256:f7786d219a1a1bf27d0aa1869821d11a6f8e90415cfffc1e37791690d4a848a1", size = 38370, upload-time = "2024-09-24T06:18:30.013Z" }, - { url = "https://files.pythonhosted.org/packages/c9/fb/1587c2705a3a47a3d0067eecf9a6fec510761c96dec45c7b038fb5c8ff46/crc32c-2.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:887f6844bb3ad35f0778cd10793ad217f7123a5422e40041231b8c4c7329649d", size = 39795, upload-time = "2024-09-24T06:18:31.324Z" }, - { url = "https://files.pythonhosted.org/packages/1d/02/998dc21333413ce63fe4c1ca70eafe61ca26afc7eb353f20cecdb77d614e/crc32c-2.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f7d1c4e761fe42bf856130daf8b2658df33fe0ced3c43dadafdfeaa42b57b950", size = 49568, upload-time = "2024-09-24T06:18:32.425Z" }, - { url = "https://files.pythonhosted.org/packages/9c/3e/e3656bfa76e50ef87b7136fef2dbf3c46e225629432fc9184fdd7fd187ff/crc32c-2.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:73361c79a6e4605204457f19fda18b042a94508a52e53d10a4239da5fb0f6a34", size = 37019, upload-time = "2024-09-24T06:18:34.097Z" }, - { url = "https://files.pythonhosted.org/packages/0b/7d/5ff9904046ad15a08772515db19df43107bf5e3901a89c36a577b5f40ba0/crc32c-2.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:afd778fc8ac0ed2ffbfb122a9aa6a0e409a8019b894a1799cda12c01534493e0", size = 35373, upload-time = "2024-09-24T06:18:35.02Z" }, - { url = "https://files.pythonhosted.org/packages/4d/41/4aedc961893f26858ab89fc772d0eaba91f9870f19eaa933999dcacb94ec/crc32c-2.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56ef661b34e9f25991fface7f9ad85e81bbc1b3fe3b916fd58c893eabe2fa0b8", size = 54675, upload-time = "2024-09-24T06:18:35.954Z" }, - { url = "https://files.pythonhosted.org/packages/d6/63/8cabf09b7e39b9fec8f7010646c8b33057fc8d67e6093b3cc15563d23533/crc32c-2.7.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:571aa4429444b5d7f588e4377663592145d2d25eb1635abb530f1281794fc7c9", size = 52386, upload-time = "2024-09-24T06:18:36.896Z" }, - { url = "https://files.pythonhosted.org/packages/79/13/13576941bf7cf95026abae43d8427c812c0054408212bf8ed490eda846b0/crc32c-2.7.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c02a3bd67dea95cdb25844aaf44ca2e1b0c1fd70b287ad08c874a95ef4bb38db", size = 53495, upload-time = "2024-09-24T06:18:38.099Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b6/55ffb26d0517d2d6c6f430ce2ad36ae7647c995c5bfd7abce7f32bb2bad1/crc32c-2.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99d17637c4867672cb8adeea007294e3c3df9d43964369516cfe2c1f47ce500a", size = 54456, upload-time = "2024-09-24T06:18:39.051Z" }, - { url = "https://files.pythonhosted.org/packages/c2/1a/5562e54cb629ecc5543d3604dba86ddfc7c7b7bf31d64005b38a00d31d31/crc32c-2.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f4a400ac3c69a32e180d8753fd7ec7bccb80ade7ab0812855dce8a208e72495f", size = 52647, upload-time = "2024-09-24T06:18:40.021Z" }, - { url = "https://files.pythonhosted.org/packages/48/ec/ce4138eaf356cd9aae60bbe931755e5e0151b3eca5f491fce6c01b97fd59/crc32c-2.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:588587772e55624dd9c7a906ec9e8773ae0b6ac5e270fc0bc84ee2758eba90d5", size = 53332, upload-time = "2024-09-24T06:18:40.925Z" }, - { url = "https://files.pythonhosted.org/packages/5e/b5/144b42cd838a901175a916078781cb2c3c9f977151c9ba085aebd6d15b22/crc32c-2.7.1-cp312-cp312-win32.whl", hash = "sha256:9f14b60e5a14206e8173dd617fa0c4df35e098a305594082f930dae5488da428", size = 38371, upload-time = "2024-09-24T06:18:42.711Z" }, - { url = "https://files.pythonhosted.org/packages/ae/c4/7929dcd5d9b57db0cce4fe6f6c191049380fc6d8c9b9f5581967f4ec018e/crc32c-2.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:7c810a246660a24dc818047dc5f89c7ce7b2814e1e08a8e99993f4103f7219e8", size = 39805, upload-time = "2024-09-24T06:18:43.6Z" }, - { url = "https://files.pythonhosted.org/packages/bf/98/1a6d60d5b3b5edc8382777b64100343cb4aa6a7e172fae4a6cfcb8ebbbd9/crc32c-2.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:24949bffb06fc411cc18188d33357923cb935273642164d0bb37a5f375654169", size = 49567, upload-time = "2024-09-24T06:18:44.485Z" }, - { url = "https://files.pythonhosted.org/packages/4f/56/0dd652d4e950e6348bbf16b964b3325e4ad8220470774128fc0b0dd069cb/crc32c-2.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2d5d326e7e118d4fa60187770d86b66af2fdfc63ce9eeb265f0d3e7d49bebe0b", size = 37018, upload-time = "2024-09-24T06:18:45.434Z" }, - { url = "https://files.pythonhosted.org/packages/47/02/2bd65fdef10139b6a802d83a7f966b7750fe5ffb1042f7cbe5dbb6403869/crc32c-2.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ba110df60c64c8e2d77a9425b982a520ccdb7abe42f06604f4d98a45bb1fff62", size = 35374, upload-time = "2024-09-24T06:18:46.304Z" }, - { url = "https://files.pythonhosted.org/packages/a9/0d/3e797d1ed92d357a6a4c5b41cea15a538b27a8fdf18c7863747eb50b73ad/crc32c-2.7.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c277f9d16a3283e064d54854af0976b72abaa89824955579b2b3f37444f89aae", size = 54641, upload-time = "2024-09-24T06:18:47.207Z" }, - { url = "https://files.pythonhosted.org/packages/a7/d3/4ddeef755caaa75680c559562b6c71f5910fee4c4f3a2eb5ea8b57f0e48c/crc32c-2.7.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:881af0478a01331244e27197356929edbdeaef6a9f81b5c6bacfea18d2139289", size = 52338, upload-time = "2024-09-24T06:18:49.31Z" }, - { url = "https://files.pythonhosted.org/packages/01/cf/32f019be5de9f6e180926a50ee5f08648e686c7d9a59f2c5d0806a77b1c7/crc32c-2.7.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:724d5ff4d29ff093a983ae656be3307093706d850ea2a233bf29fcacc335d945", size = 53447, upload-time = "2024-09-24T06:18:50.296Z" }, - { url = "https://files.pythonhosted.org/packages/b2/8b/92f3f62f3bafe8f7ab4af7bfb7246dc683fd11ec0d6dfb73f91e09079f69/crc32c-2.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b2416c4d88696ac322632555c0f81ab35e15f154bc96055da6cf110d642dbc10", size = 54484, upload-time = "2024-09-24T06:18:51.311Z" }, - { url = "https://files.pythonhosted.org/packages/98/b2/113a50f8781f76af5ac65ffdb907e72bddbe974de8e02247f0d58bc48040/crc32c-2.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:60254251b88ec9b9795215f0f9ec015a6b5eef8b2c5fba1267c672d83c78fc02", size = 52703, upload-time = "2024-09-24T06:18:52.488Z" }, - { url = "https://files.pythonhosted.org/packages/b4/6c/309229e9acda8cf36a8ff4061d70b54d905f79b7037e16883ce6590a24ab/crc32c-2.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:edefc0e46f3c37372183f70338e5bdee42f6789b62fcd36ec53aa933e9dfbeaf", size = 53367, upload-time = "2024-09-24T06:18:53.49Z" }, - { url = "https://files.pythonhosted.org/packages/b5/2a/6c6324d920396e1bd9f3efbe8753da071be0ca52bd22d6c82d446b8d6975/crc32c-2.7.1-cp313-cp313-win32.whl", hash = "sha256:813af8111218970fe2adb833c5e5239f091b9c9e76f03b4dd91aaba86e99b499", size = 38377, upload-time = "2024-09-24T06:18:54.487Z" }, - { url = "https://files.pythonhosted.org/packages/db/a0/f01ccfab538db07ef3f6b4ede46357ff147a81dd4f3c59ca6a34c791a549/crc32c-2.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:7d9ede7be8e4ec1c9e90aaf6884decbeef10e3473e6ddac032706d710cab5888", size = 39803, upload-time = "2024-09-24T06:18:55.419Z" }, - { url = "https://files.pythonhosted.org/packages/1b/80/61dcae7568b33acfde70c9d651c7d891c0c578c39cc049107c1cf61f1367/crc32c-2.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db9ac92294284b22521356715784b91cc9094eee42a5282ab281b872510d1831", size = 49386, upload-time = "2024-09-24T06:18:56.813Z" }, - { url = "https://files.pythonhosted.org/packages/1e/f1/80f17c089799ab2b4c247443bdd101d6ceda30c46d7f193e16b5ca29c5a0/crc32c-2.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8fcd7f2f29a30dc92af64a9ee3d38bde0c82bd20ad939999427aac94bbd87373", size = 36937, upload-time = "2024-09-24T06:18:57.77Z" }, - { url = "https://files.pythonhosted.org/packages/63/42/5fcfc71a3de493d920fd2590843762a2749981ea56b802b380e5df82309d/crc32c-2.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5c056ef043393085523e149276a7ce0cb534b872e04f3e20d74d9a94a75c0ad7", size = 35292, upload-time = "2024-09-24T06:18:58.676Z" }, - { url = "https://files.pythonhosted.org/packages/03/de/fef962e898a953558fe1c55141644553e84ef4190693a31244c59a0856c7/crc32c-2.7.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03a92551a343702629af91f78d205801219692b6909f8fa126b830e332bfb0e0", size = 54223, upload-time = "2024-09-24T06:18:59.675Z" }, - { url = "https://files.pythonhosted.org/packages/21/14/fceca1a6f45c0a1814fe8602a65657b75c27425162445925ba87438cad6b/crc32c-2.7.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb9424ec1a8ca54763155a703e763bcede82e6569fe94762614bb2de1412d4e1", size = 51588, upload-time = "2024-09-24T06:19:00.938Z" }, - { url = "https://files.pythonhosted.org/packages/13/3b/13d40a7dfbf9ef05c84a0da45544ee72080dca4ce090679e5105689984bd/crc32c-2.7.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88732070f6175530db04e0bb36880ac45c33d49f8ac43fa0e50cfb1830049d23", size = 52678, upload-time = "2024-09-24T06:19:02.661Z" }, - { url = "https://files.pythonhosted.org/packages/36/09/65ffc4fb9fa60ff6714eeb50a92284a4525e5943f0b040b572c0c76368c1/crc32c-2.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:57a20dfc27995f568f64775eea2bbb58ae269f1a1144561df5e4a4955f79db32", size = 53847, upload-time = "2024-09-24T06:19:03.705Z" }, - { url = "https://files.pythonhosted.org/packages/24/71/938e926085b7288da052db7c84416f3ce25e71baf7ab5b63824c7bcb6f22/crc32c-2.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f7186d098bfd2cff25eac6880b7c7ad80431b90610036131c1c7dd0eab42a332", size = 51860, upload-time = "2024-09-24T06:19:04.726Z" }, - { url = "https://files.pythonhosted.org/packages/3c/d8/4526d5380189d6f2fa27256c204100f30214fe402f47cf6e9fb9a91ab890/crc32c-2.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:55a77e29a265418fa34bef15bd0f2c60afae5348988aaf35ed163b4bbf93cf37", size = 52508, upload-time = "2024-09-24T06:19:05.731Z" }, - { url = "https://files.pythonhosted.org/packages/19/30/15f7e35176488b77e5b88751947d321d603fccac273099ace27c7b2d50a6/crc32c-2.7.1-cp313-cp313t-win32.whl", hash = "sha256:ae38a4b6aa361595d81cab441405fbee905c72273e80a1c010fb878ae77ac769", size = 38319, upload-time = "2024-09-24T06:19:07.233Z" }, - { url = "https://files.pythonhosted.org/packages/19/c4/0b3eee04dac195f4730d102d7a9fbea894ae7a32ce075f84336df96a385d/crc32c-2.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:eee2a43b663feb6c79a6c1c6e5eae339c2b72cfac31ee54ec0209fa736cf7ee5", size = 39781, upload-time = "2024-09-24T06:19:08.182Z" }, - { url = "https://files.pythonhosted.org/packages/7f/e0/14d392075db47c53a3890aa110e3640b22fb9736949c47b13d5b5e4599f7/crc32c-2.7.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2e83fedebcdeb80c19e76b7a0e5103528bb062521c40702bf34516a429e81df3", size = 36448, upload-time = "2024-09-24T06:19:50.456Z" }, - { url = "https://files.pythonhosted.org/packages/03/27/f1dab3066c90e9424d22bc942eecdc2e77267f7e805ddb5a2419bbcbace6/crc32c-2.7.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30004a7383538ef93bda9b22f7b3805bc0aa5625ab2675690e1b676b19417d4b", size = 38184, upload-time = "2024-09-24T06:19:51.466Z" }, - { url = "https://files.pythonhosted.org/packages/2c/f3/479acfa99803c261cdd6b44f37b55bd77bdbce6163ec1f51b2014b095495/crc32c-2.7.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a01b0983aa87f517c12418f9898ecf2083bf86f4ea04122e053357c3edb0d73f", size = 38256, upload-time = "2024-09-24T06:19:52.486Z" }, - { url = "https://files.pythonhosted.org/packages/7b/31/4edb9c45457c54d51ca539f850761f31b7ab764177902b6f3247ff8c1b75/crc32c-2.7.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb2b963c42128b38872e9ed63f04a73ce1ff89a1dfad7ea38add6fe6296497b8", size = 37868, upload-time = "2024-09-24T06:19:54.159Z" }, - { url = "https://files.pythonhosted.org/packages/a6/b0/5680db08eff8f2116a4f9bd949f8bbe9cf260e1c3451228f54c60b110d79/crc32c-2.7.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cdd5e576fee5d255c1e68a4dae4420f21e57e6f05900b38d5ae47c713fc3330d", size = 39826, upload-time = "2024-09-24T06:19:55.4Z" }, + { url = "https://files.pythonhosted.org/packages/40/6d/b6ee155462a0156b94312bdd82d2b92ea56e909740045a87ccb98bf52405/pymdown_extensions-10.20.1-py3-none-any.whl", hash = "sha256:24af7feacbca56504b313b7b418c4f5e1317bb5fea60f03d57be7fcc40912aa0", size = 268768 }, ] [[package]] -name = "cryptography" -version = "46.0.5" +name = "pympler" +version = "1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/37/c384631908029676d8e7213dd956bb686af303a80db7afbc9be36bc49495/pympler-1.1.tar.gz", hash = "sha256:1eaa867cb8992c218430f1708fdaccda53df064144d1c5656b1e6f1ee6000424", size = 179954 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" }, - { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, - { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, - { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, - { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, - { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, - { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, - { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, - { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, - { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230, upload-time = "2026-02-10T19:17:30.518Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909, upload-time = "2026-02-10T19:17:32.083Z" }, - { url = "https://files.pythonhosted.org/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", size = 7119287, upload-time = "2026-02-10T19:17:33.801Z" }, - { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" }, - { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" }, - { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539, upload-time = "2026-02-10T19:17:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" }, - { url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" }, - { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" }, - { url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170, upload-time = "2026-02-10T19:17:46.997Z" }, - { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" }, - { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" }, - { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" }, - { url = "https://files.pythonhosted.org/packages/86/ef/5d00ef966ddd71ac2e6951d278884a84a40ffbd88948ef0e294b214ae9e4/cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a", size = 3003637, upload-time = "2026-02-10T19:17:52.997Z" }, - { url = "https://files.pythonhosted.org/packages/b7/57/f3f4160123da6d098db78350fdfd9705057aad21de7388eacb2401dceab9/cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4", size = 3469487, upload-time = "2026-02-10T19:17:54.549Z" }, - { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" }, - { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, - { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, - { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, - { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, - { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, - { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, - { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, - { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, - { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, - { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220, upload-time = "2026-02-10T19:18:17.361Z" }, - { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" }, - { url = "https://files.pythonhosted.org/packages/eb/dd/2d9fdb07cebdf3d51179730afb7d5e576153c6744c3ff8fded23030c204e/cryptography-46.0.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c", size = 3476964, upload-time = "2026-02-10T19:18:20.687Z" }, - { url = "https://files.pythonhosted.org/packages/e9/6f/6cc6cc9955caa6eaf83660b0da2b077c7fe8ff9950a3c5e45d605038d439/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a", size = 4218321, upload-time = "2026-02-10T19:18:22.349Z" }, - { url = "https://files.pythonhosted.org/packages/3e/5d/c4da701939eeee699566a6c1367427ab91a8b7088cc2328c09dbee940415/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356", size = 4381786, upload-time = "2026-02-10T19:18:24.529Z" }, - { url = "https://files.pythonhosted.org/packages/ac/97/a538654732974a94ff96c1db621fa464f455c02d4bb7d2652f4edc21d600/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da", size = 4217990, upload-time = "2026-02-10T19:18:25.957Z" }, - { url = "https://files.pythonhosted.org/packages/ae/11/7e500d2dd3ba891197b9efd2da5454b74336d64a7cc419aa7327ab74e5f6/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257", size = 4381252, upload-time = "2026-02-10T19:18:27.496Z" }, - { url = "https://files.pythonhosted.org/packages/bc/58/6b3d24e6b9bc474a2dcdee65dfd1f008867015408a271562e4b690561a4d/cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7", size = 3407605, upload-time = "2026-02-10T19:18:29.233Z" }, + { url = "https://files.pythonhosted.org/packages/79/4f/a6a2e2b202d7fd97eadfe90979845b8706676b41cbd3b42ba75adf329d1f/Pympler-1.1-py3-none-any.whl", hash = "sha256:5b223d6027d0619584116a0cbc28e8d2e378f7a79c1e5e024f9ff3b673c58506", size = 165766 }, ] [[package]] -name = "dask" -version = "2025.7.0" +name = "pyproject-hooks" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "cloudpickle" }, - { name = "fsspec" }, - { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, - { name = "packaging" }, - { name = "partd" }, - { name = "pyyaml" }, - { name = "toolz" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/08/00/cbb7cb07742955dfe77368aa40725d7000414a8a49f415ba40c5a4379ba9/dask-2025.7.0.tar.gz", hash = "sha256:c3a0d4e78882e85ea81dbc71e6459713e45676e2d17e776c2f3f19848039e4cf", size = 10972242, upload-time = "2025-07-14T20:03:42.701Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/f9/3e04725358c17329652da8c1b2dbd88de723f3dc78bf52ca6d28d52c9279/dask-2025.7.0-py3-none-any.whl", hash = "sha256:073c29c4e99c2400a39a8a67874f35c1d15bf7af9ae3d0c30af3c7c1199f24ae", size = 1475117, upload-time = "2025-07-14T20:03:33.095Z" }, + { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216 }, ] -[package.optional-dependencies] -array = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +[[package]] +name = "pystac" +source = { editable = "." } +dependencies = [ + { name = "python-dateutil" }, + { name = "typing-extensions" }, ] -[[package]] -name = "debugpy" -version = "1.8.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/3a9a28ddb750a76eaec445c7f4d3147ea2c579a97dbd9e25d39001b92b21/debugpy-1.8.15.tar.gz", hash = "sha256:58d7a20b7773ab5ee6bdfb2e6cf622fdf1e40c9d5aef2857d85391526719ac00", size = 1643279, upload-time = "2025-07-15T16:43:29.135Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/51/0b4315169f0d945271db037ae6b98c0548a2d48cc036335cd1b2f5516c1b/debugpy-1.8.15-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:e9a8125c85172e3ec30985012e7a81ea5e70bbb836637f8a4104f454f9b06c97", size = 2084890, upload-time = "2025-07-15T16:43:31.239Z" }, - { url = "https://files.pythonhosted.org/packages/36/cc/a5391dedb079280d7b72418022e00ba8227ae0b5bc8b2e3d1ecffc5d6b01/debugpy-1.8.15-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fd0b6b5eccaa745c214fd240ea82f46049d99ef74b185a3517dad3ea1ec55d9", size = 3561470, upload-time = "2025-07-15T16:43:32.515Z" }, - { url = "https://files.pythonhosted.org/packages/e8/92/acf64b92010c66b33c077dee3862c733798a2c90e7d14b25c01d771e2a0d/debugpy-1.8.15-cp310-cp310-win32.whl", hash = "sha256:8181cce4d344010f6bfe94a531c351a46a96b0f7987750932b2908e7a1e14a55", size = 5229194, upload-time = "2025-07-15T16:43:33.997Z" }, - { url = "https://files.pythonhosted.org/packages/3f/f5/c58c015c9ff78de35901bea3ab4dbf7946d7a4aa867ee73875df06ba6468/debugpy-1.8.15-cp310-cp310-win_amd64.whl", hash = "sha256:af2dcae4e4cd6e8b35f982ccab29fe65f7e8766e10720a717bc80c464584ee21", size = 5260900, upload-time = "2025-07-15T16:43:35.413Z" }, - { url = "https://files.pythonhosted.org/packages/d2/b3/1c44a2ed311199ab11c2299c9474a6c7cd80d19278defd333aeb7c287995/debugpy-1.8.15-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:babc4fb1962dd6a37e94d611280e3d0d11a1f5e6c72ac9b3d87a08212c4b6dd3", size = 2183442, upload-time = "2025-07-15T16:43:36.733Z" }, - { url = "https://files.pythonhosted.org/packages/f6/69/e2dcb721491e1c294d348681227c9b44fb95218f379aa88e12a19d85528d/debugpy-1.8.15-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f778e68f2986a58479d0ac4f643e0b8c82fdd97c2e200d4d61e7c2d13838eb53", size = 3134215, upload-time = "2025-07-15T16:43:38.116Z" }, - { url = "https://files.pythonhosted.org/packages/17/76/4ce63b95d8294dcf2fd1820860b300a420d077df4e93afcaa25a984c2ca7/debugpy-1.8.15-cp311-cp311-win32.whl", hash = "sha256:f9d1b5abd75cd965e2deabb1a06b0e93a1546f31f9f621d2705e78104377c702", size = 5154037, upload-time = "2025-07-15T16:43:39.471Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a7/e5a7c784465eb9c976d84408873d597dc7ce74a0fc69ed009548a1a94813/debugpy-1.8.15-cp311-cp311-win_amd64.whl", hash = "sha256:62954fb904bec463e2b5a415777f6d1926c97febb08ef1694da0e5d1463c5c3b", size = 5178133, upload-time = "2025-07-15T16:43:40.969Z" }, - { url = "https://files.pythonhosted.org/packages/ab/4a/4508d256e52897f5cdfee6a6d7580974811e911c6d01321df3264508a5ac/debugpy-1.8.15-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:3dcc7225cb317469721ab5136cda9ff9c8b6e6fb43e87c9e15d5b108b99d01ba", size = 2511197, upload-time = "2025-07-15T16:43:42.343Z" }, - { url = "https://files.pythonhosted.org/packages/99/8d/7f6ef1097e7fecf26b4ef72338d08e41644a41b7ee958a19f494ffcffc29/debugpy-1.8.15-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:047a493ca93c85ccede1dbbaf4e66816794bdc214213dde41a9a61e42d27f8fc", size = 4229517, upload-time = "2025-07-15T16:43:44.14Z" }, - { url = "https://files.pythonhosted.org/packages/3f/e8/e8c6a9aa33a9c9c6dacbf31747384f6ed2adde4de2e9693c766bdf323aa3/debugpy-1.8.15-cp312-cp312-win32.whl", hash = "sha256:b08e9b0bc260cf324c890626961dad4ffd973f7568fbf57feb3c3a65ab6b6327", size = 5276132, upload-time = "2025-07-15T16:43:45.529Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ad/231050c6177b3476b85fcea01e565dac83607b5233d003ff067e2ee44d8f/debugpy-1.8.15-cp312-cp312-win_amd64.whl", hash = "sha256:e2a4fe357c92334272eb2845fcfcdbec3ef9f22c16cf613c388ac0887aed15fa", size = 5317645, upload-time = "2025-07-15T16:43:46.968Z" }, - { url = "https://files.pythonhosted.org/packages/28/70/2928aad2310726d5920b18ed9f54b9f06df5aa4c10cf9b45fa18ff0ab7e8/debugpy-1.8.15-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:f5e01291ad7d6649aed5773256c5bba7a1a556196300232de1474c3c372592bf", size = 2495538, upload-time = "2025-07-15T16:43:48.927Z" }, - { url = "https://files.pythonhosted.org/packages/9e/c6/9b8ffb4ca91fac8b2877eef63c9cc0e87dd2570b1120054c272815ec4cd0/debugpy-1.8.15-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94dc0f0d00e528d915e0ce1c78e771475b2335b376c49afcc7382ee0b146bab6", size = 4221874, upload-time = "2025-07-15T16:43:50.282Z" }, - { url = "https://files.pythonhosted.org/packages/55/8a/9b8d59674b4bf489318c7c46a1aab58e606e583651438084b7e029bf3c43/debugpy-1.8.15-cp313-cp313-win32.whl", hash = "sha256:fcf0748d4f6e25f89dc5e013d1129ca6f26ad4da405e0723a4f704583896a709", size = 5275949, upload-time = "2025-07-15T16:43:52.079Z" }, - { url = "https://files.pythonhosted.org/packages/72/83/9e58e6fdfa8710a5e6ec06c2401241b9ad48b71c0a7eb99570a1f1edb1d3/debugpy-1.8.15-cp313-cp313-win_amd64.whl", hash = "sha256:73c943776cb83e36baf95e8f7f8da765896fd94b05991e7bc162456d25500683", size = 5317720, upload-time = "2025-07-15T16:43:53.703Z" }, - { url = "https://files.pythonhosted.org/packages/07/d5/98748d9860e767a1248b5e31ffa7ce8cb7006e97bf8abbf3d891d0a8ba4e/debugpy-1.8.15-py2.py3-none-any.whl", hash = "sha256:bce2e6c5ff4f2e00b98d45e7e01a49c7b489ff6df5f12d881c67d2f1ac635f3d", size = 5282697, upload-time = "2025-07-15T16:44:07.996Z" }, +[package.optional-dependencies] +jinja2 = [ + { name = "jinja2" }, +] +obstore = [ + { name = "obspec" }, + { name = "obstore" }, +] +validation = [ + { name = "jsonschema" }, + { name = "referencing" }, ] -[[package]] -name = "decorator" -version = "5.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, +[package.dev-dependencies] +bench = [ + { name = "asv" }, +] +dev = [ + { name = "basedpyright" }, + { name = "html5lib" }, + { name = "jsonschema" }, + { name = "prek" }, + { name = "pytest" }, + { name = "pytest-mock" }, + { name = "pytest-recording" }, + { name = "requests-mock" }, + { name = "ruff" }, +] +docs = [ + { name = "mkdocs-material" }, + { name = "mkdocstrings-python" }, ] -[[package]] -name = "defusedxml" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, +[package.metadata] +requires-dist = [ + { name = "jinja2", marker = "extra == 'jinja2'", specifier = ">=3.1.6" }, + { name = "jsonschema", marker = "extra == 'validation'", specifier = ">=4.25.1" }, + { name = "obspec", marker = "extra == 'obstore'", specifier = ">=0.1.0" }, + { name = "obstore", marker = "extra == 'obstore'", specifier = ">=0.8.2" }, + { name = "python-dateutil", specifier = ">=2.9.0.post0" }, + { name = "referencing", marker = "extra == 'validation'", specifier = ">=0.37.0" }, + { name = "typing-extensions", specifier = ">=4.15.0" }, ] +provides-extras = ["jinja2", "obstore", "validation"] -[[package]] -name = "distlib" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +[package.metadata.requires-dev] +bench = [{ name = "asv", specifier = ">=0.6.5" }] +dev = [ + { name = "basedpyright", specifier = ">=1.36.1" }, + { name = "html5lib", specifier = ">=1.1" }, + { name = "jsonschema", specifier = ">=4.25.1" }, + { name = "prek", specifier = ">=0.2.27" }, + { name = "pytest", specifier = ">=9.0.2" }, + { name = "pytest-mock", specifier = ">=3.15.1" }, + { name = "pytest-recording", specifier = ">=0.13.4" }, + { name = "requests-mock", specifier = ">=1.12.1" }, + { name = "ruff", specifier = ">=0.14.9" }, +] +docs = [ + { name = "mkdocs-material", specifier = ">=9.7.0" }, + { name = "mkdocstrings-python", specifier = ">=2.0.1" }, ] [[package]] -name = "doc8" -version = "2.0.0" +name = "pytest" +version = "9.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "docutils" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, { name = "pygments" }, - { name = "restructuredtext-lint" }, - { name = "stevedore" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/92/91/88bb55225046a2ee9c2243d47346c78d2ed861c769168f451568625ad670/doc8-2.0.0.tar.gz", hash = "sha256:1267ad32758971fbcf991442417a3935c7bc9e52550e73622e0e56ba55ea1d40", size = 28436, upload-time = "2025-06-13T13:08:53.174Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/e9/90b7d243364d3dce38c8c2a1b8c103d7a8d1383c2b24c735fae0eee038dd/doc8-2.0.0-py3-none-any.whl", hash = "sha256:9862710027f793c25f9b1899150660e4bf1d4c9a6738742e71f32011e2e3f590", size = 25861, upload-time = "2025-06-13T13:08:51.839Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801 }, ] [[package]] -name = "docutils" -version = "0.21.2" +name = "pytest-mock" +version = "3.15.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095 }, ] [[package]] -name = "donfig" -version = "0.8.1.post1" +name = "pytest-recording" +version = "0.13.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyyaml", marker = "python_full_version >= '3.11'" }, + { name = "pytest" }, + { name = "vcrpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/71/80cc718ff6d7abfbabacb1f57aaa42e9c1552bfdd01e64ddd704e4a03638/donfig-0.8.1.post1.tar.gz", hash = "sha256:3bef3413a4c1c601b585e8d297256d0c1470ea012afa6e8461dc28bfb7c23f52", size = 19506, upload-time = "2024-05-23T14:14:31.513Z" } +sdist = { url = "https://files.pythonhosted.org/packages/32/9c/f4027c5f1693847b06d11caf4b4f6bb09f22c1581ada4663877ec166b8c6/pytest_recording-0.13.4.tar.gz", hash = "sha256:568d64b2a85992eec4ae0a419c855d5fd96782c5fb016784d86f18053792768c", size = 26576 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl", hash = "sha256:2a3175ce74a06109ff9307d90a230f81215cbac9a751f4d1c6194644b8204f9d", size = 21592, upload-time = "2024-05-23T14:13:55.283Z" }, + { url = "https://files.pythonhosted.org/packages/42/c2/ce34735972cc42d912173e79f200fe66530225190c06655c5632a9d88f1e/pytest_recording-0.13.4-py3-none-any.whl", hash = "sha256:ad49a434b51b1c4f78e85b1e6b74fdcc2a0a581ca16e52c798c6ace971f7f439", size = 13723 }, ] [[package]] -name = "exceptiongroup" -version = "1.3.0" +name = "python-dateutil" +version = "2.9.0.post0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, ] [[package]] -name = "executing" -version = "2.2.0" +name = "pywin32" +version = "311" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693, upload-time = "2025-01-22T15:41:29.403Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702, upload-time = "2025-01-22T15:41:25.929Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543 }, + { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040 }, + { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102 }, + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700 }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700 }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318 }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714 }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800 }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540 }, ] [[package]] -name = "fasteners" -version = "0.20" +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063 }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973 }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116 }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011 }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870 }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089 }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181 }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658 }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003 }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344 }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669 }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252 }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081 }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159 }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626 }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613 }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115 }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814 }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809 }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454 }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355 }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175 }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228 }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194 }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429 }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912 }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108 }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641 }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901 }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132 }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261 }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272 }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923 }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062 }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 }, +] + +[[package]] +name = "pyyaml-env-tag" +version = "1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/18/7881a99ba5244bfc82f06017316ffe93217dbbbcfa52b887caa1d4f2a6d3/fasteners-0.20.tar.gz", hash = "sha256:55dce8792a41b56f727ba6e123fcaee77fd87e638a6863cec00007bfea84c8d8", size = 25087, upload-time = "2025-08-11T10:19:37.785Z" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737 } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/ac/e5d886f892666d2d1e5cb8c1a41146e1d79ae8896477b1153a21711d3b44/fasteners-0.20-py3-none-any.whl", hash = "sha256:9422c40d1e350e4259f509fb2e608d6bc43c0136f79a00db1b49046029d0b3b7", size = 18702, upload-time = "2025-08-11T10:19:35.716Z" }, + { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722 }, ] [[package]] -name = "fastjsonschema" -version = "2.21.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8b/50/4b769ce1ac4071a1ef6d86b1a3fb56cdc3a37615e8c5519e1af96cdac366/fastjsonschema-2.21.1.tar.gz", hash = "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4", size = 373939, upload-time = "2024-12-02T10:55:15.133Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/2b/0817a2b257fe88725c25589d89aec060581aabf668707a8d03b2e9e0cb2a/fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667", size = 23924, upload-time = "2024-12-02T10:55:07.599Z" }, -] - -[[package]] -name = "filelock" -version = "3.20.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, -] - -[[package]] -name = "fqdn" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015, upload-time = "2021-03-11T07:16:29.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121, upload-time = "2021-03-11T07:16:28.351Z" }, -] - -[[package]] -name = "frozenlist" -version = "1.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/b1/b64018016eeb087db503b038296fd782586432b9c077fc5c7839e9cb6ef6/frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f", size = 45078, upload-time = "2025-06-09T23:02:35.538Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/36/0da0a49409f6b47cc2d060dc8c9040b897b5902a8a4e37d9bc1deb11f680/frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a", size = 81304, upload-time = "2025-06-09T22:59:46.226Z" }, - { url = "https://files.pythonhosted.org/packages/77/f0/77c11d13d39513b298e267b22eb6cb559c103d56f155aa9a49097221f0b6/frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61", size = 47735, upload-time = "2025-06-09T22:59:48.133Z" }, - { url = "https://files.pythonhosted.org/packages/37/12/9d07fa18971a44150593de56b2f2947c46604819976784bcf6ea0d5db43b/frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d", size = 46775, upload-time = "2025-06-09T22:59:49.564Z" }, - { url = "https://files.pythonhosted.org/packages/70/34/f73539227e06288fcd1f8a76853e755b2b48bca6747e99e283111c18bcd4/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e", size = 224644, upload-time = "2025-06-09T22:59:51.35Z" }, - { url = "https://files.pythonhosted.org/packages/fb/68/c1d9c2f4a6e438e14613bad0f2973567586610cc22dcb1e1241da71de9d3/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9", size = 222125, upload-time = "2025-06-09T22:59:52.884Z" }, - { url = "https://files.pythonhosted.org/packages/b9/d0/98e8f9a515228d708344d7c6986752be3e3192d1795f748c24bcf154ad99/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c", size = 233455, upload-time = "2025-06-09T22:59:54.74Z" }, - { url = "https://files.pythonhosted.org/packages/79/df/8a11bcec5600557f40338407d3e5bea80376ed1c01a6c0910fcfdc4b8993/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981", size = 227339, upload-time = "2025-06-09T22:59:56.187Z" }, - { url = "https://files.pythonhosted.org/packages/50/82/41cb97d9c9a5ff94438c63cc343eb7980dac4187eb625a51bdfdb7707314/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615", size = 212969, upload-time = "2025-06-09T22:59:57.604Z" }, - { url = "https://files.pythonhosted.org/packages/13/47/f9179ee5ee4f55629e4f28c660b3fdf2775c8bfde8f9c53f2de2d93f52a9/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50", size = 222862, upload-time = "2025-06-09T22:59:59.498Z" }, - { url = "https://files.pythonhosted.org/packages/1a/52/df81e41ec6b953902c8b7e3a83bee48b195cb0e5ec2eabae5d8330c78038/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa", size = 222492, upload-time = "2025-06-09T23:00:01.026Z" }, - { url = "https://files.pythonhosted.org/packages/84/17/30d6ea87fa95a9408245a948604b82c1a4b8b3e153cea596421a2aef2754/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577", size = 238250, upload-time = "2025-06-09T23:00:03.401Z" }, - { url = "https://files.pythonhosted.org/packages/8f/00/ecbeb51669e3c3df76cf2ddd66ae3e48345ec213a55e3887d216eb4fbab3/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59", size = 218720, upload-time = "2025-06-09T23:00:05.282Z" }, - { url = "https://files.pythonhosted.org/packages/1a/c0/c224ce0e0eb31cc57f67742071bb470ba8246623c1823a7530be0e76164c/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e", size = 232585, upload-time = "2025-06-09T23:00:07.962Z" }, - { url = "https://files.pythonhosted.org/packages/55/3c/34cb694abf532f31f365106deebdeac9e45c19304d83cf7d51ebbb4ca4d1/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd", size = 234248, upload-time = "2025-06-09T23:00:09.428Z" }, - { url = "https://files.pythonhosted.org/packages/98/c0/2052d8b6cecda2e70bd81299e3512fa332abb6dcd2969b9c80dfcdddbf75/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718", size = 221621, upload-time = "2025-06-09T23:00:11.32Z" }, - { url = "https://files.pythonhosted.org/packages/c5/bf/7dcebae315436903b1d98ffb791a09d674c88480c158aa171958a3ac07f0/frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e", size = 39578, upload-time = "2025-06-09T23:00:13.526Z" }, - { url = "https://files.pythonhosted.org/packages/8f/5f/f69818f017fa9a3d24d1ae39763e29b7f60a59e46d5f91b9c6b21622f4cd/frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464", size = 43830, upload-time = "2025-06-09T23:00:14.98Z" }, - { url = "https://files.pythonhosted.org/packages/34/7e/803dde33760128acd393a27eb002f2020ddb8d99d30a44bfbaab31c5f08a/frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a", size = 82251, upload-time = "2025-06-09T23:00:16.279Z" }, - { url = "https://files.pythonhosted.org/packages/75/a9/9c2c5760b6ba45eae11334db454c189d43d34a4c0b489feb2175e5e64277/frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750", size = 48183, upload-time = "2025-06-09T23:00:17.698Z" }, - { url = "https://files.pythonhosted.org/packages/47/be/4038e2d869f8a2da165f35a6befb9158c259819be22eeaf9c9a8f6a87771/frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd", size = 47107, upload-time = "2025-06-09T23:00:18.952Z" }, - { url = "https://files.pythonhosted.org/packages/79/26/85314b8a83187c76a37183ceed886381a5f992975786f883472fcb6dc5f2/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2", size = 237333, upload-time = "2025-06-09T23:00:20.275Z" }, - { url = "https://files.pythonhosted.org/packages/1f/fd/e5b64f7d2c92a41639ffb2ad44a6a82f347787abc0c7df5f49057cf11770/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f", size = 231724, upload-time = "2025-06-09T23:00:21.705Z" }, - { url = "https://files.pythonhosted.org/packages/20/fb/03395c0a43a5976af4bf7534759d214405fbbb4c114683f434dfdd3128ef/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30", size = 245842, upload-time = "2025-06-09T23:00:23.148Z" }, - { url = "https://files.pythonhosted.org/packages/d0/15/c01c8e1dffdac5d9803507d824f27aed2ba76b6ed0026fab4d9866e82f1f/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98", size = 239767, upload-time = "2025-06-09T23:00:25.103Z" }, - { url = "https://files.pythonhosted.org/packages/14/99/3f4c6fe882c1f5514b6848aa0a69b20cb5e5d8e8f51a339d48c0e9305ed0/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86", size = 224130, upload-time = "2025-06-09T23:00:27.061Z" }, - { url = "https://files.pythonhosted.org/packages/4d/83/220a374bd7b2aeba9d0725130665afe11de347d95c3620b9b82cc2fcab97/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae", size = 235301, upload-time = "2025-06-09T23:00:29.02Z" }, - { url = "https://files.pythonhosted.org/packages/03/3c/3e3390d75334a063181625343e8daab61b77e1b8214802cc4e8a1bb678fc/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8", size = 234606, upload-time = "2025-06-09T23:00:30.514Z" }, - { url = "https://files.pythonhosted.org/packages/23/1e/58232c19608b7a549d72d9903005e2d82488f12554a32de2d5fb59b9b1ba/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31", size = 248372, upload-time = "2025-06-09T23:00:31.966Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a4/e4a567e01702a88a74ce8a324691e62a629bf47d4f8607f24bf1c7216e7f/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7", size = 229860, upload-time = "2025-06-09T23:00:33.375Z" }, - { url = "https://files.pythonhosted.org/packages/73/a6/63b3374f7d22268b41a9db73d68a8233afa30ed164c46107b33c4d18ecdd/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5", size = 245893, upload-time = "2025-06-09T23:00:35.002Z" }, - { url = "https://files.pythonhosted.org/packages/6d/eb/d18b3f6e64799a79673c4ba0b45e4cfbe49c240edfd03a68be20002eaeaa/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898", size = 246323, upload-time = "2025-06-09T23:00:36.468Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f5/720f3812e3d06cd89a1d5db9ff6450088b8f5c449dae8ffb2971a44da506/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56", size = 233149, upload-time = "2025-06-09T23:00:37.963Z" }, - { url = "https://files.pythonhosted.org/packages/69/68/03efbf545e217d5db8446acfd4c447c15b7c8cf4dbd4a58403111df9322d/frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7", size = 39565, upload-time = "2025-06-09T23:00:39.753Z" }, - { url = "https://files.pythonhosted.org/packages/58/17/fe61124c5c333ae87f09bb67186d65038834a47d974fc10a5fadb4cc5ae1/frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d", size = 44019, upload-time = "2025-06-09T23:00:40.988Z" }, - { url = "https://files.pythonhosted.org/packages/ef/a2/c8131383f1e66adad5f6ecfcce383d584ca94055a34d683bbb24ac5f2f1c/frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2", size = 81424, upload-time = "2025-06-09T23:00:42.24Z" }, - { url = "https://files.pythonhosted.org/packages/4c/9d/02754159955088cb52567337d1113f945b9e444c4960771ea90eb73de8db/frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb", size = 47952, upload-time = "2025-06-09T23:00:43.481Z" }, - { url = "https://files.pythonhosted.org/packages/01/7a/0046ef1bd6699b40acd2067ed6d6670b4db2f425c56980fa21c982c2a9db/frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478", size = 46688, upload-time = "2025-06-09T23:00:44.793Z" }, - { url = "https://files.pythonhosted.org/packages/d6/a2/a910bafe29c86997363fb4c02069df4ff0b5bc39d33c5198b4e9dd42d8f8/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8", size = 243084, upload-time = "2025-06-09T23:00:46.125Z" }, - { url = "https://files.pythonhosted.org/packages/64/3e/5036af9d5031374c64c387469bfcc3af537fc0f5b1187d83a1cf6fab1639/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08", size = 233524, upload-time = "2025-06-09T23:00:47.73Z" }, - { url = "https://files.pythonhosted.org/packages/06/39/6a17b7c107a2887e781a48ecf20ad20f1c39d94b2a548c83615b5b879f28/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4", size = 248493, upload-time = "2025-06-09T23:00:49.742Z" }, - { url = "https://files.pythonhosted.org/packages/be/00/711d1337c7327d88c44d91dd0f556a1c47fb99afc060ae0ef66b4d24793d/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b", size = 244116, upload-time = "2025-06-09T23:00:51.352Z" }, - { url = "https://files.pythonhosted.org/packages/24/fe/74e6ec0639c115df13d5850e75722750adabdc7de24e37e05a40527ca539/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e", size = 224557, upload-time = "2025-06-09T23:00:52.855Z" }, - { url = "https://files.pythonhosted.org/packages/8d/db/48421f62a6f77c553575201e89048e97198046b793f4a089c79a6e3268bd/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca", size = 241820, upload-time = "2025-06-09T23:00:54.43Z" }, - { url = "https://files.pythonhosted.org/packages/1d/fa/cb4a76bea23047c8462976ea7b7a2bf53997a0ca171302deae9d6dd12096/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df", size = 236542, upload-time = "2025-06-09T23:00:56.409Z" }, - { url = "https://files.pythonhosted.org/packages/5d/32/476a4b5cfaa0ec94d3f808f193301debff2ea42288a099afe60757ef6282/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5", size = 249350, upload-time = "2025-06-09T23:00:58.468Z" }, - { url = "https://files.pythonhosted.org/packages/8d/ba/9a28042f84a6bf8ea5dbc81cfff8eaef18d78b2a1ad9d51c7bc5b029ad16/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025", size = 225093, upload-time = "2025-06-09T23:01:00.015Z" }, - { url = "https://files.pythonhosted.org/packages/bc/29/3a32959e68f9cf000b04e79ba574527c17e8842e38c91d68214a37455786/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01", size = 245482, upload-time = "2025-06-09T23:01:01.474Z" }, - { url = "https://files.pythonhosted.org/packages/80/e8/edf2f9e00da553f07f5fa165325cfc302dead715cab6ac8336a5f3d0adc2/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08", size = 249590, upload-time = "2025-06-09T23:01:02.961Z" }, - { url = "https://files.pythonhosted.org/packages/1c/80/9a0eb48b944050f94cc51ee1c413eb14a39543cc4f760ed12657a5a3c45a/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43", size = 237785, upload-time = "2025-06-09T23:01:05.095Z" }, - { url = "https://files.pythonhosted.org/packages/f3/74/87601e0fb0369b7a2baf404ea921769c53b7ae00dee7dcfe5162c8c6dbf0/frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3", size = 39487, upload-time = "2025-06-09T23:01:06.54Z" }, - { url = "https://files.pythonhosted.org/packages/0b/15/c026e9a9fc17585a9d461f65d8593d281fedf55fbf7eb53f16c6df2392f9/frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a", size = 43874, upload-time = "2025-06-09T23:01:07.752Z" }, - { url = "https://files.pythonhosted.org/packages/24/90/6b2cebdabdbd50367273c20ff6b57a3dfa89bd0762de02c3a1eb42cb6462/frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee", size = 79791, upload-time = "2025-06-09T23:01:09.368Z" }, - { url = "https://files.pythonhosted.org/packages/83/2e/5b70b6a3325363293fe5fc3ae74cdcbc3e996c2a11dde2fd9f1fb0776d19/frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d", size = 47165, upload-time = "2025-06-09T23:01:10.653Z" }, - { url = "https://files.pythonhosted.org/packages/f4/25/a0895c99270ca6966110f4ad98e87e5662eab416a17e7fd53c364bf8b954/frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43", size = 45881, upload-time = "2025-06-09T23:01:12.296Z" }, - { url = "https://files.pythonhosted.org/packages/19/7c/71bb0bbe0832793c601fff68cd0cf6143753d0c667f9aec93d3c323f4b55/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d", size = 232409, upload-time = "2025-06-09T23:01:13.641Z" }, - { url = "https://files.pythonhosted.org/packages/c0/45/ed2798718910fe6eb3ba574082aaceff4528e6323f9a8570be0f7028d8e9/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee", size = 225132, upload-time = "2025-06-09T23:01:15.264Z" }, - { url = "https://files.pythonhosted.org/packages/ba/e2/8417ae0f8eacb1d071d4950f32f229aa6bf68ab69aab797b72a07ea68d4f/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb", size = 237638, upload-time = "2025-06-09T23:01:16.752Z" }, - { url = "https://files.pythonhosted.org/packages/f8/b7/2ace5450ce85f2af05a871b8c8719b341294775a0a6c5585d5e6170f2ce7/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f", size = 233539, upload-time = "2025-06-09T23:01:18.202Z" }, - { url = "https://files.pythonhosted.org/packages/46/b9/6989292c5539553dba63f3c83dc4598186ab2888f67c0dc1d917e6887db6/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60", size = 215646, upload-time = "2025-06-09T23:01:19.649Z" }, - { url = "https://files.pythonhosted.org/packages/72/31/bc8c5c99c7818293458fe745dab4fd5730ff49697ccc82b554eb69f16a24/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00", size = 232233, upload-time = "2025-06-09T23:01:21.175Z" }, - { url = "https://files.pythonhosted.org/packages/59/52/460db4d7ba0811b9ccb85af996019f5d70831f2f5f255f7cc61f86199795/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b", size = 227996, upload-time = "2025-06-09T23:01:23.098Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c9/f4b39e904c03927b7ecf891804fd3b4df3db29b9e487c6418e37988d6e9d/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c", size = 242280, upload-time = "2025-06-09T23:01:24.808Z" }, - { url = "https://files.pythonhosted.org/packages/b8/33/3f8d6ced42f162d743e3517781566b8481322be321b486d9d262adf70bfb/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949", size = 217717, upload-time = "2025-06-09T23:01:26.28Z" }, - { url = "https://files.pythonhosted.org/packages/3e/e8/ad683e75da6ccef50d0ab0c2b2324b32f84fc88ceee778ed79b8e2d2fe2e/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca", size = 236644, upload-time = "2025-06-09T23:01:27.887Z" }, - { url = "https://files.pythonhosted.org/packages/b2/14/8d19ccdd3799310722195a72ac94ddc677541fb4bef4091d8e7775752360/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b", size = 238879, upload-time = "2025-06-09T23:01:29.524Z" }, - { url = "https://files.pythonhosted.org/packages/ce/13/c12bf657494c2fd1079a48b2db49fa4196325909249a52d8f09bc9123fd7/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e", size = 232502, upload-time = "2025-06-09T23:01:31.287Z" }, - { url = "https://files.pythonhosted.org/packages/d7/8b/e7f9dfde869825489382bc0d512c15e96d3964180c9499efcec72e85db7e/frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1", size = 39169, upload-time = "2025-06-09T23:01:35.503Z" }, - { url = "https://files.pythonhosted.org/packages/35/89/a487a98d94205d85745080a37860ff5744b9820a2c9acbcdd9440bfddf98/frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba", size = 43219, upload-time = "2025-06-09T23:01:36.784Z" }, - { url = "https://files.pythonhosted.org/packages/56/d5/5c4cf2319a49eddd9dd7145e66c4866bdc6f3dbc67ca3d59685149c11e0d/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d", size = 84345, upload-time = "2025-06-09T23:01:38.295Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7d/ec2c1e1dc16b85bc9d526009961953df9cec8481b6886debb36ec9107799/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d", size = 48880, upload-time = "2025-06-09T23:01:39.887Z" }, - { url = "https://files.pythonhosted.org/packages/69/86/f9596807b03de126e11e7d42ac91e3d0b19a6599c714a1989a4e85eeefc4/frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b", size = 48498, upload-time = "2025-06-09T23:01:41.318Z" }, - { url = "https://files.pythonhosted.org/packages/5e/cb/df6de220f5036001005f2d726b789b2c0b65f2363b104bbc16f5be8084f8/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146", size = 292296, upload-time = "2025-06-09T23:01:42.685Z" }, - { url = "https://files.pythonhosted.org/packages/83/1f/de84c642f17c8f851a2905cee2dae401e5e0daca9b5ef121e120e19aa825/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74", size = 273103, upload-time = "2025-06-09T23:01:44.166Z" }, - { url = "https://files.pythonhosted.org/packages/88/3c/c840bfa474ba3fa13c772b93070893c6e9d5c0350885760376cbe3b6c1b3/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1", size = 292869, upload-time = "2025-06-09T23:01:45.681Z" }, - { url = "https://files.pythonhosted.org/packages/a6/1c/3efa6e7d5a39a1d5ef0abeb51c48fb657765794a46cf124e5aca2c7a592c/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1", size = 291467, upload-time = "2025-06-09T23:01:47.234Z" }, - { url = "https://files.pythonhosted.org/packages/4f/00/d5c5e09d4922c395e2f2f6b79b9a20dab4b67daaf78ab92e7729341f61f6/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384", size = 266028, upload-time = "2025-06-09T23:01:48.819Z" }, - { url = "https://files.pythonhosted.org/packages/4e/27/72765be905619dfde25a7f33813ac0341eb6b076abede17a2e3fbfade0cb/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb", size = 284294, upload-time = "2025-06-09T23:01:50.394Z" }, - { url = "https://files.pythonhosted.org/packages/88/67/c94103a23001b17808eb7dd1200c156bb69fb68e63fcf0693dde4cd6228c/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c", size = 281898, upload-time = "2025-06-09T23:01:52.234Z" }, - { url = "https://files.pythonhosted.org/packages/42/34/a3e2c00c00f9e2a9db5653bca3fec306349e71aff14ae45ecc6d0951dd24/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65", size = 290465, upload-time = "2025-06-09T23:01:53.788Z" }, - { url = "https://files.pythonhosted.org/packages/bb/73/f89b7fbce8b0b0c095d82b008afd0590f71ccb3dee6eee41791cf8cd25fd/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3", size = 266385, upload-time = "2025-06-09T23:01:55.769Z" }, - { url = "https://files.pythonhosted.org/packages/cd/45/e365fdb554159462ca12df54bc59bfa7a9a273ecc21e99e72e597564d1ae/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657", size = 288771, upload-time = "2025-06-09T23:01:57.4Z" }, - { url = "https://files.pythonhosted.org/packages/00/11/47b6117002a0e904f004d70ec5194fe9144f117c33c851e3d51c765962d0/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104", size = 288206, upload-time = "2025-06-09T23:01:58.936Z" }, - { url = "https://files.pythonhosted.org/packages/40/37/5f9f3c3fd7f7746082ec67bcdc204db72dad081f4f83a503d33220a92973/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf", size = 282620, upload-time = "2025-06-09T23:02:00.493Z" }, - { url = "https://files.pythonhosted.org/packages/0b/31/8fbc5af2d183bff20f21aa743b4088eac4445d2bb1cdece449ae80e4e2d1/frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81", size = 43059, upload-time = "2025-06-09T23:02:02.072Z" }, - { url = "https://files.pythonhosted.org/packages/bb/ed/41956f52105b8dbc26e457c5705340c67c8cc2b79f394b79bffc09d0e938/frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e", size = 47516, upload-time = "2025-06-09T23:02:03.779Z" }, - { url = "https://files.pythonhosted.org/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106, upload-time = "2025-06-09T23:02:34.204Z" }, -] - -[[package]] -name = "fsspec" -version = "2025.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8b/02/0835e6ab9cfc03916fe3f78c0956cfcdb6ff2669ffa6651065d5ebf7fc98/fsspec-2025.7.0.tar.gz", hash = "sha256:786120687ffa54b8283d942929540d8bc5ccfa820deb555a2b5d0ed2b737bf58", size = 304432, upload-time = "2025-07-15T16:05:21.19Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/e0/014d5d9d7a4564cf1c40b5039bc882db69fd881111e03ab3657ac0b218e2/fsspec-2025.7.0-py3-none-any.whl", hash = "sha256:8b012e39f63c7d5f10474de957f3ab793b47b45ae7d39f2fb735f8bbe25c0e21", size = 199597, upload-time = "2025-07-15T16:05:19.529Z" }, -] - -[[package]] -name = "h11" -version = "0.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, -] - -[[package]] -name = "html5lib" -version = "1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, - { name = "webencodings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ac/b6/b55c3f49042f1df3dcd422b7f224f939892ee94f22abcf503a9b7339eaf2/html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f", size = 272215, upload-time = "2020-06-22T23:32:38.834Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/dd/a834df6482147d48e225a49515aabc28974ad5a4ca3215c18a882565b028/html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d", size = 112173, upload-time = "2020-06-22T23:32:36.781Z" }, -] - -[[package]] -name = "httpcore" -version = "1.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, -] - -[[package]] -name = "httpx" -version = "0.28.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "certifi" }, - { name = "httpcore" }, - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, -] - -[[package]] -name = "identify" -version = "2.6.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/88/d193a27416618628a5eea64e3223acd800b40749a96ffb322a9b55a49ed1/identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6", size = 99254, upload-time = "2025-05-23T20:37:53.3Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/cd/18f8da995b658420625f7ef13f037be53ae04ec5ad33f9b718240dcfd48c/identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2", size = 99145, upload-time = "2025-05-23T20:37:51.495Z" }, -] - -[[package]] -name = "idna" -version = "3.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, -] - -[[package]] -name = "imagesize" -version = "1.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026, upload-time = "2022-07-01T12:21:05.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769, upload-time = "2022-07-01T12:21:02.467Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, -] - -[[package]] -name = "ipykernel" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "appnope", marker = "sys_platform == 'darwin'" }, - { name = "comm" }, - { name = "debugpy" }, - { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "matplotlib-inline" }, - { name = "nest-asyncio" }, - { name = "packaging" }, - { name = "psutil" }, - { name = "pyzmq" }, - { name = "tornado" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/a4/4948be6eb88628505b83a1f2f40d90254cab66abf2043b3c40fa07dfce0f/ipykernel-7.1.0.tar.gz", hash = "sha256:58a3fc88533d5930c3546dc7eac66c6d288acde4f801e2001e65edc5dc9cf0db", size = 174579, upload-time = "2025-10-27T09:46:39.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/17/20c2552266728ceba271967b87919664ecc0e33efca29c3efc6baf88c5f9/ipykernel-7.1.0-py3-none-any.whl", hash = "sha256:763b5ec6c5b7776f6a8d7ce09b267693b4e5ce75cb50ae696aaefb3c85e1ea4c", size = 117968, upload-time = "2025-10-27T09:46:37.805Z" }, -] - -[[package]] -name = "ipython" -version = "8.37.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11' and platform_python_implementation == 'PyPy'", - "python_full_version < '3.11' and platform_python_implementation != 'PyPy'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, - { name = "decorator", marker = "python_full_version < '3.11'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "jedi", marker = "python_full_version < '3.11'" }, - { name = "matplotlib-inline", marker = "python_full_version < '3.11'" }, - { name = "pexpect", marker = "python_full_version < '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit", marker = "python_full_version < '3.11'" }, - { name = "pygments", marker = "python_full_version < '3.11'" }, - { name = "stack-data", marker = "python_full_version < '3.11'" }, - { name = "traitlets", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088, upload-time = "2025-05-31T16:39:09.613Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864, upload-time = "2025-05-31T16:39:06.38Z" }, -] - -[[package]] -name = "ipython" -version = "9.4.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12' and platform_python_implementation == 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", - "python_full_version >= '3.12' and platform_python_implementation != 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, - { name = "decorator", marker = "python_full_version >= '3.11'" }, - { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" }, - { name = "jedi", marker = "python_full_version >= '3.11'" }, - { name = "matplotlib-inline", marker = "python_full_version >= '3.11'" }, - { name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit", marker = "python_full_version >= '3.11'" }, - { name = "pygments", marker = "python_full_version >= '3.11'" }, - { name = "stack-data", marker = "python_full_version >= '3.11'" }, - { name = "traitlets", marker = "python_full_version >= '3.11'" }, - { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/54/80/406f9e3bde1c1fd9bf5a0be9d090f8ae623e401b7670d8f6fdf2ab679891/ipython-9.4.0.tar.gz", hash = "sha256:c033c6d4e7914c3d9768aabe76bbe87ba1dc66a92a05db6bfa1125d81f2ee270", size = 4385338, upload-time = "2025-07-01T11:11:30.606Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/63/f8/0031ee2b906a15a33d6bfc12dd09c3dfa966b3cb5b284ecfb7549e6ac3c4/ipython-9.4.0-py3-none-any.whl", hash = "sha256:25850f025a446d9b359e8d296ba175a36aedd32e83ca9b5060430fe16801f066", size = 611021, upload-time = "2025-07-01T11:11:27.85Z" }, -] - -[[package]] -name = "ipython-pygments-lexers" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pygments", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, -] - -[[package]] -name = "ipywidgets" -version = "8.1.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "comm" }, - { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "jupyterlab-widgets" }, - { name = "traitlets" }, - { name = "widgetsnbextension" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3e/48/d3dbac45c2814cb73812f98dd6b38bbcc957a4e7bb31d6ea9c03bf94ed87/ipywidgets-8.1.7.tar.gz", hash = "sha256:15f1ac050b9ccbefd45dccfbb2ef6bed0029d8278682d569d71b8dd96bee0376", size = 116721, upload-time = "2025-05-05T12:42:03.489Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/6a/9166369a2f092bd286d24e6307de555d63616e8ddb373ebad2b5635ca4cd/ipywidgets-8.1.7-py3-none-any.whl", hash = "sha256:764f2602d25471c213919b8a1997df04bef869251db4ca8efba1b76b1bd9f7bb", size = 139806, upload-time = "2025-05-05T12:41:56.833Z" }, -] - -[[package]] -name = "isodate" -version = "0.7.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload-time = "2024-10-08T23:04:11.5Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload-time = "2024-10-08T23:04:09.501Z" }, -] - -[[package]] -name = "isoduration" -version = "20.11.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "arrow" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649, upload-time = "2020-11-01T11:00:00.312Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321, upload-time = "2020-11-01T10:59:58.02Z" }, -] - -[[package]] -name = "jedi" -version = "0.19.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "parso" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, -] - -[[package]] -name = "jmespath" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843, upload-time = "2022-06-17T18:00:12.224Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" }, -] - -[[package]] -name = "json5" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/be/c6c745ec4c4539b25a278b70e29793f10382947df0d9efba2fa09120895d/json5-0.12.0.tar.gz", hash = "sha256:0b4b6ff56801a1c7dc817b0241bca4ce474a0e6a163bfef3fc594d3fd263ff3a", size = 51907, upload-time = "2025-04-03T16:33:13.201Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/9f/3500910d5a98549e3098807493851eeef2b89cdd3032227558a104dfe926/json5-0.12.0-py3-none-any.whl", hash = "sha256:6d37aa6c08b0609f16e1ec5ff94697e2cbbfbad5ac112afa05794da9ab7810db", size = 36079, upload-time = "2025-04-03T16:33:11.927Z" }, -] - -[[package]] -name = "jsonpointer" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114, upload-time = "2024-06-10T19:24:42.462Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.25.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d5/00/a297a868e9d0784450faa7365c2172a7d6110c763e30ba861867c32ae6a9/jsonschema-4.25.0.tar.gz", hash = "sha256:e63acf5c11762c0e6672ffb61482bdf57f0876684d8d249c0fe2d730d48bc55f", size = 356830, upload-time = "2025-07-18T15:39:45.11Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/54/c86cd8e011fe98803d7e382fd67c0df5ceab8d2b7ad8c5a81524f791551c/jsonschema-4.25.0-py3-none-any.whl", hash = "sha256:24c2e8da302de79c8b9382fee3e76b355e44d2a4364bb207159ce10b517bd716", size = 89184, upload-time = "2025-07-18T15:39:42.956Z" }, -] - -[package.optional-dependencies] -format-nongpl = [ - { name = "fqdn" }, - { name = "idna" }, - { name = "isoduration" }, - { name = "jsonpointer" }, - { name = "rfc3339-validator" }, - { name = "rfc3986-validator" }, - { name = "rfc3987-syntax" }, - { name = "uri-template" }, - { name = "webcolors" }, -] - -[[package]] -name = "jsonschema-specifications" -version = "2025.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513, upload-time = "2025-04-23T12:34:07.418Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" }, -] - -[[package]] -name = "jupyter" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ipykernel" }, - { name = "ipywidgets" }, - { name = "jupyter-console" }, - { name = "jupyterlab" }, - { name = "nbconvert" }, - { name = "notebook" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959, upload-time = "2024-08-30T07:15:48.299Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657, upload-time = "2024-08-30T07:15:47.045Z" }, -] - -[[package]] -name = "jupyter-client" -version = "8.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-core" }, - { name = "python-dateutil" }, - { name = "pyzmq" }, - { name = "tornado" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/22/bf9f12fdaeae18019a468b68952a60fe6dbab5d67cd2a103cac7659b41ca/jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419", size = 342019, upload-time = "2024-09-17T10:44:17.613Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f", size = 106105, upload-time = "2024-09-17T10:44:15.218Z" }, -] - -[[package]] -name = "jupyter-console" -version = "6.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ipykernel" }, - { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "prompt-toolkit" }, - { name = "pygments" }, - { name = "pyzmq" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363, upload-time = "2023-03-06T14:13:31.02Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510, upload-time = "2023-03-06T14:13:28.229Z" }, -] - -[[package]] -name = "jupyter-core" -version = "5.8.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "platformdirs" }, - { name = "pywin32", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/99/1b/72906d554acfeb588332eaaa6f61577705e9ec752ddb486f302dafa292d9/jupyter_core-5.8.1.tar.gz", hash = "sha256:0a5f9706f70e64786b75acba995988915ebd4601c8a52e534a40b51c95f59941", size = 88923, upload-time = "2025-05-27T07:38:16.655Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/57/6bffd4b20b88da3800c5d691e0337761576ee688eb01299eae865689d2df/jupyter_core-5.8.1-py3-none-any.whl", hash = "sha256:c28d268fc90fb53f1338ded2eb410704c5449a358406e8a948b75706e24863d0", size = 28880, upload-time = "2025-05-27T07:38:15.137Z" }, -] - -[[package]] -name = "jupyter-events" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jsonschema", extra = ["format-nongpl"] }, - { name = "packaging" }, - { name = "python-json-logger" }, - { name = "pyyaml" }, - { name = "referencing" }, - { name = "rfc3339-validator" }, - { name = "rfc3986-validator" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9d/c3/306d090461e4cf3cd91eceaff84bede12a8e52cd821c2d20c9a4fd728385/jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b", size = 62196, upload-time = "2025-02-03T17:23:41.485Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb", size = 19430, upload-time = "2025-02-03T17:23:38.643Z" }, -] - -[[package]] -name = "jupyter-lsp" -version = "2.2.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-server" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/3d/40bdb41b665d3302390ed1356cebd5917c10769d1f190ee4ca595900840e/jupyter_lsp-2.2.6.tar.gz", hash = "sha256:0566bd9bb04fd9e6774a937ed01522b555ba78be37bebef787c8ab22de4c0361", size = 48948, upload-time = "2025-07-18T21:35:19.885Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/7c/12f68daf85b469b4896d5e4a629baa33c806d61de75ac5b39d8ef27ec4a2/jupyter_lsp-2.2.6-py3-none-any.whl", hash = "sha256:283783752bf0b459ee7fa88effa72104d87dd343b82d5c06cf113ef755b15b6d", size = 69371, upload-time = "2025-07-18T21:35:16.585Z" }, -] - -[[package]] -name = "jupyter-server" -version = "2.16.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "argon2-cffi" }, - { name = "jinja2" }, - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "jupyter-events" }, - { name = "jupyter-server-terminals" }, - { name = "nbconvert" }, - { name = "nbformat" }, - { name = "overrides" }, - { name = "packaging" }, - { name = "prometheus-client" }, - { name = "pywinpty", marker = "os_name == 'nt'" }, - { name = "pyzmq" }, - { name = "send2trash" }, - { name = "terminado" }, - { name = "tornado" }, - { name = "traitlets" }, - { name = "websocket-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/c8/ba2bbcd758c47f1124c4ca14061e8ce60d9c6fd537faee9534a95f83521a/jupyter_server-2.16.0.tar.gz", hash = "sha256:65d4b44fdf2dcbbdfe0aa1ace4a842d4aaf746a2b7b168134d5aaed35621b7f6", size = 728177, upload-time = "2025-05-12T16:44:46.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/1f/5ebbced977171d09a7b0c08a285ff9a20aafb9c51bde07e52349ff1ddd71/jupyter_server-2.16.0-py3-none-any.whl", hash = "sha256:3d8db5be3bc64403b1c65b400a1d7f4647a5ce743f3b20dbdefe8ddb7b55af9e", size = 386904, upload-time = "2025-05-12T16:44:43.335Z" }, -] - -[[package]] -name = "jupyter-server-terminals" -version = "0.5.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pywinpty", marker = "os_name == 'nt'" }, - { name = "terminado" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/d5/562469734f476159e99a55426d697cbf8e7eb5efe89fb0e0b4f83a3d3459/jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269", size = 31430, upload-time = "2024-03-12T14:37:03.049Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa", size = 13656, upload-time = "2024-03-12T14:37:00.708Z" }, -] - -[[package]] -name = "jupyterlab" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "async-lru" }, - { name = "httpx" }, - { name = "ipykernel" }, - { name = "jinja2" }, - { name = "jupyter-core" }, - { name = "jupyter-lsp" }, - { name = "jupyter-server" }, - { name = "jupyterlab-server" }, - { name = "notebook-shim" }, - { name = "packaging" }, - { name = "setuptools" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "tornado" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/09/21/413d142686a4e8f4268d985becbdb4daf060524726248e73be4773786987/jupyterlab-4.5.1.tar.gz", hash = "sha256:09da1ddfbd9eec18b5101dbb8515612aa1e47443321fb99503725a88e93d20d9", size = 23992251, upload-time = "2025-12-15T16:58:59.361Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-py3-none-any.whl", hash = "sha256:31b059de96de0754ff1f2ce6279774b6aab8c34d7082e9752db58207c99bd514", size = 12384821, upload-time = "2025-12-15T16:58:55.563Z" }, -] - -[[package]] -name = "jupyterlab-pygments" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900, upload-time = "2023-11-23T09:26:37.44Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884, upload-time = "2023-11-23T09:26:34.325Z" }, -] - -[[package]] -name = "jupyterlab-server" -version = "2.28.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "babel" }, - { name = "jinja2" }, - { name = "json5" }, - { name = "jsonschema" }, - { name = "jupyter-server" }, - { name = "packaging" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996, upload-time = "2025-10-22T13:59:18.37Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830, upload-time = "2025-10-22T13:59:16.767Z" }, -] - -[[package]] -name = "jupyterlab-widgets" -version = "3.0.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b9/7d/160595ca88ee87ac6ba95d82177d29ec60aaa63821d3077babb22ce031a5/jupyterlab_widgets-3.0.15.tar.gz", hash = "sha256:2920888a0c2922351a9202817957a68c07d99673504d6cd37345299e971bb08b", size = 213149, upload-time = "2025-05-05T12:32:31.004Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/6a/ca128561b22b60bd5a0c4ea26649e68c8556b82bc70a0c396eebc977fe86/jupyterlab_widgets-3.0.15-py3-none-any.whl", hash = "sha256:d59023d7d7ef71400d51e6fee9a88867f6e65e10a4201605d2d7f3e8f012a31c", size = 216571, upload-time = "2025-05-05T12:32:29.534Z" }, -] - -[[package]] -name = "lark" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/60/bc7622aefb2aee1c0b4ba23c1446d3e30225c8770b38d7aedbfb65ca9d5a/lark-1.2.2.tar.gz", hash = "sha256:ca807d0162cd16cef15a8feecb862d7319e7a09bdb13aef927968e45040fed80", size = 252132, upload-time = "2024-08-13T19:49:00.652Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/00/d90b10b962b4277f5e64a78b6609968859ff86889f5b898c1a778c06ec00/lark-1.2.2-py3-none-any.whl", hash = "sha256:c2276486b02f0f1b90be155f2c8ba4a8e194d42775786db622faccd652d8e80c", size = 111036, upload-time = "2024-08-13T19:48:58.603Z" }, -] - -[[package]] -name = "locket" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/83/97b29fe05cb6ae28d2dbd30b81e2e402a3eed5f460c26e9eaa5895ceacf5/locket-1.0.0.tar.gz", hash = "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632", size = 4350, upload-time = "2022-04-20T22:04:44.312Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl", hash = "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3", size = 4398, upload-time = "2022-04-20T22:04:42.23Z" }, -] - -[[package]] -name = "markupsafe" -version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" }, - { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" }, - { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" }, - { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" }, - { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964, upload-time = "2024-10-18T15:20:55.906Z" }, - { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" }, - { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366, upload-time = "2024-10-18T15:20:58.235Z" }, - { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" }, - { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065, upload-time = "2024-10-18T15:21:00.307Z" }, - { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514, upload-time = "2024-10-18T15:21:01.122Z" }, - { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" }, - { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" }, - { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" }, - { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" }, - { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" }, - { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" }, - { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" }, - { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" }, - { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" }, - { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, - { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, - { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, - { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, - { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, - { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, - { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, - { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, - { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, - { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, - { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, - { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, - { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, - { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, - { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, - { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, - { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, -] - -[[package]] -name = "matplotlib-inline" -version = "0.1.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159, upload-time = "2024-04-15T13:44:44.803Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899, upload-time = "2024-04-15T13:44:43.265Z" }, -] - -[[package]] -name = "mistune" -version = "3.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c4/79/bda47f7dd7c3c55770478d6d02c9960c430b0cf1773b72366ff89126ea31/mistune-3.1.3.tar.gz", hash = "sha256:a7035c21782b2becb6be62f8f25d3df81ccb4d6fa477a6525b15af06539f02a0", size = 94347, upload-time = "2025-03-19T14:27:24.955Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/4d/23c4e4f09da849e127e9f123241946c23c1e30f45a88366879e064211815/mistune-3.1.3-py3-none-any.whl", hash = "sha256:1a32314113cff28aa6432e99e522677c8587fd83e3d51c29b82a52409c842bd9", size = 53410, upload-time = "2025-03-19T14:27:23.451Z" }, -] - -[[package]] -name = "msal" -version = "1.33.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d5/da/81acbe0c1fd7e9e4ec35f55dadeba9833a847b9a6ba2e2d1e4432da901dd/msal-1.33.0.tar.gz", hash = "sha256:836ad80faa3e25a7d71015c990ce61f704a87328b1e73bcbb0623a18cbf17510", size = 153801, upload-time = "2025-07-22T19:36:33.693Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/5b/fbc73e91f7727ae1e79b21ed833308e99dc11cc1cd3d4717f579775de5e9/msal-1.33.0-py3-none-any.whl", hash = "sha256:c0cd41cecf8eaed733ee7e3be9e040291eba53b0f262d3ae9c58f38b04244273", size = 116853, upload-time = "2025-07-22T19:36:32.403Z" }, -] - -[[package]] -name = "msal-extensions" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "msal" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315, upload-time = "2025-03-14T23:51:03.902Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583, upload-time = "2025-03-14T23:51:03.016Z" }, -] - -[[package]] -name = "multidict" -version = "6.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/2c/5dad12e82fbdf7470f29bff2171484bf07cb3b16ada60a6589af8f376440/multidict-6.6.3.tar.gz", hash = "sha256:798a9eb12dab0a6c2e29c1de6f3468af5cb2da6053a20dfa3344907eed0937cc", size = 101006, upload-time = "2025-06-30T15:53:46.929Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/67/414933982bce2efce7cbcb3169eaaf901e0f25baec69432b4874dfb1f297/multidict-6.6.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a2be5b7b35271f7fff1397204ba6708365e3d773579fe2a30625e16c4b4ce817", size = 77017, upload-time = "2025-06-30T15:50:58.931Z" }, - { url = "https://files.pythonhosted.org/packages/8a/fe/d8a3ee1fad37dc2ef4f75488b0d9d4f25bf204aad8306cbab63d97bff64a/multidict-6.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12f4581d2930840295c461764b9a65732ec01250b46c6b2c510d7ee68872b140", size = 44897, upload-time = "2025-06-30T15:51:00.999Z" }, - { url = "https://files.pythonhosted.org/packages/1f/e0/265d89af8c98240265d82b8cbcf35897f83b76cd59ee3ab3879050fd8c45/multidict-6.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dd7793bab517e706c9ed9d7310b06c8672fd0aeee5781bfad612f56b8e0f7d14", size = 44574, upload-time = "2025-06-30T15:51:02.449Z" }, - { url = "https://files.pythonhosted.org/packages/e6/05/6b759379f7e8e04ccc97cfb2a5dcc5cdbd44a97f072b2272dc51281e6a40/multidict-6.6.3-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:72d8815f2cd3cf3df0f83cac3f3ef801d908b2d90409ae28102e0553af85545a", size = 225729, upload-time = "2025-06-30T15:51:03.794Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f5/8d5a15488edd9a91fa4aad97228d785df208ed6298580883aa3d9def1959/multidict-6.6.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:531e331a2ee53543ab32b16334e2deb26f4e6b9b28e41f8e0c87e99a6c8e2d69", size = 242515, upload-time = "2025-06-30T15:51:05.002Z" }, - { url = "https://files.pythonhosted.org/packages/6e/b5/a8f317d47d0ac5bb746d6d8325885c8967c2a8ce0bb57be5399e3642cccb/multidict-6.6.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:42ca5aa9329a63be8dc49040f63817d1ac980e02eeddba763a9ae5b4027b9c9c", size = 222224, upload-time = "2025-06-30T15:51:06.148Z" }, - { url = "https://files.pythonhosted.org/packages/76/88/18b2a0d5e80515fa22716556061189c2853ecf2aa2133081ebbe85ebea38/multidict-6.6.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:208b9b9757060b9faa6f11ab4bc52846e4f3c2fb8b14d5680c8aac80af3dc751", size = 253124, upload-time = "2025-06-30T15:51:07.375Z" }, - { url = "https://files.pythonhosted.org/packages/62/bf/ebfcfd6b55a1b05ef16d0775ae34c0fe15e8dab570d69ca9941073b969e7/multidict-6.6.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:acf6b97bd0884891af6a8b43d0f586ab2fcf8e717cbd47ab4bdddc09e20652d8", size = 251529, upload-time = "2025-06-30T15:51:08.691Z" }, - { url = "https://files.pythonhosted.org/packages/44/11/780615a98fd3775fc309d0234d563941af69ade2df0bb82c91dda6ddaea1/multidict-6.6.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:68e9e12ed00e2089725669bdc88602b0b6f8d23c0c95e52b95f0bc69f7fe9b55", size = 241627, upload-time = "2025-06-30T15:51:10.605Z" }, - { url = "https://files.pythonhosted.org/packages/28/3d/35f33045e21034b388686213752cabc3a1b9d03e20969e6fa8f1b1d82db1/multidict-6.6.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:05db2f66c9addb10cfa226e1acb363450fab2ff8a6df73c622fefe2f5af6d4e7", size = 239351, upload-time = "2025-06-30T15:51:12.18Z" }, - { url = "https://files.pythonhosted.org/packages/6e/cc/ff84c03b95b430015d2166d9aae775a3985d757b94f6635010d0038d9241/multidict-6.6.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0db58da8eafb514db832a1b44f8fa7906fdd102f7d982025f816a93ba45e3dcb", size = 233429, upload-time = "2025-06-30T15:51:13.533Z" }, - { url = "https://files.pythonhosted.org/packages/2e/f0/8cd49a0b37bdea673a4b793c2093f2f4ba8e7c9d6d7c9bd672fd6d38cd11/multidict-6.6.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:14117a41c8fdb3ee19c743b1c027da0736fdb79584d61a766da53d399b71176c", size = 243094, upload-time = "2025-06-30T15:51:14.815Z" }, - { url = "https://files.pythonhosted.org/packages/96/19/5d9a0cfdafe65d82b616a45ae950975820289069f885328e8185e64283c2/multidict-6.6.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:877443eaaabcd0b74ff32ebeed6f6176c71850feb7d6a1d2db65945256ea535c", size = 248957, upload-time = "2025-06-30T15:51:16.076Z" }, - { url = "https://files.pythonhosted.org/packages/e6/dc/c90066151da87d1e489f147b9b4327927241e65f1876702fafec6729c014/multidict-6.6.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:70b72e749a4f6e7ed8fb334fa8d8496384840319512746a5f42fa0aec79f4d61", size = 243590, upload-time = "2025-06-30T15:51:17.413Z" }, - { url = "https://files.pythonhosted.org/packages/ec/39/458afb0cccbb0ee9164365273be3e039efddcfcb94ef35924b7dbdb05db0/multidict-6.6.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:43571f785b86afd02b3855c5ac8e86ec921b760298d6f82ff2a61daf5a35330b", size = 237487, upload-time = "2025-06-30T15:51:19.039Z" }, - { url = "https://files.pythonhosted.org/packages/35/38/0016adac3990426610a081787011177e661875546b434f50a26319dc8372/multidict-6.6.3-cp310-cp310-win32.whl", hash = "sha256:20c5a0c3c13a15fd5ea86c42311859f970070e4e24de5a550e99d7c271d76318", size = 41390, upload-time = "2025-06-30T15:51:20.362Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/17897a8f3f2c5363d969b4c635aa40375fe1f09168dc09a7826780bfb2a4/multidict-6.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:ab0a34a007704c625e25a9116c6770b4d3617a071c8a7c30cd338dfbadfe6485", size = 45954, upload-time = "2025-06-30T15:51:21.383Z" }, - { url = "https://files.pythonhosted.org/packages/2d/5f/d4a717c1e457fe44072e33fa400d2b93eb0f2819c4d669381f925b7cba1f/multidict-6.6.3-cp310-cp310-win_arm64.whl", hash = "sha256:769841d70ca8bdd140a715746199fc6473414bd02efd678d75681d2d6a8986c5", size = 42981, upload-time = "2025-06-30T15:51:22.809Z" }, - { url = "https://files.pythonhosted.org/packages/08/f0/1a39863ced51f639c81a5463fbfa9eb4df59c20d1a8769ab9ef4ca57ae04/multidict-6.6.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:18f4eba0cbac3546b8ae31e0bbc55b02c801ae3cbaf80c247fcdd89b456ff58c", size = 76445, upload-time = "2025-06-30T15:51:24.01Z" }, - { url = "https://files.pythonhosted.org/packages/c9/0e/a7cfa451c7b0365cd844e90b41e21fab32edaa1e42fc0c9f68461ce44ed7/multidict-6.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef43b5dd842382329e4797c46f10748d8c2b6e0614f46b4afe4aee9ac33159df", size = 44610, upload-time = "2025-06-30T15:51:25.158Z" }, - { url = "https://files.pythonhosted.org/packages/c6/bb/a14a4efc5ee748cc1904b0748be278c31b9295ce5f4d2ef66526f410b94d/multidict-6.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bd1fd5eec01494e0f2e8e446a74a85d5e49afb63d75a9934e4a5423dba21d", size = 44267, upload-time = "2025-06-30T15:51:26.326Z" }, - { url = "https://files.pythonhosted.org/packages/c2/f8/410677d563c2d55e063ef74fe578f9d53fe6b0a51649597a5861f83ffa15/multidict-6.6.3-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5bd8d6f793a787153956cd35e24f60485bf0651c238e207b9a54f7458b16d539", size = 230004, upload-time = "2025-06-30T15:51:27.491Z" }, - { url = "https://files.pythonhosted.org/packages/fd/df/2b787f80059314a98e1ec6a4cc7576244986df3e56b3c755e6fc7c99e038/multidict-6.6.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bf99b4daf908c73856bd87ee0a2499c3c9a3d19bb04b9c6025e66af3fd07462", size = 247196, upload-time = "2025-06-30T15:51:28.762Z" }, - { url = "https://files.pythonhosted.org/packages/05/f2/f9117089151b9a8ab39f9019620d10d9718eec2ac89e7ca9d30f3ec78e96/multidict-6.6.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b9e59946b49dafaf990fd9c17ceafa62976e8471a14952163d10a7a630413a9", size = 225337, upload-time = "2025-06-30T15:51:30.025Z" }, - { url = "https://files.pythonhosted.org/packages/93/2d/7115300ec5b699faa152c56799b089a53ed69e399c3c2d528251f0aeda1a/multidict-6.6.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e2db616467070d0533832d204c54eea6836a5e628f2cb1e6dfd8cd6ba7277cb7", size = 257079, upload-time = "2025-06-30T15:51:31.716Z" }, - { url = "https://files.pythonhosted.org/packages/15/ea/ff4bab367623e39c20d3b07637225c7688d79e4f3cc1f3b9f89867677f9a/multidict-6.6.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7394888236621f61dcdd25189b2768ae5cc280f041029a5bcf1122ac63df79f9", size = 255461, upload-time = "2025-06-30T15:51:33.029Z" }, - { url = "https://files.pythonhosted.org/packages/74/07/2c9246cda322dfe08be85f1b8739646f2c4c5113a1422d7a407763422ec4/multidict-6.6.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f114d8478733ca7388e7c7e0ab34b72547476b97009d643644ac33d4d3fe1821", size = 246611, upload-time = "2025-06-30T15:51:34.47Z" }, - { url = "https://files.pythonhosted.org/packages/a8/62/279c13d584207d5697a752a66ffc9bb19355a95f7659140cb1b3cf82180e/multidict-6.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cdf22e4db76d323bcdc733514bf732e9fb349707c98d341d40ebcc6e9318ef3d", size = 243102, upload-time = "2025-06-30T15:51:36.525Z" }, - { url = "https://files.pythonhosted.org/packages/69/cc/e06636f48c6d51e724a8bc8d9e1db5f136fe1df066d7cafe37ef4000f86a/multidict-6.6.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e995a34c3d44ab511bfc11aa26869b9d66c2d8c799fa0e74b28a473a692532d6", size = 238693, upload-time = "2025-06-30T15:51:38.278Z" }, - { url = "https://files.pythonhosted.org/packages/89/a4/66c9d8fb9acf3b226cdd468ed009537ac65b520aebdc1703dd6908b19d33/multidict-6.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:766a4a5996f54361d8d5a9050140aa5362fe48ce51c755a50c0bc3706460c430", size = 246582, upload-time = "2025-06-30T15:51:39.709Z" }, - { url = "https://files.pythonhosted.org/packages/cf/01/c69e0317be556e46257826d5449feb4e6aa0d18573e567a48a2c14156f1f/multidict-6.6.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3893a0d7d28a7fe6ca7a1f760593bc13038d1d35daf52199d431b61d2660602b", size = 253355, upload-time = "2025-06-30T15:51:41.013Z" }, - { url = "https://files.pythonhosted.org/packages/c0/da/9cc1da0299762d20e626fe0042e71b5694f9f72d7d3f9678397cbaa71b2b/multidict-6.6.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:934796c81ea996e61914ba58064920d6cad5d99140ac3167901eb932150e2e56", size = 247774, upload-time = "2025-06-30T15:51:42.291Z" }, - { url = "https://files.pythonhosted.org/packages/e6/91/b22756afec99cc31105ddd4a52f95ab32b1a4a58f4d417979c570c4a922e/multidict-6.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9ed948328aec2072bc00f05d961ceadfd3e9bfc2966c1319aeaf7b7c21219183", size = 242275, upload-time = "2025-06-30T15:51:43.642Z" }, - { url = "https://files.pythonhosted.org/packages/be/f1/adcc185b878036a20399d5be5228f3cbe7f823d78985d101d425af35c800/multidict-6.6.3-cp311-cp311-win32.whl", hash = "sha256:9f5b28c074c76afc3e4c610c488e3493976fe0e596dd3db6c8ddfbb0134dcac5", size = 41290, upload-time = "2025-06-30T15:51:45.264Z" }, - { url = "https://files.pythonhosted.org/packages/e0/d4/27652c1c6526ea6b4f5ddd397e93f4232ff5de42bea71d339bc6a6cc497f/multidict-6.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:bc7f6fbc61b1c16050a389c630da0b32fc6d4a3d191394ab78972bf5edc568c2", size = 45942, upload-time = "2025-06-30T15:51:46.377Z" }, - { url = "https://files.pythonhosted.org/packages/16/18/23f4932019804e56d3c2413e237f866444b774b0263bcb81df2fdecaf593/multidict-6.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:d4e47d8faffaae822fb5cba20937c048d4f734f43572e7079298a6c39fb172cb", size = 42880, upload-time = "2025-06-30T15:51:47.561Z" }, - { url = "https://files.pythonhosted.org/packages/0e/a0/6b57988ea102da0623ea814160ed78d45a2645e4bbb499c2896d12833a70/multidict-6.6.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:056bebbeda16b2e38642d75e9e5310c484b7c24e3841dc0fb943206a72ec89d6", size = 76514, upload-time = "2025-06-30T15:51:48.728Z" }, - { url = "https://files.pythonhosted.org/packages/07/7a/d1e92665b0850c6c0508f101f9cf0410c1afa24973e1115fe9c6a185ebf7/multidict-6.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e5f481cccb3c5c5e5de5d00b5141dc589c1047e60d07e85bbd7dea3d4580d63f", size = 45394, upload-time = "2025-06-30T15:51:49.986Z" }, - { url = "https://files.pythonhosted.org/packages/52/6f/dd104490e01be6ef8bf9573705d8572f8c2d2c561f06e3826b081d9e6591/multidict-6.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:10bea2ee839a759ee368b5a6e47787f399b41e70cf0c20d90dfaf4158dfb4e55", size = 43590, upload-time = "2025-06-30T15:51:51.331Z" }, - { url = "https://files.pythonhosted.org/packages/44/fe/06e0e01b1b0611e6581b7fd5a85b43dacc08b6cea3034f902f383b0873e5/multidict-6.6.3-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2334cfb0fa9549d6ce2c21af2bfbcd3ac4ec3646b1b1581c88e3e2b1779ec92b", size = 237292, upload-time = "2025-06-30T15:51:52.584Z" }, - { url = "https://files.pythonhosted.org/packages/ce/71/4f0e558fb77696b89c233c1ee2d92f3e1d5459070a0e89153c9e9e804186/multidict-6.6.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8fee016722550a2276ca2cb5bb624480e0ed2bd49125b2b73b7010b9090e888", size = 258385, upload-time = "2025-06-30T15:51:53.913Z" }, - { url = "https://files.pythonhosted.org/packages/e3/25/cca0e68228addad24903801ed1ab42e21307a1b4b6dd2cf63da5d3ae082a/multidict-6.6.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5511cb35f5c50a2db21047c875eb42f308c5583edf96bd8ebf7d770a9d68f6d", size = 242328, upload-time = "2025-06-30T15:51:55.672Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a3/46f2d420d86bbcb8fe660b26a10a219871a0fbf4d43cb846a4031533f3e0/multidict-6.6.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:712b348f7f449948e0a6c4564a21c7db965af900973a67db432d724619b3c680", size = 268057, upload-time = "2025-06-30T15:51:57.037Z" }, - { url = "https://files.pythonhosted.org/packages/9e/73/1c743542fe00794a2ec7466abd3f312ccb8fad8dff9f36d42e18fb1ec33e/multidict-6.6.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e4e15d2138ee2694e038e33b7c3da70e6b0ad8868b9f8094a72e1414aeda9c1a", size = 269341, upload-time = "2025-06-30T15:51:59.111Z" }, - { url = "https://files.pythonhosted.org/packages/a4/11/6ec9dcbe2264b92778eeb85407d1df18812248bf3506a5a1754bc035db0c/multidict-6.6.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8df25594989aebff8a130f7899fa03cbfcc5d2b5f4a461cf2518236fe6f15961", size = 256081, upload-time = "2025-06-30T15:52:00.533Z" }, - { url = "https://files.pythonhosted.org/packages/9b/2b/631b1e2afeb5f1696846d747d36cda075bfdc0bc7245d6ba5c319278d6c4/multidict-6.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:159ca68bfd284a8860f8d8112cf0521113bffd9c17568579e4d13d1f1dc76b65", size = 253581, upload-time = "2025-06-30T15:52:02.43Z" }, - { url = "https://files.pythonhosted.org/packages/bf/0e/7e3b93f79efeb6111d3bf9a1a69e555ba1d07ad1c11bceb56b7310d0d7ee/multidict-6.6.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e098c17856a8c9ade81b4810888c5ad1914099657226283cab3062c0540b0643", size = 250750, upload-time = "2025-06-30T15:52:04.26Z" }, - { url = "https://files.pythonhosted.org/packages/ad/9e/086846c1d6601948e7de556ee464a2d4c85e33883e749f46b9547d7b0704/multidict-6.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:67c92ed673049dec52d7ed39f8cf9ebbadf5032c774058b4406d18c8f8fe7063", size = 251548, upload-time = "2025-06-30T15:52:06.002Z" }, - { url = "https://files.pythonhosted.org/packages/8c/7b/86ec260118e522f1a31550e87b23542294880c97cfbf6fb18cc67b044c66/multidict-6.6.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:bd0578596e3a835ef451784053cfd327d607fc39ea1a14812139339a18a0dbc3", size = 262718, upload-time = "2025-06-30T15:52:07.707Z" }, - { url = "https://files.pythonhosted.org/packages/8c/bd/22ce8f47abb0be04692c9fc4638508b8340987b18691aa7775d927b73f72/multidict-6.6.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:346055630a2df2115cd23ae271910b4cae40f4e336773550dca4889b12916e75", size = 259603, upload-time = "2025-06-30T15:52:09.58Z" }, - { url = "https://files.pythonhosted.org/packages/07/9c/91b7ac1691be95cd1f4a26e36a74b97cda6aa9820632d31aab4410f46ebd/multidict-6.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:555ff55a359302b79de97e0468e9ee80637b0de1fce77721639f7cd9440b3a10", size = 251351, upload-time = "2025-06-30T15:52:10.947Z" }, - { url = "https://files.pythonhosted.org/packages/6f/5c/4d7adc739884f7a9fbe00d1eac8c034023ef8bad71f2ebe12823ca2e3649/multidict-6.6.3-cp312-cp312-win32.whl", hash = "sha256:73ab034fb8d58ff85c2bcbadc470efc3fafeea8affcf8722855fb94557f14cc5", size = 41860, upload-time = "2025-06-30T15:52:12.334Z" }, - { url = "https://files.pythonhosted.org/packages/6a/a3/0fbc7afdf7cb1aa12a086b02959307848eb6bcc8f66fcb66c0cb57e2a2c1/multidict-6.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:04cbcce84f63b9af41bad04a54d4cc4e60e90c35b9e6ccb130be2d75b71f8c17", size = 45982, upload-time = "2025-06-30T15:52:13.6Z" }, - { url = "https://files.pythonhosted.org/packages/b8/95/8c825bd70ff9b02462dc18d1295dd08d3e9e4eb66856d292ffa62cfe1920/multidict-6.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:0f1130b896ecb52d2a1e615260f3ea2af55fa7dc3d7c3003ba0c3121a759b18b", size = 43210, upload-time = "2025-06-30T15:52:14.893Z" }, - { url = "https://files.pythonhosted.org/packages/52/1d/0bebcbbb4f000751fbd09957257903d6e002943fc668d841a4cf2fb7f872/multidict-6.6.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:540d3c06d48507357a7d57721e5094b4f7093399a0106c211f33540fdc374d55", size = 75843, upload-time = "2025-06-30T15:52:16.155Z" }, - { url = "https://files.pythonhosted.org/packages/07/8f/cbe241b0434cfe257f65c2b1bcf9e8d5fb52bc708c5061fb29b0fed22bdf/multidict-6.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9c19cea2a690f04247d43f366d03e4eb110a0dc4cd1bbeee4d445435428ed35b", size = 45053, upload-time = "2025-06-30T15:52:17.429Z" }, - { url = "https://files.pythonhosted.org/packages/32/d2/0b3b23f9dbad5b270b22a3ac3ea73ed0a50ef2d9a390447061178ed6bdb8/multidict-6.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7af039820cfd00effec86bda5d8debef711a3e86a1d3772e85bea0f243a4bd65", size = 43273, upload-time = "2025-06-30T15:52:19.346Z" }, - { url = "https://files.pythonhosted.org/packages/fd/fe/6eb68927e823999e3683bc49678eb20374ba9615097d085298fd5b386564/multidict-6.6.3-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:500b84f51654fdc3944e936f2922114349bf8fdcac77c3092b03449f0e5bc2b3", size = 237124, upload-time = "2025-06-30T15:52:20.773Z" }, - { url = "https://files.pythonhosted.org/packages/e7/ab/320d8507e7726c460cb77117848b3834ea0d59e769f36fdae495f7669929/multidict-6.6.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3fc723ab8a5c5ed6c50418e9bfcd8e6dceba6c271cee6728a10a4ed8561520c", size = 256892, upload-time = "2025-06-30T15:52:22.242Z" }, - { url = "https://files.pythonhosted.org/packages/76/60/38ee422db515ac69834e60142a1a69111ac96026e76e8e9aa347fd2e4591/multidict-6.6.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:94c47ea3ade005b5976789baaed66d4de4480d0a0bf31cef6edaa41c1e7b56a6", size = 240547, upload-time = "2025-06-30T15:52:23.736Z" }, - { url = "https://files.pythonhosted.org/packages/27/fb/905224fde2dff042b030c27ad95a7ae744325cf54b890b443d30a789b80e/multidict-6.6.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dbc7cf464cc6d67e83e136c9f55726da3a30176f020a36ead246eceed87f1cd8", size = 266223, upload-time = "2025-06-30T15:52:25.185Z" }, - { url = "https://files.pythonhosted.org/packages/76/35/dc38ab361051beae08d1a53965e3e1a418752fc5be4d3fb983c5582d8784/multidict-6.6.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:900eb9f9da25ada070f8ee4a23f884e0ee66fe4e1a38c3af644256a508ad81ca", size = 267262, upload-time = "2025-06-30T15:52:26.969Z" }, - { url = "https://files.pythonhosted.org/packages/1f/a3/0a485b7f36e422421b17e2bbb5a81c1af10eac1d4476f2ff92927c730479/multidict-6.6.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c6df517cf177da5d47ab15407143a89cd1a23f8b335f3a28d57e8b0a3dbb884", size = 254345, upload-time = "2025-06-30T15:52:28.467Z" }, - { url = "https://files.pythonhosted.org/packages/b4/59/bcdd52c1dab7c0e0d75ff19cac751fbd5f850d1fc39172ce809a74aa9ea4/multidict-6.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4ef421045f13879e21c994b36e728d8e7d126c91a64b9185810ab51d474f27e7", size = 252248, upload-time = "2025-06-30T15:52:29.938Z" }, - { url = "https://files.pythonhosted.org/packages/bb/a4/2d96aaa6eae8067ce108d4acee6f45ced5728beda55c0f02ae1072c730d1/multidict-6.6.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6c1e61bb4f80895c081790b6b09fa49e13566df8fbff817da3f85b3a8192e36b", size = 250115, upload-time = "2025-06-30T15:52:31.416Z" }, - { url = "https://files.pythonhosted.org/packages/25/d2/ed9f847fa5c7d0677d4f02ea2c163d5e48573de3f57bacf5670e43a5ffaa/multidict-6.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e5e8523bb12d7623cd8300dbd91b9e439a46a028cd078ca695eb66ba31adee3c", size = 249649, upload-time = "2025-06-30T15:52:32.996Z" }, - { url = "https://files.pythonhosted.org/packages/1f/af/9155850372563fc550803d3f25373308aa70f59b52cff25854086ecb4a79/multidict-6.6.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ef58340cc896219e4e653dade08fea5c55c6df41bcc68122e3be3e9d873d9a7b", size = 261203, upload-time = "2025-06-30T15:52:34.521Z" }, - { url = "https://files.pythonhosted.org/packages/36/2f/c6a728f699896252cf309769089568a33c6439626648843f78743660709d/multidict-6.6.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc9dc435ec8699e7b602b94fe0cd4703e69273a01cbc34409af29e7820f777f1", size = 258051, upload-time = "2025-06-30T15:52:35.999Z" }, - { url = "https://files.pythonhosted.org/packages/d0/60/689880776d6b18fa2b70f6cc74ff87dd6c6b9b47bd9cf74c16fecfaa6ad9/multidict-6.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9e864486ef4ab07db5e9cb997bad2b681514158d6954dd1958dfb163b83d53e6", size = 249601, upload-time = "2025-06-30T15:52:37.473Z" }, - { url = "https://files.pythonhosted.org/packages/75/5e/325b11f2222a549019cf2ef879c1f81f94a0d40ace3ef55cf529915ba6cc/multidict-6.6.3-cp313-cp313-win32.whl", hash = "sha256:5633a82fba8e841bc5c5c06b16e21529573cd654f67fd833650a215520a6210e", size = 41683, upload-time = "2025-06-30T15:52:38.927Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ad/cf46e73f5d6e3c775cabd2a05976547f3f18b39bee06260369a42501f053/multidict-6.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:e93089c1570a4ad54c3714a12c2cef549dc9d58e97bcded193d928649cab78e9", size = 45811, upload-time = "2025-06-30T15:52:40.207Z" }, - { url = "https://files.pythonhosted.org/packages/c5/c9/2e3fe950db28fb7c62e1a5f46e1e38759b072e2089209bc033c2798bb5ec/multidict-6.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:c60b401f192e79caec61f166da9c924e9f8bc65548d4246842df91651e83d600", size = 43056, upload-time = "2025-06-30T15:52:41.575Z" }, - { url = "https://files.pythonhosted.org/packages/3a/58/aaf8114cf34966e084a8cc9517771288adb53465188843d5a19862cb6dc3/multidict-6.6.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:02fd8f32d403a6ff13864b0851f1f523d4c988051eea0471d4f1fd8010f11134", size = 82811, upload-time = "2025-06-30T15:52:43.281Z" }, - { url = "https://files.pythonhosted.org/packages/71/af/5402e7b58a1f5b987a07ad98f2501fdba2a4f4b4c30cf114e3ce8db64c87/multidict-6.6.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f3aa090106b1543f3f87b2041eef3c156c8da2aed90c63a2fbed62d875c49c37", size = 48304, upload-time = "2025-06-30T15:52:45.026Z" }, - { url = "https://files.pythonhosted.org/packages/39/65/ab3c8cafe21adb45b24a50266fd747147dec7847425bc2a0f6934b3ae9ce/multidict-6.6.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e924fb978615a5e33ff644cc42e6aa241effcf4f3322c09d4f8cebde95aff5f8", size = 46775, upload-time = "2025-06-30T15:52:46.459Z" }, - { url = "https://files.pythonhosted.org/packages/49/ba/9fcc1b332f67cc0c0c8079e263bfab6660f87fe4e28a35921771ff3eea0d/multidict-6.6.3-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:b9fe5a0e57c6dbd0e2ce81ca66272282c32cd11d31658ee9553849d91289e1c1", size = 229773, upload-time = "2025-06-30T15:52:47.88Z" }, - { url = "https://files.pythonhosted.org/packages/a4/14/0145a251f555f7c754ce2dcbcd012939bbd1f34f066fa5d28a50e722a054/multidict-6.6.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b24576f208793ebae00280c59927c3b7c2a3b1655e443a25f753c4611bc1c373", size = 250083, upload-time = "2025-06-30T15:52:49.366Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d4/d5c0bd2bbb173b586c249a151a26d2fb3ec7d53c96e42091c9fef4e1f10c/multidict-6.6.3-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:135631cb6c58eac37d7ac0df380294fecdc026b28837fa07c02e459c7fb9c54e", size = 228980, upload-time = "2025-06-30T15:52:50.903Z" }, - { url = "https://files.pythonhosted.org/packages/21/32/c9a2d8444a50ec48c4733ccc67254100c10e1c8ae8e40c7a2d2183b59b97/multidict-6.6.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:274d416b0df887aef98f19f21578653982cfb8a05b4e187d4a17103322eeaf8f", size = 257776, upload-time = "2025-06-30T15:52:52.764Z" }, - { url = "https://files.pythonhosted.org/packages/68/d0/14fa1699f4ef629eae08ad6201c6b476098f5efb051b296f4c26be7a9fdf/multidict-6.6.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e252017a817fad7ce05cafbe5711ed40faeb580e63b16755a3a24e66fa1d87c0", size = 256882, upload-time = "2025-06-30T15:52:54.596Z" }, - { url = "https://files.pythonhosted.org/packages/da/88/84a27570fbe303c65607d517a5f147cd2fc046c2d1da02b84b17b9bdc2aa/multidict-6.6.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4cc8d848cd4fe1cdee28c13ea79ab0ed37fc2e89dd77bac86a2e7959a8c3bc", size = 247816, upload-time = "2025-06-30T15:52:56.175Z" }, - { url = "https://files.pythonhosted.org/packages/1c/60/dca352a0c999ce96a5d8b8ee0b2b9f729dcad2e0b0c195f8286269a2074c/multidict-6.6.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9e236a7094b9c4c1b7585f6b9cca34b9d833cf079f7e4c49e6a4a6ec9bfdc68f", size = 245341, upload-time = "2025-06-30T15:52:57.752Z" }, - { url = "https://files.pythonhosted.org/packages/50/ef/433fa3ed06028f03946f3993223dada70fb700f763f70c00079533c34578/multidict-6.6.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e0cb0ab69915c55627c933f0b555a943d98ba71b4d1c57bc0d0a66e2567c7471", size = 235854, upload-time = "2025-06-30T15:52:59.74Z" }, - { url = "https://files.pythonhosted.org/packages/1b/1f/487612ab56fbe35715320905215a57fede20de7db40a261759690dc80471/multidict-6.6.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:81ef2f64593aba09c5212a3d0f8c906a0d38d710a011f2f42759704d4557d3f2", size = 243432, upload-time = "2025-06-30T15:53:01.602Z" }, - { url = "https://files.pythonhosted.org/packages/da/6f/ce8b79de16cd885c6f9052c96a3671373d00c59b3ee635ea93e6e81b8ccf/multidict-6.6.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:b9cbc60010de3562545fa198bfc6d3825df430ea96d2cc509c39bd71e2e7d648", size = 252731, upload-time = "2025-06-30T15:53:03.517Z" }, - { url = "https://files.pythonhosted.org/packages/bb/fe/a2514a6aba78e5abefa1624ca85ae18f542d95ac5cde2e3815a9fbf369aa/multidict-6.6.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:70d974eaaa37211390cd02ef93b7e938de564bbffa866f0b08d07e5e65da783d", size = 247086, upload-time = "2025-06-30T15:53:05.48Z" }, - { url = "https://files.pythonhosted.org/packages/8c/22/b788718d63bb3cce752d107a57c85fcd1a212c6c778628567c9713f9345a/multidict-6.6.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3713303e4a6663c6d01d648a68f2848701001f3390a030edaaf3fc949c90bf7c", size = 243338, upload-time = "2025-06-30T15:53:07.522Z" }, - { url = "https://files.pythonhosted.org/packages/22/d6/fdb3d0670819f2228f3f7d9af613d5e652c15d170c83e5f1c94fbc55a25b/multidict-6.6.3-cp313-cp313t-win32.whl", hash = "sha256:639ecc9fe7cd73f2495f62c213e964843826f44505a3e5d82805aa85cac6f89e", size = 47812, upload-time = "2025-06-30T15:53:09.263Z" }, - { url = "https://files.pythonhosted.org/packages/b6/d6/a9d2c808f2c489ad199723197419207ecbfbc1776f6e155e1ecea9c883aa/multidict-6.6.3-cp313-cp313t-win_amd64.whl", hash = "sha256:9f97e181f344a0ef3881b573d31de8542cc0dbc559ec68c8f8b5ce2c2e91646d", size = 53011, upload-time = "2025-06-30T15:53:11.038Z" }, - { url = "https://files.pythonhosted.org/packages/f2/40/b68001cba8188dd267590a111f9661b6256debc327137667e832bf5d66e8/multidict-6.6.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ce8b7693da41a3c4fde5871c738a81490cea5496c671d74374c8ab889e1834fb", size = 45254, upload-time = "2025-06-30T15:53:12.421Z" }, - { url = "https://files.pythonhosted.org/packages/d8/30/9aec301e9772b098c1f5c0ca0279237c9766d94b97802e9888010c64b0ed/multidict-6.6.3-py3-none-any.whl", hash = "sha256:8db10f29c7541fc5da4defd8cd697e1ca429db743fa716325f236079b96f775a", size = 12313, upload-time = "2025-06-30T15:53:45.437Z" }, -] - -[[package]] -name = "mypy" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1e/e3/034322d5a779685218ed69286c32faa505247f1f096251ef66c8fd203b08/mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03", size = 3352114, upload-time = "2025-07-14T20:34:30.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/31/e762baa3b73905c856d45ab77b4af850e8159dffffd86a52879539a08c6b/mypy-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8e08de6138043108b3b18f09d3f817a4783912e48828ab397ecf183135d84d6", size = 10998313, upload-time = "2025-07-14T20:33:24.519Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c1/25b2f0d46fb7e0b5e2bee61ec3a47fe13eff9e3c2f2234f144858bbe6485/mypy-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce4a17920ec144647d448fc43725b5873548b1aae6c603225626747ededf582d", size = 10128922, upload-time = "2025-07-14T20:34:06.414Z" }, - { url = "https://files.pythonhosted.org/packages/02/78/6d646603a57aa8a2886df1b8881fe777ea60f28098790c1089230cd9c61d/mypy-1.17.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ff25d151cc057fdddb1cb1881ef36e9c41fa2a5e78d8dd71bee6e4dcd2bc05b", size = 11913524, upload-time = "2025-07-14T20:33:19.109Z" }, - { url = "https://files.pythonhosted.org/packages/4f/19/dae6c55e87ee426fb76980f7e78484450cad1c01c55a1dc4e91c930bea01/mypy-1.17.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93468cf29aa9a132bceb103bd8475f78cacde2b1b9a94fd978d50d4bdf616c9a", size = 12650527, upload-time = "2025-07-14T20:32:44.095Z" }, - { url = "https://files.pythonhosted.org/packages/86/e1/f916845a235235a6c1e4d4d065a3930113767001d491b8b2e1b61ca56647/mypy-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:98189382b310f16343151f65dd7e6867386d3e35f7878c45cfa11383d175d91f", size = 12897284, upload-time = "2025-07-14T20:33:38.168Z" }, - { url = "https://files.pythonhosted.org/packages/ae/dc/414760708a4ea1b096bd214d26a24e30ac5e917ef293bc33cdb6fe22d2da/mypy-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:c004135a300ab06a045c1c0d8e3f10215e71d7b4f5bb9a42ab80236364429937", size = 9506493, upload-time = "2025-07-14T20:34:01.093Z" }, - { url = "https://files.pythonhosted.org/packages/d4/24/82efb502b0b0f661c49aa21cfe3e1999ddf64bf5500fc03b5a1536a39d39/mypy-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d4fe5c72fd262d9c2c91c1117d16aac555e05f5beb2bae6a755274c6eec42be", size = 10914150, upload-time = "2025-07-14T20:31:51.985Z" }, - { url = "https://files.pythonhosted.org/packages/03/96/8ef9a6ff8cedadff4400e2254689ca1dc4b420b92c55255b44573de10c54/mypy-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96b196e5c16f41b4f7736840e8455958e832871990c7ba26bf58175e357ed61", size = 10039845, upload-time = "2025-07-14T20:32:30.527Z" }, - { url = "https://files.pythonhosted.org/packages/df/32/7ce359a56be779d38021d07941cfbb099b41411d72d827230a36203dbb81/mypy-1.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a0ff2dd10337ceb521c080d4147755ee302dcde6e1a913babd59473904615f", size = 11837246, upload-time = "2025-07-14T20:32:01.28Z" }, - { url = "https://files.pythonhosted.org/packages/82/16/b775047054de4d8dbd668df9137707e54b07fe18c7923839cd1e524bf756/mypy-1.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cfcc1179c4447854e9e406d3af0f77736d631ec87d31c6281ecd5025df625d", size = 12571106, upload-time = "2025-07-14T20:34:26.942Z" }, - { url = "https://files.pythonhosted.org/packages/a1/cf/fa33eaf29a606102c8d9ffa45a386a04c2203d9ad18bf4eef3e20c43ebc8/mypy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56f180ff6430e6373db7a1d569317675b0a451caf5fef6ce4ab365f5f2f6c3", size = 12759960, upload-time = "2025-07-14T20:33:42.882Z" }, - { url = "https://files.pythonhosted.org/packages/94/75/3f5a29209f27e739ca57e6350bc6b783a38c7621bdf9cac3ab8a08665801/mypy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:eafaf8b9252734400f9b77df98b4eee3d2eecab16104680d51341c75702cad70", size = 9503888, upload-time = "2025-07-14T20:32:34.392Z" }, - { url = "https://files.pythonhosted.org/packages/12/e9/e6824ed620bbf51d3bf4d6cbbe4953e83eaf31a448d1b3cfb3620ccb641c/mypy-1.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f986f1cab8dbec39ba6e0eaa42d4d3ac6686516a5d3dccd64be095db05ebc6bb", size = 11086395, upload-time = "2025-07-14T20:34:11.452Z" }, - { url = "https://files.pythonhosted.org/packages/ba/51/a4afd1ae279707953be175d303f04a5a7bd7e28dc62463ad29c1c857927e/mypy-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:51e455a54d199dd6e931cd7ea987d061c2afbaf0960f7f66deef47c90d1b304d", size = 10120052, upload-time = "2025-07-14T20:33:09.897Z" }, - { url = "https://files.pythonhosted.org/packages/8a/71/19adfeac926ba8205f1d1466d0d360d07b46486bf64360c54cb5a2bd86a8/mypy-1.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3204d773bab5ff4ebbd1f8efa11b498027cd57017c003ae970f310e5b96be8d8", size = 11861806, upload-time = "2025-07-14T20:32:16.028Z" }, - { url = "https://files.pythonhosted.org/packages/0b/64/d6120eca3835baf7179e6797a0b61d6c47e0bc2324b1f6819d8428d5b9ba/mypy-1.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1051df7ec0886fa246a530ae917c473491e9a0ba6938cfd0ec2abc1076495c3e", size = 12744371, upload-time = "2025-07-14T20:33:33.503Z" }, - { url = "https://files.pythonhosted.org/packages/1f/dc/56f53b5255a166f5bd0f137eed960e5065f2744509dfe69474ff0ba772a5/mypy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f773c6d14dcc108a5b141b4456b0871df638eb411a89cd1c0c001fc4a9d08fc8", size = 12914558, upload-time = "2025-07-14T20:33:56.961Z" }, - { url = "https://files.pythonhosted.org/packages/69/ac/070bad311171badc9add2910e7f89271695a25c136de24bbafc7eded56d5/mypy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1619a485fd0e9c959b943c7b519ed26b712de3002d7de43154a489a2d0fd817d", size = 9585447, upload-time = "2025-07-14T20:32:20.594Z" }, - { url = "https://files.pythonhosted.org/packages/be/7b/5f8ab461369b9e62157072156935cec9d272196556bdc7c2ff5f4c7c0f9b/mypy-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c41aa59211e49d717d92b3bb1238c06d387c9325d3122085113c79118bebb06", size = 11070019, upload-time = "2025-07-14T20:32:07.99Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f8/c49c9e5a2ac0badcc54beb24e774d2499748302c9568f7f09e8730e953fa/mypy-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e69db1fb65b3114f98c753e3930a00514f5b68794ba80590eb02090d54a5d4a", size = 10114457, upload-time = "2025-07-14T20:33:47.285Z" }, - { url = "https://files.pythonhosted.org/packages/89/0c/fb3f9c939ad9beed3e328008b3fb90b20fda2cddc0f7e4c20dbefefc3b33/mypy-1.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03ba330b76710f83d6ac500053f7727270b6b8553b0423348ffb3af6f2f7b889", size = 11857838, upload-time = "2025-07-14T20:33:14.462Z" }, - { url = "https://files.pythonhosted.org/packages/4c/66/85607ab5137d65e4f54d9797b77d5a038ef34f714929cf8ad30b03f628df/mypy-1.17.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:037bc0f0b124ce46bfde955c647f3e395c6174476a968c0f22c95a8d2f589bba", size = 12731358, upload-time = "2025-07-14T20:32:25.579Z" }, - { url = "https://files.pythonhosted.org/packages/73/d0/341dbbfb35ce53d01f8f2969facbb66486cee9804048bf6c01b048127501/mypy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38876106cb6132259683632b287238858bd58de267d80defb6f418e9ee50658", size = 12917480, upload-time = "2025-07-14T20:34:21.868Z" }, - { url = "https://files.pythonhosted.org/packages/64/63/70c8b7dbfc520089ac48d01367a97e8acd734f65bd07813081f508a8c94c/mypy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d30ba01c0f151998f367506fab31c2ac4527e6a7b2690107c7a7f9e3cb419a9c", size = 9589666, upload-time = "2025-07-14T20:34:16.841Z" }, - { url = "https://files.pythonhosted.org/packages/e3/fc/ee058cc4316f219078464555873e99d170bde1d9569abd833300dbeb484a/mypy-1.17.0-py3-none-any.whl", hash = "sha256:15d9d0018237ab058e5de3d8fce61b6fa72cc59cc78fd91f1b474bce12abf496", size = 2283195, upload-time = "2025-07-14T20:31:54.753Z" }, -] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, -] - -[[package]] -name = "nbclient" -version = "0.10.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "nbformat" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/87/66/7ffd18d58eae90d5721f9f39212327695b749e23ad44b3881744eaf4d9e8/nbclient-0.10.2.tar.gz", hash = "sha256:90b7fc6b810630db87a6d0c2250b1f0ab4cf4d3c27a299b0cde78a4ed3fd9193", size = 62424, upload-time = "2024-12-19T10:32:27.164Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/6d/e7fa07f03a4a7b221d94b4d586edb754a9b0dc3c9e2c93353e9fa4e0d117/nbclient-0.10.2-py3-none-any.whl", hash = "sha256:4ffee11e788b4a27fabeb7955547e4318a5298f34342a4bfd01f2e1faaeadc3d", size = 25434, upload-time = "2024-12-19T10:32:24.139Z" }, -] - -[[package]] -name = "nbconvert" -version = "7.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beautifulsoup4" }, - { name = "bleach", extra = ["css"] }, - { name = "defusedxml" }, - { name = "jinja2" }, - { name = "jupyter-core" }, - { name = "jupyterlab-pygments" }, - { name = "markupsafe" }, - { name = "mistune" }, - { name = "nbclient" }, - { name = "nbformat" }, - { name = "packaging" }, - { name = "pandocfilters" }, - { name = "pygments" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/47/81f886b699450d0569f7bc551df2b1673d18df7ff25cc0c21ca36ed8a5ff/nbconvert-7.17.0.tar.gz", hash = "sha256:1b2696f1b5be12309f6c7d707c24af604b87dfaf6d950794c7b07acab96dda78", size = 862855, upload-time = "2026-01-29T16:37:48.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl", hash = "sha256:4f99a63b337b9a23504347afdab24a11faa7d86b405e5c8f9881cd313336d518", size = 261510, upload-time = "2026-01-29T16:37:46.322Z" }, -] - -[[package]] -name = "nbformat" -version = "5.10.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastjsonschema" }, - { name = "jsonschema" }, - { name = "jupyter-core" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749, upload-time = "2024-04-04T11:20:37.371Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454, upload-time = "2024-04-04T11:20:34.895Z" }, -] - -[[package]] -name = "nbsphinx" -version = "0.9.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "jinja2" }, - { name = "nbconvert" }, - { name = "nbformat" }, - { name = "sphinx" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1e/84/b1856b7651ac34e965aa567a158714c7f3bd42a1b1ce76bf423ffb99872c/nbsphinx-0.9.7.tar.gz", hash = "sha256:abd298a686d55fa894ef697c51d44f24e53aa312dadae38e82920f250a5456fe", size = 180479, upload-time = "2025-03-03T19:46:08.069Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/2d/8c8e635bcc6757573d311bb3c5445426382f280da32b8cd6d82d501ef4a4/nbsphinx-0.9.7-py3-none-any.whl", hash = "sha256:7292c3767fea29e405c60743eee5393682a83982ab202ff98f5eb2db02629da8", size = 31660, upload-time = "2025-03-03T19:46:06.581Z" }, -] - -[[package]] -name = "nest-asyncio" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, -] - -[[package]] -name = "nodeenv" -version = "1.9.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, -] - -[[package]] -name = "notebook" -version = "7.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-server" }, - { name = "jupyterlab" }, - { name = "jupyterlab-server" }, - { name = "notebook-shim" }, - { name = "tornado" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8a/a9/882707b0aa639e6d7d3e7df4bfbe07479d832e9a8f02d8471002a4ea6d65/notebook-7.5.1.tar.gz", hash = "sha256:b2fb4cef4d47d08c33aecce1c6c6e84be05436fbd791f88fce8df9fbca088b75", size = 14058696, upload-time = "2025-12-16T07:38:59.223Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/86/ca516cb58ad2cb2064124d31cf0fd8b012fca64bebeb26da2d2ddf03fc79/notebook-7.5.1-py3-none-any.whl", hash = "sha256:f4e2451c19910c33b88709b84537e11f6368c1cdff1aa0c43db701aea535dd44", size = 14468080, upload-time = "2025-12-16T07:38:55.644Z" }, -] - -[[package]] -name = "notebook-shim" -version = "0.2.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-server" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167, upload-time = "2024-02-14T23:35:18.353Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307, upload-time = "2024-02-14T23:35:16.286Z" }, -] - -[[package]] -name = "numcodecs" -version = "0.13.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11' and platform_python_implementation == 'PyPy'", - "python_full_version < '3.11' and platform_python_implementation != 'PyPy'", -] -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/85/56/8895a76abe4ec94ebd01eeb6d74f587bc4cddd46569670e1402852a5da13/numcodecs-0.13.1.tar.gz", hash = "sha256:a3cf37881df0898f3a9c0d4477df88133fe85185bffe57ba31bcc2fa207709bc", size = 5955215, upload-time = "2024-10-09T16:28:00.188Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/c0/6d72cde772bcec196b7188731d41282993b2958440f77fdf0db216f722da/numcodecs-0.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:96add4f783c5ce57cc7e650b6cac79dd101daf887c479a00a29bc1487ced180b", size = 1580012, upload-time = "2024-10-09T16:27:19.069Z" }, - { url = "https://files.pythonhosted.org/packages/94/1d/f81fc1fa9210bbea97258242393a1f9feab4f6d8fb201f81f76003005e4b/numcodecs-0.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:237b7171609e868a20fd313748494444458ccd696062f67e198f7f8f52000c15", size = 1176919, upload-time = "2024-10-09T16:27:21.634Z" }, - { url = "https://files.pythonhosted.org/packages/16/e4/b9ec2f4dfc34ecf724bc1beb96a9f6fa9b91801645688ffadacd485089da/numcodecs-0.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96e42f73c31b8c24259c5fac6adba0c3ebf95536e37749dc6c62ade2989dca28", size = 8625842, upload-time = "2024-10-09T16:27:24.168Z" }, - { url = "https://files.pythonhosted.org/packages/fe/90/299952e1477954ec4f92813fa03e743945e3ff711bb4f6c9aace431cb3da/numcodecs-0.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:eda7d7823c9282e65234731fd6bd3986b1f9e035755f7fed248d7d366bb291ab", size = 828638, upload-time = "2024-10-09T16:27:27.063Z" }, - { url = "https://files.pythonhosted.org/packages/f0/78/34b8e869ef143e88d62e8231f4dbfcad85e5c41302a11fc5bd2228a13df5/numcodecs-0.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2eda97dd2f90add98df6d295f2c6ae846043396e3d51a739ca5db6c03b5eb666", size = 1580199, upload-time = "2024-10-09T16:27:29.336Z" }, - { url = "https://files.pythonhosted.org/packages/3b/cf/f70797d86bb585d258d1e6993dced30396f2044725b96ce8bcf87a02be9c/numcodecs-0.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2a86f5367af9168e30f99727ff03b27d849c31ad4522060dde0bce2923b3a8bc", size = 1177203, upload-time = "2024-10-09T16:27:31.011Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b5/d14ad69b63fde041153dfd05d7181a49c0d4864de31a7a1093c8370da957/numcodecs-0.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233bc7f26abce24d57e44ea8ebeb5cd17084690b4e7409dd470fdb75528d615f", size = 8868743, upload-time = "2024-10-09T16:27:32.833Z" }, - { url = "https://files.pythonhosted.org/packages/13/d4/27a7b5af0b33f6d61e198faf177fbbf3cb83ff10d9d1a6857b7efc525ad5/numcodecs-0.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:796b3e6740107e4fa624cc636248a1580138b3f1c579160f260f76ff13a4261b", size = 829603, upload-time = "2024-10-09T16:27:35.415Z" }, - { url = "https://files.pythonhosted.org/packages/37/3a/bc09808425e7d3df41e5fc73fc7a802c429ba8c6b05e55f133654ade019d/numcodecs-0.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5195bea384a6428f8afcece793860b1ab0ae28143c853f0b2b20d55a8947c917", size = 1575806, upload-time = "2024-10-09T16:27:37.804Z" }, - { url = "https://files.pythonhosted.org/packages/3a/cc/dc74d0bfdf9ec192332a089d199f1e543e747c556b5659118db7a437dcca/numcodecs-0.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3501a848adaddce98a71a262fee15cd3618312692aa419da77acd18af4a6a3f6", size = 1178233, upload-time = "2024-10-09T16:27:40.169Z" }, - { url = "https://files.pythonhosted.org/packages/d4/ce/434e8e3970b8e92ae9ab6d9db16cb9bc7aa1cd02e17c11de6848224100a1/numcodecs-0.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2230484e6102e5fa3cc1a5dd37ca1f92dfbd183d91662074d6f7574e3e8f53", size = 8857827, upload-time = "2024-10-09T16:27:42.743Z" }, - { url = "https://files.pythonhosted.org/packages/83/e7/1d8b1b266a92f9013c755b1c146c5ad71a2bff147ecbc67f86546a2e4d6a/numcodecs-0.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:e5db4824ebd5389ea30e54bc8aeccb82d514d28b6b68da6c536b8fa4596f4bca", size = 826539, upload-time = "2024-10-09T16:27:44.808Z" }, - { url = "https://files.pythonhosted.org/packages/83/8b/06771dead2cc4a8ae1ea9907737cf1c8d37a323392fa28f938a586373468/numcodecs-0.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a60d75179fd6692e301ddfb3b266d51eb598606dcae7b9fc57f986e8d65cb43", size = 1571660, upload-time = "2024-10-09T16:27:47.125Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ea/d925bf85f92dfe4635356018da9fe4bfecb07b1c72f62b01c1bc47f936b1/numcodecs-0.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f593c7506b0ab248961a3b13cb148cc6e8355662ff124ac591822310bc55ecf", size = 1169925, upload-time = "2024-10-09T16:27:49.512Z" }, - { url = "https://files.pythonhosted.org/packages/0f/d6/643a3839d571d8e439a2c77dc4b0b8cab18d96ac808e4a81dbe88e959ab6/numcodecs-0.13.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80d3071465f03522e776a31045ddf2cfee7f52df468b977ed3afdd7fe5869701", size = 8814257, upload-time = "2024-10-09T16:27:52.059Z" }, - { url = "https://files.pythonhosted.org/packages/a6/c5/f3e56bc9b4e438a287fff738993d6d11abef368c0328a612ac2842ba9fca/numcodecs-0.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:90d3065ae74c9342048ae0046006f99dcb1388b7288da5a19b3bddf9c30c3176", size = 821887, upload-time = "2024-10-09T16:27:55.039Z" }, -] - -[[package]] -name = "numcodecs" -version = "0.16.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12' and platform_python_implementation == 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", - "python_full_version >= '3.12' and platform_python_implementation != 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", -] -dependencies = [ - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/1d/837d946aab385abe1e472ec08a6816d84b00f4ceeae5445eb8f25c5c6ca9/numcodecs-0.16.2.tar.gz", hash = "sha256:9922dae0c3b01b5bed3b4bae239f4787e891daa3262c27971298669d029d10e9", size = 6271668, upload-time = "2025-08-13T16:09:26.125Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/19/38b3fbb2335e31fdd253e1de0531b32e8245b86a7b9d46c474fa6d2cb5c5/numcodecs-0.16.2-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:4c1f1357896cfc5451157fd90a2858aa0f6a66409f91f3016d257b76c4c244c1", size = 1629357, upload-time = "2025-08-13T16:09:00.425Z" }, - { url = "https://files.pythonhosted.org/packages/3d/f8/ba257b7627477f5650aa8274e7a6f9a72c4d51c1e7ac968b14b65d872c76/numcodecs-0.16.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d88aa4f82bb8fe3fa2e9c19fd44b6c268acec6fc7bcff59c92e169830d09531a", size = 1158894, upload-time = "2025-08-13T16:09:02.285Z" }, - { url = "https://files.pythonhosted.org/packages/e3/31/0c90266a9ba048223656d552c5b40ea22d788be77797e8a0db9d698a3847/numcodecs-0.16.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:276f657ec4ffef9e2cc344b732b8906fe8612c3b9484504830ccdc7a2dd0715a", size = 8285212, upload-time = "2025-08-13T16:09:03.716Z" }, - { url = "https://files.pythonhosted.org/packages/01/24/7889b678c0b7e3f605e1ec2fe1ddee0f48920098fecb0cadd5bd66483044/numcodecs-0.16.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1b7d8174e14c66191e0e1a4a44dd264bd776fb96b1ce3adc0216154f34b755c", size = 8811508, upload-time = "2025-08-13T16:09:05.68Z" }, - { url = "https://files.pythonhosted.org/packages/87/26/23b474b188a8e005bbd5e516e3415ec9f2b59fbf1f36636e5cfc14f59c54/numcodecs-0.16.2-cp311-cp311-win_amd64.whl", hash = "sha256:559163a7d97ec8aef1654e47c6e97f88dcc4c38a975338bf1bfb214d3c8d70f9", size = 804099, upload-time = "2025-08-13T16:09:07.589Z" }, - { url = "https://files.pythonhosted.org/packages/03/c3/5470273d6d5c986521140ccec6476664ea4e03c0cfc51b370fb03368bb41/numcodecs-0.16.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30f04c2b7bb802133866e7fb554d47943864f977dfe8a95c814eb801c797df3c", size = 1668488, upload-time = "2025-08-13T16:09:08.942Z" }, - { url = "https://files.pythonhosted.org/packages/db/bf/cc1aaea87371097d6b5236ec44f8eb96387b52204b4e671fac716e5de325/numcodecs-0.16.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ee0b2776cf47b7702ba0ccc0b6afaad28afbf8d5bb7b9a5274b5f08ecc651b2", size = 1155083, upload-time = "2025-08-13T16:09:10.641Z" }, - { url = "https://files.pythonhosted.org/packages/6b/c1/ba5ab0cf4c4d737635d20d8b72a61c26f8f99c0529606dfbfa3e5d3a4221/numcodecs-0.16.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8e94f0e90aaf4d01b2e26476d3b081c2cf8c17036af45e04e735de9c0cf64f", size = 8260568, upload-time = "2025-08-13T16:09:12.063Z" }, - { url = "https://files.pythonhosted.org/packages/3b/64/7177bf632520705893683fa4ca202ed540450bf971c0453ad1351baa2007/numcodecs-0.16.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b565b16366749011e290343617571db861b2b2e58b038697afde6d02f537c91", size = 8792262, upload-time = "2025-08-13T16:09:14.058Z" }, - { url = "https://files.pythonhosted.org/packages/10/90/df01799f4c1bb8618b842582d10d362829e980c4d5eb9701c1aeadf5c4e3/numcodecs-0.16.2-cp312-cp312-win_amd64.whl", hash = "sha256:e4cfdde4e99bf47580f4eb3a876630c73ba14e4a1380fec5959ac727e22ce0d2", size = 803444, upload-time = "2025-08-13T16:09:16.09Z" }, - { url = "https://files.pythonhosted.org/packages/b6/e3/f61c422259a4b6c8c2496d284f85ed17f8686b3a53feb797d3bd66ef499c/numcodecs-0.16.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f965ccb2f6d215ffd2e3239ec02e33139d7ce311ff49704d340704b81dda653", size = 1664476, upload-time = "2025-08-13T16:09:17.327Z" }, - { url = "https://files.pythonhosted.org/packages/b9/a7/fa4d66b86e277643d135af263efc0dd1f98cf1228d3b4554b843c0c1a09b/numcodecs-0.16.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b4bc110b73d59de7f46310c680f075f9007ead915174c895368274c646c9ea74", size = 1148718, upload-time = "2025-08-13T16:09:19.075Z" }, - { url = "https://files.pythonhosted.org/packages/17/43/9656a6b0ed7250ca3a5c126a6077a29398c3dca9176224dba4634847a4a4/numcodecs-0.16.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51615cf2811343a8a3fa42953cb4120ac7187875a161260444f53ada5710063e", size = 8205330, upload-time = "2025-08-13T16:09:20.904Z" }, - { url = "https://files.pythonhosted.org/packages/55/7f/0ab8db32ef9b51c60f7b759c2b155e1edcb08febb508c22a9d04b19ec735/numcodecs-0.16.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a18ced1ecbdd0e9ee716820dbb3a094c896eed8005273bbcab9980bdac270ae", size = 8750769, upload-time = "2025-08-13T16:09:22.516Z" }, - { url = "https://files.pythonhosted.org/packages/f0/0c/25f96c7969bdbfcc1427dc82eba92f2ef4df84c63369c95ab99af6404c23/numcodecs-0.16.2-cp313-cp313-win_amd64.whl", hash = "sha256:f640ed8406e1eb5806787a3e5be223d455b75c99eb2088a290947ed6dbd77e8e", size = 800281, upload-time = "2025-08-13T16:09:24.691Z" }, -] - -[package.optional-dependencies] -crc32c = [ - { name = "crc32c", marker = "python_full_version >= '3.11'" }, -] - -[[package]] -name = "numpy" -version = "2.2.6" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11' and platform_python_implementation == 'PyPy'", - "python_full_version < '3.11' and platform_python_implementation != 'PyPy'", -] -sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, - { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, - { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, - { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, - { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, - { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, - { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, - { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, - { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, - { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, - { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, - { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, - { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, - { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, - { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, - { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, - { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, - { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, - { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, - { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, - { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, - { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, - { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, - { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, - { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, - { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, - { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, - { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, - { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, - { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, - { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, - { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, - { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, - { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, - { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, - { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, - { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, - { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, - { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, - { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, - { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, - { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, - { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, - { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, - { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, - { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, - { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, - { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, - { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, -] - -[[package]] -name = "numpy" -version = "2.3.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12' and platform_python_implementation == 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", - "python_full_version >= '3.12' and platform_python_implementation != 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", -] -sdist = { url = "https://files.pythonhosted.org/packages/37/7d/3fec4199c5ffb892bed55cff901e4f39a58c81df9c44c280499e92cad264/numpy-2.3.2.tar.gz", hash = "sha256:e0486a11ec30cdecb53f184d496d1c6a20786c81e55e41640270130056f8ee48", size = 20489306, upload-time = "2025-07-24T21:32:07.553Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/26/1320083986108998bd487e2931eed2aeedf914b6e8905431487543ec911d/numpy-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:852ae5bed3478b92f093e30f785c98e0cb62fa0a939ed057c31716e18a7a22b9", size = 21259016, upload-time = "2025-07-24T20:24:35.214Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2b/792b341463fa93fc7e55abbdbe87dac316c5b8cb5e94fb7a59fb6fa0cda5/numpy-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a0e27186e781a69959d0230dd9909b5e26024f8da10683bd6344baea1885168", size = 14451158, upload-time = "2025-07-24T20:24:58.397Z" }, - { url = "https://files.pythonhosted.org/packages/b7/13/e792d7209261afb0c9f4759ffef6135b35c77c6349a151f488f531d13595/numpy-2.3.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f0a1a8476ad77a228e41619af2fa9505cf69df928e9aaa165746584ea17fed2b", size = 5379817, upload-time = "2025-07-24T20:25:07.746Z" }, - { url = "https://files.pythonhosted.org/packages/49/ce/055274fcba4107c022b2113a213c7287346563f48d62e8d2a5176ad93217/numpy-2.3.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cbc95b3813920145032412f7e33d12080f11dc776262df1712e1638207dde9e8", size = 6913606, upload-time = "2025-07-24T20:25:18.84Z" }, - { url = "https://files.pythonhosted.org/packages/17/f2/e4d72e6bc5ff01e2ab613dc198d560714971900c03674b41947e38606502/numpy-2.3.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75018be4980a7324edc5930fe39aa391d5734531b1926968605416ff58c332d", size = 14589652, upload-time = "2025-07-24T20:25:40.356Z" }, - { url = "https://files.pythonhosted.org/packages/c8/b0/fbeee3000a51ebf7222016e2939b5c5ecf8000a19555d04a18f1e02521b8/numpy-2.3.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20b8200721840f5621b7bd03f8dcd78de33ec522fc40dc2641aa09537df010c3", size = 16938816, upload-time = "2025-07-24T20:26:05.721Z" }, - { url = "https://files.pythonhosted.org/packages/a9/ec/2f6c45c3484cc159621ea8fc000ac5a86f1575f090cac78ac27193ce82cd/numpy-2.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f91e5c028504660d606340a084db4b216567ded1056ea2b4be4f9d10b67197f", size = 16370512, upload-time = "2025-07-24T20:26:30.545Z" }, - { url = "https://files.pythonhosted.org/packages/b5/01/dd67cf511850bd7aefd6347aaae0956ed415abea741ae107834aae7d6d4e/numpy-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fb1752a3bb9a3ad2d6b090b88a9a0ae1cd6f004ef95f75825e2f382c183b2097", size = 18884947, upload-time = "2025-07-24T20:26:58.24Z" }, - { url = "https://files.pythonhosted.org/packages/a7/17/2cf60fd3e6a61d006778735edf67a222787a8c1a7842aed43ef96d777446/numpy-2.3.2-cp311-cp311-win32.whl", hash = "sha256:4ae6863868aaee2f57503c7a5052b3a2807cf7a3914475e637a0ecd366ced220", size = 6599494, upload-time = "2025-07-24T20:27:09.786Z" }, - { url = "https://files.pythonhosted.org/packages/d5/03/0eade211c504bda872a594f045f98ddcc6caef2b7c63610946845e304d3f/numpy-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:240259d6564f1c65424bcd10f435145a7644a65a6811cfc3201c4a429ba79170", size = 13087889, upload-time = "2025-07-24T20:27:29.558Z" }, - { url = "https://files.pythonhosted.org/packages/13/32/2c7979d39dafb2a25087e12310fc7f3b9d3c7d960df4f4bc97955ae0ce1d/numpy-2.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:4209f874d45f921bde2cff1ffcd8a3695f545ad2ffbef6d3d3c6768162efab89", size = 10459560, upload-time = "2025-07-24T20:27:46.803Z" }, - { url = "https://files.pythonhosted.org/packages/00/6d/745dd1c1c5c284d17725e5c802ca4d45cfc6803519d777f087b71c9f4069/numpy-2.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bc3186bea41fae9d8e90c2b4fb5f0a1f5a690682da79b92574d63f56b529080b", size = 20956420, upload-time = "2025-07-24T20:28:18.002Z" }, - { url = "https://files.pythonhosted.org/packages/bc/96/e7b533ea5740641dd62b07a790af5d9d8fec36000b8e2d0472bd7574105f/numpy-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f4f0215edb189048a3c03bd5b19345bdfa7b45a7a6f72ae5945d2a28272727f", size = 14184660, upload-time = "2025-07-24T20:28:39.522Z" }, - { url = "https://files.pythonhosted.org/packages/2b/53/102c6122db45a62aa20d1b18c9986f67e6b97e0d6fbc1ae13e3e4c84430c/numpy-2.3.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b1224a734cd509f70816455c3cffe13a4f599b1bf7130f913ba0e2c0b2006c0", size = 5113382, upload-time = "2025-07-24T20:28:48.544Z" }, - { url = "https://files.pythonhosted.org/packages/2b/21/376257efcbf63e624250717e82b4fae93d60178f09eb03ed766dbb48ec9c/numpy-2.3.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3dcf02866b977a38ba3ec10215220609ab9667378a9e2150615673f3ffd6c73b", size = 6647258, upload-time = "2025-07-24T20:28:59.104Z" }, - { url = "https://files.pythonhosted.org/packages/91/ba/f4ebf257f08affa464fe6036e13f2bf9d4642a40228781dc1235da81be9f/numpy-2.3.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:572d5512df5470f50ada8d1972c5f1082d9a0b7aa5944db8084077570cf98370", size = 14281409, upload-time = "2025-07-24T20:40:30.298Z" }, - { url = "https://files.pythonhosted.org/packages/59/ef/f96536f1df42c668cbacb727a8c6da7afc9c05ece6d558927fb1722693e1/numpy-2.3.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8145dd6d10df13c559d1e4314df29695613575183fa2e2d11fac4c208c8a1f73", size = 16641317, upload-time = "2025-07-24T20:40:56.625Z" }, - { url = "https://files.pythonhosted.org/packages/f6/a7/af813a7b4f9a42f498dde8a4c6fcbff8100eed00182cc91dbaf095645f38/numpy-2.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:103ea7063fa624af04a791c39f97070bf93b96d7af7eb23530cd087dc8dbe9dc", size = 16056262, upload-time = "2025-07-24T20:41:20.797Z" }, - { url = "https://files.pythonhosted.org/packages/8b/5d/41c4ef8404caaa7f05ed1cfb06afe16a25895260eacbd29b4d84dff2920b/numpy-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc927d7f289d14f5e037be917539620603294454130b6de200091e23d27dc9be", size = 18579342, upload-time = "2025-07-24T20:41:50.753Z" }, - { url = "https://files.pythonhosted.org/packages/a1/4f/9950e44c5a11636f4a3af6e825ec23003475cc9a466edb7a759ed3ea63bd/numpy-2.3.2-cp312-cp312-win32.whl", hash = "sha256:d95f59afe7f808c103be692175008bab926b59309ade3e6d25009e9a171f7036", size = 6320610, upload-time = "2025-07-24T20:42:01.551Z" }, - { url = "https://files.pythonhosted.org/packages/7c/2f/244643a5ce54a94f0a9a2ab578189c061e4a87c002e037b0829dd77293b6/numpy-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:9e196ade2400c0c737d93465327d1ae7c06c7cb8a1756121ebf54b06ca183c7f", size = 12786292, upload-time = "2025-07-24T20:42:20.738Z" }, - { url = "https://files.pythonhosted.org/packages/54/cd/7b5f49d5d78db7badab22d8323c1b6ae458fbf86c4fdfa194ab3cd4eb39b/numpy-2.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:ee807923782faaf60d0d7331f5e86da7d5e3079e28b291973c545476c2b00d07", size = 10194071, upload-time = "2025-07-24T20:42:36.657Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c0/c6bb172c916b00700ed3bf71cb56175fd1f7dbecebf8353545d0b5519f6c/numpy-2.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c8d9727f5316a256425892b043736d63e89ed15bbfe6556c5ff4d9d4448ff3b3", size = 20949074, upload-time = "2025-07-24T20:43:07.813Z" }, - { url = "https://files.pythonhosted.org/packages/20/4e/c116466d22acaf4573e58421c956c6076dc526e24a6be0903219775d862e/numpy-2.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:efc81393f25f14d11c9d161e46e6ee348637c0a1e8a54bf9dedc472a3fae993b", size = 14177311, upload-time = "2025-07-24T20:43:29.335Z" }, - { url = "https://files.pythonhosted.org/packages/78/45/d4698c182895af189c463fc91d70805d455a227261d950e4e0f1310c2550/numpy-2.3.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dd937f088a2df683cbb79dda9a772b62a3e5a8a7e76690612c2737f38c6ef1b6", size = 5106022, upload-time = "2025-07-24T20:43:37.999Z" }, - { url = "https://files.pythonhosted.org/packages/9f/76/3e6880fef4420179309dba72a8c11f6166c431cf6dee54c577af8906f914/numpy-2.3.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:11e58218c0c46c80509186e460d79fbdc9ca1eb8d8aee39d8f2dc768eb781089", size = 6640135, upload-time = "2025-07-24T20:43:49.28Z" }, - { url = "https://files.pythonhosted.org/packages/34/fa/87ff7f25b3c4ce9085a62554460b7db686fef1e0207e8977795c7b7d7ba1/numpy-2.3.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5ad4ebcb683a1f99f4f392cc522ee20a18b2bb12a2c1c42c3d48d5a1adc9d3d2", size = 14278147, upload-time = "2025-07-24T20:44:10.328Z" }, - { url = "https://files.pythonhosted.org/packages/1d/0f/571b2c7a3833ae419fe69ff7b479a78d313581785203cc70a8db90121b9a/numpy-2.3.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:938065908d1d869c7d75d8ec45f735a034771c6ea07088867f713d1cd3bbbe4f", size = 16635989, upload-time = "2025-07-24T20:44:34.88Z" }, - { url = "https://files.pythonhosted.org/packages/24/5a/84ae8dca9c9a4c592fe11340b36a86ffa9fd3e40513198daf8a97839345c/numpy-2.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:66459dccc65d8ec98cc7df61307b64bf9e08101f9598755d42d8ae65d9a7a6ee", size = 16053052, upload-time = "2025-07-24T20:44:58.872Z" }, - { url = "https://files.pythonhosted.org/packages/57/7c/e5725d99a9133b9813fcf148d3f858df98511686e853169dbaf63aec6097/numpy-2.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a7af9ed2aa9ec5950daf05bb11abc4076a108bd3c7db9aa7251d5f107079b6a6", size = 18577955, upload-time = "2025-07-24T20:45:26.714Z" }, - { url = "https://files.pythonhosted.org/packages/ae/11/7c546fcf42145f29b71e4d6f429e96d8d68e5a7ba1830b2e68d7418f0bbd/numpy-2.3.2-cp313-cp313-win32.whl", hash = "sha256:906a30249315f9c8e17b085cc5f87d3f369b35fedd0051d4a84686967bdbbd0b", size = 6311843, upload-time = "2025-07-24T20:49:24.444Z" }, - { url = "https://files.pythonhosted.org/packages/aa/6f/a428fd1cb7ed39b4280d057720fed5121b0d7754fd2a9768640160f5517b/numpy-2.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:c63d95dc9d67b676e9108fe0d2182987ccb0f11933c1e8959f42fa0da8d4fa56", size = 12782876, upload-time = "2025-07-24T20:49:43.227Z" }, - { url = "https://files.pythonhosted.org/packages/65/85/4ea455c9040a12595fb6c43f2c217257c7b52dd0ba332c6a6c1d28b289fe/numpy-2.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:b05a89f2fb84d21235f93de47129dd4f11c16f64c87c33f5e284e6a3a54e43f2", size = 10192786, upload-time = "2025-07-24T20:49:59.443Z" }, - { url = "https://files.pythonhosted.org/packages/80/23/8278f40282d10c3f258ec3ff1b103d4994bcad78b0cba9208317f6bb73da/numpy-2.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e6ecfeddfa83b02318f4d84acf15fbdbf9ded18e46989a15a8b6995dfbf85ab", size = 21047395, upload-time = "2025-07-24T20:45:58.821Z" }, - { url = "https://files.pythonhosted.org/packages/1f/2d/624f2ce4a5df52628b4ccd16a4f9437b37c35f4f8a50d00e962aae6efd7a/numpy-2.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:508b0eada3eded10a3b55725b40806a4b855961040180028f52580c4729916a2", size = 14300374, upload-time = "2025-07-24T20:46:20.207Z" }, - { url = "https://files.pythonhosted.org/packages/f6/62/ff1e512cdbb829b80a6bd08318a58698867bca0ca2499d101b4af063ee97/numpy-2.3.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:754d6755d9a7588bdc6ac47dc4ee97867271b17cee39cb87aef079574366db0a", size = 5228864, upload-time = "2025-07-24T20:46:30.58Z" }, - { url = "https://files.pythonhosted.org/packages/7d/8e/74bc18078fff03192d4032cfa99d5a5ca937807136d6f5790ce07ca53515/numpy-2.3.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f66e7d2b2d7712410d3bc5684149040ef5f19856f20277cd17ea83e5006286", size = 6737533, upload-time = "2025-07-24T20:46:46.111Z" }, - { url = "https://files.pythonhosted.org/packages/19/ea/0731efe2c9073ccca5698ef6a8c3667c4cf4eea53fcdcd0b50140aba03bc/numpy-2.3.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de6ea4e5a65d5a90c7d286ddff2b87f3f4ad61faa3db8dabe936b34c2275b6f8", size = 14352007, upload-time = "2025-07-24T20:47:07.1Z" }, - { url = "https://files.pythonhosted.org/packages/cf/90/36be0865f16dfed20f4bc7f75235b963d5939707d4b591f086777412ff7b/numpy-2.3.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3ef07ec8cbc8fc9e369c8dcd52019510c12da4de81367d8b20bc692aa07573a", size = 16701914, upload-time = "2025-07-24T20:47:32.459Z" }, - { url = "https://files.pythonhosted.org/packages/94/30/06cd055e24cb6c38e5989a9e747042b4e723535758e6153f11afea88c01b/numpy-2.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:27c9f90e7481275c7800dc9c24b7cc40ace3fdb970ae4d21eaff983a32f70c91", size = 16132708, upload-time = "2025-07-24T20:47:58.129Z" }, - { url = "https://files.pythonhosted.org/packages/9a/14/ecede608ea73e58267fd7cb78f42341b3b37ba576e778a1a06baffbe585c/numpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:07b62978075b67eee4065b166d000d457c82a1efe726cce608b9db9dd66a73a5", size = 18651678, upload-time = "2025-07-24T20:48:25.402Z" }, - { url = "https://files.pythonhosted.org/packages/40/f3/2fe6066b8d07c3685509bc24d56386534c008b462a488b7f503ba82b8923/numpy-2.3.2-cp313-cp313t-win32.whl", hash = "sha256:c771cfac34a4f2c0de8e8c97312d07d64fd8f8ed45bc9f5726a7e947270152b5", size = 6441832, upload-time = "2025-07-24T20:48:37.181Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ba/0937d66d05204d8f28630c9c60bc3eda68824abde4cf756c4d6aad03b0c6/numpy-2.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:72dbebb2dcc8305c431b2836bcc66af967df91be793d63a24e3d9b741374c450", size = 12927049, upload-time = "2025-07-24T20:48:56.24Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ed/13542dd59c104d5e654dfa2ac282c199ba64846a74c2c4bcdbc3a0f75df1/numpy-2.3.2-cp313-cp313t-win_arm64.whl", hash = "sha256:72c6df2267e926a6d5286b0a6d556ebe49eae261062059317837fda12ddf0c1a", size = 10262935, upload-time = "2025-07-24T20:49:13.136Z" }, - { url = "https://files.pythonhosted.org/packages/c9/7c/7659048aaf498f7611b783e000c7268fcc4dcf0ce21cd10aad7b2e8f9591/numpy-2.3.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:448a66d052d0cf14ce9865d159bfc403282c9bc7bb2a31b03cc18b651eca8b1a", size = 20950906, upload-time = "2025-07-24T20:50:30.346Z" }, - { url = "https://files.pythonhosted.org/packages/80/db/984bea9d4ddf7112a04cfdfb22b1050af5757864cfffe8e09e44b7f11a10/numpy-2.3.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:546aaf78e81b4081b2eba1d105c3b34064783027a06b3ab20b6eba21fb64132b", size = 14185607, upload-time = "2025-07-24T20:50:51.923Z" }, - { url = "https://files.pythonhosted.org/packages/e4/76/b3d6f414f4eca568f469ac112a3b510938d892bc5a6c190cb883af080b77/numpy-2.3.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:87c930d52f45df092f7578889711a0768094debf73cfcde105e2d66954358125", size = 5114110, upload-time = "2025-07-24T20:51:01.041Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d2/6f5e6826abd6bca52392ed88fe44a4b52aacb60567ac3bc86c67834c3a56/numpy-2.3.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:8dc082ea901a62edb8f59713c6a7e28a85daddcb67454c839de57656478f5b19", size = 6642050, upload-time = "2025-07-24T20:51:11.64Z" }, - { url = "https://files.pythonhosted.org/packages/c4/43/f12b2ade99199e39c73ad182f103f9d9791f48d885c600c8e05927865baf/numpy-2.3.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af58de8745f7fa9ca1c0c7c943616c6fe28e75d0c81f5c295810e3c83b5be92f", size = 14296292, upload-time = "2025-07-24T20:51:33.488Z" }, - { url = "https://files.pythonhosted.org/packages/5d/f9/77c07d94bf110a916b17210fac38680ed8734c236bfed9982fd8524a7b47/numpy-2.3.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed5527c4cf10f16c6d0b6bee1f89958bccb0ad2522c8cadc2efd318bcd545f5", size = 16638913, upload-time = "2025-07-24T20:51:58.517Z" }, - { url = "https://files.pythonhosted.org/packages/9b/d1/9d9f2c8ea399cc05cfff8a7437453bd4e7d894373a93cdc46361bbb49a7d/numpy-2.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:095737ed986e00393ec18ec0b21b47c22889ae4b0cd2d5e88342e08b01141f58", size = 16071180, upload-time = "2025-07-24T20:52:22.827Z" }, - { url = "https://files.pythonhosted.org/packages/4c/41/82e2c68aff2a0c9bf315e47d61951099fed65d8cb2c8d9dc388cb87e947e/numpy-2.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5e40e80299607f597e1a8a247ff8d71d79c5b52baa11cc1cce30aa92d2da6e0", size = 18576809, upload-time = "2025-07-24T20:52:51.015Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/4b4fd3efb0837ed252d0f583c5c35a75121038a8c4e065f2c259be06d2d8/numpy-2.3.2-cp314-cp314-win32.whl", hash = "sha256:7d6e390423cc1f76e1b8108c9b6889d20a7a1f59d9a60cac4a050fa734d6c1e2", size = 6366410, upload-time = "2025-07-24T20:56:44.949Z" }, - { url = "https://files.pythonhosted.org/packages/11/9e/b4c24a6b8467b61aced5c8dc7dcfce23621baa2e17f661edb2444a418040/numpy-2.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:b9d0878b21e3918d76d2209c924ebb272340da1fb51abc00f986c258cd5e957b", size = 12918821, upload-time = "2025-07-24T20:57:06.479Z" }, - { url = "https://files.pythonhosted.org/packages/0e/0f/0dc44007c70b1007c1cef86b06986a3812dd7106d8f946c09cfa75782556/numpy-2.3.2-cp314-cp314-win_arm64.whl", hash = "sha256:2738534837c6a1d0c39340a190177d7d66fdf432894f469728da901f8f6dc910", size = 10477303, upload-time = "2025-07-24T20:57:22.879Z" }, - { url = "https://files.pythonhosted.org/packages/8b/3e/075752b79140b78ddfc9c0a1634d234cfdbc6f9bbbfa6b7504e445ad7d19/numpy-2.3.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4d002ecf7c9b53240be3bb69d80f86ddbd34078bae04d87be81c1f58466f264e", size = 21047524, upload-time = "2025-07-24T20:53:22.086Z" }, - { url = "https://files.pythonhosted.org/packages/fe/6d/60e8247564a72426570d0e0ea1151b95ce5bd2f1597bb878a18d32aec855/numpy-2.3.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:293b2192c6bcce487dbc6326de5853787f870aeb6c43f8f9c6496db5b1781e45", size = 14300519, upload-time = "2025-07-24T20:53:44.053Z" }, - { url = "https://files.pythonhosted.org/packages/4d/73/d8326c442cd428d47a067070c3ac6cc3b651a6e53613a1668342a12d4479/numpy-2.3.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0a4f2021a6da53a0d580d6ef5db29947025ae8b35b3250141805ea9a32bbe86b", size = 5228972, upload-time = "2025-07-24T20:53:53.81Z" }, - { url = "https://files.pythonhosted.org/packages/34/2e/e71b2d6dad075271e7079db776196829019b90ce3ece5c69639e4f6fdc44/numpy-2.3.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9c144440db4bf3bb6372d2c3e49834cc0ff7bb4c24975ab33e01199e645416f2", size = 6737439, upload-time = "2025-07-24T20:54:04.742Z" }, - { url = "https://files.pythonhosted.org/packages/15/b0/d004bcd56c2c5e0500ffc65385eb6d569ffd3363cb5e593ae742749b2daa/numpy-2.3.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f92d6c2a8535dc4fe4419562294ff957f83a16ebdec66df0805e473ffaad8bd0", size = 14352479, upload-time = "2025-07-24T20:54:25.819Z" }, - { url = "https://files.pythonhosted.org/packages/11/e3/285142fcff8721e0c99b51686426165059874c150ea9ab898e12a492e291/numpy-2.3.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cefc2219baa48e468e3db7e706305fcd0c095534a192a08f31e98d83a7d45fb0", size = 16702805, upload-time = "2025-07-24T20:54:50.814Z" }, - { url = "https://files.pythonhosted.org/packages/33/c3/33b56b0e47e604af2c7cd065edca892d180f5899599b76830652875249a3/numpy-2.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:76c3e9501ceb50b2ff3824c3589d5d1ab4ac857b0ee3f8f49629d0de55ecf7c2", size = 16133830, upload-time = "2025-07-24T20:55:17.306Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ae/7b1476a1f4d6a48bc669b8deb09939c56dd2a439db1ab03017844374fb67/numpy-2.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:122bf5ed9a0221b3419672493878ba4967121514b1d7d4656a7580cd11dddcbf", size = 18652665, upload-time = "2025-07-24T20:55:46.665Z" }, - { url = "https://files.pythonhosted.org/packages/14/ba/5b5c9978c4bb161034148ade2de9db44ec316fab89ce8c400db0e0c81f86/numpy-2.3.2-cp314-cp314t-win32.whl", hash = "sha256:6f1ae3dcb840edccc45af496f312528c15b1f79ac318169d094e85e4bb35fdf1", size = 6514777, upload-time = "2025-07-24T20:55:57.66Z" }, - { url = "https://files.pythonhosted.org/packages/eb/46/3dbaf0ae7c17cdc46b9f662c56da2054887b8d9e737c1476f335c83d33db/numpy-2.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:087ffc25890d89a43536f75c5fe8770922008758e8eeeef61733957041ed2f9b", size = 13111856, upload-time = "2025-07-24T20:56:17.318Z" }, - { url = "https://files.pythonhosted.org/packages/c1/9e/1652778bce745a67b5fe05adde60ed362d38eb17d919a540e813d30f6874/numpy-2.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:092aeb3449833ea9c0bf0089d70c29ae480685dd2377ec9cdbbb620257f84631", size = 10544226, upload-time = "2025-07-24T20:56:34.509Z" }, - { url = "https://files.pythonhosted.org/packages/cf/ea/50ebc91d28b275b23b7128ef25c3d08152bc4068f42742867e07a870a42a/numpy-2.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:14a91ebac98813a49bc6aa1a0dfc09513dcec1d97eaf31ca21a87221a1cdcb15", size = 21130338, upload-time = "2025-07-24T20:57:54.37Z" }, - { url = "https://files.pythonhosted.org/packages/9f/57/cdd5eac00dd5f137277355c318a955c0d8fb8aa486020c22afd305f8b88f/numpy-2.3.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:71669b5daae692189540cffc4c439468d35a3f84f0c88b078ecd94337f6cb0ec", size = 14375776, upload-time = "2025-07-24T20:58:16.303Z" }, - { url = "https://files.pythonhosted.org/packages/83/85/27280c7f34fcd305c2209c0cdca4d70775e4859a9eaa92f850087f8dea50/numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:69779198d9caee6e547adb933941ed7520f896fd9656834c300bdf4dd8642712", size = 5304882, upload-time = "2025-07-24T20:58:26.199Z" }, - { url = "https://files.pythonhosted.org/packages/48/b4/6500b24d278e15dd796f43824e69939d00981d37d9779e32499e823aa0aa/numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2c3271cc4097beb5a60f010bcc1cc204b300bb3eafb4399376418a83a1c6373c", size = 6818405, upload-time = "2025-07-24T20:58:37.341Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c9/142c1e03f199d202da8e980c2496213509291b6024fd2735ad28ae7065c7/numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8446acd11fe3dc1830568c941d44449fd5cb83068e5c70bd5a470d323d448296", size = 14419651, upload-time = "2025-07-24T20:58:59.048Z" }, - { url = "https://files.pythonhosted.org/packages/8b/95/8023e87cbea31a750a6c00ff9427d65ebc5fef104a136bfa69f76266d614/numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa098a5ab53fa407fded5870865c6275a5cd4101cfdef8d6fafc48286a96e981", size = 16760166, upload-time = "2025-07-24T21:28:56.38Z" }, - { url = "https://files.pythonhosted.org/packages/78/e3/6690b3f85a05506733c7e90b577e4762517404ea78bab2ca3a5cb1aeb78d/numpy-2.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6936aff90dda378c09bea075af0d9c675fe3a977a9d2402f95a87f440f59f619", size = 12977811, upload-time = "2025-07-24T21:29:18.234Z" }, -] - -[[package]] -name = "odc-geo" -version = "0.4.10" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "affine" }, - { name = "cachetools" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pyproj", version = "3.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "pyproj", version = "3.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "shapely" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/53/f6/f22d9728ed1652f092bdb8e0f04cf68f7212d273ad118225a252fc8cbfb4/odc_geo-0.4.10.tar.gz", hash = "sha256:5670a797c4243e9379b20905ea37ff23ad287e7fa37c672dc53546923daf6d52", size = 192809, upload-time = "2025-03-03T04:12:24.077Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/e2/311fb383d9534eef7bfbe858fad931b6e3dbe85843c50592f50063c3bc83/odc_geo-0.4.10-py3-none-any.whl", hash = "sha256:ab5f26f56883a11286a9583c04d30190e35a2702e8aa72fa3b4b438bea851b19", size = 155067, upload-time = "2025-03-03T04:12:22.114Z" }, -] - -[[package]] -name = "odc-loader" -version = "0.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dask", extra = ["array"] }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "odc-geo" }, - { name = "rasterio" }, - { name = "xarray", version = "2025.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "xarray", version = "2025.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/99/c6/fd9dbf40aaf94bf3c9712a9ffdd0a791599764c3e4b80b5917aaf6280ec8/odc_loader-0.5.1.tar.gz", hash = "sha256:bf3adf53b10248bbab3bb1fcb043995c12e204dbde4f9a751cd3331b1c6a1212", size = 41616, upload-time = "2025-04-03T00:14:03.126Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/48/d45d414b8228325051b0a09f68322ef26717eb9b6517579ae395adf2fbfc/odc_loader-0.5.1-py3-none-any.whl", hash = "sha256:fbe505e2f0e8ac4db2542935bdf5f706e6147817d818073a8a98d3226bd6470f", size = 50717, upload-time = "2025-04-03T00:14:01.687Z" }, -] - -[[package]] -name = "odc-stac" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "affine" }, - { name = "dask", extra = ["array"] }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "odc-geo" }, - { name = "odc-loader" }, - { name = "pandas" }, - { name = "pystac" }, - { name = "rasterio" }, - { name = "toolz" }, - { name = "typing-extensions" }, - { name = "xarray", version = "2025.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "xarray", version = "2025.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7f/2e/b5f29cd12f8a5b23f499d7da716cb6d0c55f9542d805111a1e6d92347a31/odc_stac-0.4.0.tar.gz", hash = "sha256:3535767940840e8c3ec2e3dda15ac36e30de0e51624dcc82e1052e19a7dab091", size = 114343, upload-time = "2025-05-01T05:20:51.33Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/21/05c243c9b563519ae5b7442a73424ad0537c7d312cf8969bdf9d0fa31c00/odc_stac-0.4.0-py3-none-any.whl", hash = "sha256:a76b9d96d7aea53cabcd7ba99b0cf467a6eab6d2ad6a52802af36b5c407c5952", size = 44259, upload-time = "2025-05-01T05:20:49.632Z" }, -] - -[[package]] -name = "orjson" -version = "3.11.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/3b/fd9ff8ff64ae3900f11554d5cfc835fb73e501e043c420ad32ec574fe27f/orjson-3.11.1.tar.gz", hash = "sha256:48d82770a5fd88778063604c566f9c7c71820270c9cc9338d25147cbf34afd96", size = 5393373, upload-time = "2025-07-25T14:33:52.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/8b/7dd88f416e2e5834fd9809d871f471aae7d12dfd83d4786166fa5a926601/orjson-3.11.1-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:92d771c492b64119456afb50f2dff3e03a2db8b5af0eba32c5932d306f970532", size = 241312, upload-time = "2025-07-25T14:31:52.841Z" }, - { url = "https://files.pythonhosted.org/packages/f3/5d/5bfc371bd010ffbec90e64338aa59abcb13ed94191112199048653ee2f34/orjson-3.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0085ef83a4141c2ed23bfec5fecbfdb1e95dd42fc8e8c76057bdeeec1608ea65", size = 132791, upload-time = "2025-07-25T14:31:55.547Z" }, - { url = "https://files.pythonhosted.org/packages/48/e2/c07854a6bad71e4249345efadb686c0aff250073bdab8ba9be7626af6516/orjson-3.11.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5caf7f13f2e1b4e137060aed892d4541d07dabc3f29e6d891e2383c7ed483440", size = 128690, upload-time = "2025-07-25T14:31:56.708Z" }, - { url = "https://files.pythonhosted.org/packages/48/e4/2e075348e7772aa1404d51d8df25ff4d6ee3daf682732cb21308e3b59c32/orjson-3.11.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f716bcc166524eddfcf9f13f8209ac19a7f27b05cf591e883419079d98c8c99d", size = 130646, upload-time = "2025-07-25T14:31:58.165Z" }, - { url = "https://files.pythonhosted.org/packages/97/09/50daacd3ac7ae564186924c8d1121940f2c78c64d6804dbe81dd735ab087/orjson-3.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:507d6012fab05465d8bf21f5d7f4635ba4b6d60132874e349beff12fb51af7fe", size = 132620, upload-time = "2025-07-25T14:31:59.226Z" }, - { url = "https://files.pythonhosted.org/packages/da/21/5f22093fa90e6d6fcf8111942b530a4ad19ee1cc0b06ddad4a63b16ab852/orjson-3.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1545083b0931f754c80fd2422a73d83bea7a6d1b6de104a5f2c8dd3d64c291e", size = 135121, upload-time = "2025-07-25T14:32:00.653Z" }, - { url = "https://files.pythonhosted.org/packages/48/90/77ad4bfa6bd400a3d241695e3e39975e32fe027aea5cb0b171bd2080c427/orjson-3.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e217ce3bad76351e1eb29ebe5ca630326f45cd2141f62620107a229909501a3", size = 131131, upload-time = "2025-07-25T14:32:01.821Z" }, - { url = "https://files.pythonhosted.org/packages/5a/64/d383675229f7ffd971b6ec6cdd3016b00877bb6b2d5fc1fd099c2ec2ad57/orjson-3.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06ef26e009304bda4df42e4afe518994cde6f89b4b04c0ff24021064f83f4fbb", size = 131025, upload-time = "2025-07-25T14:32:02.879Z" }, - { url = "https://files.pythonhosted.org/packages/d4/82/e4017d8d98597f6056afaf75021ff390154d1e2722c66ba45a4d50f82606/orjson-3.11.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ba49683b87bea3ae1489a88e766e767d4f423a669a61270b6d6a7ead1c33bd65", size = 404464, upload-time = "2025-07-25T14:32:04.384Z" }, - { url = "https://files.pythonhosted.org/packages/77/7e/45c7f813c30d386c0168a32ce703494262458af6b222a3eeac1c0bb88822/orjson-3.11.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5072488fcc5cbcda2ece966d248e43ea1d222e19dd4c56d3f82747777f24d864", size = 146416, upload-time = "2025-07-25T14:32:05.57Z" }, - { url = "https://files.pythonhosted.org/packages/41/71/6ccb4d7875ec3349409960769a28349f477856f05de9fd961454c2b99230/orjson-3.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f58ae2bcd119226fe4aa934b5880fe57b8e97b69e51d5d91c88a89477a307016", size = 135497, upload-time = "2025-07-25T14:32:06.704Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ce/df8dac7da075962fdbfca55d53e3601aa910c9f23606033bf0f084835720/orjson-3.11.1-cp310-cp310-win32.whl", hash = "sha256:6723be919c07906781b9c63cc52dc7d2fb101336c99dd7e85d3531d73fb493f7", size = 136807, upload-time = "2025-07-25T14:32:08.303Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a0/f6c2be24709d1742d878b4530fa0c3f4a5e190d51397b680abbf44d11dbf/orjson-3.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:5fd44d69ddfdfb4e8d0d83f09d27a4db34930fba153fbf79f8d4ae8b47914e04", size = 131561, upload-time = "2025-07-25T14:32:09.444Z" }, - { url = "https://files.pythonhosted.org/packages/a5/92/7ab270b5b3df8d5b0d3e572ddf2f03c9f6a79726338badf1ec8594e1469d/orjson-3.11.1-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:15e2a57ce3b57c1a36acffcc02e823afefceee0a532180c2568c62213c98e3ef", size = 240918, upload-time = "2025-07-25T14:32:11.021Z" }, - { url = "https://files.pythonhosted.org/packages/80/41/df44684cfbd2e2e03bf9b09fdb14b7abcfff267998790b6acfb69ad435f0/orjson-3.11.1-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:17040a83ecaa130474af05bbb59a13cfeb2157d76385556041f945da936b1afd", size = 129386, upload-time = "2025-07-25T14:32:12.361Z" }, - { url = "https://files.pythonhosted.org/packages/c1/08/958f56edd18ba1827ad0c74b2b41a7ae0864718adee8ccb5d1a5528f8761/orjson-3.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a68f23f09e5626cc0867a96cf618f68b91acb4753d33a80bf16111fd7f9928c", size = 132508, upload-time = "2025-07-25T14:32:13.917Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b6/5e56e189dacbf51e53ba8150c20e61ee746f6d57b697f5c52315ffc88a83/orjson-3.11.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47e07528bb6ccbd6e32a55e330979048b59bfc5518b47c89bc7ab9e3de15174a", size = 128501, upload-time = "2025-07-25T14:32:15.13Z" }, - { url = "https://files.pythonhosted.org/packages/fe/de/f6c301a514f5934405fd4b8f3d3efc758c911d06c3de3f4be1e30d675fa4/orjson-3.11.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3807cce72bf40a9d251d689cbec28d2efd27e0f6673709f948f971afd52cb09", size = 130465, upload-time = "2025-07-25T14:32:17.355Z" }, - { url = "https://files.pythonhosted.org/packages/47/08/f7dbaab87d6f05eebff2d7b8e6a8ed5f13b2fe3e3ae49472b527d03dbd7a/orjson-3.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b2dc7e88da4ca201c940f5e6127998d9e89aa64264292334dad62854bc7fc27", size = 132416, upload-time = "2025-07-25T14:32:18.933Z" }, - { url = "https://files.pythonhosted.org/packages/43/3f/dd5a185273b7ba6aa238cfc67bf9edaa1885ae51ce942bc1a71d0f99f574/orjson-3.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3091dad33ac9e67c0a550cfff8ad5be156e2614d6f5d2a9247df0627751a1495", size = 134924, upload-time = "2025-07-25T14:32:20.134Z" }, - { url = "https://files.pythonhosted.org/packages/db/ef/729d23510eaa81f0ce9d938d99d72dcf5e4ed3609d9d0bcf9c8a282cc41a/orjson-3.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ed0fce2307843b79a0c83de49f65b86197f1e2310de07af9db2a1a77a61ce4c", size = 130938, upload-time = "2025-07-25T14:32:21.769Z" }, - { url = "https://files.pythonhosted.org/packages/82/96/120feb6807f9e1f4c68fc842a0f227db8575eafb1a41b2537567b91c19d8/orjson-3.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5a31e84782a18c30abd56774c0cfa7b9884589f4d37d9acabfa0504dad59bb9d", size = 130811, upload-time = "2025-07-25T14:32:22.931Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/4695e946a453fa22ff945da4b1ed0691b3f4ec86b828d398288db4a0ff79/orjson-3.11.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26b6c821abf1ae515fbb8e140a2406c9f9004f3e52acb780b3dee9bfffddbd84", size = 404272, upload-time = "2025-07-25T14:32:25.238Z" }, - { url = "https://files.pythonhosted.org/packages/cd/7b/1c953e2c9e55af126c6cb678a30796deb46d7713abdeb706b8765929464c/orjson-3.11.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f857b3d134b36a8436f1e24dcb525b6b945108b30746c1b0b556200b5cb76d39", size = 146196, upload-time = "2025-07-25T14:32:26.909Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c2/bef5d3bc83f2e178592ff317e2cf7bd38ebc16b641f076ea49f27aadd1d3/orjson-3.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df146f2a14116ce80f7da669785fcb411406d8e80136558b0ecda4c924b9ac55", size = 135336, upload-time = "2025-07-25T14:32:28.22Z" }, - { url = "https://files.pythonhosted.org/packages/92/95/bc6006881ebdb4608ed900a763c3e3c6be0d24c3aadd62beb774f9464ec6/orjson-3.11.1-cp311-cp311-win32.whl", hash = "sha256:d777c57c1f86855fe5492b973f1012be776e0398571f7cc3970e9a58ecf4dc17", size = 136665, upload-time = "2025-07-25T14:32:29.976Z" }, - { url = "https://files.pythonhosted.org/packages/59/c3/1f2b9cc0c60ea2473d386fed2df2b25ece50aeb73c798d4669aadff3061e/orjson-3.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:e9a5fd589951f02ec2fcb8d69339258bbf74b41b104c556e6d4420ea5e059313", size = 131388, upload-time = "2025-07-25T14:32:31.595Z" }, - { url = "https://files.pythonhosted.org/packages/b0/e5/40c97e5a6b85944022fe54b463470045b8651b7bb2f1e16a95c42812bf97/orjson-3.11.1-cp311-cp311-win_arm64.whl", hash = "sha256:4cddbe41ee04fddad35d75b9cf3e3736ad0b80588280766156b94783167777af", size = 126786, upload-time = "2025-07-25T14:32:32.787Z" }, - { url = "https://files.pythonhosted.org/packages/98/77/e55513826b712807caadb2b733eee192c1df105c6bbf0d965c253b72f124/orjson-3.11.1-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2b7c8be96db3a977367250c6367793a3c5851a6ca4263f92f0b48d00702f9910", size = 240955, upload-time = "2025-07-25T14:32:34.056Z" }, - { url = "https://files.pythonhosted.org/packages/c9/88/a78132dddcc9c3b80a9fa050b3516bb2c996a9d78ca6fb47c8da2a80a696/orjson-3.11.1-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:72e18088f567bd4a45db5e3196677d9ed1605e356e500c8e32dd6e303167a13d", size = 129294, upload-time = "2025-07-25T14:32:35.323Z" }, - { url = "https://files.pythonhosted.org/packages/09/02/6591e0dcb2af6bceea96cb1b5f4b48c1445492a3ef2891ac4aa306bb6f73/orjson-3.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d346e2ae1ce17888f7040b65a5a4a0c9734cb20ffbd228728661e020b4c8b3a5", size = 132310, upload-time = "2025-07-25T14:32:36.53Z" }, - { url = "https://files.pythonhosted.org/packages/e9/36/c1cfbc617bcfa4835db275d5e0fe9bbdbe561a4b53d3b2de16540ec29c50/orjson-3.11.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4bda5426ebb02ceb806a7d7ec9ba9ee5e0c93fca62375151a7b1c00bc634d06b", size = 128529, upload-time = "2025-07-25T14:32:37.817Z" }, - { url = "https://files.pythonhosted.org/packages/7c/bd/91a156c5df3aaf1d68b2ab5be06f1969955a8d3e328d7794f4338ac1d017/orjson-3.11.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10506cebe908542c4f024861102673db534fd2e03eb9b95b30d94438fa220abf", size = 130925, upload-time = "2025-07-25T14:32:39.03Z" }, - { url = "https://files.pythonhosted.org/packages/a3/4c/a65cc24e9a5f87c9833a50161ab97b5edbec98bec99dfbba13827549debc/orjson-3.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45202ee3f5494644e064c41abd1320497fb92fd31fc73af708708af664ac3b56", size = 132432, upload-time = "2025-07-25T14:32:40.619Z" }, - { url = "https://files.pythonhosted.org/packages/2e/4d/3fc3e5d7115f4f7d01b481e29e5a79bcbcc45711a2723242787455424f40/orjson-3.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5adaf01b92e0402a9ac5c3ebe04effe2bbb115f0914a0a53d34ea239a746289", size = 135069, upload-time = "2025-07-25T14:32:41.84Z" }, - { url = "https://files.pythonhosted.org/packages/dc/c6/7585aa8522af896060dc0cd7c336ba6c574ae854416811ee6642c505cc95/orjson-3.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6162a1a757a1f1f4a94bc6ffac834a3602e04ad5db022dd8395a54ed9dd51c81", size = 131045, upload-time = "2025-07-25T14:32:43.085Z" }, - { url = "https://files.pythonhosted.org/packages/6a/4e/b8a0a943793d2708ebc39e743c943251e08ee0f3279c880aefd8e9cb0c70/orjson-3.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:78404206977c9f946613d3f916727c189d43193e708d760ea5d4b2087d6b0968", size = 130597, upload-time = "2025-07-25T14:32:44.336Z" }, - { url = "https://files.pythonhosted.org/packages/72/2b/7d30e2aed2f585d5d385fb45c71d9b16ba09be58c04e8767ae6edc6c9282/orjson-3.11.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:db48f8e81072e26df6cdb0e9fff808c28597c6ac20a13d595756cf9ba1fed48a", size = 404207, upload-time = "2025-07-25T14:32:45.612Z" }, - { url = "https://files.pythonhosted.org/packages/1b/7e/772369ec66fcbce79477f0891918309594cd00e39b67a68d4c445d2ab754/orjson-3.11.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0c1e394e67ced6bb16fea7054d99fbdd99a539cf4d446d40378d4c06e0a8548d", size = 146628, upload-time = "2025-07-25T14:32:46.981Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c8/62bdb59229d7e393ae309cef41e32cc1f0b567b21dfd0742da70efb8b40c/orjson-3.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e7a840752c93d4eecd1378e9bb465c3703e127b58f675cd5c620f361b6cf57a4", size = 135449, upload-time = "2025-07-25T14:32:48.727Z" }, - { url = "https://files.pythonhosted.org/packages/02/47/1c99aa60e19f781424eabeaacd9e999eafe5b59c81ead4273b773f0f3af1/orjson-3.11.1-cp312-cp312-win32.whl", hash = "sha256:4537b0e09f45d2b74cb69c7f39ca1e62c24c0488d6bf01cd24673c74cd9596bf", size = 136653, upload-time = "2025-07-25T14:32:50.622Z" }, - { url = "https://files.pythonhosted.org/packages/31/9a/132999929a2892ab07e916669accecc83e5bff17e11a1186b4c6f23231f0/orjson-3.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:dbee6b050062540ae404530cacec1bf25e56e8d87d8d9b610b935afeb6725cae", size = 131426, upload-time = "2025-07-25T14:32:51.883Z" }, - { url = "https://files.pythonhosted.org/packages/9c/77/d984ee5a1ca341090902e080b187721ba5d1573a8d9759e0c540975acfb2/orjson-3.11.1-cp312-cp312-win_arm64.whl", hash = "sha256:f55e557d4248322d87c4673e085c7634039ff04b47bfc823b87149ae12bef60d", size = 126635, upload-time = "2025-07-25T14:32:53.2Z" }, - { url = "https://files.pythonhosted.org/packages/c9/e9/880ef869e6f66279ce3a381a32afa0f34e29a94250146911eee029e56efc/orjson-3.11.1-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:53cfefe4af059e65aabe9683f76b9c88bf34b4341a77d329227c2424e0e59b0e", size = 240835, upload-time = "2025-07-25T14:32:54.507Z" }, - { url = "https://files.pythonhosted.org/packages/f0/1f/52039ef3d03eeea21763b46bc99ebe11d9de8510c72b7b5569433084a17e/orjson-3.11.1-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:93d5abed5a6f9e1b6f9b5bf6ed4423c11932b5447c2f7281d3b64e0f26c6d064", size = 129226, upload-time = "2025-07-25T14:32:55.908Z" }, - { url = "https://files.pythonhosted.org/packages/ee/da/59fdffc9465a760be2cd3764ef9cd5535eec8f095419f972fddb123b6d0e/orjson-3.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dbf06642f3db2966df504944cdd0eb68ca2717f0353bb20b20acd78109374a6", size = 132261, upload-time = "2025-07-25T14:32:57.538Z" }, - { url = "https://files.pythonhosted.org/packages/bb/5c/8610911c7e969db7cf928c8baac4b2f1e68d314bc3057acf5ca64f758435/orjson-3.11.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dddf4e78747fa7f2188273f84562017a3c4f0824485b78372513c1681ea7a894", size = 128614, upload-time = "2025-07-25T14:32:58.808Z" }, - { url = "https://files.pythonhosted.org/packages/f7/a1/a1db9d4310d014c90f3b7e9b72c6fb162cba82c5f46d0b345669eaebdd3a/orjson-3.11.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa3fe8653c9f57f0e16f008e43626485b6723b84b2f741f54d1258095b655912", size = 130968, upload-time = "2025-07-25T14:33:00.038Z" }, - { url = "https://files.pythonhosted.org/packages/56/ff/11acd1fd7c38ea7a1b5d6bf582ae3da05931bee64620995eb08fd63c77fe/orjson-3.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6334d2382aff975a61f6f4d1c3daf39368b887c7de08f7c16c58f485dcf7adb2", size = 132439, upload-time = "2025-07-25T14:33:01.354Z" }, - { url = "https://files.pythonhosted.org/packages/70/f9/bb564dd9450bf8725e034a8ad7f4ae9d4710a34caf63b85ce1c0c6d40af0/orjson-3.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a3d0855b643f259ee0cb76fe3df4c04483354409a520a902b067c674842eb6b8", size = 135299, upload-time = "2025-07-25T14:33:03.079Z" }, - { url = "https://files.pythonhosted.org/packages/94/bb/c8eafe6051405e241dda3691db4d9132d3c3462d1d10a17f50837dd130b4/orjson-3.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0eacdfeefd0a79987926476eb16e0245546bedeb8febbbbcf4b653e79257a8e4", size = 131004, upload-time = "2025-07-25T14:33:04.416Z" }, - { url = "https://files.pythonhosted.org/packages/a2/40/bed8d7dcf1bd2df8813bf010a25f645863a2f75e8e0ebdb2b55784cf1a62/orjson-3.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0ed07faf9e4873518c60480325dcbc16d17c59a165532cccfb409b4cdbaeff24", size = 130583, upload-time = "2025-07-25T14:33:05.768Z" }, - { url = "https://files.pythonhosted.org/packages/57/e7/cfa2eb803ad52d74fbb5424a429b5be164e51d23f1d853e5e037173a5c48/orjson-3.11.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d6d308dd578ae3658f62bb9eba54801533225823cd3248c902be1ebc79b5e014", size = 404218, upload-time = "2025-07-25T14:33:07.117Z" }, - { url = "https://files.pythonhosted.org/packages/d5/21/bc703af5bc6e9c7e18dcf4404dcc4ec305ab9bb6c82d3aee5952c0c56abf/orjson-3.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c4aa13ca959ba6b15c0a98d3d204b850f9dc36c08c9ce422ffb024eb30d6e058", size = 146605, upload-time = "2025-07-25T14:33:08.55Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fe/d26a0150534c4965a06f556aa68bf3c3b82999d5d7b0facd3af7b390c4af/orjson-3.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:be3d0653322abc9b68e5bcdaee6cfd58fcbe9973740ab222b87f4d687232ab1f", size = 135434, upload-time = "2025-07-25T14:33:09.967Z" }, - { url = "https://files.pythonhosted.org/packages/89/b6/1cb28365f08cbcffc464f8512320c6eb6db6a653f03d66de47ea3c19385f/orjson-3.11.1-cp313-cp313-win32.whl", hash = "sha256:4dd34e7e2518de8d7834268846f8cab7204364f427c56fb2251e098da86f5092", size = 136596, upload-time = "2025-07-25T14:33:11.333Z" }, - { url = "https://files.pythonhosted.org/packages/f9/35/7870d0d3ed843652676d84d8a6038791113eacc85237b673b925802826b8/orjson-3.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:d6895d32032b6362540e6d0694b19130bb4f2ad04694002dce7d8af588ca5f77", size = 131319, upload-time = "2025-07-25T14:33:12.614Z" }, - { url = "https://files.pythonhosted.org/packages/b7/3e/5bcd50fd865eb664d4edfdaaaff51e333593ceb5695a22c0d0a0d2b187ba/orjson-3.11.1-cp313-cp313-win_arm64.whl", hash = "sha256:bb7c36d5d3570fcbb01d24fa447a21a7fe5a41141fd88e78f7994053cc4e28f4", size = 126613, upload-time = "2025-07-25T14:33:13.927Z" }, - { url = "https://files.pythonhosted.org/packages/61/d8/0a5cd31ed100b4e569e143cb0cddefc21f0bcb8ce284f44bca0bb0e10f3d/orjson-3.11.1-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7b71ef394327b3d0b39f6ea7ade2ecda2731a56c6a7cbf0d6a7301203b92a89b", size = 240819, upload-time = "2025-07-25T14:33:15.223Z" }, - { url = "https://files.pythonhosted.org/packages/b9/95/7eb2c76c92192ceca16bc81845ff100bbb93f568b4b94d914b6a4da47d61/orjson-3.11.1-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:77c0fe28ed659b62273995244ae2aa430e432c71f86e4573ab16caa2f2e3ca5e", size = 129218, upload-time = "2025-07-25T14:33:16.637Z" }, - { url = "https://files.pythonhosted.org/packages/da/84/e6b67f301b18adbbc346882f456bea44daebbd032ba725dbd7b741e3a7f1/orjson-3.11.1-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:1495692f1f1ba2467df429343388a0ed259382835922e124c0cfdd56b3d1f727", size = 132238, upload-time = "2025-07-25T14:33:17.934Z" }, - { url = "https://files.pythonhosted.org/packages/84/78/a45a86e29d9b2f391f9d00b22da51bc4b46b86b788fd42df2c5fcf3e8005/orjson-3.11.1-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:08c6a762fca63ca4dc04f66c48ea5d2428db55839fec996890e1bfaf057b658c", size = 130998, upload-time = "2025-07-25T14:33:19.282Z" }, - { url = "https://files.pythonhosted.org/packages/ea/8f/6eb3ee6760d93b2ce996a8529164ee1f5bafbdf64b74c7314b68db622b32/orjson-3.11.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e26794fe3976810b2c01fda29bd9ac7c91a3c1284b29cc9a383989f7b614037", size = 130559, upload-time = "2025-07-25T14:33:20.589Z" }, - { url = "https://files.pythonhosted.org/packages/1b/78/9572ae94bdba6813917c9387e7834224c011ea6b4530ade07d718fd31598/orjson-3.11.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4b4b4f8f0b1d3ef8dc73e55363a0ffe012a42f4e2f1a140bf559698dca39b3fa", size = 404231, upload-time = "2025-07-25T14:33:22.019Z" }, - { url = "https://files.pythonhosted.org/packages/1f/a3/68381ad0757e084927c5ee6cfdeab1c6c89405949ee493db557e60871c4c/orjson-3.11.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:848be553ea35aa89bfefbed2e27c8a41244c862956ab8ba00dc0b27e84fd58de", size = 146658, upload-time = "2025-07-25T14:33:23.675Z" }, - { url = "https://files.pythonhosted.org/packages/00/db/fac56acf77aab778296c3f541a3eec643266f28ecd71d6c0cba251e47655/orjson-3.11.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c964c29711a4b1df52f8d9966f015402a6cf87753a406c1c4405c407dd66fd45", size = 135443, upload-time = "2025-07-25T14:33:25.04Z" }, - { url = "https://files.pythonhosted.org/packages/76/b1/326fa4b87426197ead61c1eec2eeb3babc9eb33b480ac1f93894e40c8c08/orjson-3.11.1-cp314-cp314-win32.whl", hash = "sha256:33aada2e6b6bc9c540d396528b91e666cedb383740fee6e6a917f561b390ecb1", size = 136643, upload-time = "2025-07-25T14:33:26.449Z" }, - { url = "https://files.pythonhosted.org/packages/0f/8e/2987ae2109f3bfd39680f8a187d1bc09ad7f8fb019dcdc719b08c7242ade/orjson-3.11.1-cp314-cp314-win_amd64.whl", hash = "sha256:68e10fd804e44e36188b9952543e3fa22f5aa8394da1b5283ca2b423735c06e8", size = 131324, upload-time = "2025-07-25T14:33:27.896Z" }, - { url = "https://files.pythonhosted.org/packages/21/5f/253e08e6974752b124fbf3a4de3ad53baa766b0cb4a333d47706d307e396/orjson-3.11.1-cp314-cp314-win_arm64.whl", hash = "sha256:f3cf6c07f8b32127d836be8e1c55d4f34843f7df346536da768e9f73f22078a1", size = 126605, upload-time = "2025-07-25T14:33:29.244Z" }, -] - -[[package]] -name = "overrides" -version = "7.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812, upload-time = "2024-01-27T21:01:33.423Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832, upload-time = "2024-01-27T21:01:31.393Z" }, -] - -[[package]] -name = "packaging" -version = "25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, -] - -[[package]] -name = "pandas" -version = "2.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/79/8e/0e90233ac205ad182bd6b422532695d2b9414944a280488105d598c70023/pandas-2.3.2.tar.gz", hash = "sha256:ab7b58f8f82706890924ccdfb5f48002b83d2b5a3845976a9fb705d36c34dcdb", size = 4488684, upload-time = "2025-08-21T10:28:29.257Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/16/a8eeb70aad84ccbf14076793f90e0031eded63c1899aeae9fdfbf37881f4/pandas-2.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52bc29a946304c360561974c6542d1dd628ddafa69134a7131fdfd6a5d7a1a35", size = 11539648, upload-time = "2025-08-21T10:26:36.236Z" }, - { url = "https://files.pythonhosted.org/packages/47/f1/c5bdaea13bf3708554d93e948b7ea74121ce6e0d59537ca4c4f77731072b/pandas-2.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:220cc5c35ffaa764dd5bb17cf42df283b5cb7fdf49e10a7b053a06c9cb48ee2b", size = 10786923, upload-time = "2025-08-21T10:26:40.518Z" }, - { url = "https://files.pythonhosted.org/packages/bb/10/811fa01476d29ffed692e735825516ad0e56d925961819e6126b4ba32147/pandas-2.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42c05e15111221384019897df20c6fe893b2f697d03c811ee67ec9e0bb5a3424", size = 11726241, upload-time = "2025-08-21T10:26:43.175Z" }, - { url = "https://files.pythonhosted.org/packages/c4/6a/40b043b06e08df1ea1b6d20f0e0c2f2c4ec8c4f07d1c92948273d943a50b/pandas-2.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc03acc273c5515ab69f898df99d9d4f12c4d70dbfc24c3acc6203751d0804cf", size = 12349533, upload-time = "2025-08-21T10:26:46.611Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ea/2e081a2302e41a9bca7056659fdd2b85ef94923723e41665b42d65afd347/pandas-2.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d25c20a03e8870f6339bcf67281b946bd20b86f1a544ebbebb87e66a8d642cba", size = 13202407, upload-time = "2025-08-21T10:26:49.068Z" }, - { url = "https://files.pythonhosted.org/packages/f4/12/7ff9f6a79e2ee8869dcf70741ef998b97ea20050fe25f83dc759764c1e32/pandas-2.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21bb612d148bb5860b7eb2c10faacf1a810799245afd342cf297d7551513fbb6", size = 13837212, upload-time = "2025-08-21T10:26:51.832Z" }, - { url = "https://files.pythonhosted.org/packages/d8/df/5ab92fcd76455a632b3db34a746e1074d432c0cdbbd28d7cd1daba46a75d/pandas-2.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:b62d586eb25cb8cb70a5746a378fc3194cb7f11ea77170d59f889f5dfe3cec7a", size = 11338099, upload-time = "2025-08-21T10:26:54.382Z" }, - { url = "https://files.pythonhosted.org/packages/7a/59/f3e010879f118c2d400902d2d871c2226cef29b08c09fb8dc41111730400/pandas-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1333e9c299adcbb68ee89a9bb568fc3f20f9cbb419f1dd5225071e6cddb2a743", size = 11563308, upload-time = "2025-08-21T10:26:56.656Z" }, - { url = "https://files.pythonhosted.org/packages/38/18/48f10f1cc5c397af59571d638d211f494dba481f449c19adbd282aa8f4ca/pandas-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76972bcbd7de8e91ad5f0ca884a9f2c477a2125354af624e022c49e5bd0dfff4", size = 10820319, upload-time = "2025-08-21T10:26:59.162Z" }, - { url = "https://files.pythonhosted.org/packages/95/3b/1e9b69632898b048e223834cd9702052bcf06b15e1ae716eda3196fb972e/pandas-2.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b98bdd7c456a05eef7cd21fd6b29e3ca243591fe531c62be94a2cc987efb5ac2", size = 11790097, upload-time = "2025-08-21T10:27:02.204Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ef/0e2ffb30b1f7fbc9a588bd01e3c14a0d96854d09a887e15e30cc19961227/pandas-2.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d81573b3f7db40d020983f78721e9bfc425f411e616ef019a10ebf597aedb2e", size = 12397958, upload-time = "2025-08-21T10:27:05.409Z" }, - { url = "https://files.pythonhosted.org/packages/23/82/e6b85f0d92e9afb0e7f705a51d1399b79c7380c19687bfbf3d2837743249/pandas-2.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e190b738675a73b581736cc8ec71ae113d6c3768d0bd18bffa5b9a0927b0b6ea", size = 13225600, upload-time = "2025-08-21T10:27:07.791Z" }, - { url = "https://files.pythonhosted.org/packages/e8/f1/f682015893d9ed51611948bd83683670842286a8edd4f68c2c1c3b231eef/pandas-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c253828cb08f47488d60f43c5fc95114c771bbfff085da54bfc79cb4f9e3a372", size = 13879433, upload-time = "2025-08-21T10:27:10.347Z" }, - { url = "https://files.pythonhosted.org/packages/a7/e7/ae86261695b6c8a36d6a4c8d5f9b9ede8248510d689a2f379a18354b37d7/pandas-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:9467697b8083f9667b212633ad6aa4ab32436dcbaf4cd57325debb0ddef2012f", size = 11336557, upload-time = "2025-08-21T10:27:12.983Z" }, - { url = "https://files.pythonhosted.org/packages/ec/db/614c20fb7a85a14828edd23f1c02db58a30abf3ce76f38806155d160313c/pandas-2.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fbb977f802156e7a3f829e9d1d5398f6192375a3e2d1a9ee0803e35fe70a2b9", size = 11587652, upload-time = "2025-08-21T10:27:15.888Z" }, - { url = "https://files.pythonhosted.org/packages/99/b0/756e52f6582cade5e746f19bad0517ff27ba9c73404607c0306585c201b3/pandas-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b9b52693123dd234b7c985c68b709b0b009f4521000d0525f2b95c22f15944b", size = 10717686, upload-time = "2025-08-21T10:27:18.486Z" }, - { url = "https://files.pythonhosted.org/packages/37/4c/dd5ccc1e357abfeee8353123282de17997f90ff67855f86154e5a13b81e5/pandas-2.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bd281310d4f412733f319a5bc552f86d62cddc5f51d2e392c8787335c994175", size = 11278722, upload-time = "2025-08-21T10:27:21.149Z" }, - { url = "https://files.pythonhosted.org/packages/d3/a4/f7edcfa47e0a88cda0be8b068a5bae710bf264f867edfdf7b71584ace362/pandas-2.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96d31a6b4354e3b9b8a2c848af75d31da390657e3ac6f30c05c82068b9ed79b9", size = 11987803, upload-time = "2025-08-21T10:27:23.767Z" }, - { url = "https://files.pythonhosted.org/packages/f6/61/1bce4129f93ab66f1c68b7ed1c12bac6a70b1b56c5dab359c6bbcd480b52/pandas-2.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:df4df0b9d02bb873a106971bb85d448378ef14b86ba96f035f50bbd3688456b4", size = 12766345, upload-time = "2025-08-21T10:27:26.6Z" }, - { url = "https://files.pythonhosted.org/packages/8e/46/80d53de70fee835531da3a1dae827a1e76e77a43ad22a8cd0f8142b61587/pandas-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:213a5adf93d020b74327cb2c1b842884dbdd37f895f42dcc2f09d451d949f811", size = 13439314, upload-time = "2025-08-21T10:27:29.213Z" }, - { url = "https://files.pythonhosted.org/packages/28/30/8114832daff7489f179971dbc1d854109b7f4365a546e3ea75b6516cea95/pandas-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c13b81a9347eb8c7548f53fd9a4f08d4dfe996836543f805c987bafa03317ae", size = 10983326, upload-time = "2025-08-21T10:27:31.901Z" }, - { url = "https://files.pythonhosted.org/packages/27/64/a2f7bf678af502e16b472527735d168b22b7824e45a4d7e96a4fbb634b59/pandas-2.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0c6ecbac99a354a051ef21c5307601093cb9e0f4b1855984a084bfec9302699e", size = 11531061, upload-time = "2025-08-21T10:27:34.647Z" }, - { url = "https://files.pythonhosted.org/packages/54/4c/c3d21b2b7769ef2f4c2b9299fcadd601efa6729f1357a8dbce8dd949ed70/pandas-2.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c6f048aa0fd080d6a06cc7e7537c09b53be6642d330ac6f54a600c3ace857ee9", size = 10668666, upload-time = "2025-08-21T10:27:37.203Z" }, - { url = "https://files.pythonhosted.org/packages/50/e2/f775ba76ecfb3424d7f5862620841cf0edb592e9abd2d2a5387d305fe7a8/pandas-2.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0064187b80a5be6f2f9c9d6bdde29372468751dfa89f4211a3c5871854cfbf7a", size = 11332835, upload-time = "2025-08-21T10:27:40.188Z" }, - { url = "https://files.pythonhosted.org/packages/8f/52/0634adaace9be2d8cac9ef78f05c47f3a675882e068438b9d7ec7ef0c13f/pandas-2.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ac8c320bded4718b298281339c1a50fb00a6ba78cb2a63521c39bec95b0209b", size = 12057211, upload-time = "2025-08-21T10:27:43.117Z" }, - { url = "https://files.pythonhosted.org/packages/0b/9d/2df913f14b2deb9c748975fdb2491da1a78773debb25abbc7cbc67c6b549/pandas-2.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:114c2fe4f4328cf98ce5716d1532f3ab79c5919f95a9cfee81d9140064a2e4d6", size = 12749277, upload-time = "2025-08-21T10:27:45.474Z" }, - { url = "https://files.pythonhosted.org/packages/87/af/da1a2417026bd14d98c236dba88e39837182459d29dcfcea510b2ac9e8a1/pandas-2.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:48fa91c4dfb3b2b9bfdb5c24cd3567575f4e13f9636810462ffed8925352be5a", size = 13415256, upload-time = "2025-08-21T10:27:49.885Z" }, - { url = "https://files.pythonhosted.org/packages/22/3c/f2af1ce8840ef648584a6156489636b5692c162771918aa95707c165ad2b/pandas-2.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:12d039facec710f7ba305786837d0225a3444af7bbd9c15c32ca2d40d157ed8b", size = 10982579, upload-time = "2025-08-21T10:28:08.435Z" }, - { url = "https://files.pythonhosted.org/packages/f3/98/8df69c4097a6719e357dc249bf437b8efbde808038268e584421696cbddf/pandas-2.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c624b615ce97864eb588779ed4046186f967374185c047070545253a52ab2d57", size = 12028163, upload-time = "2025-08-21T10:27:52.232Z" }, - { url = "https://files.pythonhosted.org/packages/0e/23/f95cbcbea319f349e10ff90db488b905c6883f03cbabd34f6b03cbc3c044/pandas-2.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0cee69d583b9b128823d9514171cabb6861e09409af805b54459bd0c821a35c2", size = 11391860, upload-time = "2025-08-21T10:27:54.673Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1b/6a984e98c4abee22058aa75bfb8eb90dce58cf8d7296f8bc56c14bc330b0/pandas-2.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2319656ed81124982900b4c37f0e0c58c015af9a7bbc62342ba5ad07ace82ba9", size = 11309830, upload-time = "2025-08-21T10:27:56.957Z" }, - { url = "https://files.pythonhosted.org/packages/15/d5/f0486090eb18dd8710bf60afeaf638ba6817047c0c8ae5c6a25598665609/pandas-2.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b37205ad6f00d52f16b6d09f406434ba928c1a1966e2771006a9033c736d30d2", size = 11883216, upload-time = "2025-08-21T10:27:59.302Z" }, - { url = "https://files.pythonhosted.org/packages/10/86/692050c119696da19e20245bbd650d8dfca6ceb577da027c3a73c62a047e/pandas-2.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:837248b4fc3a9b83b9c6214699a13f069dc13510a6a6d7f9ba33145d2841a012", size = 12699743, upload-time = "2025-08-21T10:28:02.447Z" }, - { url = "https://files.pythonhosted.org/packages/cd/d7/612123674d7b17cf345aad0a10289b2a384bff404e0463a83c4a3a59d205/pandas-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d2c3554bd31b731cd6490d94a28f3abb8dd770634a9e06eb6d2911b9827db370", size = 13186141, upload-time = "2025-08-21T10:28:05.377Z" }, -] - -[[package]] -name = "pandocfilters" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454, upload-time = "2024-01-18T20:08:13.726Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663, upload-time = "2024-01-18T20:08:11.28Z" }, -] - -[[package]] -name = "parso" -version = "0.8.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609, upload-time = "2024-04-05T09:43:55.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650, upload-time = "2024-04-05T09:43:53.299Z" }, -] - -[[package]] -name = "partd" -version = "1.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "locket" }, - { name = "toolz" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b2/3a/3f06f34820a31257ddcabdfafc2672c5816be79c7e353b02c1f318daa7d4/partd-1.4.2.tar.gz", hash = "sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c", size = 21029, upload-time = "2024-05-06T19:51:41.945Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/e7/40fb618334dcdf7c5a316c0e7343c5cd82d3d866edc100d98e29bc945ecd/partd-1.4.2-py3-none-any.whl", hash = "sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f", size = 18905, upload-time = "2024-05-06T19:51:39.271Z" }, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, -] - -[[package]] -name = "pbr" -version = "6.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "setuptools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/d2/510cc0d218e753ba62a1bc1434651db3cd797a9716a0a66cc714cb4f0935/pbr-6.1.1.tar.gz", hash = "sha256:93ea72ce6989eb2eed99d0f75721474f69ad88128afdef5ac377eb797c4bf76b", size = 125702, upload-time = "2025-02-04T14:28:06.514Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/ac/684d71315abc7b1214d59304e23a982472967f6bf4bde5a98f1503f648dc/pbr-6.1.1-py2.py3-none-any.whl", hash = "sha256:38d4daea5d9fa63b3f626131b9d34947fd0c8be9b05a29276870580050a25a76", size = 108997, upload-time = "2025-02-04T14:28:03.168Z" }, -] - -[[package]] -name = "pexpect" -version = "4.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ptyprocess" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, -] - -[[package]] -name = "planetary-computer" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "packaging" }, - { name = "pydantic" }, - { name = "pystac" }, - { name = "pystac-client" }, - { name = "python-dotenv" }, - { name = "pytz" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9b/d3/ccfb0e823e9beb72b7e6b749f0985c4db1530d4e4a145b1b5d12f5de417e/planetary-computer-1.0.0.tar.gz", hash = "sha256:5958a8e1d8ba1aafc7ac45878df2d7d03405806ae31ed2e675333faebca960cc", size = 17076, upload-time = "2023-07-05T13:06:58.679Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/7a/3965a2b58d172e106c1064af19707d29f86a0142c28ff40185490a0a8cfe/planetary_computer-1.0.0-py3-none-any.whl", hash = "sha256:7af5839f9346c1d23d53fff4e80e955db18a2d81992877816e22dcbc2f90c40d", size = 14163, upload-time = "2023-07-05T13:06:57.06Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.3.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "pre-commit" -version = "4.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cfgv" }, - { name = "identify" }, - { name = "nodeenv" }, - { name = "pyyaml" }, - { name = "virtualenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/08/39/679ca9b26c7bb2999ff122d50faa301e49af82ca9c066ec061cfbc0c6784/pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", size = 193424, upload-time = "2025-03-18T21:35:20.987Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707, upload-time = "2025-03-18T21:35:19.343Z" }, -] - -[[package]] -name = "prometheus-client" -version = "0.22.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/cf/40dde0a2be27cc1eb41e333d1a674a74ce8b8b0457269cc640fd42b07cf7/prometheus_client-0.22.1.tar.gz", hash = "sha256:190f1331e783cf21eb60bca559354e0a4d4378facecf78f5428c39b675d20d28", size = 69746, upload-time = "2025-06-02T14:29:01.152Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/ae/ec06af4fe3ee72d16973474f122541746196aaa16cea6f66d18b963c6177/prometheus_client-0.22.1-py3-none-any.whl", hash = "sha256:cca895342e308174341b2cbf99a56bef291fbc0ef7b9e5412a0f26d653ba7094", size = 58694, upload-time = "2025-06-02T14:29:00.068Z" }, -] - -[[package]] -name = "prompt-toolkit" -version = "3.0.51" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/6e/9d084c929dfe9e3bfe0c6a47e31f78a25c54627d64a66e884a8bf5474f1c/prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed", size = 428940, upload-time = "2025-04-15T09:18:47.731Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", size = 387810, upload-time = "2025-04-15T09:18:44.753Z" }, -] - -[[package]] -name = "propcache" -version = "0.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139, upload-time = "2025-06-09T22:56:06.081Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/14/510deed325e262afeb8b360043c5d7c960da7d3ecd6d6f9496c9c56dc7f4/propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770", size = 73178, upload-time = "2025-06-09T22:53:40.126Z" }, - { url = "https://files.pythonhosted.org/packages/cd/4e/ad52a7925ff01c1325653a730c7ec3175a23f948f08626a534133427dcff/propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3", size = 43133, upload-time = "2025-06-09T22:53:41.965Z" }, - { url = "https://files.pythonhosted.org/packages/63/7c/e9399ba5da7780871db4eac178e9c2e204c23dd3e7d32df202092a1ed400/propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3", size = 43039, upload-time = "2025-06-09T22:53:43.268Z" }, - { url = "https://files.pythonhosted.org/packages/22/e1/58da211eb8fdc6fc854002387d38f415a6ca5f5c67c1315b204a5d3e9d7a/propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e", size = 201903, upload-time = "2025-06-09T22:53:44.872Z" }, - { url = "https://files.pythonhosted.org/packages/c4/0a/550ea0f52aac455cb90111c8bab995208443e46d925e51e2f6ebdf869525/propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220", size = 213362, upload-time = "2025-06-09T22:53:46.707Z" }, - { url = "https://files.pythonhosted.org/packages/5a/af/9893b7d878deda9bb69fcf54600b247fba7317761b7db11fede6e0f28bd0/propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb", size = 210525, upload-time = "2025-06-09T22:53:48.547Z" }, - { url = "https://files.pythonhosted.org/packages/7c/bb/38fd08b278ca85cde36d848091ad2b45954bc5f15cce494bb300b9285831/propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614", size = 198283, upload-time = "2025-06-09T22:53:50.067Z" }, - { url = "https://files.pythonhosted.org/packages/78/8c/9fe55bd01d362bafb413dfe508c48753111a1e269737fa143ba85693592c/propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50", size = 191872, upload-time = "2025-06-09T22:53:51.438Z" }, - { url = "https://files.pythonhosted.org/packages/54/14/4701c33852937a22584e08abb531d654c8bcf7948a8f87ad0a4822394147/propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339", size = 199452, upload-time = "2025-06-09T22:53:53.229Z" }, - { url = "https://files.pythonhosted.org/packages/16/44/447f2253d859602095356007657ee535e0093215ea0b3d1d6a41d16e5201/propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0", size = 191567, upload-time = "2025-06-09T22:53:54.541Z" }, - { url = "https://files.pythonhosted.org/packages/f2/b3/e4756258749bb2d3b46defcff606a2f47410bab82be5824a67e84015b267/propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2", size = 193015, upload-time = "2025-06-09T22:53:56.44Z" }, - { url = "https://files.pythonhosted.org/packages/1e/df/e6d3c7574233164b6330b9fd697beeac402afd367280e6dc377bb99b43d9/propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7", size = 204660, upload-time = "2025-06-09T22:53:57.839Z" }, - { url = "https://files.pythonhosted.org/packages/b2/53/e4d31dd5170b4a0e2e6b730f2385a96410633b4833dc25fe5dffd1f73294/propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b", size = 206105, upload-time = "2025-06-09T22:53:59.638Z" }, - { url = "https://files.pythonhosted.org/packages/7f/fe/74d54cf9fbe2a20ff786e5f7afcfde446588f0cf15fb2daacfbc267b866c/propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c", size = 196980, upload-time = "2025-06-09T22:54:01.071Z" }, - { url = "https://files.pythonhosted.org/packages/22/ec/c469c9d59dada8a7679625e0440b544fe72e99311a4679c279562051f6fc/propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70", size = 37679, upload-time = "2025-06-09T22:54:03.003Z" }, - { url = "https://files.pythonhosted.org/packages/38/35/07a471371ac89d418f8d0b699c75ea6dca2041fbda360823de21f6a9ce0a/propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9", size = 41459, upload-time = "2025-06-09T22:54:04.134Z" }, - { url = "https://files.pythonhosted.org/packages/80/8d/e8b436717ab9c2cfc23b116d2c297305aa4cd8339172a456d61ebf5669b8/propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be", size = 74207, upload-time = "2025-06-09T22:54:05.399Z" }, - { url = "https://files.pythonhosted.org/packages/d6/29/1e34000e9766d112171764b9fa3226fa0153ab565d0c242c70e9945318a7/propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f", size = 43648, upload-time = "2025-06-09T22:54:08.023Z" }, - { url = "https://files.pythonhosted.org/packages/46/92/1ad5af0df781e76988897da39b5f086c2bf0f028b7f9bd1f409bb05b6874/propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9", size = 43496, upload-time = "2025-06-09T22:54:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/ce/e96392460f9fb68461fabab3e095cb00c8ddf901205be4eae5ce246e5b7e/propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf", size = 217288, upload-time = "2025-06-09T22:54:10.466Z" }, - { url = "https://files.pythonhosted.org/packages/c5/2a/866726ea345299f7ceefc861a5e782b045545ae6940851930a6adaf1fca6/propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9", size = 227456, upload-time = "2025-06-09T22:54:11.828Z" }, - { url = "https://files.pythonhosted.org/packages/de/03/07d992ccb6d930398689187e1b3c718339a1c06b8b145a8d9650e4726166/propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66", size = 225429, upload-time = "2025-06-09T22:54:13.823Z" }, - { url = "https://files.pythonhosted.org/packages/5d/e6/116ba39448753b1330f48ab8ba927dcd6cf0baea8a0ccbc512dfb49ba670/propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df", size = 213472, upload-time = "2025-06-09T22:54:15.232Z" }, - { url = "https://files.pythonhosted.org/packages/a6/85/f01f5d97e54e428885a5497ccf7f54404cbb4f906688a1690cd51bf597dc/propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2", size = 204480, upload-time = "2025-06-09T22:54:17.104Z" }, - { url = "https://files.pythonhosted.org/packages/e3/79/7bf5ab9033b8b8194cc3f7cf1aaa0e9c3256320726f64a3e1f113a812dce/propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7", size = 214530, upload-time = "2025-06-09T22:54:18.512Z" }, - { url = "https://files.pythonhosted.org/packages/31/0b/bd3e0c00509b609317df4a18e6b05a450ef2d9a963e1d8bc9c9415d86f30/propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95", size = 205230, upload-time = "2025-06-09T22:54:19.947Z" }, - { url = "https://files.pythonhosted.org/packages/7a/23/fae0ff9b54b0de4e819bbe559508da132d5683c32d84d0dc2ccce3563ed4/propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e", size = 206754, upload-time = "2025-06-09T22:54:21.716Z" }, - { url = "https://files.pythonhosted.org/packages/b7/7f/ad6a3c22630aaa5f618b4dc3c3598974a72abb4c18e45a50b3cdd091eb2f/propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e", size = 218430, upload-time = "2025-06-09T22:54:23.17Z" }, - { url = "https://files.pythonhosted.org/packages/5b/2c/ba4f1c0e8a4b4c75910742f0d333759d441f65a1c7f34683b4a74c0ee015/propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf", size = 223884, upload-time = "2025-06-09T22:54:25.539Z" }, - { url = "https://files.pythonhosted.org/packages/88/e4/ebe30fc399e98572019eee82ad0caf512401661985cbd3da5e3140ffa1b0/propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e", size = 211480, upload-time = "2025-06-09T22:54:26.892Z" }, - { url = "https://files.pythonhosted.org/packages/96/0a/7d5260b914e01d1d0906f7f38af101f8d8ed0dc47426219eeaf05e8ea7c2/propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897", size = 37757, upload-time = "2025-06-09T22:54:28.241Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2d/89fe4489a884bc0da0c3278c552bd4ffe06a1ace559db5ef02ef24ab446b/propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39", size = 41500, upload-time = "2025-06-09T22:54:29.4Z" }, - { url = "https://files.pythonhosted.org/packages/a8/42/9ca01b0a6f48e81615dca4765a8f1dd2c057e0540f6116a27dc5ee01dfb6/propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10", size = 73674, upload-time = "2025-06-09T22:54:30.551Z" }, - { url = "https://files.pythonhosted.org/packages/af/6e/21293133beb550f9c901bbece755d582bfaf2176bee4774000bd4dd41884/propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154", size = 43570, upload-time = "2025-06-09T22:54:32.296Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c8/0393a0a3a2b8760eb3bde3c147f62b20044f0ddac81e9d6ed7318ec0d852/propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615", size = 43094, upload-time = "2025-06-09T22:54:33.929Z" }, - { url = "https://files.pythonhosted.org/packages/37/2c/489afe311a690399d04a3e03b069225670c1d489eb7b044a566511c1c498/propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db", size = 226958, upload-time = "2025-06-09T22:54:35.186Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ca/63b520d2f3d418c968bf596839ae26cf7f87bead026b6192d4da6a08c467/propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1", size = 234894, upload-time = "2025-06-09T22:54:36.708Z" }, - { url = "https://files.pythonhosted.org/packages/11/60/1d0ed6fff455a028d678df30cc28dcee7af77fa2b0e6962ce1df95c9a2a9/propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c", size = 233672, upload-time = "2025-06-09T22:54:38.062Z" }, - { url = "https://files.pythonhosted.org/packages/37/7c/54fd5301ef38505ab235d98827207176a5c9b2aa61939b10a460ca53e123/propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67", size = 224395, upload-time = "2025-06-09T22:54:39.634Z" }, - { url = "https://files.pythonhosted.org/packages/ee/1a/89a40e0846f5de05fdc6779883bf46ba980e6df4d2ff8fb02643de126592/propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b", size = 212510, upload-time = "2025-06-09T22:54:41.565Z" }, - { url = "https://files.pythonhosted.org/packages/5e/33/ca98368586c9566a6b8d5ef66e30484f8da84c0aac3f2d9aec6d31a11bd5/propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8", size = 222949, upload-time = "2025-06-09T22:54:43.038Z" }, - { url = "https://files.pythonhosted.org/packages/ba/11/ace870d0aafe443b33b2f0b7efdb872b7c3abd505bfb4890716ad7865e9d/propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251", size = 217258, upload-time = "2025-06-09T22:54:44.376Z" }, - { url = "https://files.pythonhosted.org/packages/5b/d2/86fd6f7adffcfc74b42c10a6b7db721d1d9ca1055c45d39a1a8f2a740a21/propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474", size = 213036, upload-time = "2025-06-09T22:54:46.243Z" }, - { url = "https://files.pythonhosted.org/packages/07/94/2d7d1e328f45ff34a0a284cf5a2847013701e24c2a53117e7c280a4316b3/propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535", size = 227684, upload-time = "2025-06-09T22:54:47.63Z" }, - { url = "https://files.pythonhosted.org/packages/b7/05/37ae63a0087677e90b1d14710e532ff104d44bc1efa3b3970fff99b891dc/propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06", size = 234562, upload-time = "2025-06-09T22:54:48.982Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7c/3f539fcae630408d0bd8bf3208b9a647ccad10976eda62402a80adf8fc34/propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1", size = 222142, upload-time = "2025-06-09T22:54:50.424Z" }, - { url = "https://files.pythonhosted.org/packages/7c/d2/34b9eac8c35f79f8a962546b3e97e9d4b990c420ee66ac8255d5d9611648/propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1", size = 37711, upload-time = "2025-06-09T22:54:52.072Z" }, - { url = "https://files.pythonhosted.org/packages/19/61/d582be5d226cf79071681d1b46b848d6cb03d7b70af7063e33a2787eaa03/propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c", size = 41479, upload-time = "2025-06-09T22:54:53.234Z" }, - { url = "https://files.pythonhosted.org/packages/dc/d1/8c747fafa558c603c4ca19d8e20b288aa0c7cda74e9402f50f31eb65267e/propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945", size = 71286, upload-time = "2025-06-09T22:54:54.369Z" }, - { url = "https://files.pythonhosted.org/packages/61/99/d606cb7986b60d89c36de8a85d58764323b3a5ff07770a99d8e993b3fa73/propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252", size = 42425, upload-time = "2025-06-09T22:54:55.642Z" }, - { url = "https://files.pythonhosted.org/packages/8c/96/ef98f91bbb42b79e9bb82bdd348b255eb9d65f14dbbe3b1594644c4073f7/propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f", size = 41846, upload-time = "2025-06-09T22:54:57.246Z" }, - { url = "https://files.pythonhosted.org/packages/5b/ad/3f0f9a705fb630d175146cd7b1d2bf5555c9beaed54e94132b21aac098a6/propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33", size = 208871, upload-time = "2025-06-09T22:54:58.975Z" }, - { url = "https://files.pythonhosted.org/packages/3a/38/2085cda93d2c8b6ec3e92af2c89489a36a5886b712a34ab25de9fbca7992/propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e", size = 215720, upload-time = "2025-06-09T22:55:00.471Z" }, - { url = "https://files.pythonhosted.org/packages/61/c1/d72ea2dc83ac7f2c8e182786ab0fc2c7bd123a1ff9b7975bee671866fe5f/propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1", size = 215203, upload-time = "2025-06-09T22:55:01.834Z" }, - { url = "https://files.pythonhosted.org/packages/af/81/b324c44ae60c56ef12007105f1460d5c304b0626ab0cc6b07c8f2a9aa0b8/propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3", size = 206365, upload-time = "2025-06-09T22:55:03.199Z" }, - { url = "https://files.pythonhosted.org/packages/09/73/88549128bb89e66d2aff242488f62869014ae092db63ccea53c1cc75a81d/propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1", size = 196016, upload-time = "2025-06-09T22:55:04.518Z" }, - { url = "https://files.pythonhosted.org/packages/b9/3f/3bdd14e737d145114a5eb83cb172903afba7242f67c5877f9909a20d948d/propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6", size = 205596, upload-time = "2025-06-09T22:55:05.942Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ca/2f4aa819c357d3107c3763d7ef42c03980f9ed5c48c82e01e25945d437c1/propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387", size = 200977, upload-time = "2025-06-09T22:55:07.792Z" }, - { url = "https://files.pythonhosted.org/packages/cd/4a/e65276c7477533c59085251ae88505caf6831c0e85ff8b2e31ebcbb949b1/propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4", size = 197220, upload-time = "2025-06-09T22:55:09.173Z" }, - { url = "https://files.pythonhosted.org/packages/7c/54/fc7152e517cf5578278b242396ce4d4b36795423988ef39bb8cd5bf274c8/propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88", size = 210642, upload-time = "2025-06-09T22:55:10.62Z" }, - { url = "https://files.pythonhosted.org/packages/b9/80/abeb4a896d2767bf5f1ea7b92eb7be6a5330645bd7fb844049c0e4045d9d/propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206", size = 212789, upload-time = "2025-06-09T22:55:12.029Z" }, - { url = "https://files.pythonhosted.org/packages/b3/db/ea12a49aa7b2b6d68a5da8293dcf50068d48d088100ac016ad92a6a780e6/propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43", size = 205880, upload-time = "2025-06-09T22:55:13.45Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e5/9076a0bbbfb65d1198007059c65639dfd56266cf8e477a9707e4b1999ff4/propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02", size = 37220, upload-time = "2025-06-09T22:55:15.284Z" }, - { url = "https://files.pythonhosted.org/packages/d3/f5/b369e026b09a26cd77aa88d8fffd69141d2ae00a2abaaf5380d2603f4b7f/propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05", size = 40678, upload-time = "2025-06-09T22:55:16.445Z" }, - { url = "https://files.pythonhosted.org/packages/a4/3a/6ece377b55544941a08d03581c7bc400a3c8cd3c2865900a68d5de79e21f/propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b", size = 76560, upload-time = "2025-06-09T22:55:17.598Z" }, - { url = "https://files.pythonhosted.org/packages/0c/da/64a2bb16418740fa634b0e9c3d29edff1db07f56d3546ca2d86ddf0305e1/propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0", size = 44676, upload-time = "2025-06-09T22:55:18.922Z" }, - { url = "https://files.pythonhosted.org/packages/36/7b/f025e06ea51cb72c52fb87e9b395cced02786610b60a3ed51da8af017170/propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e", size = 44701, upload-time = "2025-06-09T22:55:20.106Z" }, - { url = "https://files.pythonhosted.org/packages/a4/00/faa1b1b7c3b74fc277f8642f32a4c72ba1d7b2de36d7cdfb676db7f4303e/propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28", size = 276934, upload-time = "2025-06-09T22:55:21.5Z" }, - { url = "https://files.pythonhosted.org/packages/74/ab/935beb6f1756e0476a4d5938ff44bf0d13a055fed880caf93859b4f1baf4/propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a", size = 278316, upload-time = "2025-06-09T22:55:22.918Z" }, - { url = "https://files.pythonhosted.org/packages/f8/9d/994a5c1ce4389610838d1caec74bdf0e98b306c70314d46dbe4fcf21a3e2/propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c", size = 282619, upload-time = "2025-06-09T22:55:24.651Z" }, - { url = "https://files.pythonhosted.org/packages/2b/00/a10afce3d1ed0287cef2e09506d3be9822513f2c1e96457ee369adb9a6cd/propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725", size = 265896, upload-time = "2025-06-09T22:55:26.049Z" }, - { url = "https://files.pythonhosted.org/packages/2e/a8/2aa6716ffa566ca57c749edb909ad27884680887d68517e4be41b02299f3/propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892", size = 252111, upload-time = "2025-06-09T22:55:27.381Z" }, - { url = "https://files.pythonhosted.org/packages/36/4f/345ca9183b85ac29c8694b0941f7484bf419c7f0fea2d1e386b4f7893eed/propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44", size = 268334, upload-time = "2025-06-09T22:55:28.747Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ca/fcd54f78b59e3f97b3b9715501e3147f5340167733d27db423aa321e7148/propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe", size = 255026, upload-time = "2025-06-09T22:55:30.184Z" }, - { url = "https://files.pythonhosted.org/packages/8b/95/8e6a6bbbd78ac89c30c225210a5c687790e532ba4088afb8c0445b77ef37/propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81", size = 250724, upload-time = "2025-06-09T22:55:31.646Z" }, - { url = "https://files.pythonhosted.org/packages/ee/b0/0dd03616142baba28e8b2d14ce5df6631b4673850a3d4f9c0f9dd714a404/propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba", size = 268868, upload-time = "2025-06-09T22:55:33.209Z" }, - { url = "https://files.pythonhosted.org/packages/c5/98/2c12407a7e4fbacd94ddd32f3b1e3d5231e77c30ef7162b12a60e2dd5ce3/propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770", size = 271322, upload-time = "2025-06-09T22:55:35.065Z" }, - { url = "https://files.pythonhosted.org/packages/35/91/9cb56efbb428b006bb85db28591e40b7736847b8331d43fe335acf95f6c8/propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330", size = 265778, upload-time = "2025-06-09T22:55:36.45Z" }, - { url = "https://files.pythonhosted.org/packages/9a/4c/b0fe775a2bdd01e176b14b574be679d84fc83958335790f7c9a686c1f468/propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394", size = 41175, upload-time = "2025-06-09T22:55:38.436Z" }, - { url = "https://files.pythonhosted.org/packages/a4/ff/47f08595e3d9b5e149c150f88d9714574f1a7cbd89fe2817158a952674bf/propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198", size = 44857, upload-time = "2025-06-09T22:55:39.687Z" }, - { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" }, -] - -[[package]] -name = "psutil" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload-time = "2025-02-13T21:54:07.946Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload-time = "2025-02-13T21:54:12.36Z" }, - { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload-time = "2025-02-13T21:54:16.07Z" }, - { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload-time = "2025-02-13T21:54:18.662Z" }, - { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload-time = "2025-02-13T21:54:21.811Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload-time = "2025-02-13T21:54:24.68Z" }, - { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload-time = "2025-02-13T21:54:34.31Z" }, - { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload-time = "2025-02-13T21:54:37.486Z" }, -] - -[[package]] -name = "ptyprocess" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, -] - -[[package]] -name = "pure-eval" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, -] - -[[package]] -name = "pycparser" -version = "2.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, -] - -[[package]] -name = "pydantic" -version = "2.11.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-types" }, - { name = "pydantic-core" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, -] - -[[package]] -name = "pydantic-core" -version = "2.33.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" }, - { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" }, - { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" }, - { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" }, - { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" }, - { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" }, - { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" }, - { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" }, - { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" }, - { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" }, - { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" }, - { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" }, - { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, - { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, - { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, - { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, - { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, - { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, - { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, - { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, - { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, - { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, - { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, - { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, - { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, - { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, - { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, - { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, - { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, - { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, - { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, - { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, - { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, - { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, - { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, - { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, - { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, - { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, - { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, - { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, - { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, - { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, - { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, - { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, - { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, - { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, - { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, - { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, - { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, - { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" }, - { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" }, - { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" }, - { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" }, - { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" }, - { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" }, - { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" }, - { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, - { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, - { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, - { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, - { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, - { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, - { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, -] - -[[package]] -name = "pydata-sphinx-theme" -version = "0.16.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "accessible-pygments" }, - { name = "babel" }, - { name = "beautifulsoup4" }, - { name = "docutils" }, - { name = "pygments" }, - { name = "sphinx" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/00/20/bb50f9de3a6de69e6abd6b087b52fa2418a0418b19597601605f855ad044/pydata_sphinx_theme-0.16.1.tar.gz", hash = "sha256:a08b7f0b7f70387219dc659bff0893a7554d5eb39b59d3b8ef37b8401b7642d7", size = 2412693, upload-time = "2024-12-17T10:53:39.537Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/0d/8ba33fa83a7dcde13eb3c1c2a0c1cc29950a048bfed6d9b0d8b6bd710b4c/pydata_sphinx_theme-0.16.1-py3-none-any.whl", hash = "sha256:225331e8ac4b32682c18fcac5a57a6f717c4e632cea5dd0e247b55155faeccde", size = 6723264, upload-time = "2024-12-17T10:53:35.645Z" }, -] - -[[package]] -name = "pygments" -version = "2.19.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, -] - -[[package]] -name = "pyjwt" -version = "2.10.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pympler" -version = "1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pywin32", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dd/37/c384631908029676d8e7213dd956bb686af303a80db7afbc9be36bc49495/pympler-1.1.tar.gz", hash = "sha256:1eaa867cb8992c218430f1708fdaccda53df064144d1c5656b1e6f1ee6000424", size = 179954, upload-time = "2024-06-28T19:56:06.563Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/4f/a6a2e2b202d7fd97eadfe90979845b8706676b41cbd3b42ba75adf329d1f/Pympler-1.1-py3-none-any.whl", hash = "sha256:5b223d6027d0619584116a0cbc28e8d2e378f7a79c1e5e024f9ff3b673c58506", size = 165766, upload-time = "2024-06-28T19:56:05.087Z" }, -] - -[[package]] -name = "pyparsing" -version = "3.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload-time = "2025-03-25T05:01:28.114Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload-time = "2025-03-25T05:01:24.908Z" }, -] - -[[package]] -name = "pyproj" -version = "3.7.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11' and platform_python_implementation == 'PyPy'", - "python_full_version < '3.11' and platform_python_implementation != 'PyPy'", -] -dependencies = [ - { name = "certifi", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/67/10/a8480ea27ea4bbe896c168808854d00f2a9b49f95c0319ddcbba693c8a90/pyproj-3.7.1.tar.gz", hash = "sha256:60d72facd7b6b79853f19744779abcd3f804c4e0d4fa8815469db20c9f640a47", size = 226339, upload-time = "2025-02-16T04:28:46.621Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/a3/c4cd4bba5b336075f145fe784fcaf4ef56ffbc979833303303e7a659dda2/pyproj-3.7.1-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:bf09dbeb333c34e9c546364e7df1ff40474f9fddf9e70657ecb0e4f670ff0b0e", size = 6262524, upload-time = "2025-02-16T04:27:19.725Z" }, - { url = "https://files.pythonhosted.org/packages/40/45/4fdf18f4cc1995f1992771d2a51cf186a9d7a8ec973c9693f8453850c707/pyproj-3.7.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:6575b2e53cc9e3e461ad6f0692a5564b96e7782c28631c7771c668770915e169", size = 4665102, upload-time = "2025-02-16T04:27:24.428Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d2/360eb127380106cee83569954ae696b88a891c804d7a93abe3fbc15f5976/pyproj-3.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8cb516ee35ed57789b46b96080edf4e503fdb62dbb2e3c6581e0d6c83fca014b", size = 9432667, upload-time = "2025-02-16T04:27:27.04Z" }, - { url = "https://files.pythonhosted.org/packages/76/a5/c6e11b9a99ce146741fb4d184d5c468446c6d6015b183cae82ac822a6cfa/pyproj-3.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e47c4e93b88d99dd118875ee3ca0171932444cdc0b52d493371b5d98d0f30ee", size = 9259185, upload-time = "2025-02-16T04:27:30.35Z" }, - { url = "https://files.pythonhosted.org/packages/41/56/a3c15c42145797a99363fa0fdb4e9805dccb8b4a76a6d7b2cdf36ebcc2a1/pyproj-3.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3e8d276caeae34fcbe4813855d0d97b9b825bab8d7a8b86d859c24a6213a5a0d", size = 10469103, upload-time = "2025-02-16T04:27:33.542Z" }, - { url = "https://files.pythonhosted.org/packages/ef/73/c9194c2802fefe2a4fd4230bdd5ab083e7604e93c64d0356fa49c363bad6/pyproj-3.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f173f851ee75e54acdaa053382b6825b400cb2085663a9bb073728a59c60aebb", size = 10401391, upload-time = "2025-02-16T04:27:36.051Z" }, - { url = "https://files.pythonhosted.org/packages/c5/1d/ce8bb5b9251b04d7c22d63619bb3db3d2397f79000a9ae05b3fd86a5837e/pyproj-3.7.1-cp310-cp310-win32.whl", hash = "sha256:f550281ed6e5ea88fcf04a7c6154e246d5714be495c50c9e8e6b12d3fb63e158", size = 5869997, upload-time = "2025-02-16T04:27:38.302Z" }, - { url = "https://files.pythonhosted.org/packages/09/6a/ca145467fd2e5b21e3d5b8c2b9645dcfb3b68f08b62417699a1f5689008e/pyproj-3.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:3537668992a709a2e7f068069192138618c00d0ba113572fdd5ee5ffde8222f3", size = 6278581, upload-time = "2025-02-16T04:27:41.051Z" }, - { url = "https://files.pythonhosted.org/packages/ab/0d/63670fc527e664068b70b7cab599aa38b7420dd009bdc29ea257e7f3dfb3/pyproj-3.7.1-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:a94e26c1a4950cea40116775588a2ca7cf56f1f434ff54ee35a84718f3841a3d", size = 6264315, upload-time = "2025-02-16T04:27:44.539Z" }, - { url = "https://files.pythonhosted.org/packages/25/9d/cbaf82cfb290d1f1fa42feb9ba9464013bb3891e40c4199f8072112e4589/pyproj-3.7.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:263b54ba5004b6b957d55757d846fc5081bc02980caa0279c4fc95fa0fff6067", size = 4666267, upload-time = "2025-02-16T04:27:47.019Z" }, - { url = "https://files.pythonhosted.org/packages/79/53/24f9f9b8918c0550f3ff49ad5de4cf3f0688c9f91ff191476db8979146fe/pyproj-3.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6d6a2ccd5607cd15ef990c51e6f2dd27ec0a741e72069c387088bba3aab60fa", size = 9680510, upload-time = "2025-02-16T04:27:49.239Z" }, - { url = "https://files.pythonhosted.org/packages/3c/ac/12fab74a908d40b63174dc704587febd0729414804bbfd873cabe504ff2d/pyproj-3.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c5dcf24ede53d8abab7d8a77f69ff1936c6a8843ef4fcc574646e4be66e5739", size = 9493619, upload-time = "2025-02-16T04:27:52.65Z" }, - { url = "https://files.pythonhosted.org/packages/c4/45/26311d6437135da2153a178125db5dfb6abce831ce04d10ec207eabac70a/pyproj-3.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c2e7449840a44ce860d8bea2c6c1c4bc63fa07cba801dcce581d14dcb031a02", size = 10709755, upload-time = "2025-02-16T04:27:55.239Z" }, - { url = "https://files.pythonhosted.org/packages/99/52/4ecd0986f27d0e6c8ee3a7bc5c63da15acd30ac23034f871325b297e61fd/pyproj-3.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0829865c1d3a3543f918b3919dc601eea572d6091c0dd175e1a054db9c109274", size = 10642970, upload-time = "2025-02-16T04:27:58.343Z" }, - { url = "https://files.pythonhosted.org/packages/3f/a5/d3bfc018fc92195a000d1d28acc1f3f1df15ff9f09ece68f45a2636c0134/pyproj-3.7.1-cp311-cp311-win32.whl", hash = "sha256:6181960b4b812e82e588407fe5c9c68ada267c3b084db078f248db5d7f45d18a", size = 5868295, upload-time = "2025-02-16T04:28:01.712Z" }, - { url = "https://files.pythonhosted.org/packages/92/39/ef6f06a5b223dbea308cfcbb7a0f72e7b506aef1850e061b2c73b0818715/pyproj-3.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ad0ff443a785d84e2b380869fdd82e6bfc11eba6057d25b4409a9bbfa867970", size = 6279871, upload-time = "2025-02-16T04:28:04.988Z" }, - { url = "https://files.pythonhosted.org/packages/e6/c9/876d4345b8d17f37ac59ebd39f8fa52fc6a6a9891a420f72d050edb6b899/pyproj-3.7.1-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:2781029d90df7f8d431e29562a3f2d8eafdf233c4010d6fc0381858dc7373217", size = 6264087, upload-time = "2025-02-16T04:28:09.036Z" }, - { url = "https://files.pythonhosted.org/packages/ff/e6/5f8691f8c90e7f402cc80a6276eb19d2ec1faa150d5ae2dd9c7b0a254da8/pyproj-3.7.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:d61bf8ab04c73c1da08eedaf21a103b72fa5b0a9b854762905f65ff8b375d394", size = 4669628, upload-time = "2025-02-16T04:28:10.944Z" }, - { url = "https://files.pythonhosted.org/packages/42/ec/16475bbb79c1c68845c0a0d9c60c4fb31e61b8a2a20bc18b1a81e81c7f68/pyproj-3.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04abc517a8555d1b05fcee768db3280143fe42ec39fdd926a2feef31631a1f2f", size = 9721415, upload-time = "2025-02-16T04:28:13.342Z" }, - { url = "https://files.pythonhosted.org/packages/b3/a3/448f05b15e318bd6bea9a32cfaf11e886c4ae61fa3eee6e09ed5c3b74bb2/pyproj-3.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084c0a475688f934d386c2ab3b6ce03398a473cd48adfda70d9ab8f87f2394a0", size = 9556447, upload-time = "2025-02-16T04:28:15.818Z" }, - { url = "https://files.pythonhosted.org/packages/6a/ae/bd15fe8d8bd914ead6d60bca7f895a4e6f8ef7e3928295134ff9a7dad14c/pyproj-3.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a20727a23b1e49c7dc7fe3c3df8e56a8a7acdade80ac2f5cca29d7ca5564c145", size = 10758317, upload-time = "2025-02-16T04:28:18.338Z" }, - { url = "https://files.pythonhosted.org/packages/9d/d9/5ccefb8bca925f44256b188a91c31238cae29ab6ee7f53661ecc04616146/pyproj-3.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bf84d766646f1ebd706d883755df4370aaf02b48187cedaa7e4239f16bc8213d", size = 10771259, upload-time = "2025-02-16T04:28:20.822Z" }, - { url = "https://files.pythonhosted.org/packages/2a/7d/31dedff9c35fa703162f922eeb0baa6c44a3288469a5fd88d209e2892f9e/pyproj-3.7.1-cp312-cp312-win32.whl", hash = "sha256:5f0da2711364d7cb9f115b52289d4a9b61e8bca0da57f44a3a9d6fc9bdeb7274", size = 5859914, upload-time = "2025-02-16T04:28:23.303Z" }, - { url = "https://files.pythonhosted.org/packages/3e/47/c6ab03d6564a7c937590cff81a2742b5990f096cce7c1a622d325be340ee/pyproj-3.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:aee664a9d806612af30a19dba49e55a7a78ebfec3e9d198f6a6176e1d140ec98", size = 6273196, upload-time = "2025-02-16T04:28:25.227Z" }, - { url = "https://files.pythonhosted.org/packages/ef/01/984828464c9960036c602753fc0f21f24f0aa9043c18fa3f2f2b66a86340/pyproj-3.7.1-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:5f8d02ef4431dee414d1753d13fa82a21a2f61494737b5f642ea668d76164d6d", size = 6253062, upload-time = "2025-02-16T04:28:27.861Z" }, - { url = "https://files.pythonhosted.org/packages/68/65/6ecdcdc829811a2c160cdfe2f068a009fc572fd4349664f758ccb0853a7c/pyproj-3.7.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:0b853ae99bda66cbe24b4ccfe26d70601d84375940a47f553413d9df570065e0", size = 4660548, upload-time = "2025-02-16T04:28:29.526Z" }, - { url = "https://files.pythonhosted.org/packages/67/da/dda94c4490803679230ba4c17a12f151b307a0d58e8110820405ca2d98db/pyproj-3.7.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83db380c52087f9e9bdd8a527943b2e7324f275881125e39475c4f9277bdeec4", size = 9662464, upload-time = "2025-02-16T04:28:31.437Z" }, - { url = "https://files.pythonhosted.org/packages/6f/57/f61b7d22c91ae1d12ee00ac4c0038714e774ebcd851b9133e5f4f930dd40/pyproj-3.7.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b35ed213892e211a3ce2bea002aa1183e1a2a9b79e51bb3c6b15549a831ae528", size = 9497461, upload-time = "2025-02-16T04:28:33.848Z" }, - { url = "https://files.pythonhosted.org/packages/b7/f6/932128236f79d2ac7d39fe1a19667fdf7155d9a81d31fb9472a7a497790f/pyproj-3.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a8b15b0463d1303bab113d1a6af2860a0d79013c3a66fcc5475ce26ef717fd4f", size = 10708869, upload-time = "2025-02-16T04:28:37.34Z" }, - { url = "https://files.pythonhosted.org/packages/1d/0d/07ac7712994454a254c383c0d08aff9916a2851e6512d59da8dc369b1b02/pyproj-3.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:87229e42b75e89f4dad6459200f92988c5998dfb093c7c631fb48524c86cd5dc", size = 10729260, upload-time = "2025-02-16T04:28:40.639Z" }, - { url = "https://files.pythonhosted.org/packages/b0/d0/9c604bc72c37ba69b867b6df724d6a5af6789e8c375022c952f65b2af558/pyproj-3.7.1-cp313-cp313-win32.whl", hash = "sha256:d666c3a3faaf3b1d7fc4a544059c4eab9d06f84a604b070b7aa2f318e227798e", size = 5855462, upload-time = "2025-02-16T04:28:42.827Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/68a2b7f5fb6400c64aad82d72bcc4bc531775e62eedff993a77c780defd0/pyproj-3.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:d3caac7473be22b6d6e102dde6c46de73b96bc98334e577dfaee9886f102ea2e", size = 6266573, upload-time = "2025-02-16T04:28:44.727Z" }, -] - -[[package]] -name = "pyproj" -version = "3.7.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12' and platform_python_implementation == 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", - "python_full_version >= '3.12' and platform_python_implementation != 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", -] -dependencies = [ - { name = "certifi", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/04/90/67bd7260b4ea9b8b20b4f58afef6c223ecb3abf368eb4ec5bc2cdef81b49/pyproj-3.7.2.tar.gz", hash = "sha256:39a0cf1ecc7e282d1d30f36594ebd55c9fae1fda8a2622cee5d100430628f88c", size = 226279, upload-time = "2025-08-14T12:05:42.18Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/bd/f205552cd1713b08f93b09e39a3ec99edef0b3ebbbca67b486fdf1abe2de/pyproj-3.7.2-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:2514d61f24c4e0bb9913e2c51487ecdaeca5f8748d8313c933693416ca41d4d5", size = 6227022, upload-time = "2025-08-14T12:03:51.474Z" }, - { url = "https://files.pythonhosted.org/packages/75/4c/9a937e659b8b418ab573c6d340d27e68716928953273e0837e7922fcac34/pyproj-3.7.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:8693ca3892d82e70de077701ee76dd13d7bca4ae1c9d1e739d72004df015923a", size = 4625810, upload-time = "2025-08-14T12:03:53.808Z" }, - { url = "https://files.pythonhosted.org/packages/c0/7d/a9f41e814dc4d1dc54e95b2ccaf0b3ebe3eb18b1740df05fe334724c3d89/pyproj-3.7.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5e26484d80fea56273ed1555abaea161e9661d81a6c07815d54b8e883d4ceb25", size = 9638694, upload-time = "2025-08-14T12:03:55.669Z" }, - { url = "https://files.pythonhosted.org/packages/ad/ab/9bdb4a6216b712a1f9aab1c0fcbee5d3726f34a366f29c3e8c08a78d6b70/pyproj-3.7.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:281cb92847814e8018010c48b4069ff858a30236638631c1a91dd7bfa68f8a8a", size = 9493977, upload-time = "2025-08-14T12:03:57.937Z" }, - { url = "https://files.pythonhosted.org/packages/c9/db/2db75b1b6190f1137b1c4e8ef6a22e1c338e46320f6329bfac819143e063/pyproj-3.7.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9c8577f0b7bb09118ec2e57e3babdc977127dd66326d6c5d755c76b063e6d9dc", size = 10841151, upload-time = "2025-08-14T12:04:00.271Z" }, - { url = "https://files.pythonhosted.org/packages/89/f7/989643394ba23a286e9b7b3f09981496172f9e0d4512457ffea7dc47ffc7/pyproj-3.7.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a23f59904fac3a5e7364b3aa44d288234af267ca041adb2c2b14a903cd5d3ac5", size = 10751585, upload-time = "2025-08-14T12:04:02.228Z" }, - { url = "https://files.pythonhosted.org/packages/53/6d/ad928fe975a6c14a093c92e6a319ca18f479f3336bb353a740bdba335681/pyproj-3.7.2-cp311-cp311-win32.whl", hash = "sha256:f2af4ed34b2cf3e031a2d85b067a3ecbd38df073c567e04b52fa7a0202afde8a", size = 5908533, upload-time = "2025-08-14T12:04:04.821Z" }, - { url = "https://files.pythonhosted.org/packages/79/e0/b95584605cec9ed50b7ebaf7975d1c4ddeec5a86b7a20554ed8b60042bd7/pyproj-3.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:0b7cb633565129677b2a183c4d807c727d1c736fcb0568a12299383056e67433", size = 6320742, upload-time = "2025-08-14T12:04:06.357Z" }, - { url = "https://files.pythonhosted.org/packages/b7/4d/536e8f93bca808175c2d0a5ac9fdf69b960d8ab6b14f25030dccb07464d7/pyproj-3.7.2-cp311-cp311-win_arm64.whl", hash = "sha256:38b08d85e3a38e455625b80e9eb9f78027c8e2649a21dec4df1f9c3525460c71", size = 6245772, upload-time = "2025-08-14T12:04:08.365Z" }, - { url = "https://files.pythonhosted.org/packages/8d/ab/9893ea9fb066be70ed9074ae543914a618c131ed8dff2da1e08b3a4df4db/pyproj-3.7.2-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:0a9bb26a6356fb5b033433a6d1b4542158fb71e3c51de49b4c318a1dff3aeaab", size = 6219832, upload-time = "2025-08-14T12:04:10.264Z" }, - { url = "https://files.pythonhosted.org/packages/53/78/4c64199146eed7184eb0e85bedec60a4aa8853b6ffe1ab1f3a8b962e70a0/pyproj-3.7.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:567caa03021178861fad27fabde87500ec6d2ee173dd32f3e2d9871e40eebd68", size = 4620650, upload-time = "2025-08-14T12:04:11.978Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ac/14a78d17943898a93ef4f8c6a9d4169911c994e3161e54a7cedeba9d8dde/pyproj-3.7.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c203101d1dc3c038a56cff0447acc515dd29d6e14811406ac539c21eed422b2a", size = 9667087, upload-time = "2025-08-14T12:04:13.964Z" }, - { url = "https://files.pythonhosted.org/packages/b8/be/212882c450bba74fc8d7d35cbd57e4af84792f0a56194819d98106b075af/pyproj-3.7.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:1edc34266c0c23ced85f95a1ee8b47c9035eae6aca5b6b340327250e8e281630", size = 9552797, upload-time = "2025-08-14T12:04:16.624Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c0/c0f25c87b5d2a8686341c53c1792a222a480d6c9caf60311fec12c99ec26/pyproj-3.7.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa9f26c21bc0e2dc3d224cb1eb4020cf23e76af179a7c66fea49b828611e4260", size = 10837036, upload-time = "2025-08-14T12:04:18.733Z" }, - { url = "https://files.pythonhosted.org/packages/5d/37/5cbd6772addde2090c91113332623a86e8c7d583eccb2ad02ea634c4a89f/pyproj-3.7.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9428b318530625cb389b9ddc9c51251e172808a4af79b82809376daaeabe5e9", size = 10775952, upload-time = "2025-08-14T12:04:20.709Z" }, - { url = "https://files.pythonhosted.org/packages/69/a1/dc250e3cf83eb4b3b9a2cf86fdb5e25288bd40037ae449695550f9e96b2f/pyproj-3.7.2-cp312-cp312-win32.whl", hash = "sha256:b3d99ed57d319da042f175f4554fc7038aa4bcecc4ac89e217e350346b742c9d", size = 5898872, upload-time = "2025-08-14T12:04:22.485Z" }, - { url = "https://files.pythonhosted.org/packages/4a/a6/6fe724b72b70f2b00152d77282e14964d60ab092ec225e67c196c9b463e5/pyproj-3.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:11614a054cd86a2ed968a657d00987a86eeb91fdcbd9ad3310478685dc14a128", size = 6312176, upload-time = "2025-08-14T12:04:24.736Z" }, - { url = "https://files.pythonhosted.org/packages/5d/68/915cc32c02a91e76d02c8f55d5a138d6ef9e47a0d96d259df98f4842e558/pyproj-3.7.2-cp312-cp312-win_arm64.whl", hash = "sha256:509a146d1398bafe4f53273398c3bb0b4732535065fa995270e52a9d3676bca3", size = 6233452, upload-time = "2025-08-14T12:04:27.287Z" }, - { url = "https://files.pythonhosted.org/packages/be/14/faf1b90d267cea68d7e70662e7f88cefdb1bc890bd596c74b959e0517a72/pyproj-3.7.2-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:19466e529b1b15eeefdf8ff26b06fa745856c044f2f77bf0edbae94078c1dfa1", size = 6214580, upload-time = "2025-08-14T12:04:28.804Z" }, - { url = "https://files.pythonhosted.org/packages/35/48/da9a45b184d375f62667f62eba0ca68569b0bd980a0bb7ffcc1d50440520/pyproj-3.7.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:c79b9b84c4a626c5dc324c0d666be0bfcebd99f7538d66e8898c2444221b3da7", size = 4615388, upload-time = "2025-08-14T12:04:30.553Z" }, - { url = "https://files.pythonhosted.org/packages/5e/e7/d2b459a4a64bca328b712c1b544e109df88e5c800f7c143cfbc404d39bfb/pyproj-3.7.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:ceecf374cacca317bc09e165db38ac548ee3cad07c3609442bd70311c59c21aa", size = 9628455, upload-time = "2025-08-14T12:04:32.435Z" }, - { url = "https://files.pythonhosted.org/packages/f8/85/c2b1706e51942de19076eff082f8495e57d5151364e78b5bef4af4a1d94a/pyproj-3.7.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5141a538ffdbe4bfd157421828bb2e07123a90a7a2d6f30fa1462abcfb5ce681", size = 9514269, upload-time = "2025-08-14T12:04:34.599Z" }, - { url = "https://files.pythonhosted.org/packages/34/38/07a9b89ae7467872f9a476883a5bad9e4f4d1219d31060f0f2b282276cbe/pyproj-3.7.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f000841e98ea99acbb7b8ca168d67773b0191de95187228a16110245c5d954d5", size = 10808437, upload-time = "2025-08-14T12:04:36.485Z" }, - { url = "https://files.pythonhosted.org/packages/12/56/fda1daeabbd39dec5b07f67233d09f31facb762587b498e6fc4572be9837/pyproj-3.7.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8115faf2597f281a42ab608ceac346b4eb1383d3b45ab474fd37341c4bf82a67", size = 10745540, upload-time = "2025-08-14T12:04:38.568Z" }, - { url = "https://files.pythonhosted.org/packages/0d/90/c793182cbba65a39a11db2ac6b479fe76c59e6509ae75e5744c344a0da9d/pyproj-3.7.2-cp313-cp313-win32.whl", hash = "sha256:f18c0579dd6be00b970cb1a6719197fceecc407515bab37da0066f0184aafdf3", size = 5896506, upload-time = "2025-08-14T12:04:41.059Z" }, - { url = "https://files.pythonhosted.org/packages/be/0f/747974129cf0d800906f81cd25efd098c96509026e454d4b66868779ab04/pyproj-3.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:bb41c29d5f60854b1075853fe80c58950b398d4ebb404eb532536ac8d2834ed7", size = 6310195, upload-time = "2025-08-14T12:04:42.974Z" }, - { url = "https://files.pythonhosted.org/packages/82/64/fc7598a53172c4931ec6edf5228280663063150625d3f6423b4c20f9daff/pyproj-3.7.2-cp313-cp313-win_arm64.whl", hash = "sha256:2b617d573be4118c11cd96b8891a0b7f65778fa7733ed8ecdb297a447d439100", size = 6230748, upload-time = "2025-08-14T12:04:44.491Z" }, - { url = "https://files.pythonhosted.org/packages/aa/f0/611dd5cddb0d277f94b7af12981f56e1441bf8d22695065d4f0df5218498/pyproj-3.7.2-cp313-cp313t-macosx_13_0_x86_64.whl", hash = "sha256:d27b48f0e81beeaa2b4d60c516c3a1cfbb0c7ff6ef71256d8e9c07792f735279", size = 6241729, upload-time = "2025-08-14T12:04:46.274Z" }, - { url = "https://files.pythonhosted.org/packages/15/93/40bd4a6c523ff9965e480870611aed7eda5aa2c6128c6537345a2b77b542/pyproj-3.7.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:55a3610d75023c7b1c6e583e48ef8f62918e85a2ae81300569d9f104d6684bb6", size = 4652497, upload-time = "2025-08-14T12:04:48.203Z" }, - { url = "https://files.pythonhosted.org/packages/1b/ae/7150ead53c117880b35e0d37960d3138fe640a235feb9605cb9386f50bb0/pyproj-3.7.2-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8d7349182fa622696787cc9e195508d2a41a64765da9b8a6bee846702b9e6220", size = 9942610, upload-time = "2025-08-14T12:04:49.652Z" }, - { url = "https://files.pythonhosted.org/packages/d8/17/7a4a7eafecf2b46ab64e5c08176c20ceb5844b503eaa551bf12ccac77322/pyproj-3.7.2-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:d230b186eb876ed4f29a7c5ee310144c3a0e44e89e55f65fb3607e13f6db337c", size = 9692390, upload-time = "2025-08-14T12:04:51.731Z" }, - { url = "https://files.pythonhosted.org/packages/c3/55/ae18f040f6410f0ea547a21ada7ef3e26e6c82befa125b303b02759c0e9d/pyproj-3.7.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:237499c7862c578d0369e2b8ac56eec550e391a025ff70e2af8417139dabb41c", size = 11047596, upload-time = "2025-08-14T12:04:53.748Z" }, - { url = "https://files.pythonhosted.org/packages/e6/2e/d3fff4d2909473f26ae799f9dda04caa322c417a51ff3b25763f7d03b233/pyproj-3.7.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8c225f5978abd506fd9a78eaaf794435e823c9156091cabaab5374efb29d7f69", size = 10896975, upload-time = "2025-08-14T12:04:55.875Z" }, - { url = "https://files.pythonhosted.org/packages/f2/bc/8fc7d3963d87057b7b51ebe68c1e7c51c23129eee5072ba6b86558544a46/pyproj-3.7.2-cp313-cp313t-win32.whl", hash = "sha256:2da731876d27639ff9d2d81c151f6ab90a1546455fabd93368e753047be344a2", size = 5953057, upload-time = "2025-08-14T12:04:58.466Z" }, - { url = "https://files.pythonhosted.org/packages/cc/27/ea9809966cc47d2d51e6d5ae631ea895f7c7c7b9b3c29718f900a8f7d197/pyproj-3.7.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f54d91ae18dd23b6c0ab48126d446820e725419da10617d86a1b69ada6d881d3", size = 6375414, upload-time = "2025-08-14T12:04:59.861Z" }, - { url = "https://files.pythonhosted.org/packages/5b/f8/1ef0129fba9a555c658e22af68989f35e7ba7b9136f25758809efec0cd6e/pyproj-3.7.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fc52ba896cfc3214dc9f9ca3c0677a623e8fdd096b257c14a31e719d21ff3fdd", size = 6262501, upload-time = "2025-08-14T12:05:01.39Z" }, - { url = "https://files.pythonhosted.org/packages/42/17/c2b050d3f5b71b6edd0d96ae16c990fdc42a5f1366464a5c2772146de33a/pyproj-3.7.2-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:2aaa328605ace41db050d06bac1adc11f01b71fe95c18661497763116c3a0f02", size = 6214541, upload-time = "2025-08-14T12:05:03.166Z" }, - { url = "https://files.pythonhosted.org/packages/03/68/68ada9c8aea96ded09a66cfd9bf87aa6db8c2edebe93f5bf9b66b0143fbc/pyproj-3.7.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:35dccbce8201313c596a970fde90e33605248b66272595c061b511c8100ccc08", size = 4617456, upload-time = "2025-08-14T12:05:04.563Z" }, - { url = "https://files.pythonhosted.org/packages/81/e4/4c50ceca7d0e937977866b02cb64e6ccf4df979a5871e521f9e255df6073/pyproj-3.7.2-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:25b0b7cb0042444c29a164b993c45c1b8013d6c48baa61dc1160d834a277e83b", size = 9615590, upload-time = "2025-08-14T12:05:06.094Z" }, - { url = "https://files.pythonhosted.org/packages/05/1e/ada6fb15a1d75b5bd9b554355a69a798c55a7dcc93b8d41596265c1772e3/pyproj-3.7.2-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:85def3a6388e9ba51f964619aa002a9d2098e77c6454ff47773bb68871024281", size = 9474960, upload-time = "2025-08-14T12:05:07.973Z" }, - { url = "https://files.pythonhosted.org/packages/51/07/9d48ad0a8db36e16f842f2c8a694c1d9d7dcf9137264846bef77585a71f3/pyproj-3.7.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b1bccefec3875ab81eabf49059e2b2ea77362c178b66fd3528c3e4df242f1516", size = 10799478, upload-time = "2025-08-14T12:05:14.102Z" }, - { url = "https://files.pythonhosted.org/packages/85/cf/2f812b529079f72f51ff2d6456b7fef06c01735e5cfd62d54ffb2b548028/pyproj-3.7.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d5371ca114d6990b675247355a801925814eca53e6c4b2f1b5c0a956336ee36e", size = 10710030, upload-time = "2025-08-14T12:05:16.317Z" }, - { url = "https://files.pythonhosted.org/packages/99/9b/4626a19e1f03eba4c0e77b91a6cf0f73aa9cb5d51a22ee385c22812bcc2c/pyproj-3.7.2-cp314-cp314-win32.whl", hash = "sha256:77f066626030f41be543274f5ac79f2a511fe89860ecd0914f22131b40a0ec25", size = 5991181, upload-time = "2025-08-14T12:05:19.492Z" }, - { url = "https://files.pythonhosted.org/packages/04/b2/5a6610554306a83a563080c2cf2c57565563eadd280e15388efa00fb5b33/pyproj-3.7.2-cp314-cp314-win_amd64.whl", hash = "sha256:5a964da1696b8522806f4276ab04ccfff8f9eb95133a92a25900697609d40112", size = 6434721, upload-time = "2025-08-14T12:05:21.022Z" }, - { url = "https://files.pythonhosted.org/packages/ae/ce/6c910ea2e1c74ef673c5d48c482564b8a7824a44c4e35cca2e765b68cfcc/pyproj-3.7.2-cp314-cp314-win_arm64.whl", hash = "sha256:e258ab4dbd3cf627809067c0ba8f9884ea76c8e5999d039fb37a1619c6c3e1f6", size = 6363821, upload-time = "2025-08-14T12:05:22.627Z" }, - { url = "https://files.pythonhosted.org/packages/e4/e4/5532f6f7491812ba782a2177fe9de73fd8e2912b59f46a1d056b84b9b8f2/pyproj-3.7.2-cp314-cp314t-macosx_13_0_x86_64.whl", hash = "sha256:bbbac2f930c6d266f70ec75df35ef851d96fdb3701c674f42fd23a9314573b37", size = 6241773, upload-time = "2025-08-14T12:05:24.577Z" }, - { url = "https://files.pythonhosted.org/packages/20/1f/0938c3f2bbbef1789132d1726d9b0e662f10cfc22522743937f421ad664e/pyproj-3.7.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:b7544e0a3d6339dc9151e9c8f3ea62a936ab7cc446a806ec448bbe86aebb979b", size = 4652537, upload-time = "2025-08-14T12:05:26.391Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a8/488b1ed47d25972f33874f91f09ca8f2227902f05f63a2b80dc73e7b1c97/pyproj-3.7.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:f7f5133dca4c703e8acadf6f30bc567d39a42c6af321e7f81975c2518f3ed357", size = 9940864, upload-time = "2025-08-14T12:05:27.985Z" }, - { url = "https://files.pythonhosted.org/packages/c7/cc/7f4c895d0cb98e47b6a85a6d79eaca03eb266129eed2f845125c09cf31ff/pyproj-3.7.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:5aff3343038d7426aa5076f07feb88065f50e0502d1b0d7c22ddfdd2c75a3f81", size = 9688868, upload-time = "2025-08-14T12:05:30.425Z" }, - { url = "https://files.pythonhosted.org/packages/b2/b7/c7e306b8bb0f071d9825b753ee4920f066c40fbfcce9372c4f3cfb2fc4ed/pyproj-3.7.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b0552178c61f2ac1c820d087e8ba6e62b29442debddbb09d51c4bf8acc84d888", size = 11045910, upload-time = "2025-08-14T12:05:32.507Z" }, - { url = "https://files.pythonhosted.org/packages/42/fb/538a4d2df695980e2dde5c04d965fbdd1fe8c20a3194dc4aaa3952a4d1be/pyproj-3.7.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:47d87db2d2c436c5fd0409b34d70bb6cdb875cca2ebe7a9d1c442367b0ab8d59", size = 10895724, upload-time = "2025-08-14T12:05:35.465Z" }, - { url = "https://files.pythonhosted.org/packages/e8/8b/a3f0618b03957de9db5489a04558a8826f43906628bb0b766033aa3b5548/pyproj-3.7.2-cp314-cp314t-win32.whl", hash = "sha256:c9b6f1d8ad3e80a0ee0903a778b6ece7dca1d1d40f6d114ae01bc8ddbad971aa", size = 6056848, upload-time = "2025-08-14T12:05:37.553Z" }, - { url = "https://files.pythonhosted.org/packages/bc/56/413240dd5149dd3291eda55aa55a659da4431244a2fd1319d0ae89407cfb/pyproj-3.7.2-cp314-cp314t-win_amd64.whl", hash = "sha256:1914e29e27933ba6f9822663ee0600f169014a2859f851c054c88cf5ea8a333c", size = 6517676, upload-time = "2025-08-14T12:05:39.126Z" }, - { url = "https://files.pythonhosted.org/packages/15/73/a7141a1a0559bf1a7aa42a11c879ceb19f02f5c6c371c6d57fd86cefd4d1/pyproj-3.7.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d9d25bae416a24397e0d85739f84d323b55f6511e45a522dd7d7eae70d10c7e4", size = 6391844, upload-time = "2025-08-14T12:05:40.745Z" }, -] - -[[package]] -name = "pyproject-hooks" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, -] - -[[package]] -name = "pystac" -source = { editable = "." } -dependencies = [ - { name = "python-dateutil" }, -] - -[package.optional-dependencies] -jinja2 = [ - { name = "jinja2" }, -] -orjson = [ - { name = "orjson" }, -] -urllib3 = [ - { name = "urllib3" }, -] -validation = [ - { name = "jsonschema" }, -] - -[package.dev-dependencies] -dev = [ - { name = "asv" }, - { name = "codespell" }, - { name = "coverage" }, - { name = "doc8" }, - { name = "filelock" }, - { name = "html5lib" }, - { name = "jinja2" }, - { name = "jsonschema" }, - { name = "mypy" }, - { name = "orjson" }, - { name = "packaging" }, - { name = "pre-commit" }, - { name = "pytest" }, - { name = "pytest-cov" }, - { name = "pytest-mock" }, - { name = "pytest-recording" }, - { name = "requests-mock" }, - { name = "ruff" }, - { name = "types-html5lib" }, - { name = "types-jsonschema" }, - { name = "types-orjson" }, - { name = "types-python-dateutil" }, - { name = "types-urllib3" }, - { name = "urllib3" }, - { name = "virtualenv" }, -] -docs = [ - { name = "adlfs" }, - { name = "aiohttp" }, - { name = "boto3" }, - { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "jinja2" }, - { name = "jupyter" }, - { name = "jupyterlab" }, - { name = "nbsphinx" }, - { name = "odc-stac" }, - { name = "planetary-computer" }, - { name = "pydata-sphinx-theme" }, - { name = "rasterio" }, - { name = "rioxarray" }, - { name = "shapely" }, - { name = "sphinx" }, - { name = "sphinx-autobuild" }, - { name = "sphinx-design" }, - { name = "sphinxcontrib-fulltoc" }, - { name = "starlette" }, - { name = "xpystac", version = "0.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "xpystac", version = "0.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "zarr", version = "2.18.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "zarr", version = "3.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] - -[package.metadata] -requires-dist = [ - { name = "jinja2", marker = "extra == 'jinja2'", specifier = "<4.0" }, - { name = "jsonschema", marker = "extra == 'validation'", specifier = "~=4.18" }, - { name = "orjson", marker = "extra == 'orjson'", specifier = ">=3.5" }, - { name = "python-dateutil", specifier = ">=2.7.0" }, - { name = "urllib3", marker = "extra == 'urllib3'", specifier = ">=2.6.3" }, -] -provides-extras = ["jinja2", "orjson", "urllib3", "validation"] - -[package.metadata.requires-dev] -dev = [ - { name = "asv", specifier = ">=0.6.4" }, - { name = "codespell", specifier = "<2.5" }, - { name = "coverage", specifier = ">=7.6.2" }, - { name = "doc8", specifier = ">=1.1.2" }, - { name = "filelock", specifier = ">=3.20.1" }, - { name = "html5lib", specifier = ">=1.1" }, - { name = "jinja2", specifier = ">=3.1.4" }, - { name = "jsonschema", specifier = ">=4.23.0" }, - { name = "mypy", specifier = ">=1.11.2" }, - { name = "orjson", specifier = ">=3.10.7" }, - { name = "packaging", specifier = ">=24.1" }, - { name = "pre-commit", specifier = ">=4.0.1" }, - { name = "pytest", specifier = ">=8.3.3" }, - { name = "pytest-cov", specifier = ">=5.0.0" }, - { name = "pytest-mock", specifier = ">=3.14.0" }, - { name = "pytest-recording", specifier = ">=0.13.2" }, - { name = "requests-mock", specifier = ">=1.12.1" }, - { name = "ruff", specifier = ">=0.6.9" }, - { name = "types-html5lib", specifier = ">=1.1.11.20240806" }, - { name = "types-jsonschema", specifier = ">=4.23.0.20240813" }, - { name = "types-orjson", specifier = ">=3.6.2" }, - { name = "types-python-dateutil", specifier = ">=2.9.0.20241003" }, - { name = "types-urllib3", specifier = ">=1.26.25.14" }, - { name = "urllib3", specifier = ">=1.26.19" }, - { name = "virtualenv", specifier = ">=20.26.6" }, -] -docs = [ - { name = "adlfs", specifier = ">=2024.12.0" }, - { name = "aiohttp", specifier = ">=3.13.3" }, - { name = "boto3", specifier = ">=1.35.39" }, - { name = "ipython", specifier = ">=8.28.0" }, - { name = "jinja2", specifier = ">=3.1.4" }, - { name = "jupyter", specifier = ">=1.1.1" }, - { name = "jupyterlab", specifier = ">=4.4.8" }, - { name = "nbsphinx", specifier = ">=0.9.5" }, - { name = "odc-stac", specifier = ">=0.4.0" }, - { name = "planetary-computer", specifier = ">=1.0.0" }, - { name = "pydata-sphinx-theme", specifier = ">=0.15.4" }, - { name = "rasterio", specifier = ">=1.4.1" }, - { name = "rioxarray", specifier = ">=0.19.0" }, - { name = "shapely", specifier = ">=2.0.6" }, - { name = "sphinx", specifier = ">=8.1.1" }, - { name = "sphinx-autobuild", specifier = ">=2024.10.3" }, - { name = "sphinx-design", specifier = ">=0.6.1" }, - { name = "sphinxcontrib-fulltoc", specifier = ">=1.2.0" }, - { name = "starlette", specifier = ">=0.49.1" }, - { name = "xpystac", specifier = ">=0.2.0" }, - { name = "zarr", specifier = ">=2.18.3" }, -] - -[[package]] -name = "pystac-client" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pystac", extra = ["validation"] }, - { name = "python-dateutil" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c5/8d/b98aeffd325fc208e1624cf586d0c4dfb927bc7a2bce20d3b58ee80d2483/pystac_client-0.9.0.tar.gz", hash = "sha256:3908951583bcc6a3aaaf2828024a8e03764e6ca9d9f9f1d8149df587e14dd744", size = 52339, upload-time = "2025-07-18T15:44:41.1Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/d2/5f6367b14c9f250d1a6725d18bd1e9584f5ab1587e292f3a847e59189598/pystac_client-0.9.0-py3-none-any.whl", hash = "sha256:eed146b5980f93646aaa3a59080f11f1dcab6000b0bfbc28b1d0c6fd0a61eda1", size = 41826, upload-time = "2025-07-18T15:44:40.197Z" }, -] - -[[package]] -name = "pytest" -version = "8.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, -] - -[[package]] -name = "pytest-cov" -version = "6.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage", extra = ["toml"] }, - { name = "pluggy" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, -] - -[[package]] -name = "pytest-mock" -version = "3.14.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" }, -] - -[[package]] -name = "pytest-recording" -version = "0.13.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "vcrpy", version = "5.1.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation == 'PyPy'" }, - { name = "vcrpy", version = "7.0.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/32/9c/f4027c5f1693847b06d11caf4b4f6bb09f22c1581ada4663877ec166b8c6/pytest_recording-0.13.4.tar.gz", hash = "sha256:568d64b2a85992eec4ae0a419c855d5fd96782c5fb016784d86f18053792768c", size = 26576, upload-time = "2025-05-08T10:41:11.231Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/c2/ce34735972cc42d912173e79f200fe66530225190c06655c5632a9d88f1e/pytest_recording-0.13.4-py3-none-any.whl", hash = "sha256:ad49a434b51b1c4f78e85b1e6b74fdcc2a0a581ca16e52c798c6ace971f7f439", size = 13723, upload-time = "2025-05-08T10:41:09.684Z" }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, -] - -[[package]] -name = "python-json-logger" -version = "3.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/de/d3144a0bceede957f961e975f3752760fbe390d57fbe194baf709d8f1f7b/python_json_logger-3.3.0.tar.gz", hash = "sha256:12b7e74b17775e7d565129296105bbe3910842d9d0eb083fc83a6a617aa8df84", size = 16642, upload-time = "2025-03-07T07:08:27.301Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/20/0f2523b9e50a8052bc6a8b732dfc8568abbdc42010aef03a2d750bdab3b2/python_json_logger-3.3.0-py3-none-any.whl", hash = "sha256:dd980fae8cffb24c13caf6e158d3d61c0d6d22342f932cb6e9deedab3d35eec7", size = 15163, upload-time = "2025-03-07T07:08:25.627Z" }, -] - -[[package]] -name = "pytz" -version = "2025.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" }, - { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" }, - { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" }, - { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, - { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, - { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, - { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, - { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, - { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, - { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, - { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, - { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, - { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, - { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, -] - -[[package]] -name = "pywinpty" -version = "2.0.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/7c/917f9c4681bb8d34bfbe0b79d36bbcd902651aeab48790df3d30ba0202fb/pywinpty-2.0.15.tar.gz", hash = "sha256:312cf39153a8736c617d45ce8b6ad6cd2107de121df91c455b10ce6bba7a39b2", size = 29017, upload-time = "2025-02-03T21:53:23.265Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/b7/855db919ae526d2628f3f2e6c281c4cdff7a9a8af51bb84659a9f07b1861/pywinpty-2.0.15-cp310-cp310-win_amd64.whl", hash = "sha256:8e7f5de756a615a38b96cd86fa3cd65f901ce54ce147a3179c45907fa11b4c4e", size = 1405161, upload-time = "2025-02-03T21:56:25.008Z" }, - { url = "https://files.pythonhosted.org/packages/5e/ac/6884dcb7108af66ad53f73ef4dad096e768c9203a6e6ce5e6b0c4a46e238/pywinpty-2.0.15-cp311-cp311-win_amd64.whl", hash = "sha256:9a6bcec2df2707aaa9d08b86071970ee32c5026e10bcc3cc5f6f391d85baf7ca", size = 1405249, upload-time = "2025-02-03T21:55:47.114Z" }, - { url = "https://files.pythonhosted.org/packages/88/e5/9714def18c3a411809771a3fbcec70bffa764b9675afb00048a620fca604/pywinpty-2.0.15-cp312-cp312-win_amd64.whl", hash = "sha256:83a8f20b430bbc5d8957249f875341a60219a4e971580f2ba694fbfb54a45ebc", size = 1405243, upload-time = "2025-02-03T21:56:52.476Z" }, - { url = "https://files.pythonhosted.org/packages/fb/16/2ab7b3b7f55f3c6929e5f629e1a68362981e4e5fed592a2ed1cb4b4914a5/pywinpty-2.0.15-cp313-cp313-win_amd64.whl", hash = "sha256:ab5920877dd632c124b4ed17bc6dd6ef3b9f86cd492b963ffdb1a67b85b0f408", size = 1405020, upload-time = "2025-02-03T21:56:04.753Z" }, - { url = "https://files.pythonhosted.org/packages/7c/16/edef3515dd2030db2795dbfbe392232c7a0f3dc41b98e92b38b42ba497c7/pywinpty-2.0.15-cp313-cp313t-win_amd64.whl", hash = "sha256:a4560ad8c01e537708d2790dbe7da7d986791de805d89dd0d3697ca59e9e4901", size = 1404151, upload-time = "2025-02-03T21:55:53.628Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, - { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, - { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, - { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, - { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, - { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, - { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, - { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, - { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, -] - -[[package]] -name = "pyzmq" -version = "27.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "implementation_name == 'pypy' and platform_python_implementation == 'PyPy'" }, - { name = "cffi", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "implementation_name == 'pypy' and platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f1/06/50a4e9648b3e8b992bef8eb632e457307553a89d294103213cfd47b3da69/pyzmq-27.0.0.tar.gz", hash = "sha256:b1f08eeb9ce1510e6939b6e5dcd46a17765e2333daae78ecf4606808442e52cf", size = 280478, upload-time = "2025-06-13T14:09:07.087Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/09/1681d4b047626d352c083770618ac29655ab1f5c20eee31dc94c000b9b7b/pyzmq-27.0.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:b973ee650e8f442ce482c1d99ca7ab537c69098d53a3d046676a484fd710c87a", size = 1329291, upload-time = "2025-06-13T14:06:57.945Z" }, - { url = "https://files.pythonhosted.org/packages/9d/b2/9c9385225fdd54db9506ed8accbb9ea63ca813ba59d43d7f282a6a16a30b/pyzmq-27.0.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:661942bc7cd0223d569d808f2e5696d9cc120acc73bf3e88a1f1be7ab648a7e4", size = 905952, upload-time = "2025-06-13T14:07:03.232Z" }, - { url = "https://files.pythonhosted.org/packages/41/73/333c72c7ec182cdffe25649e3da1c3b9f3cf1cede63cfdc23d1384d4a601/pyzmq-27.0.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50360fb2a056ffd16e5f4177eee67f1dd1017332ea53fb095fe7b5bf29c70246", size = 666165, upload-time = "2025-06-13T14:07:04.667Z" }, - { url = "https://files.pythonhosted.org/packages/a5/fe/fc7b9c1a50981928e25635a926653cb755364316db59ccd6e79cfb9a0b4f/pyzmq-27.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf209a6dc4b420ed32a7093642843cbf8703ed0a7d86c16c0b98af46762ebefb", size = 853755, upload-time = "2025-06-13T14:07:06.93Z" }, - { url = "https://files.pythonhosted.org/packages/8c/4c/740ed4b6e8fa160cd19dc5abec8db68f440564b2d5b79c1d697d9862a2f7/pyzmq-27.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c2dace4a7041cca2fba5357a2d7c97c5effdf52f63a1ef252cfa496875a3762d", size = 1654868, upload-time = "2025-06-13T14:07:08.224Z" }, - { url = "https://files.pythonhosted.org/packages/97/00/875b2ecfcfc78ab962a59bd384995186818524ea957dc8ad3144611fae12/pyzmq-27.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:63af72b2955fc77caf0a77444baa2431fcabb4370219da38e1a9f8d12aaebe28", size = 2033443, upload-time = "2025-06-13T14:07:09.653Z" }, - { url = "https://files.pythonhosted.org/packages/60/55/6dd9c470c42d713297c5f2a56f7903dc1ebdb4ab2edda996445c21651900/pyzmq-27.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e8c4adce8e37e75c4215297d7745551b8dcfa5f728f23ce09bf4e678a9399413", size = 1891288, upload-time = "2025-06-13T14:07:11.099Z" }, - { url = "https://files.pythonhosted.org/packages/28/5d/54b0ef50d40d7c65a627f4a4b4127024ba9820f2af8acd933a4d30ae192e/pyzmq-27.0.0-cp310-cp310-win32.whl", hash = "sha256:5d5ef4718ecab24f785794e0e7536436698b459bfbc19a1650ef55280119d93b", size = 567936, upload-time = "2025-06-13T14:07:12.468Z" }, - { url = "https://files.pythonhosted.org/packages/18/ea/dedca4321de748ca48d3bcdb72274d4d54e8d84ea49088d3de174bd45d88/pyzmq-27.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:e40609380480b3d12c30f841323f42451c755b8fece84235236f5fe5ffca8c1c", size = 628686, upload-time = "2025-06-13T14:07:14.051Z" }, - { url = "https://files.pythonhosted.org/packages/d4/a7/fcdeedc306e71e94ac262cba2d02337d885f5cdb7e8efced8e5ffe327808/pyzmq-27.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:6b0397b0be277b46762956f576e04dc06ced265759e8c2ff41a0ee1aa0064198", size = 559039, upload-time = "2025-06-13T14:07:15.289Z" }, - { url = "https://files.pythonhosted.org/packages/44/df/84c630654106d9bd9339cdb564aa941ed41b023a0264251d6743766bb50e/pyzmq-27.0.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:21457825249b2a53834fa969c69713f8b5a79583689387a5e7aed880963ac564", size = 1332718, upload-time = "2025-06-13T14:07:16.555Z" }, - { url = "https://files.pythonhosted.org/packages/c1/8e/f6a5461a07654d9840d256476434ae0ff08340bba562a455f231969772cb/pyzmq-27.0.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1958947983fef513e6e98eff9cb487b60bf14f588dc0e6bf35fa13751d2c8251", size = 908248, upload-time = "2025-06-13T14:07:18.033Z" }, - { url = "https://files.pythonhosted.org/packages/7c/93/82863e8d695a9a3ae424b63662733ae204a295a2627d52af2f62c2cd8af9/pyzmq-27.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0dc628b5493f9a8cd9844b8bee9732ef587ab00002157c9329e4fc0ef4d3afa", size = 668647, upload-time = "2025-06-13T14:07:19.378Z" }, - { url = "https://files.pythonhosted.org/packages/f3/85/15278769b348121eacdbfcbd8c4d40f1102f32fa6af5be1ffc032ed684be/pyzmq-27.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7bbe9e1ed2c8d3da736a15694d87c12493e54cc9dc9790796f0321794bbc91f", size = 856600, upload-time = "2025-06-13T14:07:20.906Z" }, - { url = "https://files.pythonhosted.org/packages/d4/af/1c469b3d479bd095edb28e27f12eee10b8f00b356acbefa6aeb14dd295d1/pyzmq-27.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dc1091f59143b471d19eb64f54bae4f54bcf2a466ffb66fe45d94d8d734eb495", size = 1657748, upload-time = "2025-06-13T14:07:22.549Z" }, - { url = "https://files.pythonhosted.org/packages/8c/f4/17f965d0ee6380b1d6326da842a50e4b8b9699745161207945f3745e8cb5/pyzmq-27.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7011ade88c8e535cf140f8d1a59428676fbbce7c6e54fefce58bf117aefb6667", size = 2034311, upload-time = "2025-06-13T14:07:23.966Z" }, - { url = "https://files.pythonhosted.org/packages/e0/6e/7c391d81fa3149fd759de45d298003de6cfab343fb03e92c099821c448db/pyzmq-27.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c386339d7e3f064213aede5d03d054b237937fbca6dd2197ac8cf3b25a6b14e", size = 1893630, upload-time = "2025-06-13T14:07:25.899Z" }, - { url = "https://files.pythonhosted.org/packages/0e/e0/eaffe7a86f60e556399e224229e7769b717f72fec0706b70ab2c03aa04cb/pyzmq-27.0.0-cp311-cp311-win32.whl", hash = "sha256:0546a720c1f407b2172cb04b6b094a78773491497e3644863cf5c96c42df8cff", size = 567706, upload-time = "2025-06-13T14:07:27.595Z" }, - { url = "https://files.pythonhosted.org/packages/c9/05/89354a8cffdcce6e547d48adaaf7be17007fc75572123ff4ca90a4ca04fc/pyzmq-27.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:15f39d50bd6c9091c67315ceb878a4f531957b121d2a05ebd077eb35ddc5efed", size = 630322, upload-time = "2025-06-13T14:07:28.938Z" }, - { url = "https://files.pythonhosted.org/packages/fa/07/4ab976d5e1e63976719389cc4f3bfd248a7f5f2bb2ebe727542363c61b5f/pyzmq-27.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c5817641eebb391a2268c27fecd4162448e03538387093cdbd8bf3510c316b38", size = 558435, upload-time = "2025-06-13T14:07:30.256Z" }, - { url = "https://files.pythonhosted.org/packages/93/a7/9ad68f55b8834ede477842214feba6a4c786d936c022a67625497aacf61d/pyzmq-27.0.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:cbabc59dcfaac66655c040dfcb8118f133fb5dde185e5fc152628354c1598e52", size = 1305438, upload-time = "2025-06-13T14:07:31.676Z" }, - { url = "https://files.pythonhosted.org/packages/ba/ee/26aa0f98665a22bc90ebe12dced1de5f3eaca05363b717f6fb229b3421b3/pyzmq-27.0.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:cb0ac5179cba4b2f94f1aa208fbb77b62c4c9bf24dd446278b8b602cf85fcda3", size = 895095, upload-time = "2025-06-13T14:07:33.104Z" }, - { url = "https://files.pythonhosted.org/packages/cf/85/c57e7ab216ecd8aa4cc7e3b83b06cc4e9cf45c87b0afc095f10cd5ce87c1/pyzmq-27.0.0-cp312-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53a48f0228eab6cbf69fde3aa3c03cbe04e50e623ef92ae395fce47ef8a76152", size = 651826, upload-time = "2025-06-13T14:07:34.831Z" }, - { url = "https://files.pythonhosted.org/packages/69/9a/9ea7e230feda9400fb0ae0d61d7d6ddda635e718d941c44eeab22a179d34/pyzmq-27.0.0-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:111db5f395e09f7e775f759d598f43cb815fc58e0147623c4816486e1a39dc22", size = 839750, upload-time = "2025-06-13T14:07:36.553Z" }, - { url = "https://files.pythonhosted.org/packages/08/66/4cebfbe71f3dfbd417011daca267539f62ed0fbc68105357b68bbb1a25b7/pyzmq-27.0.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c8878011653dcdc27cc2c57e04ff96f0471e797f5c19ac3d7813a245bcb24371", size = 1641357, upload-time = "2025-06-13T14:07:38.21Z" }, - { url = "https://files.pythonhosted.org/packages/ac/f6/b0f62578c08d2471c791287149cb8c2aaea414ae98c6e995c7dbe008adfb/pyzmq-27.0.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:c0ed2c1f335ba55b5fdc964622254917d6b782311c50e138863eda409fbb3b6d", size = 2020281, upload-time = "2025-06-13T14:07:39.599Z" }, - { url = "https://files.pythonhosted.org/packages/37/b9/4f670b15c7498495da9159edc374ec09c88a86d9cd5a47d892f69df23450/pyzmq-27.0.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e918d70862d4cfd4b1c187310015646a14e1f5917922ab45b29f28f345eeb6be", size = 1877110, upload-time = "2025-06-13T14:07:41.027Z" }, - { url = "https://files.pythonhosted.org/packages/66/31/9dee25c226295b740609f0d46db2fe972b23b6f5cf786360980524a3ba92/pyzmq-27.0.0-cp312-abi3-win32.whl", hash = "sha256:88b4e43cab04c3c0f0d55df3b1eef62df2b629a1a369b5289a58f6fa8b07c4f4", size = 559297, upload-time = "2025-06-13T14:07:42.533Z" }, - { url = "https://files.pythonhosted.org/packages/9b/12/52da5509800f7ff2d287b2f2b4e636e7ea0f001181cba6964ff6c1537778/pyzmq-27.0.0-cp312-abi3-win_amd64.whl", hash = "sha256:dce4199bf5f648a902ce37e7b3afa286f305cd2ef7a8b6ec907470ccb6c8b371", size = 619203, upload-time = "2025-06-13T14:07:43.843Z" }, - { url = "https://files.pythonhosted.org/packages/93/6d/7f2e53b19d1edb1eb4f09ec7c3a1f945ca0aac272099eab757d15699202b/pyzmq-27.0.0-cp312-abi3-win_arm64.whl", hash = "sha256:56e46bbb85d52c1072b3f809cc1ce77251d560bc036d3a312b96db1afe76db2e", size = 551927, upload-time = "2025-06-13T14:07:45.51Z" }, - { url = "https://files.pythonhosted.org/packages/19/62/876b27c4ff777db4ceba1c69ea90d3c825bb4f8d5e7cd987ce5802e33c55/pyzmq-27.0.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c36ad534c0c29b4afa088dc53543c525b23c0797e01b69fef59b1a9c0e38b688", size = 1340826, upload-time = "2025-06-13T14:07:46.881Z" }, - { url = "https://files.pythonhosted.org/packages/43/69/58ef8f4f59d3bcd505260c73bee87b008850f45edca40ddaba54273c35f4/pyzmq-27.0.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:67855c14173aec36395d7777aaba3cc527b393821f30143fd20b98e1ff31fd38", size = 897283, upload-time = "2025-06-13T14:07:49.562Z" }, - { url = "https://files.pythonhosted.org/packages/43/15/93a0d0396700a60475ad3c5d42c5f1c308d3570bc94626b86c71ef9953e0/pyzmq-27.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8617c7d43cd8ccdb62aebe984bfed77ca8f036e6c3e46dd3dddda64b10f0ab7a", size = 660567, upload-time = "2025-06-13T14:07:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b3/fe055513e498ca32f64509abae19b9c9eb4d7c829e02bd8997dd51b029eb/pyzmq-27.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:67bfbcbd0a04c575e8103a6061d03e393d9f80ffdb9beb3189261e9e9bc5d5e9", size = 847681, upload-time = "2025-06-13T14:07:52.77Z" }, - { url = "https://files.pythonhosted.org/packages/b6/4f/ff15300b00b5b602191f3df06bbc8dd4164e805fdd65bb77ffbb9c5facdc/pyzmq-27.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5cd11d46d7b7e5958121b3eaf4cd8638eff3a720ec527692132f05a57f14341d", size = 1650148, upload-time = "2025-06-13T14:07:54.178Z" }, - { url = "https://files.pythonhosted.org/packages/c4/6f/84bdfff2a224a6f26a24249a342e5906993c50b0761e311e81b39aef52a7/pyzmq-27.0.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:b801c2e40c5aa6072c2f4876de8dccd100af6d9918d4d0d7aa54a1d982fd4f44", size = 2023768, upload-time = "2025-06-13T14:07:55.714Z" }, - { url = "https://files.pythonhosted.org/packages/64/39/dc2db178c26a42228c5ac94a9cc595030458aa64c8d796a7727947afbf55/pyzmq-27.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:20d5cb29e8c5f76a127c75b6e7a77e846bc4b655c373baa098c26a61b7ecd0ef", size = 1885199, upload-time = "2025-06-13T14:07:57.166Z" }, - { url = "https://files.pythonhosted.org/packages/c7/21/dae7b06a1f8cdee5d8e7a63d99c5d129c401acc40410bef2cbf42025e26f/pyzmq-27.0.0-cp313-cp313t-win32.whl", hash = "sha256:a20528da85c7ac7a19b7384e8c3f8fa707841fd85afc4ed56eda59d93e3d98ad", size = 575439, upload-time = "2025-06-13T14:07:58.959Z" }, - { url = "https://files.pythonhosted.org/packages/eb/bc/1709dc55f0970cf4cb8259e435e6773f9946f41a045c2cb90e870b7072da/pyzmq-27.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d8229f2efece6a660ee211d74d91dbc2a76b95544d46c74c615e491900dc107f", size = 639933, upload-time = "2025-06-13T14:08:00.777Z" }, - { url = "https://files.pythonhosted.org/packages/09/6f/be6523a7f3821c0b5370912ef02822c028611360e0d206dd945bdbf9eaef/pyzmq-27.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:656c1866505a5735d0660b7da6d7147174bbf59d4975fc2b7f09f43c9bc25745", size = 835950, upload-time = "2025-06-13T14:08:35Z" }, - { url = "https://files.pythonhosted.org/packages/c6/1e/a50fdd5c15018de07ab82a61bc460841be967ee7bbe7abee3b714d66f7ac/pyzmq-27.0.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:74175b9e12779382432dd1d1f5960ebe7465d36649b98a06c6b26be24d173fab", size = 799876, upload-time = "2025-06-13T14:08:36.849Z" }, - { url = "https://files.pythonhosted.org/packages/88/a1/89eb5b71f5a504f8f887aceb8e1eb3626e00c00aa8085381cdff475440dc/pyzmq-27.0.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8c6de908465697a8708e4d6843a1e884f567962fc61eb1706856545141d0cbb", size = 567400, upload-time = "2025-06-13T14:08:38.95Z" }, - { url = "https://files.pythonhosted.org/packages/56/aa/4571dbcff56cfb034bac73fde8294e123c975ce3eea89aff31bf6dc6382b/pyzmq-27.0.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c644aaacc01d0df5c7072826df45e67301f191c55f68d7b2916d83a9ddc1b551", size = 747031, upload-time = "2025-06-13T14:08:40.413Z" }, - { url = "https://files.pythonhosted.org/packages/46/e0/d25f30fe0991293c5b2f5ef3b070d35fa6d57c0c7428898c3ab4913d0297/pyzmq-27.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:10f70c1d9a446a85013a36871a296007f6fe4232b530aa254baf9da3f8328bc0", size = 544726, upload-time = "2025-06-13T14:08:41.997Z" }, - { url = "https://files.pythonhosted.org/packages/98/a6/92394373b8dbc1edc9d53c951e8d3989d518185174ee54492ec27711779d/pyzmq-27.0.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd1dc59763effd1576f8368047c9c31468fce0af89d76b5067641137506792ae", size = 835948, upload-time = "2025-06-13T14:08:43.516Z" }, - { url = "https://files.pythonhosted.org/packages/56/f3/4dc38d75d9995bfc18773df3e41f2a2ca9b740b06f1a15dbf404077e7588/pyzmq-27.0.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:60e8cc82d968174650c1860d7b716366caab9973787a1c060cf8043130f7d0f7", size = 799874, upload-time = "2025-06-13T14:08:45.017Z" }, - { url = "https://files.pythonhosted.org/packages/ab/ba/64af397e0f421453dc68e31d5e0784d554bf39013a2de0872056e96e58af/pyzmq-27.0.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:14fe7aaac86e4e93ea779a821967360c781d7ac5115b3f1a171ced77065a0174", size = 567400, upload-time = "2025-06-13T14:08:46.855Z" }, - { url = "https://files.pythonhosted.org/packages/63/87/ec956cbe98809270b59a22891d5758edae147a258e658bf3024a8254c855/pyzmq-27.0.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6ad0562d4e6abb785be3e4dd68599c41be821b521da38c402bc9ab2a8e7ebc7e", size = 747031, upload-time = "2025-06-13T14:08:48.419Z" }, - { url = "https://files.pythonhosted.org/packages/be/8a/4a3764a68abc02e2fbb0668d225b6fda5cd39586dd099cee8b2ed6ab0452/pyzmq-27.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:9df43a2459cd3a3563404c1456b2c4c69564daa7dbaf15724c09821a3329ce46", size = 544726, upload-time = "2025-06-13T14:08:49.903Z" }, -] - -[[package]] -name = "rasterio" -version = "1.4.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "affine" }, - { name = "attrs" }, - { name = "certifi" }, - { name = "click" }, - { name = "click-plugins" }, - { name = "cligj" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pyparsing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/de/19/ab4326e419b543da623ce4191f68e3f36a4d9adc64f3df5c78f044d8d9ca/rasterio-1.4.3.tar.gz", hash = "sha256:201f05dbc7c4739dacb2c78a1cf4e09c0b7265b0a4d16ccbd1753ce4f2af350a", size = 442990, upload-time = "2024-12-02T14:49:25.571Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/1b/fbc6e3f11fe42898c787d27b6844f660bdd7081967d5f68b950c4bd9f043/rasterio-1.4.3-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:80f994b92e5dda78f13291710bd5c43efcfd164f69a8a2c20489115df9d178c8", size = 21539592, upload-time = "2024-12-02T14:48:24.761Z" }, - { url = "https://files.pythonhosted.org/packages/28/e6/97914b0c1824106ae9499446515915db3b4a8924d0568b6e888f4d305472/rasterio-1.4.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1a6e6ca9ec361599b48c9918ce25adb1a9203b8c8ca9b34ad78dccb3aef7945a", size = 18770366, upload-time = "2024-12-02T14:48:28.58Z" }, - { url = "https://files.pythonhosted.org/packages/c1/f5/5cc3a8ee9deee2292432d69237ab4c5364f886844234d8e6dad29358aef0/rasterio-1.4.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b8a4311582274de2346450e5361d092b80b8b5c7b02fda6203402ba101ffabf", size = 22216305, upload-time = "2024-12-02T14:48:32.15Z" }, - { url = "https://files.pythonhosted.org/packages/51/2f/f72f77633aecba9afda903f9201c566520cc2dfeb0e1e0d36c102aa18189/rasterio-1.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:e79847a5a0e01399457a1e02d8c92040cb56729d054fe7796f0c17b246b18bf0", size = 25442319, upload-time = "2024-12-02T14:48:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/f3/fd/ba3850e4cbccc47d03037f2c96889f7f221a674a7be6665c7da7cd483a07/rasterio-1.4.3-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:9c30114d95ebba4ff49f078b3c193d29ff56d832588649400a3fa78f1dda1c96", size = 21540767, upload-time = "2024-12-02T14:48:38.794Z" }, - { url = "https://files.pythonhosted.org/packages/b3/59/ca86697161206233eea6353237b0c0f02f6f70434144db162f964a7e1b19/rasterio-1.4.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:812c854e7177064aeb58def2d59752887ad6b3d39ff3f858ed9df3f2ddc95613", size = 18762975, upload-time = "2024-12-02T14:48:41.895Z" }, - { url = "https://files.pythonhosted.org/packages/aa/fa/723fa6a48a419b044146cd92fa6a66ead8532d96c352fbc2f2a1546cb5b6/rasterio-1.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54eef32d20a0dfbba59a8bb9828e562c3e9e97e2355b8dfe4a5274117976059f", size = 22204391, upload-time = "2024-12-02T14:48:44.447Z" }, - { url = "https://files.pythonhosted.org/packages/7e/1f/56462740694de764fde264051224fcbf800dad43cac92a66753153128866/rasterio-1.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:4009f7ce4e0883d8e5b400970daa3f1ca309caac8916d955722ee4486654d452", size = 25452478, upload-time = "2024-12-02T14:48:46.893Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f2/b7417292ceace70d815760f7e41fe5b0244ebff78ede11b1ffa9ca01c370/rasterio-1.4.3-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:e703e4b2c74c678786d5d110a3f30e26f3acfd65f09ccf35f69683a532f7a772", size = 21514543, upload-time = "2024-12-02T14:48:49.757Z" }, - { url = "https://files.pythonhosted.org/packages/b2/ea/e21010457847b26bb4aea3983e9b44afbcefef07defc5e9a3285a8fe2f0c/rasterio-1.4.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:38a126f8dbf405cd3450b5bd10c6cc493a2e1be4cf83442d26f5e4f412372d36", size = 18735924, upload-time = "2024-12-02T14:48:53.263Z" }, - { url = "https://files.pythonhosted.org/packages/67/72/331727423b28fffdfd8bf18bdc55c18d374c0fefd2dde390cd833f8f4477/rasterio-1.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e90c2c300294265c16becc9822337ded0f01fb8664500b4d77890d633d8cd0e", size = 22251721, upload-time = "2024-12-02T14:48:56.533Z" }, - { url = "https://files.pythonhosted.org/packages/be/cc/453816b489af94b9a243eda889865973d518989ba6923b2381f6d6722b43/rasterio-1.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:a962ad4c29feaf38b1d7a94389313127de3646a5b9b734fbf9a04e16051a27ff", size = 25430154, upload-time = "2024-12-02T14:48:59.261Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e0/718c06b825d1f62077913e5bff1e70b71ac673718b135d55a0256d88d4ba/rasterio-1.4.3-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:5d4fcb635379b3d7b2f5e944c153849e3d27e93f35ad73ad4d3f0b8a580f0c8e", size = 21532284, upload-time = "2024-12-02T14:49:03.325Z" }, - { url = "https://files.pythonhosted.org/packages/bb/a8/3b6b11923300d6835453d1157fabb518338067a67366c5c52e9df9a2314f/rasterio-1.4.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:98a9c89eade8c779e8ac1e525269faaa18c6b9818fc3c72cfc4627df71c66d0d", size = 18729960, upload-time = "2024-12-02T14:49:06.423Z" }, - { url = "https://files.pythonhosted.org/packages/05/19/94d6c66184c7d0f9374330c714f62c147dbb53eda9efdcc8fc6e2ac454c5/rasterio-1.4.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9bab1a0bb22b8bed1db34b5258db93d790ed4e61ef21ac055a7c6933c8d5e84", size = 22237518, upload-time = "2024-12-02T14:49:09.155Z" }, - { url = "https://files.pythonhosted.org/packages/df/88/9db5f49ebfdd9c12365e4cac76c34ccb1a642b1c8cbab4124b3c681495de/rasterio-1.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:1839960e2f3057a6daa323ccf67b330f8f2f0dbd4a50cc7031e88e649301c5c0", size = 25424949, upload-time = "2024-12-02T14:49:11.742Z" }, -] - -[[package]] -name = "referencing" -version = "0.36.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "rpds-py" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" }, -] - -[[package]] -name = "requests" -version = "2.32.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, -] - -[[package]] -name = "requests-mock" -version = "1.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/92/32/587625f91f9a0a3d84688bf9cfc4b2480a7e8ec327cefd0ff2ac891fd2cf/requests-mock-1.12.1.tar.gz", hash = "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401", size = 60901, upload-time = "2024-03-29T03:54:29.446Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/97/ec/889fbc557727da0c34a33850950310240f2040f3b1955175fdb2b36a8910/requests_mock-1.12.1-py2.py3-none-any.whl", hash = "sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563", size = 27695, upload-time = "2024-03-29T03:54:27.64Z" }, -] - -[[package]] -name = "restructuredtext-lint" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/48/9c/6d8035cafa2d2d314f34e6cd9313a299de095b26e96f1c7312878f988eec/restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45", size = 16723, upload-time = "2022-02-24T05:51:10.907Z" } - -[[package]] -name = "rfc3339-validator" -version = "0.1.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, -] - -[[package]] -name = "rfc3986-validator" -version = "0.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760, upload-time = "2019-10-28T16:00:19.144Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242, upload-time = "2019-10-28T16:00:13.976Z" }, -] - -[[package]] -name = "rfc3987-syntax" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "lark" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239, upload-time = "2025-07-18T01:05:05.015Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046, upload-time = "2025-07-18T01:05:03.843Z" }, -] - -[[package]] -name = "rioxarray" -version = "0.19.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "packaging" }, - { name = "pyproj", version = "3.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "pyproj", version = "3.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "rasterio" }, - { name = "xarray", version = "2025.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "xarray", version = "2025.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/8e/fe4e87460f8c62d8d5c683e09f19fbde5d9cfcfd0342d02df1f452999b5d/rioxarray-0.19.0.tar.gz", hash = "sha256:7819a0036fd874c8c8e280447cbbe43d8dc72fc4a14ac7852a665b1bdb7d4b04", size = 54600, upload-time = "2025-04-21T17:46:54.183Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/2f/63d2cacc0e525f8e3398bcf32bd3620385f22cd1600834ec49d7f3597a7b/rioxarray-0.19.0-py3-none-any.whl", hash = "sha256:494ee4fff1781072d55ee5276f5d07b63d93b05093cb33b926a12186ba5bb8ef", size = 62151, upload-time = "2025-04-21T17:46:52.801Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/aa/4456d84bbb54adc6a916fb10c9b374f78ac840337644e4a5eda229c81275/rpds_py-0.26.0.tar.gz", hash = "sha256:20dae58a859b0906f0685642e591056f1e787f3a8b39c8e8749a45dc7d26bdb0", size = 27385, upload-time = "2025-07-01T15:57:13.958Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/31/1459645f036c3dfeacef89e8e5825e430c77dde8489f3b99eaafcd4a60f5/rpds_py-0.26.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4c70c70f9169692b36307a95f3d8c0a9fcd79f7b4a383aad5eaa0e9718b79b37", size = 372466, upload-time = "2025-07-01T15:53:40.55Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ff/3d0727f35836cc8773d3eeb9a46c40cc405854e36a8d2e951f3a8391c976/rpds_py-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:777c62479d12395bfb932944e61e915741e364c843afc3196b694db3d669fcd0", size = 357825, upload-time = "2025-07-01T15:53:42.247Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ce/badc5e06120a54099ae287fa96d82cbb650a5f85cf247ffe19c7b157fd1f/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec671691e72dff75817386aa02d81e708b5a7ec0dec6669ec05213ff6b77e1bd", size = 381530, upload-time = "2025-07-01T15:53:43.585Z" }, - { url = "https://files.pythonhosted.org/packages/1e/a5/fa5d96a66c95d06c62d7a30707b6a4cfec696ab8ae280ee7be14e961e118/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a1cb5d6ce81379401bbb7f6dbe3d56de537fb8235979843f0d53bc2e9815a79", size = 396933, upload-time = "2025-07-01T15:53:45.78Z" }, - { url = "https://files.pythonhosted.org/packages/00/a7/7049d66750f18605c591a9db47d4a059e112a0c9ff8de8daf8fa0f446bba/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f789e32fa1fb6a7bf890e0124e7b42d1e60d28ebff57fe806719abb75f0e9a3", size = 513973, upload-time = "2025-07-01T15:53:47.085Z" }, - { url = "https://files.pythonhosted.org/packages/0e/f1/528d02c7d6b29d29fac8fd784b354d3571cc2153f33f842599ef0cf20dd2/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c55b0a669976cf258afd718de3d9ad1b7d1fe0a91cd1ab36f38b03d4d4aeaaf", size = 402293, upload-time = "2025-07-01T15:53:48.117Z" }, - { url = "https://files.pythonhosted.org/packages/15/93/fde36cd6e4685df2cd08508f6c45a841e82f5bb98c8d5ecf05649522acb5/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c70d9ec912802ecfd6cd390dadb34a9578b04f9bcb8e863d0a7598ba5e9e7ccc", size = 383787, upload-time = "2025-07-01T15:53:50.874Z" }, - { url = "https://files.pythonhosted.org/packages/69/f2/5007553aaba1dcae5d663143683c3dfd03d9395289f495f0aebc93e90f24/rpds_py-0.26.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3021933c2cb7def39d927b9862292e0f4c75a13d7de70eb0ab06efed4c508c19", size = 416312, upload-time = "2025-07-01T15:53:52.046Z" }, - { url = "https://files.pythonhosted.org/packages/8f/a7/ce52c75c1e624a79e48a69e611f1c08844564e44c85db2b6f711d76d10ce/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a7898b6ca3b7d6659e55cdac825a2e58c638cbf335cde41f4619e290dd0ad11", size = 558403, upload-time = "2025-07-01T15:53:53.192Z" }, - { url = "https://files.pythonhosted.org/packages/79/d5/e119db99341cc75b538bf4cb80504129fa22ce216672fb2c28e4a101f4d9/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:12bff2ad9447188377f1b2794772f91fe68bb4bbfa5a39d7941fbebdbf8c500f", size = 588323, upload-time = "2025-07-01T15:53:54.336Z" }, - { url = "https://files.pythonhosted.org/packages/93/94/d28272a0b02f5fe24c78c20e13bbcb95f03dc1451b68e7830ca040c60bd6/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:191aa858f7d4902e975d4cf2f2d9243816c91e9605070aeb09c0a800d187e323", size = 554541, upload-time = "2025-07-01T15:53:55.469Z" }, - { url = "https://files.pythonhosted.org/packages/93/e0/8c41166602f1b791da892d976057eba30685486d2e2c061ce234679c922b/rpds_py-0.26.0-cp310-cp310-win32.whl", hash = "sha256:b37a04d9f52cb76b6b78f35109b513f6519efb481d8ca4c321f6a3b9580b3f45", size = 220442, upload-time = "2025-07-01T15:53:56.524Z" }, - { url = "https://files.pythonhosted.org/packages/87/f0/509736bb752a7ab50fb0270c2a4134d671a7b3038030837e5536c3de0e0b/rpds_py-0.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:38721d4c9edd3eb6670437d8d5e2070063f305bfa2d5aa4278c51cedcd508a84", size = 231314, upload-time = "2025-07-01T15:53:57.842Z" }, - { url = "https://files.pythonhosted.org/packages/09/4c/4ee8f7e512030ff79fda1df3243c88d70fc874634e2dbe5df13ba4210078/rpds_py-0.26.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9e8cb77286025bdb21be2941d64ac6ca016130bfdcd228739e8ab137eb4406ed", size = 372610, upload-time = "2025-07-01T15:53:58.844Z" }, - { url = "https://files.pythonhosted.org/packages/fa/9d/3dc16be00f14fc1f03c71b1d67c8df98263ab2710a2fbd65a6193214a527/rpds_py-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e09330b21d98adc8ccb2dbb9fc6cb434e8908d4c119aeaa772cb1caab5440a0", size = 358032, upload-time = "2025-07-01T15:53:59.985Z" }, - { url = "https://files.pythonhosted.org/packages/e7/5a/7f1bf8f045da2866324a08ae80af63e64e7bfaf83bd31f865a7b91a58601/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9c1b92b774b2e68d11193dc39620d62fd8ab33f0a3c77ecdabe19c179cdbc1", size = 381525, upload-time = "2025-07-01T15:54:01.162Z" }, - { url = "https://files.pythonhosted.org/packages/45/8a/04479398c755a066ace10e3d158866beb600867cacae194c50ffa783abd0/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:824e6d3503ab990d7090768e4dfd9e840837bae057f212ff9f4f05ec6d1975e7", size = 397089, upload-time = "2025-07-01T15:54:02.319Z" }, - { url = "https://files.pythonhosted.org/packages/72/88/9203f47268db488a1b6d469d69c12201ede776bb728b9d9f29dbfd7df406/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ad7fd2258228bf288f2331f0a6148ad0186b2e3643055ed0db30990e59817a6", size = 514255, upload-time = "2025-07-01T15:54:03.38Z" }, - { url = "https://files.pythonhosted.org/packages/f5/b4/01ce5d1e853ddf81fbbd4311ab1eff0b3cf162d559288d10fd127e2588b5/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0dc23bbb3e06ec1ea72d515fb572c1fea59695aefbffb106501138762e1e915e", size = 402283, upload-time = "2025-07-01T15:54:04.923Z" }, - { url = "https://files.pythonhosted.org/packages/34/a2/004c99936997bfc644d590a9defd9e9c93f8286568f9c16cdaf3e14429a7/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d80bf832ac7b1920ee29a426cdca335f96a2b5caa839811803e999b41ba9030d", size = 383881, upload-time = "2025-07-01T15:54:06.482Z" }, - { url = "https://files.pythonhosted.org/packages/05/1b/ef5fba4a8f81ce04c427bfd96223f92f05e6cd72291ce9d7523db3b03a6c/rpds_py-0.26.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0919f38f5542c0a87e7b4afcafab6fd2c15386632d249e9a087498571250abe3", size = 415822, upload-time = "2025-07-01T15:54:07.605Z" }, - { url = "https://files.pythonhosted.org/packages/16/80/5c54195aec456b292f7bd8aa61741c8232964063fd8a75fdde9c1e982328/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d422b945683e409000c888e384546dbab9009bb92f7c0b456e217988cf316107", size = 558347, upload-time = "2025-07-01T15:54:08.591Z" }, - { url = "https://files.pythonhosted.org/packages/f2/1c/1845c1b1fd6d827187c43afe1841d91678d7241cbdb5420a4c6de180a538/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a7711fa562ba2da1aa757e11024ad6d93bad6ad7ede5afb9af144623e5f76a", size = 587956, upload-time = "2025-07-01T15:54:09.963Z" }, - { url = "https://files.pythonhosted.org/packages/2e/ff/9e979329dd131aa73a438c077252ddabd7df6d1a7ad7b9aacf6261f10faa/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238e8c8610cb7c29460e37184f6799547f7e09e6a9bdbdab4e8edb90986a2318", size = 554363, upload-time = "2025-07-01T15:54:11.073Z" }, - { url = "https://files.pythonhosted.org/packages/00/8b/d78cfe034b71ffbe72873a136e71acc7a831a03e37771cfe59f33f6de8a2/rpds_py-0.26.0-cp311-cp311-win32.whl", hash = "sha256:893b022bfbdf26d7bedb083efeea624e8550ca6eb98bf7fea30211ce95b9201a", size = 220123, upload-time = "2025-07-01T15:54:12.382Z" }, - { url = "https://files.pythonhosted.org/packages/94/c1/3c8c94c7dd3905dbfde768381ce98778500a80db9924731d87ddcdb117e9/rpds_py-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:87a5531de9f71aceb8af041d72fc4cab4943648d91875ed56d2e629bef6d4c03", size = 231732, upload-time = "2025-07-01T15:54:13.434Z" }, - { url = "https://files.pythonhosted.org/packages/67/93/e936fbed1b734eabf36ccb5d93c6a2e9246fbb13c1da011624b7286fae3e/rpds_py-0.26.0-cp311-cp311-win_arm64.whl", hash = "sha256:de2713f48c1ad57f89ac25b3cb7daed2156d8e822cf0eca9b96a6f990718cc41", size = 221917, upload-time = "2025-07-01T15:54:14.559Z" }, - { url = "https://files.pythonhosted.org/packages/ea/86/90eb87c6f87085868bd077c7a9938006eb1ce19ed4d06944a90d3560fce2/rpds_py-0.26.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:894514d47e012e794f1350f076c427d2347ebf82f9b958d554d12819849a369d", size = 363933, upload-time = "2025-07-01T15:54:15.734Z" }, - { url = "https://files.pythonhosted.org/packages/63/78/4469f24d34636242c924626082b9586f064ada0b5dbb1e9d096ee7a8e0c6/rpds_py-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc921b96fa95a097add244da36a1d9e4f3039160d1d30f1b35837bf108c21136", size = 350447, upload-time = "2025-07-01T15:54:16.922Z" }, - { url = "https://files.pythonhosted.org/packages/ad/91/c448ed45efdfdade82348d5e7995e15612754826ea640afc20915119734f/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1157659470aa42a75448b6e943c895be8c70531c43cb78b9ba990778955582", size = 384711, upload-time = "2025-07-01T15:54:18.101Z" }, - { url = "https://files.pythonhosted.org/packages/ec/43/e5c86fef4be7f49828bdd4ecc8931f0287b1152c0bb0163049b3218740e7/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:521ccf56f45bb3a791182dc6b88ae5f8fa079dd705ee42138c76deb1238e554e", size = 400865, upload-time = "2025-07-01T15:54:19.295Z" }, - { url = "https://files.pythonhosted.org/packages/55/34/e00f726a4d44f22d5c5fe2e5ddd3ac3d7fd3f74a175607781fbdd06fe375/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9def736773fd56b305c0eef698be5192c77bfa30d55a0e5885f80126c4831a15", size = 517763, upload-time = "2025-07-01T15:54:20.858Z" }, - { url = "https://files.pythonhosted.org/packages/52/1c/52dc20c31b147af724b16104500fba13e60123ea0334beba7b40e33354b4/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdad4ea3b4513b475e027be79e5a0ceac8ee1c113a1a11e5edc3c30c29f964d8", size = 406651, upload-time = "2025-07-01T15:54:22.508Z" }, - { url = "https://files.pythonhosted.org/packages/2e/77/87d7bfabfc4e821caa35481a2ff6ae0b73e6a391bb6b343db2c91c2b9844/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82b165b07f416bdccf5c84546a484cc8f15137ca38325403864bfdf2b5b72f6a", size = 386079, upload-time = "2025-07-01T15:54:23.987Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d4/7f2200c2d3ee145b65b3cddc4310d51f7da6a26634f3ac87125fd789152a/rpds_py-0.26.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d04cab0a54b9dba4d278fe955a1390da3cf71f57feb78ddc7cb67cbe0bd30323", size = 421379, upload-time = "2025-07-01T15:54:25.073Z" }, - { url = "https://files.pythonhosted.org/packages/ae/13/9fdd428b9c820869924ab62236b8688b122baa22d23efdd1c566938a39ba/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:79061ba1a11b6a12743a2b0f72a46aa2758613d454aa6ba4f5a265cc48850158", size = 562033, upload-time = "2025-07-01T15:54:26.225Z" }, - { url = "https://files.pythonhosted.org/packages/f3/e1/b69686c3bcbe775abac3a4c1c30a164a2076d28df7926041f6c0eb5e8d28/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f405c93675d8d4c5ac87364bb38d06c988e11028a64b52a47158a355079661f3", size = 591639, upload-time = "2025-07-01T15:54:27.424Z" }, - { url = "https://files.pythonhosted.org/packages/5c/c9/1e3d8c8863c84a90197ac577bbc3d796a92502124c27092413426f670990/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dafd4c44b74aa4bed4b250f1aed165b8ef5de743bcca3b88fc9619b6087093d2", size = 557105, upload-time = "2025-07-01T15:54:29.93Z" }, - { url = "https://files.pythonhosted.org/packages/9f/c5/90c569649057622959f6dcc40f7b516539608a414dfd54b8d77e3b201ac0/rpds_py-0.26.0-cp312-cp312-win32.whl", hash = "sha256:3da5852aad63fa0c6f836f3359647870e21ea96cf433eb393ffa45263a170d44", size = 223272, upload-time = "2025-07-01T15:54:31.128Z" }, - { url = "https://files.pythonhosted.org/packages/7d/16/19f5d9f2a556cfed454eebe4d354c38d51c20f3db69e7b4ce6cff904905d/rpds_py-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf47cfdabc2194a669dcf7a8dbba62e37a04c5041d2125fae0233b720da6f05c", size = 234995, upload-time = "2025-07-01T15:54:32.195Z" }, - { url = "https://files.pythonhosted.org/packages/83/f0/7935e40b529c0e752dfaa7880224771b51175fce08b41ab4a92eb2fbdc7f/rpds_py-0.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:20ab1ae4fa534f73647aad289003f1104092890849e0266271351922ed5574f8", size = 223198, upload-time = "2025-07-01T15:54:33.271Z" }, - { url = "https://files.pythonhosted.org/packages/6a/67/bb62d0109493b12b1c6ab00de7a5566aa84c0e44217c2d94bee1bd370da9/rpds_py-0.26.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:696764a5be111b036256c0b18cd29783fab22154690fc698062fc1b0084b511d", size = 363917, upload-time = "2025-07-01T15:54:34.755Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f3/34e6ae1925a5706c0f002a8d2d7f172373b855768149796af87bd65dcdb9/rpds_py-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6c15d2080a63aaed876e228efe4f814bc7889c63b1e112ad46fdc8b368b9e1", size = 350073, upload-time = "2025-07-01T15:54:36.292Z" }, - { url = "https://files.pythonhosted.org/packages/75/83/1953a9d4f4e4de7fd0533733e041c28135f3c21485faaef56a8aadbd96b5/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390e3170babf42462739a93321e657444f0862c6d722a291accc46f9d21ed04e", size = 384214, upload-time = "2025-07-01T15:54:37.469Z" }, - { url = "https://files.pythonhosted.org/packages/48/0e/983ed1b792b3322ea1d065e67f4b230f3b96025f5ce3878cc40af09b7533/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7da84c2c74c0f5bc97d853d9e17bb83e2dcafcff0dc48286916001cc114379a1", size = 400113, upload-time = "2025-07-01T15:54:38.954Z" }, - { url = "https://files.pythonhosted.org/packages/69/7f/36c0925fff6f660a80be259c5b4f5e53a16851f946eb080351d057698528/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c5fe114a6dd480a510b6d3661d09d67d1622c4bf20660a474507aaee7eeeee9", size = 515189, upload-time = "2025-07-01T15:54:40.57Z" }, - { url = "https://files.pythonhosted.org/packages/13/45/cbf07fc03ba7a9b54662c9badb58294ecfb24f828b9732970bd1a431ed5c/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3100b3090269f3a7ea727b06a6080d4eb7439dca4c0e91a07c5d133bb1727ea7", size = 406998, upload-time = "2025-07-01T15:54:43.025Z" }, - { url = "https://files.pythonhosted.org/packages/6c/b0/8fa5e36e58657997873fd6a1cf621285ca822ca75b4b3434ead047daa307/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c03c9b0c64afd0320ae57de4c982801271c0c211aa2d37f3003ff5feb75bb04", size = 385903, upload-time = "2025-07-01T15:54:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f7/b25437772f9f57d7a9fbd73ed86d0dcd76b4c7c6998348c070d90f23e315/rpds_py-0.26.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5963b72ccd199ade6ee493723d18a3f21ba7d5b957017607f815788cef50eaf1", size = 419785, upload-time = "2025-07-01T15:54:46.043Z" }, - { url = "https://files.pythonhosted.org/packages/a7/6b/63ffa55743dfcb4baf2e9e77a0b11f7f97ed96a54558fcb5717a4b2cd732/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9da4e873860ad5bab3291438525cae80169daecbfafe5657f7f5fb4d6b3f96b9", size = 561329, upload-time = "2025-07-01T15:54:47.64Z" }, - { url = "https://files.pythonhosted.org/packages/2f/07/1f4f5e2886c480a2346b1e6759c00278b8a69e697ae952d82ae2e6ee5db0/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5afaddaa8e8c7f1f7b4c5c725c0070b6eed0228f705b90a1732a48e84350f4e9", size = 590875, upload-time = "2025-07-01T15:54:48.9Z" }, - { url = "https://files.pythonhosted.org/packages/cc/bc/e6639f1b91c3a55f8c41b47d73e6307051b6e246254a827ede730624c0f8/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4916dc96489616a6f9667e7526af8fa693c0fdb4f3acb0e5d9f4400eb06a47ba", size = 556636, upload-time = "2025-07-01T15:54:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/05/4c/b3917c45566f9f9a209d38d9b54a1833f2bb1032a3e04c66f75726f28876/rpds_py-0.26.0-cp313-cp313-win32.whl", hash = "sha256:2a343f91b17097c546b93f7999976fd6c9d5900617aa848c81d794e062ab302b", size = 222663, upload-time = "2025-07-01T15:54:52.023Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0b/0851bdd6025775aaa2365bb8de0697ee2558184c800bfef8d7aef5ccde58/rpds_py-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:0a0b60701f2300c81b2ac88a5fb893ccfa408e1c4a555a77f908a2596eb875a5", size = 234428, upload-time = "2025-07-01T15:54:53.692Z" }, - { url = "https://files.pythonhosted.org/packages/ed/e8/a47c64ed53149c75fb581e14a237b7b7cd18217e969c30d474d335105622/rpds_py-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:257d011919f133a4746958257f2c75238e3ff54255acd5e3e11f3ff41fd14256", size = 222571, upload-time = "2025-07-01T15:54:54.822Z" }, - { url = "https://files.pythonhosted.org/packages/89/bf/3d970ba2e2bcd17d2912cb42874107390f72873e38e79267224110de5e61/rpds_py-0.26.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:529c8156d7506fba5740e05da8795688f87119cce330c244519cf706a4a3d618", size = 360475, upload-time = "2025-07-01T15:54:56.228Z" }, - { url = "https://files.pythonhosted.org/packages/82/9f/283e7e2979fc4ec2d8ecee506d5a3675fce5ed9b4b7cb387ea5d37c2f18d/rpds_py-0.26.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f53ec51f9d24e9638a40cabb95078ade8c99251945dad8d57bf4aabe86ecee35", size = 346692, upload-time = "2025-07-01T15:54:58.561Z" }, - { url = "https://files.pythonhosted.org/packages/e3/03/7e50423c04d78daf391da3cc4330bdb97042fc192a58b186f2d5deb7befd/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab504c4d654e4a29558eaa5bb8cea5fdc1703ea60a8099ffd9c758472cf913f", size = 379415, upload-time = "2025-07-01T15:54:59.751Z" }, - { url = "https://files.pythonhosted.org/packages/57/00/d11ee60d4d3b16808432417951c63df803afb0e0fc672b5e8d07e9edaaae/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd0641abca296bc1a00183fe44f7fced8807ed49d501f188faa642d0e4975b83", size = 391783, upload-time = "2025-07-01T15:55:00.898Z" }, - { url = "https://files.pythonhosted.org/packages/08/b3/1069c394d9c0d6d23c5b522e1f6546b65793a22950f6e0210adcc6f97c3e/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b312fecc1d017b5327afa81d4da1480f51c68810963a7336d92203dbb3d4f1", size = 512844, upload-time = "2025-07-01T15:55:02.201Z" }, - { url = "https://files.pythonhosted.org/packages/08/3b/c4fbf0926800ed70b2c245ceca99c49f066456755f5d6eb8863c2c51e6d0/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c741107203954f6fc34d3066d213d0a0c40f7bb5aafd698fb39888af277c70d8", size = 402105, upload-time = "2025-07-01T15:55:03.698Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b0/db69b52ca07413e568dae9dc674627a22297abb144c4d6022c6d78f1e5cc/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3e55a7db08dc9a6ed5fb7103019d2c1a38a349ac41901f9f66d7f95750942f", size = 383440, upload-time = "2025-07-01T15:55:05.398Z" }, - { url = "https://files.pythonhosted.org/packages/4c/e1/c65255ad5b63903e56b3bb3ff9dcc3f4f5c3badde5d08c741ee03903e951/rpds_py-0.26.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e851920caab2dbcae311fd28f4313c6953993893eb5c1bb367ec69d9a39e7ed", size = 412759, upload-time = "2025-07-01T15:55:08.316Z" }, - { url = "https://files.pythonhosted.org/packages/e4/22/bb731077872377a93c6e93b8a9487d0406c70208985831034ccdeed39c8e/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dfbf280da5f876d0b00c81f26bedce274e72a678c28845453885a9b3c22ae632", size = 556032, upload-time = "2025-07-01T15:55:09.52Z" }, - { url = "https://files.pythonhosted.org/packages/e0/8b/393322ce7bac5c4530fb96fc79cc9ea2f83e968ff5f6e873f905c493e1c4/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1cc81d14ddfa53d7f3906694d35d54d9d3f850ef8e4e99ee68bc0d1e5fed9a9c", size = 585416, upload-time = "2025-07-01T15:55:11.216Z" }, - { url = "https://files.pythonhosted.org/packages/49/ae/769dc372211835bf759319a7aae70525c6eb523e3371842c65b7ef41c9c6/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dca83c498b4650a91efcf7b88d669b170256bf8017a5db6f3e06c2bf031f57e0", size = 554049, upload-time = "2025-07-01T15:55:13.004Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f9/4c43f9cc203d6ba44ce3146246cdc38619d92c7bd7bad4946a3491bd5b70/rpds_py-0.26.0-cp313-cp313t-win32.whl", hash = "sha256:4d11382bcaf12f80b51d790dee295c56a159633a8e81e6323b16e55d81ae37e9", size = 218428, upload-time = "2025-07-01T15:55:14.486Z" }, - { url = "https://files.pythonhosted.org/packages/7e/8b/9286b7e822036a4a977f2f1e851c7345c20528dbd56b687bb67ed68a8ede/rpds_py-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff110acded3c22c033e637dd8896e411c7d3a11289b2edf041f86663dbc791e9", size = 231524, upload-time = "2025-07-01T15:55:15.745Z" }, - { url = "https://files.pythonhosted.org/packages/55/07/029b7c45db910c74e182de626dfdae0ad489a949d84a468465cd0ca36355/rpds_py-0.26.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:da619979df60a940cd434084355c514c25cf8eb4cf9a508510682f6c851a4f7a", size = 364292, upload-time = "2025-07-01T15:55:17.001Z" }, - { url = "https://files.pythonhosted.org/packages/13/d1/9b3d3f986216b4d1f584878dca15ce4797aaf5d372d738974ba737bf68d6/rpds_py-0.26.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ea89a2458a1a75f87caabefe789c87539ea4e43b40f18cff526052e35bbb4fdf", size = 350334, upload-time = "2025-07-01T15:55:18.922Z" }, - { url = "https://files.pythonhosted.org/packages/18/98/16d5e7bc9ec715fa9668731d0cf97f6b032724e61696e2db3d47aeb89214/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feac1045b3327a45944e7dcbeb57530339f6b17baff154df51ef8b0da34c8c12", size = 384875, upload-time = "2025-07-01T15:55:20.399Z" }, - { url = "https://files.pythonhosted.org/packages/f9/13/aa5e2b1ec5ab0e86a5c464d53514c0467bec6ba2507027d35fc81818358e/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b818a592bd69bfe437ee8368603d4a2d928c34cffcdf77c2e761a759ffd17d20", size = 399993, upload-time = "2025-07-01T15:55:21.729Z" }, - { url = "https://files.pythonhosted.org/packages/17/03/8021810b0e97923abdbab6474c8b77c69bcb4b2c58330777df9ff69dc559/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a8b0dd8648709b62d9372fc00a57466f5fdeefed666afe3fea5a6c9539a0331", size = 516683, upload-time = "2025-07-01T15:55:22.918Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b1/da8e61c87c2f3d836954239fdbbfb477bb7b54d74974d8f6fcb34342d166/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d3498ad0df07d81112aa6ec6c95a7e7b1ae00929fb73e7ebee0f3faaeabad2f", size = 408825, upload-time = "2025-07-01T15:55:24.207Z" }, - { url = "https://files.pythonhosted.org/packages/38/bc/1fc173edaaa0e52c94b02a655db20697cb5fa954ad5a8e15a2c784c5cbdd/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24a4146ccb15be237fdef10f331c568e1b0e505f8c8c9ed5d67759dac58ac246", size = 387292, upload-time = "2025-07-01T15:55:25.554Z" }, - { url = "https://files.pythonhosted.org/packages/7c/eb/3a9bb4bd90867d21916f253caf4f0d0be7098671b6715ad1cead9fe7bab9/rpds_py-0.26.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a9a63785467b2d73635957d32a4f6e73d5e4df497a16a6392fa066b753e87387", size = 420435, upload-time = "2025-07-01T15:55:27.798Z" }, - { url = "https://files.pythonhosted.org/packages/cd/16/e066dcdb56f5632713445271a3f8d3d0b426d51ae9c0cca387799df58b02/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:de4ed93a8c91debfd5a047be327b7cc8b0cc6afe32a716bbbc4aedca9e2a83af", size = 562410, upload-time = "2025-07-01T15:55:29.057Z" }, - { url = "https://files.pythonhosted.org/packages/60/22/ddbdec7eb82a0dc2e455be44c97c71c232983e21349836ce9f272e8a3c29/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:caf51943715b12af827696ec395bfa68f090a4c1a1d2509eb4e2cb69abbbdb33", size = 590724, upload-time = "2025-07-01T15:55:30.719Z" }, - { url = "https://files.pythonhosted.org/packages/2c/b4/95744085e65b7187d83f2fcb0bef70716a1ea0a9e5d8f7f39a86e5d83424/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4a59e5bc386de021f56337f757301b337d7ab58baa40174fb150accd480bc953", size = 558285, upload-time = "2025-07-01T15:55:31.981Z" }, - { url = "https://files.pythonhosted.org/packages/37/37/6309a75e464d1da2559446f9c811aa4d16343cebe3dbb73701e63f760caa/rpds_py-0.26.0-cp314-cp314-win32.whl", hash = "sha256:92c8db839367ef16a662478f0a2fe13e15f2227da3c1430a782ad0f6ee009ec9", size = 223459, upload-time = "2025-07-01T15:55:33.312Z" }, - { url = "https://files.pythonhosted.org/packages/d9/6f/8e9c11214c46098b1d1391b7e02b70bb689ab963db3b19540cba17315291/rpds_py-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:b0afb8cdd034150d4d9f53926226ed27ad15b7f465e93d7468caaf5eafae0d37", size = 236083, upload-time = "2025-07-01T15:55:34.933Z" }, - { url = "https://files.pythonhosted.org/packages/47/af/9c4638994dd623d51c39892edd9d08e8be8220a4b7e874fa02c2d6e91955/rpds_py-0.26.0-cp314-cp314-win_arm64.whl", hash = "sha256:ca3f059f4ba485d90c8dc75cb5ca897e15325e4e609812ce57f896607c1c0867", size = 223291, upload-time = "2025-07-01T15:55:36.202Z" }, - { url = "https://files.pythonhosted.org/packages/4d/db/669a241144460474aab03e254326b32c42def83eb23458a10d163cb9b5ce/rpds_py-0.26.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:5afea17ab3a126006dc2f293b14ffc7ef3c85336cf451564a0515ed7648033da", size = 361445, upload-time = "2025-07-01T15:55:37.483Z" }, - { url = "https://files.pythonhosted.org/packages/3b/2d/133f61cc5807c6c2fd086a46df0eb8f63a23f5df8306ff9f6d0fd168fecc/rpds_py-0.26.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:69f0c0a3df7fd3a7eec50a00396104bb9a843ea6d45fcc31c2d5243446ffd7a7", size = 347206, upload-time = "2025-07-01T15:55:38.828Z" }, - { url = "https://files.pythonhosted.org/packages/05/bf/0e8fb4c05f70273469eecf82f6ccf37248558526a45321644826555db31b/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:801a71f70f9813e82d2513c9a96532551fce1e278ec0c64610992c49c04c2dad", size = 380330, upload-time = "2025-07-01T15:55:40.175Z" }, - { url = "https://files.pythonhosted.org/packages/d4/a8/060d24185d8b24d3923322f8d0ede16df4ade226a74e747b8c7c978e3dd3/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df52098cde6d5e02fa75c1f6244f07971773adb4a26625edd5c18fee906fa84d", size = 392254, upload-time = "2025-07-01T15:55:42.015Z" }, - { url = "https://files.pythonhosted.org/packages/b9/7b/7c2e8a9ee3e6bc0bae26bf29f5219955ca2fbb761dca996a83f5d2f773fe/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bc596b30f86dc6f0929499c9e574601679d0341a0108c25b9b358a042f51bca", size = 516094, upload-time = "2025-07-01T15:55:43.603Z" }, - { url = "https://files.pythonhosted.org/packages/75/d6/f61cafbed8ba1499b9af9f1777a2a199cd888f74a96133d8833ce5eaa9c5/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9dfbe56b299cf5875b68eb6f0ebaadc9cac520a1989cac0db0765abfb3709c19", size = 402889, upload-time = "2025-07-01T15:55:45.275Z" }, - { url = "https://files.pythonhosted.org/packages/92/19/c8ac0a8a8df2dd30cdec27f69298a5c13e9029500d6d76718130f5e5be10/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac64f4b2bdb4ea622175c9ab7cf09444e412e22c0e02e906978b3b488af5fde8", size = 384301, upload-time = "2025-07-01T15:55:47.098Z" }, - { url = "https://files.pythonhosted.org/packages/41/e1/6b1859898bc292a9ce5776016c7312b672da00e25cec74d7beced1027286/rpds_py-0.26.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:181ef9b6bbf9845a264f9aa45c31836e9f3c1f13be565d0d010e964c661d1e2b", size = 412891, upload-time = "2025-07-01T15:55:48.412Z" }, - { url = "https://files.pythonhosted.org/packages/ef/b9/ceb39af29913c07966a61367b3c08b4f71fad841e32c6b59a129d5974698/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:49028aa684c144ea502a8e847d23aed5e4c2ef7cadfa7d5eaafcb40864844b7a", size = 557044, upload-time = "2025-07-01T15:55:49.816Z" }, - { url = "https://files.pythonhosted.org/packages/2f/27/35637b98380731a521f8ec4f3fd94e477964f04f6b2f8f7af8a2d889a4af/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e5d524d68a474a9688336045bbf76cb0def88549c1b2ad9dbfec1fb7cfbe9170", size = 585774, upload-time = "2025-07-01T15:55:51.192Z" }, - { url = "https://files.pythonhosted.org/packages/52/d9/3f0f105420fecd18551b678c9a6ce60bd23986098b252a56d35781b3e7e9/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c1851f429b822831bd2edcbe0cfd12ee9ea77868f8d3daf267b189371671c80e", size = 554886, upload-time = "2025-07-01T15:55:52.541Z" }, - { url = "https://files.pythonhosted.org/packages/6b/c5/347c056a90dc8dd9bc240a08c527315008e1b5042e7a4cf4ac027be9d38a/rpds_py-0.26.0-cp314-cp314t-win32.whl", hash = "sha256:7bdb17009696214c3b66bb3590c6d62e14ac5935e53e929bcdbc5a495987a84f", size = 219027, upload-time = "2025-07-01T15:55:53.874Z" }, - { url = "https://files.pythonhosted.org/packages/75/04/5302cea1aa26d886d34cadbf2dc77d90d7737e576c0065f357b96dc7a1a6/rpds_py-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f14440b9573a6f76b4ee4770c13f0b5921f71dde3b6fcb8dabbefd13b7fe05d7", size = 232821, upload-time = "2025-07-01T15:55:55.167Z" }, - { url = "https://files.pythonhosted.org/packages/ef/9a/1f033b0b31253d03d785b0cd905bc127e555ab496ea6b4c7c2e1f951f2fd/rpds_py-0.26.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3c0909c5234543ada2515c05dc08595b08d621ba919629e94427e8e03539c958", size = 373226, upload-time = "2025-07-01T15:56:16.578Z" }, - { url = "https://files.pythonhosted.org/packages/58/29/5f88023fd6aaaa8ca3c4a6357ebb23f6f07da6079093ccf27c99efce87db/rpds_py-0.26.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c1fb0cda2abcc0ac62f64e2ea4b4e64c57dfd6b885e693095460c61bde7bb18e", size = 359230, upload-time = "2025-07-01T15:56:17.978Z" }, - { url = "https://files.pythonhosted.org/packages/6c/6c/13eaebd28b439da6964dde22712b52e53fe2824af0223b8e403249d10405/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84d142d2d6cf9b31c12aa4878d82ed3b2324226270b89b676ac62ccd7df52d08", size = 382363, upload-time = "2025-07-01T15:56:19.977Z" }, - { url = "https://files.pythonhosted.org/packages/55/fc/3bb9c486b06da19448646f96147796de23c5811ef77cbfc26f17307b6a9d/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a547e21c5610b7e9093d870be50682a6a6cf180d6da0f42c47c306073bfdbbf6", size = 397146, upload-time = "2025-07-01T15:56:21.39Z" }, - { url = "https://files.pythonhosted.org/packages/15/18/9d1b79eb4d18e64ba8bba9e7dec6f9d6920b639f22f07ee9368ca35d4673/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35e9a70a0f335371275cdcd08bc5b8051ac494dd58bff3bbfb421038220dc871", size = 514804, upload-time = "2025-07-01T15:56:22.78Z" }, - { url = "https://files.pythonhosted.org/packages/4f/5a/175ad7191bdbcd28785204621b225ad70e85cdfd1e09cc414cb554633b21/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0dfa6115c6def37905344d56fb54c03afc49104e2ca473d5dedec0f6606913b4", size = 402820, upload-time = "2025-07-01T15:56:24.584Z" }, - { url = "https://files.pythonhosted.org/packages/11/45/6a67ecf6d61c4d4aff4bc056e864eec4b2447787e11d1c2c9a0242c6e92a/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:313cfcd6af1a55a286a3c9a25f64af6d0e46cf60bc5798f1db152d97a216ff6f", size = 384567, upload-time = "2025-07-01T15:56:26.064Z" }, - { url = "https://files.pythonhosted.org/packages/a1/ba/16589da828732b46454c61858950a78fe4c931ea4bf95f17432ffe64b241/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f7bf2496fa563c046d05e4d232d7b7fd61346e2402052064b773e5c378bf6f73", size = 416520, upload-time = "2025-07-01T15:56:27.608Z" }, - { url = "https://files.pythonhosted.org/packages/81/4b/00092999fc7c0c266045e984d56b7314734cc400a6c6dc4d61a35f135a9d/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:aa81873e2c8c5aa616ab8e017a481a96742fdf9313c40f14338ca7dbf50cb55f", size = 559362, upload-time = "2025-07-01T15:56:29.078Z" }, - { url = "https://files.pythonhosted.org/packages/96/0c/43737053cde1f93ac4945157f7be1428724ab943e2132a0d235a7e161d4e/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:68ffcf982715f5b5b7686bdd349ff75d422e8f22551000c24b30eaa1b7f7ae84", size = 588113, upload-time = "2025-07-01T15:56:30.485Z" }, - { url = "https://files.pythonhosted.org/packages/46/46/8e38f6161466e60a997ed7e9951ae5de131dedc3cf778ad35994b4af823d/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6188de70e190847bb6db3dc3981cbadff87d27d6fe9b4f0e18726d55795cee9b", size = 555429, upload-time = "2025-07-01T15:56:31.956Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ac/65da605e9f1dd643ebe615d5bbd11b6efa1d69644fc4bf623ea5ae385a82/rpds_py-0.26.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1c962145c7473723df9722ba4c058de12eb5ebedcb4e27e7d902920aa3831ee8", size = 231950, upload-time = "2025-07-01T15:56:33.337Z" }, - { url = "https://files.pythonhosted.org/packages/51/f2/b5c85b758a00c513bb0389f8fc8e61eb5423050c91c958cdd21843faa3e6/rpds_py-0.26.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f61a9326f80ca59214d1cceb0a09bb2ece5b2563d4e0cd37bfd5515c28510674", size = 373505, upload-time = "2025-07-01T15:56:34.716Z" }, - { url = "https://files.pythonhosted.org/packages/23/e0/25db45e391251118e915e541995bb5f5ac5691a3b98fb233020ba53afc9b/rpds_py-0.26.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:183f857a53bcf4b1b42ef0f57ca553ab56bdd170e49d8091e96c51c3d69ca696", size = 359468, upload-time = "2025-07-01T15:56:36.219Z" }, - { url = "https://files.pythonhosted.org/packages/0b/73/dd5ee6075bb6491be3a646b301dfd814f9486d924137a5098e61f0487e16/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:941c1cfdf4799d623cf3aa1d326a6b4fdb7a5799ee2687f3516738216d2262fb", size = 382680, upload-time = "2025-07-01T15:56:37.644Z" }, - { url = "https://files.pythonhosted.org/packages/2f/10/84b522ff58763a5c443f5bcedc1820240e454ce4e620e88520f04589e2ea/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72a8d9564a717ee291f554eeb4bfeafe2309d5ec0aa6c475170bdab0f9ee8e88", size = 397035, upload-time = "2025-07-01T15:56:39.241Z" }, - { url = "https://files.pythonhosted.org/packages/06/ea/8667604229a10a520fcbf78b30ccc278977dcc0627beb7ea2c96b3becef0/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:511d15193cbe013619dd05414c35a7dedf2088fcee93c6bbb7c77859765bd4e8", size = 514922, upload-time = "2025-07-01T15:56:40.645Z" }, - { url = "https://files.pythonhosted.org/packages/24/e6/9ed5b625c0661c4882fc8cdf302bf8e96c73c40de99c31e0b95ed37d508c/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aea1f9741b603a8d8fedb0ed5502c2bc0accbc51f43e2ad1337fe7259c2b77a5", size = 402822, upload-time = "2025-07-01T15:56:42.137Z" }, - { url = "https://files.pythonhosted.org/packages/8a/58/212c7b6fd51946047fb45d3733da27e2fa8f7384a13457c874186af691b1/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4019a9d473c708cf2f16415688ef0b4639e07abaa569d72f74745bbeffafa2c7", size = 384336, upload-time = "2025-07-01T15:56:44.239Z" }, - { url = "https://files.pythonhosted.org/packages/aa/f5/a40ba78748ae8ebf4934d4b88e77b98497378bc2c24ba55ebe87a4e87057/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:093d63b4b0f52d98ebae33b8c50900d3d67e0666094b1be7a12fffd7f65de74b", size = 416871, upload-time = "2025-07-01T15:56:46.284Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a6/33b1fc0c9f7dcfcfc4a4353daa6308b3ece22496ceece348b3e7a7559a09/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2abe21d8ba64cded53a2a677e149ceb76dcf44284202d737178afe7ba540c1eb", size = 559439, upload-time = "2025-07-01T15:56:48.549Z" }, - { url = "https://files.pythonhosted.org/packages/71/2d/ceb3f9c12f8cfa56d34995097f6cd99da1325642c60d1b6680dd9df03ed8/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:4feb7511c29f8442cbbc28149a92093d32e815a28aa2c50d333826ad2a20fdf0", size = 588380, upload-time = "2025-07-01T15:56:50.086Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ed/9de62c2150ca8e2e5858acf3f4f4d0d180a38feef9fdab4078bea63d8dba/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e99685fc95d386da368013e7fb4269dd39c30d99f812a8372d62f244f662709c", size = 555334, upload-time = "2025-07-01T15:56:51.703Z" }, -] - -[[package]] -name = "ruff" -version = "0.12.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/cd/01015eb5034605fd98d829c5839ec2c6b4582b479707f7c1c2af861e8258/ruff-0.12.5.tar.gz", hash = "sha256:b209db6102b66f13625940b7f8c7d0f18e20039bb7f6101fbdac935c9612057e", size = 5170722, upload-time = "2025-07-24T13:26:37.456Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/de/ad2f68f0798ff15dd8c0bcc2889558970d9a685b3249565a937cd820ad34/ruff-0.12.5-py3-none-linux_armv6l.whl", hash = "sha256:1de2c887e9dec6cb31fcb9948299de5b2db38144e66403b9660c9548a67abd92", size = 11819133, upload-time = "2025-07-24T13:25:56.369Z" }, - { url = "https://files.pythonhosted.org/packages/f8/fc/c6b65cd0e7fbe60f17e7ad619dca796aa49fbca34bb9bea5f8faf1ec2643/ruff-0.12.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d1ab65e7d8152f519e7dea4de892317c9da7a108da1c56b6a3c1d5e7cf4c5e9a", size = 12501114, upload-time = "2025-07-24T13:25:59.471Z" }, - { url = "https://files.pythonhosted.org/packages/c5/de/c6bec1dce5ead9f9e6a946ea15e8d698c35f19edc508289d70a577921b30/ruff-0.12.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:962775ed5b27c7aa3fdc0d8f4d4433deae7659ef99ea20f783d666e77338b8cf", size = 11716873, upload-time = "2025-07-24T13:26:01.496Z" }, - { url = "https://files.pythonhosted.org/packages/a1/16/cf372d2ebe91e4eb5b82a2275c3acfa879e0566a7ac94d331ea37b765ac8/ruff-0.12.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73b4cae449597e7195a49eb1cdca89fd9fbb16140c7579899e87f4c85bf82f73", size = 11958829, upload-time = "2025-07-24T13:26:03.721Z" }, - { url = "https://files.pythonhosted.org/packages/25/bf/cd07e8f6a3a6ec746c62556b4c4b79eeb9b0328b362bb8431b7b8afd3856/ruff-0.12.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b13489c3dc50de5e2d40110c0cce371e00186b880842e245186ca862bf9a1ac", size = 11626619, upload-time = "2025-07-24T13:26:06.118Z" }, - { url = "https://files.pythonhosted.org/packages/d8/c9/c2ccb3b8cbb5661ffda6925f81a13edbb786e623876141b04919d1128370/ruff-0.12.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1504fea81461cf4841778b3ef0a078757602a3b3ea4b008feb1308cb3f23e08", size = 13221894, upload-time = "2025-07-24T13:26:08.292Z" }, - { url = "https://files.pythonhosted.org/packages/6b/58/68a5be2c8e5590ecdad922b2bcd5583af19ba648f7648f95c51c3c1eca81/ruff-0.12.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c7da4129016ae26c32dfcbd5b671fe652b5ab7fc40095d80dcff78175e7eddd4", size = 14163909, upload-time = "2025-07-24T13:26:10.474Z" }, - { url = "https://files.pythonhosted.org/packages/bd/d1/ef6b19622009ba8386fdb792c0743f709cf917b0b2f1400589cbe4739a33/ruff-0.12.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ca972c80f7ebcfd8af75a0f18b17c42d9f1ef203d163669150453f50ca98ab7b", size = 13583652, upload-time = "2025-07-24T13:26:13.381Z" }, - { url = "https://files.pythonhosted.org/packages/62/e3/1c98c566fe6809a0c83751d825a03727f242cdbe0d142c9e292725585521/ruff-0.12.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8dbbf9f25dfb501f4237ae7501d6364b76a01341c6f1b2cd6764fe449124bb2a", size = 12700451, upload-time = "2025-07-24T13:26:15.488Z" }, - { url = "https://files.pythonhosted.org/packages/24/ff/96058f6506aac0fbc0d0fc0d60b0d0bd746240a0594657a2d94ad28033ba/ruff-0.12.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c47dea6ae39421851685141ba9734767f960113d51e83fd7bb9958d5be8763a", size = 12937465, upload-time = "2025-07-24T13:26:17.808Z" }, - { url = "https://files.pythonhosted.org/packages/eb/d3/68bc5e7ab96c94b3589d1789f2dd6dd4b27b263310019529ac9be1e8f31b/ruff-0.12.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5076aa0e61e30f848846f0265c873c249d4b558105b221be1828f9f79903dc5", size = 11771136, upload-time = "2025-07-24T13:26:20.422Z" }, - { url = "https://files.pythonhosted.org/packages/52/75/7356af30a14584981cabfefcf6106dea98cec9a7af4acb5daaf4b114845f/ruff-0.12.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a5a4c7830dadd3d8c39b1cc85386e2c1e62344f20766be6f173c22fb5f72f293", size = 11601644, upload-time = "2025-07-24T13:26:22.928Z" }, - { url = "https://files.pythonhosted.org/packages/c2/67/91c71d27205871737cae11025ee2b098f512104e26ffd8656fd93d0ada0a/ruff-0.12.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:46699f73c2b5b137b9dc0fc1a190b43e35b008b398c6066ea1350cce6326adcb", size = 12478068, upload-time = "2025-07-24T13:26:26.134Z" }, - { url = "https://files.pythonhosted.org/packages/34/04/b6b00383cf2f48e8e78e14eb258942fdf2a9bf0287fbf5cdd398b749193a/ruff-0.12.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5a655a0a0d396f0f072faafc18ebd59adde8ca85fb848dc1b0d9f024b9c4d3bb", size = 12991537, upload-time = "2025-07-24T13:26:28.533Z" }, - { url = "https://files.pythonhosted.org/packages/3e/b9/053d6445dc7544fb6594785056d8ece61daae7214859ada4a152ad56b6e0/ruff-0.12.5-py3-none-win32.whl", hash = "sha256:dfeb2627c459b0b78ca2bbdc38dd11cc9a0a88bf91db982058b26ce41714ffa9", size = 11751575, upload-time = "2025-07-24T13:26:30.835Z" }, - { url = "https://files.pythonhosted.org/packages/bc/0f/ab16e8259493137598b9149734fec2e06fdeda9837e6f634f5c4e35916da/ruff-0.12.5-py3-none-win_amd64.whl", hash = "sha256:ae0d90cf5f49466c954991b9d8b953bd093c32c27608e409ae3564c63c5306a5", size = 12882273, upload-time = "2025-07-24T13:26:32.929Z" }, - { url = "https://files.pythonhosted.org/packages/00/db/c376b0661c24cf770cb8815268190668ec1330eba8374a126ceef8c72d55/ruff-0.12.5-py3-none-win_arm64.whl", hash = "sha256:48cdbfc633de2c5c37d9f090ba3b352d1576b0015bfc3bc98eaf230275b7e805", size = 11951564, upload-time = "2025-07-24T13:26:34.994Z" }, -] - -[[package]] -name = "s3transfer" -version = "0.13.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "botocore" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/05/d52bf1e65044b4e5e27d4e63e8d1579dbdec54fce685908ae09bc3720030/s3transfer-0.13.1.tar.gz", hash = "sha256:c3fdba22ba1bd367922f27ec8032d6a1cf5f10c934fb5d68cf60fd5a23d936cf", size = 150589, upload-time = "2025-07-18T19:22:42.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/4f/d073e09df851cfa251ef7840007d04db3293a0482ce607d2b993926089be/s3transfer-0.13.1-py3-none-any.whl", hash = "sha256:a981aa7429be23fe6dfc13e80e4020057cbab622b08c0315288758d67cabc724", size = 85308, upload-time = "2025-07-18T19:22:40.947Z" }, -] - -[[package]] -name = "send2trash" -version = "1.8.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/3a/aec9b02217bb79b87bbc1a21bc6abc51e3d5dcf65c30487ac96c0908c722/Send2Trash-1.8.3.tar.gz", hash = "sha256:b18e7a3966d99871aefeb00cfbcfdced55ce4871194810fc71f4aa484b953abf", size = 17394, upload-time = "2024-04-07T00:01:09.267Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/b0/4562db6223154aa4e22f939003cb92514c79f3d4dccca3444253fd17f902/Send2Trash-1.8.3-py3-none-any.whl", hash = "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9", size = 18072, upload-time = "2024-04-07T00:01:07.438Z" }, -] - -[[package]] -name = "setuptools" -version = "80.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, -] - -[[package]] -name = "shapely" -version = "2.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ca/3c/2da625233f4e605155926566c0e7ea8dda361877f48e8b1655e53456f252/shapely-2.1.1.tar.gz", hash = "sha256:500621967f2ffe9642454808009044c21e5b35db89ce69f8a2042c2ffd0e2772", size = 315422, upload-time = "2025-05-19T11:04:41.265Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/fa/f18025c95b86116dd8f1ec58cab078bd59ab51456b448136ca27463be533/shapely-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d8ccc872a632acb7bdcb69e5e78df27213f7efd195882668ffba5405497337c6", size = 1825117, upload-time = "2025-05-19T11:03:43.547Z" }, - { url = "https://files.pythonhosted.org/packages/c7/65/46b519555ee9fb851234288be7c78be11e6260995281071d13abf2c313d0/shapely-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f24f2ecda1e6c091da64bcbef8dd121380948074875bd1b247b3d17e99407099", size = 1628541, upload-time = "2025-05-19T11:03:45.162Z" }, - { url = "https://files.pythonhosted.org/packages/29/51/0b158a261df94e33505eadfe737db9531f346dfa60850945ad25fd4162f1/shapely-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45112a5be0b745b49e50f8829ce490eb67fefb0cea8d4f8ac5764bfedaa83d2d", size = 2948453, upload-time = "2025-05-19T11:03:46.681Z" }, - { url = "https://files.pythonhosted.org/packages/a9/4f/6c9bb4bd7b1a14d7051641b9b479ad2a643d5cbc382bcf5bd52fd0896974/shapely-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c10ce6f11904d65e9bbb3e41e774903c944e20b3f0b282559885302f52f224a", size = 3057029, upload-time = "2025-05-19T11:03:48.346Z" }, - { url = "https://files.pythonhosted.org/packages/89/0b/ad1b0af491d753a83ea93138eee12a4597f763ae12727968d05934fe7c78/shapely-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:61168010dfe4e45f956ffbbaf080c88afce199ea81eb1f0ac43230065df320bd", size = 3894342, upload-time = "2025-05-19T11:03:49.602Z" }, - { url = "https://files.pythonhosted.org/packages/7d/96/73232c5de0b9fdf0ec7ddfc95c43aaf928740e87d9f168bff0e928d78c6d/shapely-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cacf067cdff741cd5c56a21c52f54ece4e4dad9d311130493a791997da4a886b", size = 4056766, upload-time = "2025-05-19T11:03:51.252Z" }, - { url = "https://files.pythonhosted.org/packages/43/cc/eec3c01f754f5b3e0c47574b198f9deb70465579ad0dad0e1cef2ce9e103/shapely-2.1.1-cp310-cp310-win32.whl", hash = "sha256:23b8772c3b815e7790fb2eab75a0b3951f435bc0fce7bb146cb064f17d35ab4f", size = 1523744, upload-time = "2025-05-19T11:03:52.624Z" }, - { url = "https://files.pythonhosted.org/packages/50/fc/a7187e6dadb10b91e66a9e715d28105cde6489e1017cce476876185a43da/shapely-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:2c7b2b6143abf4fa77851cef8ef690e03feade9a0d48acd6dc41d9e0e78d7ca6", size = 1703061, upload-time = "2025-05-19T11:03:54.695Z" }, - { url = "https://files.pythonhosted.org/packages/19/97/2df985b1e03f90c503796ad5ecd3d9ed305123b64d4ccb54616b30295b29/shapely-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:587a1aa72bc858fab9b8c20427b5f6027b7cbc92743b8e2c73b9de55aa71c7a7", size = 1819368, upload-time = "2025-05-19T11:03:55.937Z" }, - { url = "https://files.pythonhosted.org/packages/56/17/504518860370f0a28908b18864f43d72f03581e2b6680540ca668f07aa42/shapely-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9fa5c53b0791a4b998f9ad84aad456c988600757a96b0a05e14bba10cebaaaea", size = 1625362, upload-time = "2025-05-19T11:03:57.06Z" }, - { url = "https://files.pythonhosted.org/packages/36/a1/9677337d729b79fce1ef3296aac6b8ef4743419086f669e8a8070eff8f40/shapely-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aabecd038841ab5310d23495253f01c2a82a3aedae5ab9ca489be214aa458aa7", size = 2999005, upload-time = "2025-05-19T11:03:58.692Z" }, - { url = "https://files.pythonhosted.org/packages/a2/17/e09357274699c6e012bbb5a8ea14765a4d5860bb658df1931c9f90d53bd3/shapely-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586f6aee1edec04e16227517a866df3e9a2e43c1f635efc32978bb3dc9c63753", size = 3108489, upload-time = "2025-05-19T11:04:00.059Z" }, - { url = "https://files.pythonhosted.org/packages/17/5d/93a6c37c4b4e9955ad40834f42b17260ca74ecf36df2e81bb14d12221b90/shapely-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b9878b9e37ad26c72aada8de0c9cfe418d9e2ff36992a1693b7f65a075b28647", size = 3945727, upload-time = "2025-05-19T11:04:01.786Z" }, - { url = "https://files.pythonhosted.org/packages/a3/1a/ad696648f16fd82dd6bfcca0b3b8fbafa7aacc13431c7fc4c9b49e481681/shapely-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9a531c48f289ba355e37b134e98e28c557ff13965d4653a5228d0f42a09aed0", size = 4109311, upload-time = "2025-05-19T11:04:03.134Z" }, - { url = "https://files.pythonhosted.org/packages/d4/38/150dd245beab179ec0d4472bf6799bf18f21b1efbef59ac87de3377dbf1c/shapely-2.1.1-cp311-cp311-win32.whl", hash = "sha256:4866de2673a971820c75c0167b1f1cd8fb76f2d641101c23d3ca021ad0449bab", size = 1522982, upload-time = "2025-05-19T11:04:05.217Z" }, - { url = "https://files.pythonhosted.org/packages/93/5b/842022c00fbb051083c1c85430f3bb55565b7fd2d775f4f398c0ba8052ce/shapely-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:20a9d79958b3d6c70d8a886b250047ea32ff40489d7abb47d01498c704557a93", size = 1703872, upload-time = "2025-05-19T11:04:06.791Z" }, - { url = "https://files.pythonhosted.org/packages/fb/64/9544dc07dfe80a2d489060791300827c941c451e2910f7364b19607ea352/shapely-2.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2827365b58bf98efb60affc94a8e01c56dd1995a80aabe4b701465d86dcbba43", size = 1833021, upload-time = "2025-05-19T11:04:08.022Z" }, - { url = "https://files.pythonhosted.org/packages/07/aa/fb5f545e72e89b6a0f04a0effda144f5be956c9c312c7d4e00dfddbddbcf/shapely-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9c551f7fa7f1e917af2347fe983f21f212863f1d04f08eece01e9c275903fad", size = 1643018, upload-time = "2025-05-19T11:04:09.343Z" }, - { url = "https://files.pythonhosted.org/packages/03/46/61e03edba81de729f09d880ce7ae5c1af873a0814206bbfb4402ab5c3388/shapely-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78dec4d4fbe7b1db8dc36de3031767e7ece5911fb7782bc9e95c5cdec58fb1e9", size = 2986417, upload-time = "2025-05-19T11:04:10.56Z" }, - { url = "https://files.pythonhosted.org/packages/1f/1e/83ec268ab8254a446b4178b45616ab5822d7b9d2b7eb6e27cf0b82f45601/shapely-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:872d3c0a7b8b37da0e23d80496ec5973c4692920b90de9f502b5beb994bbaaef", size = 3098224, upload-time = "2025-05-19T11:04:11.903Z" }, - { url = "https://files.pythonhosted.org/packages/f1/44/0c21e7717c243e067c9ef8fa9126de24239f8345a5bba9280f7bb9935959/shapely-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2e2b9125ebfbc28ecf5353511de62f75a8515ae9470521c9a693e4bb9fbe0cf1", size = 3925982, upload-time = "2025-05-19T11:04:13.224Z" }, - { url = "https://files.pythonhosted.org/packages/15/50/d3b4e15fefc103a0eb13d83bad5f65cd6e07a5d8b2ae920e767932a247d1/shapely-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4b96cea171b3d7f6786976a0520f178c42792897653ecca0c5422fb1e6946e6d", size = 4089122, upload-time = "2025-05-19T11:04:14.477Z" }, - { url = "https://files.pythonhosted.org/packages/bd/05/9a68f27fc6110baeedeeebc14fd86e73fa38738c5b741302408fb6355577/shapely-2.1.1-cp312-cp312-win32.whl", hash = "sha256:39dca52201e02996df02e447f729da97cfb6ff41a03cb50f5547f19d02905af8", size = 1522437, upload-time = "2025-05-19T11:04:16.203Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e9/a4560e12b9338842a1f82c9016d2543eaa084fce30a1ca11991143086b57/shapely-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:13d643256f81d55a50013eff6321142781cf777eb6a9e207c2c9e6315ba6044a", size = 1703479, upload-time = "2025-05-19T11:04:18.497Z" }, - { url = "https://files.pythonhosted.org/packages/71/8e/2bc836437f4b84d62efc1faddce0d4e023a5d990bbddd3c78b2004ebc246/shapely-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3004a644d9e89e26c20286d5fdc10f41b1744c48ce910bd1867fdff963fe6c48", size = 1832107, upload-time = "2025-05-19T11:04:19.736Z" }, - { url = "https://files.pythonhosted.org/packages/12/a2/12c7cae5b62d5d851c2db836eadd0986f63918a91976495861f7c492f4a9/shapely-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1415146fa12d80a47d13cfad5310b3c8b9c2aa8c14a0c845c9d3d75e77cb54f6", size = 1642355, upload-time = "2025-05-19T11:04:21.035Z" }, - { url = "https://files.pythonhosted.org/packages/5b/7e/6d28b43d53fea56de69c744e34c2b999ed4042f7a811dc1bceb876071c95/shapely-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21fcab88b7520820ec16d09d6bea68652ca13993c84dffc6129dc3607c95594c", size = 2968871, upload-time = "2025-05-19T11:04:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/dd/87/1017c31e52370b2b79e4d29e07cbb590ab9e5e58cf7e2bdfe363765d6251/shapely-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5ce6a5cc52c974b291237a96c08c5592e50f066871704fb5b12be2639d9026a", size = 3080830, upload-time = "2025-05-19T11:04:23.997Z" }, - { url = "https://files.pythonhosted.org/packages/1d/fe/f4a03d81abd96a6ce31c49cd8aaba970eaaa98e191bd1e4d43041e57ae5a/shapely-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:04e4c12a45a1d70aeb266618d8cf81a2de9c4df511b63e105b90bfdfb52146de", size = 3908961, upload-time = "2025-05-19T11:04:25.702Z" }, - { url = "https://files.pythonhosted.org/packages/ef/59/7605289a95a6844056a2017ab36d9b0cb9d6a3c3b5317c1f968c193031c9/shapely-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6ca74d851ca5264aae16c2b47e96735579686cb69fa93c4078070a0ec845b8d8", size = 4079623, upload-time = "2025-05-19T11:04:27.171Z" }, - { url = "https://files.pythonhosted.org/packages/bc/4d/9fea036eff2ef4059d30247128b2d67aaa5f0b25e9fc27e1d15cc1b84704/shapely-2.1.1-cp313-cp313-win32.whl", hash = "sha256:fd9130501bf42ffb7e0695b9ea17a27ae8ce68d50b56b6941c7f9b3d3453bc52", size = 1521916, upload-time = "2025-05-19T11:04:28.405Z" }, - { url = "https://files.pythonhosted.org/packages/12/d9/6d13b8957a17c95794f0c4dfb65ecd0957e6c7131a56ce18d135c1107a52/shapely-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:ab8d878687b438a2f4c138ed1a80941c6ab0029e0f4c785ecfe114413b498a97", size = 1702746, upload-time = "2025-05-19T11:04:29.643Z" }, - { url = "https://files.pythonhosted.org/packages/60/36/b1452e3e7f35f5f6454d96f3be6e2bb87082720ff6c9437ecc215fa79be0/shapely-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0c062384316a47f776305ed2fa22182717508ffdeb4a56d0ff4087a77b2a0f6d", size = 1833482, upload-time = "2025-05-19T11:04:30.852Z" }, - { url = "https://files.pythonhosted.org/packages/ce/ca/8e6f59be0718893eb3e478141285796a923636dc8f086f83e5b0ec0036d0/shapely-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4ecf6c196b896e8f1360cc219ed4eee1c1e5f5883e505d449f263bd053fb8c05", size = 1642256, upload-time = "2025-05-19T11:04:32.068Z" }, - { url = "https://files.pythonhosted.org/packages/ab/78/0053aea449bb1d4503999525fec6232f049abcdc8df60d290416110de943/shapely-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb00070b4c4860f6743c600285109c273cca5241e970ad56bb87bef0be1ea3a0", size = 3016614, upload-time = "2025-05-19T11:04:33.7Z" }, - { url = "https://files.pythonhosted.org/packages/ee/53/36f1b1de1dfafd1b457dcbafa785b298ce1b8a3e7026b79619e708a245d5/shapely-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d14a9afa5fa980fbe7bf63706fdfb8ff588f638f145a1d9dbc18374b5b7de913", size = 3093542, upload-time = "2025-05-19T11:04:34.952Z" }, - { url = "https://files.pythonhosted.org/packages/b9/bf/0619f37ceec6b924d84427c88835b61f27f43560239936ff88915c37da19/shapely-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b640e390dabde790e3fb947198b466e63223e0a9ccd787da5f07bcb14756c28d", size = 3945961, upload-time = "2025-05-19T11:04:36.32Z" }, - { url = "https://files.pythonhosted.org/packages/93/c9/20ca4afeb572763b07a7997f00854cb9499df6af85929e93012b189d8917/shapely-2.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:69e08bf9697c1b73ec6aa70437db922bafcea7baca131c90c26d59491a9760f9", size = 4089514, upload-time = "2025-05-19T11:04:37.683Z" }, - { url = "https://files.pythonhosted.org/packages/33/6a/27036a5a560b80012a544366bceafd491e8abb94a8db14047b5346b5a749/shapely-2.1.1-cp313-cp313t-win32.whl", hash = "sha256:ef2d09d5a964cc90c2c18b03566cf918a61c248596998a0301d5b632beadb9db", size = 1540607, upload-time = "2025-05-19T11:04:38.925Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f1/5e9b3ba5c7aa7ebfaf269657e728067d16a7c99401c7973ddf5f0cf121bd/shapely-2.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8cb8f17c377260452e9d7720eeaf59082c5f8ea48cf104524d953e5d36d4bdb7", size = 1723061, upload-time = "2025-05-19T11:04:40.082Z" }, -] - -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, -] - -[[package]] -name = "snowballstemmer" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, -] - -[[package]] -name = "soupsieve" -version = "2.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a", size = 103418, upload-time = "2025-04-20T18:50:08.518Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677, upload-time = "2025-04-20T18:50:07.196Z" }, -] - -[[package]] -name = "sphinx" -version = "8.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "alabaster" }, - { name = "babel" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "docutils" }, - { name = "imagesize" }, - { name = "jinja2" }, - { name = "packaging" }, - { name = "pygments" }, - { name = "requests" }, - { name = "snowballstemmer" }, - { name = "sphinxcontrib-applehelp" }, - { name = "sphinxcontrib-devhelp" }, - { name = "sphinxcontrib-htmlhelp" }, - { name = "sphinxcontrib-jsmath" }, - { name = "sphinxcontrib-qthelp" }, - { name = "sphinxcontrib-serializinghtml" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125, upload-time = "2024-10-13T20:27:10.448Z" }, -] - -[[package]] -name = "sphinx-autobuild" -version = "2024.10.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama" }, - { name = "sphinx" }, - { name = "starlette" }, - { name = "uvicorn" }, - { name = "watchfiles" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a5/2c/155e1de2c1ba96a72e5dba152c509a8b41e047ee5c2def9e9f0d812f8be7/sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1", size = 14023, upload-time = "2024-10-02T23:15:30.172Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/c0/eba125db38c84d3c74717008fd3cb5000b68cd7e2cbafd1349c6a38c3d3b/sphinx_autobuild-2024.10.3-py3-none-any.whl", hash = "sha256:158e16c36f9d633e613c9aaf81c19b0fc458ca78b112533b20dafcda430d60fa", size = 11908, upload-time = "2024-10-02T23:15:28.739Z" }, -] - -[[package]] -name = "sphinx-design" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2b/69/b34e0cb5336f09c6866d53b4a19d76c227cdec1bbc7ac4de63ca7d58c9c7/sphinx_design-0.6.1.tar.gz", hash = "sha256:b44eea3719386d04d765c1a8257caca2b3e6f8421d7b3a5e742c0fd45f84e632", size = 2193689, upload-time = "2024-08-02T13:48:44.277Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/43/65c0acbd8cc6f50195a3a1fc195c404988b15c67090e73c7a41a9f57d6bd/sphinx_design-0.6.1-py3-none-any.whl", hash = "sha256:b11f37db1a802a183d61b159d9a202314d4d2fe29c163437001324fe2f19549c", size = 2215338, upload-time = "2024-08-02T13:48:42.106Z" }, -] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, -] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, -] - -[[package]] -name = "sphinxcontrib-fulltoc" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/d1297db9b75650681e5429e92e13df139ee6b64303ff1b2eea4ebd32c0a9/sphinxcontrib-fulltoc-1.2.0.tar.gz", hash = "sha256:c845d62fc467f3135d4543e9f10e13ef91852683bd1c90fd19d07f9d36757cd9", size = 13752, upload-time = "2017-04-10T14:12:57.75Z" } - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, -] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, -] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, -] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, -] - -[[package]] -name = "stack-data" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asttokens" }, - { name = "executing" }, - { name = "pure-eval" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, -] - -[[package]] -name = "starlette" -version = "0.50.0" +name = "referencing" +version = "0.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio" }, + { name = "attrs" }, + { name = "rpds-py" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, -] - -[[package]] -name = "stevedore" -version = "5.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pbr" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/3f/13cacea96900bbd31bb05c6b74135f85d15564fc583802be56976c940470/stevedore-5.4.1.tar.gz", hash = "sha256:3135b5ae50fe12816ef291baff420acb727fcd356106e3e9cbfa9e5985cd6f4b", size = 513858, upload-time = "2025-02-20T14:03:57.285Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/45/8c4ebc0c460e6ec38e62ab245ad3c7fc10b210116cea7c16d61602aa9558/stevedore-5.4.1-py3-none-any.whl", hash = "sha256:d10a31c7b86cba16c1f6e8d15416955fc797052351a56af15e608ad20811fcfe", size = 49533, upload-time = "2025-02-20T14:03:55.849Z" }, -] - -[[package]] -name = "tabulate" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036 } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766 }, ] [[package]] -name = "terminado" -version = "0.18.1" +name = "requests" +version = "2.32.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess", marker = "os_name != 'nt'" }, - { name = "pywinpty", marker = "os_name == 'nt'" }, - { name = "tornado" }, + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701, upload-time = "2024-03-12T14:34:39.026Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154, upload-time = "2024-03-12T14:34:36.569Z" }, + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 }, ] [[package]] -name = "tinycss2" -version = "1.4.0" +name = "requests-mock" +version = "1.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "webencodings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085, upload-time = "2024-10-24T14:58:29.895Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610, upload-time = "2024-10-24T14:58:28.029Z" }, -] - -[[package]] -name = "tomli" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, - { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, - { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, - { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, - { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, - { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, - { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, - { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, - { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, - { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, - { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, - { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, - { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, - { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, - { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, - { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, - { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, - { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, - { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, - { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, - { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, - { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, - { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, - { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, - { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, -] - -[[package]] -name = "toolz" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/0b/d80dfa675bf592f636d1ea0b835eab4ec8df6e9415d8cfd766df54456123/toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02", size = 66790, upload-time = "2024-10-04T16:17:04.001Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/98/eb27cc78ad3af8e302c9d8ff4977f5026676e130d28dd7578132a457170c/toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236", size = 56383, upload-time = "2024-10-04T16:17:01.533Z" }, -] - -[[package]] -name = "tornado" -version = "6.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/89/c72771c81d25d53fe33e3dca61c233b665b2780f21820ba6fd2c6793c12b/tornado-6.5.1.tar.gz", hash = "sha256:84ceece391e8eb9b2b95578db65e920d2a61070260594819589609ba9bc6308c", size = 509934, upload-time = "2025-05-22T18:15:38.788Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/89/f4532dee6843c9e0ebc4e28d4be04c67f54f60813e4bf73d595fe7567452/tornado-6.5.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d50065ba7fd11d3bd41bcad0825227cc9a95154bad83239357094c36708001f7", size = 441948, upload-time = "2025-05-22T18:15:20.862Z" }, - { url = "https://files.pythonhosted.org/packages/15/9a/557406b62cffa395d18772e0cdcf03bed2fff03b374677348eef9f6a3792/tornado-6.5.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9e9ca370f717997cb85606d074b0e5b247282cf5e2e1611568b8821afe0342d6", size = 440112, upload-time = "2025-05-22T18:15:22.591Z" }, - { url = "https://files.pythonhosted.org/packages/55/82/7721b7319013a3cf881f4dffa4f60ceff07b31b394e459984e7a36dc99ec/tornado-6.5.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b77e9dfa7ed69754a54c89d82ef746398be82f749df69c4d3abe75c4d1ff4888", size = 443672, upload-time = "2025-05-22T18:15:24.027Z" }, - { url = "https://files.pythonhosted.org/packages/7d/42/d11c4376e7d101171b94e03cef0cbce43e823ed6567ceda571f54cf6e3ce/tornado-6.5.1-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:253b76040ee3bab8bcf7ba9feb136436a3787208717a1fb9f2c16b744fba7331", size = 443019, upload-time = "2025-05-22T18:15:25.735Z" }, - { url = "https://files.pythonhosted.org/packages/7d/f7/0c48ba992d875521ac761e6e04b0a1750f8150ae42ea26df1852d6a98942/tornado-6.5.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:308473f4cc5a76227157cdf904de33ac268af770b2c5f05ca6c1161d82fdd95e", size = 443252, upload-time = "2025-05-22T18:15:27.499Z" }, - { url = "https://files.pythonhosted.org/packages/89/46/d8d7413d11987e316df4ad42e16023cd62666a3c0dfa1518ffa30b8df06c/tornado-6.5.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:caec6314ce8a81cf69bd89909f4b633b9f523834dc1a352021775d45e51d9401", size = 443930, upload-time = "2025-05-22T18:15:29.299Z" }, - { url = "https://files.pythonhosted.org/packages/78/b2/f8049221c96a06df89bed68260e8ca94beca5ea532ffc63b1175ad31f9cc/tornado-6.5.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:13ce6e3396c24e2808774741331638ee6c2f50b114b97a55c5b442df65fd9692", size = 443351, upload-time = "2025-05-22T18:15:31.038Z" }, - { url = "https://files.pythonhosted.org/packages/76/ff/6a0079e65b326cc222a54720a748e04a4db246870c4da54ece4577bfa702/tornado-6.5.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5cae6145f4cdf5ab24744526cc0f55a17d76f02c98f4cff9daa08ae9a217448a", size = 443328, upload-time = "2025-05-22T18:15:32.426Z" }, - { url = "https://files.pythonhosted.org/packages/49/18/e3f902a1d21f14035b5bc6246a8c0f51e0eef562ace3a2cea403c1fb7021/tornado-6.5.1-cp39-abi3-win32.whl", hash = "sha256:e0a36e1bc684dca10b1aa75a31df8bdfed656831489bc1e6a6ebed05dc1ec365", size = 444396, upload-time = "2025-05-22T18:15:34.205Z" }, - { url = "https://files.pythonhosted.org/packages/7b/09/6526e32bf1049ee7de3bebba81572673b19a2a8541f795d887e92af1a8bc/tornado-6.5.1-cp39-abi3-win_amd64.whl", hash = "sha256:908e7d64567cecd4c2b458075589a775063453aeb1d2a1853eedb806922f568b", size = 444840, upload-time = "2025-05-22T18:15:36.1Z" }, - { url = "https://files.pythonhosted.org/packages/55/a7/535c44c7bea4578e48281d83c615219f3ab19e6abc67625ef637c73987be/tornado-6.5.1-cp39-abi3-win_arm64.whl", hash = "sha256:02420a0eb7bf617257b9935e2b754d1b63897525d8a289c9d65690d580b4dcf7", size = 443596, upload-time = "2025-05-22T18:15:37.433Z" }, -] - -[[package]] -name = "traitlets" -version = "5.14.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, + { name = "requests" }, ] - -[[package]] -name = "types-html5lib" -version = "1.1.11.20250708" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/3b/1f5ba4358cfc1421cced5cdb9d2b08b4b99e4f9a41da88ce079f6d1a7bf1/types_html5lib-1.1.11.20250708.tar.gz", hash = "sha256:24321720fdbac71cee50d5a4bec9b7448495b7217974cffe3fcf1ede4eef7afe", size = 16799, upload-time = "2025-07-08T03:13:53.14Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/32/587625f91f9a0a3d84688bf9cfc4b2480a7e8ec327cefd0ff2ac891fd2cf/requests-mock-1.12.1.tar.gz", hash = "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401", size = 60901 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/50/5fc23cf647eee23acdd337c8150861d39980cf11f33dd87f78e87d2a4bad/types_html5lib-1.1.11.20250708-py3-none-any.whl", hash = "sha256:bb898066b155de7081cb182179e2ded31b9e0e234605e2cb46536894e68a6954", size = 22913, upload-time = "2025-07-08T03:13:52.098Z" }, + { url = "https://files.pythonhosted.org/packages/97/ec/889fbc557727da0c34a33850950310240f2040f3b1955175fdb2b36a8910/requests_mock-1.12.1-py2.py3-none-any.whl", hash = "sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563", size = 27695 }, ] [[package]] -name = "types-jsonschema" -version = "4.25.0.20250720" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/00/f3/7dba3ea10a52f57f6bbc7905b34ae0b04fd1487448c089c96aab710d745d/types_jsonschema-4.25.0.20250720.tar.gz", hash = "sha256:765a3b6144798fe3161fd8cbe570a756ed3e8c0e5adb7c09693eb49faad39dbd", size = 15470, upload-time = "2025-07-20T03:29:35.208Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/b6/e9ca8ba6803992fd5a8a83a21ae61e7d050cc24b51d33215860510b561d9/types_jsonschema-4.25.0.20250720-py3-none-any.whl", hash = "sha256:7d7897c715310d8bf9ae27a2cedba78bbb09e4cad83ce06d2aa79b73a88941df", size = 15769, upload-time = "2025-07-20T03:29:34.157Z" }, +name = "rpds-py" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086 }, + { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053 }, + { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763 }, + { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951 }, + { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622 }, + { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492 }, + { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080 }, + { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680 }, + { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589 }, + { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289 }, + { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737 }, + { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120 }, + { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782 }, + { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463 }, + { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868 }, + { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887 }, + { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904 }, + { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945 }, + { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783 }, + { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021 }, + { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589 }, + { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025 }, + { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895 }, + { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799 }, + { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731 }, + { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027 }, + { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020 }, + { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139 }, + { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224 }, + { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645 }, + { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443 }, + { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375 }, + { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850 }, + { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812 }, + { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841 }, + { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149 }, + { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843 }, + { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507 }, + { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949 }, + { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790 }, + { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217 }, + { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806 }, + { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341 }, + { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768 }, + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099 }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192 }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080 }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841 }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670 }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005 }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112 }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049 }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661 }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606 }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126 }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371 }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298 }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604 }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391 }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868 }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747 }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795 }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330 }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194 }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340 }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765 }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834 }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470 }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630 }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148 }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030 }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570 }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532 }, ] [[package]] -name = "types-orjson" -version = "3.6.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/97/3f78cfdf663e5668e8b490d8c84d6de089d2d8dbad935f0dc43555d52a90/types-orjson-3.6.2.tar.gz", hash = "sha256:cf9afcc79a86325c7aff251790338109ed6f6b1bab09d2d4262dd18c85a3c638", size = 1999, upload-time = "2022-01-07T11:31:10.518Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/55/84/b34abd2d08381c5113e475908a1d79d27dc9a15f669213cee4ca03d1a891/types_orjson-3.6.2-py3-none-any.whl", hash = "sha256:22ee9a79236b6b0bfb35a0684eded62ad930a88a56797fa3c449b026cf7dbfe4", size = 2224, upload-time = "2022-01-07T11:31:09.271Z" }, +name = "ruff" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/39/5cee96809fbca590abea6b46c6d1c586b49663d1d2830a751cc8fc42c666/ruff-0.15.0.tar.gz", hash = "sha256:6bdea47cdbea30d40f8f8d7d69c0854ba7c15420ec75a26f463290949d7f7e9a", size = 4524893 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/88/3fd1b0aa4b6330d6aaa63a285bc96c9f71970351579152d231ed90914586/ruff-0.15.0-py3-none-linux_armv6l.whl", hash = "sha256:aac4ebaa612a82b23d45964586f24ae9bc23ca101919f5590bdb368d74ad5455", size = 10354332 }, + { url = "https://files.pythonhosted.org/packages/72/f6/62e173fbb7eb75cc29fe2576a1e20f0a46f671a2587b5f604bfb0eaf5f6f/ruff-0.15.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dcd4be7cc75cfbbca24a98d04d0b9b36a270d0833241f776b788d59f4142b14d", size = 10767189 }, + { url = "https://files.pythonhosted.org/packages/99/e4/968ae17b676d1d2ff101d56dc69cf333e3a4c985e1ec23803df84fc7bf9e/ruff-0.15.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d747e3319b2bce179c7c1eaad3d884dc0a199b5f4d5187620530adf9105268ce", size = 10075384 }, + { url = "https://files.pythonhosted.org/packages/a2/bf/9843c6044ab9e20af879c751487e61333ca79a2c8c3058b15722386b8cae/ruff-0.15.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:650bd9c56ae03102c51a5e4b554d74d825ff3abe4db22b90fd32d816c2e90621", size = 10481363 }, + { url = "https://files.pythonhosted.org/packages/55/d9/4ada5ccf4cd1f532db1c8d44b6f664f2208d3d93acbeec18f82315e15193/ruff-0.15.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6664b7eac559e3048223a2da77769c2f92b43a6dfd4720cef42654299a599c9", size = 10187736 }, + { url = "https://files.pythonhosted.org/packages/86/e2/f25eaecd446af7bb132af0a1d5b135a62971a41f5366ff41d06d25e77a91/ruff-0.15.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f811f97b0f092b35320d1556f3353bf238763420ade5d9e62ebd2b73f2ff179", size = 10968415 }, + { url = "https://files.pythonhosted.org/packages/e7/dc/f06a8558d06333bf79b497d29a50c3a673d9251214e0d7ec78f90b30aa79/ruff-0.15.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761ec0a66680fab6454236635a39abaf14198818c8cdf691e036f4bc0f406b2d", size = 11809643 }, + { url = "https://files.pythonhosted.org/packages/dd/45/0ece8db2c474ad7df13af3a6d50f76e22a09d078af63078f005057ca59eb/ruff-0.15.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:940f11c2604d317e797b289f4f9f3fa5555ffe4fb574b55ed006c3d9b6f0eb78", size = 11234787 }, + { url = "https://files.pythonhosted.org/packages/8a/d9/0e3a81467a120fd265658d127db648e4d3acfe3e4f6f5d4ea79fac47e587/ruff-0.15.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcbca3d40558789126da91d7ef9a7c87772ee107033db7191edefa34e2c7f1b4", size = 11112797 }, + { url = "https://files.pythonhosted.org/packages/b2/cb/8c0b3b0c692683f8ff31351dfb6241047fa873a4481a76df4335a8bff716/ruff-0.15.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9a121a96db1d75fa3eb39c4539e607f628920dd72ff1f7c5ee4f1b768ac62d6e", size = 11033133 }, + { url = "https://files.pythonhosted.org/packages/f8/5e/23b87370cf0f9081a8c89a753e69a4e8778805b8802ccfe175cc410e50b9/ruff-0.15.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5298d518e493061f2eabd4abd067c7e4fb89e2f63291c94332e35631c07c3662", size = 10442646 }, + { url = "https://files.pythonhosted.org/packages/e1/9a/3c94de5ce642830167e6d00b5c75aacd73e6347b4c7fc6828699b150a5ee/ruff-0.15.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afb6e603d6375ff0d6b0cee563fa21ab570fd15e65c852cb24922cef25050cf1", size = 10195750 }, + { url = "https://files.pythonhosted.org/packages/30/15/e396325080d600b436acc970848d69df9c13977942fb62bb8722d729bee8/ruff-0.15.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77e515f6b15f828b94dc17d2b4ace334c9ddb7d9468c54b2f9ed2b9c1593ef16", size = 10676120 }, + { url = "https://files.pythonhosted.org/packages/8d/c9/229a23d52a2983de1ad0fb0ee37d36e0257e6f28bfd6b498ee2c76361874/ruff-0.15.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6f6e80850a01eb13b3e42ee0ebdf6e4497151b48c35051aab51c101266d187a3", size = 11201636 }, + { url = "https://files.pythonhosted.org/packages/6f/b0/69adf22f4e24f3677208adb715c578266842e6e6a3cc77483f48dd999ede/ruff-0.15.0-py3-none-win32.whl", hash = "sha256:238a717ef803e501b6d51e0bdd0d2c6e8513fe9eec14002445134d3907cd46c3", size = 10465945 }, + { url = "https://files.pythonhosted.org/packages/51/ad/f813b6e2c97e9b4598be25e94a9147b9af7e60523b0cb5d94d307c15229d/ruff-0.15.0-py3-none-win_amd64.whl", hash = "sha256:dd5e4d3301dc01de614da3cdffc33d4b1b96fb89e45721f1598e5532ccf78b18", size = 11564657 }, + { url = "https://files.pythonhosted.org/packages/f6/b0/2d823f6e77ebe560f4e397d078487e8d52c1516b331e3521bc75db4272ca/ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a", size = 10865753 }, ] [[package]] -name = "types-python-dateutil" -version = "2.9.0.20250708" +name = "six" +version = "1.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c9/95/6bdde7607da2e1e99ec1c1672a759d42f26644bbacf939916e086db34870/types_python_dateutil-2.9.0.20250708.tar.gz", hash = "sha256:ccdbd75dab2d6c9696c350579f34cffe2c281e4c5f27a585b2a2438dd1d5c8ab", size = 15834, upload-time = "2025-07-08T03:14:03.382Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } wheels = [ - { url = "https://files.pythonhosted.org/packages/72/52/43e70a8e57fefb172c22a21000b03ebcc15e47e97f5cb8495b9c2832efb4/types_python_dateutil-2.9.0.20250708-py3-none-any.whl", hash = "sha256:4d6d0cc1cc4d24a2dc3816024e502564094497b713f7befda4d5bc7a8e3fd21f", size = 17724, upload-time = "2025-07-08T03:14:02.593Z" }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, ] [[package]] -name = "types-urllib3" -version = "1.26.25.14" +name = "tabulate" +version = "0.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/73/de/b9d7a68ad39092368fb21dd6194b362b98a1daeea5dcfef5e1adb5031c7e/types-urllib3-1.26.25.14.tar.gz", hash = "sha256:229b7f577c951b8c1b92c1bc2b2fdb0b49847bd2af6d1cc2a2e3dd340f3bda8f", size = 11239, upload-time = "2023-07-20T15:19:31.307Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/7b/3fc711b2efea5e85a7a0bbfe269ea944aa767bbba5ec52f9ee45d362ccf3/types_urllib3-1.26.25.14-py3-none-any.whl", hash = "sha256:9683bbb7fb72e32bfe9d2be6e04875fbe1b3eeec3cbb4ea231435aa7fd6b4f0e", size = 15377, upload-time = "2023-07-20T15:19:30.379Z" }, + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, ] [[package]] name = "typing-extensions" -version = "4.14.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, -] - -[[package]] -name = "typing-inspection" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, -] - -[[package]] -name = "tzdata" -version = "2025.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, -] - -[[package]] -name = "uri-template" -version = "1.3.0" +version = "4.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678, upload-time = "2023-06-21T01:49:05.374Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140, upload-time = "2023-06-21T01:49:03.467Z" }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, ] [[package]] name = "urllib3" version = "2.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.35.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, -] - -[[package]] -name = "vcrpy" -version = "5.1.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12' and platform_python_implementation == 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", - "python_full_version < '3.11' and platform_python_implementation == 'PyPy'", -] -dependencies = [ - { name = "pyyaml", marker = "platform_python_implementation == 'PyPy'" }, - { name = "wrapt", marker = "platform_python_implementation == 'PyPy'" }, - { name = "yarl", marker = "platform_python_implementation == 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a5/ea/a166a3cce4ac5958ba9bbd9768acdb1ba38ae17ff7986da09fa5b9dbc633/vcrpy-5.1.0.tar.gz", hash = "sha256:bbf1532f2618a04f11bce2a99af3a9647a32c880957293ff91e0a5f187b6b3d2", size = 84576, upload-time = "2023-07-31T03:19:32.231Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/5b/3f70bcb279ad30026cc4f1df0a0491a0205a24dddd88301f396c485de9e7/vcrpy-5.1.0-py2.py3-none-any.whl", hash = "sha256:605e7b7a63dcd940db1df3ab2697ca7faf0e835c0852882142bafb19649d599e", size = 41969, upload-time = "2023-07-31T03:19:30.128Z" }, + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584 }, ] [[package]] name = "vcrpy" -version = "7.0.0" +version = "8.1.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12' and platform_python_implementation != 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", - "python_full_version < '3.11' and platform_python_implementation != 'PyPy'", -] dependencies = [ - { name = "pyyaml", marker = "platform_python_implementation != 'PyPy'" }, - { name = "urllib3", marker = "platform_python_implementation != 'PyPy'" }, - { name = "wrapt", marker = "platform_python_implementation != 'PyPy'" }, - { name = "yarl", marker = "platform_python_implementation != 'PyPy'" }, + { name = "pyyaml" }, + { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/d3/856e06184d4572aada1dd559ddec3bedc46df1f2edc5ab2c91121a2cccdb/vcrpy-7.0.0.tar.gz", hash = "sha256:176391ad0425edde1680c5b20738ea3dc7fb942520a48d2993448050986b3a50", size = 85502, upload-time = "2024-12-31T00:07:57.894Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/07/bcfd5ebd7cb308026ab78a353e091bd699593358be49197d39d004e5ad83/vcrpy-8.1.1.tar.gz", hash = "sha256:58e3053e33b423f3594031cb758c3f4d1df931307f1e67928e30cf352df7709f", size = 85770 } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/5d/1f15b252890c968d42b348d1e9b0aa12d5bf3e776704178ec37cceccdb63/vcrpy-7.0.0-py2.py3-none-any.whl", hash = "sha256:55791e26c18daa363435054d8b35bd41a4ac441b6676167635d1b37a71dbe124", size = 42321, upload-time = "2024-12-31T00:07:55.277Z" }, + { url = "https://files.pythonhosted.org/packages/3a/d7/f79b05a5d728f8786876a7d75dfb0c5cae27e428081b2d60152fb52f155f/vcrpy-8.1.1-py3-none-any.whl", hash = "sha256:2d16f31ad56493efb6165182dd99767207031b0da3f68b18f975545ede8ac4b9", size = 42445 }, ] [[package]] @@ -4442,500 +1129,104 @@ dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/aa/a3/4d310fa5f00863544e1d0f4de93bddec248499ccf97d4791bc3122c9d4f3/virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba", size = 6032239, upload-time = "2026-01-09T18:21:01.296Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/2a/dc2228b2888f51192c7dc766106cd475f1b768c10caaf9727659726f7391/virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f", size = 6008258, upload-time = "2026-01-09T18:20:59.425Z" }, -] - -[[package]] -name = "watchfiles" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406, upload-time = "2025-06-15T19:06:59.42Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/dd/579d1dc57f0f895426a1211c4ef3b0cb37eb9e642bb04bdcd962b5df206a/watchfiles-1.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:27f30e14aa1c1e91cb653f03a63445739919aef84c8d2517997a83155e7a2fcc", size = 405757, upload-time = "2025-06-15T19:04:51.058Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a0/7a0318cd874393344d48c34d53b3dd419466adf59a29ba5b51c88dd18b86/watchfiles-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3366f56c272232860ab45c77c3ca7b74ee819c8e1f6f35a7125556b198bbc6df", size = 397511, upload-time = "2025-06-15T19:04:52.79Z" }, - { url = "https://files.pythonhosted.org/packages/06/be/503514656d0555ec2195f60d810eca29b938772e9bfb112d5cd5ad6f6a9e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8412eacef34cae2836d891836a7fff7b754d6bcac61f6c12ba5ca9bc7e427b68", size = 450739, upload-time = "2025-06-15T19:04:54.203Z" }, - { url = "https://files.pythonhosted.org/packages/4e/0d/a05dd9e5f136cdc29751816d0890d084ab99f8c17b86f25697288ca09bc7/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df670918eb7dd719642e05979fc84704af913d563fd17ed636f7c4783003fdcc", size = 458106, upload-time = "2025-06-15T19:04:55.607Z" }, - { url = "https://files.pythonhosted.org/packages/f1/fa/9cd16e4dfdb831072b7ac39e7bea986e52128526251038eb481effe9f48e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7642b9bc4827b5518ebdb3b82698ada8c14c7661ddec5fe719f3e56ccd13c97", size = 484264, upload-time = "2025-06-15T19:04:57.009Z" }, - { url = "https://files.pythonhosted.org/packages/32/04/1da8a637c7e2b70e750a0308e9c8e662ada0cca46211fa9ef24a23937e0b/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:199207b2d3eeaeb80ef4411875a6243d9ad8bc35b07fc42daa6b801cc39cc41c", size = 597612, upload-time = "2025-06-15T19:04:58.409Z" }, - { url = "https://files.pythonhosted.org/packages/30/01/109f2762e968d3e58c95731a206e5d7d2a7abaed4299dd8a94597250153c/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a479466da6db5c1e8754caee6c262cd373e6e6c363172d74394f4bff3d84d7b5", size = 477242, upload-time = "2025-06-15T19:04:59.786Z" }, - { url = "https://files.pythonhosted.org/packages/b5/b8/46f58cf4969d3b7bc3ca35a98e739fa4085b0657a1540ccc29a1a0bc016f/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:935f9edd022ec13e447e5723a7d14456c8af254544cefbc533f6dd276c9aa0d9", size = 453148, upload-time = "2025-06-15T19:05:01.103Z" }, - { url = "https://files.pythonhosted.org/packages/a5/cd/8267594263b1770f1eb76914940d7b2d03ee55eca212302329608208e061/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8076a5769d6bdf5f673a19d51da05fc79e2bbf25e9fe755c47595785c06a8c72", size = 626574, upload-time = "2025-06-15T19:05:02.582Z" }, - { url = "https://files.pythonhosted.org/packages/a1/2f/7f2722e85899bed337cba715723e19185e288ef361360718973f891805be/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86b1e28d4c37e89220e924305cd9f82866bb0ace666943a6e4196c5df4d58dcc", size = 624378, upload-time = "2025-06-15T19:05:03.719Z" }, - { url = "https://files.pythonhosted.org/packages/bf/20/64c88ec43d90a568234d021ab4b2a6f42a5230d772b987c3f9c00cc27b8b/watchfiles-1.1.0-cp310-cp310-win32.whl", hash = "sha256:d1caf40c1c657b27858f9774d5c0e232089bca9cb8ee17ce7478c6e9264d2587", size = 279829, upload-time = "2025-06-15T19:05:04.822Z" }, - { url = "https://files.pythonhosted.org/packages/39/5c/a9c1ed33de7af80935e4eac09570de679c6e21c07070aa99f74b4431f4d6/watchfiles-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a89c75a5b9bc329131115a409d0acc16e8da8dfd5867ba59f1dd66ae7ea8fa82", size = 292192, upload-time = "2025-06-15T19:05:06.348Z" }, - { url = "https://files.pythonhosted.org/packages/8b/78/7401154b78ab484ccaaeef970dc2af0cb88b5ba8a1b415383da444cdd8d3/watchfiles-1.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2", size = 405751, upload-time = "2025-06-15T19:05:07.679Z" }, - { url = "https://files.pythonhosted.org/packages/76/63/e6c3dbc1f78d001589b75e56a288c47723de28c580ad715eb116639152b5/watchfiles-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c", size = 397313, upload-time = "2025-06-15T19:05:08.764Z" }, - { url = "https://files.pythonhosted.org/packages/6c/a2/8afa359ff52e99af1632f90cbf359da46184207e893a5f179301b0c8d6df/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d", size = 450792, upload-time = "2025-06-15T19:05:09.869Z" }, - { url = "https://files.pythonhosted.org/packages/1d/bf/7446b401667f5c64972a57a0233be1104157fc3abf72c4ef2666c1bd09b2/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7", size = 458196, upload-time = "2025-06-15T19:05:11.91Z" }, - { url = "https://files.pythonhosted.org/packages/58/2f/501ddbdfa3fa874ea5597c77eeea3d413579c29af26c1091b08d0c792280/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c", size = 484788, upload-time = "2025-06-15T19:05:13.373Z" }, - { url = "https://files.pythonhosted.org/packages/61/1e/9c18eb2eb5c953c96bc0e5f626f0e53cfef4bd19bd50d71d1a049c63a575/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575", size = 597879, upload-time = "2025-06-15T19:05:14.725Z" }, - { url = "https://files.pythonhosted.org/packages/8b/6c/1467402e5185d89388b4486745af1e0325007af0017c3384cc786fff0542/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8", size = 477447, upload-time = "2025-06-15T19:05:15.775Z" }, - { url = "https://files.pythonhosted.org/packages/2b/a1/ec0a606bde4853d6c4a578f9391eeb3684a9aea736a8eb217e3e00aa89a1/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f", size = 453145, upload-time = "2025-06-15T19:05:17.17Z" }, - { url = "https://files.pythonhosted.org/packages/90/b9/ef6f0c247a6a35d689fc970dc7f6734f9257451aefb30def5d100d6246a5/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4", size = 626539, upload-time = "2025-06-15T19:05:18.557Z" }, - { url = "https://files.pythonhosted.org/packages/34/44/6ffda5537085106ff5aaa762b0d130ac6c75a08015dd1621376f708c94de/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d", size = 624472, upload-time = "2025-06-15T19:05:19.588Z" }, - { url = "https://files.pythonhosted.org/packages/c3/e3/71170985c48028fa3f0a50946916a14055e741db11c2e7bc2f3b61f4d0e3/watchfiles-1.1.0-cp311-cp311-win32.whl", hash = "sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2", size = 279348, upload-time = "2025-06-15T19:05:20.856Z" }, - { url = "https://files.pythonhosted.org/packages/89/1b/3e39c68b68a7a171070f81fc2561d23ce8d6859659406842a0e4bebf3bba/watchfiles-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12", size = 292607, upload-time = "2025-06-15T19:05:21.937Z" }, - { url = "https://files.pythonhosted.org/packages/61/9f/2973b7539f2bdb6ea86d2c87f70f615a71a1fc2dba2911795cea25968aea/watchfiles-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a", size = 285056, upload-time = "2025-06-15T19:05:23.12Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b8/858957045a38a4079203a33aaa7d23ea9269ca7761c8a074af3524fbb240/watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179", size = 402339, upload-time = "2025-06-15T19:05:24.516Z" }, - { url = "https://files.pythonhosted.org/packages/80/28/98b222cca751ba68e88521fabd79a4fab64005fc5976ea49b53fa205d1fa/watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5", size = 394409, upload-time = "2025-06-15T19:05:25.469Z" }, - { url = "https://files.pythonhosted.org/packages/86/50/dee79968566c03190677c26f7f47960aff738d32087087bdf63a5473e7df/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297", size = 450939, upload-time = "2025-06-15T19:05:26.494Z" }, - { url = "https://files.pythonhosted.org/packages/40/45/a7b56fb129700f3cfe2594a01aa38d033b92a33dddce86c8dfdfc1247b72/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0", size = 457270, upload-time = "2025-06-15T19:05:27.466Z" }, - { url = "https://files.pythonhosted.org/packages/b5/c8/fa5ef9476b1d02dc6b5e258f515fcaaecf559037edf8b6feffcbc097c4b8/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e", size = 483370, upload-time = "2025-06-15T19:05:28.548Z" }, - { url = "https://files.pythonhosted.org/packages/98/68/42cfcdd6533ec94f0a7aab83f759ec11280f70b11bfba0b0f885e298f9bd/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee", size = 598654, upload-time = "2025-06-15T19:05:29.997Z" }, - { url = "https://files.pythonhosted.org/packages/d3/74/b2a1544224118cc28df7e59008a929e711f9c68ce7d554e171b2dc531352/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd", size = 478667, upload-time = "2025-06-15T19:05:31.172Z" }, - { url = "https://files.pythonhosted.org/packages/8c/77/e3362fe308358dc9f8588102481e599c83e1b91c2ae843780a7ded939a35/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f", size = 452213, upload-time = "2025-06-15T19:05:32.299Z" }, - { url = "https://files.pythonhosted.org/packages/6e/17/c8f1a36540c9a1558d4faf08e909399e8133599fa359bf52ec8fcee5be6f/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4", size = 626718, upload-time = "2025-06-15T19:05:33.415Z" }, - { url = "https://files.pythonhosted.org/packages/26/45/fb599be38b4bd38032643783d7496a26a6f9ae05dea1a42e58229a20ac13/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f", size = 623098, upload-time = "2025-06-15T19:05:34.534Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e7/fdf40e038475498e160cd167333c946e45d8563ae4dd65caf757e9ffe6b4/watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd", size = 279209, upload-time = "2025-06-15T19:05:35.577Z" }, - { url = "https://files.pythonhosted.org/packages/3f/d3/3ae9d5124ec75143bdf088d436cba39812122edc47709cd2caafeac3266f/watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47", size = 292786, upload-time = "2025-06-15T19:05:36.559Z" }, - { url = "https://files.pythonhosted.org/packages/26/2f/7dd4fc8b5f2b34b545e19629b4a018bfb1de23b3a496766a2c1165ca890d/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6", size = 284343, upload-time = "2025-06-15T19:05:37.5Z" }, - { url = "https://files.pythonhosted.org/packages/d3/42/fae874df96595556a9089ade83be34a2e04f0f11eb53a8dbf8a8a5e562b4/watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30", size = 402004, upload-time = "2025-06-15T19:05:38.499Z" }, - { url = "https://files.pythonhosted.org/packages/fa/55/a77e533e59c3003d9803c09c44c3651224067cbe7fb5d574ddbaa31e11ca/watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a", size = 393671, upload-time = "2025-06-15T19:05:39.52Z" }, - { url = "https://files.pythonhosted.org/packages/05/68/b0afb3f79c8e832e6571022611adbdc36e35a44e14f129ba09709aa4bb7a/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc", size = 449772, upload-time = "2025-06-15T19:05:40.897Z" }, - { url = "https://files.pythonhosted.org/packages/ff/05/46dd1f6879bc40e1e74c6c39a1b9ab9e790bf1f5a2fe6c08b463d9a807f4/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b", size = 456789, upload-time = "2025-06-15T19:05:42.045Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ca/0eeb2c06227ca7f12e50a47a3679df0cd1ba487ea19cf844a905920f8e95/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895", size = 482551, upload-time = "2025-06-15T19:05:43.781Z" }, - { url = "https://files.pythonhosted.org/packages/31/47/2cecbd8694095647406645f822781008cc524320466ea393f55fe70eed3b/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a", size = 597420, upload-time = "2025-06-15T19:05:45.244Z" }, - { url = "https://files.pythonhosted.org/packages/d9/7e/82abc4240e0806846548559d70f0b1a6dfdca75c1b4f9fa62b504ae9b083/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b", size = 477950, upload-time = "2025-06-15T19:05:46.332Z" }, - { url = "https://files.pythonhosted.org/packages/25/0d/4d564798a49bf5482a4fa9416dea6b6c0733a3b5700cb8a5a503c4b15853/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c", size = 451706, upload-time = "2025-06-15T19:05:47.459Z" }, - { url = "https://files.pythonhosted.org/packages/81/b5/5516cf46b033192d544102ea07c65b6f770f10ed1d0a6d388f5d3874f6e4/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b", size = 625814, upload-time = "2025-06-15T19:05:48.654Z" }, - { url = "https://files.pythonhosted.org/packages/0c/dd/7c1331f902f30669ac3e754680b6edb9a0dd06dea5438e61128111fadd2c/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb", size = 622820, upload-time = "2025-06-15T19:05:50.088Z" }, - { url = "https://files.pythonhosted.org/packages/1b/14/36d7a8e27cd128d7b1009e7715a7c02f6c131be9d4ce1e5c3b73d0e342d8/watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9", size = 279194, upload-time = "2025-06-15T19:05:51.186Z" }, - { url = "https://files.pythonhosted.org/packages/25/41/2dd88054b849aa546dbeef5696019c58f8e0774f4d1c42123273304cdb2e/watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7", size = 292349, upload-time = "2025-06-15T19:05:52.201Z" }, - { url = "https://files.pythonhosted.org/packages/c8/cf/421d659de88285eb13941cf11a81f875c176f76a6d99342599be88e08d03/watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5", size = 283836, upload-time = "2025-06-15T19:05:53.265Z" }, - { url = "https://files.pythonhosted.org/packages/45/10/6faf6858d527e3599cc50ec9fcae73590fbddc1420bd4fdccfebffeedbc6/watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1", size = 400343, upload-time = "2025-06-15T19:05:54.252Z" }, - { url = "https://files.pythonhosted.org/packages/03/20/5cb7d3966f5e8c718006d0e97dfe379a82f16fecd3caa7810f634412047a/watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339", size = 392916, upload-time = "2025-06-15T19:05:55.264Z" }, - { url = "https://files.pythonhosted.org/packages/8c/07/d8f1176328fa9e9581b6f120b017e286d2a2d22ae3f554efd9515c8e1b49/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633", size = 449582, upload-time = "2025-06-15T19:05:56.317Z" }, - { url = "https://files.pythonhosted.org/packages/66/e8/80a14a453cf6038e81d072a86c05276692a1826471fef91df7537dba8b46/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011", size = 456752, upload-time = "2025-06-15T19:05:57.359Z" }, - { url = "https://files.pythonhosted.org/packages/5a/25/0853b3fe0e3c2f5af9ea60eb2e781eade939760239a72c2d38fc4cc335f6/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670", size = 481436, upload-time = "2025-06-15T19:05:58.447Z" }, - { url = "https://files.pythonhosted.org/packages/fe/9e/4af0056c258b861fbb29dcb36258de1e2b857be4a9509e6298abcf31e5c9/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf", size = 596016, upload-time = "2025-06-15T19:05:59.59Z" }, - { url = "https://files.pythonhosted.org/packages/c5/fa/95d604b58aa375e781daf350897aaaa089cff59d84147e9ccff2447c8294/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4", size = 476727, upload-time = "2025-06-15T19:06:01.086Z" }, - { url = "https://files.pythonhosted.org/packages/65/95/fe479b2664f19be4cf5ceeb21be05afd491d95f142e72d26a42f41b7c4f8/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20", size = 451864, upload-time = "2025-06-15T19:06:02.144Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8a/3c4af14b93a15ce55901cd7a92e1a4701910f1768c78fb30f61d2b79785b/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef", size = 625626, upload-time = "2025-06-15T19:06:03.578Z" }, - { url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744, upload-time = "2025-06-15T19:06:05.066Z" }, - { url = "https://files.pythonhosted.org/packages/2c/00/70f75c47f05dea6fd30df90f047765f6fc2d6eb8b5a3921379b0b04defa2/watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297", size = 402114, upload-time = "2025-06-15T19:06:06.186Z" }, - { url = "https://files.pythonhosted.org/packages/53/03/acd69c48db4a1ed1de26b349d94077cca2238ff98fd64393f3e97484cae6/watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018", size = 393879, upload-time = "2025-06-15T19:06:07.369Z" }, - { url = "https://files.pythonhosted.org/packages/2f/c8/a9a2a6f9c8baa4eceae5887fecd421e1b7ce86802bcfc8b6a942e2add834/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0", size = 450026, upload-time = "2025-06-15T19:06:08.476Z" }, - { url = "https://files.pythonhosted.org/packages/fe/51/d572260d98388e6e2b967425c985e07d47ee6f62e6455cefb46a6e06eda5/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12", size = 457917, upload-time = "2025-06-15T19:06:09.988Z" }, - { url = "https://files.pythonhosted.org/packages/c6/2d/4258e52917bf9f12909b6ec314ff9636276f3542f9d3807d143f27309104/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb", size = 483602, upload-time = "2025-06-15T19:06:11.088Z" }, - { url = "https://files.pythonhosted.org/packages/84/99/bee17a5f341a4345fe7b7972a475809af9e528deba056f8963d61ea49f75/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77", size = 596758, upload-time = "2025-06-15T19:06:12.197Z" }, - { url = "https://files.pythonhosted.org/packages/40/76/e4bec1d59b25b89d2b0716b41b461ed655a9a53c60dc78ad5771fda5b3e6/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92", size = 477601, upload-time = "2025-06-15T19:06:13.391Z" }, - { url = "https://files.pythonhosted.org/packages/1f/fa/a514292956f4a9ce3c567ec0c13cce427c158e9f272062685a8a727d08fc/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e", size = 451936, upload-time = "2025-06-15T19:06:14.656Z" }, - { url = "https://files.pythonhosted.org/packages/32/5d/c3bf927ec3bbeb4566984eba8dd7a8eb69569400f5509904545576741f88/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b", size = 626243, upload-time = "2025-06-15T19:06:16.232Z" }, - { url = "https://files.pythonhosted.org/packages/e6/65/6e12c042f1a68c556802a84d54bb06d35577c81e29fba14019562479159c/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259", size = 623073, upload-time = "2025-06-15T19:06:17.457Z" }, - { url = "https://files.pythonhosted.org/packages/89/ab/7f79d9bf57329e7cbb0a6fd4c7bd7d0cee1e4a8ef0041459f5409da3506c/watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f", size = 400872, upload-time = "2025-06-15T19:06:18.57Z" }, - { url = "https://files.pythonhosted.org/packages/df/d5/3f7bf9912798e9e6c516094db6b8932df53b223660c781ee37607030b6d3/watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e", size = 392877, upload-time = "2025-06-15T19:06:19.55Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c5/54ec7601a2798604e01c75294770dbee8150e81c6e471445d7601610b495/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa", size = 449645, upload-time = "2025-06-15T19:06:20.66Z" }, - { url = "https://files.pythonhosted.org/packages/0a/04/c2f44afc3b2fce21ca0b7802cbd37ed90a29874f96069ed30a36dfe57c2b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8", size = 457424, upload-time = "2025-06-15T19:06:21.712Z" }, - { url = "https://files.pythonhosted.org/packages/9f/b0/eec32cb6c14d248095261a04f290636da3df3119d4040ef91a4a50b29fa5/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f", size = 481584, upload-time = "2025-06-15T19:06:22.777Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e2/ca4bb71c68a937d7145aa25709e4f5d68eb7698a25ce266e84b55d591bbd/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e", size = 596675, upload-time = "2025-06-15T19:06:24.226Z" }, - { url = "https://files.pythonhosted.org/packages/a1/dd/b0e4b7fb5acf783816bc950180a6cd7c6c1d2cf7e9372c0ea634e722712b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb", size = 477363, upload-time = "2025-06-15T19:06:25.42Z" }, - { url = "https://files.pythonhosted.org/packages/69/c4/088825b75489cb5b6a761a4542645718893d395d8c530b38734f19da44d2/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147", size = 452240, upload-time = "2025-06-15T19:06:26.552Z" }, - { url = "https://files.pythonhosted.org/packages/10/8c/22b074814970eeef43b7c44df98c3e9667c1f7bf5b83e0ff0201b0bd43f9/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8", size = 625607, upload-time = "2025-06-15T19:06:27.606Z" }, - { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315, upload-time = "2025-06-15T19:06:29.076Z" }, - { url = "https://files.pythonhosted.org/packages/be/7c/a3d7c55cfa377c2f62c4ae3c6502b997186bc5e38156bafcb9b653de9a6d/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a6fd40bbb50d24976eb275ccb55cd1951dfb63dbc27cae3066a6ca5f4beabd5", size = 406748, upload-time = "2025-06-15T19:06:44.2Z" }, - { url = "https://files.pythonhosted.org/packages/38/d0/c46f1b2c0ca47f3667b144de6f0515f6d1c670d72f2ca29861cac78abaa1/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f811079d2f9795b5d48b55a37aa7773680a5659afe34b54cc1d86590a51507d", size = 398801, upload-time = "2025-06-15T19:06:45.774Z" }, - { url = "https://files.pythonhosted.org/packages/70/9c/9a6a42e97f92eeed77c3485a43ea96723900aefa3ac739a8c73f4bff2cd7/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2726d7bfd9f76158c84c10a409b77a320426540df8c35be172444394b17f7ea", size = 451528, upload-time = "2025-06-15T19:06:46.791Z" }, - { url = "https://files.pythonhosted.org/packages/51/7b/98c7f4f7ce7ff03023cf971cd84a3ee3b790021ae7584ffffa0eb2554b96/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df32d59cb9780f66d165a9a7a26f19df2c7d24e3bd58713108b41d0ff4f929c6", size = 454095, upload-time = "2025-06-15T19:06:48.211Z" }, - { url = "https://files.pythonhosted.org/packages/8c/6b/686dcf5d3525ad17b384fd94708e95193529b460a1b7bf40851f1328ec6e/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3", size = 406910, upload-time = "2025-06-15T19:06:49.335Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d3/71c2dcf81dc1edcf8af9f4d8d63b1316fb0a2dd90cbfd427e8d9dd584a90/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c", size = 398816, upload-time = "2025-06-15T19:06:50.433Z" }, - { url = "https://files.pythonhosted.org/packages/b8/fa/12269467b2fc006f8fce4cd6c3acfa77491dd0777d2a747415f28ccc8c60/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432", size = 451584, upload-time = "2025-06-15T19:06:51.834Z" }, - { url = "https://files.pythonhosted.org/packages/bd/d3/254cea30f918f489db09d6a8435a7de7047f8cb68584477a515f160541d6/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792", size = 454009, upload-time = "2025-06-15T19:06:52.896Z" }, ] - -[[package]] -name = "wcwidth" -version = "0.2.13" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/a3/4d310fa5f00863544e1d0f4de93bddec248499ccf97d4791bc3122c9d4f3/virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba", size = 6032239 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" }, + { url = "https://files.pythonhosted.org/packages/6a/2a/dc2228b2888f51192c7dc766106cd475f1b768c10caaf9727659726f7391/virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f", size = 6008258 }, ] [[package]] -name = "webcolors" -version = "24.11.1" +name = "watchdog" +version = "6.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/29/061ec845fb58521848f3739e466efd8250b4b7b98c1b6c5bf4d40b419b7e/webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6", size = 45064, upload-time = "2024-11-11T07:43:24.224Z" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/e8/c0e05e4684d13459f93d312077a9a2efbe04d59c393bc2b8802248c908d4/webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9", size = 14934, upload-time = "2024-11-11T07:43:22.529Z" }, + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471 }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449 }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054 }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480 }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451 }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057 }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, ] [[package]] name = "webencodings" version = "0.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, -] - -[[package]] -name = "websocket-client" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648, upload-time = "2024-04-23T22:16:16.976Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826, upload-time = "2024-04-23T22:16:14.422Z" }, -] - -[[package]] -name = "websockets" -version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080, upload-time = "2025-03-05T20:01:37.304Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329, upload-time = "2025-03-05T20:01:39.668Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312, upload-time = "2025-03-05T20:01:41.815Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319, upload-time = "2025-03-05T20:01:43.967Z" }, - { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631, upload-time = "2025-03-05T20:01:46.104Z" }, - { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016, upload-time = "2025-03-05T20:01:47.603Z" }, - { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426, upload-time = "2025-03-05T20:01:48.949Z" }, - { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360, upload-time = "2025-03-05T20:01:50.938Z" }, - { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388, upload-time = "2025-03-05T20:01:52.213Z" }, - { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830, upload-time = "2025-03-05T20:01:53.922Z" }, - { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" }, - { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" }, - { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" }, - { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" }, - { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" }, - { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" }, - { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" }, - { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" }, - { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" }, - { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, - { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, - { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, - { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, - { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, - { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, - { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, - { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, - { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, - { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, - { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, - { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, - { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, - { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, - { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, - { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, - { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" }, - { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" }, - { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" }, - { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" }, - { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" }, - { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, -] - -[[package]] -name = "widgetsnbextension" -version = "4.0.14" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/53/2e0253c5efd69c9656b1843892052a31c36d37ad42812b5da45c62191f7e/widgetsnbextension-4.0.14.tar.gz", hash = "sha256:a3629b04e3edb893212df862038c7232f62973373869db5084aed739b437b5af", size = 1097428, upload-time = "2025-04-10T13:01:25.628Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/51/5447876806d1088a0f8f71e16542bf350918128d0a69437df26047c8e46f/widgetsnbextension-4.0.14-py3-none-any.whl", hash = "sha256:4875a9eaf72fbf5079dc372a51a9f268fc38d46f767cbf85c43a36da5cb9b575", size = 2196503, upload-time = "2025-04-10T13:01:23.086Z" }, + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774 }, ] [[package]] name = "wrapt" -version = "1.17.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531, upload-time = "2025-01-14T10:35:45.465Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/d1/1daec934997e8b160040c78d7b31789f19b122110a75eca3d4e8da0049e1/wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984", size = 53307, upload-time = "2025-01-14T10:33:13.616Z" }, - { url = "https://files.pythonhosted.org/packages/1b/7b/13369d42651b809389c1a7153baa01d9700430576c81a2f5c5e460df0ed9/wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22", size = 38486, upload-time = "2025-01-14T10:33:15.947Z" }, - { url = "https://files.pythonhosted.org/packages/62/bf/e0105016f907c30b4bd9e377867c48c34dc9c6c0c104556c9c9126bd89ed/wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7", size = 38777, upload-time = "2025-01-14T10:33:17.462Z" }, - { url = "https://files.pythonhosted.org/packages/27/70/0f6e0679845cbf8b165e027d43402a55494779295c4b08414097b258ac87/wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c", size = 83314, upload-time = "2025-01-14T10:33:21.282Z" }, - { url = "https://files.pythonhosted.org/packages/0f/77/0576d841bf84af8579124a93d216f55d6f74374e4445264cb378a6ed33eb/wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72", size = 74947, upload-time = "2025-01-14T10:33:24.414Z" }, - { url = "https://files.pythonhosted.org/packages/90/ec/00759565518f268ed707dcc40f7eeec38637d46b098a1f5143bff488fe97/wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061", size = 82778, upload-time = "2025-01-14T10:33:26.152Z" }, - { url = "https://files.pythonhosted.org/packages/f8/5a/7cffd26b1c607b0b0c8a9ca9d75757ad7620c9c0a9b4a25d3f8a1480fafc/wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2", size = 81716, upload-time = "2025-01-14T10:33:27.372Z" }, - { url = "https://files.pythonhosted.org/packages/7e/09/dccf68fa98e862df7e6a60a61d43d644b7d095a5fc36dbb591bbd4a1c7b2/wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c", size = 74548, upload-time = "2025-01-14T10:33:28.52Z" }, - { url = "https://files.pythonhosted.org/packages/b7/8e/067021fa3c8814952c5e228d916963c1115b983e21393289de15128e867e/wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62", size = 81334, upload-time = "2025-01-14T10:33:29.643Z" }, - { url = "https://files.pythonhosted.org/packages/4b/0d/9d4b5219ae4393f718699ca1c05f5ebc0c40d076f7e65fd48f5f693294fb/wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563", size = 36427, upload-time = "2025-01-14T10:33:30.832Z" }, - { url = "https://files.pythonhosted.org/packages/72/6a/c5a83e8f61aec1e1aeef939807602fb880e5872371e95df2137142f5c58e/wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f", size = 38774, upload-time = "2025-01-14T10:33:32.897Z" }, - { url = "https://files.pythonhosted.org/packages/cd/f7/a2aab2cbc7a665efab072344a8949a71081eed1d2f451f7f7d2b966594a2/wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58", size = 53308, upload-time = "2025-01-14T10:33:33.992Z" }, - { url = "https://files.pythonhosted.org/packages/50/ff/149aba8365fdacef52b31a258c4dc1c57c79759c335eff0b3316a2664a64/wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda", size = 38488, upload-time = "2025-01-14T10:33:35.264Z" }, - { url = "https://files.pythonhosted.org/packages/65/46/5a917ce85b5c3b490d35c02bf71aedaa9f2f63f2d15d9949cc4ba56e8ba9/wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438", size = 38776, upload-time = "2025-01-14T10:33:38.28Z" }, - { url = "https://files.pythonhosted.org/packages/ca/74/336c918d2915a4943501c77566db41d1bd6e9f4dbc317f356b9a244dfe83/wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a", size = 83776, upload-time = "2025-01-14T10:33:40.678Z" }, - { url = "https://files.pythonhosted.org/packages/09/99/c0c844a5ccde0fe5761d4305485297f91d67cf2a1a824c5f282e661ec7ff/wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000", size = 75420, upload-time = "2025-01-14T10:33:41.868Z" }, - { url = "https://files.pythonhosted.org/packages/b4/b0/9fc566b0fe08b282c850063591a756057c3247b2362b9286429ec5bf1721/wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6", size = 83199, upload-time = "2025-01-14T10:33:43.598Z" }, - { url = "https://files.pythonhosted.org/packages/9d/4b/71996e62d543b0a0bd95dda485219856def3347e3e9380cc0d6cf10cfb2f/wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b", size = 82307, upload-time = "2025-01-14T10:33:48.499Z" }, - { url = "https://files.pythonhosted.org/packages/39/35/0282c0d8789c0dc9bcc738911776c762a701f95cfe113fb8f0b40e45c2b9/wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662", size = 75025, upload-time = "2025-01-14T10:33:51.191Z" }, - { url = "https://files.pythonhosted.org/packages/4f/6d/90c9fd2c3c6fee181feecb620d95105370198b6b98a0770cba090441a828/wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", size = 81879, upload-time = "2025-01-14T10:33:52.328Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fa/9fb6e594f2ce03ef03eddbdb5f4f90acb1452221a5351116c7c4708ac865/wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", size = 36419, upload-time = "2025-01-14T10:33:53.551Z" }, - { url = "https://files.pythonhosted.org/packages/47/f8/fb1773491a253cbc123c5d5dc15c86041f746ed30416535f2a8df1f4a392/wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", size = 38773, upload-time = "2025-01-14T10:33:56.323Z" }, - { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799, upload-time = "2025-01-14T10:33:57.4Z" }, - { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821, upload-time = "2025-01-14T10:33:59.334Z" }, - { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919, upload-time = "2025-01-14T10:34:04.093Z" }, - { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721, upload-time = "2025-01-14T10:34:07.163Z" }, - { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899, upload-time = "2025-01-14T10:34:09.82Z" }, - { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222, upload-time = "2025-01-14T10:34:11.258Z" }, - { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707, upload-time = "2025-01-14T10:34:12.49Z" }, - { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685, upload-time = "2025-01-14T10:34:15.043Z" }, - { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567, upload-time = "2025-01-14T10:34:16.563Z" }, - { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672, upload-time = "2025-01-14T10:34:17.727Z" }, - { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865, upload-time = "2025-01-14T10:34:19.577Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800, upload-time = "2025-01-14T10:34:21.571Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824, upload-time = "2025-01-14T10:34:22.999Z" }, - { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920, upload-time = "2025-01-14T10:34:25.386Z" }, - { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690, upload-time = "2025-01-14T10:34:28.058Z" }, - { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861, upload-time = "2025-01-14T10:34:29.167Z" }, - { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174, upload-time = "2025-01-14T10:34:31.702Z" }, - { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721, upload-time = "2025-01-14T10:34:32.91Z" }, - { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763, upload-time = "2025-01-14T10:34:34.903Z" }, - { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585, upload-time = "2025-01-14T10:34:36.13Z" }, - { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676, upload-time = "2025-01-14T10:34:37.962Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871, upload-time = "2025-01-14T10:34:39.13Z" }, - { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312, upload-time = "2025-01-14T10:34:40.604Z" }, - { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062, upload-time = "2025-01-14T10:34:45.011Z" }, - { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155, upload-time = "2025-01-14T10:34:47.25Z" }, - { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471, upload-time = "2025-01-14T10:34:50.934Z" }, - { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208, upload-time = "2025-01-14T10:34:52.297Z" }, - { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339, upload-time = "2025-01-14T10:34:53.489Z" }, - { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232, upload-time = "2025-01-14T10:34:55.327Z" }, - { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476, upload-time = "2025-01-14T10:34:58.055Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377, upload-time = "2025-01-14T10:34:59.3Z" }, - { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986, upload-time = "2025-01-14T10:35:00.498Z" }, - { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750, upload-time = "2025-01-14T10:35:03.378Z" }, - { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594, upload-time = "2025-01-14T10:35:44.018Z" }, -] - -[[package]] -name = "xarray" -version = "2025.6.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11' and platform_python_implementation == 'PyPy'", - "python_full_version < '3.11' and platform_python_implementation != 'PyPy'", -] -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "packaging", marker = "python_full_version < '3.11'" }, - { name = "pandas", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/ec/e50d833518f10b0c24feb184b209bb6856f25b919ba8c1f89678b930b1cd/xarray-2025.6.1.tar.gz", hash = "sha256:a84f3f07544634a130d7dc615ae44175419f4c77957a7255161ed99c69c7c8b0", size = 3003185, upload-time = "2025-06-12T03:04:09.099Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/8a/6b50c1dd2260d407c1a499d47cf829f59f07007e0dcebafdabb24d1d26a5/xarray-2025.6.1-py3-none-any.whl", hash = "sha256:8b988b47f67a383bdc3b04c5db475cd165e580134c1f1943d52aee4a9c97651b", size = 1314739, upload-time = "2025-06-12T03:04:06.708Z" }, -] - -[[package]] -name = "xarray" -version = "2025.8.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12' and platform_python_implementation == 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", - "python_full_version >= '3.12' and platform_python_implementation != 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", -] -dependencies = [ - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "packaging", marker = "python_full_version >= '3.11'" }, - { name = "pandas", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d2/55/18055bc943029d25fb8f260b7e3b1485c30646ccf503a5e4a744d31a3b78/xarray-2025.8.0.tar.gz", hash = "sha256:323d4169ce72d4ef849de2b0bd122f9cd2905b82c7558169930dc16070982bab", size = 3034425, upload-time = "2025-08-14T16:52:13.872Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/c8/0f8db9d9478de8d70cbcae2056588401e26168e269d6d9919bf2ecb01f78/xarray-2025.8.0-py3-none-any.whl", hash = "sha256:1c454f32b38c93df68e450238c9473fe21248b8572d42ddd58c5170bb30934ee", size = 1342279, upload-time = "2025-08-14T16:52:10.956Z" }, -] - -[[package]] -name = "xpystac" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11' and platform_python_implementation == 'PyPy'", - "python_full_version < '3.11' and platform_python_implementation != 'PyPy'", -] -dependencies = [ - { name = "pystac", marker = "python_full_version < '3.11'" }, - { name = "xarray", version = "2025.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/ad4ba3747c1be7f476ebcd5afd2ca5bccba69c7e436700ac38283e770e68/xpystac-0.2.0.tar.gz", hash = "sha256:f3c6b98c8c70af37132c629c8e7d642a33df88ce99ae24d1b97ccdc46bb370f1", size = 121143, upload-time = "2025-03-11T15:22:29.614Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/24/67/9c44b4798cd5d0dbc45243318cf89482b043ed05dec12c59558b3a04aab8/xpystac-0.2.0-py3-none-any.whl", hash = "sha256:f1da44fb273b47235b027ff5749430e088525bea73a8d9e0955180eeb7f1334c", size = 7068, upload-time = "2025-03-11T15:22:28.753Z" }, -] - -[[package]] -name = "xpystac" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12' and platform_python_implementation == 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", - "python_full_version >= '3.12' and platform_python_implementation != 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", -] -dependencies = [ - { name = "pystac", marker = "python_full_version >= '3.11'" }, - { name = "xarray", version = "2025.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e5/c5/94a094c0f5c425e0cd036b8534a545e803eb3c7a4d4dc516bd5ef83d74eb/xpystac-0.3.0.tar.gz", hash = "sha256:e2d75483b10fbd6de32fa0c242633384151c9615630e182c390ca7cb063e2922", size = 118794, upload-time = "2025-08-20T17:15:59.214Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/62/0facd92075a4ed6207a1fe8f3b035067d5998c32397d1bec1e06aa4a059a/xpystac-0.3.0-py3-none-any.whl", hash = "sha256:7bada281cdfe1a005be129e96096994de1134bbfc9bd4cf2670401750487508a", size = 8846, upload-time = "2025-08-20T17:15:58.299Z" }, -] - -[[package]] -name = "yarl" -version = "1.20.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "multidict" }, - { name = "propcache" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3c/fb/efaa23fa4e45537b827620f04cf8f3cd658b76642205162e072703a5b963/yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac", size = 186428, upload-time = "2025-06-10T00:46:09.923Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/65/7fed0d774abf47487c64be14e9223749468922817b5e8792b8a64792a1bb/yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4", size = 132910, upload-time = "2025-06-10T00:42:31.108Z" }, - { url = "https://files.pythonhosted.org/packages/8a/7b/988f55a52da99df9e56dc733b8e4e5a6ae2090081dc2754fc8fd34e60aa0/yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a", size = 90644, upload-time = "2025-06-10T00:42:33.851Z" }, - { url = "https://files.pythonhosted.org/packages/f7/de/30d98f03e95d30c7e3cc093759982d038c8833ec2451001d45ef4854edc1/yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed", size = 89322, upload-time = "2025-06-10T00:42:35.688Z" }, - { url = "https://files.pythonhosted.org/packages/e0/7a/f2f314f5ebfe9200724b0b748de2186b927acb334cf964fd312eb86fc286/yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e", size = 323786, upload-time = "2025-06-10T00:42:37.817Z" }, - { url = "https://files.pythonhosted.org/packages/15/3f/718d26f189db96d993d14b984ce91de52e76309d0fd1d4296f34039856aa/yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73", size = 319627, upload-time = "2025-06-10T00:42:39.937Z" }, - { url = "https://files.pythonhosted.org/packages/a5/76/8fcfbf5fa2369157b9898962a4a7d96764b287b085b5b3d9ffae69cdefd1/yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e", size = 339149, upload-time = "2025-06-10T00:42:42.627Z" }, - { url = "https://files.pythonhosted.org/packages/3c/95/d7fc301cc4661785967acc04f54a4a42d5124905e27db27bb578aac49b5c/yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8", size = 333327, upload-time = "2025-06-10T00:42:44.842Z" }, - { url = "https://files.pythonhosted.org/packages/65/94/e21269718349582eee81efc5c1c08ee71c816bfc1585b77d0ec3f58089eb/yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23", size = 326054, upload-time = "2025-06-10T00:42:47.149Z" }, - { url = "https://files.pythonhosted.org/packages/32/ae/8616d1f07853704523519f6131d21f092e567c5af93de7e3e94b38d7f065/yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70", size = 315035, upload-time = "2025-06-10T00:42:48.852Z" }, - { url = "https://files.pythonhosted.org/packages/48/aa/0ace06280861ef055855333707db5e49c6e3a08840a7ce62682259d0a6c0/yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb", size = 338962, upload-time = "2025-06-10T00:42:51.024Z" }, - { url = "https://files.pythonhosted.org/packages/20/52/1e9d0e6916f45a8fb50e6844f01cb34692455f1acd548606cbda8134cd1e/yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2", size = 335399, upload-time = "2025-06-10T00:42:53.007Z" }, - { url = "https://files.pythonhosted.org/packages/f2/65/60452df742952c630e82f394cd409de10610481d9043aa14c61bf846b7b1/yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30", size = 338649, upload-time = "2025-06-10T00:42:54.964Z" }, - { url = "https://files.pythonhosted.org/packages/7b/f5/6cd4ff38dcde57a70f23719a838665ee17079640c77087404c3d34da6727/yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309", size = 358563, upload-time = "2025-06-10T00:42:57.28Z" }, - { url = "https://files.pythonhosted.org/packages/d1/90/c42eefd79d0d8222cb3227bdd51b640c0c1d0aa33fe4cc86c36eccba77d3/yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24", size = 357609, upload-time = "2025-06-10T00:42:59.055Z" }, - { url = "https://files.pythonhosted.org/packages/03/c8/cea6b232cb4617514232e0f8a718153a95b5d82b5290711b201545825532/yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13", size = 350224, upload-time = "2025-06-10T00:43:01.248Z" }, - { url = "https://files.pythonhosted.org/packages/ce/a3/eaa0ab9712f1f3d01faf43cf6f1f7210ce4ea4a7e9b28b489a2261ca8db9/yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8", size = 81753, upload-time = "2025-06-10T00:43:03.486Z" }, - { url = "https://files.pythonhosted.org/packages/8f/34/e4abde70a9256465fe31c88ed02c3f8502b7b5dead693a4f350a06413f28/yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16", size = 86817, upload-time = "2025-06-10T00:43:05.231Z" }, - { url = "https://files.pythonhosted.org/packages/b1/18/893b50efc2350e47a874c5c2d67e55a0ea5df91186b2a6f5ac52eff887cd/yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e", size = 133833, upload-time = "2025-06-10T00:43:07.393Z" }, - { url = "https://files.pythonhosted.org/packages/89/ed/b8773448030e6fc47fa797f099ab9eab151a43a25717f9ac043844ad5ea3/yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b", size = 91070, upload-time = "2025-06-10T00:43:09.538Z" }, - { url = "https://files.pythonhosted.org/packages/e3/e3/409bd17b1e42619bf69f60e4f031ce1ccb29bd7380117a55529e76933464/yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b", size = 89818, upload-time = "2025-06-10T00:43:11.575Z" }, - { url = "https://files.pythonhosted.org/packages/f8/77/64d8431a4d77c856eb2d82aa3de2ad6741365245a29b3a9543cd598ed8c5/yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4", size = 347003, upload-time = "2025-06-10T00:43:14.088Z" }, - { url = "https://files.pythonhosted.org/packages/8d/d2/0c7e4def093dcef0bd9fa22d4d24b023788b0a33b8d0088b51aa51e21e99/yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1", size = 336537, upload-time = "2025-06-10T00:43:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/f0/f3/fc514f4b2cf02cb59d10cbfe228691d25929ce8f72a38db07d3febc3f706/yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833", size = 362358, upload-time = "2025-06-10T00:43:18.704Z" }, - { url = "https://files.pythonhosted.org/packages/ea/6d/a313ac8d8391381ff9006ac05f1d4331cee3b1efaa833a53d12253733255/yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d", size = 357362, upload-time = "2025-06-10T00:43:20.888Z" }, - { url = "https://files.pythonhosted.org/packages/00/70/8f78a95d6935a70263d46caa3dd18e1f223cf2f2ff2037baa01a22bc5b22/yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8", size = 348979, upload-time = "2025-06-10T00:43:23.169Z" }, - { url = "https://files.pythonhosted.org/packages/cb/05/42773027968968f4f15143553970ee36ead27038d627f457cc44bbbeecf3/yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf", size = 337274, upload-time = "2025-06-10T00:43:27.111Z" }, - { url = "https://files.pythonhosted.org/packages/05/be/665634aa196954156741ea591d2f946f1b78ceee8bb8f28488bf28c0dd62/yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e", size = 363294, upload-time = "2025-06-10T00:43:28.96Z" }, - { url = "https://files.pythonhosted.org/packages/eb/90/73448401d36fa4e210ece5579895731f190d5119c4b66b43b52182e88cd5/yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389", size = 358169, upload-time = "2025-06-10T00:43:30.701Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b0/fce922d46dc1eb43c811f1889f7daa6001b27a4005587e94878570300881/yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f", size = 362776, upload-time = "2025-06-10T00:43:32.51Z" }, - { url = "https://files.pythonhosted.org/packages/f1/0d/b172628fce039dae8977fd22caeff3eeebffd52e86060413f5673767c427/yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845", size = 381341, upload-time = "2025-06-10T00:43:34.543Z" }, - { url = "https://files.pythonhosted.org/packages/6b/9b/5b886d7671f4580209e855974fe1cecec409aa4a89ea58b8f0560dc529b1/yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1", size = 379988, upload-time = "2025-06-10T00:43:36.489Z" }, - { url = "https://files.pythonhosted.org/packages/73/be/75ef5fd0fcd8f083a5d13f78fd3f009528132a1f2a1d7c925c39fa20aa79/yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e", size = 371113, upload-time = "2025-06-10T00:43:38.592Z" }, - { url = "https://files.pythonhosted.org/packages/50/4f/62faab3b479dfdcb741fe9e3f0323e2a7d5cd1ab2edc73221d57ad4834b2/yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773", size = 81485, upload-time = "2025-06-10T00:43:41.038Z" }, - { url = "https://files.pythonhosted.org/packages/f0/09/d9c7942f8f05c32ec72cd5c8e041c8b29b5807328b68b4801ff2511d4d5e/yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e", size = 86686, upload-time = "2025-06-10T00:43:42.692Z" }, - { url = "https://files.pythonhosted.org/packages/5f/9a/cb7fad7d73c69f296eda6815e4a2c7ed53fc70c2f136479a91c8e5fbdb6d/yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9", size = 133667, upload-time = "2025-06-10T00:43:44.369Z" }, - { url = "https://files.pythonhosted.org/packages/67/38/688577a1cb1e656e3971fb66a3492501c5a5df56d99722e57c98249e5b8a/yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a", size = 91025, upload-time = "2025-06-10T00:43:46.295Z" }, - { url = "https://files.pythonhosted.org/packages/50/ec/72991ae51febeb11a42813fc259f0d4c8e0507f2b74b5514618d8b640365/yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2", size = 89709, upload-time = "2025-06-10T00:43:48.22Z" }, - { url = "https://files.pythonhosted.org/packages/99/da/4d798025490e89426e9f976702e5f9482005c548c579bdae792a4c37769e/yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee", size = 352287, upload-time = "2025-06-10T00:43:49.924Z" }, - { url = "https://files.pythonhosted.org/packages/1a/26/54a15c6a567aac1c61b18aa0f4b8aa2e285a52d547d1be8bf48abe2b3991/yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819", size = 345429, upload-time = "2025-06-10T00:43:51.7Z" }, - { url = "https://files.pythonhosted.org/packages/d6/95/9dcf2386cb875b234353b93ec43e40219e14900e046bf6ac118f94b1e353/yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16", size = 365429, upload-time = "2025-06-10T00:43:53.494Z" }, - { url = "https://files.pythonhosted.org/packages/91/b2/33a8750f6a4bc224242a635f5f2cff6d6ad5ba651f6edcccf721992c21a0/yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6", size = 363862, upload-time = "2025-06-10T00:43:55.766Z" }, - { url = "https://files.pythonhosted.org/packages/98/28/3ab7acc5b51f4434b181b0cee8f1f4b77a65919700a355fb3617f9488874/yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd", size = 355616, upload-time = "2025-06-10T00:43:58.056Z" }, - { url = "https://files.pythonhosted.org/packages/36/a3/f666894aa947a371724ec7cd2e5daa78ee8a777b21509b4252dd7bd15e29/yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a", size = 339954, upload-time = "2025-06-10T00:43:59.773Z" }, - { url = "https://files.pythonhosted.org/packages/f1/81/5f466427e09773c04219d3450d7a1256138a010b6c9f0af2d48565e9ad13/yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38", size = 365575, upload-time = "2025-06-10T00:44:02.051Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e3/e4b0ad8403e97e6c9972dd587388940a032f030ebec196ab81a3b8e94d31/yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef", size = 365061, upload-time = "2025-06-10T00:44:04.196Z" }, - { url = "https://files.pythonhosted.org/packages/ac/99/b8a142e79eb86c926f9f06452eb13ecb1bb5713bd01dc0038faf5452e544/yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f", size = 364142, upload-time = "2025-06-10T00:44:06.527Z" }, - { url = "https://files.pythonhosted.org/packages/34/f2/08ed34a4a506d82a1a3e5bab99ccd930a040f9b6449e9fd050320e45845c/yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8", size = 381894, upload-time = "2025-06-10T00:44:08.379Z" }, - { url = "https://files.pythonhosted.org/packages/92/f8/9a3fbf0968eac704f681726eff595dce9b49c8a25cd92bf83df209668285/yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a", size = 383378, upload-time = "2025-06-10T00:44:10.51Z" }, - { url = "https://files.pythonhosted.org/packages/af/85/9363f77bdfa1e4d690957cd39d192c4cacd1c58965df0470a4905253b54f/yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004", size = 374069, upload-time = "2025-06-10T00:44:12.834Z" }, - { url = "https://files.pythonhosted.org/packages/35/99/9918c8739ba271dcd935400cff8b32e3cd319eaf02fcd023d5dcd487a7c8/yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5", size = 81249, upload-time = "2025-06-10T00:44:14.731Z" }, - { url = "https://files.pythonhosted.org/packages/eb/83/5d9092950565481b413b31a23e75dd3418ff0a277d6e0abf3729d4d1ce25/yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698", size = 86710, upload-time = "2025-06-10T00:44:16.716Z" }, - { url = "https://files.pythonhosted.org/packages/8a/e1/2411b6d7f769a07687acee88a062af5833cf1966b7266f3d8dfb3d3dc7d3/yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a", size = 131811, upload-time = "2025-06-10T00:44:18.933Z" }, - { url = "https://files.pythonhosted.org/packages/b2/27/584394e1cb76fb771371770eccad35de400e7b434ce3142c2dd27392c968/yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3", size = 90078, upload-time = "2025-06-10T00:44:20.635Z" }, - { url = "https://files.pythonhosted.org/packages/bf/9a/3246ae92d4049099f52d9b0fe3486e3b500e29b7ea872d0f152966fc209d/yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7", size = 88748, upload-time = "2025-06-10T00:44:22.34Z" }, - { url = "https://files.pythonhosted.org/packages/a3/25/35afe384e31115a1a801fbcf84012d7a066d89035befae7c5d4284df1e03/yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691", size = 349595, upload-time = "2025-06-10T00:44:24.314Z" }, - { url = "https://files.pythonhosted.org/packages/28/2d/8aca6cb2cabc8f12efcb82749b9cefecbccfc7b0384e56cd71058ccee433/yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31", size = 342616, upload-time = "2025-06-10T00:44:26.167Z" }, - { url = "https://files.pythonhosted.org/packages/0b/e9/1312633d16b31acf0098d30440ca855e3492d66623dafb8e25b03d00c3da/yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28", size = 361324, upload-time = "2025-06-10T00:44:27.915Z" }, - { url = "https://files.pythonhosted.org/packages/bc/a0/688cc99463f12f7669eec7c8acc71ef56a1521b99eab7cd3abb75af887b0/yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653", size = 359676, upload-time = "2025-06-10T00:44:30.041Z" }, - { url = "https://files.pythonhosted.org/packages/af/44/46407d7f7a56e9a85a4c207724c9f2c545c060380718eea9088f222ba697/yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5", size = 352614, upload-time = "2025-06-10T00:44:32.171Z" }, - { url = "https://files.pythonhosted.org/packages/b1/91/31163295e82b8d5485d31d9cf7754d973d41915cadce070491778d9c9825/yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02", size = 336766, upload-time = "2025-06-10T00:44:34.494Z" }, - { url = "https://files.pythonhosted.org/packages/b4/8e/c41a5bc482121f51c083c4c2bcd16b9e01e1cf8729e380273a952513a21f/yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53", size = 364615, upload-time = "2025-06-10T00:44:36.856Z" }, - { url = "https://files.pythonhosted.org/packages/e3/5b/61a3b054238d33d70ea06ebba7e58597891b71c699e247df35cc984ab393/yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc", size = 360982, upload-time = "2025-06-10T00:44:39.141Z" }, - { url = "https://files.pythonhosted.org/packages/df/a3/6a72fb83f8d478cb201d14927bc8040af901811a88e0ff2da7842dd0ed19/yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04", size = 369792, upload-time = "2025-06-10T00:44:40.934Z" }, - { url = "https://files.pythonhosted.org/packages/7c/af/4cc3c36dfc7c077f8dedb561eb21f69e1e9f2456b91b593882b0b18c19dc/yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4", size = 382049, upload-time = "2025-06-10T00:44:42.854Z" }, - { url = "https://files.pythonhosted.org/packages/19/3a/e54e2c4752160115183a66dc9ee75a153f81f3ab2ba4bf79c3c53b33de34/yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b", size = 384774, upload-time = "2025-06-10T00:44:45.275Z" }, - { url = "https://files.pythonhosted.org/packages/9c/20/200ae86dabfca89060ec6447649f219b4cbd94531e425e50d57e5f5ac330/yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1", size = 374252, upload-time = "2025-06-10T00:44:47.31Z" }, - { url = "https://files.pythonhosted.org/packages/83/75/11ee332f2f516b3d094e89448da73d557687f7d137d5a0f48c40ff211487/yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7", size = 81198, upload-time = "2025-06-10T00:44:49.164Z" }, - { url = "https://files.pythonhosted.org/packages/ba/ba/39b1ecbf51620b40ab402b0fc817f0ff750f6d92712b44689c2c215be89d/yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c", size = 86346, upload-time = "2025-06-10T00:44:51.182Z" }, - { url = "https://files.pythonhosted.org/packages/43/c7/669c52519dca4c95153c8ad96dd123c79f354a376346b198f438e56ffeb4/yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d", size = 138826, upload-time = "2025-06-10T00:44:52.883Z" }, - { url = "https://files.pythonhosted.org/packages/6a/42/fc0053719b44f6ad04a75d7f05e0e9674d45ef62f2d9ad2c1163e5c05827/yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf", size = 93217, upload-time = "2025-06-10T00:44:54.658Z" }, - { url = "https://files.pythonhosted.org/packages/4f/7f/fa59c4c27e2a076bba0d959386e26eba77eb52ea4a0aac48e3515c186b4c/yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3", size = 92700, upload-time = "2025-06-10T00:44:56.784Z" }, - { url = "https://files.pythonhosted.org/packages/2f/d4/062b2f48e7c93481e88eff97a6312dca15ea200e959f23e96d8ab898c5b8/yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d", size = 347644, upload-time = "2025-06-10T00:44:59.071Z" }, - { url = "https://files.pythonhosted.org/packages/89/47/78b7f40d13c8f62b499cc702fdf69e090455518ae544c00a3bf4afc9fc77/yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c", size = 323452, upload-time = "2025-06-10T00:45:01.605Z" }, - { url = "https://files.pythonhosted.org/packages/eb/2b/490d3b2dc66f52987d4ee0d3090a147ea67732ce6b4d61e362c1846d0d32/yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1", size = 346378, upload-time = "2025-06-10T00:45:03.946Z" }, - { url = "https://files.pythonhosted.org/packages/66/ad/775da9c8a94ce925d1537f939a4f17d782efef1f973039d821cbe4bcc211/yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce", size = 353261, upload-time = "2025-06-10T00:45:05.992Z" }, - { url = "https://files.pythonhosted.org/packages/4b/23/0ed0922b47a4f5c6eb9065d5ff1e459747226ddce5c6a4c111e728c9f701/yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3", size = 335987, upload-time = "2025-06-10T00:45:08.227Z" }, - { url = "https://files.pythonhosted.org/packages/3e/49/bc728a7fe7d0e9336e2b78f0958a2d6b288ba89f25a1762407a222bf53c3/yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be", size = 329361, upload-time = "2025-06-10T00:45:10.11Z" }, - { url = "https://files.pythonhosted.org/packages/93/8f/b811b9d1f617c83c907e7082a76e2b92b655400e61730cd61a1f67178393/yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16", size = 346460, upload-time = "2025-06-10T00:45:12.055Z" }, - { url = "https://files.pythonhosted.org/packages/70/fd/af94f04f275f95da2c3b8b5e1d49e3e79f1ed8b6ceb0f1664cbd902773ff/yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513", size = 334486, upload-time = "2025-06-10T00:45:13.995Z" }, - { url = "https://files.pythonhosted.org/packages/84/65/04c62e82704e7dd0a9b3f61dbaa8447f8507655fd16c51da0637b39b2910/yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f", size = 342219, upload-time = "2025-06-10T00:45:16.479Z" }, - { url = "https://files.pythonhosted.org/packages/91/95/459ca62eb958381b342d94ab9a4b6aec1ddec1f7057c487e926f03c06d30/yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390", size = 350693, upload-time = "2025-06-10T00:45:18.399Z" }, - { url = "https://files.pythonhosted.org/packages/a6/00/d393e82dd955ad20617abc546a8f1aee40534d599ff555ea053d0ec9bf03/yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458", size = 355803, upload-time = "2025-06-10T00:45:20.677Z" }, - { url = "https://files.pythonhosted.org/packages/9e/ed/c5fb04869b99b717985e244fd93029c7a8e8febdfcffa06093e32d7d44e7/yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e", size = 341709, upload-time = "2025-06-10T00:45:23.221Z" }, - { url = "https://files.pythonhosted.org/packages/24/fd/725b8e73ac2a50e78a4534ac43c6addf5c1c2d65380dd48a9169cc6739a9/yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d", size = 86591, upload-time = "2025-06-10T00:45:25.793Z" }, - { url = "https://files.pythonhosted.org/packages/94/c3/b2e9f38bc3e11191981d57ea08cab2166e74ea770024a646617c9cddd9f6/yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f", size = 93003, upload-time = "2025-06-10T00:45:27.752Z" }, - { url = "https://files.pythonhosted.org/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77", size = 46542, upload-time = "2025-06-10T00:46:07.521Z" }, -] - -[[package]] -name = "zarr" -version = "2.18.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11' and platform_python_implementation == 'PyPy'", - "python_full_version < '3.11' and platform_python_implementation != 'PyPy'", -] -dependencies = [ - { name = "asciitree", marker = "python_full_version < '3.11'" }, - { name = "fasteners", marker = "python_full_version < '3.11' and sys_platform != 'emscripten'" }, - { name = "numcodecs", version = "0.13.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/23/c4/187a21ce7cf7c8f00c060dd0e04c2a81139bb7b1ab178bba83f2e1134ce2/zarr-2.18.3.tar.gz", hash = "sha256:2580d8cb6dd84621771a10d31c4d777dca8a27706a1a89b29f42d2d37e2df5ce", size = 3603224, upload-time = "2024-09-04T23:20:16.595Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/c9/142095e654c2b97133ff71df60979422717b29738b08bc8a1709a5d5e0d0/zarr-2.18.3-py3-none-any.whl", hash = "sha256:b1f7dfd2496f436745cdd4c7bcf8d3b4bc1dceef5fdd0d589c87130d842496dd", size = 210723, upload-time = "2024-09-04T23:20:14.491Z" }, -] - -[[package]] -name = "zarr" -version = "3.1.1" +version = "2.1.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12' and platform_python_implementation == 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", - "python_full_version >= '3.12' and platform_python_implementation != 'PyPy'", - "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", -] -dependencies = [ - { name = "donfig", marker = "python_full_version >= '3.11'" }, - { name = "numcodecs", version = "0.16.2", source = { registry = "https://pypi.org/simple" }, extra = ["crc32c"], marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "packaging", marker = "python_full_version >= '3.11'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/15/a9/29fe1800380092ae03ac6207d757f3e5affaf1fcd2e5ef074cf4fc68f0fa/zarr-3.1.1.tar.gz", hash = "sha256:17db72f37f2489452d2137ac891c4133b8f976f9189d8efd3e75f3b3add84e8c", size = 314075, upload-time = "2025-07-30T11:51:36.81Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/48/bde2f58cfbc9fd6ab844e2f2fd79d5e54195c12a17aa9b47c0b0e701a421/zarr-3.1.1-py3-none-any.whl", hash = "sha256:9a0b7e7c27bf62965b8eef6b8b8fdb9b47381f0738be35e40f37be6479b546be", size = 255373, upload-time = "2025-07-30T11:51:34.623Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/f7/37/ae31f40bec90de2f88d9597d0b5281e23ffe85b893a47ca5d9c05c63a4f6/wrapt-2.1.1.tar.gz", hash = "sha256:5fdcb09bf6db023d88f312bd0767594b414655d58090fc1c46b3414415f67fac", size = 81329 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/cb/4d5255d19bbd12be7f8ee2c1fb4269dddec9cef777ef17174d357468efaa/wrapt-2.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab8e3793b239db021a18782a5823fcdea63b9fe75d0e340957f5828ef55fcc02", size = 61143 }, + { url = "https://files.pythonhosted.org/packages/6f/07/7ed02daa35542023464e3c8b7cb937fa61f6c61c0361ecf8f5fecf8ad8da/wrapt-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7c0300007836373d1c2df105b40777986accb738053a92fe09b615a7a4547e9f", size = 61740 }, + { url = "https://files.pythonhosted.org/packages/c4/60/a237a4e4a36f6d966061ccc9b017627d448161b19e0a3ab80a7c7c97f859/wrapt-2.1.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2b27c070fd1132ab23957bcd4ee3ba707a91e653a9268dc1afbd39b77b2799f7", size = 121327 }, + { url = "https://files.pythonhosted.org/packages/ae/fe/9139058a3daa8818fc67e6460a2340e8bbcf3aef8b15d0301338bbe181ca/wrapt-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b0e36d845e8b6f50949b6b65fc6cd279f47a1944582ed4ec8258cd136d89a64", size = 122903 }, + { url = "https://files.pythonhosted.org/packages/91/10/b8479202b4164649675846a531763531f0a6608339558b5a0a718fc49a8d/wrapt-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4aeea04a9889370fcfb1ef828c4cc583f36a875061505cd6cd9ba24d8b43cc36", size = 121333 }, + { url = "https://files.pythonhosted.org/packages/5f/75/75fc793b791d79444aca2c03ccde64e8b99eda321b003f267d570b7b0985/wrapt-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d88b46bb0dce9f74b6817bc1758ff2125e1ca9e1377d62ea35b6896142ab6825", size = 120458 }, + { url = "https://files.pythonhosted.org/packages/d7/8f/c3f30d511082ca6d947c405f9d8f6c8eaf83cfde527c439ec2c9a30eb5ea/wrapt-2.1.1-cp312-cp312-win32.whl", hash = "sha256:63decff76ca685b5c557082dfbea865f3f5f6d45766a89bff8dc61d336348833", size = 58086 }, + { url = "https://files.pythonhosted.org/packages/0a/c8/37625b643eea2849f10c3b90f69c7462faa4134448d4443234adaf122ae5/wrapt-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:b828235d26c1e35aca4107039802ae4b1411be0fe0367dd5b7e4d90e562fcbcd", size = 60328 }, + { url = "https://files.pythonhosted.org/packages/ce/79/56242f07572d5682ba8065a9d4d9c2218313f576e3c3471873c2a5355ffd/wrapt-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:75128507413a9f1bcbe2db88fd18fbdbf80f264b82fa33a6996cdeaf01c52352", size = 58722 }, + { url = "https://files.pythonhosted.org/packages/f7/ca/3cf290212855b19af9fcc41b725b5620b32f470d6aad970c2593500817eb/wrapt-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ce9646e17fa7c3e2e7a87e696c7de66512c2b4f789a8db95c613588985a2e139", size = 61150 }, + { url = "https://files.pythonhosted.org/packages/9d/33/5b8f89a82a9859ce82da4870c799ad11ce15648b6e1c820fec3e23f4a19f/wrapt-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:428cfc801925454395aa468ba7ddb3ed63dc0d881df7b81626cdd433b4e2b11b", size = 61743 }, + { url = "https://files.pythonhosted.org/packages/1e/2f/60c51304fbdf47ce992d9eefa61fbd2c0e64feee60aaa439baf42ea6f40b/wrapt-2.1.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5797f65e4d58065a49088c3b32af5410751cd485e83ba89e5a45e2aa8905af98", size = 121341 }, + { url = "https://files.pythonhosted.org/packages/ad/03/ce5256e66dd94e521ad5e753c78185c01b6eddbed3147be541f4d38c0cb7/wrapt-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a2db44a71202c5ae4bb5f27c6d3afbc5b23053f2e7e78aa29704541b5dad789", size = 122947 }, + { url = "https://files.pythonhosted.org/packages/eb/ae/50ca8854b81b946a11a36fcd6ead32336e6db2c14b6e4a8b092b80741178/wrapt-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8d5350c3590af09c1703dd60ec78a7370c0186e11eaafb9dda025a30eee6492d", size = 121370 }, + { url = "https://files.pythonhosted.org/packages/fb/d9/d6a7c654e0043319b4cc137a4caaf7aa16b46b51ee8df98d1060254705b7/wrapt-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d9b076411bed964e752c01b49fd224cc385f3a96f520c797d38412d70d08359", size = 120465 }, + { url = "https://files.pythonhosted.org/packages/55/90/65be41e40845d951f714b5a77e84f377a3787b1e8eee6555a680da6d0db5/wrapt-2.1.1-cp313-cp313-win32.whl", hash = "sha256:0bb7207130ce6486727baa85373503bf3334cc28016f6928a0fa7e19d7ecdc06", size = 58090 }, + { url = "https://files.pythonhosted.org/packages/5f/66/6a09e0294c4fc8c26028a03a15191721c9271672467cc33e6617ee0d91d2/wrapt-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:cbfee35c711046b15147b0ae7db9b976f01c9520e6636d992cd9e69e5e2b03b1", size = 60341 }, + { url = "https://files.pythonhosted.org/packages/7a/f0/20ceb8b701e9a71555c87a5ddecbed76ec16742cf1e4b87bbaf26735f998/wrapt-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:7d2756061022aebbf57ba14af9c16e8044e055c22d38de7bf40d92b565ecd2b0", size = 58731 }, + { url = "https://files.pythonhosted.org/packages/80/b4/fe95beb8946700b3db371f6ce25115217e7075ca063663b8cca2888ba55c/wrapt-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4814a3e58bc6971e46baa910ecee69699110a2bf06c201e24277c65115a20c20", size = 62969 }, + { url = "https://files.pythonhosted.org/packages/b8/89/477b0bdc784e3299edf69c279697372b8bd4c31d9c6966eae405442899df/wrapt-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:106c5123232ab9b9f4903692e1fa0bdc231510098f04c13c3081f8ad71c3d612", size = 63606 }, + { url = "https://files.pythonhosted.org/packages/ed/55/9d0c1269ab76de87715b3b905df54dd25d55bbffd0b98696893eb613469f/wrapt-2.1.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1a40b83ff2535e6e56f190aff123821eea89a24c589f7af33413b9c19eb2c738", size = 152536 }, + { url = "https://files.pythonhosted.org/packages/44/18/2004766030462f79ad86efaa62000b5e39b1ff001dcce86650e1625f40ae/wrapt-2.1.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:789cea26e740d71cf1882e3a42bb29052bc4ada15770c90072cb47bf73fb3dbf", size = 158697 }, + { url = "https://files.pythonhosted.org/packages/e1/bb/0a880fa0f35e94ee843df4ee4dd52a699c9263f36881311cfb412c09c3e5/wrapt-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ba49c14222d5e5c0ee394495a8655e991dc06cbca5398153aefa5ac08cd6ccd7", size = 155563 }, + { url = "https://files.pythonhosted.org/packages/42/ff/cd1b7c4846c8678fac359a6eb975dc7ab5bd606030adb22acc8b4a9f53f1/wrapt-2.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ac8cda531fe55be838a17c62c806824472bb962b3afa47ecbd59b27b78496f4e", size = 150161 }, + { url = "https://files.pythonhosted.org/packages/38/ec/67c90a7082f452964b4621e4890e9a490f1add23cdeb7483cc1706743291/wrapt-2.1.1-cp313-cp313t-win32.whl", hash = "sha256:b8af75fe20d381dd5bcc9db2e86a86d7fcfbf615383a7147b85da97c1182225b", size = 59783 }, + { url = "https://files.pythonhosted.org/packages/ec/08/466afe4855847d8febdfa2c57c87e991fc5820afbdef01a273683dfd15a0/wrapt-2.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:45c5631c9b6c792b78be2d7352129f776dd72c605be2c3a4e9be346be8376d83", size = 63082 }, + { url = "https://files.pythonhosted.org/packages/9a/62/60b629463c28b15b1eeadb3a0691e17568622b12aa5bfa7ebe9b514bfbeb/wrapt-2.1.1-cp313-cp313t-win_arm64.whl", hash = "sha256:da815b9263947ac98d088b6414ac83507809a1d385e4632d9489867228d6d81c", size = 60251 }, + { url = "https://files.pythonhosted.org/packages/95/a0/1c2396e272f91efe6b16a6a8bce7ad53856c8f9ae4f34ceaa711d63ec9e1/wrapt-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9aa1765054245bb01a37f615503290d4e207e3fd59226e78341afb587e9c1236", size = 61311 }, + { url = "https://files.pythonhosted.org/packages/b0/9a/d2faba7e61072a7507b5722db63562fdb22f5a24e237d460d18755627f15/wrapt-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:feff14b63a6d86c1eee33a57f77573649f2550935981625be7ff3cb7342efe05", size = 61805 }, + { url = "https://files.pythonhosted.org/packages/db/56/073989deb4b5d7d6e7ea424476a4ae4bda02140f2dbeaafb14ba4864dd60/wrapt-2.1.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:81fc5f22d5fcfdbabde96bb3f5379b9f4476d05c6d524d7259dc5dfb501d3281", size = 120308 }, + { url = "https://files.pythonhosted.org/packages/d1/b6/84f37261295e38167a29eb82affaf1dc15948dc416925fe2091beee8e4ac/wrapt-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:951b228ecf66def855d22e006ab9a1fc12535111ae7db2ec576c728f8ddb39e8", size = 122688 }, + { url = "https://files.pythonhosted.org/packages/ea/80/32db2eec6671f80c65b7ff175be61bc73d7f5223f6910b0c921bbc4bd11c/wrapt-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ddf582a95641b9a8c8bd643e83f34ecbbfe1b68bc3850093605e469ab680ae3", size = 121115 }, + { url = "https://files.pythonhosted.org/packages/49/ef/dcd00383df0cd696614127902153bf067971a5aabcd3c9dcb2d8ef354b2a/wrapt-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fc5c500966bf48913f795f1984704e6d452ba2414207b15e1f8c339a059d5b16", size = 119484 }, + { url = "https://files.pythonhosted.org/packages/76/29/0630280cdd2bd8f86f35cb6854abee1c9d6d1a28a0c6b6417cd15d378325/wrapt-2.1.1-cp314-cp314-win32.whl", hash = "sha256:4aa4baadb1f94b71151b8e44a0c044f6af37396c3b8bcd474b78b49e2130a23b", size = 58514 }, + { url = "https://files.pythonhosted.org/packages/db/19/5bed84f9089ed2065f6aeda5dfc4f043743f642bc871454b261c3d7d322b/wrapt-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:860e9d3fd81816a9f4e40812f28be4439ab01f260603c749d14be3c0a1170d19", size = 60763 }, + { url = "https://files.pythonhosted.org/packages/e4/cb/b967f2f9669e4249b4fe82e630d2a01bc6b9e362b9b12ed91bbe23ae8df4/wrapt-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:3c59e103017a2c1ea0ddf589cbefd63f91081d7ce9d491d69ff2512bb1157e23", size = 59051 }, + { url = "https://files.pythonhosted.org/packages/eb/19/6fed62be29f97eb8a56aff236c3f960a4b4a86e8379dc7046a8005901a97/wrapt-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9fa7c7e1bee9278fc4f5dd8275bc8d25493281a8ec6c61959e37cc46acf02007", size = 63059 }, + { url = "https://files.pythonhosted.org/packages/0a/1c/b757fd0adb53d91547ed8fad76ba14a5932d83dde4c994846a2804596378/wrapt-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:39c35e12e8215628984248bd9c8897ce0a474be2a773db207eb93414219d8469", size = 63618 }, + { url = "https://files.pythonhosted.org/packages/10/fe/e5ae17b1480957c7988d991b93df9f2425fc51f128cf88144d6a18d0eb12/wrapt-2.1.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:94ded4540cac9125eaa8ddf5f651a7ec0da6f5b9f248fe0347b597098f8ec14c", size = 152544 }, + { url = "https://files.pythonhosted.org/packages/3e/cc/99aed210c6b547b8a6e4cb9d1425e4466727158a6aeb833aa7997e9e08dd/wrapt-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da0af328373f97ed9bdfea24549ac1b944096a5a71b30e41c9b8b53ab3eec04a", size = 158700 }, + { url = "https://files.pythonhosted.org/packages/81/0e/d442f745f4957944d5f8ad38bc3a96620bfff3562533b87e486e979f3d99/wrapt-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4ad839b55f0bf235f8e337ce060572d7a06592592f600f3a3029168e838469d3", size = 155561 }, + { url = "https://files.pythonhosted.org/packages/51/ac/9891816280e0018c48f8dfd61b136af7b0dcb4a088895db2531acde5631b/wrapt-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0d89c49356e5e2a50fa86b40e0510082abcd0530f926cbd71cf25bee6b9d82d7", size = 150188 }, + { url = "https://files.pythonhosted.org/packages/24/98/e2f273b6d70d41f98d0739aa9a269d0b633684a5fb17b9229709375748d4/wrapt-2.1.1-cp314-cp314t-win32.whl", hash = "sha256:f4c7dd22cf7f36aafe772f3d88656559205c3af1b7900adfccb70edeb0d2abc4", size = 60425 }, + { url = "https://files.pythonhosted.org/packages/1e/06/b500bfc38a4f82d89f34a13069e748c82c5430d365d9e6b75afb3ab74457/wrapt-2.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f76bc12c583ab01e73ba0ea585465a41e48d968f6d1311b4daec4f8654e356e3", size = 63855 }, + { url = "https://files.pythonhosted.org/packages/d9/cc/5f6193c32166faee1d2a613f278608e6f3b95b96589d020f0088459c46c9/wrapt-2.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7ea74fc0bec172f1ae5f3505b6655c541786a5cabe4bbc0d9723a56ac32eb9b9", size = 60443 }, + { url = "https://files.pythonhosted.org/packages/c4/da/5a086bf4c22a41995312db104ec2ffeee2cf6accca9faaee5315c790377d/wrapt-2.1.1-py3-none-any.whl", hash = "sha256:3b0f4629eb954394a3d7c7a1c8cca25f0b07cefe6aa8545e862e9778152de5b7", size = 43886 }, ] [[package]] name = "zipp" version = "3.23.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276 }, ]