[3.9] bpo-42073: allow classmethod to wrap other classmethod-like descriptors. by eriknw · Pull Request #22757 · python/cpython

@eriknw

bpo-19072 (python#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 python#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__`.

@eriknw eriknw changed the title bpo-42073: allow classmethod to wrap other classmethod-like descriptors. [3.9] bpo-42073: allow classmethod to wrap other classmethod-like descriptors.

Oct 18, 2020

@ambv ambv changed the base branch from 3.9 to main

July 13, 2021 14:52

@ambv ambv changed the base branch from main to 3.9

July 13, 2021 14:53

@ambv ambv mentioned this pull request

Jul 13, 2021