Skip to content

Commit 17c52ed

Browse files
author
Jon Duckworth
authored
Merge pull request #554 from duckontheweb/fix/gh-521-add-if-missing-asset-error
Raise exception when using add_if_missing on ownerless Asset
2 parents b8853d3 + 30a272d commit 17c52ed

File tree

17 files changed

+104
-126
lines changed

17 files changed

+104
-126
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
### Changed
2424

2525
- 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))
26+
- Calling `ExtensionManagementMixin.validate_has_extension` with `add_if_missing = True`
27+
on an ownerless `Asset` will raise a `STACError` ([#554](https://github.com/stac-utils/pystac/pull/554))
2628
- `PointcloudSchema` -> `Schema`, `PointcloudStatistic` -> `Statistic` for consistency
2729
with naming convention in other extensions ([#548](https://github.com/stac-utils/pystac/pull/548))
2830

pystac/extensions/base.py

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
from abc import ABC, abstractmethod
2-
from typing import Generic, Iterable, List, Optional, Dict, Any, Type, TypeVar, Union
2+
from typing import (
3+
cast,
4+
Generic,
5+
Iterable,
6+
List,
7+
Optional,
8+
Dict,
9+
Any,
10+
Type,
11+
TypeVar,
12+
Union,
13+
)
314

415
import pystac
516

@@ -126,15 +137,41 @@ def has_extension(cls, obj: S) -> bool:
126137
)
127138

128139
@classmethod
129-
def validate_has_extension(cls, obj: Union[S, pystac.Asset]) -> None:
130-
"""Given a :class:`~pystac.STACObject` or :class:`pystac.Asset` instance, checks
131-
if the object (or its owner in the case of an Asset) has this extension's schema
132-
URI in it's :attr:`~pystac.STACObject.stac_extensions` list."""
133-
extensible = obj.owner if isinstance(obj, pystac.Asset) else obj
134-
if (
135-
extensible is not None
136-
and cls.get_schema_uri() not in extensible.stac_extensions
137-
):
140+
def validate_owner_has_extension(
141+
cls, asset: pystac.Asset, add_if_missing: bool
142+
) -> None:
143+
"""Given an :class:`~pystac.Asset`, checks if the asset's owner has this
144+
extension's schema URI in its :attr:`~pystac.STACObject.stac_extensions` list.
145+
If ``add_if_missing`` is ``True``, the schema URI will be added to the owner.
146+
147+
Raises:
148+
STACError : If ``add_if_missing`` is ``True`` and ``asset.owner`` is
149+
``None``.
150+
"""
151+
if asset.owner is None:
152+
if add_if_missing:
153+
raise pystac.STACError(
154+
"Can only add schema URIs to Assets with an owner."
155+
)
156+
else:
157+
return
158+
return cls.validate_has_extension(cast(S, asset.owner), add_if_missing)
159+
160+
@classmethod
161+
def validate_has_extension(cls, obj: S, add_if_missing: bool) -> None:
162+
"""Given a :class:`~pystac.STACObject`, checks if the object has this
163+
extension's schema URI in its :attr:`~pystac.STACObject.stac_extensions` list.
164+
If ``add_if_missing`` is ``True``, the schema URI will be added to the object.
165+
166+
Args:
167+
obj : The object to validate.
168+
add_if_missing : Whether to add the schema URI to the object if it is
169+
not already present.
170+
"""
171+
if add_if_missing:
172+
cls.add_to(obj)
173+
174+
if cls.get_schema_uri() not in obj.stac_extensions:
138175
raise pystac.ExtensionNotImplemented(
139176
f"Could not find extension schema URI {cls.get_schema_uri()} in object."
140177
)

pystac/extensions/datacube.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -340,19 +340,13 @@ def get_schema_uri(cls) -> str:
340340
@classmethod
341341
def ext(cls, obj: T, add_if_missing: bool = False) -> "DatacubeExtension[T]":
342342
if isinstance(obj, pystac.Collection):
343-
if add_if_missing:
344-
cls.add_to(obj)
345-
cls.validate_has_extension(obj)
343+
cls.validate_has_extension(obj, add_if_missing)
346344
return cast(DatacubeExtension[T], CollectionDatacubeExtension(obj))
347345
if isinstance(obj, pystac.Item):
348-
if add_if_missing:
349-
cls.add_to(obj)
350-
cls.validate_has_extension(obj)
346+
cls.validate_has_extension(obj, add_if_missing)
351347
return cast(DatacubeExtension[T], ItemDatacubeExtension(obj))
352348
elif isinstance(obj, pystac.Asset):
353-
if add_if_missing and obj.owner is not None:
354-
cls.add_to(obj.owner)
355-
cls.validate_has_extension(obj)
349+
cls.validate_owner_has_extension(obj, add_if_missing)
356350
return cast(DatacubeExtension[T], AssetDatacubeExtension(obj))
357351
else:
358352
raise pystac.ExtensionTypeError(

pystac/extensions/eo.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -361,14 +361,10 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> "EOExtension[T]":
361361
pystac.ExtensionTypeError : If an invalid object type is passed.
362362
"""
363363
if isinstance(obj, pystac.Item):
364-
if add_if_missing:
365-
cls.add_to(obj)
366-
cls.validate_has_extension(obj)
364+
cls.validate_has_extension(obj, add_if_missing)
367365
return cast(EOExtension[T], ItemEOExtension(obj))
368366
elif isinstance(obj, pystac.Asset):
369-
if add_if_missing and isinstance(obj.owner, pystac.Item):
370-
cls.add_to(obj.owner)
371-
cls.validate_has_extension(obj)
367+
cls.validate_owner_has_extension(obj, add_if_missing)
372368
return cast(EOExtension[T], AssetEOExtension(obj))
373369
else:
374370
raise pystac.ExtensionTypeError(
@@ -380,10 +376,7 @@ def summaries(
380376
cls, obj: pystac.Collection, add_if_missing: bool = False
381377
) -> "SummariesEOExtension":
382378
"""Returns the extended summaries object for the given collection."""
383-
if not add_if_missing:
384-
cls.validate_has_extension(obj)
385-
else:
386-
cls.add_to(obj)
379+
cls.validate_has_extension(obj, add_if_missing)
387380
return SummariesEOExtension(obj)
388381

389382

pystac/extensions/file.py

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

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

99
import pystac
1010
from pystac.extensions.base import ExtensionManagementMixin, PropertiesExtension
@@ -85,7 +85,9 @@ def summary(self, v: str) -> None:
8585
self.properties["summary"] = v
8686

8787

88-
class FileExtension(PropertiesExtension, ExtensionManagementMixin[pystac.Item]):
88+
class FileExtension(
89+
PropertiesExtension, ExtensionManagementMixin[Union[pystac.Item, pystac.Collection]]
90+
):
8991
"""A class that can be used to extend the properties of an :class:`~pystac.Asset`
9092
with properties from the :stac-ext:`File Info Extension <file>`.
9193
@@ -197,9 +199,7 @@ def ext(cls, obj: pystac.Asset, add_if_missing: bool = False) -> "FileExtension"
197199
This extension can be applied to instances of :class:`~pystac.Asset`.
198200
"""
199201
if isinstance(obj, pystac.Asset):
200-
if add_if_missing and isinstance(obj.owner, pystac.Item):
201-
cls.add_to(obj.owner)
202-
cls.validate_has_extension(obj)
202+
cls.validate_owner_has_extension(obj, add_if_missing)
203203
return cls(obj)
204204
else:
205205
raise pystac.ExtensionTypeError(

pystac/extensions/item_assets.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,7 @@ def ext(
119119
cls, obj: pystac.Collection, add_if_missing: bool = False
120120
) -> "ItemAssetsExtension":
121121
if isinstance(obj, pystac.Collection):
122-
if add_if_missing:
123-
cls.add_to(obj)
122+
cls.validate_has_extension(obj, add_if_missing)
124123
return cls(obj)
125124
else:
126125
raise pystac.ExtensionTypeError(

pystac/extensions/label.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -691,9 +691,7 @@ def ext(cls, obj: pystac.Item, add_if_missing: bool = False) -> "LabelExtension"
691691
This extension can be applied to instances of :class:`~pystac.Item`.
692692
"""
693693
if isinstance(obj, pystac.Item):
694-
if add_if_missing:
695-
cls.add_to(obj)
696-
cls.validate_has_extension(obj)
694+
cls.validate_has_extension(obj, add_if_missing)
697695
return cls(obj)
698696
else:
699697
raise pystac.ExtensionTypeError(
@@ -705,10 +703,7 @@ def summaries(
705703
cls, obj: pystac.Collection, add_if_missing: bool = False
706704
) -> "SummariesLabelExtension":
707705
"""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)
706+
cls.validate_has_extension(obj, add_if_missing)
712707
return SummariesLabelExtension(obj)
713708

714709

pystac/extensions/pointcloud.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -468,14 +468,14 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> "PointcloudExtension[T]":
468468
pystac.ExtensionTypeError : If an invalid object type is passed.
469469
"""
470470
if isinstance(obj, pystac.Item):
471-
if add_if_missing:
472-
cls.add_to(obj)
473-
cls.validate_has_extension(obj)
471+
cls.validate_has_extension(obj, add_if_missing)
474472
return cast(PointcloudExtension[T], ItemPointcloudExtension(obj))
475473
elif isinstance(obj, pystac.Asset):
476-
if add_if_missing and isinstance(obj.owner, pystac.Item):
477-
cls.add_to(obj.owner)
478-
cls.validate_has_extension(obj)
474+
if obj.owner is not None and not isinstance(obj.owner, pystac.Item):
475+
raise pystac.ExtensionTypeError(
476+
"Pointcloud extension does not apply to Collection Assets."
477+
)
478+
cls.validate_owner_has_extension(obj, add_if_missing)
479479
return cast(PointcloudExtension[T], AssetPointcloudExtension(obj))
480480
else:
481481
raise pystac.ExtensionTypeError(
@@ -486,10 +486,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> "PointcloudExtension[T]":
486486
def summaries(
487487
cls, obj: pystac.Collection, add_if_missing: bool = False
488488
) -> "SummariesPointcloudExtension":
489-
if not add_if_missing:
490-
cls.validate_has_extension(obj)
491-
else:
492-
cls.add_to(obj)
489+
cls.validate_has_extension(obj, add_if_missing)
493490
return SummariesPointcloudExtension(obj)
494491

495492

pystac/extensions/projection.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -272,14 +272,10 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> "ProjectionExtension[T]":
272272
pystac.ExtensionTypeError : If an invalid object type is passed.
273273
"""
274274
if isinstance(obj, pystac.Item):
275-
if add_if_missing:
276-
cls.add_to(obj)
277-
cls.validate_has_extension(obj)
275+
cls.validate_has_extension(obj, add_if_missing)
278276
return cast(ProjectionExtension[T], ItemProjectionExtension(obj))
279277
elif isinstance(obj, pystac.Asset):
280-
if add_if_missing and isinstance(obj.owner, pystac.Item):
281-
cls.add_to(obj.owner)
282-
cls.validate_has_extension(obj)
278+
cls.validate_owner_has_extension(obj, add_if_missing)
283279
return cast(ProjectionExtension[T], AssetProjectionExtension(obj))
284280
else:
285281
raise pystac.ExtensionTypeError(
@@ -291,10 +287,7 @@ def summaries(
291287
cls, obj: pystac.Collection, add_if_missing: bool = False
292288
) -> "SummariesProjectionExtension":
293289
"""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)
290+
cls.validate_has_extension(obj, add_if_missing)
298291
return SummariesProjectionExtension(obj)
299292

300293

pystac/extensions/raster.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -704,9 +704,7 @@ def ext(cls, obj: pystac.Asset, add_if_missing: bool = False) -> "RasterExtensio
704704
pystac.ExtensionTypeError : If an invalid object type is passed.
705705
"""
706706
if isinstance(obj, pystac.Asset):
707-
if add_if_missing and isinstance(obj.owner, pystac.Item):
708-
cls.add_to(obj.owner)
709-
cls.validate_has_extension(obj)
707+
cls.validate_owner_has_extension(obj, add_if_missing)
710708
return cls(obj)
711709
else:
712710
raise pystac.ExtensionTypeError(
@@ -717,10 +715,7 @@ def ext(cls, obj: pystac.Asset, add_if_missing: bool = False) -> "RasterExtensio
717715
def summaries(
718716
cls, obj: pystac.Collection, add_if_missing: bool = False
719717
) -> "SummariesRasterExtension":
720-
if not add_if_missing:
721-
cls.validate_has_extension(obj)
722-
else:
723-
cls.add_to(obj)
718+
cls.validate_has_extension(obj, add_if_missing)
724719
return SummariesRasterExtension(obj)
725720

726721

0 commit comments

Comments
 (0)