Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 68c79d2

Browse filesBrowse files
gh-112006: Fix inspect.unwrap() for types where __wrapped__ is a data descriptor (GH-115540)
This also fixes inspect.Signature.from_callable() for builtins classmethod() and staticmethod().
1 parent b05afdd commit 68c79d2
Copy full SHA for 68c79d2

File tree

Expand file treeCollapse file tree

3 files changed

+32
-13
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+32
-13
lines changed

‎Lib/inspect.py

Copy file name to clipboardExpand all lines: Lib/inspect.py
+3-7Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -762,18 +762,14 @@ def unwrap(func, *, stop=None):
762762
:exc:`ValueError` is raised if a cycle is encountered.
763763
764764
"""
765-
if stop is None:
766-
def _is_wrapper(f):
767-
return hasattr(f, '__wrapped__')
768-
else:
769-
def _is_wrapper(f):
770-
return hasattr(f, '__wrapped__') and not stop(f)
771765
f = func # remember the original func for error reporting
772766
# Memoise by id to tolerate non-hashable objects, but store objects to
773767
# ensure they aren't destroyed, which would allow their IDs to be reused.
774768
memo = {id(f): f}
775769
recursion_limit = sys.getrecursionlimit()
776-
while _is_wrapper(func):
770+
while not isinstance(func, type) and hasattr(func, '__wrapped__'):
771+
if stop is not None and stop(func):
772+
break
777773
func = func.__wrapped__
778774
id_func = id(func)
779775
if (id_func in memo) or (len(memo) >= recursion_limit):

‎Lib/test/test_inspect/test_inspect.py

Copy file name to clipboardExpand all lines: Lib/test/test_inspect/test_inspect.py
+26-6Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3137,6 +3137,10 @@ def m1d(*args, **kwargs):
31373137
int))
31383138

31393139
def test_signature_on_classmethod(self):
3140+
self.assertEqual(self.signature(classmethod),
3141+
((('function', ..., ..., "positional_only"),),
3142+
...))
3143+
31403144
class Test:
31413145
@classmethod
31423146
def foo(cls, arg1, *, arg2=1):
@@ -3155,6 +3159,10 @@ def foo(cls, arg1, *, arg2=1):
31553159
...))
31563160

31573161
def test_signature_on_staticmethod(self):
3162+
self.assertEqual(self.signature(staticmethod),
3163+
((('function', ..., ..., "positional_only"),),
3164+
...))
3165+
31583166
class Test:
31593167
@staticmethod
31603168
def foo(cls, *, arg):
@@ -3678,16 +3686,20 @@ class Bar(Spam, Foo):
36783686
((('a', ..., ..., "positional_or_keyword"),),
36793687
...))
36803688

3681-
class Wrapped:
3682-
pass
3683-
Wrapped.__wrapped__ = lambda a: None
3684-
self.assertEqual(self.signature(Wrapped),
3689+
def test_signature_on_wrapper(self):
3690+
class Wrapper:
3691+
def __call__(self, b):
3692+
pass
3693+
wrapper = Wrapper()
3694+
wrapper.__wrapped__ = lambda a: None
3695+
self.assertEqual(self.signature(wrapper),
36853696
((('a', ..., ..., "positional_or_keyword"),),
36863697
...))
36873698
# wrapper loop:
3688-
Wrapped.__wrapped__ = Wrapped
3699+
wrapper = Wrapper()
3700+
wrapper.__wrapped__ = wrapper
36893701
with self.assertRaisesRegex(ValueError, 'wrapper loop'):
3690-
self.signature(Wrapped)
3702+
self.signature(wrapper)
36913703

36923704
def test_signature_on_lambdas(self):
36933705
self.assertEqual(self.signature((lambda a=10: a)),
@@ -4999,6 +5011,14 @@ def test_recursion_limit(self):
49995011
with self.assertRaisesRegex(ValueError, 'wrapper loop'):
50005012
inspect.unwrap(obj)
50015013

5014+
def test_wrapped_descriptor(self):
5015+
self.assertIs(inspect.unwrap(NTimesUnwrappable), NTimesUnwrappable)
5016+
self.assertIs(inspect.unwrap(staticmethod), staticmethod)
5017+
self.assertIs(inspect.unwrap(classmethod), classmethod)
5018+
self.assertIs(inspect.unwrap(staticmethod(classmethod)), classmethod)
5019+
self.assertIs(inspect.unwrap(classmethod(staticmethod)), staticmethod)
5020+
5021+
50025022
class TestMain(unittest.TestCase):
50035023
def test_only_source(self):
50045024
module = importlib.import_module('unittest')
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :func:`inspect.unwrap` for types with the ``__wrapper__`` data
2+
descriptor. Fix :meth:`inspect.Signature.from_callable` for builtins
3+
:func:`classmethod` and :func:`staticmethod`.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.