python / cpython

The Python programming language
https://www.python.org
Other
63.51k stars 30.42k forks source link

deepcopy of weakref proxies #87418

Open 6f7117d2-587d-4aa8-8861-178ebfcbf9a6 opened 3 years ago

6f7117d2-587d-4aa8-8861-178ebfcbf9a6 commented 3 years ago
BPO 43252
Nosy @iritkatriel, @konrad-schwarz

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields: ```python assignee = None closed_at = None created_at = labels = ['type-bug', 'library', '3.9'] title = 'deepcopy of weakref proxies' updated_at = user = 'https://github.com/konrad-schwarz' ``` bugs.python.org fields: ```python activity = actor = 'konrad.schwarz' assignee = 'none' closed = False closed_date = None closer = None components = ['Library (Lib)'] creation = creator = 'konrad.schwarz' dependencies = [] files = [] hgrepos = [] issue_num = 43252 keywords = [] message_count = 3.0 messages = ['387226', '391094', '391121'] nosy_count = 2.0 nosy_names = ['iritkatriel', 'konrad.schwarz'] pr_nums = [] priority = 'normal' resolution = None stage = None status = 'open' superseder = None type = 'behavior' url = 'https://bugs.python.org/issue43252' versions = ['Python 3.9'] ```

6f7117d2-587d-4aa8-8861-178ebfcbf9a6 commented 3 years ago

copy.deepcopy()-ing a tree structure that has internal parent and cross-reference links implemented as weakref.proxy() objects causes the weak reference proxies themselves to be copied (still refering to their original referents) rather than weak references to deep-copied referents to be created.

A workaround is to add the following __deepcopy method to affected classes: def __deepcopy (self, memo): # taken from Stackoverflow: "How to override the # copy deepcopy operations for a python object" # and "Python: dereferencing weakproxy" cls = self.__class result = cls.__new (cls) memo [id (self)] = result for k, v in self.__dict.items (): if isinstance (v, weakref.ProxyType): newv = weakref.proxy (copy.deepcopy ( v.\_repr.__self__, memo)) else: new_v = copy.deepcopy (v, memo) setattr (result, k, new_v) return result

iritkatriel commented 3 years ago

If you deep-copy the referents, what would hold a reference to them? Wouldn't the new objects be immediately GCed?

6f7117d2-587d-4aa8-8861-178ebfcbf9a6 commented 3 years ago

Well, in the example code, the memo dictionary contains the (hard) reference to newly created objects but ultimately, at the close of the deepcopy, the objects are (hard) referenced using the normal "child" attributes.

I don't however know how this would work if this was implemented within the weakref code but I expect that their __deepcopy__ method also has access to the memo variable. If, at the close of deepcopy and the destruction of memo, no (hard) references exist to an object, then it would be collected, but that matches standard weak reference semantics.

Even if it were not possible to solve this programmatically within Python, I think the problem (and e.g. the workaround given below) should be mentioned in the documentation of deepcopy and/or weakref.