bpo-37838: get_type_hints for wrapped functions with forward referenc… · python/cpython@30e5bd8 (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 |
---|---|---|
@@ -1787,6 +1787,16 @@ async def g_with(am: AsyncContextManager[int]): | ||
1787 | 1787 | |
1788 | 1788 | gth = get_type_hints |
1789 | 1789 | |
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 | + | |
1790 | 1800 | |
1791 | 1801 | class GetTypeHintTests(BaseTestCase): |
1792 | 1802 | def test_get_type_hints_from_various_objects(self): |
@@ -1885,6 +1895,11 @@ def test_get_type_hints_ClassVar(self): | ||
1885 | 1895 | 'x': ClassVar[Optional[B]]}) |
1886 | 1896 | self.assertEqual(gth(G), {'lst': ClassVar[List[T]]}) |
1887 | 1897 | |
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 | + | |
1888 | 1903 | |
1889 | 1904 | class CollectionsAbcTests(BaseTestCase): |
1890 | 1905 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -981,7 +981,11 @@ def get_type_hints(obj, globalns=None, localns=None): | ||
981 | 981 | if isinstance(obj, types.ModuleType): |
982 | 982 | globalns = obj.__dict__ |
983 | 983 | 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__', {}) | |
985 | 989 | if localns is None: |
986 | 990 | localns = globalns |
987 | 991 | 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`. |