Skip to content

Commit 4052dd2

Browse files
committed
gh-141510: Fix copy.deepcopy() for recursive frozendict
1 parent beb8e3f commit 4052dd2

2 files changed

Lines changed: 19 additions & 1 deletion

File tree

Lib/copy.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,17 @@ def _deepcopy_dict(x, memo, deepcopy=deepcopy):
204204
d[dict] = _deepcopy_dict
205205

206206
def _deepcopy_frozendict(x, memo, deepcopy=deepcopy):
207-
y = _deepcopy_dict(x, memo, deepcopy)
207+
y = {}
208+
for key, value in x.items():
209+
y[deepcopy(key, memo)] = deepcopy(value, memo)
210+
211+
# We're not going to put the frozendict in the memo, but it's still
212+
# important we check for it, in case the frozendict contains recursive
213+
# mutable structures.
214+
try:
215+
return memo[id(x)]
216+
except KeyError:
217+
pass
208218
return frozendict(y)
209219
d[frozendict] = _deepcopy_frozendict
210220

Lib/test/test_copy.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,14 @@ def test_deepcopy_frozendict(self):
432432
self.assertIsNot(x, y)
433433
self.assertIsNot(x["foo"], y["foo"])
434434

435+
# recursive frozendict
436+
x = frozendict(foo=[])
437+
x['foo'].append(x)
438+
y = copy.deepcopy(x)
439+
self.assertEqual(y.keys(), x.keys())
440+
self.assertIsNot(x, y)
441+
self.assertIsNot(x["foo"], y["foo"])
442+
435443
@support.skip_emscripten_stack_overflow()
436444
@support.skip_wasi_stack_overflow()
437445
def test_deepcopy_reflexive_dict(self):

0 commit comments

Comments
 (0)