Skip to content

Commit

Permalink
Deepcopy Collection properties on clone (stac-utils#794)
Browse files Browse the repository at this point in the history
* Make sure that Collections deepcopy their properties on clone

Implements `clone` method on Summaries as well.

* Fix broken test

* Fix mypy errors

* Add changelog entry

* fix: minor formatting on changelog

* Use copy() method to make copies of list of strings

deepcopy() is unnecessary on properties that are only list of strings

Co-authored-by: Pete Gadomski <[email protected]>
  • Loading branch information
sunu and gadomski authored Apr 29, 2022
1 parent a6c8927 commit 876b60a
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

- "How to create STAC catalogs" tutorial ([#775](https://github.com/stac-utils/pystac/pull/775))
- Add a `variables` argument, to accompany `dimensions`, for the `apply` method of stac objects extended with datacube ([#782](https://github.com/stac-utils/pystac/pull/782))
- Deepcopy collection properties on clone. Implement `clone` method for `Summaries` ([#794](https://github.com/stac-utils/pystac/pull/794))

## [v1.4.0]

Expand Down
2 changes: 1 addition & 1 deletion pystac/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ def clone(self) -> "Catalog":
id=self.id,
description=self.description,
title=self.title,
stac_extensions=self.stac_extensions,
stac_extensions=self.stac_extensions.copy(),
extra_fields=deepcopy(self.extra_fields),
catalog_type=self.catalog_type,
)
Expand Down
10 changes: 5 additions & 5 deletions pystac/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,13 +562,13 @@ def clone(self) -> "Collection":
description=self.description,
extent=self.extent.clone(),
title=self.title,
stac_extensions=self.stac_extensions,
extra_fields=self.extra_fields,
stac_extensions=self.stac_extensions.copy(),
extra_fields=deepcopy(self.extra_fields),
catalog_type=self.catalog_type,
license=self.license,
keywords=self.keywords,
providers=self.providers,
summaries=self.summaries,
keywords=self.keywords.copy() if self.keywords is not None else None,
providers=deepcopy(self.providers),
summaries=self.summaries.clone(),
)

clone._resolved_objects.cache(clone)
Expand Down
16 changes: 16 additions & 0 deletions pystac/summaries.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from copy import deepcopy
import sys
import numbers
from enum import Enum
Expand Down Expand Up @@ -287,6 +288,21 @@ def is_empty(self) -> bool:
any(self.lists) or any(self.ranges) or any(self.schemas) or any(self.other)
)

def clone(self) -> "Summaries":
"""Clones this object.
Returns:
Summaries: The clone of this object
"""
summaries = Summaries(
summaries=deepcopy(self._summaries), maxcount=self.maxcount
)
summaries.lists = deepcopy(self.lists)
summaries.other = deepcopy(self.other)
summaries.ranges = deepcopy(self.ranges)
summaries.schemas = deepcopy(self.schemas)
return summaries

def to_dict(self) -> Dict[str, Any]:
return {
**{k: v for k, v in self.lists.items() if len(v) < self.maxcount},
Expand Down
2 changes: 1 addition & 1 deletion tests/extensions/test_scientific.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ def test_set_doi_summaries(self) -> None:
sci_summaries = ScientificExtension.summaries(collection)

sci_summaries.doi = [PUB2_DOI]
new_dois = ScientificExtension.summaries(self.collection).doi
new_dois = ScientificExtension.summaries(collection).doi

assert new_dois is not None
self.assertListEqual([PUB2_DOI], new_dois)
Expand Down
13 changes: 13 additions & 0 deletions tests/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,19 @@ def test_clone_uses_previous_catalog_type(self) -> None:
clone = catalog.clone()
self.assertEqual(clone.catalog_type, CatalogType.SELF_CONTAINED)

def test_clone_cant_mutate_original(self) -> None:
collection = TestCases.test_case_8()
assert collection.keywords is not None
self.assertListEqual(collection.keywords, ["disaster", "open"])
clone = collection.clone()
clone.extra_fields["test"] = "extra"
self.assertNotIn("test", collection.extra_fields)
assert clone.keywords is not None
clone.keywords.append("clone")
self.assertListEqual(clone.keywords, ["disaster", "open", "clone"])
self.assertListEqual(collection.keywords, ["disaster", "open"])
self.assertNotEqual(id(collection.summaries), id(clone.summaries))

def test_multiple_extents(self) -> None:
cat1 = TestCases.test_case_1()
country = cat1.get_child("country-1")
Expand Down
11 changes: 11 additions & 0 deletions tests/test_summaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ def test_summary_not_empty(self) -> None:
summaries = Summarizer().summarize(coll.get_all_items())
self.assertFalse(summaries.is_empty())

def test_clone_summary(self) -> None:
coll = TestCases.test_case_5()
summaries = Summarizer().summarize(coll.get_all_items())
summaries_dict = summaries.to_dict()
self.assertEqual(len(summaries_dict["eo:bands"]), 4)
self.assertEqual(len(summaries_dict["proj:epsg"]), 1)
clone = summaries.clone()
self.assertTrue(isinstance(clone, Summaries))
clone_dict = clone.to_dict()
self.assertDictEqual(clone_dict, summaries_dict)


class RangeSummaryTest(unittest.TestCase):
def setUp(self) -> None:
Expand Down

0 comments on commit 876b60a

Please sign in to comment.