diff options
author | benedwards14 <53377856+benedwards14@users.noreply.github.com> | 2019-11-21 17:24:58 (GMT) |
---|---|---|
committer | Ivan Levkivskyi <levkivskyi@gmail.com> | 2019-11-21 17:24:58 (GMT) |
commit | 0aca3a3a1e68b4ca2d334ab5255dfc267719096e (patch) | |
tree | 49924b3d2c93eddef6fc735c49a6177b0d29744d | |
parent | 82f897bf8f72d09f537054d64a94e645ad23d8d6 (diff) | |
download | cpython-0aca3a3a1e68b4ca2d334ab5255dfc267719096e.zip cpython-0aca3a3a1e68b4ca2d334ab5255dfc267719096e.tar.gz cpython-0aca3a3a1e68b4ca2d334ab5255dfc267719096e.tar.bz2 |
bpo-37838: get_type_hints for wrapped functions with forward reference (GH-17126)
https://bugs.python.org/issue37838
-rw-r--r-- | Lib/test/ann_module.py | 7 | ||||
-rw-r--r-- | Lib/test/test_typing.py | 15 | ||||
-rw-r--r-- | Lib/typing.py | 6 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2019-11-21-11-39-17.bpo-37838.lRFcEC.rst | 1 |
4 files changed, 28 insertions, 1 deletions
diff --git a/Lib/test/ann_module.py b/Lib/test/ann_module.py index 9e6b87d..0567d6d 100644 --- a/Lib/test/ann_module.py +++ b/Lib/test/ann_module.py @@ -6,6 +6,7 @@ Empty lines above are for good reason (testing for correct line numbers) """ from typing import Optional +from functools import wraps __annotations__[1] = 2 @@ -51,3 +52,9 @@ def foo(x: int = 10): def bar(y: List[str]): x: str = 'yes' bar() + +def dec(func): + @wraps(func) + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + return wrapper diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 49417ef..ccd617c 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2778,6 +2778,16 @@ except StopIteration as e: gth = get_type_hints +class ForRefExample: + @ann_module.dec + def func(self: 'ForRefExample'): + pass + + @ann_module.dec + @ann_module.dec + def nested(self: 'ForRefExample'): + pass + class GetTypeHintTests(BaseTestCase): def test_get_type_hints_from_various_objects(self): @@ -2876,6 +2886,11 @@ class GetTypeHintTests(BaseTestCase): 'x': ClassVar[Optional[B]]}) self.assertEqual(gth(G), {'lst': ClassVar[List[T]]}) + def test_get_type_hints_wrapped_decoratored_func(self): + expects = {'self': ForRefExample} + self.assertEqual(gth(ForRefExample.func), expects) + self.assertEqual(gth(ForRefExample.nested), expects) + class GetUtilitiesTestCase(TestCase): def test_get_origin(self): diff --git a/Lib/typing.py b/Lib/typing.py index 27be37a..5523ee0 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1234,7 +1234,11 @@ def get_type_hints(obj, globalns=None, localns=None): if isinstance(obj, types.ModuleType): globalns = obj.__dict__ else: - globalns = getattr(obj, '__globals__', {}) + nsobj = obj + # Find globalns for the unwrapped object. + while hasattr(nsobj, '__wrapped__'): + nsobj = nsobj.__wrapped__ + globalns = getattr(nsobj, '__globals__', {}) if localns is None: localns = globalns elif localns is None: diff --git a/Misc/NEWS.d/next/Library/2019-11-21-11-39-17.bpo-37838.lRFcEC.rst b/Misc/NEWS.d/next/Library/2019-11-21-11-39-17.bpo-37838.lRFcEC.rst new file mode 100644 index 0000000..96d804a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-11-21-11-39-17.bpo-37838.lRFcEC.rst @@ -0,0 +1 @@ +:meth:`typing.get_type_hints` properly handles functions decorated with :meth:`functools.wraps`. |