mirror of
https://github.com/python/cpython.git
synced 2026-05-06 04:37:33 -04:00
gh-146553: Fix infinite loop in typing.get_type_hints() on circular __wrapped__ (#148595)
This commit is contained in:
@@ -6888,6 +6888,24 @@ class GetTypeHintsTests(BaseTestCase):
|
||||
self.assertEqual(gth(ForRefExample.func), expects)
|
||||
self.assertEqual(gth(ForRefExample.nested), expects)
|
||||
|
||||
def test_get_type_hints_wrapped_cycle_self(self):
|
||||
# gh-146553: __wrapped__ self-reference must raise ValueError,
|
||||
# not loop forever.
|
||||
def f(x: int) -> str: ...
|
||||
f.__wrapped__ = f
|
||||
with self.assertRaisesRegex(ValueError, 'wrapper loop'):
|
||||
get_type_hints(f)
|
||||
|
||||
def test_get_type_hints_wrapped_cycle_mutual(self):
|
||||
# gh-146553: mutual __wrapped__ cycle (a -> b -> a) must raise
|
||||
# ValueError, not loop forever.
|
||||
def a(): ...
|
||||
def b(): ...
|
||||
a.__wrapped__ = b
|
||||
b.__wrapped__ = a
|
||||
with self.assertRaisesRegex(ValueError, 'wrapper loop'):
|
||||
get_type_hints(a)
|
||||
|
||||
def test_get_type_hints_annotated(self):
|
||||
def foobar(x: List['X']): ...
|
||||
X = Annotated[int, (1, 10)]
|
||||
|
||||
@@ -2486,8 +2486,12 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
|
||||
else:
|
||||
nsobj = obj
|
||||
# Find globalns for the unwrapped object.
|
||||
seen = {id(nsobj)}
|
||||
while hasattr(nsobj, '__wrapped__'):
|
||||
nsobj = nsobj.__wrapped__
|
||||
if id(nsobj) in seen:
|
||||
raise ValueError(f'wrapper loop when unwrapping {obj!r}')
|
||||
seen.add(id(nsobj))
|
||||
globalns = getattr(nsobj, '__globals__', {})
|
||||
if localns is None:
|
||||
localns = globalns
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Fix infinite loop in :func:`typing.get_type_hints` when ``__wrapped__``
|
||||
forms a cycle. Patch by Shamil Abdulaev.
|
||||
Reference in New Issue
Block a user