gh-137226: Fix behavior of ForwardRef.evaluate with type_params (#137227)

The previous behavior was copied from earlier typing code. It works around the way
typing.get_type_hints passes its namespaces, but I don't think the behavior is logical
or correct.
This commit is contained in:
Jelle Zijlstra 2025-08-13 06:47:47 -07:00 committed by GitHub
parent 70730ad041
commit 089a324a42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 47 additions and 14 deletions

View file

@ -2366,10 +2366,13 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
# *base_globals* first rather than *base_locals*.
# This only affects ForwardRefs.
base_globals, base_locals = base_locals, base_globals
type_params = base.__type_params__
base_globals, base_locals = _add_type_params_to_scope(
type_params, base_globals, base_locals, True)
for name, value in ann.items():
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__,
value = _eval_type(value, base_globals, base_locals, (),
format=format, owner=obj)
if value is None:
value = type(None)
@ -2405,6 +2408,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
elif localns is None:
localns = globalns
type_params = getattr(obj, "__type_params__", ())
globalns, localns = _add_type_params_to_scope(type_params, globalns, localns, False)
for name, value in hints.items():
if isinstance(value, str):
# class-level forward refs were handled above, this must be either
@ -2414,13 +2418,27 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
is_argument=not isinstance(obj, types.ModuleType),
is_class=False,
)
value = _eval_type(value, globalns, localns, type_params, format=format, owner=obj)
value = _eval_type(value, globalns, localns, (), 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()}
# Add type parameters to the globals and locals scope. This is needed for
# compatibility.
def _add_type_params_to_scope(type_params, globalns, localns, is_class):
if not type_params:
return globalns, localns
globalns = dict(globalns)
localns = dict(localns)
for param in type_params:
if not is_class or param.__name__ not in globalns:
globalns[param.__name__] = param
localns.pop(param.__name__, None)
return globalns, localns
def _strip_annotations(t):
"""Strip the annotations from a given type."""
if isinstance(t, _AnnotatedAlias):