Skip to content

Fix weakref.ref() with a callback accepting non-weakrefable objects - #1266

Open
jseop-lim wants to merge 2 commits into
oracle:masterfrom
jseop-lim:fix/weakref-callback-weakrefable
Open

jseop-lim wants to merge 2 commits into
oracle:masterfrom
jseop-lim:fix/weakref-callback-weakrefable

Conversation

@jseop-lim

Copy link
Copy Markdown
Contributor

Description

Fixes #1256

ReferenceTypeNode.refTypeWithCallback, used by weakref.ref(obj, cb) and weakref.proxy(obj, cb), skipped the getWeaklistoffset() check that refType does for a None callback, so dict, list, int, None and other builtin objects got a weak reference. CPython checks _PyType_SUPPORTS_WEAKREFS before it looks at the callback (weakrefobject.c#L405-L415).

getBuiltinTypeWeaklistoffset was also missing collections.deque, array.array, mmap.mmap, _contextvars.Context, _queue.SimpleQueue, _struct.Struct and the typing.TypeVar family. These worked only with a callback, so the check alone would have rejected them. With the entries, they now accept a weak reference without a callback too, as on CPython.

AS-IS

>>> weakref.proxy({}, callback)
<weakproxy at 1415; to 'dict' at 1419>
>>> weakref.ref(None, callback)
<weakref at 1753773164; to 'NoneType' at 1613378103>
>>> weakref.ref(d)   # d = collections.deque()
TypeError: cannot create weak reference to 'collections.deque' object

TO-BE

>>> weakref.proxy({}, callback)
TypeError: cannot create weak reference to 'dict' object
>>> weakref.ref(None, callback)
TypeError: cannot create weak reference to 'NoneType' object
>>> weakref.ref(d)
<weakref at 0x10632dfd0; to 'collections.deque' at 0x106351b70>

(matching CPython)

Changes

  • Raise TypeError in refTypeWithCallback when the object's class is a PythonBuiltinClassType with a zero getWeaklistoffset(), as refType does.
  • Add deque, array, mmap, Context, SimpleQueue and Struct to getBuiltinTypeWeaklistoffset with their CPython 3.13 offsets, and TypeVar, TypeVarTuple, ParamSpec, ParamSpecArgs and ParamSpecKwargs with the managed weakref offset -32.
  • Add test_weakref_with_callback_object_type_support and test_weakref_builtin_types_with_weaklist to tests/test_weakref.py.

Testing

mx python-jvm on darwin-aarch64. tests/test_weakref.py passes (13 tests), and both new tests fail on the unpatched build. The tagged lib-python suites test_weakref, test_weakset, test_typing, test_deque, test_array, test_mmap, test_queue, test_struct, test_context, test_functools, test_re and test_threading_local pass. Over 79 builtin, stdlib and user-defined objects, weakref.ref(obj), weakref.ref(obj, cb) and weakref.proxy(obj, cb) now succeed or raise TypeError exactly as on CPython 3.13.14, apart from a __slots__ = () class and operator.attrgetter, which GraalPy already accepted without a callback.

refTypeWithCallback skipped the weaklistoffset check that refType does,
so weakref.ref(obj, cb) and weakref.proxy(obj, cb) accepted dicts,
None and other builtin objects CPython cannot reference weakly.

Add the check, and give the builtin types that CPython does reference
weakly but that were missing from getBuiltinTypeWeaklistoffset their
CPython 3.13 offsets: deque, array, mmap, Context, SimpleQueue, Struct
and the TypeVar family. Without the entries the check would reject
them, and today they already fail without a callback.

Signed-off-by: Jeongseop Lim <jeongseop_lim@korea.ac.kr>
Each object and constructor gets its own assertion, so a failure
names the case, and the mmap is closed after use.

Signed-off-by: Jeongseop Lim <jeongseop_lim@korea.ac.kr>
@oracle-contributor-agreement oracle-contributor-agreement Bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label Oct 31, 2026

This branch has not been deployed

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

Labels

OCA Verified All contributors have signed the Oracle Contributor Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: weakref.ref() and weakref.proxy() with a callback accept objects that do not support weak references

1 participant