diff --git a/Lib/test/test_decorators.py b/Lib/test/test_decorators.py --- a/Lib/test/test_decorators.py +++ b/Lib/test/test_decorators.py @@ -260,16 +260,53 @@ class TestDecorators(unittest.TestCase): # Test the equivalence claim in chapter 7 of the reference manual. # actions = [] def bar(): return 42 bar = c1.make_decorator(c1.arg)(c2.make_decorator(c2.arg)(c3.make_decorator(c3.arg)(bar))) self.assertEqual(bar(), 42) self.assertEqual(actions, expected_actions) + def test_wrapped_descriptor_inside_classmethod(self): + class BoundWrapper: + def __init__(self, wrapped): + self.__wrapped__ = wrapped + + def __call__(self, *args, **kwargs): + return self.__wrapped__(*args, **kwargs) + + class Wrapper: + def __init__(self, wrapped): + self.__wrapped__ = wrapped + + def __get__(self, instance, owner): + bound_function = self.__wrapped__.__get__(instance, owner) + return BoundWrapper(bound_function) + + def decorator(wrapped): + return Wrapper(wrapped) + + class Class: + @decorator + @classmethod + def inner(cls): + # this should already work + return 'spam' + + @classmethod + @decorator + def outer(cls): + return 'eggs' + + self.assertEqual(Class.inner(), 'spam') + self.assertEqual(Class.outer(), 'eggs') + self.assertEqual(Class().inner(), 'spam') + self.assertEqual(Class().outer(), 'eggs') + + class TestClassDecorators(unittest.TestCase): def test_simple(self): def plain(x): x.extra = 'Hello' return x @plain class C(object): pass diff --git a/Objects/funcobject.c b/Objects/funcobject.c --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -725,16 +725,20 @@ cm_descr_get(PyObject *self, PyObject *o if (cm->cm_callable == NULL) { PyErr_SetString(PyExc_RuntimeError, "uninitialized classmethod object"); return NULL; } if (type == NULL) type = (PyObject *)(Py_TYPE(obj)); + if (Py_TYPE(cm->cm_callable)->tp_descr_get != NULL) { + return Py_TYPE(cm->cm_callable)->tp_descr_get(cm->cm_callable, type, + NULL); + } return PyMethod_New(cm->cm_callable, type); } static int cm_init(PyObject *self, PyObject *args, PyObject *kwds) { classmethod *cm = (classmethod *)self; PyObject *callable;