]> git.ipfire.org Git - thirdparty/Python/cpython.git/commit
bpo-42073: allow classmethod to wrap other classmethod-like descriptors (#27115)
authorŁukasz Langa <lukasz@langa.pl>
Thu, 15 Jul 2021 13:16:19 +0000 (15:16 +0200)
committerGitHub <noreply@github.com>
Thu, 15 Jul 2021 13:16:19 +0000 (15:16 +0200)
commitb83861f0265e07207a6ae2c49c40fa8f447893f2
tree2524698435840e4ffdf2446d67b8853933d9e12d
parent641345d636320a6fca04a5271fa4c4c5ba3e5437
bpo-42073: allow classmethod to wrap other classmethod-like descriptors (#27115)

Patch by Erik Welch.

bpo-19072 (#8405) allows `classmethod` to wrap other descriptors, but this does
not work when the wrapped descriptor mimics classmethod.  The current PR fixes
this.

In Python 3.8 and before, one could create a callable descriptor such that this
works as expected (see Lib/test/test_decorators.py for examples):
```python
class A:
    @myclassmethod
    def f1(cls):
        return cls

    @classmethod
    @myclassmethod
    def f2(cls):
        return cls
```
In Python 3.8 and before, `A.f2()` return `A`. Currently in Python 3.9, it
returns `type(A)`.  This PR make `A.f2()` return `A` again.

As of #8405, classmethod calls `obj.__get__(type)` if `obj` has `__get__`.
This allows one to chain `@classmethod` and `@property` together.  When
using classmethod-like descriptors, it's the second argument to `__get__`--the
owner or the type--that is important, but this argument is currently missing.
Since it is None, the "owner" argument is assumed to be the type of the first
argument, which, in this case, is wrong (we want `A`, not `type(A)`).

This PR updates classmethod to call `obj.__get__(type, type)` if `obj` has
`__get__`.

Co-authored-by: Erik Welch <erik.n.welch@gmail.com>
Lib/test/test_decorators.py
Misc/NEWS.d/next/Core and Builtins/2021-07-13-17-47-32.bpo-42073.9wopiC.rst [new file with mode: 0644]
Objects/funcobject.c