Repository navigation
Bound instance methods fail to satisfy runtime_checkable Protocol #127059
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Nov 20, 2024 - added3.12only security fixesonly security fixes3.13only security fixesonly security fixes3.14bugs and security fixesbugs and security fixes
on Nov 20, 2024 Thanks @AlexWaygood. I am happy to help with this if I am able, but I imagine the first step is deciding what (if anything) to do about it!
My first question here was "So why doesn't the
object.__getattribute__(x, "__dict__")call work whenxis a bound instance method? And it seems like the answer is thattypes.MethodTypeoverridesobject.__getattribute__:>>> class Foo: pass ... >>> Foo.__getattribute__ is object.__getattribute__ True >>> import types >>> types.MethodType.__getattribute__ is object.__getattribute__ False
My next question was, "Why does
MethodTypeoverrideobject.__getattribute__?" And it looks like this is so that bound methods forward attribute access to the underlying function object:>>> class Foo: ... def f(self): pass ... >>> Foo.f.bar = 42 >>> >>> Foo.f.__dict__ is Foo().f.__dict__ True >>> Foo().f.bar 42
inspect.getattr_staticis therefore working as intended here, since the whole point ofinspect.getattr_staticis that -- unlike regular attribute access -- it will not resolve dynamic attribute lookup implemented via special__getattribute__methods and similar. Resolving dynamic attribute lookup can execute arbitrary code, and that's exactly whatinspect.getattr_staticgoes out of its way to avoid.I think the choice to switch to
getattr_staticwas still the correct one overall, so it doesn't seem like the behaviour of runtime-checkable protocols can be changed so that this once again works for you like it used to. Therefore the third question is, "Should we update the docs to explicitly note the problem here with bound instance methods?"I'm not sure we should change the docs here. We already have a note in "What's new" outlining that the change we made might cause problems like this. I'm not sure this specific case should be called out there, as it seems like something of an edge case that not many users are likely to run into. Python 3.12 has now been released for over a year, and this is the first time anybody has raised this issue. Even for "What's new", it's important to keep the document focussed and streamlined.
So, to summarize: I'm really sorry that this change broke your code! But, I'm not sure there's anything to be done here :(
This all makes sense. I was actually part way through refactoring my code for compliance with strict pyright (which does not allow setting arbitrary attributes on functions) when I discovered it doesn't run on 3.12, so this is something that will go away soon in my case. I mainly thought it was interesting and wondered if it was a bug. Hopefully this issue will now appear in searches and your excellent summary above will provide the documentation of this behaviour.
Thanks for your help!
Reacted by Alex Waygood
Bug report
Bug description:
#103034 added some protections around
isinstancechecks ofruntime_checkableProtocolsusinginspect.getattr_static. This usesinspect._check_instancewhich usesobject.__getattribute__(obj, "__dict__"), which fails for bound instance methods (althoughobj.__dict__does work), so they cannot fulfillruntime_checkableProtocolsanymore.Is this an intended/known change? Could it be noted in the changelog here as an example of case that will no longer work?
Created from #103034 (comment)
cc @AlexWaygood
CPython versions tested on:
3.12
Operating systems tested on:
Linux