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`. |