Skip to content

Commit f443f6a

Browse files
authored
Add collection to ExtensionManagementMixin type params where appropriate (#547)
Fix typing of extensions summaries and validate extension URI presence
1 parent cee997d commit f443f6a

17 files changed

+261
-52
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414

1515
- The `from_dict` method on STACObjects will set the object's root link when a `root` parameter is present. An ItemCollection `from_dict` with a root parameter will set the root on each of it's Items. ([#549](https://github.com/stac-utils/pystac/pull/549))
1616

17+
### Fixed
18+
19+
- Added `Collections` as a type that can be extended for extensions whose fields can appear in collection summaries ([#547](https://github.com/stac-utils/pystac/pull/547))
20+
1721
### Deprecated
1822

1923
## [v1.0.0-rc.3]

pystac/extensions/eo.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
Optional,
1313
Tuple,
1414
TypeVar,
15+
Union,
1516
cast,
1617
)
1718

@@ -274,7 +275,9 @@ def band_description(common_name: str) -> Optional[str]:
274275

275276

276277
class EOExtension(
277-
Generic[T], PropertiesExtension, ExtensionManagementMixin[pystac.Item]
278+
Generic[T],
279+
PropertiesExtension,
280+
ExtensionManagementMixin[Union[pystac.Item, pystac.Collection]],
278281
):
279282
"""An abstract class that can be used to extend the properties of an
280283
:class:`~pystac.Item` or :class:`~pystac.Asset` with properties from the
@@ -296,7 +299,7 @@ def apply(
296299
self, bands: Optional[List[Band]] = None, cloud_cover: Optional[float] = None
297300
) -> None:
298301
"""Applies label extension properties to the extended :class:`~pystac.Item` or
299-
:class:`~pystac.Collection`.
302+
:class:`~pystac.Asset`.
300303
301304
Args:
302305
bands : A list of available bands where each item is a :class:`~Band`
@@ -373,9 +376,15 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> "EOExtension[T]":
373376
f"EO extension does not apply to type '{type(obj).__name__}'"
374377
)
375378

376-
@staticmethod
377-
def summaries(obj: pystac.Collection) -> "SummariesEOExtension":
379+
@classmethod
380+
def summaries(
381+
cls, obj: pystac.Collection, add_if_missing: bool = False
382+
) -> "SummariesEOExtension":
378383
"""Returns the extended summaries object for the given collection."""
384+
if not add_if_missing:
385+
cls.validate_has_extension(obj)
386+
else:
387+
cls.add_to(obj)
379388
return SummariesEOExtension(obj)
380389

381390

pystac/extensions/label.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ def __eq__(self, o: object) -> bool:
425425
return self.to_dict() == o
426426

427427

428-
class LabelExtension(ExtensionManagementMixin[pystac.Item]):
428+
class LabelExtension(ExtensionManagementMixin[Union[pystac.Item, pystac.Collection]]):
429429
"""A class that can be used to extend the properties of an
430430
:class:`~pystac.Item` with properties from the :stac-ext:`Label Extension <label>`.
431431
@@ -700,9 +700,15 @@ def ext(cls, obj: pystac.Item, add_if_missing: bool = False) -> "LabelExtension"
700700
f"Label extension does not apply to type '{type(obj).__name__}'"
701701
)
702702

703-
@staticmethod
704-
def summaries(obj: pystac.Collection) -> "SummariesLabelExtension":
703+
@classmethod
704+
def summaries(
705+
cls, obj: pystac.Collection, add_if_missing: bool = False
706+
) -> "SummariesLabelExtension":
705707
"""Returns the extended summaries object for the given collection."""
708+
if not add_if_missing:
709+
cls.validate_has_extension(obj)
710+
else:
711+
cls.add_to(obj)
706712
return SummariesLabelExtension(obj)
707713

708714

pystac/extensions/projection.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"""
55

66
import json
7-
from typing import Any, Dict, Generic, Iterable, List, Optional, TypeVar, cast
7+
from typing import Any, Dict, Generic, Iterable, List, Optional, TypeVar, Union, cast
88

99
import pystac
1010
from pystac.extensions.hooks import ExtensionHooks
@@ -31,7 +31,9 @@
3131

3232

3333
class ProjectionExtension(
34-
Generic[T], PropertiesExtension, ExtensionManagementMixin[pystac.Item]
34+
Generic[T],
35+
PropertiesExtension,
36+
ExtensionManagementMixin[Union[pystac.Item, pystac.Collection]],
3537
):
3638
"""An abstract class that can be used to extend the properties of an
3739
:class:`~pystac.Item` with properties from the :stac-ext:`Projection
@@ -284,9 +286,15 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> "ProjectionExtension[T]":
284286
f"Projection extension does not apply to type '{type(obj).__name__}'"
285287
)
286288

287-
@staticmethod
288-
def summaries(obj: pystac.Collection) -> "SummariesProjectionExtension":
289+
@classmethod
290+
def summaries(
291+
cls, obj: pystac.Collection, add_if_missing: bool = False
292+
) -> "SummariesProjectionExtension":
289293
"""Returns the extended summaries object for the given collection."""
294+
if not add_if_missing:
295+
cls.validate_has_extension(obj)
296+
else:
297+
cls.add_to(obj)
290298
return SummariesProjectionExtension(obj)
291299

292300

pystac/extensions/raster.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"""
55

66
import enum
7-
from typing import Any, Dict, Iterable, List, Optional
7+
from typing import Any, Dict, Iterable, List, Optional, Union
88

99
import pystac
1010
from pystac.extensions.base import (
@@ -625,7 +625,9 @@ def to_dict(self) -> Dict[str, Any]:
625625
return self.properties
626626

627627

628-
class RasterExtension(PropertiesExtension, ExtensionManagementMixin[pystac.Item]):
628+
class RasterExtension(
629+
PropertiesExtension, ExtensionManagementMixin[Union[pystac.Item, pystac.Collection]]
630+
):
629631
"""An abstract class that can be used to extend the properties of an
630632
:class:`~pystac.Item` or :class:`~pystac.Asset` with properties from
631633
the :stac-ext:`Raster Extension <raster>`. This class is generic over
@@ -711,8 +713,14 @@ def ext(cls, obj: pystac.Asset, add_if_missing: bool = False) -> "RasterExtensio
711713
f"Raster extension does not apply to type '{type(obj).__name__}'"
712714
)
713715

714-
@staticmethod
715-
def summaries(obj: pystac.Collection) -> "SummariesRasterExtension":
716+
@classmethod
717+
def summaries(
718+
cls, obj: pystac.Collection, add_if_missing: bool = False
719+
) -> "SummariesRasterExtension":
720+
if not add_if_missing:
721+
cls.validate_has_extension(obj)
722+
else:
723+
cls.add_to(obj)
716724
return SummariesRasterExtension(obj)
717725

718726

pystac/extensions/sat.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import enum
77
from datetime import datetime as Datetime
88
from pystac.summaries import RangeSummary
9-
from typing import Dict, Any, List, Iterable, Generic, Optional, TypeVar, cast
9+
from typing import Dict, Any, List, Iterable, Generic, Optional, TypeVar, Union, cast
1010

1111
import pystac
1212
from pystac.extensions.base import (
@@ -38,7 +38,9 @@ class OrbitState(str, enum.Enum):
3838

3939

4040
class SatExtension(
41-
Generic[T], PropertiesExtension, ExtensionManagementMixin[pystac.Item]
41+
Generic[T],
42+
PropertiesExtension,
43+
ExtensionManagementMixin[Union[pystac.Item, pystac.Collection]],
4244
):
4345
"""An abstract class that can be used to extend the properties of an
4446
:class:`~pystac.Item` or :class:`~pystac.Asset` with properties from the
@@ -161,9 +163,15 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> "SatExtension[T]":
161163
f"Satellite extension does not apply to type '{type(obj).__name__}'"
162164
)
163165

164-
@staticmethod
165-
def summaries(obj: pystac.Collection) -> "SummariesSatExtension":
166+
@classmethod
167+
def summaries(
168+
cls, obj: pystac.Collection, add_if_missing: bool = False
169+
) -> "SummariesSatExtension":
166170
"""Returns the extended summaries object for the given collection."""
171+
if not add_if_missing:
172+
cls.validate_has_extension(obj)
173+
else:
174+
cls.add_to(obj)
167175
return SummariesSatExtension(obj)
168176

169177

pystac/extensions/scientific.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,15 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> "ScientificExtension[T]":
248248
f"Scientific extension does not apply to type '{type(obj).__name__}'"
249249
)
250250

251-
@staticmethod
252-
def summaries(obj: pystac.Collection) -> "SummariesScientificExtension":
251+
@classmethod
252+
def summaries(
253+
cls, obj: pystac.Collection, add_if_missing: bool = False
254+
) -> "SummariesScientificExtension":
253255
"""Returns the extended summaries object for the given collection."""
256+
if not add_if_missing:
257+
cls.validate_has_extension(obj)
258+
else:
259+
cls.add_to(obj)
254260
return SummariesScientificExtension(obj)
255261

256262

pystac/extensions/timestamps.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from datetime import datetime as datetime
77
from pystac.summaries import RangeSummary
8-
from typing import Dict, Any, Iterable, Generic, Optional, TypeVar, cast
8+
from typing import Dict, Any, Iterable, Generic, Optional, TypeVar, Union, cast
99

1010
import pystac
1111
from pystac.extensions.base import (
@@ -26,7 +26,9 @@
2626

2727

2828
class TimestampsExtension(
29-
Generic[T], PropertiesExtension, ExtensionManagementMixin[pystac.Item]
29+
Generic[T],
30+
PropertiesExtension,
31+
ExtensionManagementMixin[Union[pystac.Item, pystac.Collection]],
3032
):
3133
"""An abstract class that can be used to extend the properties of an
3234
:class:`~pystac.Item` or :class:`~pystac.Asset` with properties from the
@@ -141,9 +143,15 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> "TimestampsExtension[T]":
141143
f"Timestamps extension does not apply to type '{type(obj).__name__}'"
142144
)
143145

144-
@staticmethod
145-
def summaries(obj: pystac.Collection) -> "SummariesTimestampsExtension":
146+
@classmethod
147+
def summaries(
148+
cls, obj: pystac.Collection, add_if_missing: bool = False
149+
) -> "SummariesTimestampsExtension":
146150
"""Returns the extended summaries object for the given collection."""
151+
if not add_if_missing:
152+
cls.validate_has_extension(obj)
153+
else:
154+
cls.add_to(obj)
147155
return SummariesTimestampsExtension(obj)
148156

149157

pystac/extensions/view.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
https://github.com/stac-extensions/view
44
"""
55

6-
from typing import Any, Dict, Generic, Iterable, Optional, TypeVar, cast
6+
from typing import Any, Dict, Generic, Iterable, Optional, TypeVar, Union, cast
77

88
import pystac
99
from pystac.summaries import RangeSummary
@@ -27,7 +27,9 @@
2727

2828

2929
class ViewExtension(
30-
Generic[T], PropertiesExtension, ExtensionManagementMixin[pystac.Item]
30+
Generic[T],
31+
PropertiesExtension,
32+
ExtensionManagementMixin[Union[pystac.Item, pystac.Collection]],
3133
):
3234
"""An abstract class that can be used to extend the properties of an
3335
:class:`~pystac.Item` with properties from the :stac-ext:`View Geometry
@@ -170,9 +172,15 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> "ViewExtension[T]":
170172
f"View extension does not apply to type '{type(obj).__name__}'"
171173
)
172174

173-
@staticmethod
174-
def summaries(obj: pystac.Collection) -> "SummariesViewExtension":
175+
@classmethod
176+
def summaries(
177+
cls, obj: pystac.Collection, add_if_missing: bool = False
178+
) -> "SummariesViewExtension":
175179
"""Returns the extended summaries object for the given collection."""
180+
if not add_if_missing:
181+
cls.validate_has_extension(obj)
182+
else:
183+
cls.add_to(obj)
176184
return SummariesViewExtension(obj)
177185

178186

tests/extensions/test_eo.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,23 @@ def test_summaries(self) -> None:
214214
self.assertEqual(len(col_dict["summaries"]["eo:bands"]), 1)
215215
self.assertEqual(col_dict["summaries"]["eo:cloud_cover"]["minimum"], 1.0)
216216

217+
def test_summaries_adds_uri(self) -> None:
218+
col = pystac.Collection.from_file(self.EO_COLLECTION_URI)
219+
col.stac_extensions = []
220+
self.assertRaisesRegex(
221+
pystac.ExtensionNotImplemented,
222+
r"Could not find extension schema URI.*",
223+
EOExtension.summaries,
224+
col,
225+
False,
226+
)
227+
_ = EOExtension.summaries(col, add_if_missing=True)
228+
229+
self.assertIn(EOExtension.get_schema_uri(), col.stac_extensions)
230+
231+
EOExtension.remove_from(col)
232+
self.assertNotIn(EOExtension.get_schema_uri(), col.stac_extensions)
233+
217234
def test_read_pre_09_fields_into_common_metadata(self) -> None:
218235
eo_item = pystac.Item.from_file(
219236
TestCases.get_path(

0 commit comments

Comments
 (0)