Skip to content

fix: migrate by default #1509

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion pystac/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion pystac/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pystac/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions pystac/stac_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
Expand Down
7 changes: 4 additions & 3 deletions pystac/stac_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions tests/extensions/test_classification.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/extensions/test_eo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/extensions/test_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/extensions/test_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/extensions/test_raster.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions tests/test_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand Down
15 changes: 12 additions & 3 deletions tests/test_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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))
Expand Down Expand Up @@ -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"
2 changes: 1 addition & 1 deletion tests/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading