diff options
author | Ćukasz Langa <lukasz@langa.pl> | 2017-09-14 18:33:00 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-14 18:33:00 (GMT) |
commit | f350a268a7071ce7d7a5bb86a9b1229782d4963b (patch) | |
tree | 04b38394dea9be76bea00f41b488c3e3a223dd34 /Lib/typing.py | |
parent | d393c1b227f22fb9af66040b2b367c99a4d1fa9a (diff) | |
download | cpython-f350a268a7071ce7d7a5bb86a9b1229782d4963b.zip cpython-f350a268a7071ce7d7a5bb86a9b1229782d4963b.tar.gz cpython-f350a268a7071ce7d7a5bb86a9b1229782d4963b.tar.bz2 |
bpo-28556: typing.get_type_hints: better globalns for classes and modules (#3582)
This makes the default behavior (without specifying `globalns` manually) more
predictable for users, finds the right globalns automatically.
Implementation for classes assumes has a `__module__` attribute and that module
is present in `sys.modules`. It does this recursively for all bases in the
MRO. For modules, the implementation just uses their `__dict__` directly.
This is backwards compatible, will just raise fewer exceptions in naive user
code.
Originally implemented and reviewed at https://github.com/python/typing/pull/470.
Diffstat (limited to 'Lib/typing.py')
-rw-r--r-- | Lib/typing.py | 27 |
1 files changed, 18 insertions, 9 deletions
diff --git a/Lib/typing.py b/Lib/typing.py index 609f813..c00a3a1 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1481,8 +1481,9 @@ def get_type_hints(obj, globalns=None, localns=None): search order is locals first, then globals. - If no dict arguments are passed, an attempt is made to use the - globals from obj, and these are also used as the locals. If the - object does not appear to have globals, an exception is raised. + globals from obj (or the respective module's globals for classes), + and these are also used as the locals. If the object does not appear + to have globals, an empty dictionary is used. - If one dict argument is passed, it is used for both globals and locals. @@ -1493,25 +1494,33 @@ def get_type_hints(obj, globalns=None, localns=None): if getattr(obj, '__no_type_check__', None): return {} - if globalns is None: - globalns = getattr(obj, '__globals__', {}) - if localns is None: - localns = globalns - elif localns is None: - localns = globalns # Classes require a special treatment. if isinstance(obj, type): hints = {} for base in reversed(obj.__mro__): + if globalns is None: + base_globals = sys.modules[base.__module__].__dict__ + else: + base_globals = globalns ann = base.__dict__.get('__annotations__', {}) for name, value in ann.items(): if value is None: value = type(None) if isinstance(value, str): value = _ForwardRef(value) - value = _eval_type(value, globalns, localns) + value = _eval_type(value, base_globals, localns) hints[name] = value return hints + + if globalns is None: + if isinstance(obj, types.ModuleType): + globalns = obj.__dict__ + else: + globalns = getattr(obj, '__globals__', {}) + if localns is None: + localns = globalns + elif localns is None: + localns = globalns hints = getattr(obj, '__annotations__', None) if hints is None: # Return empty annotations for something that _could_ have them. |