-
-
Notifications
You must be signed in to change notification settings - Fork 33k
gh-137317: fix inspect signature of class with descriptor wrapper #137862
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
cbab1ea
dca94be
9541a02
d6eb10d
c5b53dc
8a4fb30
889e9ec
3087840
67dc247
31e28fd
e1c11ec
43cc52f
0e63bd5
8c1f6ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4027,44 +4027,217 @@ def __init__(self, b): | |
('bar', 2, ..., "keyword_only")), | ||
...)) | ||
|
||
def test_signature_on_class_with_decorated_new(self): | ||
def test_signature_on_class_with_decorated_init(self): | ||
def identity(func): | ||
@functools.wraps(func) | ||
def wrapped(*args, **kwargs): | ||
return func(*args, **kwargs) | ||
return wrapped | ||
|
||
class Foo: | ||
class C: | ||
@identity | ||
def __new__(cls, a, b): | ||
def __init__(self, b): | ||
pass | ||
|
||
self.assertEqual(self.signature(Foo), | ||
((('a', ..., ..., "positional_or_keyword"), | ||
('b', ..., ..., "positional_or_keyword")), | ||
...)) | ||
C(1) # does not raise | ||
self.assertEqual(self.signature(C), | ||
((('b', ..., ..., "positional_or_keyword"),), | ||
...)) | ||
|
||
self.assertEqual(self.signature(Foo.__new__), | ||
((('cls', ..., ..., "positional_or_keyword"), | ||
('a', ..., ..., "positional_or_keyword"), | ||
('b', ..., ..., "positional_or_keyword")), | ||
...)) | ||
with self.subTest('classmethod'): | ||
class C: | ||
@classmethod | ||
@identity | ||
def __init__(cls, b): | ||
pass | ||
|
||
class Bar: | ||
__new__ = identity(object.__new__) | ||
C(1) # does not raise | ||
self.assertEqual(self.signature(C), | ||
((('b', ..., ..., "positional_or_keyword"),), | ||
...)) | ||
|
||
varargs_signature = ( | ||
(('args', ..., ..., 'var_positional'), | ||
('kwargs', ..., ..., 'var_keyword')), | ||
..., | ||
) | ||
with self.subTest('staticmethod'): | ||
class C: | ||
@staticmethod | ||
@identity | ||
def __init__(b): | ||
pass | ||
|
||
C(1) # does not raise | ||
self.assertEqual(self.signature(C), | ||
((('b', ..., ..., "positional_or_keyword"),), | ||
...)) | ||
|
||
with self.subTest('MethodType'): | ||
class A: | ||
@identity | ||
def call(self, a): | ||
pass | ||
|
||
class C: | ||
__init__ = A().call | ||
|
||
C(1) # does not raise | ||
self.assertEqual(self.signature(C), | ||
((('a', ..., ..., "positional_or_keyword"),), | ||
...)) | ||
|
||
with self.subTest('partial'): | ||
class C: | ||
__init__ = identity(functools.partial(lambda x, a, b: None, 2)) | ||
|
||
|
||
C(1) # does not raise | ||
self.assertEqual(self.signature(C), | ||
((('b', ..., ..., "positional_or_keyword"),), | ||
...)) | ||
|
||
with self.subTest('partialmethod'): | ||
class C: | ||
@identity | ||
def _init(self, x, a): | ||
self.a = (x, a) | ||
__init__ = functools.partialmethod(_init, 2) | ||
|
||
self.assertEqual(C(1).a, (2, 1)) | ||
self.assertEqual(self.signature(C), | ||
((('a', ..., ..., "positional_or_keyword"),), | ||
...)) | ||
|
||
class Desc: | ||
def __init__(self, func): | ||
self.func = identity(func) | ||
|
||
def __get__(self, instance, owner): | ||
return self.func.__get__(instance, owner) | ||
|
||
with self.subTest('descriptor'): | ||
class C: | ||
__init__ = Desc(lambda self, a: None) | ||
|
||
C(1) # does not raise | ||
self.assertEqual(self.signature(C), | ||
((('a', ..., ..., "positional_or_keyword"),), | ||
...)) | ||
|
||
self.assertEqual(self.signature(C.__init__), | ||
((('self', ..., ..., "positional_or_keyword"), | ||
('a', ..., ..., "positional_or_keyword")), | ||
...)) | ||
|
||
varargs_signature = ( | ||
(('args', ..., ..., 'var_positional'), | ||
('kwargs', ..., ..., 'var_keyword')), | ||
..., | ||
) | ||
self.assertEqual(self.signature(C, follow_wrapped=False), | ||
varargs_signature) | ||
self.assertEqual(self.signature(C.__new__, follow_wrapped=False), | ||
varargs_signature) | ||
|
||
self.assertEqual(self.signature(Bar), ((), ...)) | ||
self.assertEqual(self.signature(Bar.__new__), varargs_signature) | ||
self.assertEqual(self.signature(Bar, follow_wrapped=False), | ||
varargs_signature) | ||
self.assertEqual(self.signature(Bar.__new__, follow_wrapped=False), | ||
varargs_signature) | ||
|
||
def test_signature_on_class_with_decorated_new(self): | ||
def identity(func): | ||
@functools.wraps(func) | ||
def wrapped(*args, **kwargs): | ||
return func(*args, **kwargs) | ||
return wrapped | ||
|
||
with self.subTest('FunctionType'): | ||
class C: | ||
@identity | ||
def __new__(cls, a): | ||
return a | ||
|
||
self.assertEqual(C(1), 1) | ||
self.assertEqual(self.signature(C), | ||
((('a', ..., ..., "positional_or_keyword"),), | ||
...)) | ||
|
||
with self.subTest('classmethod'): | ||
class C: | ||
@classmethod | ||
@identity | ||
def __new__(cls, cls2, a): | ||
return a | ||
|
||
self.assertEqual(C(1), 1) | ||
self.assertEqual(self.signature(C), | ||
((('a', ..., ..., "positional_or_keyword"),), | ||
...)) | ||
|
||
with self.subTest('staticmethod'): | ||
class C: | ||
@staticmethod | ||
@identity | ||
def __new__(cls, a): | ||
return a | ||
|
||
self.assertEqual(C(1), 1) | ||
self.assertEqual(self.signature(C), | ||
((('a', ..., ..., "positional_or_keyword"),), | ||
...)) | ||
|
||
with self.subTest('MethodType'): | ||
class A: | ||
@identity | ||
def call(self, cls, a): | ||
return a | ||
class C: | ||
__new__ = A().call | ||
|
||
self.assertEqual(C(1), 1) | ||
self.assertEqual(self.signature(C), | ||
((('a', ..., ..., "positional_or_keyword"),), | ||
...)) | ||
|
||
with self.subTest('partial'): | ||
class C: | ||
__new__ = identity(functools.partial(lambda x, cls, a: (x, a), 2)) | ||
yanyongyu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
self.assertEqual(C(1), (2, 1)) | ||
self.assertEqual(self.signature(C), | ||
((('a', ..., ..., "positional_or_keyword"),), | ||
...)) | ||
|
||
with self.subTest('partialmethod'): | ||
class C: | ||
__new__ = functools.partialmethod(identity(lambda cls, x, a: (x, a)), 2) | ||
|
||
self.assertEqual(C(1), (2, 1)) | ||
self.assertEqual(self.signature(C), | ||
((('a', ..., ..., "positional_or_keyword"),), | ||
...)) | ||
|
||
class Desc: | ||
def __init__(self, func): | ||
self.func = identity(func) | ||
|
||
def __get__(self, instance, owner): | ||
return self.func.__get__(instance, owner) | ||
|
||
with self.subTest('descriptor'): | ||
class C: | ||
__new__ = Desc(lambda cls, a: a) | ||
|
||
self.assertEqual(C(1), 1) | ||
self.assertEqual(self.signature(C), | ||
((('a', ..., ..., "positional_or_keyword"),), | ||
...)) | ||
|
||
self.assertEqual(self.signature(C.__new__), | ||
((('cls', ..., ..., "positional_or_keyword"), | ||
('a', ..., ..., "positional_or_keyword")), | ||
...)) | ||
|
||
varargs_signature = ( | ||
(('args', ..., ..., 'var_positional'), | ||
('kwargs', ..., ..., 'var_keyword')), | ||
..., | ||
) | ||
self.assertEqual(self.signature(C, follow_wrapped=False), | ||
varargs_signature) | ||
self.assertEqual(self.signature(C.__new__, follow_wrapped=False), | ||
varargs_signature) | ||
|
||
def test_signature_on_class_with_init(self): | ||
class C: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
:func:`inspect.signature` now correctly handles classes that use a descriptor | ||
on a wrapped :meth:`!__init__` or :meth:`!__new__` method. | ||
Contributed by Yongyu Yan. |
Uh oh!
There was an error while loading. Please reload this page.