Skip to content

Commit

Permalink
Merge pull request #561 from duckontheweb/change/gh-315-required-prop…
Browse files Browse the repository at this point in the history
…erty-exception

Use get_required to raise RequiredPropertyMissing when appropriate
  • Loading branch information
Jon Duckworth authored Jul 17, 2021
2 parents 0ba1a1d + c12f907 commit ac7b1d6
Show file tree
Hide file tree
Showing 8 changed files with 43 additions and 69 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@
- Calling `ExtensionManagementMixin.validate_has_extension` with `add_if_missing = True`
on an ownerless `Asset` will raise a `STACError` ([#554](https://github.com/stac-utils/pystac/pull/554))
- `PointcloudSchema` -> `Schema`, `PointcloudStatistic` -> `Statistic` for consistency
with naming convention in other extensions ([#548](https://github.com/stac-utils/pystac/pull/548))
with naming convention in other extensions
([#548](https://github.com/stac-utils/pystac/pull/548))
- `RequiredPropertyMissing` always raised when trying to get a required property that is
`None` (`STACError` or `KeyError` was previously being raised in some cases)
([#561](https://github.com/stac-utils/pystac/pull/561))

### Fixed

Expand Down
24 changes: 10 additions & 14 deletions pystac/extensions/datacube.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
PropertiesExtension,
)
from pystac.extensions.hooks import ExtensionHooks
from pystac.utils import get_required, map_opt
from pystac.utils import get_required

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

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

@staticmethod
def from_dict(d: Dict[str, Any]) -> "Dimension":
dim_type = d.get(DIM_TYPE_PROP)
if dim_type is None:
raise pystac.RequiredPropertyMissing("cube_dimension", DIM_TYPE_PROP)
dim_type: str = get_required(
d.get(DIM_TYPE_PROP), "cube_dimension", DIM_TYPE_PROP
)
if dim_type == "spatial":
axis = d.get(DIM_AXIS_PROP)
if axis is None:
raise pystac.RequiredPropertyMissing("cube_dimension", DIM_AXIS_PROP)
axis: str = get_required(
d.get(DIM_AXIS_PROP), "cube_dimension", DIM_AXIS_PROP
)
if axis == "z":
return VerticalSpatialDimension(d)
else:
Expand Down Expand Up @@ -320,14 +320,10 @@ def apply(self, dimensions: Dict[str, Dimension]) -> None:

@property
def dimensions(self) -> Dict[str, Dimension]:
return get_required(
map_opt(
lambda d: {k: Dimension.from_dict(v) for k, v in d.items()},
self._get_property(DIMENSIONS_PROP, Dict[str, Any]),
),
self,
DIMENSIONS_PROP,
result = get_required(
self._get_property(DIMENSIONS_PROP, Dict[str, Any]), self, DIMENSIONS_PROP
)
return {k: Dimension.from_dict(v) for k, v in result.items()}

@dimensions.setter
def dimensions(self, v: Dict[str, Dimension]) -> None:
Expand Down
2 changes: 1 addition & 1 deletion pystac/extensions/eo.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def name(self) -> str:
Returns:
str
"""
return get_required(self.properties["name"], self, "name")
return get_required(self.properties.get("name"), self, "name")

@name.setter
def name(self, v: str) -> None:
Expand Down
4 changes: 2 additions & 2 deletions pystac/extensions/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def create(cls, values: List[Any], summary: str) -> "MappingObject":
def values(self) -> List[Any]:
"""Gets or sets the list of value(s) in the file. At least one array element is
required."""
return get_required(self.properties["values"], self, "values")
return get_required(self.properties.get("values"), self, "values")

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

@summary.setter
def summary(self, v: str) -> None:
Expand Down
47 changes: 12 additions & 35 deletions pystac/extensions/pointcloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,7 @@ def create(cls, name: str, size: int, type: SchemaType) -> "Schema":
@property
def size(self) -> int:
"""Gets or sets the size value."""
result: Optional[int] = self.properties.get("size")
if result is None:
raise pystac.STACError(
f"Pointcloud schema does not have size property: {self.properties}"
)
return result
return get_required(self.properties.get("size"), self, "size")

@size.setter
def size(self, v: int) -> None:
Expand All @@ -105,12 +100,7 @@ def size(self, v: int) -> None:
@property
def name(self) -> str:
"""Gets or sets the name property for this Schema."""
result: Optional[str] = self.properties.get("name")
if result is None:
raise pystac.STACError(
f"Pointcloud schema does not have name property: {self.properties}"
)
return result
return get_required(self.properties.get("name"), self, "name")

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

def __repr__(self) -> str:
return "<Schema name={} size={} type={}>".format(
self.name, self.size, self.type
self.properties.get("name"),
self.properties.get("size"),
self.properties.get("type"),
)

def to_dict(self) -> Dict[str, Any]:
Expand Down Expand Up @@ -217,12 +209,7 @@ def create(
@property
def name(self) -> str:
"""Gets or sets the name property."""
result: Optional[str] = self.properties.get("name")
if result is None:
raise pystac.STACError(
f"Pointcloud statistics does not have name property: {self.properties}"
)
return result
return get_required(self.properties.get("name"), self, "name")

@name.setter
def name(self, v: str) -> None:
Expand Down Expand Up @@ -381,10 +368,7 @@ def apply(
@property
def count(self) -> int:
"""Gets or sets the number of points in the Item."""
result = self._get_property(COUNT_PROP, int)
if result is None:
raise pystac.RequiredPropertyMissing(self, COUNT_PROP)
return result
return get_required(self._get_property(COUNT_PROP, int), self, COUNT_PROP)

@count.setter
def count(self, v: int) -> None:
Expand All @@ -393,11 +377,7 @@ def count(self, v: int) -> None:
@property
def type(self) -> Union[PhenomenologyType, str]:
"""Gets or sets the phenomenology type for the point cloud."""
return get_required(
self._get_property(TYPE_PROP, str),
self,
TYPE_PROP,
)
return get_required(self._get_property(TYPE_PROP, str), self, TYPE_PROP)

@type.setter
def type(self, v: Union[PhenomenologyType, str]) -> None:
Expand All @@ -406,10 +386,7 @@ def type(self, v: Union[PhenomenologyType, str]) -> None:
@property
def encoding(self) -> str:
"""Gets or sets the content encoding or format of the data."""
result = self._get_property(ENCODING_PROP, str)
if result is None:
raise pystac.RequiredPropertyMissing(self, ENCODING_PROP)
return result
return get_required(self._get_property(ENCODING_PROP, str), self, ENCODING_PROP)

@encoding.setter
def encoding(self, v: str) -> None:
Expand All @@ -420,9 +397,9 @@ def schemas(self) -> List[Schema]:
"""Gets or sets the list of :class:`Schema` instances defining
dimensions and types for the data.
"""
result = self._get_property(SCHEMAS_PROP, List[Dict[str, Any]])
if result is None:
raise pystac.RequiredPropertyMissing(self, SCHEMAS_PROP)
result = get_required(
self._get_property(SCHEMAS_PROP, List[Dict[str, Any]]), self, SCHEMAS_PROP
)
return [Schema(s) for s in result]

@schemas.setter
Expand Down
8 changes: 4 additions & 4 deletions pystac/extensions/raster.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ def count(self) -> int:
Returns:
int
"""
return get_required(self.properties["count"], self, "count")
return get_required(self.properties.get("count"), self, "count")

@count.setter
def count(self, v: int) -> None:
Expand All @@ -285,7 +285,7 @@ def min(self) -> float:
Returns:
float
"""
return get_required(self.properties["min"], self, "min")
return get_required(self.properties.get("min"), self, "min")

@min.setter
def min(self, v: float) -> None:
Expand All @@ -298,7 +298,7 @@ def max(self) -> float:
Returns:
float
"""
return get_required(self.properties["max"], self, "max")
return get_required(self.properties.get("max"), self, "max")

@max.setter
def max(self, v: float) -> None:
Expand All @@ -312,7 +312,7 @@ def buckets(self) -> List[int]:
Returns:
List[int]
"""
return get_required(self.properties["buckets"], self, "buckets")
return get_required(self.properties.get("buckets"), self, "buckets")

@buckets.setter
def buckets(self, v: List[int]) -> None:
Expand Down
7 changes: 3 additions & 4 deletions pystac/extensions/sar.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,10 +289,9 @@ def looks_equivalent_number(self, v: Optional[float]) -> None:
@property
def observation_direction(self) -> Optional[ObservationDirection]:
"""Gets or sets an observation direction for the item."""
result = self._get_property(OBSERVATION_DIRECTION_PROP, str)
if result is None:
return None
return ObservationDirection(result)
return map_opt(
ObservationDirection, self._get_property(OBSERVATION_DIRECTION_PROP, str)
)

@observation_direction.setter
def observation_direction(self, v: Optional[ObservationDirection]) -> None:
Expand Down
14 changes: 6 additions & 8 deletions tests/extensions/test_pointcloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import pystac
from pystac.asset import Asset
from pystac.errors import ExtensionTypeError, STACError
from pystac.errors import ExtensionTypeError, STACError, RequiredPropertyMissing
from pystac.extensions.pointcloud import (
AssetPointcloudExtension,
PhenomenologyType,
Expand Down Expand Up @@ -193,12 +193,10 @@ def test_pointcloud_schema(self) -> None:
schema.size = 0.5 # type: ignore

empty_schema = Schema({})
with self.assertRaises(STACError):
empty_schema.size
with self.assertRaises(STACError):
empty_schema.name
with self.assertRaises(STACError):
empty_schema.type
for required_prop in {"size", "name", "type"}:
with self.subTest(attr=required_prop):
with self.assertRaises(RequiredPropertyMissing):
getattr(empty_schema, required_prop)

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

empty_stat = Statistic({})
with self.assertRaises(STACError):
with self.assertRaises(RequiredPropertyMissing):
empty_stat.name

def test_statistics_accessor_when_no_stats(self) -> None:
Expand Down

0 comments on commit ac7b1d6

Please sign in to comment.