summaryrefslogtreecommitdiffstats
path: root/Lib/typing.py
diff options
context:
space:
mode:
authorGermán Méndez Bravo <german.mb@gmail.com>2021-07-17 03:49:30 (GMT)
committerGitHub <noreply@github.com>2021-07-17 03:49:30 (GMT)
commit889036f7ef7290ef15b6c3373023f6a35387af0c (patch)
tree9e9f518eaaba90dd211473bc917b9938f4144fb9 /Lib/typing.py
parentbf89ff96e6ba21bb52b8597b5e51e8ffc57e6589 (diff)
downloadcpython-889036f7ef7290ef15b6c3373023f6a35387af0c.zip
cpython-889036f7ef7290ef15b6c3373023f6a35387af0c.tar.gz
cpython-889036f7ef7290ef15b6c3373023f6a35387af0c.tar.bz2
bpo-41249: Fix postponed annotations for TypedDict (GH-27017)
This fixes TypedDict to work with get_type_hints and postponed evaluation of annotations across modules. This is done by adding the module name to ForwardRef at the time the object is created and using that to resolve the globals during the evaluation. Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Diffstat (limited to 'Lib/typing.py')
-rw-r--r--Lib/typing.py20
1 files changed, 13 insertions, 7 deletions
diff --git a/Lib/typing.py b/Lib/typing.py
index f7386ea..cc7f41d 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -135,16 +135,16 @@ __all__ = [
# legitimate imports of those modules.
-def _type_convert(arg):
+def _type_convert(arg, module=None):
"""For converting None to type(None), and strings to ForwardRef."""
if arg is None:
return type(None)
if isinstance(arg, str):
- return ForwardRef(arg)
+ return ForwardRef(arg, module=module)
return arg
-def _type_check(arg, msg, is_argument=True):
+def _type_check(arg, msg, is_argument=True, module=None):
"""Check that the argument is a type, and return it (internal helper).
As a special case, accept None and return type(None) instead. Also wrap strings
@@ -160,7 +160,7 @@ def _type_check(arg, msg, is_argument=True):
if is_argument:
invalid_generic_forms = invalid_generic_forms + (ClassVar, Final)
- arg = _type_convert(arg)
+ arg = _type_convert(arg, module=module)
if (isinstance(arg, _GenericAlias) and
arg.__origin__ in invalid_generic_forms):
raise TypeError(f"{arg} is not valid as type argument")
@@ -633,9 +633,9 @@ class ForwardRef(_Final, _root=True):
__slots__ = ('__forward_arg__', '__forward_code__',
'__forward_evaluated__', '__forward_value__',
- '__forward_is_argument__')
+ '__forward_is_argument__', '__forward_module__')
- def __init__(self, arg, is_argument=True):
+ def __init__(self, arg, is_argument=True, module=None):
if not isinstance(arg, str):
raise TypeError(f"Forward reference must be a string -- got {arg!r}")
try:
@@ -647,6 +647,7 @@ class ForwardRef(_Final, _root=True):
self.__forward_evaluated__ = False
self.__forward_value__ = None
self.__forward_is_argument__ = is_argument
+ self.__forward_module__ = module
def _evaluate(self, globalns, localns, recursive_guard):
if self.__forward_arg__ in recursive_guard:
@@ -658,6 +659,10 @@ class ForwardRef(_Final, _root=True):
globalns = localns
elif localns is None:
localns = globalns
+ if self.__forward_module__ is not None:
+ globalns = getattr(
+ sys.modules.get(self.__forward_module__, None), '__dict__', globalns
+ )
type_ =_type_check(
eval(self.__forward_code__, globalns, localns),
"Forward references must evaluate to types.",
@@ -2242,7 +2247,8 @@ class _TypedDictMeta(type):
own_annotation_keys = set(own_annotations.keys())
msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
own_annotations = {
- n: _type_check(tp, msg) for n, tp in own_annotations.items()
+ n: _type_check(tp, msg, module=tp_dict.__module__)
+ for n, tp in own_annotations.items()
}
required_keys = set()
optional_keys = set()