From e97e3ec3bf9822031fd592581d31becec71f3d9b Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Sat, 31 Jan 2026 19:27:51 +0530 Subject: [PATCH] Initialize staticmethod/classmethod callables in tp_new --- Lib/test/test_descr.py | 18 ++++++++++++++++++ Objects/funcobject.c | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 82a48ad4d1aced..851d6e54384fa7 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -1816,6 +1816,24 @@ def test_refleaks_in_staticmethod___init__(self): sm.__init__(None) self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10) + def test_staticmethod_new_none_repr(self): + sm = staticmethod.__new__(staticmethod, None) + self.assertIsInstance(repr(sm), str) + + def test_classmethod_new_none_repr(self): + cm = classmethod.__new__(classmethod, None) + self.assertIsInstance(repr(cm), str) + + def test_staticmethod_func_readonly(self): + sm = staticmethod(lambda x: x) + with self.assertRaises(AttributeError): + sm.__func__ = None + + def test_classmethod_func_readonly(self): + cm = classmethod(lambda x: x) + with self.assertRaises(AttributeError): + cm.__func__ = None + @support.impl_detail("the module 'xxsubtype' is internal") @unittest.skipIf(xxsubtype is None, "requires xxsubtype module") def test_staticmethods_in_c(self): diff --git a/Objects/funcobject.c b/Objects/funcobject.c index b659ac8023373b..4a4968037ad90d 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -1555,11 +1555,15 @@ static PyMethodDef cm_methodlist[] = { {NULL} /* Sentinel */ }; +static PyObject *cm_new(PyTypeObject *type, PyObject *args, PyObject *kwds); +static PyObject *sm_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + static PyObject* cm_repr(PyObject *self) { classmethod *cm = _PyClassMethod_CAST(self); - return PyUnicode_FromFormat("", cm->cm_callable); + PyObject *callable = cm->cm_callable != NULL ? cm->cm_callable : Py_None; + return PyUnicode_FromFormat("", callable); } PyDoc_STRVAR(classmethod_doc, @@ -1623,7 +1627,7 @@ PyTypeObject PyClassMethod_Type = { offsetof(classmethod, cm_dict), /* tp_dictoffset */ cm_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ + cm_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ }; @@ -1638,6 +1642,17 @@ PyClassMethod_New(PyObject *callable) return (PyObject *)cm; } +static PyObject * +cm_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + classmethod *cm = (classmethod *)PyType_GenericAlloc(type, 0); + if (cm == NULL) + return NULL; + cm->cm_callable = Py_NewRef(Py_None); + cm->cm_dict = NULL; + return (PyObject *)cm; +} + /* Static method object */ @@ -1796,7 +1811,8 @@ static PyObject* sm_repr(PyObject *self) { staticmethod *sm = _PyStaticMethod_CAST(self); - return PyUnicode_FromFormat("", sm->sm_callable); + PyObject *callable = sm->sm_callable != NULL ? sm->sm_callable : Py_None; + return PyUnicode_FromFormat("", callable); } PyDoc_STRVAR(staticmethod_doc, @@ -1858,7 +1874,7 @@ PyTypeObject PyStaticMethod_Type = { offsetof(staticmethod, sm_dict), /* tp_dictoffset */ sm_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ + sm_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ }; @@ -1872,3 +1888,14 @@ PyStaticMethod_New(PyObject *callable) } return (PyObject *)sm; } + +static PyObject * +sm_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + staticmethod *sm = (staticmethod *)PyType_GenericAlloc(type, 0); + if (sm == NULL) + return NULL; + sm->sm_callable = Py_NewRef(Py_None); + sm->sm_dict = NULL; + return (PyObject *)sm; +}