Skip to content

Commit b201ba3

Browse files
committed
gh-144330: Initialize classmethod and staticmethod in new
Move classmethod and staticmethod initialization from __init__() to __new__(). PyClassMethod_New() and PyStaticMethod_New() now copy attributes of the wrapped functions: __module__, __name__, __qualname__ and __doc__. Change static type initialization: initialize PyStaticMethod_Type and PyCFunction_Type earlier. Remove test_refleaks_in_classmethod___init__() and test_refleaks_in_staticmethod___init__() tests from test_descr since classmethod and staticmethod have no __init__() method anymore.
1 parent 34e5a63 commit b201ba3

File tree

4 files changed

+89
-58
lines changed

4 files changed

+89
-58
lines changed

Lib/test/test_descr.py

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,6 +1727,18 @@ def annotated(cls) -> int: pass
17271727
del method.__annotate__
17281728
self.assertIs(method.__annotate__, original_annotate)
17291729

1730+
def test_classmethod_without_dict_access(self):
1731+
class Spam:
1732+
@classmethod
1733+
def method(cls, x, y):
1734+
pass
1735+
1736+
obj = Spam.__dict__['method']
1737+
self.assertIsInstance(obj, classmethod)
1738+
self.assertEqual(obj.__annotations__, {})
1739+
self.assertEqual(obj.__name__, 'method')
1740+
self.assertEqual(obj.__module__, __name__)
1741+
17301742
def test_staticmethod_annotations_without_dict_access(self):
17311743
# gh-125017: this used to crash
17321744
class Spam:
@@ -1737,15 +1749,8 @@ def __new__(cls, x, y):
17371749
obj = Spam.__dict__['__new__']
17381750
self.assertIsInstance(obj, staticmethod)
17391751
self.assertEqual(obj.__annotations__, {})
1740-
1741-
@support.refcount_test
1742-
def test_refleaks_in_classmethod___init__(self):
1743-
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
1744-
cm = classmethod(None)
1745-
refs_before = gettotalrefcount()
1746-
for i in range(100):
1747-
cm.__init__(None)
1748-
self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
1752+
self.assertEqual(obj.__name__, '__new__')
1753+
self.assertEqual(obj.__module__, __name__)
17491754

17501755
@support.impl_detail("the module 'xxsubtype' is internal")
17511756
@unittest.skipIf(xxsubtype is None, "requires xxsubtype module")
@@ -1822,15 +1827,6 @@ class D(C):
18221827
del sm.x
18231828
self.assertNotHasAttr(sm, "x")
18241829

1825-
@support.refcount_test
1826-
def test_refleaks_in_staticmethod___init__(self):
1827-
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
1828-
sm = staticmethod(None)
1829-
refs_before = gettotalrefcount()
1830-
for i in range(100):
1831-
sm.__init__(None)
1832-
self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
1833-
18341830
@support.impl_detail("the module 'xxsubtype' is internal")
18351831
@unittest.skipIf(xxsubtype is None, "requires xxsubtype module")
18361832
def test_staticmethods_in_c(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Move ``classmethod`` and ``staticmethod`` initialization from ``__init__()``
2+
to ``__new__()``. Patch by Victor Stinner.

Objects/funcobject.c

Lines changed: 68 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,35 +1466,46 @@ static PyObject *
14661466
cm_descr_get(PyObject *self, PyObject *obj, PyObject *type)
14671467
{
14681468
classmethod *cm = (classmethod *)self;
1469-
1470-
if (cm->cm_callable == NULL) {
1471-
PyErr_SetString(PyExc_RuntimeError,
1472-
"uninitialized classmethod object");
1473-
return NULL;
1474-
}
14751469
if (type == NULL)
14761470
type = (PyObject *)(Py_TYPE(obj));
14771471
return PyMethod_New(cm->cm_callable, type);
14781472
}
14791473

14801474
static int
1481-
cm_init(PyObject *self, PyObject *args, PyObject *kwds)
1475+
cm_init(classmethod *cm, PyObject *callable)
14821476
{
1483-
classmethod *cm = (classmethod *)self;
1484-
PyObject *callable;
1485-
1486-
if (!_PyArg_NoKeywords("classmethod", kwds))
1487-
return -1;
1488-
if (!PyArg_UnpackTuple(args, "classmethod", 1, 1, &callable))
1489-
return -1;
1490-
Py_XSETREF(cm->cm_callable, Py_NewRef(callable));
1477+
assert(cm->cm_callable == NULL);
1478+
assert(callable != NULL);
1479+
cm->cm_callable = Py_NewRef(callable);
14911480

14921481
if (functools_wraps((PyObject *)cm, cm->cm_callable) < 0) {
14931482
return -1;
14941483
}
14951484
return 0;
14961485
}
14971486

1487+
static PyObject *
1488+
cm_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1489+
{
1490+
if (!_PyArg_NoKeywords("classmethod", kwds)) {
1491+
return NULL;
1492+
}
1493+
PyObject *callable; // borrowed ref
1494+
if (!PyArg_UnpackTuple(args, "classmethod", 1, 1, &callable)) {
1495+
return NULL;
1496+
}
1497+
1498+
classmethod *cm = (classmethod *)PyType_GenericAlloc(type, 0);
1499+
if (cm == NULL) {
1500+
return NULL;
1501+
}
1502+
if (cm_init(cm, callable) < 0) {
1503+
Py_DECREF(cm);
1504+
return NULL;
1505+
}
1506+
return (PyObject *)cm;
1507+
}
1508+
14981509
static PyMemberDef cm_memberlist[] = {
14991510
{"__func__", _Py_T_OBJECT, offsetof(classmethod, cm_callable), Py_READONLY},
15001511
{"__wrapped__", _Py_T_OBJECT, offsetof(classmethod, cm_callable), Py_READONLY},
@@ -1621,9 +1632,9 @@ PyTypeObject PyClassMethod_Type = {
16211632
cm_descr_get, /* tp_descr_get */
16221633
0, /* tp_descr_set */
16231634
offsetof(classmethod, cm_dict), /* tp_dictoffset */
1624-
cm_init, /* tp_init */
1635+
0, /* tp_init */
16251636
PyType_GenericAlloc, /* tp_alloc */
1626-
PyType_GenericNew, /* tp_new */
1637+
cm_new, /* tp_new */
16271638
PyObject_GC_Del, /* tp_free */
16281639
};
16291640

@@ -1632,8 +1643,12 @@ PyClassMethod_New(PyObject *callable)
16321643
{
16331644
classmethod *cm = (classmethod *)
16341645
PyType_GenericAlloc(&PyClassMethod_Type, 0);
1635-
if (cm != NULL) {
1636-
cm->cm_callable = Py_NewRef(callable);
1646+
if (cm == NULL) {
1647+
return NULL;
1648+
}
1649+
if (cm_init(cm, callable) < 0) {
1650+
Py_DECREF(cm);
1651+
return NULL;
16371652
}
16381653
return (PyObject *)cm;
16391654
}
@@ -1699,33 +1714,44 @@ static PyObject *
16991714
sm_descr_get(PyObject *self, PyObject *obj, PyObject *type)
17001715
{
17011716
staticmethod *sm = (staticmethod *)self;
1702-
1703-
if (sm->sm_callable == NULL) {
1704-
PyErr_SetString(PyExc_RuntimeError,
1705-
"uninitialized staticmethod object");
1706-
return NULL;
1707-
}
17081717
return Py_NewRef(sm->sm_callable);
17091718
}
17101719

17111720
static int
1712-
sm_init(PyObject *self, PyObject *args, PyObject *kwds)
1721+
sm_init(staticmethod *sm, PyObject *callable)
17131722
{
1714-
staticmethod *sm = (staticmethod *)self;
1715-
PyObject *callable;
1716-
1717-
if (!_PyArg_NoKeywords("staticmethod", kwds))
1718-
return -1;
1719-
if (!PyArg_UnpackTuple(args, "staticmethod", 1, 1, &callable))
1720-
return -1;
1721-
Py_XSETREF(sm->sm_callable, Py_NewRef(callable));
1723+
assert(sm->sm_callable == NULL);
1724+
assert(callable != NULL);
1725+
sm->sm_callable = Py_NewRef(callable);
17221726

17231727
if (functools_wraps((PyObject *)sm, sm->sm_callable) < 0) {
17241728
return -1;
17251729
}
17261730
return 0;
17271731
}
17281732

1733+
static PyObject *
1734+
sm_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1735+
{
1736+
if (!_PyArg_NoKeywords("staticmethod", kwds)) {
1737+
return NULL;
1738+
}
1739+
PyObject *callable; // borrowed ref
1740+
if (!PyArg_UnpackTuple(args, "staticmethod", 1, 1, &callable)) {
1741+
return NULL;
1742+
}
1743+
1744+
staticmethod *sm = (staticmethod *)PyType_GenericAlloc(type, 0);
1745+
if (sm == NULL) {
1746+
return NULL;
1747+
}
1748+
if (sm_init(sm, callable) < 0) {
1749+
Py_DECREF(sm);
1750+
return NULL;
1751+
}
1752+
return (PyObject *)sm;
1753+
}
1754+
17291755
static PyObject*
17301756
sm_call(PyObject *callable, PyObject *args, PyObject *kwargs)
17311757
{
@@ -1856,9 +1882,9 @@ PyTypeObject PyStaticMethod_Type = {
18561882
sm_descr_get, /* tp_descr_get */
18571883
0, /* tp_descr_set */
18581884
offsetof(staticmethod, sm_dict), /* tp_dictoffset */
1859-
sm_init, /* tp_init */
1885+
0, /* tp_init */
18601886
PyType_GenericAlloc, /* tp_alloc */
1861-
PyType_GenericNew, /* tp_new */
1887+
sm_new, /* tp_new */
18621888
PyObject_GC_Del, /* tp_free */
18631889
};
18641890

@@ -1867,8 +1893,12 @@ PyStaticMethod_New(PyObject *callable)
18671893
{
18681894
staticmethod *sm = (staticmethod *)
18691895
PyType_GenericAlloc(&PyStaticMethod_Type, 0);
1870-
if (sm != NULL) {
1871-
sm->sm_callable = Py_NewRef(callable);
1896+
if (sm == NULL) {
1897+
return NULL;
1898+
}
1899+
if (sm_init(sm, callable) < 0) {
1900+
Py_DECREF(sm);
1901+
return NULL;
18721902
}
18731903
return (PyObject *)sm;
18741904
}

Objects/object.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2446,13 +2446,17 @@ static PyTypeObject* static_types[] = {
24462446
&PyBaseObject_Type,
24472447
&PyType_Type,
24482448

2449+
// PyStaticMethod_Type and PyCFunction_Type are used by PyType_Ready()
2450+
// on other types and so must be initialized first.
2451+
&PyStaticMethod_Type,
2452+
&PyCFunction_Type,
2453+
24492454
// Static types with base=&PyBaseObject_Type
24502455
&PyAsyncGen_Type,
24512456
&PyByteArrayIter_Type,
24522457
&PyByteArray_Type,
24532458
&PyBytesIter_Type,
24542459
&PyBytes_Type,
2455-
&PyCFunction_Type,
24562460
&PyCallIter_Type,
24572461
&PyCapsule_Type,
24582462
&PyCell_Type,
@@ -2509,7 +2513,6 @@ static PyTypeObject* static_types[] = {
25092513
&PySetIter_Type,
25102514
&PySet_Type,
25112515
&PySlice_Type,
2512-
&PyStaticMethod_Type,
25132516
&PyStdPrinter_Type,
25142517
&PySuper_Type,
25152518
&PyTraceBack_Type,

0 commit comments

Comments
 (0)