8000 gh-125017: Fix crash on premature access to classmethod/staticmethod … · python/cpython@f203d1c · GitHub
[go: up one dir, main page]

Skip to content

Commit f203d1c

Browse files
gh-125017: Fix crash on premature access to classmethod/staticmethod annotations (#125636)
1 parent 04d6dd2 commit f203d1c

File tree

3 files changed

+43
-14
lines changed

3 files changed

+43
-14
lines changed

Lib/test/test_descr.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,6 +1618,9 @@ def annotated(cls) -> int: pass
16181618

16191619
for method in (annotated, unannotated):
16201620
with self.subTest(deco=deco, method=method):
1621+
with self.assertRaises(AttributeError):
1622+
del unannotated.__annotations__
1623+
16211624
original_annotations = dict(method.__wrapped__.__annotations__)
16221625
self.assertNotIn('__annotations__', method.__dict__)
16231626
self.assertEqual(method.__annotations__, original_annotations)
@@ -1644,6 +1647,17 @@ def annotated(cls) -> int: pass
16441647
del method.__annotate__
16451648
self.assertIs(method.__annotate__, original_annotate)
16461649

1650+
def test_staticmethod_annotations_without_dict_access(self):
1651+
# gh-125017: this used to crash
1652+
class Spam:
1653+
def __new__(cls, x, y):
1654+
pass
1655+
1656+
self.assertEqual(Spam.__new__.__annotations__, {})
1657+
obj = Spam.__dict__['__new__']
1658+
self.assertIsInstance(obj, staticmethod)
1659+
self.assertEqual(obj.__annotations__, {})
1660+
16471661
@support.refcount_test
16481662
def test_refleaks_in_classmethod___init__(self):
16491663
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix crash on certain accesses to the ``__annotations__`` of
2+
:class:`staticmethod` and :class:`classmethod` objects.

Objects/funcobject.c

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,45 +1220,62 @@ functools_wraps(PyObject *wrapper, PyObject *wrapped)
12201220
// Used for wrapping __annotations__ and __annotate__ on classmethod
12211221
// and staticmethod objects.
12221222
static PyObject *
1223-
descriptor_get_wrapped_attribute(PyObject *wrapped, PyObject *dict, PyObject *name)
1223+
descriptor_get_wrapped_attribute(PyObject *wrapped, PyObject *obj, PyObject *name)
12241224
{
1225+
PyObject *dict = PyObject_GenericGetDict(obj, NULL);
1226+
if (dict == NULL) {
1227+
return NULL;
1228+
}
12251229
PyObject *res;
12261230
if (PyDict_GetItemRef(dict, name, &res) < 0) {
1231+
Py_DECREF(dict);
12271232
return NULL;
12281233
}
12291234
if (res != NULL) {
1235+
Py_DECREF(dict);
12301236
return res;
12311237
}
12321238
res = PyObject_GetAttr(wrapped, name);
12331239
if (res == NULL) {
1240+
Py_DECREF(dict);
12341241
return NULL;
12351242
}
12361243
if (PyDict_SetItem(dict, name, res) < 0) {
1244+
Py_DECREF(dict);
12371245
Py_DECREF(res);
12381246
return NULL;
12391247
}
1248+
Py_DECREF(dict);
12401249
return res;
12411250
}
12421251

12431252
static int
1244-
descriptor_set_wrapped_attribute(PyObject *dict, PyObject *name, PyObject *value,
1253+
descriptor_set_wrapped_attribute(PyObject *oobj, PyObject *name, PyObject *value,
12451254
char *type_name)
12461255
{
1256+
PyObject *dict = PyObject_GenericGetDict(oobj, NULL);
1257+
if (dict == NULL) {
1258+
return -1;
1259+
}
12471260
if (value == NULL) {
12481261
if (PyDict_DelItem(dict, name) < 0) {
12491262
if (PyErr_ExceptionMatches(PyExc_KeyError)) {
12501263
PyErr_Clear();
12511264
PyErr_Format(PyExc_AttributeError,
12521265
"'%.200s' object has no attribute '%U'",
12531266
type_name, name);
1267+
return -1;
12541268
}
12551269
else {
1270+
Py_DECREF(dict);
12561271
return -1;
12571272
}
12581273
}
1274+
Py_DECREF(dict);
12591275
return 0;
12601276
}
12611277
else {
1278+
Py_DECREF(dict);
12621279
return PyDict_SetItem(dict, name, value);
12631280
}
12641281
}
@@ -1380,28 +1397,26 @@ static PyObject *
13801397
cm_get___annotations__(PyObject *self, void *closure)
13811398
{
13821399
classmethod *cm = _PyClassMethod_CAST(self);
1383-
return descriptor_get_wrapped_attribute(cm->cm_callable, cm->cm_dict, &_Py_ID(__annotations__));
1400+
return descriptor_get_wrapped_attribute(cm->cm_callable, self, &_Py_ID(__annotations__));
13841401
}
13851402

13861403
static int
13871404
cm_set___annotations__(PyObject *self, PyObject *value, void *closure)
13881405
{
1389-
classmethod *cm = _PyClassMethod_CAST(self);
1390-
return descriptor_set_wrapped_attribute(cm->cm_dict, &_Py_ID(__annotations__), value, "classmethod");
1406+
return descriptor_set_wrapped_attribute(self, &_Py_ID(__annotations__), value, "classmethod");
13911407
}
13921408

13931409
static PyObject *
13941410
cm_get___annotate__(PyObject *self, void *closure)
13951411
{
13961412
classmethod *cm = _PyClassMethod_CAST(self);
1397-
return descriptor_get_wrapped_attribute(cm->cm_callable, cm->cm_dict, &_Py_ID(__annotate__));
1413+
return descriptor_get_wrapped_attribute(cm->cm_callable, self, &_Py_ID(__annotate__));
13981414
}
13991415

14001416
static int
14011417
cm_set___annotate__(PyObject *self, PyObject *value, void *closure)
14021418
{
1403-
classmethod *cm = _PyClassMethod_CAST(self);
1404-
return descriptor_set_wrapped_attribute(cm->cm_dict, &_Py_ID(__annotate__), value, "classmethod");
1419+
return descriptor_set_wrapped_attribute(self, &_Py_ID(__annotate__), value, "classmethod");
14051420
}
14061421

14071422

@@ -1615,28 +1630,26 @@ static PyObject *
16151630
sm_get___annotations__(PyObject *self, void *closure)
16161631
{
16171632
staticmethod *sm = _PyStaticMethod_CAST(self);
1618-
return descriptor_get_wrapped_attribute(sm->sm_callable, sm->sm_dict, &_Py_ID(__annotations__));
1633+
return descriptor_get_wrapped_attribute(sm->sm_callable, self, &_Py_ID(__annotations__));
16191634
}
16201635

16211636
static int
16221637
sm_set___annotations__(PyObject *self, PyObject *value, void *closure)
16231638
{
1624-
staticmethod *sm = _PyStaticMethod_CAST(self);
1625-
return descriptor_set_wrapped_attribute(sm->sm_dict, &_Py_ID(__annotations__), value, "staticmethod");
1639+
return descriptor_set_wrapped_attribute(self, &_Py_ID(__annotations__), value, "staticmethod");
16261640
}
16271641

16281642
static PyObject *
16291643
sm_get___annotate__(PyObject *self, void *closure)
16301644
{
16311645
staticmethod *sm = _PyStaticMethod_CAST(self);
1632-
return descriptor_get_wrapped_attribute(sm->sm_callable, sm->sm_dict, &_Py_ID(__annotate__));
1646+
return descriptor_get_wrapped_attribute(sm->sm_callable, self, &_Py_ID(__annotate__));
16331647
}
16341648

16351649
static int
16361650
sm_set___annotate__(PyObject *self, PyObject *value, void *closure)
16371651
{
1638-
staticmethod *sm = _PyStaticMethod_CAST(self);
1639-
return descriptor_set_wrapped_attribute(sm->sm_dict, &_Py_ID(__annotate__), value, "staticmethod");
1652+
return descriptor_set_wrapped_attribute(self, &_Py_ID(__annotate__), value, "staticmethod");
16401653
}
16411654

16421655
static PyGetSetDef sm_getsetlist[] = {

0 commit comments

Comments
 (0)
0