Skip to content

Commit ac7b1d6

Browse files
author
Jon Duckworth
authored
Merge pull request #561 from duckontheweb/change/gh-315-required-property-exception
Use get_required to raise RequiredPropertyMissing when appropriate
2 parents 0ba1a1d + c12f907 commit ac7b1d6

File tree

8 files changed

+43
-69
lines changed

8 files changed

+43
-69
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@
2626
- Calling `ExtensionManagementMixin.validate_has_extension` with `add_if_missing = True`
2727
on an ownerless `Asset` will raise a `STACError` ([#554](https://github.com/stac-utils/pystac/pull/554))
2828
- `PointcloudSchema` -> `Schema`, `PointcloudStatistic` -> `Statistic` for consistency
29-
with naming convention in other extensions ([#548](https://github.com/stac-utils/pystac/pull/548))
29+
with naming convention in other extensions
30+
([#548](https://github.com/stac-utils/pystac/pull/548))
31+
- `RequiredPropertyMissing` always raised when trying to get a required property that is
32+
`None` (`STACError` or `KeyError` was previously being raised in some cases)
33+
([#561](https://github.com/stac-utils/pystac/pull/561))
3034

3135
### Fixed
3236

pystac/extensions/datacube.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
PropertiesExtension,
1313
)
1414
from pystac.extensions.hooks import ExtensionHooks
15-
from pystac.utils import get_required, map_opt
15+
from pystac.utils import get_required
1616

1717
T = TypeVar("T", pystac.Collection, pystac.Item, pystac.Asset)
1818

@@ -63,13 +63,13 @@ def to_dict(self) -> Dict[str, Any]:
6363

6464
@staticmethod
6565
def from_dict(d: Dict[str, Any]) -> "Dimension":
66-
dim_type = d.get(DIM_TYPE_PROP)
67-
if dim_type is None:
68-
raise pystac.RequiredPropertyMissing("cube_dimension", DIM_TYPE_PROP)
66+
dim_type: str = get_required(
67+
d.get(DIM_TYPE_PROP), "cube_dimension", DIM_TYPE_PROP
68+
)
6969
if dim_type == "spatial":
70-
axis = d.get(DIM_AXIS_PROP)
71-
if axis is None:
72-
raise pystac.RequiredPropertyMissing("cube_dimension", DIM_AXIS_PROP)
70+
axis: str = get_required(
71+
d.get(DIM_AXIS_PROP), "cube_dimension", DIM_AXIS_PROP
72+
)
7373
if axis == "z":
7474
return VerticalSpatialDimension(d)
7575
else:
@@ -320,14 +320,10 @@ def apply(self, dimensions: Dict[str, Dimension]) -> None:
320320

321321
@property
322322
def dimensions(self) -> Dict[str, Dimension]:
323-
return get_required(
324-
map_opt(
325-
lambda d: {k: Dimension.from_dict(v) for k, v in d.items()},
326-
self._get_property(DIMENSIONS_PROP, Dict[str, Any]),
327-
),
328-
self,
329-
DIMENSIONS_PROP,
323+
result = get_required(
324+
self._get_property(DIMENSIONS_PROP, Dict[str, Any]), self, DIMENSIONS_PROP
330325
)
326+
return {k: Dimension.from_dict(v) for k, v in result.items()}
331327

332328
@dimensions.setter
333329
def dimensions(self, v: Dict[str, Dimension]) -> None:

pystac/extensions/eo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def name(self) -> str:
121121
Returns:
122122
str
123123
"""
124-
return get_required(self.properties["name"], self, "name")
124+
return get_required(self.properties.get("name"), self, "name")
125125

126126
@name.setter
127127
def name(self, v: str) -> None:

pystac/extensions/file.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def create(cls, values: List[Any], summary: str) -> "MappingObject":
6969
def values(self) -> List[Any]:
7070
"""Gets or sets the list of value(s) in the file. At least one array element is
7171
required."""
72-
return get_required(self.properties["values"], self, "values")
72+
return get_required(self.properties.get("values"), self, "values")
7373

7474
@values.setter
7575
def values(self, v: List[Any]) -> None:
@@ -78,7 +78,7 @@ def values(self, v: List[Any]) -> None:
7878
@property
7979
def summary(self) -> str:
8080
"""Gets or sets the short description of the value(s)."""
81-
return get_required(self.properties["summary"], self, "summary")
81+
return get_required(self.properties.get("summary"), self, "summary")
8282

8383
@summary.setter
8484
def summary(self, v: str) -> None:

pystac/extensions/pointcloud.py

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,7 @@ def create(cls, name: str, size: int, type: SchemaType) -> "Schema":
8888
@property
8989
def size(self) -> int:
9090
"""Gets or sets the size value."""
91-
result: Optional[int] = self.properties.get("size")
92-
if result is None:
93-
raise pystac.STACError(
94-
f"Pointcloud schema does not have size property: {self.properties}"
95-
)
96-
return result
91+
return get_required(self.properties.get("size"), self, "size")
9792

9893
@size.setter
9994
def size(self, v: int) -> None:
@@ -105,12 +100,7 @@ def size(self, v: int) -> None:
105100
@property
106101
def name(self) -> str:
107102
"""Gets or sets the name property for this Schema."""
108-
result: Optional[str] = self.properties.get("name")
109-
if result is None:
110-
raise pystac.STACError(
111-
f"Pointcloud schema does not have name property: {self.properties}"
112-
)
113-
return result
103+
return get_required(self.properties.get("name"), self, "name")
114104

115105
@name.setter
116106
def name(self, v: str) -> None:
@@ -128,7 +118,9 @@ def type(self, v: SchemaType) -> None:
128118

129119
def __repr__(self) -> str:
130120
return "<Schema name={} size={} type={}>".format(
131-
self.name, self.size, self.type
121+
self.properties.get("name"),
122+
self.properties.get("size"),
123+
self.properties.get("type"),
132124
)
133125

134126
def to_dict(self) -> Dict[str, Any]:
@@ -217,12 +209,7 @@ def create(
217209
@property
218210
def name(self) -> str:
219211
"""Gets or sets the name property."""
220-
result: Optional[str] = self.properties.get("name")
221-
if result is None:
222-
raise pystac.STACError(
223-
f"Pointcloud statistics does not have name property: {self.properties}"
224-
)
225-
return result
212+
return get_required(self.properties.get("name"), self, "name")
226213

227214
@name.setter
228215
def name(self, v: str) -> None:
@@ -381,10 +368,7 @@ def apply(
381368
@property
382369
def count(self) -> int:
383370
"""Gets or sets the number of points in the Item."""
384-
result = self._get_property(COUNT_PROP, int)
385-
if result is None:
386-
raise pystac.RequiredPropertyMissing(self, COUNT_PROP)
387-
return result
371+
return get_required(self._get_property(COUNT_PROP, int), self, COUNT_PROP)
388372

389373
@count.setter
390374
def count(self, v: int) -> None:
@@ -393,11 +377,7 @@ def count(self, v: int) -> None:
393377
@property
394378
def type(self) -> Union[PhenomenologyType, str]:
395379
"""Gets or sets the phenomenology type for the point cloud."""
396-
return get_required(
397-
self._get_property(TYPE_PROP, str),
398-
self,
399-
TYPE_PROP,
400-
)
380+
return get_required(self._get_property(TYPE_PROP, str), self, TYPE_PROP)
401381

402382
@type.setter
403383
def type(self, v: Union[PhenomenologyType, str]) -> None:
@@ -406,10 +386,7 @@ def type(self, v: Union[PhenomenologyType, str]) -> None:
406386
@property
407387
def encoding(self) -> str:
408388
"""Gets or sets the content encoding or format of the data."""
409-
result = self._get_property(ENCODING_PROP, str)
410-
if result is None:
411-
raise pystac.RequiredPropertyMissing(self, ENCODING_PROP)
412-
return result
389+
return get_required(self._get_property(ENCODING_PROP, str), self, ENCODING_PROP)
413390

414391
@encoding.setter
415392
def encoding(self, v: str) -> None:
@@ -420,9 +397,9 @@ def schemas(self) -> List[Schema]:
420397
"""Gets or sets the list of :class:`Schema` instances defining
421398
dimensions and types for the data.
422399
"""
423-
result = self._get_property(SCHEMAS_PROP, List[Dict[str, Any]])
424-
if result is None:
425-
raise pystac.RequiredPropertyMissing(self, SCHEMAS_PROP)
400+
result = get_required(
401+
self._get_property(SCHEMAS_PROP, List[Dict[str, Any]]), self, SCHEMAS_PROP
402+
)
426403
return [Schema(s) for s in result]
427404

428405
@schemas.setter

pystac/extensions/raster.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ def count(self) -> int:
272272
Returns:
273273
int
274274
"""
275-
return get_required(self.properties["count"], self, "count")
275+
return get_required(self.properties.get("count"), self, "count")
276276

277277
@count.setter
278278
def count(self, v: int) -> None:
@@ -285,7 +285,7 @@ def min(self) -> float:
285285
Returns:
286286
float
287287
"""
288-
return get_required(self.properties["min"], self, "min")
288+
return get_required(self.properties.get("min"), self, "min")
289289

290290
@min.setter
291291
def min(self, v: float) -> None:
@@ -298,7 +298,7 @@ def max(self) -> float:
298298
Returns:
299299
float
300300
"""
301-
return get_required(self.properties["max"], self, "max")
301+
return get_required(self.properties.get("max"), self, "max")
302302

303303
@max.setter
304304
def max(self, v: float) -> None:
@@ -312,7 +312,7 @@ def buckets(self) -> List[int]:
312312
Returns:
313313
List[int]
314314
"""
315-
return get_required(self.properties["buckets"], self, "buckets")
315+
return get_required(self.properties.get("buckets"), self, "buckets")
316316

317317
@buckets.setter
318318
def buckets(self, v: List[int]) -> None:

pystac/extensions/sar.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,9 @@ def looks_equivalent_number(self, v: Optional[float]) -> None:
289289
@property
290290
def observation_direction(self) -> Optional[ObservationDirection]:
291291
"""Gets or sets an observation direction for the item."""
292-
result = self._get_property(OBSERVATION_DIRECTION_PROP, str)
293-
if result is None:
294-
return None
295-
return ObservationDirection(result)
292+
return map_opt(
293+
ObservationDirection, self._get_property(OBSERVATION_DIRECTION_PROP, str)
294+
)
296295

297296
@observation_direction.setter
298297
def observation_direction(self, v: Optional[ObservationDirection]) -> None:

tests/extensions/test_pointcloud.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import pystac
88
from pystac.asset import Asset
9-
from pystac.errors import ExtensionTypeError, STACError
9+
from pystac.errors import ExtensionTypeError, STACError, RequiredPropertyMissing
1010
from pystac.extensions.pointcloud import (
1111
AssetPointcloudExtension,
1212
PhenomenologyType,
@@ -193,12 +193,10 @@ def test_pointcloud_schema(self) -> None:
193193
schema.size = 0.5 # type: ignore
194194

195195
empty_schema = Schema({})
196-
with self.assertRaises(STACError):
197-
empty_schema.size
198-
with self.assertRaises(STACError):
199-
empty_schema.name
200-
with self.assertRaises(STACError):
201-
empty_schema.type
196+
for required_prop in {"size", "name", "type"}:
197+
with self.subTest(attr=required_prop):
198+
with self.assertRaises(RequiredPropertyMissing):
199+
getattr(empty_schema, required_prop)
202200

203201
def test_pointcloud_statistics(self) -> None:
204202
props: Dict[str, Any] = {
@@ -251,7 +249,7 @@ def test_pointcloud_statistics(self) -> None:
251249
self.assertNotIn("variance", stat.properties)
252250

253251
empty_stat = Statistic({})
254-
with self.assertRaises(STACError):
252+
with self.assertRaises(RequiredPropertyMissing):
255253
empty_stat.name
256254

257255
def test_statistics_accessor_when_no_stats(self) -> None:

0 commit comments

Comments
 (0)