summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2024-02-27 18:32:34 (GMT)
committerGitHub <noreply@github.com>2024-02-27 18:32:34 (GMT)
commit418e72041349dccdd2bf6ad58643fec3314b1691 (patch)
tree8a3ceae89771a7beafa26c2f7840f5c9083b357c
parent3af945fbb47600077850dc7fbcdbc323ddd83dd5 (diff)
downloadcpython-418e72041349dccdd2bf6ad58643fec3314b1691.zip
cpython-418e72041349dccdd2bf6ad58643fec3314b1691.tar.gz
cpython-418e72041349dccdd2bf6ad58643fec3314b1691.tar.bz2
[3.12] gh-112006: Fix inspect.unwrap() for types where __wrapped__ is a data descriptor (GH-115540) (GH-115966)
(cherry picked from commit 68c79d21fa791d7418a858b7aa4604880e988a02) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
-rw-r--r--Lib/inspect.py10
-rw-r--r--Lib/test/test_inspect/test_inspect.py24
-rw-r--r--Misc/NEWS.d/next/Library/2024-02-15-23-42-54.gh-issue-112006.4wxcK-.rst2
3 files changed, 23 insertions, 13 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py
index a550202..76490ea 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -760,18 +760,14 @@ def unwrap(func, *, stop=None):
:exc:`ValueError` is raised if a cycle is encountered.
"""
- if stop is None:
- def _is_wrapper(f):
- return hasattr(f, '__wrapped__')
- else:
- def _is_wrapper(f):
- return hasattr(f, '__wrapped__') and not stop(f)
f = func # remember the original func for error reporting
# Memoise by id to tolerate non-hashable objects, but store objects to
# ensure they aren't destroyed, which would allow their IDs to be reused.
memo = {id(f): f}
recursion_limit = sys.getrecursionlimit()
- while _is_wrapper(func):
+ while not isinstance(func, type) and hasattr(func, '__wrapped__'):
+ if stop is not None and stop(func):
+ break
func = func.__wrapped__
id_func = id(func)
if (id_func in memo) or (len(memo) >= recursion_limit):
diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py
index ec50ace..18a7c08 100644
--- a/Lib/test/test_inspect/test_inspect.py
+++ b/Lib/test/test_inspect/test_inspect.py
@@ -3488,16 +3488,20 @@ class TestSignatureObject(unittest.TestCase):
((('a', ..., ..., "positional_or_keyword"),),
...))
- class Wrapped:
- pass
- Wrapped.__wrapped__ = lambda a: None
- self.assertEqual(self.signature(Wrapped),
+ def test_signature_on_wrapper(self):
+ class Wrapper:
+ def __call__(self, b):
+ pass
+ wrapper = Wrapper()
+ wrapper.__wrapped__ = lambda a: None
+ self.assertEqual(self.signature(wrapper),
((('a', ..., ..., "positional_or_keyword"),),
...))
# wrapper loop:
- Wrapped.__wrapped__ = Wrapped
+ wrapper = Wrapper()
+ wrapper.__wrapped__ = wrapper
with self.assertRaisesRegex(ValueError, 'wrapper loop'):
- self.signature(Wrapped)
+ self.signature(wrapper)
def test_signature_on_lambdas(self):
self.assertEqual(self.signature((lambda a=10: a)),
@@ -4672,6 +4676,14 @@ class TestUnwrap(unittest.TestCase):
with self.assertRaisesRegex(ValueError, 'wrapper loop'):
inspect.unwrap(obj)
+ def test_wrapped_descriptor(self):
+ self.assertIs(inspect.unwrap(NTimesUnwrappable), NTimesUnwrappable)
+ self.assertIs(inspect.unwrap(staticmethod), staticmethod)
+ self.assertIs(inspect.unwrap(classmethod), classmethod)
+ self.assertIs(inspect.unwrap(staticmethod(classmethod)), classmethod)
+ self.assertIs(inspect.unwrap(classmethod(staticmethod)), staticmethod)
+
+
class TestMain(unittest.TestCase):
def test_only_source(self):
module = importlib.import_module('unittest')
diff --git a/Misc/NEWS.d/next/Library/2024-02-15-23-42-54.gh-issue-112006.4wxcK-.rst b/Misc/NEWS.d/next/Library/2024-02-15-23-42-54.gh-issue-112006.4wxcK-.rst
new file mode 100644
index 0000000..7e9fe97
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-02-15-23-42-54.gh-issue-112006.4wxcK-.rst
@@ -0,0 +1,2 @@
+Fix :func:`inspect.unwrap` for types with the ``__wrapper__`` data
+descriptor.