Skip to content

Commit 5539c2a

Browse files
gh-154594: Fix deepcopy memo lookup when __deepcopy__ returns None (#154595)
* gh-154594: Fix deepcopy memo lookup when __deepcopy__ returns None * Use 'd in memo' lookup instead of a sentinel Addresses review: clearer, no sentinel, and the test treats memo as opaque. * Apply review suggestions: shorten NEWS, use class attribute in test --------- Co-authored-by: Pieter Eendebak <pieter.eendebak@gmail.com>
1 parent a9e7895 commit 5539c2a

3 files changed

Lines changed: 15 additions & 3 deletions

File tree

Lib/copy.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,8 @@ def deepcopy(x, memo=None):
122122
if memo is None:
123123
memo = {}
124124
else:
125-
y = memo.get(d, None)
126-
if y is not None:
127-
return y
125+
if d in memo:
126+
return memo[d]
128127

129128
copier = _deepcopy_dispatch.get(cls)
130129
if copier is not None:

Lib/test/test_copy.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,17 @@ def m(self):
951951
self.assertIs(g.b.__self__, g)
952952
g.b()
953953

954+
def test_deepcopy_memo_none_result(self):
955+
# Objects whose deepcopy result is None must still be memoized.
956+
class C:
957+
call_count = 0
958+
def __deepcopy__(self, memo):
959+
C.call_count += 1
960+
return None
961+
obj = C()
962+
copy.deepcopy([obj, obj, obj])
963+
self.assertEqual(C.call_count, 1)
964+
954965

955966
class TestReplace(unittest.TestCase):
956967

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :func:`copy.deepcopy` so that an object whose deep copy is ``None`` is
2+
still memoized. Patch by tonghuaroot.

0 commit comments

Comments
 (0)