Skip to content

Commit be833e6

Browse files
authored
gh-146553: Fix infinite loop in typing.get_type_hints() on circular __wrapped__ (#148595)
1 parent 79321fd commit be833e6

3 files changed

Lines changed: 24 additions & 0 deletions

File tree

Lib/test/test_typing.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6888,6 +6888,24 @@ def test_get_type_hints_wrapped_decoratored_func(self):
68886888
self.assertEqual(gth(ForRefExample.func), expects)
68896889
self.assertEqual(gth(ForRefExample.nested), expects)
68906890

6891+
def test_get_type_hints_wrapped_cycle_self(self):
6892+
# gh-146553: __wrapped__ self-reference must raise ValueError,
6893+
# not loop forever.
6894+
def f(x: int) -> str: ...
6895+
f.__wrapped__ = f
6896+
with self.assertRaisesRegex(ValueError, 'wrapper loop'):
6897+
get_type_hints(f)
6898+
6899+
def test_get_type_hints_wrapped_cycle_mutual(self):
6900+
# gh-146553: mutual __wrapped__ cycle (a -> b -> a) must raise
6901+
# ValueError, not loop forever.
6902+
def a(): ...
6903+
def b(): ...
6904+
a.__wrapped__ = b
6905+
b.__wrapped__ = a
6906+
with self.assertRaisesRegex(ValueError, 'wrapper loop'):
6907+
get_type_hints(a)
6908+
68916909
def test_get_type_hints_annotated(self):
68926910
def foobar(x: List['X']): ...
68936911
X = Annotated[int, (1, 10)]

Lib/typing.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2486,8 +2486,12 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
24862486
else:
24872487
nsobj = obj
24882488
# Find globalns for the unwrapped object.
2489+
seen = {id(nsobj)}
24892490
while hasattr(nsobj, '__wrapped__'):
24902491
nsobj = nsobj.__wrapped__
2492+
if id(nsobj) in seen:
2493+
raise ValueError(f'wrapper loop when unwrapping {obj!r}')
2494+
seen.add(id(nsobj))
24912495
globalns = getattr(nsobj, '__globals__', {})
24922496
if localns is None:
24932497
localns = globalns
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix infinite loop in :func:`typing.get_type_hints` when ``__wrapped__``
2+
forms a cycle. Patch by Shamil Abdulaev.

0 commit comments

Comments
 (0)