summaryrefslogtreecommitdiffstats
path: root/Lib/typing.py
diff options
context:
space:
mode:
authorƁukasz Langa <lukasz@langa.pl>2017-09-14 18:33:00 (GMT)
committerGitHub <noreply@github.com>2017-09-14 18:33:00 (GMT)
commitf350a268a7071ce7d7a5bb86a9b1229782d4963b (patch)
tree04b38394dea9be76bea00f41b488c3e3a223dd34 /Lib/typing.py
parentd393c1b227f22fb9af66040b2b367c99a4d1fa9a (diff)
downloadcpython-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.py27
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.