From 398c68c6f6205205b5c42c151f050585171261fc Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Tue, 19 Nov 2024 09:29:03 -0500 Subject: [PATCH 01/11] Move item_assets out of extensions --- pystac/{extensions => }/item_assets.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pystac/{extensions => }/item_assets.py (100%) diff --git a/pystac/extensions/item_assets.py b/pystac/item_assets.py similarity index 100% rename from pystac/extensions/item_assets.py rename to pystac/item_assets.py From b799ef441e18ec6518e31030140d65a53c32a7af Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Tue, 19 Nov 2024 09:50:03 -0500 Subject: [PATCH 02/11] Move item_assets to dummy file --- pystac/extensions/{item_assets.py => ia.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pystac/extensions/{item_assets.py => ia.py} (100%) diff --git a/pystac/extensions/item_assets.py b/pystac/extensions/ia.py similarity index 100% rename from pystac/extensions/item_assets.py rename to pystac/extensions/ia.py From 3c73e38648ea68733246dd8c47f4bae35bc4a09e Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Tue, 19 Nov 2024 09:52:52 -0500 Subject: [PATCH 03/11] Move item_assets out of dummy file --- pystac/extensions/{ia.py => item_assets.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pystac/extensions/{ia.py => item_assets.py} (100%) diff --git a/pystac/extensions/ia.py b/pystac/extensions/item_assets.py similarity index 100% rename from pystac/extensions/ia.py rename to pystac/extensions/item_assets.py From dd88fe5227cc6573de2189e3daa90bcb58901c9a Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Tue, 19 Nov 2024 16:20:33 -0500 Subject: [PATCH 04/11] Add new `item_assets` access pattern --- pystac/__init__.py | 2 + pystac/collection.py | 23 ++++ pystac/extensions/base.py | 8 +- pystac/extensions/classification.py | 19 +-- pystac/extensions/datacube.py | 11 +- pystac/extensions/eo.py | 12 +- pystac/extensions/ext.py | 22 ++- pystac/extensions/item_assets.py | 207 +--------------------------- pystac/extensions/pointcloud.py | 11 +- pystac/extensions/projection.py | 11 +- pystac/extensions/raster.py | 17 ++- pystac/extensions/sar.py | 11 +- pystac/extensions/sat.py | 11 +- pystac/extensions/storage.py | 11 +- pystac/extensions/table.py | 11 +- pystac/extensions/version.py | 8 +- pystac/extensions/view.py | 11 +- pystac/item_assets.py | 109 +++++---------- 18 files changed, 146 insertions(+), 369 deletions(-) diff --git a/pystac/__init__.py b/pystac/__init__.py index c029ac283..c2a5b53b1 100644 --- a/pystac/__init__.py +++ b/pystac/__init__.py @@ -33,6 +33,7 @@ "RangeSummary", "Item", "Asset", + "ItemAssetDefinition", "ItemCollection", "Provider", "ProviderRole", @@ -81,6 +82,7 @@ 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 diff --git a/pystac/collection.py b/pystac/collection.py index 9c0c24b7f..5323b82ce 100644 --- a/pystac/collection.py +++ b/pystac/collection.py @@ -20,6 +20,7 @@ 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 @@ -553,6 +554,7 @@ def __init__( 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: @@ -731,6 +733,27 @@ def get_item(self, id: str, recursive: bool = False) -> Item | None: return super().get_item(id, recursive=recursive) raise e + @property + def item_assets(self) -> dict[str, ItemAssetDefinition] | None: + if self._item_assets is None and "item_assets" in self.extra_fields: + 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. diff --git a/pystac/extensions/base.py b/pystac/extensions/base.py index 1b3016fe3..241c8381e 100644 --- a/pystac/extensions/base.py +++ b/pystac/extensions/base.py @@ -5,7 +5,6 @@ from abc import ABC, abstractmethod from collections.abc import Iterable from typing import ( - TYPE_CHECKING, Any, Generic, TypeVar, @@ -14,9 +13,6 @@ import pystac -if TYPE_CHECKING: - from pystac.extensions.item_assets import AssetDefinition - VERSION_REGEX = re.compile("/v[0-9].[0-9].*/") @@ -158,7 +154,7 @@ def has_extension(cls, obj: S) -> bool: @classmethod def validate_owner_has_extension( cls, - asset: pystac.Asset | AssetDefinition, + asset: pystac.Asset | pystac.ItemAssetDefinition, add_if_missing: bool = False, ) -> None: """ @@ -190,7 +186,7 @@ def validate_owner_has_extension( @classmethod def ensure_owner_has_extension( cls, - asset_or_link: pystac.Asset | AssetDefinition | pystac.Link, + asset_or_link: pystac.Asset | pystac.ItemAssetDefinition | pystac.Link, add_if_missing: bool = False, ) -> None: """Given an :class:`~pystac.Asset`, checks if the asset's owner has this diff --git a/pystac/extensions/classification.py b/pystac/extensions/classification.py index bd8b4557c..beb30bbe7 100644 --- a/pystac/extensions/classification.py +++ b/pystac/extensions/classification.py @@ -16,7 +16,6 @@ ) import pystac -from pystac.extensions import item_assets from pystac.extensions.base import ( ExtensionManagementMixin, PropertiesExtension, @@ -27,7 +26,7 @@ from pystac.serialization.identify import STACJSONDescription, STACVersionID from pystac.utils import get_required, map_opt -T = TypeVar("T", pystac.Item, pystac.Asset, item_assets.AssetDefinition, RasterBand) +T = TypeVar("T", pystac.Item, pystac.Asset, pystac.ItemAssetDefinition, RasterBand) SCHEMA_URI_PATTERN: str = ( "https://stac-extensions.github.io/classification/v{version}/schema.json" @@ -492,7 +491,7 @@ class ClassificationExtension( """An abstract class that can be used to extend the properties of :class:`~pystac.Item`, :class:`~pystac.Asset`, :class:`~pystac.extension.raster.RasterBand`, or - :class:`~pystac.extension.item_assets.AssetDefinition` with properties from the + :class:`~pystac.ItemAssetDefinition` with properties from the :stac-ext:`Classification Extension `. This class is generic over the type of STAC object being extended. @@ -600,7 +599,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> ClassificationExtension[T] This extension can be applied to instances of :class:`~pystac.Item`, :class:`~pystac.Asset`, - :class:`~pystac.extensions.item_assets.AssetDefinition`, or + :class:`~pystac.ItemAssetDefinition`, or :class:`~pystac.extension.raster.RasterBand`. Raises: @@ -612,7 +611,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> ClassificationExtension[T] elif isinstance(obj, pystac.Asset): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(ClassificationExtension[T], AssetClassificationExtension(obj)) - elif isinstance(obj, item_assets.AssetDefinition): + elif isinstance(obj, pystac.ItemAssetDefinition): cls.ensure_owner_has_extension(obj, add_if_missing) return cast( ClassificationExtension[T], ItemAssetsClassificationExtension(obj) @@ -663,17 +662,19 @@ def __repr__(self) -> str: class ItemAssetsClassificationExtension( - ClassificationExtension[item_assets.AssetDefinition] + ClassificationExtension[pystac.ItemAssetDefinition] ): properties: dict[str, Any] - asset_defn: item_assets.AssetDefinition + asset_defn: pystac.ItemAssetDefinition - def __init__(self, item_asset: item_assets.AssetDefinition): + def __init__(self, item_asset: pystac.ItemAssetDefinition): self.asset_defn = item_asset self.properties = item_asset.properties def __repr__(self) -> str: - return f" DatacubeExtension[T]: elif isinstance(obj, pystac.Asset): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(DatacubeExtension[T], AssetDatacubeExtension(obj)) - elif isinstance(obj, item_assets.AssetDefinition): + elif isinstance(obj, pystac.ItemAssetDefinition): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(DatacubeExtension[T], ItemAssetsDatacubeExtension(obj)) else: @@ -691,11 +690,11 @@ def __repr__(self) -> str: return f"" -class ItemAssetsDatacubeExtension(DatacubeExtension[item_assets.AssetDefinition]): +class ItemAssetsDatacubeExtension(DatacubeExtension[pystac.ItemAssetDefinition]): properties: dict[str, Any] - asset_defn: item_assets.AssetDefinition + asset_defn: pystac.ItemAssetDefinition - def __init__(self, item_asset: item_assets.AssetDefinition): + def __init__(self, item_asset: pystac.ItemAssetDefinition): self.asset_defn = item_asset self.properties = item_asset.properties diff --git a/pystac/extensions/eo.py b/pystac/extensions/eo.py index 54b6cbcbf..c9628a3c7 100644 --- a/pystac/extensions/eo.py +++ b/pystac/extensions/eo.py @@ -14,7 +14,7 @@ ) import pystac -from pystac.extensions import item_assets, projection, view +from pystac.extensions import projection, view from pystac.extensions.base import ( ExtensionManagementMixin, PropertiesExtension, @@ -25,7 +25,7 @@ from pystac.summaries import RangeSummary from pystac.utils import get_required, map_opt -T = TypeVar("T", pystac.Item, pystac.Asset, item_assets.AssetDefinition) +T = TypeVar("T", pystac.Item, pystac.Asset, pystac.ItemAssetDefinition) SCHEMA_URI: str = "https://stac-extensions.github.io/eo/v1.1.0/schema.json" SCHEMA_URIS: list[str] = [ @@ -409,7 +409,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> EOExtension[T]: elif isinstance(obj, pystac.Asset): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(EOExtension[T], AssetEOExtension(obj)) - elif isinstance(obj, item_assets.AssetDefinition): + elif isinstance(obj, pystac.ItemAssetDefinition): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(EOExtension[T], ItemAssetsEOExtension(obj)) else: @@ -536,9 +536,9 @@ def __repr__(self) -> str: return f"" -class ItemAssetsEOExtension(EOExtension[item_assets.AssetDefinition]): +class ItemAssetsEOExtension(EOExtension[pystac.ItemAssetDefinition]): properties: dict[str, Any] - asset_defn: item_assets.AssetDefinition + asset_defn: pystac.ItemAssetDefinition def _get_bands(self) -> list[Band] | None: if BANDS_PROP not in self.properties: @@ -550,7 +550,7 @@ def _get_bands(self) -> list[Band] | None: ) ) - def __init__(self, item_asset: item_assets.AssetDefinition): + def __init__(self, item_asset: pystac.ItemAssetDefinition): self.asset_defn = item_asset self.properties = item_asset.properties diff --git a/pystac/extensions/ext.py b/pystac/extensions/ext.py index 2488b1c42..581886c10 100644 --- a/pystac/extensions/ext.py +++ b/pystac/extensions/ext.py @@ -1,13 +1,21 @@ from dataclasses import dataclass from typing import Any, Generic, Literal, TypeVar, cast -from pystac import Asset, Catalog, Collection, Item, Link, STACError +from pystac import ( + Asset, + Catalog, + Collection, + Item, + ItemAssetDefinition, + Link, + STACError, +) from pystac.extensions.classification import ClassificationExtension from pystac.extensions.datacube import DatacubeExtension from pystac.extensions.eo import EOExtension from pystac.extensions.file import FileExtension from pystac.extensions.grid import GridExtension -from pystac.extensions.item_assets import AssetDefinition, ItemAssetsExtension +from pystac.extensions.item_assets import ItemAssetsExtension from pystac.extensions.mgrs import MgrsExtension from pystac.extensions.pointcloud import PointcloudExtension from pystac.extensions.projection import ProjectionExtension @@ -22,8 +30,8 @@ from pystac.extensions.view import ViewExtension from pystac.extensions.xarray_assets import XarrayAssetsExtension -T = TypeVar("T", Asset, AssetDefinition, Link) -U = TypeVar("U", Asset, AssetDefinition) +T = TypeVar("T", Asset, ItemAssetDefinition, Link) +U = TypeVar("U", Asset, ItemAssetDefinition) EXTENSION_NAMES = Literal[ "classification", @@ -107,7 +115,7 @@ def cube(self) -> DatacubeExtension[Collection]: return DatacubeExtension.ext(self.stac_object) @property - def item_assets(self) -> dict[str, AssetDefinition]: + def item_assets(self) -> dict[str, ItemAssetDefinition]: return ItemAssetsExtension.ext(self.stac_object).item_assets @property @@ -300,8 +308,8 @@ def xarray(self) -> XarrayAssetsExtension[Asset]: @dataclass -class ItemAssetExt(_AssetExt[AssetDefinition]): - stac_object: AssetDefinition +class ItemAssetExt(_AssetExt[ItemAssetDefinition]): + stac_object: ItemAssetDefinition @dataclass diff --git a/pystac/extensions/item_assets.py b/pystac/extensions/item_assets.py index 37c3b72d1..68ddff7f7 100644 --- a/pystac/extensions/item_assets.py +++ b/pystac/extensions/item_assets.py @@ -2,223 +2,24 @@ from __future__ import annotations -from copy import deepcopy from typing import TYPE_CHECKING, Any, Literal import pystac from pystac.extensions.base import ExtensionManagementMixin from pystac.extensions.hooks import ExtensionHooks +from pystac.item_assets import ( + ItemAssetDefinition as AssetDefinition, +) from pystac.serialization.identify import STACJSONDescription, STACVersionID from pystac.utils import get_required if TYPE_CHECKING: - from pystac.extensions.ext import ItemAssetExt + pass SCHEMA_URI = "https://stac-extensions.github.io/item-assets/v1.0.0/schema.json" ITEM_ASSETS_PROP = "item_assets" -ASSET_TITLE_PROP = "title" -ASSET_DESC_PROP = "description" -ASSET_TYPE_PROP = "type" -ASSET_ROLES_PROP = "roles" - - -class AssetDefinition: - """Object that contains details about the datafiles that will be included in member - Items for this Collection. - - See the :stac-ext:`Asset Object ` for details. - """ - - properties: dict[str, Any] - - owner: pystac.Collection | None - - def __init__( - self, properties: dict[str, Any], owner: pystac.Collection | None = None - ) -> None: - self.properties = properties - self.owner = owner - - def __eq__(self, o: object) -> bool: - if not isinstance(o, AssetDefinition): - return NotImplemented - return self.to_dict() == o.to_dict() - - @classmethod - def create( - cls, - title: str | None, - description: str | None, - media_type: str | None, - roles: list[str] | None, - extra_fields: dict[str, Any] | None = None, - ) -> AssetDefinition: - """ - Creates a new asset definition. - - Args: - title : Displayed title for clients and users. - description : 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 : `media type\ - `__ - of the asset. - roles : `semantic roles - `__ - of the asset, similar to the use of rel in links. - extra_fields : Additional fields on the asset definition, e.g. from - extensions. - """ - asset_defn = cls({}) - asset_defn.apply( - title=title, - description=description, - media_type=media_type, - roles=roles, - extra_fields=extra_fields, - ) - return asset_defn - - def apply( - self, - title: str | None, - description: str | None, - media_type: str | None, - roles: list[str] | None, - extra_fields: dict[str, Any] | None = None, - ) -> None: - """ - Sets the properties for this asset definition. - - Args: - title : Displayed title for clients and users. - description : 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 : `media type\ - `__ - of the asset. - roles : `semantic roles - `__ - of the asset, similar to the use of rel in links. - extra_fields : Additional fields on the asset definition, e.g. from - extensions. - """ - if extra_fields: - self.properties.update(extra_fields) - self.title = title - self.description = description - self.media_type = media_type - self.roles = roles - self.owner = None - - def set_owner(self, obj: pystac.Collection) -> None: - """Sets the owning item of this AssetDefinition. - - The owning item will be used to resolve relative HREFs of this asset. - - Args: - obj: The Collection that owns this asset. - """ - self.owner = obj - - @property - def title(self) -> str | None: - """Gets or sets the displayed title for clients and users.""" - return self.properties.get(ASSET_TITLE_PROP) - - @title.setter - def title(self, v: str | None) -> None: - if v is None: - self.properties.pop(ASSET_TITLE_PROP, None) - else: - self.properties[ASSET_TITLE_PROP] = v - - @property - def description(self) -> str | None: - """Gets or sets 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.""" - return self.properties.get(ASSET_DESC_PROP) - - @description.setter - def description(self, v: str | None) -> None: - if v is None: - self.properties.pop(ASSET_DESC_PROP, None) - else: - self.properties[ASSET_DESC_PROP] = v - - @property - def media_type(self) -> str | None: - """Gets or sets the `media type - `__ - of the asset.""" - return self.properties.get(ASSET_TYPE_PROP) - - @media_type.setter - def media_type(self, v: str | None) -> None: - if v is None: - self.properties.pop(ASSET_TYPE_PROP, None) - else: - self.properties[ASSET_TYPE_PROP] = v - - @property - def roles(self) -> list[str] | None: - """Gets or sets the `semantic roles - `__ - of the asset, similar to the use of rel in links.""" - return self.properties.get(ASSET_ROLES_PROP) - - @roles.setter - def roles(self, v: list[str] | None) -> None: - if v is None: - self.properties.pop(ASSET_ROLES_PROP, None) - else: - self.properties[ASSET_ROLES_PROP] = v - - def to_dict(self) -> dict[str, Any]: - """Returns a dictionary representing this ``AssetDefinition``.""" - return deepcopy(self.properties) - - def create_asset(self, href: str) -> pystac.Asset: - """Creates a new :class:`~pystac.Asset` instance using the fields from this - ``AssetDefinition`` and the given ``href``.""" - return pystac.Asset( - href=href, - title=self.title, - description=self.description, - media_type=self.media_type, - roles=self.roles, - extra_fields={ - k: v - for k, v in self.properties.items() - if k - not in { - ASSET_TITLE_PROP, - ASSET_DESC_PROP, - ASSET_TYPE_PROP, - ASSET_ROLES_PROP, - } - }, - ) - - @property - def ext(self) -> ItemAssetExt: - """Accessor for extension classes on this item_asset - - Example:: - - collection.ext.item_assets["data"].ext.proj.epsg = 4326 - """ - from pystac.extensions.ext import ItemAssetExt - - return ItemAssetExt(stac_object=self) - class ItemAssetsExtension(ExtensionManagementMixin[pystac.Collection]): name: Literal["item_assets"] = "item_assets" diff --git a/pystac/extensions/pointcloud.py b/pystac/extensions/pointcloud.py index b82566e74..804c55372 100644 --- a/pystac/extensions/pointcloud.py +++ b/pystac/extensions/pointcloud.py @@ -13,7 +13,6 @@ ) import pystac -from pystac.extensions import item_assets from pystac.extensions.base import ( ExtensionManagementMixin, PropertiesExtension, @@ -23,7 +22,7 @@ from pystac.summaries import RangeSummary from pystac.utils import StringEnum, get_required, map_opt -T = TypeVar("T", pystac.Item, pystac.Asset, item_assets.AssetDefinition) +T = TypeVar("T", pystac.Item, pystac.Asset, pystac.ItemAssetDefinition) SCHEMA_URI: str = "https://stac-extensions.github.io/pointcloud/v1.0.0/schema.json" PREFIX: str = "pc:" @@ -468,7 +467,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> PointcloudExtension[T]: ) cls.ensure_owner_has_extension(obj, add_if_missing) return cast(PointcloudExtension[T], AssetPointcloudExtension(obj)) - elif isinstance(obj, item_assets.AssetDefinition): + elif isinstance(obj, pystac.ItemAssetDefinition): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(PointcloudExtension[T], ItemAssetsPointcloudExtension(obj)) else: @@ -534,11 +533,11 @@ def __repr__(self) -> str: return f"" -class ItemAssetsPointcloudExtension(PointcloudExtension[item_assets.AssetDefinition]): +class ItemAssetsPointcloudExtension(PointcloudExtension[pystac.ItemAssetDefinition]): properties: dict[str, Any] - asset_defn: item_assets.AssetDefinition + asset_defn: pystac.ItemAssetDefinition - def __init__(self, item_asset: item_assets.AssetDefinition): + def __init__(self, item_asset: pystac.ItemAssetDefinition): self.asset_defn = item_asset self.properties = item_asset.properties diff --git a/pystac/extensions/projection.py b/pystac/extensions/projection.py index 1982e8b55..8d3cb1523 100644 --- a/pystac/extensions/projection.py +++ b/pystac/extensions/projection.py @@ -15,7 +15,6 @@ ) import pystac -from pystac.extensions import item_assets from pystac.extensions.base import ( ExtensionManagementMixin, PropertiesExtension, @@ -23,7 +22,7 @@ ) from pystac.extensions.hooks import ExtensionHooks -T = TypeVar("T", pystac.Item, pystac.Asset, item_assets.AssetDefinition) +T = TypeVar("T", pystac.Item, pystac.Asset, pystac.ItemAssetDefinition) SCHEMA_URI: str = "https://stac-extensions.github.io/projection/v1.1.0/schema.json" SCHEMA_URIS: list[str] = [ @@ -301,7 +300,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> ProjectionExtension[T]: elif isinstance(obj, pystac.Asset): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(ProjectionExtension[T], AssetProjectionExtension(obj)) - elif isinstance(obj, item_assets.AssetDefinition): + elif isinstance(obj, pystac.ItemAssetDefinition): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(ProjectionExtension[T], ItemAssetsProjectionExtension(obj)) else: @@ -368,11 +367,11 @@ def __repr__(self) -> str: return f"" -class ItemAssetsProjectionExtension(ProjectionExtension[item_assets.AssetDefinition]): +class ItemAssetsProjectionExtension(ProjectionExtension[pystac.ItemAssetDefinition]): properties: dict[str, Any] - asset_defn: item_assets.AssetDefinition + asset_defn: pystac.ItemAssetDefinition - def __init__(self, item_asset: item_assets.AssetDefinition): + def __init__(self, item_asset: pystac.ItemAssetDefinition): self.asset_defn = item_asset self.properties = item_asset.properties diff --git a/pystac/extensions/raster.py b/pystac/extensions/raster.py index ab57d7c3e..eb01ee6bd 100644 --- a/pystac/extensions/raster.py +++ b/pystac/extensions/raster.py @@ -14,7 +14,6 @@ ) import pystac -from pystac.extensions import item_assets from pystac.extensions.base import ( ExtensionManagementMixin, PropertiesExtension, @@ -23,7 +22,7 @@ from pystac.extensions.hooks import ExtensionHooks from pystac.utils import StringEnum, get_opt, get_required, map_opt -T = TypeVar("T", pystac.Asset, item_assets.AssetDefinition) +T = TypeVar("T", pystac.Asset, pystac.ItemAssetDefinition) SCHEMA_URI = "https://stac-extensions.github.io/raster/v1.1.0/schema.json" SCHEMA_URIS = [ @@ -663,7 +662,7 @@ class RasterExtension( ): """An abstract class that can be used to extend the properties of an :class:`~pystac.Item`, :class:`~pystac.Asset`, or - :class:`~pystac.extension.item_assets.AssetDefinition` with properties from + :class:`~pystac.extension.pystac.ItemAssetDefinition` with properties from the :stac-ext:`Raster Extension `. This class is generic over the type of STAC Object to be extended (e.g. :class:`~pystac.Item`, :class:`~pystac.Asset`). @@ -736,7 +735,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> RasterExtension[T]: if isinstance(obj, pystac.Asset): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(RasterExtension[T], AssetRasterExtension(obj)) - elif isinstance(obj, item_assets.AssetDefinition): + elif isinstance(obj, pystac.ItemAssetDefinition): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(RasterExtension[T], ItemAssetsRasterExtension(obj)) else: @@ -771,16 +770,16 @@ def __repr__(self) -> str: return f"" -class ItemAssetsRasterExtension(RasterExtension[item_assets.AssetDefinition]): - asset_definition: item_assets.AssetDefinition - """A reference to the :class:`~pystac.extensions.item_assets.AssetDefinition` +class ItemAssetsRasterExtension(RasterExtension[pystac.ItemAssetDefinition]): + asset_definition: pystac.ItemAssetDefinition + """A reference to the :class:`~pystac.extensions.pystac.ItemAssetDefinition` being extended.""" properties: dict[str, Any] - """The :class:`~pystac.extensions.item_assets.AssetDefinition` fields, including + """The :class:`~pystac.extensions.pystac.ItemAssetDefinition` fields, including extension properties.""" - def __init__(self, item_asset: item_assets.AssetDefinition): + def __init__(self, item_asset: pystac.ItemAssetDefinition): self.properties = item_asset.properties self.asset_definition = item_asset diff --git a/pystac/extensions/sar.py b/pystac/extensions/sar.py index 112c63a9a..161272213 100644 --- a/pystac/extensions/sar.py +++ b/pystac/extensions/sar.py @@ -13,7 +13,6 @@ ) import pystac -from pystac.extensions import item_assets from pystac.extensions.base import ( ExtensionManagementMixin, PropertiesExtension, @@ -24,7 +23,7 @@ from pystac.summaries import RangeSummary from pystac.utils import StringEnum, get_required, map_opt -T = TypeVar("T", pystac.Item, pystac.Asset, item_assets.AssetDefinition) +T = TypeVar("T", pystac.Item, pystac.Asset, pystac.ItemAssetDefinition) SCHEMA_URI: str = "https://stac-extensions.github.io/sar/v1.0.0/schema.json" PREFIX: str = "sar:" @@ -332,7 +331,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> SarExtension[T]: ) cls.ensure_owner_has_extension(obj, add_if_missing) return cast(SarExtension[T], AssetSarExtension(obj)) - elif isinstance(obj, item_assets.AssetDefinition): + elif isinstance(obj, pystac.ItemAssetDefinition): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(SarExtension[T], ItemAssetsSarExtension(obj)) else: @@ -399,11 +398,11 @@ def __repr__(self) -> str: return f"" -class ItemAssetsSarExtension(SarExtension[item_assets.AssetDefinition]): +class ItemAssetsSarExtension(SarExtension[pystac.ItemAssetDefinition]): properties: dict[str, Any] - asset_defn: item_assets.AssetDefinition + asset_defn: pystac.ItemAssetDefinition - def __init__(self, item_asset: item_assets.AssetDefinition): + def __init__(self, item_asset: pystac.ItemAssetDefinition): self.asset_defn = item_asset self.properties = item_asset.properties diff --git a/pystac/extensions/sat.py b/pystac/extensions/sat.py index ab5b638fe..ed900b46e 100644 --- a/pystac/extensions/sat.py +++ b/pystac/extensions/sat.py @@ -14,7 +14,6 @@ ) import pystac -from pystac.extensions import item_assets from pystac.extensions.base import ( ExtensionManagementMixin, PropertiesExtension, @@ -24,7 +23,7 @@ from pystac.summaries import RangeSummary from pystac.utils import StringEnum, datetime_to_str, map_opt, str_to_datetime -T = TypeVar("T", pystac.Item, pystac.Asset, item_assets.AssetDefinition) +T = TypeVar("T", pystac.Item, pystac.Asset, pystac.ItemAssetDefinition) SCHEMA_URI = "https://stac-extensions.github.io/sat/v1.0.0/schema.json" @@ -163,7 +162,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> SatExtension[T]: elif isinstance(obj, pystac.Asset): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(SatExtension[T], AssetSatExtension(obj)) - elif isinstance(obj, item_assets.AssetDefinition): + elif isinstance(obj, pystac.ItemAssetDefinition): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(SatExtension[T], ItemAssetsSatExtension(obj)) else: @@ -232,11 +231,11 @@ def __repr__(self) -> str: return f"" -class ItemAssetsSatExtension(SatExtension[item_assets.AssetDefinition]): +class ItemAssetsSatExtension(SatExtension[pystac.ItemAssetDefinition]): properties: dict[str, Any] - asset_defn: item_assets.AssetDefinition + asset_defn: pystac.ItemAssetDefinition - def __init__(self, item_asset: item_assets.AssetDefinition): + def __init__(self, item_asset: pystac.ItemAssetDefinition): self.asset_defn = item_asset self.properties = item_asset.properties diff --git a/pystac/extensions/storage.py b/pystac/extensions/storage.py index 0b0aa0a1a..e7155887f 100644 --- a/pystac/extensions/storage.py +++ b/pystac/extensions/storage.py @@ -16,7 +16,6 @@ ) import pystac -from pystac.extensions import item_assets from pystac.extensions.base import ( ExtensionManagementMixin, PropertiesExtension, @@ -25,7 +24,7 @@ from pystac.extensions.hooks import ExtensionHooks from pystac.utils import StringEnum -T = TypeVar("T", pystac.Item, pystac.Asset, item_assets.AssetDefinition) +T = TypeVar("T", pystac.Item, pystac.Asset, pystac.ItemAssetDefinition) SCHEMA_URI: str = "https://stac-extensions.github.io/storage/v1.0.0/schema.json" PREFIX: str = "storage:" @@ -154,7 +153,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> StorageExtension[T]: elif isinstance(obj, pystac.Asset): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(StorageExtension[T], AssetStorageExtension(obj)) - elif isinstance(obj, item_assets.AssetDefinition): + elif isinstance(obj, pystac.ItemAssetDefinition): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(StorageExtension[T], ItemAssetsStorageExtension(obj)) else: @@ -221,11 +220,11 @@ def __repr__(self) -> str: return f"" -class ItemAssetsStorageExtension(StorageExtension[item_assets.AssetDefinition]): +class ItemAssetsStorageExtension(StorageExtension[pystac.ItemAssetDefinition]): properties: dict[str, Any] - asset_defn: item_assets.AssetDefinition + asset_defn: pystac.ItemAssetDefinition - def __init__(self, item_asset: item_assets.AssetDefinition): + def __init__(self, item_asset: pystac.ItemAssetDefinition): self.asset_defn = item_asset self.properties = item_asset.properties diff --git a/pystac/extensions/table.py b/pystac/extensions/table.py index 782d741a2..55feda51a 100644 --- a/pystac/extensions/table.py +++ b/pystac/extensions/table.py @@ -5,13 +5,12 @@ from typing import Any, Generic, Literal, TypeVar, Union, cast import pystac -from pystac.extensions import item_assets from pystac.extensions.base import ExtensionManagementMixin, PropertiesExtension from pystac.extensions.hooks import ExtensionHooks from pystac.utils import get_required T = TypeVar( - "T", pystac.Collection, pystac.Item, pystac.Asset, item_assets.AssetDefinition + "T", pystac.Collection, pystac.Item, pystac.Asset, pystac.ItemAssetDefinition ) SCHEMA_URI = "https://stac-extensions.github.io/table/v1.2.0/schema.json" @@ -165,7 +164,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> TableExtension[T]: if isinstance(obj, pystac.Asset): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(TableExtension[T], AssetTableExtension(obj)) - elif isinstance(obj, item_assets.AssetDefinition): + elif isinstance(obj, pystac.ItemAssetDefinition): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(TableExtension[T], ItemAssetsTableExtension(obj)) else: @@ -294,11 +293,11 @@ def __repr__(self) -> str: return f"" -class ItemAssetsTableExtension(TableExtension[item_assets.AssetDefinition]): +class ItemAssetsTableExtension(TableExtension[pystac.ItemAssetDefinition]): properties: dict[str, Any] - asset_defn: item_assets.AssetDefinition + asset_defn: pystac.ItemAssetDefinition - def __init__(self, item_asset: item_assets.AssetDefinition): + def __init__(self, item_asset: pystac.ItemAssetDefinition): self.asset_defn = item_asset self.properties = item_asset.properties diff --git a/pystac/extensions/version.py b/pystac/extensions/version.py index ee394f483..951a7c95d 100644 --- a/pystac/extensions/version.py +++ b/pystac/extensions/version.py @@ -20,6 +20,7 @@ Collection, ExtensionTypeError, Item, + ItemAssetDefinition, Link, MediaType, STACObject, @@ -28,10 +29,9 @@ from pystac.errors import DeprecatedWarning from pystac.extensions.base import ExtensionManagementMixin, PropertiesExtension from pystac.extensions.hooks import ExtensionHooks -from pystac.extensions.item_assets import AssetDefinition from pystac.utils import StringEnum, map_opt -T = TypeVar("T", Collection, Item, Catalog, Asset, AssetDefinition) +T = TypeVar("T", Collection, Item, Catalog, Asset, ItemAssetDefinition) U = TypeVar("U", Collection, Item, Catalog) SCHEMA_URI = "https://stac-extensions.github.io/version/v1.2.0/schema.json" @@ -395,10 +395,10 @@ def __repr__(self) -> str: return f"" -class ItemAssetsViewExtension(BaseVersionExtension[AssetDefinition]): +class ItemAssetsViewExtension(BaseVersionExtension[ItemAssetDefinition]): properties: dict[str, Any] - def __init__(self, item_asset: AssetDefinition): + def __init__(self, item_asset: ItemAssetDefinition): self.properties = item_asset.properties diff --git a/pystac/extensions/view.py b/pystac/extensions/view.py index 3a91249eb..29470d874 100644 --- a/pystac/extensions/view.py +++ b/pystac/extensions/view.py @@ -6,7 +6,6 @@ from typing import Any, Generic, Literal, TypeVar, Union, cast import pystac -from pystac.extensions import item_assets from pystac.extensions.base import ( ExtensionManagementMixin, PropertiesExtension, @@ -15,7 +14,7 @@ from pystac.extensions.hooks import ExtensionHooks from pystac.summaries import RangeSummary -T = TypeVar("T", pystac.Item, pystac.Asset, item_assets.AssetDefinition) +T = TypeVar("T", pystac.Item, pystac.Asset, pystac.ItemAssetDefinition) SCHEMA_URI: str = "https://stac-extensions.github.io/view/v1.0.0/schema.json" PREFIX: str = "view:" @@ -166,7 +165,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> ViewExtension[T]: elif isinstance(obj, pystac.Asset): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(ViewExtension[T], AssetViewExtension(obj)) - elif isinstance(obj, item_assets.AssetDefinition): + elif isinstance(obj, pystac.ItemAssetDefinition): cls.ensure_owner_has_extension(obj, add_if_missing) return cast(ViewExtension[T], ItemAssetsViewExtension(obj)) else: @@ -233,11 +232,11 @@ def __repr__(self) -> str: return f"" -class ItemAssetsViewExtension(ViewExtension[item_assets.AssetDefinition]): +class ItemAssetsViewExtension(ViewExtension[pystac.ItemAssetDefinition]): properties: dict[str, Any] - asset_defn: item_assets.AssetDefinition + asset_defn: pystac.ItemAssetDefinition - def __init__(self, item_asset: item_assets.AssetDefinition): + def __init__(self, item_asset: pystac.ItemAssetDefinition): self.asset_defn = item_asset self.properties = item_asset.properties diff --git a/pystac/item_assets.py b/pystac/item_assets.py index 37c3b72d1..4bcaec9dd 100644 --- a/pystac/item_assets.py +++ b/pystac/item_assets.py @@ -1,22 +1,15 @@ -"""Implements the :stac-ext:`Item Assets Definition Extension `.""" +"""Implements the ``Item Asset Definition ``.""" from __future__ import annotations from copy import deepcopy -from typing import TYPE_CHECKING, Any, Literal +from typing import TYPE_CHECKING, Any import pystac -from pystac.extensions.base import ExtensionManagementMixin -from pystac.extensions.hooks import ExtensionHooks -from pystac.serialization.identify import STACJSONDescription, STACVersionID -from pystac.utils import get_required if TYPE_CHECKING: from pystac.extensions.ext import ItemAssetExt -SCHEMA_URI = "https://stac-extensions.github.io/item-assets/v1.0.0/schema.json" - -ITEM_ASSETS_PROP = "item_assets" ASSET_TITLE_PROP = "title" ASSET_DESC_PROP = "description" @@ -24,11 +17,11 @@ ASSET_ROLES_PROP = "roles" -class AssetDefinition: +class ItemAssetDefinition: """Object that contains details about the datafiles that will be included in member Items for this Collection. - See the :stac-ext:`Asset Object ` for details. + See the `Item Asset Definition Object ` for details. """ properties: dict[str, Any] @@ -42,7 +35,7 @@ def __init__( self.owner = owner def __eq__(self, o: object) -> bool: - if not isinstance(o, AssetDefinition): + if not isinstance(o, ItemAssetDefinition): return NotImplemented return self.to_dict() == o.to_dict() @@ -54,7 +47,7 @@ def create( media_type: str | None, roles: list[str] | None, extra_fields: dict[str, Any] | None = None, - ) -> AssetDefinition: + ) -> ItemAssetDefinition: """ Creates a new asset definition. @@ -118,7 +111,7 @@ def apply( self.owner = None def set_owner(self, obj: pystac.Collection) -> None: - """Sets the owning item of this AssetDefinition. + """Sets the owning item of this ItemAssetDefinition. The owning item will be used to resolve relative HREFs of this asset. @@ -182,12 +175,12 @@ def roles(self, v: list[str] | None) -> None: self.properties[ASSET_ROLES_PROP] = v def to_dict(self) -> dict[str, Any]: - """Returns a dictionary representing this ``AssetDefinition``.""" + """Returns a dictionary representing this ``ItemAssetDefinition``.""" return deepcopy(self.properties) def create_asset(self, href: str) -> pystac.Asset: """Creates a new :class:`~pystac.Asset` instance using the fields from this - ``AssetDefinition`` and the given ``href``.""" + ``ItemAssetDefinition`` and the given ``href``.""" return pystac.Asset( href=href, title=self.title, @@ -213,76 +206,38 @@ def ext(self) -> ItemAssetExt: Example:: - collection.ext.item_assets["data"].ext.proj.epsg = 4326 + collection.item_assets["data"].ext.proj.epsg = 4326 """ from pystac.extensions.ext import ItemAssetExt return ItemAssetExt(stac_object=self) -class ItemAssetsExtension(ExtensionManagementMixin[pystac.Collection]): - name: Literal["item_assets"] = "item_assets" +class _ItemAssets(dict): # type:ignore + """Private class for exposing item_assets as a dict + + This class coerces values to ``ItemAssetDefinition``s and + sets that owner on all ``ItemAssetDefinition``s to the collection + that it is owned by. + """ + collection: pystac.Collection def __init__(self, collection: pystac.Collection) -> None: self.collection = collection - - @property - def item_assets(self) -> dict[str, AssetDefinition]: - """Gets or sets a dictionary of assets that can be found in member Items. Maps - the asset key to an :class:`AssetDefinition` instance.""" - result: dict[str, Any] = get_required( - self.collection.extra_fields.get(ITEM_ASSETS_PROP), self, ITEM_ASSETS_PROP - ) - return {k: AssetDefinition(v, self.collection) for k, v in result.items()} - - @item_assets.setter - def item_assets(self, v: dict[str, AssetDefinition]) -> None: - self.collection.extra_fields[ITEM_ASSETS_PROP] = { - k: asset_def.properties for k, asset_def in v.items() - } - - def __repr__(self) -> str: - return f"" - - @classmethod - def get_schema_uri(cls) -> str: - return SCHEMA_URI - - @classmethod - def ext( - cls, obj: pystac.Collection, add_if_missing: bool = False - ) -> ItemAssetsExtension: - """Extends the given :class:`~pystac.Collection` with properties from the - :stac-ext:`Item Assets Extension `. - - Raises: - - pystac.ExtensionTypeError : If an invalid object type is passed. - """ - if isinstance(obj, pystac.Collection): - cls.ensure_has_extension(obj, add_if_missing) - return cls(obj) + if not collection.extra_fields.get("item_assets"): + collection.extra_fields["item_assets"] = {} + self.update(collection.extra_fields["item_assets"]) + + def __setitem__(self, key: str, value: Any) -> None: + if isinstance(value, ItemAssetDefinition): + asset_definition = value + asset_definition.set_owner(self.collection) else: - raise pystac.ExtensionTypeError(cls._ext_error_message(obj)) - - -class ItemAssetsExtensionHooks(ExtensionHooks): - schema_uri: str = SCHEMA_URI - prev_extension_ids = {"asset", "item-assets"} - stac_object_types = {pystac.STACObjectType.COLLECTION} - - def migrate( - self, obj: dict[str, Any], version: STACVersionID, info: STACJSONDescription - ) -> None: - # Handle that the "item-assets" extension had the id of "assets", before - # collection assets (since removed) took over the ID of "assets" - if version < "1.0.0-beta.1" and "asset" in info.extensions: - if "assets" in obj: - obj["item_assets"] = obj["assets"] - del obj["assets"] - - super().migrate(obj, version, info) - + asset_definition = ItemAssetDefinition(value, self.collection) + self.collection.extra_fields["item_assets"][key] = asset_definition.properties + super().__setitem__(key, asset_definition) -ITEM_ASSETS_EXTENSION_HOOKS: ExtensionHooks = ItemAssetsExtensionHooks() + def update(self, *args: Any, **kwargs: Any) -> None: + for k, v in dict(*args, **kwargs).items(): + self[k] = v From a6889882c98a9c335ae36c07caeb40d6c99b18ec Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Mon, 16 Dec 2024 14:08:53 -0500 Subject: [PATCH 05/11] Export AssetDefinition --- pystac/extensions/item_assets.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pystac/extensions/item_assets.py b/pystac/extensions/item_assets.py index 68ddff7f7..6b57e0d15 100644 --- a/pystac/extensions/item_assets.py +++ b/pystac/extensions/item_assets.py @@ -7,9 +7,7 @@ import pystac from pystac.extensions.base import ExtensionManagementMixin from pystac.extensions.hooks import ExtensionHooks -from pystac.item_assets import ( - ItemAssetDefinition as AssetDefinition, -) +from pystac.item_assets import ItemAssetDefinition from pystac.serialization.identify import STACJSONDescription, STACVersionID from pystac.utils import get_required @@ -21,6 +19,12 @@ ITEM_ASSETS_PROP = "item_assets" +class AssetDefinition(ItemAssetDefinition): + def __init__(cls, *args: Any, **kwargs: Any) -> None: + # TODO: deprecation warning in here. + super().__init__(*args, **kwargs) + + class ItemAssetsExtension(ExtensionManagementMixin[pystac.Collection]): name: Literal["item_assets"] = "item_assets" collection: pystac.Collection @@ -29,16 +33,16 @@ def __init__(self, collection: pystac.Collection) -> None: self.collection = collection @property - def item_assets(self) -> dict[str, AssetDefinition]: + def item_assets(self) -> dict[str, ItemAssetDefinition]: """Gets or sets a dictionary of assets that can be found in member Items. Maps the asset key to an :class:`AssetDefinition` instance.""" result: dict[str, Any] = get_required( self.collection.extra_fields.get(ITEM_ASSETS_PROP), self, ITEM_ASSETS_PROP ) - return {k: AssetDefinition(v, self.collection) for k, v in result.items()} + return {k: ItemAssetDefinition(v, self.collection) for k, v in result.items()} @item_assets.setter - def item_assets(self, v: dict[str, AssetDefinition]) -> None: + def item_assets(self, v: dict[str, ItemAssetDefinition]) -> None: self.collection.extra_fields[ITEM_ASSETS_PROP] = { k: asset_def.properties for k, asset_def in v.items() } From 045c1a6bf272bdef5c2a20d9f5b6d4ff44fa8b84 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Fri, 3 Jan 2025 12:50:14 -0500 Subject: [PATCH 06/11] Add deprecated warning and update tests --- pystac/extensions/item_assets.py | 9 ++++ tests/extensions/test_classification.py | 30 ++++++------ tests/extensions/test_raster.py | 11 +++-- tests/{extensions => }/test_item_assets.py | 56 +++++++++++++++------- 4 files changed, 70 insertions(+), 36 deletions(-) rename tests/{extensions => }/test_item_assets.py (66%) diff --git a/pystac/extensions/item_assets.py b/pystac/extensions/item_assets.py index 6b57e0d15..277020b86 100644 --- a/pystac/extensions/item_assets.py +++ b/pystac/extensions/item_assets.py @@ -2,9 +2,11 @@ from __future__ import annotations +import warnings from typing import TYPE_CHECKING, Any, Literal import pystac +from pystac.errors import DeprecatedWarning from pystac.extensions.base import ExtensionManagementMixin from pystac.extensions.hooks import ExtensionHooks from pystac.item_assets import ItemAssetDefinition @@ -30,6 +32,13 @@ class ItemAssetsExtension(ExtensionManagementMixin[pystac.Collection]): collection: pystac.Collection def __init__(self, collection: pystac.Collection) -> None: + warnings.warn( + ( + "The ``item_assets`` extension is deprecated. " + "``item_assets`` are now top-level collection properties." + ), + DeprecatedWarning, + ) self.collection = collection @property diff --git a/tests/extensions/test_classification.py b/tests/extensions/test_classification.py index 8ff526b94..ea5c16416 100644 --- a/tests/extensions/test_classification.py +++ b/tests/extensions/test_classification.py @@ -18,7 +18,6 @@ Classification, ClassificationExtension, ) -from pystac.extensions.item_assets import ItemAssetsExtension from pystac.extensions.raster import RasterBand, RasterExtension from tests.utils import TestCases @@ -51,7 +50,7 @@ def plain_item() -> Item: @pytest.fixture -def collection() -> Collection: +def classification_collection() -> Collection: return Collection.from_file(CLASSIFICATION_COLLECTION_RASTER_URI) @@ -108,12 +107,12 @@ def test_ext_raises_if_item_does_not_conform(plain_item: Item) -> None: ClassificationExtension.ext(plain_item) -def test_ext_raises_on_collection(collection: pystac.Collection) -> None: +def test_ext_raises_on_collection(classification_collection: Collection) -> None: with pytest.raises( pystac.errors.ExtensionTypeError, match="ClassificationExtension does not apply to type 'Collection'", ) as e: - ClassificationExtension.ext(collection) # type:ignore + ClassificationExtension.ext(classification_collection) # type:ignore assert "Hint" in str(e.value) @@ -311,24 +310,27 @@ def test_add_asset_classes(plain_item: Item) -> None: assert asset.extra_fields[CLASSES_PROP] == [{"value": 0, "name": "dummy"}] -def test_item_asset_raster_classes(collection: Collection) -> None: - item_asset = ItemAssetsExtension.ext(collection, add_if_missing=True).item_assets[ - "cloud-mask-raster" - ] +def test_item_asset_raster_classes(classification_collection: Collection) -> None: + assert classification_collection.item_assets + item_asset = classification_collection.item_assets["cloud-mask-raster"] raster_bands = cast(list[RasterBand], RasterExtension.ext(item_asset).bands) raster_bands_ext = ClassificationExtension.ext(raster_bands[0]) raster_bands_ext.__repr__() assert raster_bands_ext.classes is not None -def test_item_assets_extension(collection: Collection) -> None: - item_asset = ItemAssetsExtension.ext(collection, add_if_missing=True).item_assets[ - "cloud-mask-raster" - ] +def test_item_assets_extension(classification_collection: Collection) -> None: + assert classification_collection.item_assets + item_asset = classification_collection.item_assets["cloud-mask-raster"] ext = ClassificationExtension.ext(item_asset) ext.__repr__() - assert ClassificationExtension.get_schema_uri() in collection.stac_extensions - assert collection.ext.item_assets["cloud-mask-raster"].ext.has("classification") + assert ( + ClassificationExtension.get_schema_uri() + in classification_collection.stac_extensions + ) + assert classification_collection.item_assets["cloud-mask-raster"].ext.has( + "classification" + ) def test_older_extension_version(landsat_item: Item) -> None: diff --git a/tests/extensions/test_raster.py b/tests/extensions/test_raster.py index 2ffff58cf..925113dc9 100644 --- a/tests/extensions/test_raster.py +++ b/tests/extensions/test_raster.py @@ -5,7 +5,6 @@ import pystac from pystac import ExtensionTypeError, Item -from pystac.extensions.item_assets import ItemAssetsExtension from pystac.extensions.raster import ( DataType, Histogram, @@ -282,11 +281,13 @@ def test_summaries_adds_uri(self) -> None: def test_collection_item_asset(self) -> None: coll = pystac.Collection.from_file(self.LANDSAT_COLLECTION_EXAMPLE_URI) - qa = ItemAssetsExtension.ext(coll).item_assets["qa"] - ang = ItemAssetsExtension.ext(coll).item_assets["ang"] + assert coll.item_assets - assert RasterExtension.ext(qa).bands is not None - assert RasterExtension.ext(ang).bands is None + qa = coll.item_assets["qa"] + ang = coll.item_assets["ang"] + + assert qa.ext.raster.bands is not None + assert ang.ext.raster.bands is None @pytest.fixture diff --git a/tests/extensions/test_item_assets.py b/tests/test_item_assets.py similarity index 66% rename from tests/extensions/test_item_assets.py rename to tests/test_item_assets.py index a847975b0..f1f35498c 100644 --- a/tests/extensions/test_item_assets.py +++ b/tests/test_item_assets.py @@ -1,11 +1,19 @@ import unittest +import pytest + from pystac import Collection -from pystac.extensions.item_assets import AssetDefinition, ItemAssetsExtension +from pystac.errors import DeprecatedWarning +from pystac.extensions.item_assets import 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" +) + -class TestItemAssetsExtension(unittest.TestCase): +class TestItemAssets(unittest.TestCase): def setUp(self) -> None: self.maxDiff = None self.collection = Collection.from_file( @@ -14,13 +22,13 @@ def setUp(self) -> None: def test_example(self) -> None: collection = self.collection.clone() - item_ext = ItemAssetsExtension.ext(collection) - self.assertEqual(len(item_ext.item_assets), 13) + assert collection.item_assets + self.assertEqual(len(collection.item_assets), 13) self.assertEqual( - item_ext.item_assets["B1"], - AssetDefinition( + collection.item_assets["B1"], + ItemAssetDefinition( { "type": "image/tiff; application=geotiff", "eo:bands": [ @@ -50,7 +58,7 @@ def test_create(self) -> None: description = "Coastal Band Top Of the Atmosphere" media_type = "image/tiff; application=geotiff" roles = ["data"] - asset_defn = AssetDefinition.create( + asset_defn = ItemAssetDefinition.create( title=title, description=description, media_type=media_type, roles=roles ) self.assertEqual(asset_defn.title, title) @@ -59,7 +67,7 @@ def test_create(self) -> None: self.assertEqual(asset_defn.roles, roles) def test_title(self) -> None: - asset_defn = AssetDefinition({}) + asset_defn = ItemAssetDefinition({}) title = "Very Important Asset" asset_defn.title = title @@ -68,7 +76,7 @@ def test_title(self) -> None: self.assertEqual(asset_defn.to_dict()["title"], title) def test_description(self) -> None: - asset_defn = AssetDefinition({}) + asset_defn = ItemAssetDefinition({}) description = "What an incredibly important asset this is!" asset_defn.description = description @@ -77,7 +85,7 @@ def test_description(self) -> None: self.assertEqual(asset_defn.to_dict()["description"], description) def test_media_type(self) -> None: - asset_defn = AssetDefinition({}) + asset_defn = ItemAssetDefinition({}) media_type = "application/json" asset_defn.media_type = media_type @@ -86,7 +94,7 @@ def test_media_type(self) -> None: self.assertEqual(asset_defn.to_dict()["type"], media_type) def test_roles(self) -> None: - asset_defn = AssetDefinition({}) + asset_defn = ItemAssetDefinition({}) roles = ["thumbnail"] asset_defn.roles = roles @@ -96,23 +104,37 @@ def test_roles(self) -> None: def test_extra_fields(collection: Collection) -> None: - asset_definition = AssetDefinition.create( + asset_definition = ItemAssetDefinition.create( title=None, description=None, media_type=None, roles=None, extra_fields={"raster:bands": [{"nodata": 42}]}, ) - item_assets = ItemAssetsExtension.ext(collection, add_if_missing=True) - item_assets.item_assets = {"data": asset_definition} + collection.item_assets = {"data": asset_definition} 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.ext.item_assets["data"].ext.add("raster") - assert (bands := collection.ext.item_assets["data"].ext.raster.bands) + collection.item_assets["data"].ext.add("raster") + assert (bands := collection.item_assets["data"].ext.raster.bands) assert bands[0].nodata == 42 - assert collection.ext.item_assets["data"].ext.has("raster") + assert collection.item_assets["data"].ext.has("raster") assert collection.ext.has("raster") + + +def test_item_assets_extension_is_deprecated() -> None: + collection = Collection.from_file(CLASSIFICATION_COLLECTION_RASTER_URI) + with pytest.warns(DeprecatedWarning, match="top-level collection properties"): + item_asset = ItemAssetsExtension.ext( + collection, add_if_missing=True + ).item_assets["cloud-mask-raster"] + + assert item_asset.ext.has("eo") + + with pytest.warns(DeprecatedWarning, match="top-level collection properties"): + assert collection.ext.item_assets["cloud-mask-raster"].ext.has("eo") + + assert ItemAssetsExtension.get_schema_uri() in collection.stac_extensions From 41557be3d13fef051d2e6afab255d682b7799595 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Fri, 3 Jan 2025 13:10:48 -0500 Subject: [PATCH 07/11] Remove schema uri from stac_extensions on migrate --- pystac/extensions/item_assets.py | 6 ++++++ tests/serialization/test_migrate.py | 5 ++++- tests/test_item_assets.py | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pystac/extensions/item_assets.py b/pystac/extensions/item_assets.py index 277020b86..522492390 100644 --- a/pystac/extensions/item_assets.py +++ b/pystac/extensions/item_assets.py @@ -98,5 +98,11 @@ def migrate( super().migrate(obj, version, info) + # As of STAC spec version 1.1.0 item-assets are part of core + if obj["stac_version"] >= "1.1.0" and self.schema_uri in obj.get( + "stac_extensions", [] + ): + obj["stac_extensions"].remove(self.schema_uri) + ITEM_ASSETS_EXTENSION_HOOKS: ExtensionHooks = ItemAssetsExtensionHooks() diff --git a/tests/serialization/test_migrate.py b/tests/serialization/test_migrate.py index 0d41a781c..1bc5c4acb 100644 --- a/tests/serialization/test_migrate.py +++ b/tests/serialization/test_migrate.py @@ -75,8 +75,11 @@ def test_migrates_renamed_extension(self) -> None: ) ) - assert ItemAssetsExtension.has_extension(collection) + assert ItemAssetsExtension.get_schema_uri() not in collection.stac_extensions + assert not ItemAssetsExtension.has_extension(collection) assert "item_assets" in collection.extra_fields + assert collection.item_assets + assert collection.item_assets["thumbnail"].title == "Thumbnail" def test_migrates_pre_1_0_0_rc1_stats_summary(self) -> None: collection = pystac.Collection.from_file( diff --git a/tests/test_item_assets.py b/tests/test_item_assets.py index f1f35498c..0c8e90cd6 100644 --- a/tests/test_item_assets.py +++ b/tests/test_item_assets.py @@ -127,6 +127,9 @@ def test_extra_fields(collection: Collection) -> None: 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 collection properties"): item_asset = ItemAssetsExtension.ext( collection, add_if_missing=True From a625106b24618d1bf5c70f0475e964acc7d4d8d4 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Fri, 3 Jan 2025 14:19:08 -0500 Subject: [PATCH 08/11] Update docs --- docs/api/item_assets.rst | 7 +++++++ docs/api/pystac.rst | 9 ++++++++ pystac/collection.py | 35 ++++++++++++++++++++++++++++++++ pystac/extensions/item_assets.py | 9 +++++++- pystac/item_assets.py | 17 +++++++++------- tests/test_item_assets.py | 26 ++++++++++++++++++++++-- 6 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 docs/api/item_assets.rst diff --git a/docs/api/item_assets.rst b/docs/api/item_assets.rst new file mode 100644 index 000000000..501c56420 --- /dev/null +++ b/docs/api/item_assets.rst @@ -0,0 +1,7 @@ +pystac.item_assets +================== + +.. automodule:: pystac.item_assets + :members: + :undoc-members: + :noindex: diff --git a/docs/api/pystac.rst b/docs/api/pystac.rst index 8c6da7abd..d1c676955 100644 --- a/docs/api/pystac.rst +++ b/docs/api/pystac.rst @@ -15,6 +15,7 @@ pystac Summaries Item Asset + ItemAssetDefinition CommonMetadata ItemCollection Link @@ -116,6 +117,14 @@ Asset :members: :undoc-members: +ItemAssetDefinition +------------------- + +.. autoclass:: pystac.ItemAssetDefinition + :members: + :undoc-members: + + CommonMetadata -------------- diff --git a/pystac/collection.py b/pystac/collection.py index 5323b82ce..338792c54 100644 --- a/pystac/collection.py +++ b/pystac/collection.py @@ -735,6 +735,41 @@ def get_item(self, id: str, recursive: bool = False) -> Item | None: @property def item_assets(self) -> dict[str, ItemAssetDefinition] | None: + """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 and "item_assets" in self.extra_fields: self._item_assets = _ItemAssets(self) return self._item_assets diff --git a/pystac/extensions/item_assets.py b/pystac/extensions/item_assets.py index 522492390..05194f3b2 100644 --- a/pystac/extensions/item_assets.py +++ b/pystac/extensions/item_assets.py @@ -28,6 +28,13 @@ def __init__(cls, *args: Any, **kwargs: Any) -> None: class ItemAssetsExtension(ExtensionManagementMixin[pystac.Collection]): + """ + DEPRECATED + + .. deprecated:: 1.12.0 + Use :attr:`~pystac.Collection.item_assets` instead. + """ + name: Literal["item_assets"] = "item_assets" collection: pystac.Collection @@ -35,7 +42,7 @@ def __init__(self, collection: pystac.Collection) -> None: warnings.warn( ( "The ``item_assets`` extension is deprecated. " - "``item_assets`` are now top-level collection properties." + "``item_assets`` is now a top-level property of ``Collection``." ), DeprecatedWarning, ) diff --git a/pystac/item_assets.py b/pystac/item_assets.py index 4bcaec9dd..2260cdf0b 100644 --- a/pystac/item_assets.py +++ b/pystac/item_assets.py @@ -1,4 +1,8 @@ -"""Implements the ``Item Asset Definition ``.""" +""" +Implements the `Item Asset Definition Object +`__ +for use as values in the :attr:`~pystac.Collection.item_assets` dict. +""" from __future__ import annotations @@ -18,10 +22,9 @@ class ItemAssetDefinition: - """Object that contains details about the datafiles that will be included in member - Items for this Collection. - - See the `Item Asset Definition Object ` for details. + """Implementation of the `Item Asset Definition Object + `__ + for use as values in the :attr:`~pystac.Collection.item_assets` dict. """ properties: dict[str, Any] @@ -58,10 +61,10 @@ def create( `CommonMark 0.29 `__ syntax MAY be used for rich text representation. media_type : `media type\ - `__ + `__ of the asset. roles : `semantic roles - `__ + `__ of the asset, similar to the use of rel in links. extra_fields : Additional fields on the asset definition, e.g. from extensions. diff --git a/tests/test_item_assets.py b/tests/test_item_assets.py index 0c8e90cd6..a86cf07c2 100644 --- a/tests/test_item_assets.py +++ b/tests/test_item_assets.py @@ -45,6 +45,28 @@ def test_example(self) -> None: ), ) + def test_set_using_dict(self) -> None: + collection = self.collection.clone() + + assert collection.item_assets + self.assertEqual(len(collection.item_assets), 13) + + 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 + + self.assertEqual(collection.item_assets["B1"], collection.item_assets["Bx"]) + class TestAssetDefinition(unittest.TestCase): def setUp(self) -> None: @@ -130,14 +152,14 @@ def test_item_assets_extension_is_deprecated() -> None: assert ItemAssetsExtension.get_schema_uri() not in collection.stac_extensions - with pytest.warns(DeprecatedWarning, match="top-level collection properties"): + with pytest.warns(DeprecatedWarning, match="top-level property of"): item_asset = ItemAssetsExtension.ext( collection, add_if_missing=True ).item_assets["cloud-mask-raster"] assert item_asset.ext.has("eo") - with pytest.warns(DeprecatedWarning, match="top-level collection properties"): + 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 From 19d637ffc47d4f6153602e9c61a36644703d6faf Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Fri, 3 Jan 2025 16:06:16 -0500 Subject: [PATCH 09/11] Add some more tests --- pystac/collection.py | 16 +++---- pystac/extensions/item_assets.py | 15 +++++- tests/test_item_assets.py | 81 +++++++++++++++++++++++++++++--- 3 files changed, 97 insertions(+), 15 deletions(-) diff --git a/pystac/collection.py b/pystac/collection.py index 338792c54..63fd63619 100644 --- a/pystac/collection.py +++ b/pystac/collection.py @@ -734,7 +734,7 @@ def get_item(self, id: str, recursive: bool = False) -> Item | None: raise e @property - def item_assets(self) -> dict[str, ItemAssetDefinition] | None: + def item_assets(self) -> dict[str, ItemAssetDefinition]: """Accessor for `item_assets `__ on this collection. @@ -763,14 +763,14 @@ def item_assets(self) -> dict[str, ItemAssetDefinition] | None: .. 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 + >>> 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 and "item_assets" in self.extra_fields: + if self._item_assets is None: self._item_assets = _ItemAssets(self) return self._item_assets diff --git a/pystac/extensions/item_assets.py b/pystac/extensions/item_assets.py index 05194f3b2..a5a391488 100644 --- a/pystac/extensions/item_assets.py +++ b/pystac/extensions/item_assets.py @@ -22,8 +22,21 @@ class AssetDefinition(ItemAssetDefinition): + """ + DEPRECATED + + .. deprecated:: 1.12.0 + Use :class:`~pystac.ItemAssetDefinition` instead. + """ + def __init__(cls, *args: Any, **kwargs: Any) -> None: - # TODO: deprecation warning in here. + warnings.warn( + ( + "``AssetDefinition`` is deprecated. " + "Please use ``pystac.ItemAssetDefinition`` instead." + ), + DeprecationWarning, + ) super().__init__(*args, **kwargs) diff --git a/tests/test_item_assets.py b/tests/test_item_assets.py index a86cf07c2..0e5ac895f 100644 --- a/tests/test_item_assets.py +++ b/tests/test_item_assets.py @@ -4,7 +4,7 @@ from pystac import Collection from pystac.errors import DeprecatedWarning -from pystac.extensions.item_assets import ItemAssetsExtension +from pystac.extensions.item_assets import AssetDefinition, ItemAssetsExtension from pystac.item_assets import ItemAssetDefinition from tests.utils import TestCases @@ -23,7 +23,6 @@ def setUp(self) -> None: def test_example(self) -> None: collection = self.collection.clone() - assert collection.item_assets self.assertEqual(len(collection.item_assets), 13) self.assertEqual( @@ -48,7 +47,6 @@ def test_example(self) -> None: def test_set_using_dict(self) -> None: collection = self.collection.clone() - assert collection.item_assets self.assertEqual(len(collection.item_assets), 13) collection.item_assets["Bx"] = { @@ -75,6 +73,9 @@ def setUp(self) -> None: TestCases.get_path("data-files/item-assets/example-landsat8.json") ) + def test_eq(self) -> None: + assert self.collection.item_assets["B1"] != {"title": "Coastal Band (B1)"} + def test_create(self) -> None: title = "Coastal Band (B1)" description = "Coastal Band Top Of the Atmosphere" @@ -124,6 +125,25 @@ def test_roles(self) -> None: self.assertEqual(asset_defn.roles, roles) self.assertEqual(asset_defn.to_dict()["roles"], roles) + def test_set_owner(self) -> 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(self.collection) + assert asset_definition.owner == self.collection + def test_extra_fields(collection: Collection) -> None: asset_definition = ItemAssetDefinition.create( @@ -133,7 +153,10 @@ def test_extra_fields(collection: Collection) -> 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") @@ -147,15 +170,29 @@ def test_extra_fields(collection: Collection) -> None: 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 = ItemAssetsExtension.ext( - collection, add_if_missing=True - ).item_assets["cloud-mask-raster"] + 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") @@ -163,3 +200,35 @@ def test_item_assets_extension_is_deprecated() -> None: 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 From b42d147141a16f0580966889073d3f5f7df4dd9c Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Mon, 6 Jan 2025 09:47:48 -0500 Subject: [PATCH 10/11] Remove TYPE_CHECKING --- pystac/extensions/item_assets.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pystac/extensions/item_assets.py b/pystac/extensions/item_assets.py index a5a391488..140207275 100644 --- a/pystac/extensions/item_assets.py +++ b/pystac/extensions/item_assets.py @@ -3,7 +3,7 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING, Any, Literal +from typing import Any, Literal import pystac from pystac.errors import DeprecatedWarning @@ -13,9 +13,6 @@ from pystac.serialization.identify import STACJSONDescription, STACVersionID from pystac.utils import get_required -if TYPE_CHECKING: - pass - SCHEMA_URI = "https://stac-extensions.github.io/item-assets/v1.0.0/schema.json" ITEM_ASSETS_PROP = "item_assets" From 38e6662380b27326dfabe7fc92abb8a03455a6ce Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Mon, 6 Jan 2025 09:53:02 -0500 Subject: [PATCH 11/11] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da0df99ef..e1656cea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,17 @@ ## [Unreleased] + +### Added + +- Top-level `item_assets` dict on `Collection`s ([#1476](https://github.com/stac-utils/pystac/pull/1476)) + ### Changed - Write STAC v1.1.0 ([#1427](https://github.com/stac-utils/pystac/pull/1427)) - Use [uv](https://github.com/astral-sh/uv) for development dependencies and docs ([#1439](https://github.com/stac-utils/pystac/pull/1439)) - Correctly detect absolute file path ref on windows, reflecting change in python 3.13 ([#1475](https://github.com/stac-utils/pystac/pull/14750)) (only effects python 3.13) +- Deprecated `ItemAssetExtension` ([#1476](https://github.com/stac-utils/pystac/pull/1476)) ## [v1.11.0] - 2024-09-26