-
-
Notifications
You must be signed in to change notification settings - Fork 33.2k
Description
Bug report
inspect.unwrap() follows the chain by links __wrapped__ and returns the last item in a chain or the original object if it does not have a __wrapped__ attribute (there is also additional stop predicate and protection against loops, but it is unrelated). It works well in most cases, except with a type that has the __wrapped__ data descriptor.
For example the following code
class W:
def __init__(self, x):
self._wrapped = x
@property
def __wrapped__(self):
return self._wrapped
import inspect
print(inspect.unwrap(W(chr)))
print(inspect.unwrap(W))prints
<built-in function chr>
<property object at 0x7f334092dc50>
The former output is correct, W(chr) wraps chr. But the latter is wrong: the W type does not wrap a property object.
It is not hypothetical issue. staticmethod and classmethod have now (bpo-43682/#87848) the __wrapped__ attribute. inspect.signature() uses inspect.unwrap(), and it cannot support staticmethod and classmethod even if they get correct __text_signature__. inspect.getsourcelines() also uses inspect.unwrap() indirectly and can fail with Python classes with the __wrapped__ attribute.
inspect.unwrap() should stop before such attribute. But how to detect such case? There are several ways:
- Stop if
funcis a class.pickledoes it for its special methods, this is why classes are handled separately from instances. But it means thatfunctools.wraps(),staticmethodandclassmethodcannot be used to decorate classes. Although if they are currently used, the result can be weird, because instances will have the same__wrapped__attribute as a class. I do not know how often wrapped classes are used in the real code, but there is a test for this. It may be the right way at the end, although it can break some questionable code. - Stop if
func.__wrapped__is a data descriptor. I afraid that it will affect multidecorated properties. - Stop if
func.__wrapped__is not callable. Do not know what can be consequences.
Maybe there are other ways?
Linked PRs
- gh-112006: Fix inspect.unwrap() for types where __wrapped__ is a data descriptor #115540
- [3.11] gh-112006: Fix inspect.unwrap() for types where __wrapped__ is a data descriptor (GH-115540) #115965
- [3.12] gh-112006: Fix inspect.unwrap() for types where __wrapped__ is a data descriptor (GH-115540) #115966