pystac-ext-version 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,443 @@
1
+ """Implements the :stac-ext:`Versioning Indicators Extension <version>`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Generator
6
+ from contextlib import contextmanager
7
+ from typing import (
8
+ Any,
9
+ Generic,
10
+ Literal,
11
+ TypeVar,
12
+ cast,
13
+ )
14
+
15
+ from pystac import (
16
+ Asset,
17
+ Catalog,
18
+ Collection,
19
+ ExtensionTypeError,
20
+ Item,
21
+ ItemAssetDefinition,
22
+ Link,
23
+ MediaType,
24
+ STACObject,
25
+ STACObjectType,
26
+ )
27
+ from pystac.errors import DeprecatedWarning
28
+ from pystac.extensions.base import ExtensionManagementMixin, PropertiesExtension
29
+ from pystac.extensions.hooks import ExtensionHooks
30
+ from pystac.utils import StringEnum, map_opt
31
+
32
+ #: Generalized version of :class:`~pystac.Catalog`, :class:`~pystac.Collection`,
33
+ #: :class:`~pystac.Item`, :class:`~pystac.Asset` or :class:`~pystac.ItemAssetDefinition`
34
+ T = TypeVar("T", Catalog, Collection, Item, Asset, ItemAssetDefinition)
35
+
36
+ #: Generalized version of :class:`~pystac.Catalog`, :class:`~pystac.Collection`, or
37
+ #: :class:`~pystac.Item`
38
+ U = TypeVar("U", Catalog, Collection, Item)
39
+
40
+ SCHEMA_URI = "https://stac-extensions.github.io/version/v1.2.0/schema.json"
41
+
42
+ # STAC fields - These are unusual for an extension in that they do not have
43
+ # a prefix. e.g. nothing like "ver:"
44
+ VERSION: str = "version"
45
+ DEPRECATED: str = "deprecated"
46
+ EXPERIMENTAL: str = "experimental"
47
+
48
+
49
+ class VersionRelType(StringEnum):
50
+ """A list of rel types defined in the Version Extension.
51
+
52
+ See the :stac-ext:`Version Extension Relation types
53
+ <version#relation-types>` documentation
54
+ for details."""
55
+
56
+ LATEST = "latest-version"
57
+ """Indicates a link pointing to a resource containing the latest version."""
58
+
59
+ PREDECESSOR = "predecessor-version"
60
+ """Indicates a link pointing to a resource containing the predecessor version in the
61
+ version history."""
62
+
63
+ SUCCESSOR = "successor-version"
64
+ """Indicates a link pointing to a resource containing the successor version in the
65
+ version history."""
66
+
67
+ HISTORY = "version-history"
68
+ """This link points to a version history or changelog.
69
+
70
+ This can be for example a Markdown file with the corresponding media type or
71
+ a STAC Catalog or Collection.
72
+ """
73
+
74
+
75
+ class BaseVersionExtension(
76
+ Generic[T],
77
+ PropertiesExtension,
78
+ ExtensionManagementMixin[Catalog | Collection | Item],
79
+ ):
80
+ """A base extension that just gets and sets attributes, not links.
81
+
82
+ Used for Assets and AssetDefinitions.
83
+ """
84
+
85
+ name: Literal["version"] = "version"
86
+
87
+ def apply_base(
88
+ self,
89
+ version: str | None = None,
90
+ deprecated: bool | None = None,
91
+ experimental: bool | None = None,
92
+ ) -> None:
93
+ """Applies attributes to this extension object."""
94
+ self.version = version
95
+ self.deprecated = deprecated
96
+ self.experimental = experimental
97
+
98
+ @property
99
+ def version(self) -> str | None:
100
+ """Get or sets a version string of this object."""
101
+ return self._get_property(VERSION, str)
102
+
103
+ @version.setter
104
+ def version(self, v: str | None) -> None:
105
+ self._set_property(VERSION, v, pop_if_none=True)
106
+
107
+ @property
108
+ def deprecated(self) -> bool | None:
109
+ """Get or sets whether this object is deprecated.
110
+
111
+ A value of ``True`` specifies that the object is deprecated with the
112
+ potential to be removed. It should be transitioned out of usage as soon
113
+ as possible and users should refrain from using it in new projects. A
114
+ link with relation type ``latest-version`` SHOULD be added to the links
115
+ and MUST refer to the resource that can be used instead.
116
+
117
+ NOTE:
118
+ A :class:`pystac.DeprecatedWarning` is issued if the ``deprecated``
119
+ property is ``True`` when deserializing a dictionary to an object.
120
+ The :meth:`~pystac.extensions.version.ignore_deprecated` context
121
+ manager is provided as a convenience to suppress these warnings:
122
+
123
+ >>> with ignore_deprecated():
124
+ ... deprecated_item = pystac.Item.from_dict(deprecated_item_dict)
125
+ """
126
+ return self._get_property(DEPRECATED, bool)
127
+
128
+ @deprecated.setter
129
+ def deprecated(self, v: bool | None) -> None:
130
+ self._set_property(DEPRECATED, v, pop_if_none=True)
131
+
132
+ @property
133
+ def experimental(self) -> bool | None:
134
+ """Get and set whether this object is experimental.
135
+
136
+ Specifies that the context this field is used in (e.g. Asset or
137
+ Collection) is experimental with the potential to break or be unstable.
138
+ """
139
+ return self._get_property(EXPERIMENTAL, bool)
140
+
141
+ @experimental.setter
142
+ def experimental(self, v: bool | None) -> None:
143
+ self._set_property(EXPERIMENTAL, v, pop_if_none=True)
144
+
145
+ @classmethod
146
+ def get_schema_uri(cls) -> str:
147
+ return SCHEMA_URI
148
+
149
+ @classmethod
150
+ def ext(cls, obj: T, add_if_missing: bool = False) -> BaseVersionExtension[T]:
151
+ """Extends the given STAC Object with properties from the :stac-ext:`Versioning
152
+ Indicators Extension <version>`.
153
+
154
+ Raises:
155
+
156
+ pystac.ExtensionTypeError : If an invalid object type is passed.
157
+ """
158
+ if isinstance(obj, Collection):
159
+ cls.ensure_has_extension(obj, add_if_missing)
160
+ return cast(BaseVersionExtension[T], CollectionVersionExtension(obj))
161
+ elif isinstance(obj, Catalog):
162
+ cls.ensure_has_extension(obj, add_if_missing)
163
+ return cast(BaseVersionExtension[T], CatalogVersionExtension(obj))
164
+ elif isinstance(obj, Item):
165
+ cls.ensure_has_extension(obj, add_if_missing)
166
+ return cast(BaseVersionExtension[T], ItemVersionExtension(obj))
167
+ elif isinstance(obj, Asset):
168
+ cls.ensure_owner_has_extension(obj, add_if_missing)
169
+ return cast(BaseVersionExtension[T], AssetVersionExtension(obj))
170
+ else:
171
+ raise ExtensionTypeError(cls._ext_error_message(obj))
172
+
173
+
174
+ class VersionExtension(
175
+ Generic[U],
176
+ BaseVersionExtension[U],
177
+ ):
178
+ """An abstract class that can be used to extend the properties of an
179
+ :class:`~pystac.Item`, :class:`~pystac.Collection`, or
180
+ :class:`~pystac.Catalog` with properties from the :stac-ext:`Versioning
181
+ Indicators Extension <version>`. This class is generic over the type of STAC
182
+ Object to be extended.
183
+
184
+ To create a concrete instance of :class:`VersionExtension`, use the
185
+ :meth:`VersionExtension.ext` method. For example:
186
+
187
+ .. code-block:: python
188
+
189
+ >>> item: pystac.Item = ...
190
+ >>> version_ext = VersionExtension.ext(item)
191
+ """
192
+
193
+ obj: STACObject
194
+
195
+ def __init__(self, obj: STACObject) -> None:
196
+ self.obj = obj
197
+
198
+ def apply(
199
+ self,
200
+ version: str | None = None,
201
+ deprecated: bool | None = None,
202
+ experimental: bool | None = None,
203
+ latest: U | None = None,
204
+ predecessor: U | None = None,
205
+ successor: U | None = None,
206
+ ) -> None:
207
+ """Applies version extension properties to the extended :class:`~pystac.Item` or
208
+ :class:`~pystac.Collection`.
209
+
210
+ Args:
211
+ version : The version string for the item.
212
+ deprecated : Optional flag set to ``True`` if an Item is deprecated with the
213
+ potential to be removed. Defaults to ``False`` if not present.
214
+ latest : Item representing the latest (e.g., current) version.
215
+ predecessor : Item representing the resource containing the predecessor
216
+ version in the version history.
217
+ successor : Item representing the resource containing the successor version
218
+ in the version history.
219
+ """
220
+ self.apply_base(version, deprecated, experimental)
221
+ if latest:
222
+ self.latest = latest
223
+ if predecessor:
224
+ self.predecessor = predecessor
225
+ if successor:
226
+ self.successor = successor
227
+
228
+ @property
229
+ def latest(self) -> U | None:
230
+ """Gets or sets the :class:`~pystac.Link` to the :class:`~pystac.Item`
231
+ representing the most recent version.
232
+ """
233
+ return map_opt(
234
+ lambda x: cast(U, x),
235
+ next(iter(self.obj.get_stac_objects(VersionRelType.LATEST)), None),
236
+ )
237
+
238
+ @latest.setter
239
+ def latest(self, value: U | None) -> None:
240
+ self.obj.clear_links(VersionRelType.LATEST)
241
+ if value is not None:
242
+ self.obj.add_link(Link(VersionRelType.LATEST, value, MediaType.JSON))
243
+
244
+ @property
245
+ def predecessor(self) -> U | None:
246
+ """Gets or sets the :class:`~pystac.Link` to the :class:`~pystac.Item`
247
+ representing the resource containing the predecessor version in the version
248
+ history.
249
+ """
250
+ return map_opt(
251
+ lambda x: cast(U, x),
252
+ next(iter(self.obj.get_stac_objects(VersionRelType.PREDECESSOR)), None),
253
+ )
254
+
255
+ @predecessor.setter
256
+ def predecessor(self, value: U | None) -> None:
257
+ self.obj.clear_links(VersionRelType.PREDECESSOR)
258
+ if value is not None:
259
+ self.obj.add_link(
260
+ Link(
261
+ VersionRelType.PREDECESSOR,
262
+ value,
263
+ MediaType.JSON,
264
+ )
265
+ )
266
+
267
+ @property
268
+ def successor(self) -> U | None:
269
+ """Gets or sets the :class:`~pystac.Link` to the :class:`~pystac.Item`
270
+ representing the resource containing the successor version in the version
271
+ history.
272
+ """
273
+ return map_opt(
274
+ lambda x: cast(U, x),
275
+ next(iter(self.obj.get_stac_objects(VersionRelType.SUCCESSOR)), None),
276
+ )
277
+
278
+ @successor.setter
279
+ def successor(self, value: U | None) -> None:
280
+ self.obj.clear_links(VersionRelType.SUCCESSOR)
281
+ if value is not None:
282
+ self.obj.add_link(Link(VersionRelType.SUCCESSOR, value, MediaType.JSON))
283
+
284
+ @classmethod
285
+ def ext(cls, obj: U, add_if_missing: bool = False) -> VersionExtension[U]:
286
+ """Extends the given STAC Object with properties from the :stac-ext:`Versioning
287
+ Indicators Extension <version>`.
288
+
289
+ This extension can be applied to instances of :class:`~pystac.Item` or
290
+ :class:`~pystac.Collection`.
291
+
292
+ Raises:
293
+
294
+ pystac.ExtensionTypeError : If an invalid object type is passed.
295
+ """
296
+ if isinstance(obj, Collection):
297
+ cls.ensure_has_extension(obj, add_if_missing)
298
+ return cast(VersionExtension[U], CollectionVersionExtension(obj))
299
+ elif isinstance(obj, Catalog):
300
+ cls.ensure_has_extension(obj, add_if_missing)
301
+ return cast(VersionExtension[U], CatalogVersionExtension(obj))
302
+ elif isinstance(obj, Item):
303
+ cls.ensure_has_extension(obj, add_if_missing)
304
+ return cast(VersionExtension[U], ItemVersionExtension(obj))
305
+ else:
306
+ raise ExtensionTypeError(cls._ext_error_message(obj))
307
+
308
+
309
+ class CatalogVersionExtension(VersionExtension[Catalog]):
310
+ """A concrete implementation of :class:`VersionExtension` on a
311
+ :class:`~pystac.Catalog` that extends the properties of the Catalog to
312
+ include properties defined in the :stac-ext:`Versioning Indicators Extension
313
+ <version>`.
314
+
315
+ This class should generally not be instantiated directly. Instead, call
316
+ :meth:`VersionExtension.ext` on an :class:`~pystac.Catalog` to extend it.
317
+ """
318
+
319
+ catalog: Catalog
320
+ links: list[Link]
321
+ properties: dict[str, Any]
322
+
323
+ def __init__(self, catalog: Catalog):
324
+ self.catalog = catalog
325
+ self.properties = catalog.extra_fields
326
+ self.links = catalog.links
327
+ super().__init__(self.catalog)
328
+
329
+ def __repr__(self) -> str:
330
+ return f"<CatalogVersionExtension Item id={self.catalog.id}>"
331
+
332
+
333
+ class CollectionVersionExtension(VersionExtension[Collection]):
334
+ """A concrete implementation of :class:`VersionExtension` on a
335
+ :class:`~pystac.Collection` that extends the properties of the Collection to
336
+ include properties defined in the :stac-ext:`Versioning Indicators Extension
337
+ <version>`.
338
+
339
+ This class should generally not be instantiated directly. Instead, call
340
+ :meth:`VersionExtension.ext` on an :class:`~pystac.Collection` to extend it.
341
+ """
342
+
343
+ collection: Collection
344
+ links: list[Link]
345
+ properties: dict[str, Any]
346
+
347
+ def __init__(self, collection: Collection):
348
+ self.collection = collection
349
+ self.properties = collection.extra_fields
350
+ self.links = collection.links
351
+ super().__init__(self.collection)
352
+
353
+ def __repr__(self) -> str:
354
+ return f"<CollectionVersionExtension Item id={self.collection.id}>"
355
+
356
+
357
+ class ItemVersionExtension(VersionExtension[Item]):
358
+ """A concrete implementation of :class:`VersionExtension` on an
359
+ :class:`~pystac.Item` that extends the properties of the Item to include properties
360
+ defined in the :stac-ext:`Versioning Indicators Extension <version>`.
361
+
362
+ This class should generally not be instantiated directly. Instead, call
363
+ :meth:`VersionExtension.ext` on an :class:`~pystac.Item` to extend it.
364
+ """
365
+
366
+ item: Item
367
+ links: list[Link]
368
+ properties: dict[str, Any]
369
+
370
+ def __init__(self, item: Item):
371
+ self.item = item
372
+ self.properties = item.properties
373
+ self.links = item.links
374
+ super().__init__(self.item)
375
+
376
+ def __repr__(self) -> str:
377
+ return f"<ItemVersionExtension Item id={self.item.id}>"
378
+
379
+
380
+ class AssetVersionExtension(BaseVersionExtension[Asset]):
381
+ """A concrete implementation of :class:`VersionExtension` on an
382
+ :class:`~pystac.Asset` that extends the properties of the Asset to include
383
+ properties defined in the :stac-ext:`Versioning Indicators Extension
384
+ <version>`.
385
+
386
+ This class should generally not be instantiated directly. Instead, call
387
+ :meth:`VersionExtension.ext` on an :class:`~pystac.Asset` to extend it.
388
+ """
389
+
390
+ asset: Asset
391
+ properties: dict[str, Any]
392
+
393
+ def __init__(self, asset: Asset):
394
+ self.asset = asset
395
+ self.properties = asset.extra_fields
396
+
397
+ def __repr__(self) -> str:
398
+ return f"<AssetVersionExtension Asset href={self.asset.href}>"
399
+
400
+
401
+ class ItemAssetsViewExtension(BaseVersionExtension[ItemAssetDefinition]):
402
+ properties: dict[str, Any]
403
+
404
+ def __init__(self, item_asset: ItemAssetDefinition):
405
+ self.properties = item_asset.properties
406
+
407
+
408
+ class VersionExtensionHooks(ExtensionHooks):
409
+ schema_uri = SCHEMA_URI
410
+ prev_extension_ids = {
411
+ "version",
412
+ "https://stac-extensions.github.io/version/v1.0.0/schema.json",
413
+ "https://stac-extensions.github.io/version/v1.1.0/schema.json",
414
+ }
415
+ stac_object_types = {
416
+ STACObjectType.COLLECTION,
417
+ STACObjectType.ITEM,
418
+ STACObjectType.CATALOG,
419
+ }
420
+
421
+ def get_object_links(self, obj: STACObject) -> list[str] | None:
422
+ if isinstance(obj, Collection) or isinstance(obj, Item):
423
+ return [
424
+ VersionRelType.LATEST,
425
+ VersionRelType.PREDECESSOR,
426
+ VersionRelType.SUCCESSOR,
427
+ ]
428
+ return None
429
+
430
+
431
+ VERSION_EXTENSION_HOOKS: ExtensionHooks = VersionExtensionHooks()
432
+
433
+
434
+ @contextmanager
435
+ def ignore_deprecated() -> Generator[None]:
436
+ """Context manager for suppressing the :class:`pystac.DeprecatedWarning`
437
+ when creating a deprecated :class:`~pystac.Item` or :class:`~pystac.Collection`
438
+ from a dictionary."""
439
+ import warnings
440
+
441
+ with warnings.catch_warnings():
442
+ warnings.simplefilter("ignore", category=DeprecatedWarning)
443
+ yield
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: pystac-ext-version
3
+ Version: 1.2.0
4
+ Summary: Version extension for PySTAC
5
+ Project-URL: Documentation, https://pystac.readthedocs.io
6
+ Project-URL: Repository, https://github.com/stac-utils/pystac
7
+ Project-URL: Issues, https://github.com/stac-utils/pystac/issues
8
+ Project-URL: Changelog, https://github.com/stac-utils/pystac/blob/main/CHANGELOG.md
9
+ Project-URL: Discussions, https://github.com/radiantearth/stac-spec/discussions/categories/stac-software
10
+ License: Apache-2.0
11
+ Keywords: STAC,catalog,imagery,pystac,raster,version
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: Apache Software License
15
+ Classifier: Natural Language :: English
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: pystac-core
23
+ Description-Content-Type: text/markdown
24
+
25
+ # pystac-ext-version
26
+
27
+ [PySTAC](https://pypi.org/project/pystac/) extension package for the [Versioning Indicators Extension](https://github.com/stac-extensions/version).
28
+ This extension provides fields for tracking version information and deprecation status of STAC items and collections, including links to predecessor and successor versions.
29
+
30
+ ## Supported versions
31
+
32
+ - [v1.2.0](https://stac-extensions.github.io/version/v1.2.0/schema.json)
33
+
34
+ ## Versioning
35
+
36
+ This package's version corresponds to the version of the extension specification it targets.
37
+ When we release updates to the package code without changing the target extension version, we use [post releases](https://packaging.python.org/en/latest/discussions/versioning/#post-releases), e.g. `1.2.0.post1`.
@@ -0,0 +1,5 @@
1
+ pystac/extensions/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ pystac/extensions/version.py,sha256=zsSFjVWo42cxoSEBX6Mfh0qMxwikeqt_uGrwZnpGcPw,15506
3
+ pystac_ext_version-1.2.0.dist-info/METADATA,sha256=DaSacIqpyrCQwJjdU32NayHmj-TGh1j7m0uixw8595A,1856
4
+ pystac_ext_version-1.2.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
5
+ pystac_ext_version-1.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any