aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/typing.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/typing.py')
-rw-r--r--Lib/typing.py102
1 files changed, 54 insertions, 48 deletions
diff --git a/Lib/typing.py b/Lib/typing.py
index 98af61be8b0..f1455c273d3 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -407,6 +407,17 @@ def _tp_cache(func=None, /, *, typed=False):
return decorator
+def _rebuild_generic_alias(alias: GenericAlias, args: tuple[object, ...]) -> GenericAlias:
+ is_unpacked = alias.__unpacked__
+ if _should_unflatten_callable_args(alias, args):
+ t = alias.__origin__[(args[:-1], args[-1])]
+ else:
+ t = alias.__origin__[args]
+ if is_unpacked:
+ t = Unpack[t]
+ return t
+
+
def _deprecation_warning_for_no_type_params_passed(funcname: str) -> None:
import warnings
@@ -426,20 +437,14 @@ class _Sentinel:
return '<sentinel>'
-_sentinel = _Sentinel()
-
-
-def _eval_type(t, globalns, localns, type_params=_sentinel, *, recursive_guard=frozenset(),
- format=None, owner=None):
+def _eval_type(t, globalns, localns, type_params, *, recursive_guard=frozenset(),
+ format=None, owner=None, parent_fwdref=None):
"""Evaluate all forward references in the given type t.
For use of globalns and localns see the docstring for get_type_hints().
recursive_guard is used to prevent infinite recursion with a recursive
ForwardRef.
"""
- if type_params is _sentinel:
- _deprecation_warning_for_no_type_params_passed("typing._eval_type")
- type_params = ()
if isinstance(t, _lazy_annotationlib.ForwardRef):
# If the forward_ref has __forward_module__ set, evaluate() infers the globals
# from the module, and it will probably pick better than the globals we have here.
@@ -451,28 +456,23 @@ def _eval_type(t, globalns, localns, type_params=_sentinel, *, recursive_guard=f
if isinstance(t, (_GenericAlias, GenericAlias, Union)):
if isinstance(t, GenericAlias):
args = tuple(
- _make_forward_ref(arg) if isinstance(arg, str) else arg
+ _make_forward_ref(arg, parent_fwdref=parent_fwdref) if isinstance(arg, str) else arg
for arg in t.__args__
)
- is_unpacked = t.__unpacked__
- if _should_unflatten_callable_args(t, args):
- t = t.__origin__[(args[:-1], args[-1])]
- else:
- t = t.__origin__[args]
- if is_unpacked:
- t = Unpack[t]
+ else:
+ args = t.__args__
ev_args = tuple(
_eval_type(
a, globalns, localns, type_params, recursive_guard=recursive_guard,
format=format, owner=owner,
)
- for a in t.__args__
+ for a in args
)
if ev_args == t.__args__:
return t
if isinstance(t, GenericAlias):
- return GenericAlias(t.__origin__, ev_args)
+ return _rebuild_generic_alias(t, ev_args)
if isinstance(t, Union):
return functools.reduce(operator.or_, ev_args)
else:
@@ -936,7 +936,12 @@ def TypeIs(self, parameters):
return _GenericAlias(self, (item,))
-def _make_forward_ref(code, **kwargs):
+def _make_forward_ref(code, *, parent_fwdref=None, **kwargs):
+ if parent_fwdref is not None:
+ if parent_fwdref.__forward_module__ is not None:
+ kwargs['module'] = parent_fwdref.__forward_module__
+ if parent_fwdref.__owner__ is not None:
+ kwargs['owner'] = parent_fwdref.__owner__
forward_ref = _lazy_annotationlib.ForwardRef(code, **kwargs)
# For compatibility, eagerly compile the forwardref's code.
forward_ref.__forward_code__
@@ -956,12 +961,8 @@ def evaluate_forward_ref(
"""Evaluate a forward reference as a type hint.
This is similar to calling the ForwardRef.evaluate() method,
- but unlike that method, evaluate_forward_ref() also:
-
- * Recursively evaluates forward references nested within the type hint.
- * Rejects certain objects that are not valid type hints.
- * Replaces type hints that evaluate to None with types.NoneType.
- * Supports the *FORWARDREF* and *STRING* formats.
+ but unlike that method, evaluate_forward_ref() also
+ recursively evaluates forward references nested within the type hint.
*forward_ref* must be an instance of ForwardRef. *owner*, if given,
should be the object that holds the annotations that the forward reference
@@ -981,29 +982,31 @@ def evaluate_forward_ref(
if forward_ref.__forward_arg__ in _recursive_guard:
return forward_ref
- try:
- value = forward_ref.evaluate(globals=globals, locals=locals,
- type_params=type_params, owner=owner)
- except NameError:
- if format == _lazy_annotationlib.Format.FORWARDREF:
- return forward_ref
- else:
- raise
-
- type_ = _type_check(
- value,
- "Forward references must evaluate to types.",
- is_argument=forward_ref.__forward_is_argument__,
- allow_special_forms=forward_ref.__forward_is_class__,
- )
+ if format is None:
+ format = _lazy_annotationlib.Format.VALUE
+ value = forward_ref.evaluate(globals=globals, locals=locals,
+ type_params=type_params, owner=owner, format=format)
+
+ if (isinstance(value, _lazy_annotationlib.ForwardRef)
+ and format == _lazy_annotationlib.Format.FORWARDREF):
+ return value
+
+ if isinstance(value, str):
+ value = _make_forward_ref(value, module=forward_ref.__forward_module__,
+ owner=owner or forward_ref.__owner__,
+ is_argument=forward_ref.__forward_is_argument__,
+ is_class=forward_ref.__forward_is_class__)
+ if owner is None:
+ owner = forward_ref.__owner__
return _eval_type(
- type_,
+ value,
globals,
locals,
type_params,
recursive_guard=_recursive_guard | {forward_ref.__forward_arg__},
format=format,
owner=owner,
+ parent_fwdref=forward_ref,
)
@@ -1863,7 +1866,9 @@ def _allow_reckless_class_checks(depth=2):
The abc and functools modules indiscriminately call isinstance() and
issubclass() on the whole MRO of a user class, which may contain protocols.
"""
- return _caller(depth) in {'abc', 'functools', None}
+ # gh-136047: When `_abc` module is not available, `_py_abc` is required to
+ # allow `_py_abc.ABCMeta` fallback.
+ return _caller(depth) in {'abc', '_py_abc', 'functools', None}
_PROTO_ALLOWLIST = {
@@ -2338,12 +2343,12 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
# This only affects ForwardRefs.
base_globals, base_locals = base_locals, base_globals
for name, value in ann.items():
- if value is None:
- value = type(None)
if isinstance(value, str):
value = _make_forward_ref(value, is_argument=False, is_class=True)
value = _eval_type(value, base_globals, base_locals, base.__type_params__,
format=format, owner=obj)
+ if value is None:
+ value = type(None)
hints[name] = value
if include_extras or format == Format.STRING:
return hints
@@ -2377,8 +2382,6 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
localns = globalns
type_params = getattr(obj, "__type_params__", ())
for name, value in hints.items():
- if value is None:
- value = type(None)
if isinstance(value, str):
# class-level forward refs were handled above, this must be either
# a module-level annotation or a function argument annotation
@@ -2387,7 +2390,10 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
is_argument=not isinstance(obj, types.ModuleType),
is_class=False,
)
- hints[name] = _eval_type(value, globalns, localns, type_params, format=format, owner=obj)
+ value = _eval_type(value, globalns, localns, type_params, format=format, owner=obj)
+ if value is None:
+ value = type(None)
+ hints[name] = value
return hints if include_extras else {k: _strip_annotations(t) for k, t in hints.items()}
@@ -2406,7 +2412,7 @@ def _strip_annotations(t):
stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
if stripped_args == t.__args__:
return t
- return GenericAlias(t.__origin__, stripped_args)
+ return _rebuild_generic_alias(t, stripped_args)
if isinstance(t, Union):
stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
if stripped_args == t.__args__: