Skip to content

gh-74876: Support pickling weak references and weak containers - #153818

Merged
ambv merged 2 commits into
python:mainfrom
serhiy-storchaka:weakref-native-pickle
Oct 21, 2026
Merged

ambv merged 2 commits into
python:mainfrom
serhiy-storchaka:weakref-native-pickle

Conversation

@serhiy-storchaka

@serhiy-storchaka serhiy-storchaka commented Jul 16, 2026 •

Copy link
Copy Markdown
Member

Weak references and weak containers can now be pickled.

  • weakref.ref is pickled by the pickler itself, as weakref.ref(referent). The referent is pickled weakly: it must be strongly referenced elsewhere in the same pickle, otherwise pickling raises PicklingError. A reference to a dead object is pickled by name, as the new weakref._dead_ref singleton. The callback is not preserved.
  • WeakMethod can now be pickled and copied: it reduces to a weak reference to the object the method is bound to.
  • WeakSet, WeakValueDictionary and WeakKeyDictionary can now be pickled: they reduce their elements, values and keys to weak references. WeakSet can now also be copied.

Only existing opcodes are used, so no new pickle protocol is needed. Both the C and the Python pickler emit identical streams.

The detection of weak references without a strong reference in the pickle is a single conservative rule (fail-closed): a referent is tagged when it is first reached only through a weak reference, and any completed non-weak save of it clears the tag.

Pickling a proxy is out of scope here: making it round-trip as a proxy also changes copying, so it will be proposed separately in gh-62268.

🤖 Generated with Claude Code

* weakref.ref is pickled by the pickler itself, as weakref.ref(referent).
  The referent is pickled weakly: it must be strongly referenced elsewhere
  in the same pickle, otherwise pickling raises PicklingError.  A reference
  to a dead object is pickled by name, as the new weakref._dead_ref
  singleton.  The callback is not preserved.

* WeakMethod can now be pickled and copied: it reduces to a weak reference
  to the object the method is bound to.

* WeakSet, WeakValueDictionary and WeakKeyDictionary can now be pickled:
  they reduce their elements, values and keys to weak references.  WeakSet
  can now also be copied.

Only existing opcodes are used, so no new pickle protocol is needed.
The detection of weak references without a strong reference in the pickle
is conservative: valid but exotic reference graphs may be refused.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@read-the-docs-community

read-the-docs-community Bot commented Jul 16, 2026 •

Copy link
Copy Markdown

Co-authored-by: Muse Code (Meta Muse Spark)

# Conflicts:
#	Doc/library/weakref.rst
@ambv
ambv merged commit 1a4dfd1 into python:main Oct 21, 2026
56 checks passed
@bedevere-bot

Copy link
Copy Markdown

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot aarch64 Ubuntu Oddballs 3.x (tier-1) has failed when building commit 1a4dfd1.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/1868/builds/2601) and take a look at the build logs.
  4. Check if the failure is related to this commit (1a4dfd1) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/1868/builds/2601

Failed tests:

  • test_xpickle

Failed subtests:

  • test_weakref_callback - test.test_xpickle.CPicklePython37Compat.test_weakref_callback
  • test_weakref - test.test_xpickle.CPicklePython313Compat.test_weakref
  • test_dead_weakref - test.test_xpickle.PyPicklePython311Compat.test_dead_weakref
  • test_dead_weakref - test.test_xpickle.CPicklePython310Compat.test_dead_weakref
  • test_weakref_callback - test.test_xpickle.CPicklePython310Compat.test_weakref_callback
  • test_dead_weakref - test.test_xpickle.PyPicklePython310Compat.test_dead_weakref
  • test_weakref_callback - test.test_xpickle.CPicklePython313Compat.test_weakref_callback
  • test_weakref - test.test_xpickle.PyPicklePython312Compat.test_weakref
  • test_weakref_callback - test.test_xpickle.PyPicklePython313Compat.test_weakref_callback
  • test_weakref_callback - test.test_xpickle.CPicklePython311Compat.test_weakref_callback
  • test_weakref_callback - test.test_xpickle.CPicklePython312Compat.test_weakref_callback
  • test_weakref - test.test_xpickle.CPicklePython311Compat.test_weakref
  • test_weakref - test.test_xpickle.PyPicklePython37Compat.test_weakref
  • test_weakref_callback - test.test_xpickle.PyPicklePython37Compat.test_weakref_callback
  • test_dead_weakref - test.test_xpickle.PyPicklePython37Compat.test_dead_weakref
  • test_weakref - test.test_xpickle.PyPicklePython313Compat.test_weakref
  • test_weakref - test.test_xpickle.CPicklePython310Compat.test_weakref
  • test_weakref - test.test_xpickle.CPicklePython37Compat.test_weakref
  • test_weakref_callback - test.test_xpickle.PyPicklePython310Compat.test_weakref_callback
  • test_dead_weakref - test.test_xpickle.PyPicklePython313Compat.test_dead_weakref
  • test_weakref - test.test_xpickle.CPicklePython312Compat.test_weakref
  • test_dead_weakref - test.test_xpickle.CPicklePython37Compat.test_dead_weakref
  • test_weakref_callback - test.test_xpickle.PyPicklePython311Compat.test_weakref_callback
  • test_dead_weakref - test.test_xpickle.CPicklePython313Compat.test_dead_weakref
  • test_dead_weakref - test.test_xpickle.CPicklePython312Compat.test_dead_weakref
  • test_dead_weakref - test.test_xpickle.PyPicklePython312Compat.test_dead_weakref
  • test_dead_weakref - test.test_xpickle.CPicklePython311Compat.test_dead_weakref
  • test_weakref_callback - test.test_xpickle.PyPicklePython312Compat.test_weakref_callback
  • test_weakref - test.test_xpickle.PyPicklePython310Compat.test_weakref
  • test_weakref - test.test_xpickle.PyPicklePython311Compat.test_weakref

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/pickletester.py", line 3675, in test_dead_weakref
    a, b = self.loads(self.dumps([wr, wr2], proto))
                      ~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 190, in dumps
    return self.send_to_worker(python, data)
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 172, in send_to_worker
    raise exception
AttributeError: Can't get attribute '_dead_ref' on <module 'weakref' from '/usr/lib/python3.12/weakref.py'>


Traceback (most recent call last):
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/pickletester.py", line 3659, in test_weakref_callback
    obj2, wr2 = self.loads(self.dumps([obj, wr], proto))
                           ~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 190, in dumps
    return self.send_to_worker(python, data)
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 172, in send_to_worker
    raise exception
ModuleNotFoundError: No module named 'test.pickletester'; 'test' is not a package. Did you mean: 'test.xpickle_worker'?


Traceback (most recent call last):
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/pickletester.py", line 3675, in test_dead_weakref
    a, b = self.loads(self.dumps([wr, wr2], proto))
                      ~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 190, in dumps
    return self.send_to_worker(python, data)
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 172, in send_to_worker
    raise exception
AttributeError: Can't get attribute '_dead_ref' on <module 'weakref' from '/usr/lib/python3.10/weakref.py'>


Traceback (most recent call last):
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/pickletester.py", line 3675, in test_dead_weakref
    a, b = self.loads(self.dumps([wr, wr2], proto))
                      ~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 190, in dumps
    return self.send_to_worker(python, data)
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 172, in send_to_worker
    raise exception
AttributeError: Can't get attribute '_dead_ref' on <module 'weakref' from '/usr/lib/python3.11/weakref.py'>


Traceback (most recent call last):
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/pickletester.py", line 3645, in test_weakref
    obj2, wr2 = self.loads(self.dumps([obj, wr], proto))
                           ~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 190, in dumps
    return self.send_to_worker(python, data)
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 172, in send_to_worker
    raise exception
ModuleNotFoundError: No module named 'test.pickletester'; 'test' is not a package. Did you mean: 'test.xpickle_worker'?


Traceback (most recent call last):
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/pickletester.py", line 3675, in test_dead_weakref
    a, b = self.loads(self.dumps([wr, wr2], proto))
                      ~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 190, in dumps
    return self.send_to_worker(python, data)
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 172, in send_to_worker
    raise exception
AttributeError: Can't get attribute '_dead_ref' on <module 'weakref' from '/usr/lib/python3.7/weakref.py'>


Traceback (most recent call last):
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/pickletester.py", line 3675, in test_dead_weakref
    a, b = self.loads(self.dumps([wr, wr2], proto))
                      ~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 190, in dumps
    return self.send_to_worker(python, data)
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 172, in send_to_worker
    raise exception
AttributeError: Can't get attribute '_dead_ref' on <module 'weakref' from '/usr/lib/python3.13/weakref.py'>

@encukou

encukou commented Oct 21, 2026

Copy link
Copy Markdown
Member

@serhiy-storchaka, are you planning to fix the xpickle tests?

@vstinner

Copy link
Copy Markdown
Member

aarch64 Ubuntu Oddballs 3.x started to fail since this change: https://buildbot.python.org/#/builders/1868/builds/2601

Well, see the "Buildbot failure" comment above with the big warnings :-)

Example:

ERROR: test_weakref_callback (test.test_xpickle.CPicklePython313Compat.test_weakref_callback) (proto=1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/pickletester.py", line 3659, in test_weakref_callback
    obj2, wr2 = self.loads(self.dumps([obj, wr], proto))
                           ~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 190, in dumps
    return self.send_to_worker(python, data)
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/home/ubuntu/buildarea/3.x.stan-aarch64-ubuntu.oddballs/build/Lib/test/test_xpickle.py", line 172, in send_to_worker
    raise exception
ModuleNotFoundError: No module named 'test.pickletester'; 'test' is not a package. Did you mean: 'test.xpickle_worker'?

cc @StanFromIreland

@serhiy-storchaka
serhiy-storchaka deleted the weakref-native-pickle branch October 21, 2026 18:17
@serhiy-storchaka

Copy link
Copy Markdown
Member Author

Thank you. For now these tests should be skipped. #160082

@serhiy-storchaka

Copy link
Copy Markdown
Member Author

See an alternative approach in #160219.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants