Skip to content

Commit e988e24

Browse files
committed
change STACTypeError to create short informative message
1 parent e196e50 commit e988e24

File tree

11 files changed

+53
-36
lines changed

11 files changed

+53
-36
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
- Updated raster extension to work with the item_assets extension's AssetDefinition objects ([#1110](https://github.com/stac-utils/pystac/pull/1110))
2626
- Return all validation errors from validation methods of `JsonSchemaSTACValidator` ([#1120](https://github.com/stac-utils/pystac/pull/1120))
2727
- EO extension updated to v1.1.0 ([#1131](https://github.com/stac-utils/pystac/pull/1131))
28+
- Use `id` in STACTypeError instead of entire dict ([#1126](https://github.com/stac-utils/pystac/pull/1126))
2829

2930
### Deprecated
3031

pystac/catalog.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,7 +1136,7 @@ def from_dict(
11361136
d = migrate_to_latest(d, info)
11371137

11381138
if not cls.matches_object_type(d):
1139-
raise STACTypeError(f"{d} does not represent a {cls.__name__} instance")
1139+
raise STACTypeError(d, cls)
11401140

11411141
catalog_type = CatalogType.determine_type(d)
11421142

@@ -1187,8 +1187,6 @@ def from_file(
11871187
stac_io = pystac.StacIO.default()
11881188

11891189
result = super().from_file(href, stac_io)
1190-
if not isinstance(result, Catalog):
1191-
raise pystac.STACTypeError(f"{result} is not a {Catalog}.")
11921190
result._stac_io = stac_io
11931191

11941192
return result

pystac/collection.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ def from_dict(
627627
d = migrate_to_latest(d, info)
628628

629629
if not cls.matches_object_type(d):
630-
raise STACTypeError(f"{d} does not represent a {cls.__name__} instance")
630+
raise STACTypeError(d, cls)
631631

632632
catalog_type = CatalogType.determine_type(d)
633633

@@ -754,15 +754,6 @@ def full_copy(
754754
) -> Collection:
755755
return cast(Collection, super().full_copy(root, parent))
756756

757-
@classmethod
758-
def from_file(
759-
cls: Type[C], href: str, stac_io: Optional[pystac.StacIO] = None
760-
) -> C:
761-
result = super().from_file(href, stac_io)
762-
if not isinstance(result, Collection):
763-
raise pystac.STACTypeError(f"{result} is not a {Collection}.")
764-
return result
765-
766757
@classmethod
767758
def matches_object_type(cls, d: Dict[str, Any]) -> bool:
768759
return identify_stac_object_type(d) == STACObjectType.COLLECTION

pystac/errors.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Optional, Union
1+
from typing import Any, Dict, Optional, Union
22

33

44
class TemplateError(Exception):
@@ -24,7 +24,28 @@ class STACTypeError(Exception):
2424
a Catalog JSON was read in as an Item.
2525
"""
2626

27-
pass
27+
def __init__(
28+
self,
29+
bad_dict: Dict[str, Any],
30+
expected: type,
31+
extra_message: Optional[str] = "",
32+
):
33+
"""
34+
Construct an exception with an appropriate error message from bad_dict and the
35+
expected that it didn't align with.
36+
37+
Args:
38+
bad_dict: Dictionary that did not match the expected type
39+
expected: The expected type.
40+
extra_message: message that will be appended to the exception message.
41+
"""
42+
message = (
43+
f"JSON (id = {bad_dict.get('id', 'unknown')}) does not represent"
44+
f" a {expected.__name__} instance."
45+
)
46+
if extra_message:
47+
message += " " + extra_message
48+
super().__init__(message)
2849

2950

3051
class DuplicateObjectKeyError(Exception):

pystac/item.py

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -432,9 +432,7 @@ def from_dict(
432432
d = migrate_to_latest(d, info)
433433

434434
if not cls.matches_object_type(d):
435-
raise pystac.STACTypeError(
436-
f"{d} does not represent a {cls.__name__} instance"
437-
)
435+
raise pystac.STACTypeError(d, cls)
438436

439437
# some fields are passed through to __init__
440438
pass_through_fields = [
@@ -504,23 +502,11 @@ def full_copy(
504502
) -> Item:
505503
return cast(Item, super().full_copy(root, parent))
506504

507-
@classmethod
508-
def from_file(
509-
cls: Type[T], href: str, stac_io: Optional[pystac.StacIO] = None
510-
) -> T:
511-
result = super().from_file(href, stac_io)
512-
if not isinstance(result, Item):
513-
raise pystac.STACTypeError(f"{result} is not a {Item}.")
514-
return result
515-
516505
@classmethod
517506
def matches_object_type(cls, d: Dict[str, Any]) -> bool:
518507
for field in ("type", "stac_version"):
519508
if field not in d:
520-
raise pystac.STACTypeError(
521-
f"{d} does not represent a {cls.__name__} instance"
522-
f"'{field}' is missing."
523-
)
509+
raise pystac.STACTypeError(d, cls, f"'{field}' is missing.")
524510
return identify_stac_object_type(d) == STACObjectType.ITEM
525511

526512
@property

pystac/item_collection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ def from_dict(
175175
hit of a deepcopy.
176176
"""
177177
if not cls.is_item_collection(d):
178-
raise STACTypeError("Dict is not a valid ItemCollection")
178+
raise STACTypeError(d, cls)
179179

180180
items = [
181181
pystac.Item.from_dict(item, preserve_dict=preserve_dict, root=root)

pystac/serialization/identify.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,8 @@ def identify_stac_object(json_dict: Dict[str, Any]) -> STACJSONDescription:
248248
object_type = identify_stac_object_type(json_dict)
249249

250250
if object_type is None:
251-
raise pystac.STACTypeError("JSON does not represent a STAC object.")
251+
extra_message = f"'type' attribute is {json_dict.get('type', 'not set')}"
252+
raise pystac.STACTypeError(json_dict, pystac.STACObject, extra_message)
252253

253254
version_range = STACVersionRange()
254255

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "Catalog",
3+
"id": "broken_cat",
4+
"description": "test catalog w/o stac_version",
5+
"links": [],
6+
"stac_extensions": []
7+
}

tests/serialization/test_identify.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ def test_identify_invalid_stac_object_with_version(self) -> None:
6969
with pytest.raises(pystac.STACTypeError) as ctx:
7070
identify_stac_object(invalid_dict)
7171

72-
assert "JSON does not represent a STAC object" in str(ctx.value.args[0])
72+
assert "JSON (id = concepts) does not represent a STACObject instance." in str(
73+
ctx.value.args[0]
74+
)
7375

7476
def test_identify_non_stac_raises_error(self) -> None:
7577
plain_feature_dict = {
@@ -81,7 +83,9 @@ def test_identify_non_stac_raises_error(self) -> None:
8183
with pytest.raises(pystac.STACTypeError) as ctx:
8284
identify_stac_object(plain_feature_dict)
8385

84-
assert "JSON does not represent a STAC object" in str(ctx.value.args[0])
86+
assert "JSON (id = unknown) does not represent a STACObject instance." in str(
87+
ctx.value.args[0]
88+
)
8589

8690
def test_identify_invalid_with_stac_version(self) -> None:
8791
not_stac = {"stac_version": "0.9.0", "type": "Custom"}

tests/test_catalog.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ def test_from_dict_preserves_dict(self) -> None:
105105
_ = Catalog.from_dict(param_dict, preserve_dict=False)
106106
assert param_dict != catalog_dict
107107

108+
def test_from_file_bad_catalog(self) -> None:
109+
with pytest.raises(pystac.errors.STACTypeError) as ctx:
110+
_ = Catalog.from_file(TestCases.get_path(TestCases.bad_catalog_case))
111+
assert "(id = broken_cat) does not represent a STACObject" in ctx.value.args[0]
112+
assert "is Catalog" in ctx.value.args[0]
113+
108114
def test_from_dict_set_root(self) -> None:
109115
path = TestCases.get_path("data-files/catalogs/test-case-1/catalog.json")
110116
with open(path) as f:
@@ -1281,7 +1287,7 @@ def test_catalog_with_href_caches_by_href(self) -> None:
12811287
# cached only by HREF
12821288
assert len(cache.id_keys_to_objects) == 0
12831289

1284-
def testfrom_invalid_dict_raises_exception(self) -> None:
1290+
def test_from_invalid_dict_raises_exception(self) -> None:
12851291
stac_io = pystac.StacIO.default()
12861292
collection_dict = stac_io.read_json(
12871293
TestCases.get_path("data-files/collections/multi-extent.json")

0 commit comments

Comments
 (0)