Skip to content

Commit 2fbcd7b

Browse files
committed
Fix yet another bug and add many more tests
1 parent bccaea2 commit 2fbcd7b

File tree

3 files changed

+82
-12
lines changed

3 files changed

+82
-12
lines changed

Lib/test/test_typing.py

+44-3
Original file line numberDiff line numberDiff line change
@@ -4858,7 +4858,7 @@ def f(x: X): ...
48584858
{'x': list[list[ForwardRef('X')]]}
48594859
)
48604860

4861-
def test_pep695_generic_with_future_annotations(self):
4861+
def test_pep695_generic_class_with_future_annotations(self):
48624862
original_globals = dict(ann_module695.__dict__)
48634863

48644864
hints_for_A = get_type_hints(ann_module695.A)
@@ -4867,15 +4867,21 @@ def test_pep695_generic_with_future_annotations(self):
48674867
self.assertEqual(hints_for_A["y"].__args__[0], Unpack[A_type_params[1]])
48684868
self.assertIs(hints_for_A["z"].__args__[0], A_type_params[2])
48694869

4870+
# should not have changed as a result of the get_type_hints() calls!
4871+
self.assertEqual(ann_module695.__dict__, original_globals)
4872+
4873+
def test_pep695_generic_class_with_future_annotations_and_local_shadowing(self):
48704874
hints_for_B = get_type_hints(ann_module695.B)
48714875
self.assertEqual(hints_for_B, {"x": int, "y": str, "z": bytes})
48724876

4877+
def test_pep695_generic_class_with_future_annotations_name_clash_with_global_vars(self):
48734878
hints_for_C = get_type_hints(ann_module695.C)
48744879
self.assertEqual(
48754880
set(hints_for_C.values()),
48764881
set(ann_module695.C.__type_params__)
48774882
)
48784883

4884+
def test_pep_695_generic_function_with_future_annotations(self):
48794885
hints_for_generic_function = get_type_hints(ann_module695.generic_function)
48804886
func_t_params = ann_module695.generic_function.__type_params__
48814887
self.assertEqual(
@@ -4886,6 +4892,13 @@ def test_pep695_generic_with_future_annotations(self):
48864892
self.assertIs(hints_for_generic_function["z"].__origin__, func_t_params[2])
48874893
self.assertIs(hints_for_generic_function["zz"].__origin__, func_t_params[2])
48884894

4895+
def test_pep_695_generic_function_with_future_annotations_name_clash_with_global_vars(self):
4896+
self.assertEqual(
4897+
set(get_type_hints(ann_module695.generic_function_2).values()),
4898+
set(ann_module695.generic_function_2.__type_params__)
4899+
)
4900+
4901+
def test_pep_695_generic_method_with_future_annotations(self):
48894902
hints_for_generic_method = get_type_hints(ann_module695.D.generic_method)
48904903
params = {
48914904
param.__name__: param
@@ -4896,8 +4909,36 @@ def test_pep695_generic_with_future_annotations(self):
48964909
{"x": params["Foo"], "y": params["Bar"], "return": types.NoneType}
48974910
)
48984911

4899-
# should not have changed as a result of the get_type_hints() calls!
4900-
self.assertEqual(ann_module695.__dict__, original_globals)
4912+
def test_pep_695_generic_method_with_future_annotations_name_clash_with_global_vars(self):
4913+
self.assertEqual(
4914+
set(get_type_hints(ann_module695.D.generic_method_2).values()),
4915+
set(ann_module695.D.generic_method_2.__type_params__)
4916+
)
4917+
4918+
def test_pep_695_generics_with_future_annotations_nested_in_function(self):
4919+
results = ann_module695.nested()
4920+
4921+
self.assertEqual(
4922+
set(results.hints_for_E.values()),
4923+
set(results.E.__type_params__)
4924+
)
4925+
self.assertEqual(
4926+
set(results.hints_for_E_meth.values()),
4927+
set(results.E.generic_method.__type_params__)
4928+
)
4929+
self.assertNotEqual(
4930+
set(results.hints_for_E_meth.values()),
4931+
set(results.E.__type_params__)
4932+
)
4933+
self.assertEqual(
4934+
set(results.hints_for_E_meth.values()).intersection(results.E.__type_params__),
4935+
set()
4936+
)
4937+
4938+
self.assertEqual(
4939+
set(results.hints_for_generic_func.values()),
4940+
set(results.generic_func.__type_params__)
4941+
)
49014942

49024943
def test_extended_generic_rules_subclassing(self):
49034944
class T1(Tuple[T, KT]): ...

Lib/test/typinganndata/ann_module695.py

+32
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,42 @@ def generic_function[T, *Ts, **P](
3131
) -> None: ...
3232

3333

34+
def generic_function_2[Eggs, **Spam](x: Eggs, y: Spam): pass
35+
36+
3437
class D:
3538
Foo = int
3639
Bar = str
3740

3841
def generic_method[Foo, **Bar](
3942
self, x: Foo, y: Bar
4043
) -> None: ...
44+
45+
def generic_method_2[Eggs, **Spam](self, x: Eggs, y: Spam): pass
46+
47+
48+
def nested():
49+
from types import SimpleNamespace
50+
from typing import get_type_hints
51+
52+
Eggs = bytes
53+
Spam = memoryview
54+
55+
56+
class E[Eggs, **Spam]:
57+
x: Eggs
58+
y: Spam
59+
60+
def generic_method[Eggs, **Spam](self, x: Eggs, y: Spam): pass
61+
62+
63+
def generic_function[Eggs, **Spam](x: Eggs, y: Spam): pass
64+
65+
66+
return SimpleNamespace(
67+
E=E,
68+
hints_for_E=get_type_hints(E),
69+
hints_for_E_meth=get_type_hints(E.generic_method),
70+
generic_func=generic_function,
71+
hints_for_generic_func=get_type_hints(generic_function)
72+
)

Lib/typing.py

+6-9
Original file line numberDiff line numberDiff line change
@@ -1069,15 +1069,12 @@ def _evaluate(self, globalns, localns, type_params=_sentinel, *, recursive_guard
10691069
# but should in turn be overridden by names in the class scope
10701070
# (which here are called `globalns`!)
10711071
if type_params:
1072-
if self.__forward_is_class__:
1073-
globalns, localns = dict(globalns), dict(localns)
1074-
for param in type_params:
1075-
param_name = param.__name__
1076-
if param_name not in globalns:
1077-
globalns[param_name] = param
1078-
localns.pop(param_name, None)
1079-
else:
1080-
localns = {param.__name__: param for param in type_params} | localns
1072+
globalns, localns = dict(globalns), dict(localns)
1073+
for param in type_params:
1074+
param_name = param.__name__
1075+
if not self.__forward_is_class__ or param_name not in globalns:
1076+
globalns[param_name] = param
1077+
localns.pop(param_name, None)
10811078

10821079
type_ = _type_check(
10831080
eval(self.__forward_code__, globalns, localns),

0 commit comments

Comments
 (0)