diff --git a/docs/api.rst b/docs/api.rst index 0f3624742..350b43cb0 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -164,11 +164,18 @@ RelType IO -- +StacIO +~~~~~~ + +.. autoclass:: pystac.StacIO + :members: + :undoc-members: + STAC_IO ~~~~~~~ -STAC_IO is the utility mechanism that PySTAC uses for reading and writing. Users of -PySTAC can hook into PySTAC by overriding members to utilize their own IO methods. +.. deprecated:: 1.0.0-beta.1 + Use :class:`pystac.StacIO` instead. This class will be removed in v1.0.0. .. autoclass:: pystac.stac_io.STAC_IO :members: diff --git a/pystac/__init__.py b/pystac/__init__.py index 19732af9b..68ea37d5b 100644 --- a/pystac/__init__.py +++ b/pystac/__init__.py @@ -72,7 +72,7 @@ ) -def read_file(href: str) -> STACObject: +def read_file(href: str, migrate: bool = False) -> STACObject: """Reads a STAC object from a file. This method will return either a Catalog, a Collection, or an Item based on what the @@ -82,6 +82,8 @@ def read_file(href: str) -> STACObject: Args: href : The HREF to read the object from. + migrate: Indicates whether to migrate the object to the latest + version when reading from file. Defaults to ``False``. Returns: The specific STACObject implementation class that is represented @@ -93,7 +95,18 @@ def read_file(href: str) -> STACObject: a :class:`~pystac.STACObject` and must be read using :meth:`ItemCollection.from_file ` """ - return STACObject.from_file(href) + stac_io = StacIO.default() + d = stac_io.read_json(href) + typ = pystac.serialization.identify.identify_stac_object_type(d) + + if typ == STACObjectType.CATALOG: + return Catalog.from_file(href, migrate=migrate) + elif typ == STACObjectType.COLLECTION: + return Collection.from_file(href, migrate=migrate) + elif typ == STACObjectType.ITEM: + return Item.from_file(href, migrate=migrate) + else: + raise STACTypeError(f"Cannot read file of type {typ}") def write_file( @@ -128,6 +141,7 @@ def read_dict( href: Optional[str] = None, root: Optional[Catalog] = None, stac_io: Optional[StacIO] = None, + migrate: bool = False, ) -> STACObject: """Reads a :class:`~STACObject` or :class:`~ItemCollection` from a JSON-like dict representing a serialized STAC object. @@ -156,4 +170,4 @@ def read_dict( """ if stac_io is None: stac_io = StacIO.default() - return stac_io.stac_object_from_dict(d, href, root) + return stac_io.stac_object_from_dict(d, href, root, migrate) diff --git a/pystac/catalog.py b/pystac/catalog.py index 40d6c7947..2657e08e3 100644 --- a/pystac/catalog.py +++ b/pystac/catalog.py @@ -904,7 +904,7 @@ def from_dict( if migrate: result = pystac.read_dict(d, href=href, root=root) if not isinstance(result, Catalog): - raise pystac.STACError(f"{result} is not a Catalog") + raise pystac.STACTypeError(f"{result} is not a Catalog") return result catalog_type = CatalogType.determine_type(d) @@ -919,7 +919,7 @@ def from_dict( d.pop("stac_version") - cat = Catalog( + cat = cls( id=id, description=description, title=title, @@ -945,8 +945,18 @@ def full_copy( return cast(Catalog, super().full_copy(root, parent)) @classmethod - def from_file(cls, href: str, stac_io: Optional[pystac.StacIO] = None) -> "Catalog": - result = super().from_file(href, stac_io) + def from_file( + cls, + href: str, + stac_io: Optional[pystac.StacIO] = None, + migrate: bool = False, + ) -> "Catalog": + if stac_io is None: + stac_io = pystac.StacIO.default() + + result = super().from_file(href, stac_io, migrate) if not isinstance(result, Catalog): raise pystac.STACTypeError(f"{result} is not a {Catalog}.") + result._stac_io = stac_io + return result diff --git a/pystac/collection.py b/pystac/collection.py index e68b06e09..72a954602 100644 --- a/pystac/collection.py +++ b/pystac/collection.py @@ -610,7 +610,7 @@ def from_dict( d.pop("stac_version") - collection = Collection( + collection = cls( id=id, description=description, extent=extent, @@ -670,9 +670,12 @@ def full_copy( @classmethod def from_file( - cls, href: str, stac_io: Optional[pystac.StacIO] = None + cls, + href: str, + stac_io: Optional[pystac.StacIO] = None, + migrate: bool = False, ) -> "Collection": - result = super().from_file(href, stac_io) + result = super().from_file(href, stac_io, migrate) if not isinstance(result, Collection): raise pystac.STACTypeError(f"{result} is not a {Collection}.") return result diff --git a/pystac/item.py b/pystac/item.py index f07240bf3..5b769acf9 100644 --- a/pystac/item.py +++ b/pystac/item.py @@ -9,6 +9,8 @@ from pystac import STACError, STACObjectType from pystac.asset import Asset from pystac.link import Link +from pystac.serialization.migrate import migrate_to_latest +from pystac.serialization.identify import identify_stac_object from pystac.stac_object import STACObject from pystac.utils import ( is_absolute_href, @@ -912,10 +914,8 @@ def from_dict( migrate: bool = False, ) -> "Item": if migrate: - result = pystac.read_dict(d, href=href, root=root) - if not isinstance(result, Item): - raise pystac.STACError(f"{result} is not a Catalog") - return result + info = identify_stac_object(d) + d = migrate_to_latest(d, info) d = deepcopy(d) id = d.pop("id") @@ -975,8 +975,13 @@ def full_copy( return cast(Item, super().full_copy(root, parent)) @classmethod - def from_file(cls, href: str, stac_io: Optional[pystac.StacIO] = None) -> "Item": - result = super().from_file(href, stac_io) + def from_file( + cls, + href: str, + stac_io: Optional[pystac.StacIO] = None, + migrate: bool = False, + ) -> "Item": + result = super().from_file(href, stac_io, migrate) if not isinstance(result, Item): raise pystac.STACTypeError(f"{result} is not a {Item}.") return result diff --git a/pystac/serialization/__init__.py b/pystac/serialization/__init__.py index e50412fd3..5766821c0 100644 --- a/pystac/serialization/__init__.py +++ b/pystac/serialization/__init__.py @@ -1,7 +1,4 @@ # flake8: noqa -from typing import Any, Dict, Optional, TYPE_CHECKING - -import pystac from pystac.serialization.identify import ( STACVersionRange, identify_stac_object, @@ -9,46 +6,3 @@ ) from pystac.serialization.common_properties import merge_common_properties from pystac.serialization.migrate import migrate_to_latest - -if TYPE_CHECKING: - from pystac.stac_object import STACObject - from pystac.catalog import Catalog - - -def stac_object_from_dict( - d: Dict[str, Any], href: Optional[str] = None, root: Optional["Catalog"] = None -) -> "STACObject": - """Determines how to deserialize a dictionary into a STAC object. - - Args: - d : The dict to parse. - href : Optional href that is the file location of the object being - parsed. - root : Optional root of the catalog for this object. - If provided, the root's resolved object cache can be used to search for - previously resolved instances of the STAC object. - - Note: This is used internally in StacIO instances to deserialize STAC Objects. - """ - if identify_stac_object_type(d) == pystac.STACObjectType.ITEM: - collection_cache = None - if root is not None: - collection_cache = root._resolved_objects.as_collection_cache() - - # Merge common properties in case this is an older STAC object. - merge_common_properties(d, json_href=href, collection_cache=collection_cache) - - info = identify_stac_object(d) - - d = migrate_to_latest(d, info) - - if info.object_type == pystac.STACObjectType.CATALOG: - return pystac.Catalog.from_dict(d, href=href, root=root, migrate=False) - - if info.object_type == pystac.STACObjectType.COLLECTION: - return pystac.Collection.from_dict(d, href=href, root=root, migrate=False) - - if info.object_type == pystac.STACObjectType.ITEM: - return pystac.Item.from_dict(d, href=href, root=root, migrate=False) - - raise pystac.STACTypeError(f"Unknown STAC object type {info.object_type}") diff --git a/pystac/stac_io.py b/pystac/stac_io.py index 5f49e057f..76a2b7da2 100644 --- a/pystac/stac_io.py +++ b/pystac/stac_io.py @@ -19,7 +19,12 @@ import pystac from pystac.utils import safe_urlparse -import pystac.serialization +from pystac.serialization import ( + merge_common_properties, + identify_stac_object_type, + identify_stac_object, + migrate_to_latest, +) # Use orjson if available try: @@ -94,13 +99,35 @@ def stac_object_from_dict( d: Dict[str, Any], href: Optional[str] = None, root: Optional["Catalog_Type"] = None, + migrate: bool = False, ) -> "STACObject_Type": - result = pystac.serialization.stac_object_from_dict(d, href, root) - if isinstance(result, pystac.Catalog): - # Set the stac_io instance for usage by io operations - # where this catalog is the root. + if identify_stac_object_type(d) == pystac.STACObjectType.ITEM: + collection_cache = None + if root is not None: + collection_cache = root._resolved_objects.as_collection_cache() + + # Merge common properties in case this is an older STAC object. + merge_common_properties( + d, json_href=href, collection_cache=collection_cache + ) + + info = identify_stac_object(d) + + if migrate: + d = migrate_to_latest(d, info) + + if info.object_type == pystac.STACObjectType.CATALOG: + result = pystac.Catalog.from_dict(d, href=href, root=root, migrate=False) result._stac_io = self - return result + return result + + if info.object_type == pystac.STACObjectType.COLLECTION: + return pystac.Collection.from_dict(d, href=href, root=root, migrate=False) + + if info.object_type == pystac.STACObjectType.ITEM: + return pystac.Item.from_dict(d, href=href, root=root, migrate=False) + + raise ValueError(f"Unknown STAC object type {info.object_type}") def read_json( self, source: Union[str, "Link_Type"], *args: Any, **kwargs: Any @@ -121,7 +148,10 @@ def read_json( return self._json_loads(txt, source) def read_stac_object( - self, source: Union[str, "Link_Type"], root: Optional["Catalog_Type"] = None + self, + source: Union[str, "Link_Type"], + root: Optional["Catalog_Type"] = None, + migrate: bool = False, ) -> "STACObject_Type": """Read a STACObject from a JSON file at the given source. @@ -140,7 +170,7 @@ def read_stac_object( """ d = self.read_json(source) href = source if isinstance(source, str) else source.get_absolute_href() - return self.stac_object_from_dict(d, href=href, root=root) + return self.stac_object_from_dict(d, href=href, root=root, migrate=migrate) def save_json( self, dest: Union[str, "Link_Type"], json_dict: Dict[str, Any] @@ -271,22 +301,28 @@ class STAC_IO: """ @staticmethod - def read_text_method(uri: str) -> str: + def issue_deprecation_warning() -> None: warnings.warn( - "STAC_IO is deprecated. " - "Please use instances of StacIO (e.g. StacIO.default()).", + "STAC_IO is deprecated and will be removed in v1.0.0. " + "Please use instances of StacIO (e.g. StacIO.default()) instead.", DeprecationWarning, ) + + def __init__(self) -> None: + STAC_IO.issue_deprecation_warning() + + def __init_subclass__(cls) -> None: + STAC_IO.issue_deprecation_warning() + + @staticmethod + def read_text_method(uri: str) -> str: + STAC_IO.issue_deprecation_warning() return StacIO.default().read_text(uri) @staticmethod def write_text_method(uri: str, txt: str) -> None: """Default method for writing text.""" - warnings.warn( - "STAC_IO is deprecated. " - "Please use instances of StacIO (e.g. StacIO.default()).", - DeprecationWarning, - ) + STAC_IO.issue_deprecation_warning() return StacIO.default().write_text(uri, txt) @staticmethod @@ -294,13 +330,34 @@ def stac_object_from_dict( d: Dict[str, Any], href: Optional[str] = None, root: Optional["Catalog_Type"] = None, + migrate: bool = False, ) -> "STACObject_Type": - warnings.warn( - "STAC_IO is deprecated. " - "Please use instances of StacIO (e.g. StacIO.default()).", - DeprecationWarning, - ) - return pystac.serialization.stac_object_from_dict(d, href, root) + STAC_IO.issue_deprecation_warning() + if identify_stac_object_type(d) == pystac.STACObjectType.ITEM: + collection_cache = None + if root is not None: + collection_cache = root._resolved_objects.as_collection_cache() + + # Merge common properties in case this is an older STAC object. + merge_common_properties( + d, json_href=href, collection_cache=collection_cache + ) + + info = identify_stac_object(d) + + if migrate: + d = migrate_to_latest(d, info) + + if info.object_type == pystac.STACObjectType.CATALOG: + return pystac.Catalog.from_dict(d, href=href, root=root, migrate=False) + + if info.object_type == pystac.STACObjectType.COLLECTION: + return pystac.Collection.from_dict(d, href=href, root=root, migrate=False) + + if info.object_type == pystac.STACObjectType.ITEM: + return pystac.Item.from_dict(d, href=href, root=root, migrate=False) + + raise ValueError(f"Unknown STAC object type {info.object_type}") # This is set in __init__.py _STAC_OBJECT_CLASSES = None @@ -356,6 +413,7 @@ def read_json(cls, uri: str) -> Dict[str, Any]: STAC_IO in order to enable additional URI types, replace that member with your own implementation. """ + STAC_IO.issue_deprecation_warning() result: Dict[str, Any] = json.loads(STAC_IO.read_text(uri)) return result diff --git a/pystac/stac_object.py b/pystac/stac_object.py index e40c98443..63b2bbc58 100644 --- a/pystac/stac_object.py +++ b/pystac/stac_object.py @@ -6,6 +6,8 @@ from pystac import STACError from pystac.link import Link from pystac.utils import is_absolute_href, make_absolute_href +from pystac import serialization +from pystac.serialization.identify import identify_stac_object if TYPE_CHECKING: from pystac.catalog import Catalog as Catalog_Type @@ -450,7 +452,10 @@ def clone(self) -> "STACObject": @classmethod def from_file( - cls, href: str, stac_io: Optional[pystac.StacIO] = None + cls, + href: str, + stac_io: Optional[pystac.StacIO] = None, + migrate: bool = False, ) -> "STACObject": """Reads a STACObject implementation from a file. @@ -458,6 +463,8 @@ def from_file( href : The HREF to read the object from. stac_io: Optional instance of StacIO to use. If not provided, will use the default instance. + migrate: Indicates whether to migrate the object to the latest + version when reading from file. Defaults to ``False``. Returns: The specific STACObject implementation class that is represented @@ -469,7 +476,11 @@ def from_file( if not is_absolute_href(href): href = make_absolute_href(href) - o = stac_io.read_stac_object(href) + d = stac_io.read_json(href) + if migrate: + info = identify_stac_object(d) + d = serialization.migrate.migrate_to_latest(d, info) + o = cls.from_dict(d, href=href) # Set the self HREF, if it's not already set to something else. if o.get_self_href() is None: @@ -477,10 +488,10 @@ def from_file( # If this is a root catalog, set the root to the catalog instance. root_link = o.get_root_link() - if root_link is not None: + if isinstance(o, pystac.Catalog) and root_link is not None: if not root_link.is_resolved(): if root_link.get_absolute_href() == href: - o.set_root(cast(pystac.Catalog, o)) + o.set_root(o) return o @classmethod diff --git a/tests/extensions/test_eo.py b/tests/extensions/test_eo.py index 467ae6daf..277feb26c 100644 --- a/tests/extensions/test_eo.py +++ b/tests/extensions/test_eo.py @@ -53,8 +53,8 @@ def test_to_from_dict(self) -> None: assert_to_from_dict(self, Item, item_dict) def test_validate_eo(self) -> None: - item = pystac.Item.from_file(self.LANDSAT_EXAMPLE_URI) - item2 = pystac.Item.from_file(self.BANDS_IN_ITEM_URI) + item = pystac.Item.from_file(self.LANDSAT_EXAMPLE_URI, migrate=True) + item2 = pystac.Item.from_file(self.BANDS_IN_ITEM_URI, migrate=True) item.validate() item2.validate() @@ -198,8 +198,9 @@ def test_summaries(self) -> None: def test_read_pre_09_fields_into_common_metadata(self) -> None: eo_item = pystac.Item.from_file( TestCases.get_path( - "data-files/examples/0.8.1/item-spec/examples/" "landsat8-sample.json" - ) + "data-files/examples/0.8.1/item-spec/examples/landsat8-sample.json" + ), + migrate=True, ) self.assertEqual(eo_item.common_metadata.platform, "landsat-8") @@ -208,8 +209,9 @@ def test_read_pre_09_fields_into_common_metadata(self) -> None: def test_reads_asset_bands_in_pre_1_0_version(self) -> None: item = pystac.Item.from_file( TestCases.get_path( - "data-files/examples/0.9.0/item-spec/examples/" "landsat8-sample.json" - ) + "data-files/examples/0.9.0/item-spec/examples/landsat8-sample.json" + ), + migrate=True, ) bands = EOExtension.ext(item.assets["B9"]).bands @@ -220,8 +222,9 @@ def test_reads_asset_bands_in_pre_1_0_version(self) -> None: def test_reads_gsd_in_pre_1_0_version(self) -> None: eo_item = pystac.Item.from_file( TestCases.get_path( - "data-files/examples/0.9.0/item-spec/examples/" "landsat8-sample.json" - ) + "data-files/examples/0.9.0/item-spec/examples/landsat8-sample.json" + ), + migrate=True, ) self.assertEqual(eo_item.common_metadata.gsd, 30.0) diff --git a/tests/extensions/test_file.py b/tests/extensions/test_file.py index 45aa93f20..f8b870a23 100644 --- a/tests/extensions/test_file.py +++ b/tests/extensions/test_file.py @@ -81,7 +81,7 @@ def test_migrates_old_checksum(self) -> None: "data-files/examples/1.0.0-beta.2/" "extensions/checksum/examples/sentinel1.json" ) - item = pystac.Item.from_file(example_path) + item = pystac.Item.from_file(example_path, migrate=True) self.assertTrue(FileExtension.has_extension(item)) self.assertEqual( diff --git a/tests/extensions/test_pointcloud.py b/tests/extensions/test_pointcloud.py index 9bad4a440..0b31200e6 100644 --- a/tests/extensions/test_pointcloud.py +++ b/tests/extensions/test_pointcloud.py @@ -44,7 +44,7 @@ def test_apply(self) -> None: self.assertTrue(PointcloudExtension.has_extension(item)) def test_validate_pointcloud(self) -> None: - item = pystac.Item.from_file(self.example_uri) + item = pystac.Item.from_file(self.example_uri, migrate=True) item.validate() def test_count(self) -> None: diff --git a/tests/extensions/test_raster.py b/tests/extensions/test_raster.py index aeed0d9ba..2c5e28acb 100644 --- a/tests/extensions/test_raster.py +++ b/tests/extensions/test_raster.py @@ -33,8 +33,8 @@ def test_to_from_dict(self) -> None: assert_to_from_dict(self, Item, item_dict) def test_validate_raster(self) -> None: - item = pystac.Item.from_file(self.PLANET_EXAMPLE_URI) - item2 = pystac.Item.from_file(self.SENTINEL2_EXAMPLE_URI) + item = pystac.Item.from_file(self.PLANET_EXAMPLE_URI, migrate=True) + item2 = pystac.Item.from_file(self.SENTINEL2_EXAMPLE_URI, migrate=True) item.validate() item2.validate() diff --git a/tests/extensions/test_timestamps.py b/tests/extensions/test_timestamps.py index 92e3e2805..de94baaf4 100644 --- a/tests/extensions/test_timestamps.py +++ b/tests/extensions/test_timestamps.py @@ -59,7 +59,7 @@ def test_apply(self) -> None: self.assertNotIn(p, item.properties) def test_validate_timestamps(self) -> None: - item = pystac.Item.from_file(self.example_uri) + item = pystac.Item.from_file(self.example_uri, migrate=True) item.validate() def test_expires(self) -> None: diff --git a/tests/serialization/test_migrate.py b/tests/serialization/test_migrate.py index 1fe917cee..d25b293ea 100644 --- a/tests/serialization/test_migrate.py +++ b/tests/serialization/test_migrate.py @@ -53,7 +53,8 @@ def test_migrates_removed_extension(self) -> None: item = pystac.Item.from_file( TestCases.get_path( "data-files/examples/0.8.1/extensions/sar/examples/sentinel1.json" - ) + ), + migrate=True, ) self.assertFalse("dtr" in item.stac_extensions) self.assertEqual( @@ -65,7 +66,8 @@ def test_migrates_added_extension(self) -> None: item = pystac.Item.from_file( TestCases.get_path( "data-files/examples/0.8.1/item-spec/" "examples/planet-sample.json" - ) + ), + migrate=True, ) self.assertTrue(ViewExtension.has_extension(item)) view_ext = ViewExtension.ext(item) @@ -78,7 +80,8 @@ def test_migrates_renamed_extension(self) -> None: TestCases.get_path( "data-files/examples/0.9.0/extensions/asset/" "examples/example-landsat8.json" - ) + ), + migrate=True, ) self.assertTrue(ItemAssetsExtension.has_extension(collection)) diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 74e8597d2..eb7d6391d 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -1107,3 +1107,27 @@ def test_full_copy_4(self) -> None: ].get_absolute_href() assert href is not None self.assertTrue(os.path.exists(href)) + + +class CatalogSubClassTest(unittest.TestCase): + """This tests cases related to creating classes inheriting from pystac.Catalog to + ensure that inheritance, class methods, etc. function as expected.""" + + TEST_CASE_1 = TestCases.get_path("data-files/catalogs/test-case-1/catalog.json") + + class BasicCustomCatalog(pystac.Catalog): + pass + + def setUp(self) -> None: + self.stac_io = pystac.StacIO.default() + + def test_from_dict_returns_subclass(self) -> None: + catalog_dict = self.stac_io.read_json(self.TEST_CASE_1) + custom_catalog = self.BasicCustomCatalog.from_dict(catalog_dict) + + self.assertIsInstance(custom_catalog, self.BasicCustomCatalog) + + def test_from_file_returns_subclass(self) -> None: + custom_catalog = self.BasicCustomCatalog.from_file(self.TEST_CASE_1) + + self.assertIsInstance(custom_catalog, self.BasicCustomCatalog) diff --git a/tests/test_collection.py b/tests/test_collection.py index 6f87ecd0e..978024795 100644 --- a/tests/test_collection.py +++ b/tests/test_collection.py @@ -249,3 +249,27 @@ def test_from_items(self) -> None: self.assertEqual(interval[0], datetime(2000, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC)) self.assertEqual(interval[1], datetime(2001, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC)) + + +class CollectionSubClassTest(unittest.TestCase): + """This tests cases related to creating classes inheriting from pystac.Catalog to + ensure that inheritance, class methods, etc. function as expected.""" + + MULTI_EXTENT = TestCases.get_path("data-files/collections/multi-extent.json") + + class BasicCustomCollection(pystac.Collection): + pass + + def setUp(self) -> None: + self.stac_io = pystac.StacIO.default() + + def test_from_dict_returns_subclass(self) -> None: + collection_dict = self.stac_io.read_json(self.MULTI_EXTENT) + custom_collection = self.BasicCustomCollection.from_dict(collection_dict) + + self.assertIsInstance(custom_collection, self.BasicCustomCollection) + + def test_from_file_returns_subclass(self) -> None: + custom_collection = self.BasicCustomCollection.from_file(self.MULTI_EXTENT) + + self.assertIsInstance(custom_collection, self.BasicCustomCollection) diff --git a/tests/test_item.py b/tests/test_item.py index 769fae370..834c9ba39 100644 --- a/tests/test_item.py +++ b/tests/test_item.py @@ -697,3 +697,27 @@ def test_asset_updated(self) -> None: new_a1_value = cm.get_updated(item.assets["analytic"]) self.assertEqual(new_a1_value, set_value) self.assertEqual(cm.updated, item_value) + + +class ItemSubClassTest(unittest.TestCase): + """This tests cases related to creating classes inheriting from pystac.Catalog to + ensure that inheritance, class methods, etc. function as expected.""" + + SAMPLE_ITEM = TestCases.get_path("data-files/item/sample-item.json") + + class BasicCustomItem(pystac.Item): + pass + + def setUp(self) -> None: + self.stac_io = pystac.StacIO.default() + + def test_from_dict_returns_subclass(self) -> None: + item_dict = self.stac_io.read_json(self.SAMPLE_ITEM) + custom_item = self.BasicCustomItem.from_dict(item_dict) + + self.assertIsInstance(custom_item, self.BasicCustomItem) + + def test_from_file_returns_subclass(self) -> None: + custom_item = self.BasicCustomItem.from_file(self.SAMPLE_ITEM) + + self.assertIsInstance(custom_item, self.BasicCustomItem) diff --git a/tests/validation/test_validate.py b/tests/validation/test_validate.py index c6bf68444..20b1b54bd 100644 --- a/tests/validation/test_validate.py +++ b/tests/validation/test_validate.py @@ -18,7 +18,8 @@ class ValidateTest(unittest.TestCase): def test_validate_current_version(self) -> None: catalog = pystac.read_file( - TestCases.get_path("data-files/catalogs/test-case-1/" "catalog.json") + TestCases.get_path("data-files/catalogs/test-case-1/" "catalog.json"), + migrate=True, ) catalog.validate()