Skip to content

Commit 30e5bd8

Browse files
bpo-37838: get_type_hints for wrapped functions with forward reference (GH-17126)
https://bugs.python.org/issue37838 (cherry picked from commit 0aca3a3) Co-authored-by: benedwards14 <[email protected]>
1 parent 97c301b commit 30e5bd8

File tree

4 files changed

+28
-1
lines changed

4 files changed

+28
-1
lines changed

Lib/test/ann_module.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"""
77

88
from typing import Optional
9+
from functools import wraps
910

1011
__annotations__[1] = 2
1112

@@ -51,3 +52,9 @@ def foo(x: int = 10):
5152
def bar(y: List[str]):
5253
x: str = 'yes'
5354
bar()
55+
56+
def dec(func):
57+
@wraps(func)
58+
def wrapper(*args, **kwargs):
59+
return func(*args, **kwargs)
60+
return wrapper

Lib/test/test_typing.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,6 +1787,16 @@ async def g_with(am: AsyncContextManager[int]):
17871787

17881788
gth = get_type_hints
17891789

1790+
class ForRefExample:
1791+
@ann_module.dec
1792+
def func(self: 'ForRefExample'):
1793+
pass
1794+
1795+
@ann_module.dec
1796+
@ann_module.dec
1797+
def nested(self: 'ForRefExample'):
1798+
pass
1799+
17901800

17911801
class GetTypeHintTests(BaseTestCase):
17921802
def test_get_type_hints_from_various_objects(self):
@@ -1885,6 +1895,11 @@ def test_get_type_hints_ClassVar(self):
18851895
'x': ClassVar[Optional[B]]})
18861896
self.assertEqual(gth(G), {'lst': ClassVar[List[T]]})
18871897

1898+
def test_get_type_hints_wrapped_decoratored_func(self):
1899+
expects = {'self': ForRefExample}
1900+
self.assertEqual(gth(ForRefExample.func), expects)
1901+
self.assertEqual(gth(ForRefExample.nested), expects)
1902+
18881903

18891904
class CollectionsAbcTests(BaseTestCase):
18901905

Lib/typing.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,11 @@ def get_type_hints(obj, globalns=None, localns=None):
981981
if isinstance(obj, types.ModuleType):
982982
globalns = obj.__dict__
983983
else:
984-
globalns = getattr(obj, '__globals__', {})
984+
nsobj = obj
985+
# Find globalns for the unwrapped object.
986+
while hasattr(nsobj, '__wrapped__'):
987+
nsobj = nsobj.__wrapped__
988+
globalns = getattr(nsobj, '__globals__', {})
985989
if localns is None:
986990
localns = globalns
987991
elif localns is None:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:meth:`typing.get_type_hints` properly handles functions decorated with :meth:`functools.wraps`.

0 commit comments

Comments
 (0)