bpo-43682: @staticmethod inherits attributes (GH-25268)

Static methods (@staticmethod) and class methods (@classmethod) now
inherit the method attributes (__module__, __name__, __qualname__,
__doc__, __annotations__) and have a new __wrapped__ attribute.

Changes:

* Add a repr() method to staticmethod and classmethod types.
* Add tests on the @classmethod decorator.
This commit is contained in:
Victor Stinner 2021-04-09 17:51:22 +02:00 committed by GitHub
parent 150af75432
commit 507a574de3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 133 additions and 22 deletions

View file

@ -639,7 +639,7 @@ static PyObject*
func_repr(PyFunctionObject *op)
{
return PyUnicode_FromFormat("<function %U at %p>",
op->func_qualname, op);
op->func_qualname, op);
}
static int
@ -715,6 +715,50 @@ PyTypeObject PyFunction_Type = {
};
static int
functools_copy_attr(PyObject *wrapper, PyObject *wrapped, PyObject *name)
{
PyObject *value = PyObject_GetAttr(wrapped, name);
if (value == NULL) {
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
return 0;
}
return -1;
}
int res = PyObject_SetAttr(wrapper, name, value);
Py_DECREF(value);
return res;
}
// Similar to functools.wraps(wrapper, wrapped)
static int
functools_wraps(PyObject *wrapper, PyObject *wrapped)
{
#define COPY_ATTR(ATTR) \
do { \
_Py_IDENTIFIER(ATTR); \
PyObject *attr = _PyUnicode_FromId(&PyId_ ## ATTR); \
if (attr == NULL) { \
return -1; \
} \
if (functools_copy_attr(wrapper, wrapped, attr) < 0) { \
return -1; \
} \
} while (0) \
COPY_ATTR(__module__);
COPY_ATTR(__name__);
COPY_ATTR(__qualname__);
COPY_ATTR(__doc__);
COPY_ATTR(__annotations__);
return 0;
#undef COPY_ATTR
}
/* Class method object */
/* A class method receives the class as implicit first argument,
@ -798,11 +842,16 @@ cm_init(PyObject *self, PyObject *args, PyObject *kwds)
return -1;
Py_INCREF(callable);
Py_XSETREF(cm->cm_callable, callable);
if (functools_wraps((PyObject *)cm, cm->cm_callable) < 0) {
return -1;
}
return 0;
}
static PyMemberDef cm_memberlist[] = {
{"__func__", T_OBJECT, offsetof(classmethod, cm_callable), READONLY},
{"__wrapped__", T_OBJECT, offsetof(classmethod, cm_callable), READONLY},
{NULL} /* Sentinel */
};
@ -821,13 +870,17 @@ cm_get___isabstractmethod__(classmethod *cm, void *closure)
static PyGetSetDef cm_getsetlist[] = {
{"__isabstractmethod__",
(getter)cm_get___isabstractmethod__, NULL,
NULL,
NULL},
(getter)cm_get___isabstractmethod__, NULL, NULL, NULL},
{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict, NULL, NULL},
{NULL} /* Sentinel */
};
static PyObject*
cm_repr(classmethod *cm)
{
return PyUnicode_FromFormat("<classmethod(%R)>", cm->cm_callable);
}
PyDoc_STRVAR(classmethod_doc,
"classmethod(function) -> method\n\
\n\
@ -860,7 +913,7 @@ PyTypeObject PyClassMethod_Type = {
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
0, /* tp_repr */
(reprfunc)cm_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
@ -980,11 +1033,16 @@ sm_init(PyObject *self, PyObject *args, PyObject *kwds)
return -1;
Py_INCREF(callable);
Py_XSETREF(sm->sm_callable, callable);
if (functools_wraps((PyObject *)sm, sm->sm_callable) < 0) {
return -1;
}
return 0;
}
static PyMemberDef sm_memberlist[] = {
{"__func__", T_OBJECT, offsetof(staticmethod, sm_callable), READONLY},
{"__wrapped__", T_OBJECT, offsetof(staticmethod, sm_callable), READONLY},
{NULL} /* Sentinel */
};
@ -1003,13 +1061,17 @@ sm_get___isabstractmethod__(staticmethod *sm, void *closure)
static PyGetSetDef sm_getsetlist[] = {
{"__isabstractmethod__",
(getter)sm_get___isabstractmethod__, NULL,
NULL,
NULL},
(getter)sm_get___isabstractmethod__, NULL, NULL, NULL},
{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict, NULL, NULL},
{NULL} /* Sentinel */
};
static PyObject*
sm_repr(staticmethod *sm)
{
return PyUnicode_FromFormat("<staticmethod(%R)>", sm->sm_callable);
}
PyDoc_STRVAR(staticmethod_doc,
"staticmethod(function) -> method\n\
\n\
@ -1040,7 +1102,7 @@ PyTypeObject PyStaticMethod_Type = {
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
0, /* tp_repr */
(reprfunc)sm_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */