bpo-37838: get_type_hints for wrapped functions with forward referenc… · python/cpython@0aca3a3 (original) (raw)

4 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
6 6 """
7 7
8 8 from typing import Optional
9 +from functools import wraps
9 10
10 11 __annotations__[1] = 2
11 12
@@ -51,3 +52,9 @@ def foo(x: int = 10):
51 52 def bar(y: List[str]):
52 53 x: str = 'yes'
53 54 bar()
55 +
56 +def dec(func):
57 +@wraps(func)
58 +def wrapper(*args, **kwargs):
59 +return func(*args, **kwargs)
60 +return wrapper
Original file line number Diff line number Diff line change
@@ -2778,6 +2778,16 @@ async def g_with(am: AsyncContextManager[int]):
2778 2778
2779 2779 gth = get_type_hints
2780 2780
2781 +class ForRefExample:
2782 +@ann_module.dec
2783 +def func(self: 'ForRefExample'):
2784 +pass
2785 +
2786 +@ann_module.dec
2787 +@ann_module.dec
2788 +def nested(self: 'ForRefExample'):
2789 +pass
2790 +
2781 2791
2782 2792 class GetTypeHintTests(BaseTestCase):
2783 2793 def test_get_type_hints_from_various_objects(self):
@@ -2876,6 +2886,11 @@ def test_get_type_hints_ClassVar(self):
2876 2886 'x': ClassVar[Optional[B]]})
2877 2887 self.assertEqual(gth(G), {'lst': ClassVar[List[T]]})
2878 2888
2889 +def test_get_type_hints_wrapped_decoratored_func(self):
2890 +expects = {'self': ForRefExample}
2891 +self.assertEqual(gth(ForRefExample.func), expects)
2892 +self.assertEqual(gth(ForRefExample.nested), expects)
2893 +
2879 2894
2880 2895 class GetUtilitiesTestCase(TestCase):
2881 2896 def test_get_origin(self):
Original file line number Diff line number Diff line change
@@ -1234,7 +1234,11 @@ def get_type_hints(obj, globalns=None, localns=None):
1234 1234 if isinstance(obj, types.ModuleType):
1235 1235 globalns = obj.__dict__
1236 1236 else:
1237 -globalns = getattr(obj, '__globals__', {})
1237 +nsobj = obj
1238 +# Find globalns for the unwrapped object.
1239 +while hasattr(nsobj, '__wrapped__'):
1240 +nsobj = nsobj.__wrapped__
1241 +globalns = getattr(nsobj, '__globals__', {})
1238 1242 if localns is None:
1239 1243 localns = globalns
1240 1244 elif localns is None:
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1 +:meth:`typing.get_type_hints` properly handles functions decorated with :meth:`functools.wraps`.