]> git.ipfire.org Git - thirdparty/Python/cpython.git/commit
bpo-42073: allow classmethod to wrap other classmethod-like descriptors (GH-27115...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 15 Jul 2021 13:42:11 +0000 (06:42 -0700)
committerGitHub <noreply@github.com>
Thu, 15 Jul 2021 13:42:11 +0000 (15:42 +0200)
commit2ce8af3cbcb368a35a05a5a9f97a09405124f239
tree196a15bf8d34099fb19103003e2d1146f398e592
parent3026d1381e1e3cb7cd0df28c9a0b5f067148665d
bpo-42073: allow classmethod to wrap other classmethod-like descriptors (GH-27115) (GH-27162)

Patch by Erik Welch.

bpo-19072 (GH-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 GH-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>
(cherry picked from commit b83861f0265e07207a6ae2c49c40fa8f447893f2)
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