bpo-37838: get_type_hints for wrapped functions with forward reference (GH-17126)
![](https://secure.gravatar.com/avatar/cc7737cd64a84f1b5c61a160798e97ee.jpg?s=120&d=mm&r=g)
https://github.com/python/cpython/commit/30e5bd8471d7531d051796c01e8ede01ade... commit: 30e5bd8471d7531d051796c01e8ede01ade883dc branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> committer: GitHub <noreply@github.com> date: 2019-11-21T09:43:13-08:00 summary: bpo-37838: get_type_hints for wrapped functions with forward reference (GH-17126) https://bugs.python.org/issue37838 (cherry picked from commit 0aca3a3a1e68b4ca2d334ab5255dfc267719096e) Co-authored-by: benedwards14 <53377856+benedwards14@users.noreply.github.com> files: A Misc/NEWS.d/next/Library/2019-11-21-11-39-17.bpo-37838.lRFcEC.rst M Lib/test/ann_module.py M Lib/test/test_typing.py M Lib/typing.py diff --git a/Lib/test/ann_module.py b/Lib/test/ann_module.py index 9e6b87dac40f5..0567d6de1b647 100644 --- a/Lib/test/ann_module.py +++ b/Lib/test/ann_module.py @@ -6,6 +6,7 @@ """ 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 d8abc0ac96a10..087dc2a82f038 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -1787,6 +1787,16 @@ class HasForeignBaseClass(mod_generics_cache.A): 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): @@ -1885,6 +1895,11 @@ def test_get_type_hints_ClassVar(self): '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 CollectionsAbcTests(BaseTestCase): diff --git a/Lib/typing.py b/Lib/typing.py index ea8dea54a5a3d..607eb1f8fc31a 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -981,7 +981,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 0000000000000..96d804addeb60 --- /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`.
participants (1)
-
Miss Islington (bot)