diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index c5581041f044b1..8786d44f9444ff 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -6547,8 +6547,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 index 9b1917f5597d68..b014b14217c96c 100644 --- a/Objects/clinic/weakrefobject.c.h +++ b/Objects/clinic/weakrefobject.c.h @@ -3,6 +3,7 @@ preserve [clinic start generated code]*/ #include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() +#include "pycore_modsupport.h" // _PyArg_CheckPositional() static Py_hash_t weakref_hash_impl(PyWeakReference *self); @@ -18,4 +19,108 @@ weakref_hash(PyObject *self) return return_value; } -/*[clinic end generated code: output=40453fff9335a3f7 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(weakref_ref_new__doc__, +"ReferenceType(object, callback=None, /)\n" +"--\n" +"\n" +"Create a weak reference to object.\n" +"\n" +"Calling the reference returns object, or None once it has been garbage\n" +"collected. If callback is given, it is called with the reference as\n" +"its only argument when object is about to be finalized."); + +static PyObject * +weakref_ref_new_impl(PyTypeObject *type, PyObject *object, + PyObject *callback); + +static PyObject * +weakref_ref_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyTypeObject *base_tp = &_PyWeakref_RefType; + PyObject *object; + 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; + } + object = 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, object, 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 *object; + PyObject *callback = NULL; + + assert(_PyType_CAST(type)->tp_new == weakref_ref_new); + /* 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; + } + object = args[0]; + if (nargs < 2) { + goto skip_optional; + } + callback = args[1]; +skip_optional: + return_value = weakref_ref_new_impl(_PyType_CAST(type), object, callback); + +exit: + return return_value; +} + +static int +weakref_ref_init_impl(PyWeakReference *self, PyObject *object, + PyObject *callback); + +static int +weakref_ref_init(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + PyTypeObject *base_tp = &_PyWeakref_RefType; + PyObject *object; + 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; + } + object = 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, object, callback); + +exit: + return return_value; +} +/*[clinic end generated code: output=774282aa1017bab5 input=a9049054013a1b77]*/ diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 56007d4da12734..03411286a4add6 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -8,9 +8,10 @@ #include "pycore_weakref.h" // _PyWeakref_GET_REF() /*[clinic input] -class weakref "PyWeakReference *" "&_PyWeakref_RefType" +module weakref +class weakref.ReferenceType "PyWeakReference *" "&_PyWeakref_RefType" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ad66c8b0acee6ad9]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=25211e4a7d687f3f]*/ #include "clinic/weakrefobject.c.h" @@ -195,12 +196,12 @@ weakref_vectorcall(PyObject *self, PyObject *const *args, /*[clinic input] @critical_section -weakref.__hash__ +weakref.ReferenceType.__hash__ as weakref_hash [clinic start generated code]*/ static Py_hash_t weakref_hash_impl(PyWeakReference *self) -/*[clinic end generated code: output=4904eacaabf902c9 input=703596f4b120e47c]*/ +/*[clinic end generated code: output=4904eacaabf902c9 input=f248ec0b25c064e0]*/ { if (self->hash != -1) return self->hash; @@ -465,35 +466,45 @@ 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 + + object: object + callback: object(c_default="NULL") = None + / + +Create a weak reference to object. + +Calling the reference returns object, or None once it has been garbage +collected. If callback is given, it is called with the reference as +its only argument when object 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 *object, + PyObject *callback) +/*[clinic end generated code: output=0b84aee8668f4c2f input=0cd09b468ebb6371]*/ { - 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, object, 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; + object: 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 *object, + PyObject *callback) +/*[clinic end generated code: output=d36cfa9ccd36856e input=373a3e3f2841a4fa]*/ +{ + /* The arguments were already used by __new__; only validate them. */ + return 0; } @@ -514,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, @@ -526,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, };