diff --git a/pyproject.toml b/pyproject.toml index e7e576b0..c67ea911 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta" [project] name = "ops-scenario" -version = "4.0.1" +version = "4.0.2" authors = [ { name = "Pietro Pasotti", email = "pietro.pasotti@canonical.com" } diff --git a/scenario/state.py b/scenario/state.py index 024721d8..42ee70dd 100644 --- a/scenario/state.py +++ b/scenario/state.py @@ -80,9 +80,11 @@ class StateValidationError(RuntimeError): @dataclasses.dataclass(frozen=True) class _DCBase: def replace(self, *args, **kwargs): - return dataclasses.replace(self, *args, **kwargs) + """Produce a deep copy of this class, with some arguments replaced with new ones.""" + return dataclasses.replace(self.copy(), *args, **kwargs) def copy(self) -> "Self": + """Produce a deep copy of this object.""" return copy.deepcopy(self) diff --git a/tests/test_consistency_checker.py b/tests/test_consistency_checker.py index c5000d44..a1bf26b4 100644 --- a/tests/test_consistency_checker.py +++ b/tests/test_consistency_checker.py @@ -287,7 +287,7 @@ def test_relation_without_endpoint(): assert_consistent( State( - relations=[Relation("foo", relation_id=1), Relation("bar", relation_id=1)] + relations=[Relation("foo", relation_id=1), Relation("bar", relation_id=2)] ), Event("start"), _CharmSpec( diff --git a/tests/test_dcbase.py b/tests/test_dcbase.py new file mode 100644 index 00000000..fd5ff872 --- /dev/null +++ b/tests/test_dcbase.py @@ -0,0 +1,41 @@ +import dataclasses +from typing import Dict, List + +from scenario.state import _DCBase + + +@dataclasses.dataclass(frozen=True) +class Foo(_DCBase): + a: int + b: List[int] + c: Dict[int, List[int]] + + +def test_base_case(): + l = [1, 2] + l1 = [1, 2, 3] + d = {1: l1} + f = Foo(1, l, d) + g = f.replace(a=2) + + assert g.a == 2 + assert g.b == l + assert g.c == d + assert g.c[1] == l1 + + +def test_dedup_on_replace(): + l = [1, 2] + l1 = [1, 2, 3] + d = {1: l1} + f = Foo(1, l, d) + g = f.replace(a=2) + + l.append(3) + l1.append(4) + d[2] = "foobar" + + assert g.a == 2 + assert g.b == [1, 2] + assert g.c == {1: [1, 2, 3]} + assert g.c[1] == [1, 2, 3]