diff --git a/Lib/test/test_decorators.py b/Lib/test/test_decorators.py index 298979e509f..ea43f189c0b 100644 --- a/Lib/test/test_decorators.py +++ b/Lib/test/test_decorators.py @@ -1,4 +1,5 @@ import unittest +from types import MethodType def funcattrs(**kwds): def decorate(func): @@ -307,6 +308,91 @@ class TestDecorators(unittest.TestCase): self.assertEqual(Class().inner(), 'spam') self.assertEqual(Class().outer(), 'eggs') + def test_wrapped_classmethod_inside_classmethod(self): + class MyClassMethod1: + def __init__(self, func): + self.func = func + + def __call__(self, cls): + if hasattr(self.func, '__get__'): + return self.func.__get__(cls, cls)() + return self.func(cls) + + def __get__(self, instance, owner=None): + if owner is None: + owner = type(instance) + return MethodType(self, owner) + + class MyClassMethod2: + def __init__(self, func): + if isinstance(func, classmethod): + func = func.__func__ + self.func = func + + def __call__(self, cls): + return self.func(cls) + + def __get__(self, instance, owner=None): + if owner is None: + owner = type(instance) + return MethodType(self, owner) + + for myclassmethod in [MyClassMethod1, MyClassMethod2]: + class A: + @myclassmethod + def f1(cls): + return cls + + @classmethod + @myclassmethod + def f2(cls): + return cls + + @myclassmethod + @classmethod + def f3(cls): + return cls + + @classmethod + @classmethod + def f4(cls): + return cls + + @myclassmethod + @MyClassMethod1 + def f5(cls): + return cls + + @myclassmethod + @MyClassMethod2 + def f6(cls): + return cls + + self.assertIs(A.f1(), A) + self.assertIs(A.f2(), A) + self.assertIs(A.f3(), A) + self.assertIs(A.f4(), A) + self.assertIs(A.f5(), A) + self.assertIs(A.f6(), A) + a = A() + self.assertIs(a.f1(), A) + self.assertIs(a.f2(), A) + self.assertIs(a.f3(), A) + self.assertIs(a.f4(), A) + self.assertIs(a.f5(), A) + self.assertIs(a.f6(), A) + + def f(cls): + return cls + + self.assertIs(myclassmethod(f).__get__(a)(), A) + self.assertIs(myclassmethod(f).__get__(a, A)(), A) + self.assertIs(myclassmethod(f).__get__(A, A)(), A) + self.assertIs(myclassmethod(f).__get__(A)(), type(A)) + self.assertIs(classmethod(f).__get__(a)(), A) + self.assertIs(classmethod(f).__get__(a, A)(), A) + self.assertIs(classmethod(f).__get__(A, A)(), A) + self.assertIs(classmethod(f).__get__(A)(), type(A)) class TestClassDecorators(unittest.TestCase): diff --git a/Objects/funcobject.c b/Objects/funcobject.c index bd24f67b974..74f9167566d 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -739,7 +739,7 @@ cm_descr_get(PyObject *self, PyObject *obj, PyObject *type) 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); + type); } return PyMethod_New(cm->cm_callable, type); }