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

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    performancePerformance or resource usagestdlibStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancement

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions