Skip to content

copy.deepcopy memo lookup fails when __deepcopy__ returns None #154594

Description

@tonghuaroot

Bug description

copy.deepcopy uses None as the memo-miss sentinel (memo.get(d, None)), so when an object's __deepcopy__ returns None, the memo hit is indistinguishable from a miss. The object gets deep-copied again on every subsequent reference instead of once.

This was introduced by gh-132657 / PR #138429 which replaced the old _nil = [] sentinel with None for free-threading performance.

import copy

call_count = 0

class C:
    def __deepcopy__(self, memo):
        global call_count
        call_count += 1
        memo[id(self)] = None
        return None

obj = C()
copy.deepcopy([obj, obj, obj])
print(call_count)  # 3, expected 1

CPython versions tested on

main

Operating systems tested on

macOS

Metadata

Metadata

Assignees

No one assigned

    Labels

    pendingThe issue will be closed if no feedback is providedstdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions