diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 3f14a9e66226ea..d86d2a8722ae0e 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -6463,8 +6463,7 @@ def test_warnings_module_has_signatures(self): def test_weakref_module_has_signatures(self): import weakref - no_signature = {'ReferenceType', 'ref'} - self._test_module_has_signatures(weakref, no_signature) + self._test_module_has_signatures(weakref) def test_python_function_override_signature(self): def func(*args, **kwargs): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-15-12-10-00.gh-issue-87613.Wr4fvc.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-15-12-10-00.gh-issue-87613.Wr4fvc.rst new file mode 100644 index 00000000000000..8a57b1443365e7 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-15-12-10-00.gh-issue-87613.Wr4fvc.rst @@ -0,0 +1 @@ +Speed up creating :class:`weakref.ref` objects by adding a vectorcall implementation of the constructor generated by Argument Clinic. Patch by Pieter Eendebak. diff --git a/Objects/clinic/weakrefobject.c.h b/Objects/clinic/weakrefobject.c.h new file mode 100644 index 00000000000000..654c79dad256de --- /dev/null +++ b/Objects/clinic/weakrefobject.c.h @@ -0,0 +1,109 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + +PyDoc_STRVAR(weakref_ref_new__doc__, +"ReferenceType(ob, callback=None, /)\n" +"--\n" +"\n" +"Create a weak reference to ob.\n" +"\n" +"Calling the reference returns ob, or None once ob has been garbage\n" +"collected. If callback is given, it is called with the reference as\n" +"its only argument when ob is about to be finalized."); + +static PyObject * +weakref_ref_new_impl(PyTypeObject *type, PyObject *ob, PyObject *callback); + +static PyObject * +weakref_ref_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyTypeObject *base_tp = &_PyWeakref_RefType; + PyObject *ob; + PyObject *callback = NULL; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("ReferenceType", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("ReferenceType", PyTuple_GET_SIZE(args), 1, 2)) { + goto exit; + } + ob = PyTuple_GET_ITEM(args, 0); + if (PyTuple_GET_SIZE(args) < 2) { + goto skip_optional; + } + callback = PyTuple_GET_ITEM(args, 1); +skip_optional: + return_value = weakref_ref_new_impl(type, ob, callback); + +exit: + return return_value; +} + +static PyObject * +weakref_ref_vectorcall(PyObject *type, PyObject *const *args, + size_t nargsf, PyObject *kwnames) +{ + PyObject *return_value = NULL; + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + PyObject *ob; + PyObject *callback = NULL; + + assert(Py_Is(_PyType_CAST(type), &_PyWeakref_RefType)); + /* Make sure the type object is immutable: the generated + * vectorcall doesn't deal e.g. with users reassigning __init__. */ + assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE)); + if (!_PyArg_NoKwnames("ReferenceType", kwnames)) { + goto exit; + } + if (!_PyArg_CheckPositional("ReferenceType", nargs, 1, 2)) { + goto exit; + } + ob = args[0]; + if (nargs < 2) { + goto skip_optional; + } + callback = args[1]; +skip_optional: + return_value = weakref_ref_new_impl(_PyType_CAST(type), ob, callback); + +exit: + return return_value; +} + +static int +weakref_ref_init_impl(PyWeakReference *self, PyObject *ob, + PyObject *callback); + +static int +weakref_ref_init(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + PyTypeObject *base_tp = &_PyWeakref_RefType; + PyObject *ob; + PyObject *callback = Py_None; + + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && + !_PyArg_NoKeywords("ReferenceType", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("ReferenceType", PyTuple_GET_SIZE(args), 1, 2)) { + goto exit; + } + ob = PyTuple_GET_ITEM(args, 0); + if (PyTuple_GET_SIZE(args) < 2) { + goto skip_optional; + } + callback = PyTuple_GET_ITEM(args, 1); +skip_optional: + return_value = weakref_ref_init_impl((PyWeakReference *)self, ob, callback); + +exit: + return return_value; +} +/*[clinic end generated code: output=e49af92091232111 input=a9049054013a1b77]*/ diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index daeba5368b2166..1635632ca71336 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -7,6 +7,14 @@ #include "pycore_pystate.h" #include "pycore_weakref.h" // _PyWeakref_GET_REF() +#include "clinic/weakrefobject.c.h" + +/*[clinic input] +module weakref +class weakref.ReferenceType "PyWeakReference *" "&_PyWeakref_RefType" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=25211e4a7d687f3f]*/ + #ifdef Py_GIL_DISABLED /* * Thread-safety for free-threaded builds @@ -459,35 +467,44 @@ get_or_create_weakref(PyTypeObject *type, PyObject *obj, PyObject *callback) } } -static int -parse_weakref_init_args(const char *funcname, PyObject *args, PyObject *kwargs, - PyObject **obp, PyObject **callbackp) -{ - return PyArg_UnpackTuple(args, funcname, 1, 2, obp, callbackp); -} +/*[clinic input] +@classmethod +@vectorcall +weakref.ReferenceType.__new__ as weakref_ref_new + + ob: object + callback: object(c_default="NULL") = None + / + +Create a weak reference to ob. + +Calling the reference returns ob, or None once ob has been garbage +collected. If callback is given, it is called with the reference as +its only argument when ob is about to be finalized. +[clinic start generated code]*/ static PyObject * -weakref___new__(PyTypeObject *type, PyObject *args, PyObject *kwargs) +weakref_ref_new_impl(PyTypeObject *type, PyObject *ob, PyObject *callback) +/*[clinic end generated code: output=689332971ea485ce input=f9264872536c6be7]*/ { - PyObject *ob, *callback = NULL; - if (parse_weakref_init_args("__new__", args, kwargs, &ob, &callback)) { - return (PyObject *)get_or_create_weakref(type, ob, callback); - } - return NULL; + return (PyObject *)get_or_create_weakref(type, ob, callback); } -static int -weakref___init__(PyObject *self, PyObject *args, PyObject *kwargs) -{ - PyObject *tmp; +/*[clinic input] +weakref.ReferenceType.__init__ as weakref_ref_init - if (!_PyArg_NoKeywords("ref", kwargs)) - return -1; + ob: object + callback: object = None + / +[clinic start generated code]*/ - if (parse_weakref_init_args("__init__", args, kwargs, &tmp, &tmp)) - return 0; - else - return -1; +static int +weakref_ref_init_impl(PyWeakReference *self, PyObject *ob, + PyObject *callback) +/*[clinic end generated code: output=57cec8c3671e296c input=7dd1cba6e88c7df8]*/ +{ + /* The arguments were already used by __new__; only validate them. */ + return 0; } @@ -508,6 +525,7 @@ _PyWeakref_RefType = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "weakref.ReferenceType", .tp_basicsize = sizeof(PyWeakReference), + .tp_doc = weakref_ref_new__doc__, .tp_dealloc = weakref_dealloc, .tp_vectorcall_offset = offsetof(PyWeakReference, vectorcall), .tp_call = PyVectorcall_Call, @@ -520,10 +538,11 @@ _PyWeakref_RefType = { .tp_richcompare = weakref_richcompare, .tp_methods = weakref_methods, .tp_members = weakref_members, - .tp_init = weakref___init__, + .tp_init = weakref_ref_init, .tp_alloc = PyType_GenericAlloc, - .tp_new = weakref___new__, + .tp_new = weakref_ref_new, .tp_free = PyObject_GC_Del, + .tp_vectorcall = weakref_ref_vectorcall, };