Skip to content

Commit 4e9afee

Browse files
committed
gh-146553: Use inspect.unwrap() for cycle detection instead of custom helper
1 parent 61a821b commit 4e9afee

File tree

2 files changed

+5
-25
lines changed

2 files changed

+5
-25
lines changed

Lib/test/test_typing.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6842,7 +6842,7 @@ def test_get_type_hints_wrapped_cycle_self(self):
68426842
# not loop forever.
68436843
def f(x: int) -> str: ...
68446844
f.__wrapped__ = f
6845-
with self.assertRaises(ValueError):
6845+
with self.assertRaisesRegex(ValueError, 'wrapper loop'):
68466846
get_type_hints(f)
68476847

68486848
def test_get_type_hints_wrapped_cycle_mutual(self):
@@ -6852,19 +6852,9 @@ def a(): ...
68526852
def b(): ...
68536853
a.__wrapped__ = b
68546854
b.__wrapped__ = a
6855-
with self.assertRaises(ValueError):
6855+
with self.assertRaisesRegex(ValueError, 'wrapper loop'):
68566856
get_type_hints(a)
68576857

6858-
def test_get_type_hints_wrapped_chain_no_cycle(self):
6859-
# gh-146553: a valid (non-cyclic) __wrapped__ chain must still work.
6860-
def inner(x: int) -> str: ...
6861-
def middle(x: int) -> str: ...
6862-
middle.__wrapped__ = inner
6863-
def outer(x: int) -> str: ...
6864-
outer.__wrapped__ = middle
6865-
# No cycle — should return the annotations without raising.
6866-
self.assertEqual(get_type_hints(outer), {'x': int, 'return': str})
6867-
68686858
def test_get_type_hints_annotated(self):
68696859
def foobar(x: List['X']): ...
68706860
X = Annotated[int, (1, 10)]

Lib/typing.py

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1972,6 +1972,7 @@ def _lazy_load_getattr_static():
19721972

19731973
_cleanups.append(_lazy_load_getattr_static.cache_clear)
19741974

1975+
19751976
def _pickle_psargs(psargs):
19761977
return ParamSpecArgs, (psargs.__origin__,)
19771978

@@ -2483,20 +2484,9 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
24832484
if isinstance(obj, types.ModuleType):
24842485
globalns = obj.__dict__
24852486
else:
2486-
nsobj = obj
24872487
# Find globalns for the unwrapped object.
2488-
# Use an id-based visited set to detect and break on cycles in the
2489-
# __wrapped__ chain (e.g. f.__wrapped__ = f), matching the behavior
2490-
# of inspect.unwrap().
2491-
_seen_ids = {id(nsobj)}
2492-
while hasattr(nsobj, '__wrapped__'):
2493-
nsobj = nsobj.__wrapped__
2494-
_nsobj_id = id(nsobj)
2495-
if _nsobj_id in _seen_ids:
2496-
raise ValueError(
2497-
f'wrapper loop when unwrapping {obj!r}'
2498-
)
2499-
_seen_ids.add(_nsobj_id)
2488+
import inspect
2489+
nsobj = inspect.unwrap(obj)
25002490
globalns = getattr(nsobj, '__globals__', {})
25012491
if localns is None:
25022492
localns = globalns

0 commit comments

Comments
 (0)