From 21e4f5061454d42509c2b348e3c814f845e4ac26 Mon Sep 17 00:00:00 2001 From: stripodi Date: Mon, 3 Aug 2026 17:08:46 +0200 Subject: [PATCH] feat: implement Product STAC Extension --- docs/api/extensions.rst | 1 + docs/api/extensions/product.rst | 12 + docs/conf.py | 4 + extensions/product/README.md | 13 + extensions/product/pyproject.toml | 36 ++ .../product/pystac/extensions/product.py | 283 ++++++++++++++ extensions/product/pystac/extensions/py.typed | 1 + extensions/product/tests/test_product.py | 129 +++++++ pyproject.toml | 3 + pystac/extensions/ext.py | 19 + uv.lock | 356 +++++++++--------- 11 files changed, 686 insertions(+), 171 deletions(-) create mode 100644 docs/api/extensions/product.rst create mode 100644 extensions/product/README.md create mode 100644 extensions/product/pyproject.toml create mode 100644 extensions/product/pystac/extensions/product.py create mode 100644 extensions/product/pystac/extensions/py.typed create mode 100644 extensions/product/tests/test_product.py diff --git a/docs/api/extensions.rst b/docs/api/extensions.rst index ce37d3215..89406271b 100644 --- a/docs/api/extensions.rst +++ b/docs/api/extensions.rst @@ -24,6 +24,7 @@ pystac.extensions mlm.AssetGeneralMLMExtension mlm.AssetDetailedMLMExtension pointcloud.PointcloudExtension + product.ProductExtension projection.ProjectionExtension raster.RasterExtension render.RenderExtension diff --git a/docs/api/extensions/product.rst b/docs/api/extensions/product.rst new file mode 100644 index 000000000..8951ff7ba --- /dev/null +++ b/docs/api/extensions/product.rst @@ -0,0 +1,12 @@ +pystac.extensions.product +========================= + +The Product Extension exposes product packaging, timeliness, acquisition type, +and lifecycle status metadata on Items, Collections, Assets, and Item Asset +Definitions. Lifecycle status values are represented by +:class:`~pystac.extensions.product.ProductStatus`; Collection summaries accept +lists of those values. + +.. automodule:: pystac.extensions.product + :members: + :undoc-members: diff --git a/docs/conf.py b/docs/conf.py index 9af5c5707..22baeb7f5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -256,3 +256,7 @@ ("py:class", "HREF"), # this one partially works ("py:class", "jsonschema.validators.Draft7Validator"), ] + +nitpick_ignore_regex = [ + ("py:class", r"pystac\.extensions\.[^.]+\.T"), +] diff --git a/extensions/product/README.md b/extensions/product/README.md new file mode 100644 index 000000000..2cc1efece --- /dev/null +++ b/extensions/product/README.md @@ -0,0 +1,13 @@ +# pystac-ext-product + +[PySTAC](https://pypi.org/project/pystac/) extension package for the [Product Extension](https://github.com/stac-extensions/product). +This extension provides fields for describing product type, timeliness, timeliness category, and acquisition type. + +## Supported versions + +- [v1.0.0](https://stac-extensions.github.io/product/v1.0.0/schema.json) + +## Versioning + +This package's version corresponds to the version of the extension specification it targets. +When we release updates to the package code without changing the target extension version, we use [post releases](https://packaging.python.org/en/latest/discussions/versioning/#post-releases), e.g. `1.0.0.post1`. diff --git a/extensions/product/pyproject.toml b/extensions/product/pyproject.toml new file mode 100644 index 000000000..79b56f341 --- /dev/null +++ b/extensions/product/pyproject.toml @@ -0,0 +1,36 @@ +[project] +name = "pystac-ext-product" +description = "Product extension for PySTAC" +readme = "README.md" +version = "1.0.0" +authors = [] +maintainers = [] +keywords = ["pystac", "imagery", "raster", "catalog", "STAC", "product"] +license = { text = "Apache-2.0" } +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "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", +] +requires-python = ">=3.10" +dependencies = ["pystac-core"] + +[project.urls] +Documentation = "https://pystac.readthedocs.io" +Repository = "https://github.com/stac-utils/pystac" +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" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["pystac"] diff --git a/extensions/product/pystac/extensions/product.py b/extensions/product/pystac/extensions/product.py new file mode 100644 index 000000000..d336518d4 --- /dev/null +++ b/extensions/product/pystac/extensions/product.py @@ -0,0 +1,283 @@ +"""Implements the :stac-ext:`Product Extension `.""" + +from __future__ import annotations + +from collections.abc import Iterable +from typing import Any, Generic, Literal, TypeVar, cast + +import pystac +from pystac.extensions.base import ( + ExtensionManagementMixin, + PropertiesExtension, + SummariesExtension, +) +from pystac.extensions.hooks import ExtensionHooks +from pystac.utils import StringEnum + +T = TypeVar( + "T", + pystac.Collection, + pystac.Item, + pystac.Asset, + pystac.ItemAssetDefinition, +) + +SCHEMA_URI: str = "https://stac-extensions.github.io/product/v1.0.0/schema.json" + +PREFIX: str = "product:" +TYPE_PROP: str = PREFIX + "type" +TIMELINESS_PROP: str = PREFIX + "timeliness" +TIMELINESS_CATEGORY_PROP: str = PREFIX + "timeliness_category" +ACQUISITION_TYPE_PROP: str = PREFIX + "acquisition_type" +STATUS_PROP: str = PREFIX + "status" + + +class AcquisitionType(StringEnum): + """Allowed values for product:acquisition_type.""" + + NOMINAL = "nominal" + CALIBRATION = "calibration" + OTHER = "other" + + +class ProductStatus(StringEnum): + """Allowed values for :data:`product:status `.""" + + ARCHIVED = "archived" + ACQUIRED = "acquired" + CANCELLED = "cancelled" + FAILED = "failed" + PLANNED = "planned" + POTENTIAL = "potential" + REJECTED = "rejected" + QUALITY_DEGRADED = "qualitydegraded" + + +class ProductExtension( + Generic[T], + PropertiesExtension, + ExtensionManagementMixin[pystac.Collection | pystac.Item], +): + name: Literal["product"] = "product" + + def apply( + self, + *, + product_type: str | None = None, + timeliness: str | None = None, + timeliness_category: str | None = None, + acquisition_type: AcquisitionType | str | None = None, + status: ProductStatus | None = None, + ) -> None: + if ( + timeliness_category is not None + and timeliness is None + and self.timeliness is None + ): + raise ValueError( + f"'{TIMELINESS_CATEGORY_PROP}' requires '{TIMELINESS_PROP}' to be set." + ) + + self.product_type = product_type + if timeliness is not None: + self.timeliness = timeliness + if timeliness_category is not None: + self.timeliness_category = timeliness_category + self.acquisition_type = acquisition_type + self.status = status + + @property + def product_type(self) -> str | None: + return self._get_property(TYPE_PROP, str) + + @product_type.setter + def product_type(self, v: str | None) -> None: + self._set_property(TYPE_PROP, v) + + @property + def timeliness(self) -> str | None: + return self._get_property(TIMELINESS_PROP, str) + + @timeliness.setter + def timeliness(self, v: str | None) -> None: + self._set_property(TIMELINESS_PROP, v) + + @property + def timeliness_category(self) -> str | None: + return self._get_property(TIMELINESS_CATEGORY_PROP, str) + + @timeliness_category.setter + def timeliness_category(self, v: str | None) -> None: + if v is not None and self.timeliness is None: + raise ValueError( + f"'{TIMELINESS_CATEGORY_PROP}' requires '{TIMELINESS_PROP}' to be set." + ) + self._set_property(TIMELINESS_CATEGORY_PROP, v) + + @property + def acquisition_type(self) -> AcquisitionType | str | None: + raw = self._get_property(ACQUISITION_TYPE_PROP, str) + if raw is None: + return None + try: + return AcquisitionType(raw) + except Exception: + return raw + + @acquisition_type.setter + def acquisition_type(self, v: AcquisitionType | str | None) -> None: + self._set_property(ACQUISITION_TYPE_PROP, None if v is None else str(v)) + + @property + def status(self) -> ProductStatus | None: + """Gets the product lifecycle status.""" + raw = self._get_property(STATUS_PROP, str) + return None if raw is None else ProductStatus(raw) + + @status.setter + def status(self, v: ProductStatus | None) -> None: + """Sets the product lifecycle status. + + Args: + v: The product lifecycle status. + """ + self._set_property(STATUS_PROP, None if v is None else v.value) + + @classmethod + def get_schema_uri(cls) -> str: + return SCHEMA_URI + + @classmethod + def ext(cls, obj: T, add_if_missing: bool = False) -> ProductExtension[T]: + if isinstance(obj, pystac.Collection): + cls.ensure_has_extension(obj, add_if_missing) + return cast(ProductExtension[T], CollectionProductExtension(obj)) + if isinstance(obj, pystac.Item): + cls.ensure_has_extension(obj, add_if_missing) + return cast(ProductExtension[T], ItemProductExtension(obj)) + if isinstance(obj, pystac.Asset): + cls.ensure_owner_has_extension(obj, add_if_missing) + return cast(ProductExtension[T], AssetProductExtension(obj)) + if isinstance(obj, pystac.ItemAssetDefinition): + cls.ensure_owner_has_extension(obj, add_if_missing) + return cast(ProductExtension[T], ItemAssetsProductExtension(obj)) + raise pystac.ExtensionTypeError(cls._ext_error_message(obj)) + + @classmethod + def summaries( + cls, obj: pystac.Collection, add_if_missing: bool = False + ) -> SummariesProductExtension: + cls.ensure_has_extension(obj, add_if_missing) + return SummariesProductExtension(obj) + + +class CollectionProductExtension(ProductExtension[pystac.Collection]): + collection: pystac.Collection + properties: dict[str, Any] + + def __init__(self, collection: pystac.Collection): + self.collection = collection + self.properties = collection.extra_fields + + def __repr__(self) -> str: + return f"" + + +class ItemProductExtension(ProductExtension[pystac.Item]): + item: pystac.Item + properties: dict[str, Any] + + def __init__(self, item: pystac.Item): + self.item = item + self.properties = item.properties + + def __repr__(self) -> str: + return f"" + + +class AssetProductExtension(ProductExtension[pystac.Asset]): + asset: pystac.Asset + asset_href: str + properties: dict[str, Any] + additional_read_properties: Iterable[dict[str, Any]] | None + + def __init__(self, asset: pystac.Asset): + self.asset = asset + self.asset_href = asset.href + self.properties = asset.extra_fields + if asset.owner and isinstance(asset.owner, pystac.Item): + self.additional_read_properties = [asset.owner.properties] + else: + self.additional_read_properties = None + + def __repr__(self) -> str: + return f"" + + +class ItemAssetsProductExtension(ProductExtension[pystac.ItemAssetDefinition]): + asset_defn: pystac.ItemAssetDefinition + properties: dict[str, Any] + + def __init__(self, item_asset: pystac.ItemAssetDefinition): + self.asset_defn = item_asset + self.properties = item_asset.properties + + def __repr__(self) -> str: + return "" + + +class SummariesProductExtension(SummariesExtension): + @property + def product_type(self) -> list[str] | None: + return self.summaries.get_list(TYPE_PROP) + + @product_type.setter + def product_type(self, v: list[str] | None) -> None: + self._set_summary(TYPE_PROP, v) + + @property + def timeliness(self) -> list[str] | None: + return self.summaries.get_list(TIMELINESS_PROP) + + @timeliness.setter + def timeliness(self, v: list[str] | None) -> None: + self._set_summary(TIMELINESS_PROP, v) + + @property + def timeliness_category(self) -> list[str] | None: + return self.summaries.get_list(TIMELINESS_CATEGORY_PROP) + + @timeliness_category.setter + def timeliness_category(self, v: list[str] | None) -> None: + self._set_summary(TIMELINESS_CATEGORY_PROP, v) + + @property + def acquisition_type(self) -> list[str] | None: + return self.summaries.get_list(ACQUISITION_TYPE_PROP) + + @acquisition_type.setter + def acquisition_type(self, v: list[str] | None) -> None: + self._set_summary(ACQUISITION_TYPE_PROP, v) + + @property + def status(self) -> list[ProductStatus] | None: + """Get the product lifecycle statuses from the summaries.""" + return self.summaries.get_list(STATUS_PROP) + + @status.setter + def status(self, v: list[ProductStatus] | None) -> None: + """Set the product lifecycle statuses in the summaries. + + Args: + v: The product lifecycle statuses. + """ + self._set_summary(STATUS_PROP, v) + + +class ProductExtensionHooks(ExtensionHooks): + schema_uri: str = SCHEMA_URI + prev_extension_ids = {"product"} + stac_object_types = {pystac.STACObjectType.COLLECTION, pystac.STACObjectType.ITEM} + + +PRODUCT_EXTENSION_HOOKS: ExtensionHooks = ProductExtensionHooks() diff --git a/extensions/product/pystac/extensions/py.typed b/extensions/product/pystac/extensions/py.typed new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/extensions/product/pystac/extensions/py.typed @@ -0,0 +1 @@ + diff --git a/extensions/product/tests/test_product.py b/extensions/product/tests/test_product.py new file mode 100644 index 000000000..4f5dbd1d3 --- /dev/null +++ b/extensions/product/tests/test_product.py @@ -0,0 +1,129 @@ +from __future__ import annotations + +from datetime import datetime +from typing import TypeAlias + +import pytest + +import pystac +from pystac.extensions.product import ( + ACQUISITION_TYPE_PROP, + PRODUCT_EXTENSION_HOOKS, + STATUS_PROP, + TIMELINESS_CATEGORY_PROP, + TIMELINESS_PROP, + TYPE_PROP, + AcquisitionType, + ProductExtension, + ProductStatus, +) + +TemporalIntervals: TypeAlias = list[list[datetime | None]] + + +def _dt(s: str) -> datetime: + return datetime.fromisoformat(s.replace("Z", "+00:00")) + + +def make_item() -> pystac.Item: + return pystac.Item( + id="i", + geometry=None, + bbox=None, + datetime=_dt("2020-01-01T00:00:00Z"), + properties={}, + start_datetime=None, + end_datetime=None, + ) + + +def make_collection() -> pystac.Collection: + temporal_intervals: TemporalIntervals = [[None, None]] + return pystac.Collection( + id="c", + description="d", + extent=pystac.Extent( + pystac.SpatialExtent([[-180.0, -90.0, 180.0, 90.0]]), + pystac.TemporalExtent(temporal_intervals), + ), + license="proprietary", + ) + + +def test_apply_requires_timeliness_when_setting_category() -> None: + item = make_item() + ext = ProductExtension.ext(item, add_if_missing=True) + + with pytest.raises(ValueError): + ext.apply(timeliness_category="NRT") # timeliness missing and not already set + + # If timeliness is already set, setting category is allowed + ext.timeliness = "PT3H" + ext.apply(timeliness_category="NRT") + assert item.properties[TIMELINESS_CATEGORY_PROP] == "NRT" + + +def test_item_apply_roundtrip_and_acquisition_type_enum() -> None: + item = make_item() + ext = ProductExtension.ext(item, add_if_missing=True) + + ext.apply( + product_type="SLC", + timeliness="PT3H", + timeliness_category="NRT", + acquisition_type=AcquisitionType.NOMINAL, + status=ProductStatus.ACQUIRED, + ) + + assert item.properties[TYPE_PROP] == "SLC" + assert item.properties[TIMELINESS_PROP] == "PT3H" + assert item.properties[TIMELINESS_CATEGORY_PROP] == "NRT" + assert item.properties[ACQUISITION_TYPE_PROP] == "nominal" + assert item.properties[STATUS_PROP] == "acquired" + assert ext.acquisition_type == AcquisitionType.NOMINAL + assert ext.status == ProductStatus.ACQUIRED + + # Unknown strings should roundtrip as raw strings + ext.acquisition_type = "nonstandard" + assert item.properties[ACQUISITION_TYPE_PROP] == "nonstandard" + assert ext.acquisition_type == "nonstandard" + + +def test_collection_top_level_fields() -> None: + col = make_collection() + ext = ProductExtension.ext(col, add_if_missing=True) + + ext.product_type = "L1C" + assert col.extra_fields[TYPE_PROP] == "L1C" + + +def test_summaries_wrapper_sets_lists() -> None: + col = make_collection() + sext = ProductExtension.summaries(col, add_if_missing=True) + + sext.product_type = ["L1C", "L2A"] + sext.status = [ProductStatus.PLANNED, ProductStatus.ACQUIRED] + assert col.summaries.lists[TYPE_PROP] == ["L1C", "L2A"] + assert col.summaries.lists[STATUS_PROP] == [ + ProductStatus.PLANNED, + ProductStatus.ACQUIRED, + ] + + +def test_product_status_values_match_schema() -> None: + assert {status.value for status in ProductStatus} == { + "archived", + "acquired", + "cancelled", + "failed", + "planned", + "potential", + "rejected", + "qualitydegraded", + } + + +def test_extension_hooks_are_declared() -> None: + assert PRODUCT_EXTENSION_HOOKS.schema_uri == ProductExtension.get_schema_uri() + assert "product" in PRODUCT_EXTENSION_HOOKS.prev_extension_ids + assert pystac.STACObjectType.COLLECTION in PRODUCT_EXTENSION_HOOKS.stac_object_types diff --git a/pyproject.toml b/pyproject.toml index 0bd3e592e..736e2c6e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ dependencies = [ "pystac-ext-mgrs", "pystac-ext-mlm", "pystac-ext-pointcloud", + "pystac-ext-product", "pystac-ext-projection", "pystac-ext-raster", "pystac-ext-render", @@ -145,6 +146,7 @@ mypy_path = [ "extensions/mgrs", "extensions/mlm", "extensions/pointcloud", + "extensions/product", "extensions/projection", "extensions/raster", "extensions/render", @@ -191,6 +193,7 @@ pystac-ext-label = { workspace = true } pystac-ext-mgrs = { workspace = true } pystac-ext-mlm = { workspace = true } pystac-ext-pointcloud = { workspace = true } +pystac-ext-product = { workspace = true } pystac-ext-projection = { workspace = true } pystac-ext-raster = { workspace = true } pystac-ext-render = { workspace = true } diff --git a/pystac/extensions/ext.py b/pystac/extensions/ext.py index 0b72e9feb..552179fbf 100644 --- a/pystac/extensions/ext.py +++ b/pystac/extensions/ext.py @@ -25,6 +25,7 @@ MLMExtension, ) from pystac.extensions.pointcloud import PointcloudExtension +from pystac.extensions.product import ProductExtension from pystac.extensions.projection import ProjectionExtension from pystac.extensions.raster import RasterExtension from pystac.extensions.render import Render, RenderExtension @@ -55,6 +56,7 @@ "mgrs", "mlm", "pc", + "product", "proj", "raster", "render", @@ -79,6 +81,7 @@ MgrsExtension.name: MgrsExtension, MLMExtension.name: MLMExtension, PointcloudExtension.name: PointcloudExtension, + ProductExtension.name: ProductExtension, ProjectionExtension.name: ProjectionExtension, RasterExtension.name: RasterExtension, RenderExtension.name: RenderExtension, @@ -156,6 +159,10 @@ class CollectionExt(CatalogExt): def cube(self) -> DatacubeExtension[Collection]: return DatacubeExtension.ext(self.stac_object) + @property + def product(self) -> ProductExtension[Collection]: + return ProductExtension.ext(self.stac_object) + @property def item_assets(self) -> dict[str, ItemAssetDefinition]: return ItemAssetsExtension.ext(self.stac_object).item_assets @@ -248,6 +255,10 @@ def mlm(self) -> MLMExtension[Item]: def pc(self) -> PointcloudExtension[Item]: return PointcloudExtension.ext(self.stac_object) + @property + def product(self) -> ProductExtension[Item]: + return ProductExtension.ext(self.stac_object) + @property def proj(self) -> ProjectionExtension[Item]: return ProjectionExtension.ext(self.stac_object) @@ -363,6 +374,10 @@ def eo(self) -> EOExtension[U]: def pc(self) -> PointcloudExtension[U]: return PointcloudExtension.ext(self.stac_object) + @property + def product(self) -> ProductExtension[U]: + return ProductExtension.ext(self.stac_object) + @property def proj(self) -> ProjectionExtension[U]: return ProjectionExtension.ext(self.stac_object) @@ -436,6 +451,10 @@ class ItemAssetExt(_AssetExt[ItemAssetDefinition]): def mlm(self) -> MLMExtension[ItemAssetDefinition]: return MLMExtension.ext(self.stac_object) + @property + def product(self) -> ProductExtension[ItemAssetDefinition]: + return ProductExtension.ext(self.stac_object) + @property def storage(self) -> StorageExtension[ItemAssetDefinition]: return StorageExtension.ext(self.stac_object) diff --git a/uv.lock b/uv.lock index 70a24d2ee..d5fc6e709 100644 --- a/uv.lock +++ b/uv.lock @@ -35,6 +35,7 @@ members = [ "pystac-ext-mgrs", "pystac-ext-mlm", "pystac-ext-pointcloud", + "pystac-ext-product", "pystac-ext-projection", "pystac-ext-raster", "pystac-ext-render", @@ -850,7 +851,7 @@ name = "click-plugins" version = "1.1.1.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click" }, + { name = "click", marker = "python_full_version < '3.12'" }, ] 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" } wheels = [ @@ -1178,7 +1179,7 @@ name = "donfig" version = "0.8.1.post1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyyaml" }, + { name = "pyyaml", marker = "python_full_version >= '3.11'" }, ] 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" } wheels = [ @@ -1190,7 +1191,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -1538,17 +1539,17 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, - { name = "exceptiongroup" }, - { name = "jedi" }, - { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit" }, - { name = "pygments" }, - { name = "stack-data" }, - { name = "traitlets" }, - { name = "typing-extensions" }, + { 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/40/18/f8598d287006885e7136451fdea0755af4ebcbfe342836f24deefaed1164/ipython-8.39.0.tar.gz", hash = "sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624", size = 5513971, upload-time = "2026-03-27T10:02:13.94Z" } wheels = [ @@ -1574,18 +1575,18 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, - { name = "ipython-pygments-lexers" }, - { name = "jedi" }, - { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit" }, - { name = "psutil", marker = "sys_platform != 'cygwin' and sys_platform != 'emscripten'" }, - { name = "pygments" }, - { name = "stack-data" }, - { name = "traitlets" }, - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { 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 = "psutil", marker = "python_full_version >= '3.11' and sys_platform != 'cygwin' and sys_platform != 'emscripten'" }, + { 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/53/59/165d3b4d75cc34add3122c4417ecb229085140ac573103c223cd01dde96f/ipython-9.15.0.tar.gz", hash = "sha256:da2819ce2aa83135257df830660b1176d986c3d2876db24df01974fa955b2756", size = 4442580, upload-time = "2026-06-26T11:03:35.913Z" } wheels = [ @@ -1597,7 +1598,7 @@ name = "ipython-pygments-lexers" version = "1.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pygments" }, + { 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 = [ @@ -2526,7 +2527,7 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { 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 = [ @@ -2567,9 +2568,9 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/44/bd/8a391e7c356366224734efd24da929cc4796fff468bfb179fe1af6548535/numcodecs-0.16.5.tar.gz", hash = "sha256:0d0fb60852f84c0bd9543cc4d2ab9eefd37fc8efcc410acd4777e62a1d300318", size = 6276387, upload-time = "2025-11-21T02:49:48.986Z" } wheels = [ @@ -2982,10 +2983,10 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, - { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, + { name = "pytz", marker = "python_full_version < '3.11'" }, + { name = "tzdata", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } wheels = [ @@ -3057,10 +3058,10 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "python-dateutil" }, - { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" } wheels = [ @@ -3158,7 +3159,7 @@ name = "pexpect" version = "4.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess" }, + { name = "ptyprocess", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] 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 = [ @@ -3561,13 +3562,13 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "accessible-pygments" }, - { name = "babel" }, - { name = "beautifulsoup4" }, - { name = "docutils" }, - { name = "pygments" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" } }, - { name = "typing-extensions" }, + { name = "accessible-pygments", marker = "python_full_version < '3.11'" }, + { name = "babel", marker = "python_full_version < '3.11'" }, + { name = "beautifulsoup4", marker = "python_full_version < '3.11'" }, + { name = "docutils", marker = "python_full_version < '3.11'" }, + { name = "pygments", marker = "python_full_version < '3.11'" }, + { name = "sphinx", version = "8.1.3", 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/39/7e/e3defb93f30557ae825a3b3fbc2c6728301e5e9505a85e00d8503345c42c/pydata_sphinx_theme-0.19.0.tar.gz", hash = "sha256:148ba092bd6937b3321385dc482cc69a29ad2ede36547b4f010ade782bc6a062", size = 5004933, upload-time = "2026-06-15T09:31:02.268Z" } wheels = [ @@ -3593,14 +3594,14 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "accessible-pygments" }, - { name = "babel" }, - { name = "beautifulsoup4" }, - { name = "docutils" }, - { name = "jinja2" }, - { name = "pygments" }, - { name = "requests" }, - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "accessible-pygments", marker = "python_full_version >= '3.11'" }, + { name = "babel", marker = "python_full_version >= '3.11'" }, + { name = "beautifulsoup4", marker = "python_full_version >= '3.11'" }, + { name = "docutils", marker = "python_full_version >= '3.11'" }, + { name = "jinja2", marker = "python_full_version >= '3.11'" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, + { name = "requests", marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ae/8e/add936feaaa9dade7d5b87c6852566d85e518b38ad32224dea32ef958984/pydata_sphinx_theme-0.20.0.tar.gz", hash = "sha256:0da172d41e19a66de875f4002f7054b385372ec65763852193791e658d50bb4a", size = 5004756, upload-time = "2026-07-09T09:09:14.693Z" } @@ -3663,7 +3664,7 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "certifi" }, + { 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 = [ @@ -3720,7 +3721,7 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "certifi" }, + { 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 = [ @@ -3805,6 +3806,7 @@ dependencies = [ { name = "pystac-ext-mgrs" }, { name = "pystac-ext-mlm" }, { name = "pystac-ext-pointcloud" }, + { name = "pystac-ext-product" }, { name = "pystac-ext-projection" }, { name = "pystac-ext-raster" }, { name = "pystac-ext-render" }, @@ -3913,6 +3915,7 @@ requires-dist = [ { name = "pystac-ext-mgrs", editable = "extensions/mgrs" }, { name = "pystac-ext-mlm", editable = "extensions/mlm" }, { name = "pystac-ext-pointcloud", editable = "extensions/pointcloud" }, + { name = "pystac-ext-product", editable = "extensions/product" }, { name = "pystac-ext-projection", editable = "extensions/projection" }, { name = "pystac-ext-raster", editable = "extensions/raster" }, { name = "pystac-ext-render", editable = "extensions/render" }, @@ -4132,6 +4135,17 @@ dependencies = [ [package.metadata] requires-dist = [{ name = "pystac-core", editable = "core" }] +[[package]] +name = "pystac-ext-product" +version = "1.0.0" +source = { editable = "extensions/product" } +dependencies = [ + { name = "pystac-core" }, +] + +[package.metadata] +requires-dist = [{ name = "pystac-core", editable = "core" }] + [[package]] name = "pystac-ext-projection" version = "2.0.0" @@ -4585,15 +4599,15 @@ resolution-markers = [ "python_full_version < '3.11'", ] 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 = "affine", marker = "python_full_version < '3.12'" }, + { name = "attrs", marker = "python_full_version < '3.12'" }, + { name = "certifi", marker = "python_full_version < '3.12'" }, + { name = "click", marker = "python_full_version < '3.12'" }, + { name = "click-plugins", marker = "python_full_version < '3.12'" }, + { name = "cligj", marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "pyparsing" }, + { name = "pyparsing", marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ec/fa/fce8dc9f09e5bc6520b6fc1b4ecfa510af9ca06eb42ad7bdff9c9b8989d0/rasterio-1.4.4.tar.gz", hash = "sha256:c95424e2c7f009b8f7df1095d645c52895cd332c0c2e1b4c2e073ea28b930320", size = 445004, upload-time = "2025-12-12T18:01:08.971Z" } wheels = [ @@ -4656,13 +4670,13 @@ resolution-markers = [ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "affine" }, - { name = "attrs" }, - { name = "certifi" }, - { name = "click" }, - { name = "cligj" }, - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" } }, - { name = "pyparsing" }, + { name = "affine", marker = "python_full_version >= '3.12'" }, + { name = "attrs", marker = "python_full_version >= '3.12'" }, + { name = "certifi", marker = "python_full_version >= '3.12'" }, + { name = "click", marker = "python_full_version >= '3.12'" }, + { name = "cligj", marker = "python_full_version >= '3.12'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "pyparsing", marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f6/88/edb4b66b6cb2c13f123af5a3896bf70c0cbe73ab3cd4243cb4eb0212a0f6/rasterio-1.5.0.tar.gz", hash = "sha256:1e0ea56b02eea4989b36edf8e58a5a3ef40e1b7edcb04def2603accd5ab3ee7b", size = 452184, upload-time = "2026-01-05T16:06:47.169Z" } wheels = [ @@ -4796,13 +4810,13 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "numpy", version = "2.2.6", 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'" }, { name = "numpy", version = "2.4.6", 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 = "packaging", marker = "python_full_version < '3.12'" }, + { 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", version = "1.4.4", source = { registry = "https://pypi.org/simple" } }, - { name = "xarray", version = "2025.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version != '3.11.*'" }, + { name = "rasterio", version = "1.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "xarray", version = "2025.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "xarray", version = "2026.7.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" } @@ -4826,11 +4840,11 @@ resolution-markers = [ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" } }, - { name = "packaging" }, - { name = "pyproj", version = "3.7.2", source = { registry = "https://pypi.org/simple" } }, - { name = "rasterio", version = "1.5.0", source = { registry = "https://pypi.org/simple" } }, - { name = "xarray", version = "2026.7.0", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging", marker = "python_full_version >= '3.12'" }, + { name = "pyproj", version = "3.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "rasterio", version = "1.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "xarray", version = "2026.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4b/04/9e43477ab0fce7c4c949e1131bfae55ec5228da4ba30f55760660db224b2/rioxarray-0.22.0.tar.gz", hash = "sha256:3f55f23a632ffd9eff13463634227f4afbbcf298947536e161f6cf2ce88d4373", size = 61337, upload-time = "2026-03-06T17:11:00.16Z" } wheels = [ @@ -5258,23 +5272,23 @@ resolution-markers = [ "python_full_version < '3.11'", ] 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" }, + { name = "alabaster", marker = "python_full_version < '3.11'" }, + { name = "babel", marker = "python_full_version < '3.11'" }, + { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, + { name = "docutils", marker = "python_full_version < '3.11'" }, + { name = "imagesize", marker = "python_full_version < '3.11'" }, + { name = "jinja2", marker = "python_full_version < '3.11'" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "pygments", marker = "python_full_version < '3.11'" }, + { name = "requests", marker = "python_full_version < '3.11'" }, + { name = "snowballstemmer", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.11'" }, + { 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 = [ @@ -5291,23 +5305,23 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "alabaster" }, - { name = "babel" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "docutils" }, - { name = "imagesize" }, - { name = "jinja2" }, - { name = "packaging" }, - { name = "pygments" }, - { name = "requests" }, - { name = "roman-numerals" }, - { name = "snowballstemmer" }, - { name = "sphinxcontrib-applehelp" }, - { name = "sphinxcontrib-devhelp" }, - { name = "sphinxcontrib-htmlhelp" }, - { name = "sphinxcontrib-jsmath" }, - { name = "sphinxcontrib-qthelp" }, - { name = "sphinxcontrib-serializinghtml" }, + { name = "alabaster", marker = "python_full_version == '3.11.*'" }, + { name = "babel", marker = "python_full_version == '3.11.*'" }, + { name = "colorama", marker = "python_full_version == '3.11.*' and sys_platform == 'win32'" }, + { name = "docutils", marker = "python_full_version == '3.11.*'" }, + { name = "imagesize", marker = "python_full_version == '3.11.*'" }, + { name = "jinja2", marker = "python_full_version == '3.11.*'" }, + { name = "packaging", marker = "python_full_version == '3.11.*'" }, + { name = "pygments", marker = "python_full_version == '3.11.*'" }, + { name = "requests", marker = "python_full_version == '3.11.*'" }, + { name = "roman-numerals", marker = "python_full_version == '3.11.*'" }, + { name = "snowballstemmer", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.11.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502, upload-time = "2025-12-04T07:45:27.343Z" } wheels = [ @@ -5330,23 +5344,23 @@ resolution-markers = [ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "alabaster" }, - { name = "babel" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "docutils" }, - { name = "imagesize" }, - { name = "jinja2" }, - { name = "packaging" }, - { name = "pygments" }, - { name = "requests" }, - { name = "roman-numerals" }, - { name = "snowballstemmer" }, - { name = "sphinxcontrib-applehelp" }, - { name = "sphinxcontrib-devhelp" }, - { name = "sphinxcontrib-htmlhelp" }, - { name = "sphinxcontrib-jsmath" }, - { name = "sphinxcontrib-qthelp" }, - { name = "sphinxcontrib-serializinghtml" }, + { name = "alabaster", marker = "python_full_version >= '3.12'" }, + { name = "babel", marker = "python_full_version >= '3.12'" }, + { name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" }, + { name = "docutils", marker = "python_full_version >= '3.12'" }, + { name = "imagesize", marker = "python_full_version >= '3.12'" }, + { name = "jinja2", marker = "python_full_version >= '3.12'" }, + { name = "packaging", marker = "python_full_version >= '3.12'" }, + { name = "pygments", marker = "python_full_version >= '3.12'" }, + { name = "requests", marker = "python_full_version >= '3.12'" }, + { name = "roman-numerals", marker = "python_full_version >= '3.12'" }, + { name = "snowballstemmer", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" } wheels = [ @@ -5361,12 +5375,12 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "colorama" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" } }, - { name = "starlette" }, - { name = "uvicorn" }, - { name = "watchfiles" }, - { name = "websockets" }, + { name = "colorama", marker = "python_full_version < '3.11'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "starlette", marker = "python_full_version < '3.11'" }, + { name = "uvicorn", marker = "python_full_version < '3.11'" }, + { name = "watchfiles", marker = "python_full_version < '3.11'" }, + { name = "websockets", marker = "python_full_version < '3.11'" }, ] 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 = [ @@ -5392,13 +5406,13 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "colorama" }, - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "colorama", marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "starlette" }, - { name = "uvicorn" }, - { name = "watchfiles" }, - { name = "websockets" }, + { name = "starlette", marker = "python_full_version >= '3.11'" }, + { name = "uvicorn", marker = "python_full_version >= '3.11'" }, + { name = "watchfiles", marker = "python_full_version >= '3.11'" }, + { name = "websockets", marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e0/3c/a59a3a453d4133777f7ed2e83c80b7dc817d43c74b74298ca0af869662ad/sphinx_autobuild-2025.8.25.tar.gz", hash = "sha256:9cf5aab32853c8c31af572e4fecdc09c997e2b8be5a07daf2a389e270e85b213", size = 15200, upload-time = "2025-08-25T18:44:55.436Z" } wheels = [ @@ -5413,7 +5427,7 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" } }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] 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 = [ @@ -5439,7 +5453,7 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/13/7b/804f311da4663a4aecc6cf7abd83443f3d4ded970826d0c958edc77d4527/sphinx_design-0.7.0.tar.gz", hash = "sha256:d2a3f5b19c24b916adb52f97c5f00efab4009ca337812001109084a740ec9b7a", size = 2203582, upload-time = "2026-01-19T13:12:53.297Z" } @@ -6216,9 +6230,9 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, - { name = "packaging" }, - { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" } }, + { 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", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, 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 = [ @@ -6244,10 +6258,10 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "packaging" }, - { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" } }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ea/96/3f7bdd00e505ec3698903415b30135024703e017b28c6b61f98da3193b0d/xarray-2026.7.0.tar.gz", hash = "sha256:361b495928fdbf5b58d0969bb6775339019da5e93ca74d61ddf4eb5edd6ce604", size = 3145348, upload-time = "2026-07-09T17:38:26.515Z" } wheels = [ @@ -6262,8 +6276,8 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "pystac" }, - { name = "xarray", version = "2025.6.1", source = { registry = "https://pypi.org/simple" } }, + { 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 = [ @@ -6289,8 +6303,8 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "pystac" }, - { name = "xarray", version = "2026.7.0", source = { registry = "https://pypi.org/simple" } }, + { name = "pystac", marker = "python_full_version >= '3.11'" }, + { name = "xarray", version = "2026.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1b/d5/25b563d527ead74be48dd35702405851aa9de31c9dff4c74694f3a37cecd/xpystac-0.5.0.tar.gz", hash = "sha256:d146e4e922f70918a7bea322c652b9da719fd6090a35f87aa21e2a83622b7238", size = 150247, upload-time = "2025-10-13T15:55:42.08Z" } wheels = [ @@ -6421,10 +6435,10 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "asciitree" }, - { name = "fasteners", marker = "sys_platform != 'emscripten'" }, - { name = "numcodecs", version = "0.13.1", source = { registry = "https://pypi.org/simple" } }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { 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 = [ @@ -6441,12 +6455,12 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "donfig" }, - { name = "google-crc32c" }, - { name = "numcodecs", version = "0.16.5", source = { registry = "https://pypi.org/simple" } }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, - { name = "packaging" }, - { name = "typing-extensions" }, + { name = "donfig", marker = "python_full_version == '3.11.*'" }, + { name = "google-crc32c", marker = "python_full_version == '3.11.*'" }, + { name = "numcodecs", version = "0.16.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.4.6", 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/31/5a/b8a0cf39a14c770c30bd1f2d120c54000c8cd9e84e8e79f38d9a7ce58071/zarr-3.1.6.tar.gz", hash = "sha256:d95e72cbea4b90e9a70679468b8266400331756232576ae2b43400ac5108d0eb", size = 386531, upload-time = "2026-03-23T17:25:18.748Z" } wheels = [ @@ -6469,12 +6483,12 @@ resolution-markers = [ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "donfig" }, - { name = "google-crc32c" }, - { name = "numcodecs", version = "0.16.5", source = { registry = "https://pypi.org/simple" } }, - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" } }, - { name = "packaging" }, - { name = "typing-extensions" }, + { name = "donfig", marker = "python_full_version >= '3.12'" }, + { name = "google-crc32c", marker = "python_full_version >= '3.12'" }, + { name = "numcodecs", version = "0.16.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging", marker = "python_full_version >= '3.12'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/93/8d/aeb164004f87543b06ef54f885d02c342c31ceb274e2bbec470a98927621/zarr-3.2.1.tar.gz", hash = "sha256:71565b738a0e7e8ed226f0516eba8c6bb53440ad7669a8c48ebb3534a161d035", size = 675161, upload-time = "2026-05-05T12:37:22.383Z" } wheels = [