Skip to content

Commit 7ee9e05

Browse files
Backport type_params fix from CPython (#646)
1 parent 1e8eb9c commit 7ee9e05

File tree

3 files changed

+9
-19
lines changed

3 files changed

+9
-19
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
[`annotationlib.type_repr`](https://docs.python.org/3.14/library/annotationlib.html#annotationlib.type_repr),
55
introduced in Python 3.14 (CPython PR [#124551](https://github.com/python/cpython/pull/124551),
66
originally by Jelle Zijlstra). Patch by Semyon Moroz.
7-
7+
- Fix behavior of type params in `typing_extensions.evaluate_forward_ref`. Backport of
8+
CPython PR [#137227](https://github.com/python/cpython/pull/137227) by Jelle Zijlstra.
89

910
# Release 4.14.1 (July 4, 2025)
1011

src/test_typing_extensions.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9194,10 +9194,9 @@ class Gen[Tx]:
91949194
not_Tx = TypeVar("Tx") # different TypeVar with same name
91959195
self.assertIs(evaluate_forward_ref(typing.ForwardRef("Tx"), type_params=(not_Tx,), owner=Gen), not_Tx)
91969196

9197-
# globals can take higher precedence
9198-
if _FORWARD_REF_HAS_CLASS:
9199-
self.assertIs(evaluate_forward_ref(typing.ForwardRef("Tx", is_class=True), owner=Gen, globals={"Tx": str}), str)
9200-
self.assertIs(evaluate_forward_ref(typing.ForwardRef("Tx", is_class=True), owner=Gen, type_params=(not_Tx,), globals={"Tx": str}), str)
9197+
# globals do not take higher precedence
9198+
self.assertIs(evaluate_forward_ref(typing.ForwardRef("Tx", is_class=True), owner=Gen, globals={"Tx": str}), Tx)
9199+
self.assertIs(evaluate_forward_ref(typing.ForwardRef("Tx", is_class=True), owner=Gen, type_params=(not_Tx,), globals={"Tx": str}), not_Tx)
92019200

92029201
with self.assertRaises(NameError):
92039202
evaluate_forward_ref(typing.ForwardRef("alias"), type_params=Gen.__type_params__)

src/typing_extensions.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4065,23 +4065,13 @@ def _eval_with_owner(
40654065
# as a way of emulating annotation scopes when calling `eval()`
40664066
type_params = getattr(owner, "__type_params__", None)
40674067

4068-
# type parameters require some special handling,
4069-
# as they exist in their own scope
4070-
# but `eval()` does not have a dedicated parameter for that scope.
4071-
# For classes, names in type parameter scopes should override
4072-
# names in the global scope (which here are called `localns`!),
4073-
# but should in turn be overridden by names in the class scope
4074-
# (which here are called `globalns`!)
4068+
# Type parameters exist in their own scope, which is logically
4069+
# between the locals and the globals. We simulate this by adding
4070+
# them to the globals.
40754071
if type_params is not None:
40764072
globals = dict(globals)
4077-
locals = dict(locals)
40784073
for param in type_params:
4079-
param_name = param.__name__
4080-
if (
4081-
_FORWARD_REF_HAS_CLASS and not forward_ref.__forward_is_class__
4082-
) or param_name not in globals:
4083-
globals[param_name] = param
4084-
locals.pop(param_name, None)
4074+
globals[param.__name__] = param
40854075

40864076
arg = forward_ref.__forward_arg__
40874077
if arg.isidentifier() and not keyword.iskeyword(arg):

0 commit comments

Comments
 (0)