diff --git a/box/box_list.py b/box/box_list.py index 72609ff..6dd6509 100644 --- a/box/box_list.py +++ b/box/box_list.py @@ -169,11 +169,14 @@ def __copy__(self): return self.__class__((x for x in self), **self.box_options) def __deepcopy__(self, memo=None): - out = self.__class__() + frozen = self.box_options.get("frozen_box") + out = self.__class__(**{**self.box_options, "frozen_box": False}) memo = memo or {} memo[id(self)] = out for k in self: out.append(copy.deepcopy(k, memo=memo)) + if frozen: + out.__init__(**self.box_options) return out def __hash__(self) -> int: # type: ignore[override] diff --git a/test/test_box_list.py b/test/test_box_list.py index 9cd90e0..71067c3 100644 --- a/test/test_box_list.py +++ b/test/test_box_list.py @@ -74,6 +74,28 @@ def test_frozen_list(self): bl2[1] = 4 assert bl2[1] == 4 + def test_deepcopy_preserves_box_options(self): + import copy + + # deepcopy must preserve box_options, matching __copy__ and Box.__deepcopy__ + for options in ( + {"frozen_box": True}, + {"box_dots": True}, + {"default_box": True}, + {"box_intact_types": (dict,)}, + ): + bl = BoxList([{"a": 1}], **options) + key = list(options)[0] + assert copy.copy(bl).box_options.get(key) == options[key] + assert copy.deepcopy(bl).box_options.get(key) == options[key] + + # a frozen BoxList must stay frozen (immutable + hashable) after deepcopy + frozen = BoxList([5, 4, 3], frozen_box=True) + clone = copy.deepcopy(frozen) + with pytest.raises(BoxError): + clone.append(1) + assert hash(clone) == hash(frozen) + def test_box_list_to_json(self): bl = BoxList([{"item": 1, "CamelBad": 2}]) assert json.loads(bl.to_json())[0]["item"] == 1