diff --git a/core/pystac/__init__.py b/core/pystac/__init__.py index 0f1b808b9..5b6c6ba07 100644 --- a/core/pystac/__init__.py +++ b/core/pystac/__init__.py @@ -48,7 +48,6 @@ "set_stac_version", ] -import warnings from typing import Any from pystac.errors import ( @@ -91,56 +90,11 @@ from pystac.utils import HREF import pystac.extensions.hooks -import pystac.extensions.classification -import pystac.extensions.datacube -import pystac.extensions.eo -import pystac.extensions.file -import pystac.extensions.grid -import pystac.extensions.item_assets -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - import pystac.extensions.label -import pystac.extensions.mgrs -import pystac.extensions.mlm -import pystac.extensions.pointcloud -import pystac.extensions.projection -import pystac.extensions.raster -import pystac.extensions.sar -import pystac.extensions.sat -import pystac.extensions.scientific -import pystac.extensions.storage -import pystac.extensions.table -import pystac.extensions.timestamps -import pystac.extensions.version -import pystac.extensions.view -import pystac.extensions.xarray_assets - -EXTENSION_HOOKS = pystac.extensions.hooks.RegisteredExtensionHooks( - [ - pystac.extensions.classification.CLASSIFICATION_EXTENSION_HOOKS, - pystac.extensions.datacube.DATACUBE_EXTENSION_HOOKS, - pystac.extensions.eo.EO_EXTENSION_HOOKS, - pystac.extensions.file.FILE_EXTENSION_HOOKS, - pystac.extensions.grid.GRID_EXTENSION_HOOKS, - pystac.extensions.item_assets.ITEM_ASSETS_EXTENSION_HOOKS, - pystac.extensions.label.LABEL_EXTENSION_HOOKS, - pystac.extensions.mgrs.MGRS_EXTENSION_HOOKS, - pystac.extensions.mlm.MLM_EXTENSION_HOOKS, - pystac.extensions.pointcloud.POINTCLOUD_EXTENSION_HOOKS, - pystac.extensions.projection.PROJECTION_EXTENSION_HOOKS, - pystac.extensions.raster.RASTER_EXTENSION_HOOKS, - pystac.extensions.sar.SAR_EXTENSION_HOOKS, - pystac.extensions.sat.SAT_EXTENSION_HOOKS, - pystac.extensions.scientific.SCIENTIFIC_EXTENSION_HOOKS, - pystac.extensions.storage.STORAGE_EXTENSION_HOOKS, - pystac.extensions.table.TABLE_EXTENSION_HOOKS, - pystac.extensions.timestamps.TIMESTAMPS_EXTENSION_HOOKS, - pystac.extensions.version.VERSION_EXTENSION_HOOKS, - pystac.extensions.view.VIEW_EXTENSION_HOOKS, - pystac.extensions.xarray_assets.XARRAY_ASSETS_EXTENSION_HOOKS, - ] -) +#: Extension migration/link hooks, discovered lazily from installed extension +#: packages via the ``pystac.extensions`` entry point group. ``pystac-core`` +#: intentionally has no compile-time knowledge of any extension. +EXTENSION_HOOKS = pystac.extensions.hooks.RegisteredExtensionHooks() def read_file(href: HREF, stac_io: StacIO | None = None) -> STACObject: diff --git a/core/pystac/asset.py b/core/pystac/asset.py index cfffc1337..4afa28340 100644 --- a/core/pystac/asset.py +++ b/core/pystac/asset.py @@ -272,7 +272,12 @@ def ext(self) -> AssetExt: asset.ext.proj.code = "EPSG:4326" """ - from pystac.extensions.ext import AssetExt + try: + from pystac.extensions.ext import AssetExt + except ModuleNotFoundError as e: + from pystac.errors import _raise_for_missing_ext + + _raise_for_missing_ext(e) return AssetExt(stac_object=self) diff --git a/core/pystac/catalog.py b/core/pystac/catalog.py index 3a81ad2f2..23feb28a7 100644 --- a/core/pystac/catalog.py +++ b/core/pystac/catalog.py @@ -1302,6 +1302,11 @@ def ext(self) -> CatalogExt: print(collection.ext.version) """ - from pystac.extensions.ext import CatalogExt + try: + from pystac.extensions.ext import CatalogExt + except ModuleNotFoundError as e: + from pystac.errors import _raise_for_missing_ext + + _raise_for_missing_ext(e) return CatalogExt(stac_object=self) diff --git a/core/pystac/collection.py b/core/pystac/collection.py index 536fd2830..20cc11506 100644 --- a/core/pystac/collection.py +++ b/core/pystac/collection.py @@ -15,7 +15,7 @@ from pystac import CatalogType, STACObjectType from pystac.asset import Asset, Assets from pystac.catalog import Catalog -from pystac.errors import DeprecatedWarning, ExtensionNotImplemented, STACTypeError +from pystac.errors import DeprecatedWarning, STACTypeError from pystac.item_assets import ItemAssetDefinition, _ItemAssets from pystac.layout import HrefLayoutStrategy from pystac.link import Link @@ -638,8 +638,6 @@ def from_dict( ) -> C: import warnings - from pystac.extensions.version import CollectionVersionExtension - if migrate: info = identify_stac_object(d) d = migrate_to_latest(d, info) @@ -710,16 +708,9 @@ def from_dict( if root: collection.set_root(root) - try: - version = CollectionVersionExtension.ext(collection) - if version.deprecated: - warnings.warn( - f"The collection '{collection.id}' is deprecated.", - DeprecatedWarning, - ) - # Collection asset deprecation checks pending version extension support - except ExtensionNotImplemented: - pass + message = pystac.EXTENSION_HOOKS.get_deprecation_message(collection) + if message is not None: + warnings.warn(message, DeprecatedWarning) return collection @@ -894,6 +885,11 @@ def ext(self) -> CollectionExt: print(collection.ext.xarray) """ - from pystac.extensions.ext import CollectionExt + try: + from pystac.extensions.ext import CollectionExt + except ModuleNotFoundError as e: + from pystac.errors import _raise_for_missing_ext + + _raise_for_missing_ext(e) return CollectionExt(stac_object=self) diff --git a/core/pystac/errors.py b/core/pystac/errors.py index 69c817681..022c87ec1 100644 --- a/core/pystac/errors.py +++ b/core/pystac/errors.py @@ -1,4 +1,20 @@ -from typing import Any +from typing import Any, NoReturn + + +def _raise_for_missing_ext(error: ModuleNotFoundError) -> NoReturn: + """Re-raise a friendlier error when the ``.ext`` accessor is unavailable. + + The `.ext` accessor lives in `pystac.extensions.ext`, which is distributed + with the `pystac` package rather than `pystac-core`. If that specific module + is missing, point the user at `pystac`; otherwise propagate the original + error (a genuinely broken extension install). + """ + if error.name == "pystac.extensions.ext": + raise ImportError( + "The `.ext` accessor is provided by the `pystac` package, not " + "`pystac-core`. Install `pystac` to use `.ext`." + ) from error + raise error class TemplateError(Exception): diff --git a/core/pystac/extensions/hooks.py b/core/pystac/extensions/hooks.py index 8db8ca4e4..8cb0c80ff 100644 --- a/core/pystac/extensions/hooks.py +++ b/core/pystac/extensions/hooks.py @@ -44,6 +44,14 @@ def _get_stac_object_types(self) -> set[str]: def get_object_links(self, obj: STACObject) -> list[str | pystac.RelType] | None: return None + def get_deprecation_message(self, obj: STACObject) -> str | None: + """Return a warning message if `obj` is deprecated per this extension. + + Emitted as a `pystac.DeprecatedWarning` when loading an object from a + dict. The base implementation returns None (not deprecated). + """ + return None + def has_extension(self, obj: dict[str, Any]) -> bool: schema_startswith = VERSION_REGEX.split(self.schema_uri)[0] + "/" return any( @@ -75,8 +83,27 @@ def migrate( class RegisteredExtensionHooks: hooks: dict[str, ExtensionHooks] - def __init__(self, hooks: Iterable[ExtensionHooks]): + def __init__(self, hooks: Iterable[ExtensionHooks] = ()): self.hooks = {e.schema_uri: e for e in hooks} + self._discovered = False + + def _discover(self) -> None: + """Register hooks advertised via the ``pystac.extensions`` entry point group. + + Installed extensions self-advertise their `ExtensionHooks`, so + `pystac-core` needs no compile-time knowledge of them. Runs once. + """ + if self._discovered: + return + self._discovered = True + import warnings + from importlib.metadata import entry_points + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + for entry_point in entry_points(group="pystac.extensions"): + hooks = entry_point.load() + self.hooks.setdefault(hooks.schema_uri, hooks) def add_extension_hooks(self, hooks: ExtensionHooks) -> None: e_id = hooks.schema_uri @@ -92,6 +119,7 @@ def remove_extension_hooks(self, extension_id: str) -> None: del self.hooks[extension_id] def get_extended_object_links(self, obj: STACObject) -> list[str | pystac.RelType]: + self._discover() result: list[str | pystac.RelType] | None = None for ext in obj.stac_extensions: if ext in self.hooks: @@ -106,6 +134,15 @@ def get_extended_object_links(self, obj: STACObject) -> list[str | pystac.RelTyp def migrate( self, obj: dict[str, Any], version: STACVersionID, info: STACJSONDescription ) -> None: + self._discover() for hooks in self.hooks.values(): if info.object_type in hooks._get_stac_object_types(): hooks.migrate(obj, version, info) + + def get_deprecation_message(self, obj: STACObject) -> str | None: + self._discover() + for hooks in self.hooks.values(): + message = hooks.get_deprecation_message(obj) + if message is not None: + return message + return None diff --git a/core/pystac/item.py b/core/pystac/item.py index 7bb4b84cb..eee96065b 100644 --- a/core/pystac/item.py +++ b/core/pystac/item.py @@ -8,7 +8,7 @@ from pystac.asset import Asset, Assets from pystac.catalog import Catalog from pystac.collection import Collection -from pystac.errors import DeprecatedWarning, ExtensionNotImplemented +from pystac.errors import DeprecatedWarning from pystac.link import Link from pystac.serialization import ( identify_stac_object, @@ -426,8 +426,6 @@ def from_dict( ) -> T: import warnings - from pystac.extensions.version import ItemVersionExtension - if preserve_dict: d = deepcopy(d) @@ -482,16 +480,9 @@ def from_dict( if root: item.set_root(root) - try: - version = ItemVersionExtension.ext(item) - if version.deprecated: - warnings.warn( - f"The item '{item.id}' is deprecated.", - DeprecatedWarning, - ) - # Item asset deprecation checks pending version extension support - except ExtensionNotImplemented: - pass + message = pystac.EXTENSION_HOOKS.get_deprecation_message(item) + if message is not None: + warnings.warn(message, DeprecatedWarning) return item @@ -534,6 +525,11 @@ def ext(self) -> ItemExt: item.ext.proj.code = "EPSG:4326" """ - from pystac.extensions.ext import ItemExt + try: + from pystac.extensions.ext import ItemExt + except ModuleNotFoundError as e: + from pystac.errors import _raise_for_missing_ext + + _raise_for_missing_ext(e) return ItemExt(stac_object=self) diff --git a/core/pystac/item_assets.py b/core/pystac/item_assets.py index ab3a1fadb..589d62c0a 100644 --- a/core/pystac/item_assets.py +++ b/core/pystac/item_assets.py @@ -212,7 +212,12 @@ def ext(self) -> ItemAssetExt: collection.item_assets["data"].ext.proj.epsg = 4326 """ - from pystac.extensions.ext import ItemAssetExt + try: + from pystac.extensions.ext import ItemAssetExt + except ModuleNotFoundError as e: + from pystac.errors import _raise_for_missing_ext + + _raise_for_missing_ext(e) return ItemAssetExt(stac_object=self) diff --git a/core/pystac/link.py b/core/pystac/link.py index b2567e3fd..4a5ca41b5 100644 --- a/core/pystac/link.py +++ b/core/pystac/link.py @@ -525,6 +525,11 @@ def ext(self) -> LinkExt: link.ext.file.size = 8675309 """ - from pystac.extensions.ext import LinkExt + try: + from pystac.extensions.ext import LinkExt + except ModuleNotFoundError as e: + from pystac.errors import _raise_for_missing_ext + + _raise_for_missing_ext(e) return LinkExt(stac_object=self) diff --git a/extensions/classification/pyproject.toml b/extensions/classification/pyproject.toml index ff1519f5a..2fdc17060 100644 --- a/extensions/classification/pyproject.toml +++ b/extensions/classification/pyproject.toml @@ -19,7 +19,13 @@ classifiers = [ "Programming Language :: Python :: 3.13", ] requires-python = ">=3.10" -dependencies = ["pystac-core"] +dependencies = [ + "pystac-core", + "pystac-ext-raster", +] + +[project.entry-points."pystac.extensions"] +classification = "pystac.extensions.classification:CLASSIFICATION_EXTENSION_HOOKS" [project.urls] Documentation = "https://pystac.readthedocs.io" diff --git a/extensions/datacube/pyproject.toml b/extensions/datacube/pyproject.toml index e08fba072..dc95e651b 100644 --- a/extensions/datacube/pyproject.toml +++ b/extensions/datacube/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +datacube = "pystac.extensions.datacube:DATACUBE_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/eo/pyproject.toml b/extensions/eo/pyproject.toml index 06bd7b411..b78c41855 100644 --- a/extensions/eo/pyproject.toml +++ b/extensions/eo/pyproject.toml @@ -19,7 +19,14 @@ classifiers = [ "Programming Language :: Python :: 3.13", ] requires-python = ">=3.10" -dependencies = ["pystac-core"] +dependencies = [ + "pystac-core", + "pystac-ext-projection", + "pystac-ext-view", +] + +[project.entry-points."pystac.extensions"] +eo = "pystac.extensions.eo:EO_EXTENSION_HOOKS" [project.urls] Documentation = "https://pystac.readthedocs.io" diff --git a/extensions/file/pyproject.toml b/extensions/file/pyproject.toml index f8ecc905f..13784eb33 100644 --- a/extensions/file/pyproject.toml +++ b/extensions/file/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +file = "pystac.extensions.file:FILE_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/grid/pyproject.toml b/extensions/grid/pyproject.toml index eea7e004b..d6d3617a3 100644 --- a/extensions/grid/pyproject.toml +++ b/extensions/grid/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +grid = "pystac.extensions.grid:GRID_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/item_assets/pyproject.toml b/extensions/item_assets/pyproject.toml index 5f89d8a0f..10a97c813 100644 --- a/extensions/item_assets/pyproject.toml +++ b/extensions/item_assets/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +item_assets = "pystac.extensions.item_assets:ITEM_ASSETS_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/label/pyproject.toml b/extensions/label/pyproject.toml index 08403578c..1c8efcda5 100644 --- a/extensions/label/pyproject.toml +++ b/extensions/label/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +label = "pystac.extensions.label:LABEL_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/mgrs/pyproject.toml b/extensions/mgrs/pyproject.toml index 9485ab107..325cbc4a3 100644 --- a/extensions/mgrs/pyproject.toml +++ b/extensions/mgrs/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +mgrs = "pystac.extensions.mgrs:MGRS_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/mlm/pyproject.toml b/extensions/mlm/pyproject.toml index 51ff70f30..33a989c4f 100644 --- a/extensions/mlm/pyproject.toml +++ b/extensions/mlm/pyproject.toml @@ -19,7 +19,14 @@ classifiers = [ "Programming Language :: Python :: 3.13", ] requires-python = ">=3.10" -dependencies = ["pystac-core"] +dependencies = [ + "pystac-core", + "pystac-ext-classification", + "pystac-ext-raster", +] + +[project.entry-points."pystac.extensions"] +mlm = "pystac.extensions.mlm:MLM_EXTENSION_HOOKS" [project.urls] Documentation = "https://pystac.readthedocs.io" diff --git a/extensions/pointcloud/pyproject.toml b/extensions/pointcloud/pyproject.toml index cf507aa64..995013db0 100644 --- a/extensions/pointcloud/pyproject.toml +++ b/extensions/pointcloud/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +pointcloud = "pystac.extensions.pointcloud:POINTCLOUD_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/projection/pyproject.toml b/extensions/projection/pyproject.toml index a7137059c..73485efc8 100644 --- a/extensions/projection/pyproject.toml +++ b/extensions/projection/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +projection = "pystac.extensions.projection:PROJECTION_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/raster/pyproject.toml b/extensions/raster/pyproject.toml index 76ea9e93c..6d274b66c 100644 --- a/extensions/raster/pyproject.toml +++ b/extensions/raster/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +raster = "pystac.extensions.raster:RASTER_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/sar/pyproject.toml b/extensions/sar/pyproject.toml index 5e39abd26..91f81aba9 100644 --- a/extensions/sar/pyproject.toml +++ b/extensions/sar/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +sar = "pystac.extensions.sar:SAR_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/sat/pyproject.toml b/extensions/sat/pyproject.toml index 7b5343a73..da292e923 100644 --- a/extensions/sat/pyproject.toml +++ b/extensions/sat/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +sat = "pystac.extensions.sat:SAT_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/scientific/pyproject.toml b/extensions/scientific/pyproject.toml index b287fbc8d..62bc45ecf 100644 --- a/extensions/scientific/pyproject.toml +++ b/extensions/scientific/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +scientific = "pystac.extensions.scientific:SCIENTIFIC_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/storage/pyproject.toml b/extensions/storage/pyproject.toml index e99ed4c8a..3e460cf9c 100644 --- a/extensions/storage/pyproject.toml +++ b/extensions/storage/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +storage = "pystac.extensions.storage:STORAGE_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/table/pyproject.toml b/extensions/table/pyproject.toml index 0425b99cb..f8d31a9c7 100644 --- a/extensions/table/pyproject.toml +++ b/extensions/table/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +table = "pystac.extensions.table:TABLE_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/timestamps/pyproject.toml b/extensions/timestamps/pyproject.toml index 798a3b9cc..d1e3cc7d4 100644 --- a/extensions/timestamps/pyproject.toml +++ b/extensions/timestamps/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +timestamps = "pystac.extensions.timestamps:TIMESTAMPS_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/version/pyproject.toml b/extensions/version/pyproject.toml index 22fe02028..b537f664e 100644 --- a/extensions/version/pyproject.toml +++ b/extensions/version/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +version = "pystac.extensions.version:VERSION_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/version/pystac/extensions/version.py b/extensions/version/pystac/extensions/version.py index a3a583583..f05cb04ba 100644 --- a/extensions/version/pystac/extensions/version.py +++ b/extensions/version/pystac/extensions/version.py @@ -24,7 +24,7 @@ STACObject, STACObjectType, ) -from pystac.errors import DeprecatedWarning +from pystac.errors import DeprecatedWarning, ExtensionNotImplemented from pystac.extensions.base import ExtensionManagementMixin, PropertiesExtension from pystac.extensions.hooks import ExtensionHooks from pystac.utils import StringEnum, map_opt @@ -427,6 +427,23 @@ def get_object_links(self, obj: STACObject) -> list[str] | None: ] return None + def get_deprecation_message(self, obj: STACObject) -> str | None: + version: VersionExtension[Any] + try: + if isinstance(obj, Collection): + version = VersionExtension.ext(obj) + kind = "collection" + elif isinstance(obj, Item): + version = VersionExtension.ext(obj) + kind = "item" + else: + return None + except ExtensionNotImplemented: + return None + if version.deprecated: + return f"The {kind} '{obj.id}' is deprecated." + return None + VERSION_EXTENSION_HOOKS: ExtensionHooks = VersionExtensionHooks() diff --git a/extensions/view/pyproject.toml b/extensions/view/pyproject.toml index 4ee10f51c..d675cd256 100644 --- a/extensions/view/pyproject.toml +++ b/extensions/view/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +view = "pystac.extensions.view:VIEW_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/extensions/xarray_assets/pyproject.toml b/extensions/xarray_assets/pyproject.toml index 190006a12..b10dd8b83 100644 --- a/extensions/xarray_assets/pyproject.toml +++ b/extensions/xarray_assets/pyproject.toml @@ -21,6 +21,9 @@ classifiers = [ requires-python = ">=3.10" dependencies = ["pystac-core"] +[project.entry-points."pystac.extensions"] +xarray_assets = "pystac.extensions.xarray_assets:XARRAY_ASSETS_EXTENSION_HOOKS" + [project.urls] Documentation = "https://pystac.readthedocs.io" Repository = "https://github.com/stac-utils/pystac" diff --git a/pyproject.toml b/pyproject.toml index 3fdb79de7..3ea9456bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -174,10 +174,10 @@ filterwarnings = ["error"] addopts = "--block-network --record-mode=none" [tool.hatch.build.targets.sdist] -only-include = ["README.md", "LICENSE"] +only-include = ["README.md", "LICENSE", "pystac"] [tool.hatch.build.targets.wheel] -only-include = ["README.md"] +packages = ["pystac"] [tool.uv.sources] pystac-core = { workspace = true } diff --git a/core/pystac/extensions/ext.py b/pystac/extensions/ext.py similarity index 100% rename from core/pystac/extensions/ext.py rename to pystac/extensions/ext.py diff --git a/uv.lock b/uv.lock index bac46af53..f2dd831f6 100644 --- a/uv.lock +++ b/uv.lock @@ -3902,10 +3902,14 @@ version = "2.0.0" source = { editable = "extensions/classification" } dependencies = [ { name = "pystac-core" }, + { name = "pystac-ext-raster" }, ] [package.metadata] -requires-dist = [{ name = "pystac-core", editable = "core" }] +requires-dist = [ + { name = "pystac-core", editable = "core" }, + { name = "pystac-ext-raster", editable = "extensions/raster" }, +] [[package]] name = "pystac-ext-datacube" @@ -3924,10 +3928,16 @@ version = "1.1.0" source = { editable = "extensions/eo" } dependencies = [ { name = "pystac-core" }, + { name = "pystac-ext-projection" }, + { name = "pystac-ext-view" }, ] [package.metadata] -requires-dist = [{ name = "pystac-core", editable = "core" }] +requires-dist = [ + { name = "pystac-core", editable = "core" }, + { name = "pystac-ext-projection", editable = "extensions/projection" }, + { name = "pystac-ext-view", editable = "extensions/view" }, +] [[package]] name = "pystac-ext-file" @@ -3990,10 +4000,16 @@ version = "1.4.0" source = { editable = "extensions/mlm" } dependencies = [ { name = "pystac-core" }, + { name = "pystac-ext-classification" }, + { name = "pystac-ext-raster" }, ] [package.metadata] -requires-dist = [{ name = "pystac-core", editable = "core" }] +requires-dist = [ + { name = "pystac-core", editable = "core" }, + { name = "pystac-ext-classification", editable = "extensions/classification" }, + { name = "pystac-ext-raster", editable = "extensions/raster" }, +] [[package]] name = "pystac-ext-pointcloud"