diff --git a/CHANGELOG.md b/CHANGELOG.md index e2c8e2c61..79ba9fd95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Changed + +- `migrate=True` is now the default in `from_dict` ([#1509](https://github.com/stac-utils/pystac/pull/1509)) + ### Fixed - Fall back to `epsg` when `code` is not present in the Projection extension ([#1505](https://github.com/stac-utils/pystac/pull/1505)) diff --git a/pystac/catalog.py b/pystac/catalog.py index 718650be7..c1772599c 100644 --- a/pystac/catalog.py +++ b/pystac/catalog.py @@ -1224,7 +1224,7 @@ def from_dict( d: dict[str, Any], href: str | None = None, root: Catalog | None = None, - migrate: bool = False, + migrate: bool = True, preserve_dict: bool = True, ) -> C: if migrate: diff --git a/pystac/collection.py b/pystac/collection.py index 77531e4b8..891b3cc33 100644 --- a/pystac/collection.py +++ b/pystac/collection.py @@ -632,7 +632,7 @@ def from_dict( d: dict[str, Any], href: str | None = None, root: Catalog | None = None, - migrate: bool = False, + migrate: bool = True, preserve_dict: bool = True, ) -> C: from pystac.extensions.version import CollectionVersionExtension diff --git a/pystac/item.py b/pystac/item.py index 0632891fb..f95c5a218 100644 --- a/pystac/item.py +++ b/pystac/item.py @@ -416,7 +416,7 @@ def from_dict( d: dict[str, Any], href: str | None = None, root: Catalog | None = None, - migrate: bool = False, + migrate: bool = True, preserve_dict: bool = True, ) -> T: from pystac.extensions.version import ItemVersionExtension diff --git a/pystac/stac_io.py b/pystac/stac_io.py index edf058af4..fa71dd7c0 100644 --- a/pystac/stac_io.py +++ b/pystac/stac_io.py @@ -169,19 +169,19 @@ def stac_object_from_dict( if info.object_type == pystac.STACObjectType.CATALOG: result = pystac.Catalog.from_dict( - d, href=href_str, root=root, migrate=False, preserve_dict=preserve_dict + d, href=href_str, root=root, migrate=True, preserve_dict=preserve_dict ) result._stac_io = self return result if info.object_type == pystac.STACObjectType.COLLECTION: return pystac.Collection.from_dict( - d, href=href_str, root=root, migrate=False, preserve_dict=preserve_dict + d, href=href_str, root=root, migrate=True, preserve_dict=preserve_dict ) if info.object_type == pystac.STACObjectType.ITEM: return pystac.Item.from_dict( - d, href=href_str, root=root, migrate=False, preserve_dict=preserve_dict + d, href=href_str, root=root, migrate=True, preserve_dict=preserve_dict ) raise ValueError(f"Unknown STAC object type {info.object_type}") diff --git a/pystac/stac_object.py b/pystac/stac_object.py index 591a5c862..ad83ebe9c 100644 --- a/pystac/stac_object.py +++ b/pystac/stac_object.py @@ -647,7 +647,7 @@ def from_dict( d: dict[str, Any], href: str | None = None, root: Catalog | None = None, - migrate: bool = False, + migrate: bool = True, preserve_dict: bool = True, ) -> S: """Parses this STACObject from the passed in dictionary. @@ -659,8 +659,9 @@ def from_dict( root : Optional root catalog for this object. If provided, the root of the returned STACObject will be set to this parameter. - migrate: Use True if this dict represents JSON from an older STAC object, - so that migrations are run against it. + migrate: By default, STAC objects and extensions are migrated to + their latest supported version. Set this to False to disable + this behavior. preserve_dict: If False, the dict parameter ``d`` may be modified during this method call. Otherwise the dict is not mutated. Defaults to True, which results results in a deepcopy of the diff --git a/tests/extensions/test_classification.py b/tests/extensions/test_classification.py index ea5c16416..1f0b83731 100644 --- a/tests/extensions/test_classification.py +++ b/tests/extensions/test_classification.py @@ -260,7 +260,7 @@ def _parse_times(a_dict: dict[str, Any]) -> None: a_dict[k] = a_dict[k].replace(microsecond=0) d1 = deepcopy(item_dict) - d2 = Item.from_dict(item_dict).to_dict() + d2 = Item.from_dict(item_dict, migrate=False).to_dict() _parse_times(d1) _parse_times(d2) assert d1 == d2, f"Mismatch between dictionaries: \n{d1}\n{d2}" @@ -343,7 +343,7 @@ def test_older_extension_version(landsat_item: Item) -> None: stac_extensions.add(old) item_as_dict = landsat_item.to_dict(include_self_link=False, transform_hrefs=False) item_as_dict["stac_extensions"] = list(stac_extensions) - item = Item.from_dict(item_as_dict) + item = Item.from_dict(item_as_dict, migrate=False) assert ClassificationExtension.has_extension(item) assert old in item.stac_extensions diff --git a/tests/extensions/test_eo.py b/tests/extensions/test_eo.py index b0b1703c2..fca2224b7 100644 --- a/tests/extensions/test_eo.py +++ b/tests/extensions/test_eo.py @@ -439,7 +439,7 @@ def test_older_extension_version(ext_item: Item) -> None: stac_extensions.add(old) item_as_dict = ext_item.to_dict(include_self_link=False, transform_hrefs=False) item_as_dict["stac_extensions"] = list(stac_extensions) - item = Item.from_dict(item_as_dict) + item = Item.from_dict(item_as_dict, migrate=False) assert EOExtension.has_extension(item) assert old in item.stac_extensions diff --git a/tests/extensions/test_grid.py b/tests/extensions/test_grid.py index 15117de0f..6cabec8c8 100644 --- a/tests/extensions/test_grid.py +++ b/tests/extensions/test_grid.py @@ -160,7 +160,7 @@ def test_older_extension_version(ext_item: pystac.Item) -> None: stac_extensions.add(old) item_as_dict = ext_item.to_dict(include_self_link=False, transform_hrefs=False) item_as_dict["stac_extensions"] = list(stac_extensions) - item = pystac.Item.from_dict(item_as_dict) + item = pystac.Item.from_dict(item_as_dict, migrate=False) assert GridExtension.has_extension(item) assert old in item.stac_extensions diff --git a/tests/extensions/test_projection.py b/tests/extensions/test_projection.py index 61f9392cf..39c43035d 100644 --- a/tests/extensions/test_projection.py +++ b/tests/extensions/test_projection.py @@ -602,7 +602,7 @@ def test_older_extension_version(projection_landsat8_item: Item) -> None: include_self_link=False, transform_hrefs=False ) item_as_dict["stac_extensions"] = list(stac_extensions) - item = Item.from_dict(item_as_dict) + item = Item.from_dict(item_as_dict, migrate=False) assert ProjectionExtension.has_extension(item) assert old in item.stac_extensions diff --git a/tests/extensions/test_raster.py b/tests/extensions/test_raster.py index 925113dc9..8b0db0496 100644 --- a/tests/extensions/test_raster.py +++ b/tests/extensions/test_raster.py @@ -305,7 +305,7 @@ def test_older_extension_version(ext_item: pystac.Item) -> None: stac_extensions.add(old) item_as_dict = ext_item.to_dict(include_self_link=False, transform_hrefs=False) item_as_dict["stac_extensions"] = list(stac_extensions) - item = pystac.Item.from_dict(item_as_dict) + item = pystac.Item.from_dict(item_as_dict, migrate=False) assert RasterExtension.has_extension(item) assert old in item.stac_extensions diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 36adf4b1d..6ce7832bd 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -115,7 +115,7 @@ def test_from_dict_preserves_dict(self) -> None: # assert that the parameter is not preserved with # non-default parameter - _ = Catalog.from_dict(param_dict, preserve_dict=False) + _ = Catalog.from_dict(param_dict, preserve_dict=False, migrate=False) assert param_dict != catalog_dict def test_from_file_bad_catalog(self) -> None: @@ -1595,7 +1595,7 @@ def from_dict( d: dict[str, Any], href: str | None = None, root: Catalog | None = None, - migrate: bool = False, + migrate: bool = True, preserve_dict: bool = True, ) -> CustomCatalog: return super().from_dict(d) diff --git a/tests/test_collection.py b/tests/test_collection.py index c339a8d10..0ccc5a6b1 100644 --- a/tests/test_collection.py +++ b/tests/test_collection.py @@ -306,7 +306,7 @@ def test_from_dict_preserves_dict(self) -> None: # assert that the parameter is not preserved with # non-default parameter - _ = Collection.from_dict(param_dict, preserve_dict=False) + _ = Collection.from_dict(param_dict, preserve_dict=False, migrate=False) self.assertNotEqual(param_dict, collection_dict) def test_from_dict_set_root(self) -> None: @@ -594,7 +594,7 @@ def from_dict( d: dict[str, Any], href: str | None = None, root: Catalog | None = None, - migrate: bool = False, + migrate: bool = True, preserve_dict: bool = True, ) -> CustomCollection: return super().from_dict(d) diff --git a/tests/test_item.py b/tests/test_item.py index a1c39f12e..9932e112f 100644 --- a/tests/test_item.py +++ b/tests/test_item.py @@ -464,7 +464,7 @@ def from_dict( d: dict[str, Any], href: str | None = None, root: Catalog | None = None, - migrate: bool = False, + migrate: bool = True, preserve_dict: bool = True, ) -> CustomItem: return super().from_dict(d) @@ -481,13 +481,13 @@ def test_item_from_dict_raises_useful_error() -> None: def test_item_from_dict_with_missing_stac_version_raises_useful_error() -> None: item_dict = {"type": "Feature", "id": "lalalalala"} with pytest.raises(pystac.STACTypeError, match="'stac_version' is missing"): - Item.from_dict(item_dict) + Item.from_dict(item_dict, migrate=False) def test_item_from_dict_with_missing_type_raises_useful_error() -> None: item_dict = {"stac_version": "0.8.0", "id": "lalalalala"} with pytest.raises(pystac.STACTypeError, match="'type' is missing"): - Item.from_dict(item_dict) + Item.from_dict(item_dict, migrate=False) @pytest.mark.parametrize("add_canonical", (True, False)) @@ -721,3 +721,12 @@ def test_copy_with_unresolveable_root(item: Item) -> None: def test_no_collection(item: Item) -> None: # https://github.com/stac-utils/stac-api-validator/issues/527 assert item.collection is None + + +def test_migrate_by_default() -> None: + with open( + TestCases.get_path("data-files/projection/example-with-version-1.1.json") + ) as f: + data = json.load(f) + item = pystac.Item.from_dict(data) # default used to be migrate=False + assert item.ext.proj.code == "EPSG:32614" diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index aa6af7c7e..03e5fbfd6 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -42,7 +42,7 @@ def _parse_times(a_dict: dict[str, Any]) -> None: a_dict[k] = a_dict[k].replace(microsecond=0) d1 = deepcopy(d) - d2 = stac_object_class.from_dict(d).to_dict() + d2 = stac_object_class.from_dict(d, migrate=False).to_dict() _parse_times(d1) _parse_times(d2) assert d1 == d2