Skip to content

Commit 27cd234

Browse files
[3.14] gh-146553: Fix infinite loop in typing.get_type_hints() on circular __wrapped__ (GH-148595) (#148895)
(cherry picked from commit be833e6) Co-authored-by: Shamil <[email protected]>
1 parent 3c71d36 commit 27cd234

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
@@ -6778,6 +6778,24 @@ def test_get_type_hints_wrapped_decoratored_func(self):
67786778
self.assertEqual(gth(ForRefExample.func), expects)
67796779
self.assertEqual(gth(ForRefExample.nested), expects)
67806780

6781+
def test_get_type_hints_wrapped_cycle_self(self):
6782+
# gh-146553: __wrapped__ self-reference must raise ValueError,
6783+
# not loop forever.
6784+
def f(x: int) -> str: ...
6785+
f.__wrapped__ = f
6786+
with self.assertRaisesRegex(ValueError, 'wrapper loop'):
6787+
get_type_hints(f)
6788+
6789+
def test_get_type_hints_wrapped_cycle_mutual(self):
6790+
# gh-146553: mutual __wrapped__ cycle (a -> b -> a) must raise
6791+
# ValueError, not loop forever.
6792+
def a(): ...
6793+
def b(): ...
6794+
a.__wrapped__ = b
6795+
b.__wrapped__ = a
6796+
with self.assertRaisesRegex(ValueError, 'wrapper loop'):
6797+
get_type_hints(a)
6798+
67816799
def test_get_type_hints_annotated(self):
67826800
def foobar(x: List['X']): ...
67836801
X = Annotated[int, (1, 10)]

Lib/typing.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2416,8 +2416,12 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
24162416
else:
24172417
nsobj = obj
24182418
# Find globalns for the unwrapped object.
2419+
seen = {id(nsobj)}
24192420
while hasattr(nsobj, '__wrapped__'):
24202421
nsobj = nsobj.__wrapped__
2422+
if id(nsobj) in seen:
2423+
raise ValueError(f'wrapper loop when unwrapping {obj!r}')
2424+
seen.add(id(nsobj))
24212425
globalns = getattr(nsobj, '__globals__', {})
24222426
if localns is None:
24232427
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)