python / cpython

The Python programming language
https://www.python.org
Other
63.41k stars 30.37k forks source link

Changed behavior of <instancemethod>.__get__ in Python 3.11 #113157

Open mschmitzer opened 11 months ago

mschmitzer commented 11 months ago

Bug report

Bug description:

There is a change in behavior in Python 3.11 that does not seem to be described in the "what's new" document.

In the following code:

class A:
    def meth(self):
      print(self)

class B:
    pass

a = A()
b = B()
b.meth = a.meth.__get__(b, B)
b.meth()

The b.meth() call receives a as the self argument up to Python 3.10, but b in 3.11 and 3.12.

This pattern is used by the Pyramid framework. The change breaks Pyramid applications that use add_request_method(someobject.somemethod, "name").

I have to admit that I do not understand the descriptor protocol (or the documentation of __get__) well enough to say whether this is a legitimate use case, but it does exist in the wild.

CPython versions tested on:

3.9, 3.10, 3.11, 3.12

Operating systems tested on:

Linux

Linked PRs

Eclips4 commented 11 months ago

Bisected to a91858957 cc @msullivan @rhettinger

rhettinger commented 10 months ago

@MarcSchmitzer is correct and the "return a" behavior should be restored.

For Python 3.13, this is straightforward. Since classmethod descriptor chaining has now been removed, we can just add back method_descr_get to PyMethod_Type.

For Python 3.11 and Python 3.12, the situation is more complex and we can't fix any one aspect of the problem without breaking something else. Here are the options that come to mind:

1) Leave Python 3.11 and 3.12 as-is. But that would be a problem for the Pyramid framework. 2) Revert https://github.com/python/cpython/commit/a918589578a2a807396c5f6afab7b59ab692c642 . But that would break @msullivan 's use case. 3) Apply the 3.13 solution to 3.11 and 3.12. This is the cleanest way in the sense that we will have fully restored the original long-standing behaviors before the classmethod descriptor chaining trainwreck began. However, this path breaks having classmethod wrap property which is something that we know people use — that is why we went through a deprecation period to finally remove it in 3.13.

rhettinger commented 10 months ago

@msullivan Would you be okay with reverting https://github.com/python/cpython/commit/a918589578a2a807396c5f6afab7b59ab692c642 ? We wouldn't have gone down that path had we known that it would break another part of the API.

rhettinger commented 10 months ago

For 3.11 and 3.12, I suggest that we revert https://github.com/python/cpython/commit/a918589578a2a807396c5f6afab7b59ab692c642

Any objections?

erlend-aasland commented 10 months ago

For 3.11 and 3.12, I suggest that we revert a918589

Any objections?

cc. @pablogsal as 3.11 RM, and @Yhg1s as 3.12 RM.