Add copyreg.copy() and copyreg.deepcopy()

I propose to add two registration functions to copyreg, following the copyreg.pickle() precedent, for customizing copying of instances of types that cannot be modified:

copyreg.copy(SomeType, copy_function)          # like __copy__
copyreg.deepcopy(SomeType, deepcopy_function)  # like __deepcopy__

# e.g. treat an external library's immutable objects as atomic:
copyreg.deepcopy(thirdparty.Handle, lambda obj, memo: obj)

copy.copy() and copy.deepcopy() use the registered functions in preference to the special methods and the pickle interfaces. Registration is by exact type and does not affect pickling — today the only out-of-class hook is copyreg.pickle(), which forces copy and pickle semantics to be the same.

This replaces the non-public way: the private _copy_dispatch and _deepcopy_dispatch tables have been monkey-patched in the wild for lack of a public API — even by our own test suite. The new public tables take their place (_deepcopy_dispatch is removed, as _copy_dispatch already was in 3.14).

This was requested in gh-141737, which was closed pending a discussion here. It is also the companion of PEP 837 (copyreg.json()): together they make copyreg not only the pickle registry module, but the general registry module — the place to register protocol implementations for types you cannot modify.

Issue: gh-153612, implementation: python/cpython#153613.

1 Like