Batu Lab NotesPractical developer guides

Detect when shallow copies fail to isolate nested test data

By Batu ยท English technical notes

Also published in our primary archive.

A shallow copy of an outer dictionary does not isolate a nested list: use copy.deepcopy when the fixture requires independent nested mutable values. A shallow dictionary copy creates a new dictionary, but it retains references to the values it contains.

The fixture starts with {"labels": ["fast"]}. template.copy() creates shallow, yet both dictionaries still refer to the same list at the "labels" key. Appending "checked" through shallow therefore changes template; the first assertion records that shared-state result. Next, deepcopy(template) recursively copies the nested list. Appending "isolated" through separate changes only that deep copy. The remaining assertions compare the original and copied lists directly, making the alias boundary visible in exact output.

This distinction matters for test setup: a test that mutates a shallow-copy fixture can leak state into the template or another case that shares the inner object. Deep copying has trade-offs for custom objects, recursive structures, and objects that intentionally preserve shared references, so use it when independent fixture state is the requirement rather than as a blanket substitute for understanding the data shape.

The official copy.deepcopy documentation describes recursive copying and its memo mechanism for recursive objects. No newer API version requirement applies to this long-standing standard-library function.

AI assistance disclosure: This article was drafted with AI assistance and checked using the deterministic fixture below.

from copy import deepcopy


template = {"labels": ["fast"]}
shallow = template.copy()
shallow["labels"].append("checked")

assert template["labels"] == ["fast", "checked"]

separate = deepcopy(template)
separate["labels"].append("isolated")

assert template["labels"] == ["fast", "checked"]
assert separate["labels"] == ["fast", "checked", "isolated"]

print("after shallow copy: fast,checked")
print("after deepcopy original: fast,checked")
print("after deepcopy copy: fast,checked,isolated")
after shallow copy: fast,checked
after deepcopy original: fast,checked
after deepcopy copy: fast,checked,isolated