Skip to content

Commit 58f2a99

Browse files
committed
archive extension implementation
1 parent 5b72d15 commit 58f2a99

File tree

4 files changed

+317
-0
lines changed

4 files changed

+317
-0
lines changed

pystac/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
import pystac.extensions.version
111111
import pystac.extensions.view
112112
import pystac.extensions.xarray_assets
113+
import pystac.extensions.archive
113114

114115
EXTENSION_HOOKS = pystac.extensions.hooks.RegisteredExtensionHooks(
115116
[
@@ -133,6 +134,7 @@
133134
pystac.extensions.version.VERSION_EXTENSION_HOOKS,
134135
pystac.extensions.view.VIEW_EXTENSION_HOOKS,
135136
pystac.extensions.xarray_assets.XARRAY_ASSETS_EXTENSION_HOOKS,
137+
pystac.extensions.archive.ARCHIVE_EXTENSION_HOOKS,
136138
]
137139
)
138140

pystac/extensions/archive.py

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
"""Implements the :stac-ext:`Archive Extension <archive>`."""
2+
3+
from __future__ import annotations
4+
5+
#from abc import ABC
6+
from typing import Any, Generic, Literal, TypeVar, Union, cast
7+
8+
import pystac
9+
from pystac.extensions import item_assets
10+
from pystac.extensions.base import ExtensionManagementMixin, PropertiesExtension, SummariesExtension
11+
from pystac.extensions.hooks import ExtensionHooks
12+
from pystac.utils import StringEnum
13+
14+
T = TypeVar(
15+
"T", pystac.Asset, item_assets.AssetDefinition
16+
)
17+
18+
# For time being set the URL to repo location.
19+
# Later to be in standard location like
20+
# "https://stac-extensions.github.io/archive/v1.0.0/schema.json"
21+
22+
SCHEMA_URI = "https://github.com/stac-extensions/archive/blob/main/json-schema/schema.json"
23+
PREFIX: str = "archive:"
24+
25+
# Field names
26+
ARCHIVE_HREF_PROP = PREFIX + "href"
27+
ARCHIVE_FORMAT_PROP = PREFIX + "format"
28+
ARCHIVE_TYPE_PROP = PREFIX + "type"
29+
ARCHIVE_START_PROP = PREFIX + "start"
30+
ARCHIVE_END_PROP = PREFIX + "end"
31+
32+
33+
class ArchiveExtension(
34+
Generic[T],
35+
PropertiesExtension,
36+
ExtensionManagementMixin[Union[pystac.Collection, pystac.Item]],
37+
):
38+
"""An abstract class that can be used to extend the properties of a
39+
:class:`~pystac.Collection`, :class:`~pystac.Item`, or :class:`~pystac.Asset` with
40+
properties from the :stac-ext:`Archive Extension <archive>`. This class is
41+
generic over the type of STAC Object to be extended (e.g. :class:`~pystac.Item`,
42+
:class:`~pystac.Asset`).
43+
44+
To create a concrete instance of :class:`ArchiveExtension`, use the
45+
:meth:`ArchiveExtension.ext` method. For example:
46+
47+
.. code-block:: python
48+
49+
>>> item: pystac.Item = ...
50+
>>> arch_ext = ArchiveExtension.ext(item)
51+
"""
52+
53+
name: Literal["archive"] = "archive"
54+
55+
def apply(
56+
self,
57+
href: str | None = None,
58+
format: str | None = None,
59+
type: str | None = None,
60+
start: int | None = None,
61+
end: int | None = None,
62+
) -> None:
63+
"""Applies Archive Extension properties to the extended
64+
:class:`~pystac.Collection`, :class:`~pystac.Item` or :class:`~pystac.Asset`.
65+
66+
Args:
67+
href (str) : The location of the file within the archive specified by the href field.
68+
format (str): The mimetype of the archive format.
69+
type (str): The mimetype of the file within the archive specified by the href field.
70+
start (int) : The offset of the first byte of the file within the archive.
71+
end (int) : The offset of the last byte of the file within the archive.
72+
"""
73+
self.href = href
74+
self.format = format
75+
self.type = type
76+
self.start = start
77+
self.end = end
78+
79+
@property
80+
def href(self) -> str | None:
81+
"""Get or sets the href,the location of the file within the archive.
82+
"""
83+
return self._get_property(ARCHIVE_HREF_PROP, str)
84+
85+
@href.setter
86+
def href(self, v: str | None) -> None:
87+
self._set_property(ARCHIVE_HREF_PROP, v)
88+
89+
@property
90+
def format(self) -> str | None:
91+
"""Get or sets the format,the mimetype of the archive.
92+
"""
93+
return self._get_property(ARCHIVE_FORMAT_PROP, str)
94+
95+
@format.setter
96+
def format(self, v: str | None) -> None:
97+
self._set_property(ARCHIVE_FORMAT_PROP, v)
98+
99+
@property
100+
def type(self) -> str | None:
101+
"""Get or sets the type,the mimetype of the file within the archive
102+
specified by the href field.
103+
"""
104+
return self._get_property(ARCHIVE_TYPE_PROP, str)
105+
106+
@type.setter
107+
def type(self, v: str | None) -> None:
108+
self._set_property(ARCHIVE_TYPE_PROP, v)
109+
110+
@property
111+
def start(self) -> int | None:
112+
"""Get or sets the start,the offset of the first byte of the file
113+
within the archive.
114+
"""
115+
return self._get_property(ARCHIVE_START_PROP, int)
116+
117+
@start.setter
118+
def start(self, v: int | None) -> None:
119+
self._set_property(ARCHIVE_START_PROP, v)
120+
121+
@property
122+
def end(self) -> int | None:
123+
"""Get or sets the end,the offset of the last byte of the file
124+
within the archive.
125+
"""
126+
return self._get_property(ARCHIVE_END_PROP, int)
127+
128+
@end.setter
129+
def start(self, v: int | None) -> None:
130+
self._set_property(ARCHIVE_END_PROP, v)
131+
132+
@classmethod
133+
def get_schema_uri(cls) -> str:
134+
return SCHEMA_URI
135+
136+
@classmethod
137+
def ext(cls, obj: T, add_if_missing: bool = False) -> ArchiveExtension[T]:
138+
"""Extends the given STAC Object with properties from the :stac-ext:`Archive
139+
Extension <archive>`.
140+
141+
This extension can be applied to instances of :class:`~pystac.Asset`.
142+
143+
Raises:
144+
145+
pystac.ExtensionTypeError : If an invalid object type is passed.
146+
"""
147+
if isinstance(obj, pystac.Item):
148+
cls.ensure_has_extension(obj, add_if_missing)
149+
return cast(ArchiveExtension[T], ItemArchiveExtension(obj))
150+
if isinstance(obj, pystac.Asset):
151+
cls.ensure_owner_has_extension(obj, add_if_missing)
152+
return cast(ArchiveExtension[T], AssetArchiveExtension(obj))
153+
elif isinstance(obj, item_assets.AssetDefinition):
154+
cls.ensure_owner_has_extension(obj, add_if_missing)
155+
return cast(ArchiveExtension[T], ItemAssetsArchiveExtension(obj))
156+
else:
157+
raise pystac.ExtensionTypeError(cls._ext_error_message(obj))
158+
159+
@classmethod
160+
def summaries(
161+
cls, obj: pystac.Collection, add_if_missing: bool = False
162+
) -> SummariesStorageExtension:
163+
"""Returns the extended summaries object for the given collection."""
164+
cls.ensure_has_extension(obj, add_if_missing)
165+
return SummariesStorageExtension(obj)
166+
167+
168+
#class ItemArchiveExtension(ArchiveExtension[pystac.Item]):
169+
# """A concrete implementation of :class:`ArchiveExtension` on an
170+
# :class:`~pystac.Item` that extends the properties of the Item to include properties
171+
# defined in the :stac-ext:`Archive Extension <archive>`.
172+
#
173+
# This class should generally not be instantiated directly. Instead, call
174+
# :meth:`ArchiveExtension.ext` on an :class:`~pystac.Item` to extend it.
175+
# """
176+
#
177+
# item: pystac.Item
178+
# properties: dict[str, Any]
179+
#
180+
# def __init__(self, item: pystac.Item):
181+
# self.item = item
182+
# self.properties = item.properties
183+
#
184+
# def __repr__(self) -> str:
185+
# return f"<ItemArchiveExtension Item id={self.item.id}>"
186+
187+
188+
class AssetArchiveExtension(ArchiveExtension[pystac.Asset]):
189+
"""A concrete implementation of :class:`ArchiveExtension` on an
190+
:class:`~pystac.Asset` that extends the Asset fields to include properties defined
191+
in the :stac-ext:`Archive Extension <archive>`.
192+
193+
This class should generally not be instantiated directly. Instead, call
194+
:meth:`ArchiveExtension.ext` on an :class:`~pystac.Asset` to extend it.
195+
"""
196+
197+
asset_href: str
198+
properties: dict[str, Any]
199+
additional_read_properties: list[dict[str, Any]] | None
200+
201+
def __init__(self, asset: pystac.Asset):
202+
self.asset_href = asset.href
203+
self.properties = asset.extra_fields
204+
if asset.owner and isinstance(asset.owner, pystac.Item):
205+
self.additional_read_properties = [asset.owner.properties]
206+
else:
207+
self.additional_read_properties = None
208+
209+
def __repr__(self) -> str:
210+
return f"<AssetArchiveExtension Item id={self.asset_href}>"
211+
212+
213+
class ItemAssetsArchiveExtension(ArchiveExtension[item_assets.AssetDefinition]):
214+
properties: dict[str, Any]
215+
asset_defn: item_assets.AssetDefinition
216+
217+
def __init__(self, item_asset: item_assets.AssetDefinition):
218+
self.asset_defn = item_asset
219+
self.properties = item_asset.properties
220+
221+
222+
class SummariesArchiveExtension(SummariesExtension):
223+
"""A concrete implementation of :class:`~SummariesExtension` that extends
224+
the ``summaries`` field of a :class:`~pystac.Collection` to include properties
225+
defined in the :stac-ext:`Archive Extension <storage>`.
226+
"""
227+
228+
@property
229+
def href(self) -> list[str] | None:
230+
"""Get or sets the summary of :attr:`ArchiveExtension.href` values
231+
for this Collection.
232+
"""
233+
return self.summaries.get_list(ARCHIVE_HREF_PROP)
234+
235+
@href.setter
236+
def href(self, v: list[str] | None) -> None:
237+
self._set_summary(ARCHIVE_HREF_PROP, v)
238+
239+
@property
240+
def format(self) -> list[str] | None:
241+
"""Get or sets the summary of :attr:`ArchiveExtension.format` values
242+
for this Collection.
243+
"""
244+
return self.summaries.get_list(ARCHIVE_FORMAT_PROP)
245+
246+
@format.setter
247+
def format(self, v: list[str] | None) -> None:
248+
self._set_summary(ARCHIVE_FORMAT_PROP, v)
249+
250+
@property
251+
def type(self) -> list[str] | None:
252+
"""Get or sets the summary of :attr:`ArchiveExtension.type` values
253+
for this Collection.
254+
"""
255+
return self.summaries.get_list(ARCHIVE_TYPE_PROP)
256+
257+
@type.setter
258+
def type(self, v: list[str] | None) -> None:
259+
self._set_summary(ARCHIVE_TYPE_PROP, v)
260+
261+
@property
262+
def start(self) -> list[int] | None:
263+
"""Get or sets the summary of :attr:`ArchiveExtension.start` values
264+
for this Collection.
265+
"""
266+
return self.summaries.get_list(ARCHIVE_START_PROP)
267+
268+
@start.setter
269+
def start(self, v: list[int] | None) -> None:
270+
self._set_summary(ARCHIVE_START_PROP, v)
271+
272+
@property
273+
def end(self) -> list[int] | None:
274+
"""Get or sets the summary of :attr:`ArchiveExtension.end` values
275+
for this Collection.
276+
"""
277+
return self.summaries.get_list(ARCHIVE_END_PROP)
278+
279+
@end.setter
280+
def end(self, v: list[int] | None) -> None:
281+
self._set_summary(ARCHIVE_END_PROP, v)
282+
283+
284+
class ArchiveExtensionHooks(ExtensionHooks):
285+
schema_uri: str = SCHEMA_URI
286+
287+
#For time being empty set
288+
prev_extension_ids: set[str] = set()
289+
stac_object_types = {
290+
pystac.STACObjectType.COLLECTION,
291+
pystac.STACObjectType.ITEM,
292+
}
293+
294+
295+
ARCHIVE_EXTENSION_HOOKS: ExtensionHooks = ArchiveExtensionHooks()

pystac/extensions/ext.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from pystac.extensions.version import BaseVersionExtension, VersionExtension
2222
from pystac.extensions.view import ViewExtension
2323
from pystac.extensions.xarray_assets import XarrayAssetsExtension
24+
from pystac.extensions.archive import ArchiveExtension
2425

2526
T = TypeVar("T", Asset, AssetDefinition, Link)
2627
U = TypeVar("U", Asset, AssetDefinition)
@@ -45,6 +46,7 @@
4546
"version",
4647
"view",
4748
"xarray",
49+
"archive",
4850
]
4951

5052
EXTENSION_NAME_MAPPING: dict[EXTENSION_NAMES, Any] = {
@@ -67,6 +69,7 @@
6769
VersionExtension.name: VersionExtension,
6870
ViewExtension.name: ViewExtension,
6971
XarrayAssetsExtension.name: XarrayAssetsExtension,
72+
ArchiveExtension.name: ArchiveExtension,
7073
}
7174

7275

@@ -122,6 +125,10 @@ def table(self) -> TableExtension[Collection]:
122125
def xarray(self) -> XarrayAssetsExtension[Collection]:
123126
return XarrayAssetsExtension.ext(self.stac_object)
124127

128+
@property
129+
def archive(self) -> ArchiveExtension[Collection]:
130+
return ArchiveExtension.ext(self.stac_object)
131+
125132

126133
@dataclass
127134
class ItemExt:
@@ -196,6 +203,10 @@ def view(self) -> ViewExtension[Item]:
196203
def xarray(self) -> XarrayAssetsExtension[Item]:
197204
return XarrayAssetsExtension.ext(self.stac_object)
198205

206+
@property
207+
def archive(self) -> ArchiveExtension[Item]:
208+
return ArchiveExtension.ext(self.stac_object)
209+
199210

200211
class _AssetsExt(Generic[T]):
201212
stac_object: T
@@ -281,6 +292,10 @@ def version(self) -> BaseVersionExtension[U]:
281292
def view(self) -> ViewExtension[U]:
282293
return ViewExtension.ext(self.stac_object)
283294

295+
@property
296+
def archive(self) -> ArchiveExtension[U]:
297+
return ArchiveExtension.ext(self.stac_object)
298+
284299

285300
@dataclass
286301
class AssetExt(_AssetExt[Asset]):
@@ -298,6 +313,10 @@ def timestamps(self) -> TimestampsExtension[Asset]:
298313
def xarray(self) -> XarrayAssetsExtension[Asset]:
299314
return XarrayAssetsExtension.ext(self.stac_object)
300315

316+
@property
317+
def archive(self) -> ArchiveExtension[Asset]:
318+
return ArchiveExtension.ext(self.stac_object)
319+
301320

302321
@dataclass
303322
class ItemAssetExt(_AssetExt[AssetDefinition]):

pystac/serialization/identify.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class OldExtensionShortIDs(Enum):
3131
VERSION = "version"
3232
VIEW = "view"
3333
FILE = "file"
34+
ARCHIVE = "archive"
3435

3536

3637
@total_ordering

0 commit comments

Comments
 (0)