mirror of https://github.com/python/cpython
gh-125017: Fix crash on premature access to classmethod/staticmethod annotations (#125636)
This commit is contained in:
parent
04d6dd23e2
commit
f203d1cb52
|
@ -1618,6 +1618,9 @@ class ClassPropertiesAndMethods(unittest.TestCase):
|
||||||
|
|
||||||
for method in (annotated, unannotated):
|
for method in (annotated, unannotated):
|
||||||
with self.subTest(deco=deco, method=method):
|
with self.subTest(deco=deco, method=method):
|
||||||
|
with self.assertRaises(AttributeError):
|
||||||
|
del unannotated.__annotations__
|
||||||
|
|
||||||
original_annotations = dict(method.__wrapped__.__annotations__)
|
original_annotations = dict(method.__wrapped__.__annotations__)
|
||||||
self.assertNotIn('__annotations__', method.__dict__)
|
self.assertNotIn('__annotations__', method.__dict__)
|
||||||
self.assertEqual(method.__annotations__, original_annotations)
|
self.assertEqual(method.__annotations__, original_annotations)
|
||||||
|
@ -1644,6 +1647,17 @@ class ClassPropertiesAndMethods(unittest.TestCase):
|
||||||
del method.__annotate__
|
del method.__annotate__
|
||||||
self.assertIs(method.__annotate__, original_annotate)
|
self.assertIs(method.__annotate__, original_annotate)
|
||||||
|
|
||||||
|
def test_staticmethod_annotations_without_dict_access(self):
|
||||||
|
# gh-125017: this used to crash
|
||||||
|
class Spam:
|
||||||
|
def __new__(cls, x, y):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertEqual(Spam.__new__.__annotations__, {})
|
||||||
|
obj = Spam.__dict__['__new__']
|
||||||
|
self.assertIsInstance(obj, staticmethod)
|
||||||
|
self.assertEqual(obj.__annotations__, {})
|
||||||
|
|
||||||
@support.refcount_test
|
@support.refcount_test
|
||||||
def test_refleaks_in_classmethod___init__(self):
|
def test_refleaks_in_classmethod___init__(self):
|
||||||
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
|
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix crash on certain accesses to the ``__annotations__`` of
|
||||||
|
:class:`staticmethod` and :class:`classmethod` objects.
|
|
@ -1220,30 +1220,43 @@ functools_wraps(PyObject *wrapper, PyObject *wrapped)
|
||||||
// Used for wrapping __annotations__ and __annotate__ on classmethod
|
// Used for wrapping __annotations__ and __annotate__ on classmethod
|
||||||
// and staticmethod objects.
|
// and staticmethod objects.
|
||||||
static PyObject *
|
static PyObject *
|
||||||
descriptor_get_wrapped_attribute(PyObject *wrapped, PyObject *dict, PyObject *name)
|
descriptor_get_wrapped_attribute(PyObject *wrapped, PyObject *obj, PyObject *name)
|
||||||
{
|
{
|
||||||
|
PyObject *dict = PyObject_GenericGetDict(obj, NULL);
|
||||||
|
if (dict == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
PyObject *res;
|
PyObject *res;
|
||||||
if (PyDict_GetItemRef(dict, name, &res) < 0) {
|
if (PyDict_GetItemRef(dict, name, &res) < 0) {
|
||||||
|
Py_DECREF(dict);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (res != NULL) {
|
if (res != NULL) {
|
||||||
|
Py_DECREF(dict);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
res = PyObject_GetAttr(wrapped, name);
|
res = PyObject_GetAttr(wrapped, name);
|
||||||
if (res == NULL) {
|
if (res == NULL) {
|
||||||
|
Py_DECREF(dict);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (PyDict_SetItem(dict, name, res) < 0) {
|
if (PyDict_SetItem(dict, name, res) < 0) {
|
||||||
|
Py_DECREF(dict);
|
||||||
Py_DECREF(res);
|
Py_DECREF(res);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
Py_DECREF(dict);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
descriptor_set_wrapped_attribute(PyObject *dict, PyObject *name, PyObject *value,
|
descriptor_set_wrapped_attribute(PyObject *oobj, PyObject *name, PyObject *value,
|
||||||
char *type_name)
|
char *type_name)
|
||||||
{
|
{
|
||||||
|
PyObject *dict = PyObject_GenericGetDict(oobj, NULL);
|
||||||
|
if (dict == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
if (PyDict_DelItem(dict, name) < 0) {
|
if (PyDict_DelItem(dict, name) < 0) {
|
||||||
if (PyErr_ExceptionMatches(PyExc_KeyError)) {
|
if (PyErr_ExceptionMatches(PyExc_KeyError)) {
|
||||||
|
@ -1251,14 +1264,18 @@ descriptor_set_wrapped_attribute(PyObject *dict, PyObject *name, PyObject *value
|
||||||
PyErr_Format(PyExc_AttributeError,
|
PyErr_Format(PyExc_AttributeError,
|
||||||
"'%.200s' object has no attribute '%U'",
|
"'%.200s' object has no attribute '%U'",
|
||||||
type_name, name);
|
type_name, name);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
Py_DECREF(dict);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Py_DECREF(dict);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
Py_DECREF(dict);
|
||||||
return PyDict_SetItem(dict, name, value);
|
return PyDict_SetItem(dict, name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1380,28 +1397,26 @@ static PyObject *
|
||||||
cm_get___annotations__(PyObject *self, void *closure)
|
cm_get___annotations__(PyObject *self, void *closure)
|
||||||
{
|
{
|
||||||
classmethod *cm = _PyClassMethod_CAST(self);
|
classmethod *cm = _PyClassMethod_CAST(self);
|
||||||
return descriptor_get_wrapped_attribute(cm->cm_callable, cm->cm_dict, &_Py_ID(__annotations__));
|
return descriptor_get_wrapped_attribute(cm->cm_callable, self, &_Py_ID(__annotations__));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cm_set___annotations__(PyObject *self, PyObject *value, void *closure)
|
cm_set___annotations__(PyObject *self, PyObject *value, void *closure)
|
||||||
{
|
{
|
||||||
classmethod *cm = _PyClassMethod_CAST(self);
|
return descriptor_set_wrapped_attribute(self, &_Py_ID(__annotations__), value, "classmethod");
|
||||||
return descriptor_set_wrapped_attribute(cm->cm_dict, &_Py_ID(__annotations__), value, "classmethod");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
cm_get___annotate__(PyObject *self, void *closure)
|
cm_get___annotate__(PyObject *self, void *closure)
|
||||||
{
|
{
|
||||||
classmethod *cm = _PyClassMethod_CAST(self);
|
classmethod *cm = _PyClassMethod_CAST(self);
|
||||||
return descriptor_get_wrapped_attribute(cm->cm_callable, cm->cm_dict, &_Py_ID(__annotate__));
|
return descriptor_get_wrapped_attribute(cm->cm_callable, self, &_Py_ID(__annotate__));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cm_set___annotate__(PyObject *self, PyObject *value, void *closure)
|
cm_set___annotate__(PyObject *self, PyObject *value, void *closure)
|
||||||
{
|
{
|
||||||
classmethod *cm = _PyClassMethod_CAST(self);
|
return descriptor_set_wrapped_attribute(self, &_Py_ID(__annotate__), value, "classmethod");
|
||||||
return descriptor_set_wrapped_attribute(cm->cm_dict, &_Py_ID(__annotate__), value, "classmethod");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1615,28 +1630,26 @@ static PyObject *
|
||||||
sm_get___annotations__(PyObject *self, void *closure)
|
sm_get___annotations__(PyObject *self, void *closure)
|
||||||
{
|
{
|
||||||
staticmethod *sm = _PyStaticMethod_CAST(self);
|
staticmethod *sm = _PyStaticMethod_CAST(self);
|
||||||
return descriptor_get_wrapped_attribute(sm->sm_callable, sm->sm_dict, &_Py_ID(__annotations__));
|
return descriptor_get_wrapped_attribute(sm->sm_callable, self, &_Py_ID(__annotations__));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
sm_set___annotations__(PyObject *self, PyObject *value, void *closure)
|
sm_set___annotations__(PyObject *self, PyObject *value, void *closure)
|
||||||
{
|
{
|
||||||
staticmethod *sm = _PyStaticMethod_CAST(self);
|
return descriptor_set_wrapped_attribute(self, &_Py_ID(__annotations__), value, "staticmethod");
|
||||||
return descriptor_set_wrapped_attribute(sm->sm_dict, &_Py_ID(__annotations__), value, "staticmethod");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
sm_get___annotate__(PyObject *self, void *closure)
|
sm_get___annotate__(PyObject *self, void *closure)
|
||||||
{
|
{
|
||||||
staticmethod *sm = _PyStaticMethod_CAST(self);
|
staticmethod *sm = _PyStaticMethod_CAST(self);
|
||||||
return descriptor_get_wrapped_attribute(sm->sm_callable, sm->sm_dict, &_Py_ID(__annotate__));
|
return descriptor_get_wrapped_attribute(sm->sm_callable, self, &_Py_ID(__annotate__));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
sm_set___annotate__(PyObject *self, PyObject *value, void *closure)
|
sm_set___annotate__(PyObject *self, PyObject *value, void *closure)
|
||||||
{
|
{
|
||||||
staticmethod *sm = _PyStaticMethod_CAST(self);
|
return descriptor_set_wrapped_attribute(self, &_Py_ID(__annotate__), value, "staticmethod");
|
||||||
return descriptor_set_wrapped_attribute(sm->sm_dict, &_Py_ID(__annotate__), value, "staticmethod");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyGetSetDef sm_getsetlist[] = {
|
static PyGetSetDef sm_getsetlist[] = {
|
||||||
|
|
Loading…
Reference in New Issue