aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/test/test_annotationlib.py
diff options
context:
space:
mode:
authorJelle Zijlstra <jelle.zijlstra@gmail.com>2024-07-23 14:16:50 -0700
committerGitHub <noreply@github.com>2024-07-23 21:16:50 +0000
commit7b7b90d1ce5116f29ad6c8120c0490824baa54e0 (patch)
treeb273afb5767b7a55fb52a277cc9512ebfc4ac12d /Lib/test/test_annotationlib.py
parent64e221d7ada8f6c20189035c7e81503f4c914f04 (diff)
downloadcpython-7b7b90d1ce5116f29ad6c8120c0490824baa54e0.tar.gz
cpython-7b7b90d1ce5116f29ad6c8120c0490824baa54e0.zip
gh-119180: Add `annotationlib` module to support PEP 649 (#119891)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Diffstat (limited to 'Lib/test/test_annotationlib.py')
-rw-r--r--Lib/test/test_annotationlib.py771
1 files changed, 771 insertions, 0 deletions
diff --git a/Lib/test/test_annotationlib.py b/Lib/test/test_annotationlib.py
new file mode 100644
index 00000000000..e68d63c91d1
--- /dev/null
+++ b/Lib/test/test_annotationlib.py
@@ -0,0 +1,771 @@
+"""Tests for the annotations module."""
+
+import annotationlib
+import functools
+import pickle
+import unittest
+from typing import Unpack
+
+from test.test_inspect import inspect_stock_annotations
+from test.test_inspect import inspect_stringized_annotations
+from test.test_inspect import inspect_stringized_annotations_2
+from test.test_inspect import inspect_stringized_annotations_pep695
+
+
+def times_three(fn):
+ @functools.wraps(fn)
+ def wrapper(a, b):
+ return fn(a * 3, b * 3)
+
+ return wrapper
+
+
+class TestFormat(unittest.TestCase):
+ def test_enum(self):
+ self.assertEqual(annotationlib.Format.VALUE.value, 1)
+ self.assertEqual(annotationlib.Format.VALUE, 1)
+
+ self.assertEqual(annotationlib.Format.FORWARDREF.value, 2)
+ self.assertEqual(annotationlib.Format.FORWARDREF, 2)
+
+ self.assertEqual(annotationlib.Format.SOURCE.value, 3)
+ self.assertEqual(annotationlib.Format.SOURCE, 3)
+
+
+class TestForwardRefFormat(unittest.TestCase):
+ def test_closure(self):
+ def inner(arg: x):
+ pass
+
+ anno = annotationlib.get_annotations(
+ inner, format=annotationlib.Format.FORWARDREF
+ )
+ fwdref = anno["arg"]
+ self.assertIsInstance(fwdref, annotationlib.ForwardRef)
+ self.assertEqual(fwdref.__forward_arg__, "x")
+ with self.assertRaises(NameError):
+ fwdref.evaluate()
+
+ x = 1
+ self.assertEqual(fwdref.evaluate(), x)
+
+ anno = annotationlib.get_annotations(
+ inner, format=annotationlib.Format.FORWARDREF
+ )
+ self.assertEqual(anno["arg"], x)
+
+ def test_function(self):
+ def f(x: int, y: doesntexist):
+ pass
+
+ anno = annotationlib.get_annotations(f, format=annotationlib.Format.FORWARDREF)
+ self.assertIs(anno["x"], int)
+ fwdref = anno["y"]
+ self.assertIsInstance(fwdref, annotationlib.ForwardRef)
+ self.assertEqual(fwdref.__forward_arg__, "doesntexist")
+ with self.assertRaises(NameError):
+ fwdref.evaluate()
+ self.assertEqual(fwdref.evaluate(globals={"doesntexist": 1}), 1)
+
+
+class TestSourceFormat(unittest.TestCase):
+ def test_closure(self):
+ x = 0
+
+ def inner(arg: x):
+ pass
+
+ anno = annotationlib.get_annotations(inner, format=annotationlib.Format.SOURCE)
+ self.assertEqual(anno, {"arg": "x"})
+
+ def test_function(self):
+ def f(x: int, y: doesntexist):
+ pass
+
+ anno = annotationlib.get_annotations(f, format=annotationlib.Format.SOURCE)
+ self.assertEqual(anno, {"x": "int", "y": "doesntexist"})
+
+ def test_expressions(self):
+ def f(
+ add: a + b,
+ sub: a - b,
+ mul: a * b,
+ matmul: a @ b,
+ truediv: a / b,
+ mod: a % b,
+ lshift: a << b,
+ rshift: a >> b,
+ or_: a | b,
+ xor: a ^ b,
+ and_: a & b,
+ floordiv: a // b,
+ pow_: a**b,
+ lt: a < b,
+ le: a <= b,
+ eq: a == b,
+ ne: a != b,
+ gt: a > b,
+ ge: a >= b,
+ invert: ~a,
+ neg: -a,
+ pos: +a,
+ getitem: a[b],
+ getattr: a.b,
+ call: a(b, *c, d=e), # **kwargs are not supported
+ *args: *a,
+ ):
+ pass
+
+ anno = annotationlib.get_annotations(f, format=annotationlib.Format.SOURCE)
+ self.assertEqual(
+ anno,
+ {
+ "add": "a + b",
+ "sub": "a - b",
+ "mul": "a * b",
+ "matmul": "a @ b",
+ "truediv": "a / b",
+ "mod": "a % b",
+ "lshift": "a << b",
+ "rshift": "a >> b",
+ "or_": "a | b",
+ "xor": "a ^ b",
+ "and_": "a & b",
+ "floordiv": "a // b",
+ "pow_": "a ** b",
+ "lt": "a < b",
+ "le": "a <= b",
+ "eq": "a == b",
+ "ne": "a != b",
+ "gt": "a > b",
+ "ge": "a >= b",
+ "invert": "~a",
+ "neg": "-a",
+ "pos": "+a",
+ "getitem": "a[b]",
+ "getattr": "a.b",
+ "call": "a(b, *c, d=e)",
+ "args": "*a",
+ },
+ )
+
+ def test_reverse_ops(self):
+ def f(
+ radd: 1 + a,
+ rsub: 1 - a,
+ rmul: 1 * a,
+ rmatmul: 1 @ a,
+ rtruediv: 1 / a,
+ rmod: 1 % a,
+ rlshift: 1 << a,
+ rrshift: 1 >> a,
+ ror: 1 | a,
+ rxor: 1 ^ a,
+ rand: 1 & a,
+ rfloordiv: 1 // a,
+ rpow: 1**a,
+ ):
+ pass
+
+ anno = annotationlib.get_annotations(f, format=annotationlib.Format.SOURCE)
+ self.assertEqual(
+ anno,
+ {
+ "radd": "1 + a",
+ "rsub": "1 - a",
+ "rmul": "1 * a",
+ "rmatmul": "1 @ a",
+ "rtruediv": "1 / a",
+ "rmod": "1 % a",
+ "rlshift": "1 << a",
+ "rrshift": "1 >> a",
+ "ror": "1 | a",
+ "rxor": "1 ^ a",
+ "rand": "1 & a",
+ "rfloordiv": "1 // a",
+ "rpow": "1 ** a",
+ },
+ )
+
+ def test_nested_expressions(self):
+ def f(
+ nested: list[Annotated[set[int], "set of ints", 4j]],
+ set: {a + b}, # single element because order is not guaranteed
+ dict: {a + b: c + d, "key": e + g},
+ list: [a, b, c],
+ tuple: (a, b, c),
+ slice: (a[b:c], a[b:c:d], a[:c], a[b:], a[:], a[::d], a[b::d]),
+ extended_slice: a[:, :, c:d],
+ unpack1: [*a],
+ unpack2: [*a, b, c],
+ ):
+ pass
+
+ anno = annotationlib.get_annotations(f, format=annotationlib.Format.SOURCE)
+ self.assertEqual(
+ anno,
+ {
+ "nested": "list[Annotated[set[int], 'set of ints', 4j]]",
+ "set": "{a + b}",
+ "dict": "{a + b: c + d, 'key': e + g}",
+ "list": "[a, b, c]",
+ "tuple": "(a, b, c)",
+ "slice": "(a[b:c], a[b:c:d], a[:c], a[b:], a[:], a[::d], a[b::d])",
+ "extended_slice": "a[:, :, c:d]",
+ "unpack1": "[*a]",
+ "unpack2": "[*a, b, c]",
+ },
+ )
+
+ def test_unsupported_operations(self):
+ format_msg = "Cannot stringify annotation containing string formatting"
+
+ def f(fstring: f"{a}"):
+ pass
+
+ with self.assertRaisesRegex(TypeError, format_msg):
+ annotationlib.get_annotations(f, format=annotationlib.Format.SOURCE)
+
+ def f(fstring_format: f"{a:02d}"):
+ pass
+
+ with self.assertRaisesRegex(TypeError, format_msg):
+ annotationlib.get_annotations(f, format=annotationlib.Format.SOURCE)
+
+
+class TestForwardRefClass(unittest.TestCase):
+ def test_special_attrs(self):
+ # Forward refs provide a different introspection API. __name__ and
+ # __qualname__ make little sense for forward refs as they can store
+ # complex typing expressions.
+ fr = annotationlib.ForwardRef("set[Any]")
+ self.assertFalse(hasattr(fr, "__name__"))
+ self.assertFalse(hasattr(fr, "__qualname__"))
+ self.assertEqual(fr.__module__, "annotationlib")
+ # Forward refs are currently unpicklable once they contain a code object.
+ fr.__forward_code__ # fill the cache
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ with self.assertRaises(TypeError):
+ pickle.dumps(fr, proto)
+
+
+class TestGetAnnotations(unittest.TestCase):
+ def test_builtin_type(self):
+ self.assertEqual(annotationlib.get_annotations(int), {})
+ self.assertEqual(annotationlib.get_annotations(object), {})
+
+ def test_custom_metaclass(self):
+ class Meta(type):
+ pass
+
+ class C(metaclass=Meta):
+ x: int
+
+ self.assertEqual(annotationlib.get_annotations(C), {"x": int})
+
+ def test_missing_dunder_dict(self):
+ class NoDict(type):
+ @property
+ def __dict__(cls):
+ raise AttributeError
+
+ b: str
+
+ class C1(metaclass=NoDict):
+ a: int
+
+ self.assertEqual(annotationlib.get_annotations(C1), {"a": int})
+ self.assertEqual(
+ annotationlib.get_annotations(C1, format=annotationlib.Format.FORWARDREF),
+ {"a": int},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(C1, format=annotationlib.Format.SOURCE),
+ {"a": "int"},
+ )
+ self.assertEqual(annotationlib.get_annotations(NoDict), {"b": str})
+ self.assertEqual(
+ annotationlib.get_annotations(NoDict, format=annotationlib.Format.FORWARDREF),
+ {"b": str},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(NoDict, format=annotationlib.Format.SOURCE),
+ {"b": "str"},
+ )
+
+ def test_format(self):
+ def f1(a: int):
+ pass
+
+ def f2(a: undefined):
+ pass
+
+ self.assertEqual(
+ annotationlib.get_annotations(f1, format=annotationlib.Format.VALUE),
+ {"a": int},
+ )
+ self.assertEqual(annotationlib.get_annotations(f1, format=1), {"a": int})
+
+ fwd = annotationlib.ForwardRef("undefined")
+ self.assertEqual(
+ annotationlib.get_annotations(f2, format=annotationlib.Format.FORWARDREF),
+ {"a": fwd},
+ )
+ self.assertEqual(annotationlib.get_annotations(f2, format=2), {"a": fwd})
+
+ self.assertEqual(
+ annotationlib.get_annotations(f1, format=annotationlib.Format.SOURCE),
+ {"a": "int"},
+ )
+ self.assertEqual(annotationlib.get_annotations(f1, format=3), {"a": "int"})
+
+ with self.assertRaises(ValueError):
+ annotationlib.get_annotations(f1, format=0)
+
+ with self.assertRaises(ValueError):
+ annotationlib.get_annotations(f1, format=4)
+
+ def test_custom_object_with_annotations(self):
+ class C:
+ def __init__(self):
+ self.__annotations__ = {"x": int, "y": str}
+
+ self.assertEqual(annotationlib.get_annotations(C()), {"x": int, "y": str})
+
+ def test_custom_format_eval_str(self):
+ def foo():
+ pass
+
+ with self.assertRaises(ValueError):
+ annotationlib.get_annotations(
+ foo, format=annotationlib.Format.FORWARDREF, eval_str=True
+ )
+ annotationlib.get_annotations(
+ foo, format=annotationlib.Format.SOURCE, eval_str=True
+ )
+
+ def test_stock_annotations(self):
+ def foo(a: int, b: str):
+ pass
+
+ for format in (annotationlib.Format.VALUE, annotationlib.Format.FORWARDREF):
+ with self.subTest(format=format):
+ self.assertEqual(
+ annotationlib.get_annotations(foo, format=format),
+ {"a": int, "b": str},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(foo, format=annotationlib.Format.SOURCE),
+ {"a": "int", "b": "str"},
+ )
+
+ foo.__annotations__ = {"a": "foo", "b": "str"}
+ for format in annotationlib.Format:
+ with self.subTest(format=format):
+ self.assertEqual(
+ annotationlib.get_annotations(foo, format=format),
+ {"a": "foo", "b": "str"},
+ )
+
+ self.assertEqual(
+ annotationlib.get_annotations(foo, eval_str=True, locals=locals()),
+ {"a": foo, "b": str},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(foo, eval_str=True, globals=locals()),
+ {"a": foo, "b": str},
+ )
+
+ def test_stock_annotations_in_module(self):
+ isa = inspect_stock_annotations
+
+ for kwargs in [
+ {},
+ {"eval_str": False},
+ {"format": annotationlib.Format.VALUE},
+ {"format": annotationlib.Format.FORWARDREF},
+ {"format": annotationlib.Format.VALUE, "eval_str": False},
+ {"format": annotationlib.Format.FORWARDREF, "eval_str": False},
+ ]:
+ with self.subTest(**kwargs):
+ self.assertEqual(
+ annotationlib.get_annotations(isa, **kwargs), {"a": int, "b": str}
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.MyClass, **kwargs),
+ {"a": int, "b": str},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.function, **kwargs),
+ {"a": int, "b": str, "return": isa.MyClass},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.function2, **kwargs),
+ {"a": int, "b": "str", "c": isa.MyClass, "return": isa.MyClass},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.function3, **kwargs),
+ {"a": "int", "b": "str", "c": "MyClass"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(annotationlib, **kwargs), {}
+ ) # annotations module has no annotations
+ self.assertEqual(
+ annotationlib.get_annotations(isa.UnannotatedClass, **kwargs), {}
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.unannotated_function, **kwargs),
+ {},
+ )
+
+ for kwargs in [
+ {"eval_str": True},
+ {"format": annotationlib.Format.VALUE, "eval_str": True},
+ ]:
+ with self.subTest(**kwargs):
+ self.assertEqual(
+ annotationlib.get_annotations(isa, **kwargs), {"a": int, "b": str}
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.MyClass, **kwargs),
+ {"a": int, "b": str},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.function, **kwargs),
+ {"a": int, "b": str, "return": isa.MyClass},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.function2, **kwargs),
+ {"a": int, "b": str, "c": isa.MyClass, "return": isa.MyClass},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.function3, **kwargs),
+ {"a": int, "b": str, "c": isa.MyClass},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(annotationlib, **kwargs), {}
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.UnannotatedClass, **kwargs), {}
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.unannotated_function, **kwargs),
+ {},
+ )
+
+ self.assertEqual(
+ annotationlib.get_annotations(isa, format=annotationlib.Format.SOURCE),
+ {"a": "int", "b": "str"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(
+ isa.MyClass, format=annotationlib.Format.SOURCE
+ ),
+ {"a": "int", "b": "str"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(
+ isa.function, format=annotationlib.Format.SOURCE
+ ),
+ {"a": "int", "b": "str", "return": "MyClass"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(
+ isa.function2, format=annotationlib.Format.SOURCE
+ ),
+ {"a": "int", "b": "str", "c": "MyClass", "return": "MyClass"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(
+ isa.function3, format=annotationlib.Format.SOURCE
+ ),
+ {"a": "int", "b": "str", "c": "MyClass"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(
+ annotationlib, format=annotationlib.Format.SOURCE
+ ),
+ {},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(
+ isa.UnannotatedClass, format=annotationlib.Format.SOURCE
+ ),
+ {},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(
+ isa.unannotated_function, format=annotationlib.Format.SOURCE
+ ),
+ {},
+ )
+
+ def test_stock_annotations_on_wrapper(self):
+ isa = inspect_stock_annotations
+
+ wrapped = times_three(isa.function)
+ self.assertEqual(wrapped(1, "x"), isa.MyClass(3, "xxx"))
+ self.assertIsNot(wrapped.__globals__, isa.function.__globals__)
+ self.assertEqual(
+ annotationlib.get_annotations(wrapped),
+ {"a": int, "b": str, "return": isa.MyClass},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(
+ wrapped, format=annotationlib.Format.FORWARDREF
+ ),
+ {"a": int, "b": str, "return": isa.MyClass},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(wrapped, format=annotationlib.Format.SOURCE),
+ {"a": "int", "b": "str", "return": "MyClass"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(wrapped, eval_str=True),
+ {"a": int, "b": str, "return": isa.MyClass},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(wrapped, eval_str=False),
+ {"a": int, "b": str, "return": isa.MyClass},
+ )
+
+ def test_stringized_annotations_in_module(self):
+ isa = inspect_stringized_annotations
+ for kwargs in [
+ {},
+ {"eval_str": False},
+ {"format": annotationlib.Format.VALUE},
+ {"format": annotationlib.Format.FORWARDREF},
+ {"format": annotationlib.Format.SOURCE},
+ {"format": annotationlib.Format.VALUE, "eval_str": False},
+ {"format": annotationlib.Format.FORWARDREF, "eval_str": False},
+ {"format": annotationlib.Format.SOURCE, "eval_str": False},
+ ]:
+ with self.subTest(**kwargs):
+ self.assertEqual(
+ annotationlib.get_annotations(isa, **kwargs),
+ {"a": "int", "b": "str"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.MyClass, **kwargs),
+ {"a": "int", "b": "str"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.function, **kwargs),
+ {"a": "int", "b": "str", "return": "MyClass"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.function2, **kwargs),
+ {"a": "int", "b": "'str'", "c": "MyClass", "return": "MyClass"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.function3, **kwargs),
+ {"a": "'int'", "b": "'str'", "c": "'MyClass'"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.UnannotatedClass, **kwargs), {}
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.unannotated_function, **kwargs),
+ {},
+ )
+
+ for kwargs in [
+ {"eval_str": True},
+ {"format": annotationlib.Format.VALUE, "eval_str": True},
+ ]:
+ with self.subTest(**kwargs):
+ self.assertEqual(
+ annotationlib.get_annotations(isa, **kwargs), {"a": int, "b": str}
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.MyClass, **kwargs),
+ {"a": int, "b": str},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.function, **kwargs),
+ {"a": int, "b": str, "return": isa.MyClass},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.function2, **kwargs),
+ {"a": int, "b": "str", "c": isa.MyClass, "return": isa.MyClass},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.function3, **kwargs),
+ {"a": "int", "b": "str", "c": "MyClass"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.UnannotatedClass, **kwargs), {}
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(isa.unannotated_function, **kwargs),
+ {},
+ )
+
+ def test_stringized_annotations_in_empty_module(self):
+ isa2 = inspect_stringized_annotations_2
+ self.assertEqual(annotationlib.get_annotations(isa2), {})
+ self.assertEqual(annotationlib.get_annotations(isa2, eval_str=True), {})
+ self.assertEqual(annotationlib.get_annotations(isa2, eval_str=False), {})
+
+ def test_stringized_annotations_on_wrapper(self):
+ isa = inspect_stringized_annotations
+ wrapped = times_three(isa.function)
+ self.assertEqual(wrapped(1, "x"), isa.MyClass(3, "xxx"))
+ self.assertIsNot(wrapped.__globals__, isa.function.__globals__)
+ self.assertEqual(
+ annotationlib.get_annotations(wrapped),
+ {"a": "int", "b": "str", "return": "MyClass"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(wrapped, eval_str=True),
+ {"a": int, "b": str, "return": isa.MyClass},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(wrapped, eval_str=False),
+ {"a": "int", "b": "str", "return": "MyClass"},
+ )
+
+ def test_stringized_annotations_on_class(self):
+ isa = inspect_stringized_annotations
+ # test that local namespace lookups work
+ self.assertEqual(
+ annotationlib.get_annotations(isa.MyClassWithLocalAnnotations),
+ {"x": "mytype"},
+ )
+ self.assertEqual(
+ annotationlib.get_annotations(
+ isa.MyClassWithLocalAnnotations, eval_str=True
+ ),
+ {"x": int},
+ )
+
+ def test_modify_annotations(self):
+ def f(x: int):
+ pass
+
+ self.assertEqual(annotationlib.get_annotations(f), {"x": int})
+ self.assertEqual(
+ annotationlib.get_annotations(f, format=annotationlib.Format.FORWARDREF),
+ {"x": int},
+ )
+
+ f.__annotations__["x"] = str
+ # The modification is reflected in VALUE (the default)
+ self.assertEqual(annotationlib.get_annotations(f), {"x": str})
+ # ... but not in FORWARDREF, which uses __annotate__
+ self.assertEqual(
+ annotationlib.get_annotations(f, format=annotationlib.Format.FORWARDREF),
+ {"x": int},
+ )
+
+ def test_pep695_generic_class_with_future_annotations(self):
+ ann_module695 = inspect_stringized_annotations_pep695
+ A_annotations = annotationlib.get_annotations(ann_module695.A, eval_str=True)
+ A_type_params = ann_module695.A.__type_params__
+ self.assertIs(A_annotations["x"], A_type_params[0])
+ self.assertEqual(A_annotations["y"].__args__[0], Unpack[A_type_params[1]])
+ self.assertIs(A_annotations["z"].__args__[0], A_type_params[2])
+
+ def test_pep695_generic_class_with_future_annotations_and_local_shadowing(self):
+ B_annotations = annotationlib.get_annotations(
+ inspect_stringized_annotations_pep695.B, eval_str=True
+ )
+ self.assertEqual(B_annotations, {"x": int, "y": str, "z": bytes})
+
+ def test_pep695_generic_class_with_future_annotations_name_clash_with_global_vars(self):
+ ann_module695 = inspect_stringized_annotations_pep695
+ C_annotations = annotationlib.get_annotations(ann_module695.C, eval_str=True)
+ self.assertEqual(
+ set(C_annotations.values()),
+ set(ann_module695.C.__type_params__)
+ )
+
+ def test_pep_695_generic_function_with_future_annotations(self):
+ ann_module695 = inspect_stringized_annotations_pep695
+ generic_func_annotations = annotationlib.get_annotations(
+ ann_module695.generic_function, eval_str=True
+ )
+ func_t_params = ann_module695.generic_function.__type_params__
+ self.assertEqual(
+ generic_func_annotations.keys(), {"x", "y", "z", "zz", "return"}
+ )
+ self.assertIs(generic_func_annotations["x"], func_t_params[0])
+ self.assertEqual(generic_func_annotations["y"], Unpack[func_t_params[1]])
+ self.assertIs(generic_func_annotations["z"].__origin__, func_t_params[2])
+ self.assertIs(generic_func_annotations["zz"].__origin__, func_t_params[2])
+
+ def test_pep_695_generic_function_with_future_annotations_name_clash_with_global_vars(self):
+ self.assertEqual(
+ set(
+ annotationlib.get_annotations(
+ inspect_stringized_annotations_pep695.generic_function_2,
+ eval_str=True
+ ).values()
+ ),
+ set(
+ inspect_stringized_annotations_pep695.generic_function_2.__type_params__
+ )
+ )
+
+ def test_pep_695_generic_method_with_future_annotations(self):
+ ann_module695 = inspect_stringized_annotations_pep695
+ generic_method_annotations = annotationlib.get_annotations(
+ ann_module695.D.generic_method, eval_str=True
+ )
+ params = {
+ param.__name__: param
+ for param in ann_module695.D.generic_method.__type_params__
+ }
+ self.assertEqual(
+ generic_method_annotations,
+ {"x": params["Foo"], "y": params["Bar"], "return": None}
+ )
+
+ def test_pep_695_generic_method_with_future_annotations_name_clash_with_global_vars(self):
+ self.assertEqual(
+ set(
+ annotationlib.get_annotations(
+ inspect_stringized_annotations_pep695.D.generic_method_2,
+ eval_str=True
+ ).values()
+ ),
+ set(
+ inspect_stringized_annotations_pep695.D.generic_method_2.__type_params__
+ )
+ )
+
+ def test_pep_695_generic_method_with_future_annotations_name_clash_with_global_and_local_vars(self):
+ self.assertEqual(
+ annotationlib.get_annotations(
+ inspect_stringized_annotations_pep695.E, eval_str=True
+ ),
+ {"x": str},
+ )
+
+ def test_pep_695_generics_with_future_annotations_nested_in_function(self):
+ results = inspect_stringized_annotations_pep695.nested()
+
+ self.assertEqual(
+ set(results.F_annotations.values()),
+ set(results.F.__type_params__)
+ )
+ self.assertEqual(
+ set(results.F_meth_annotations.values()),
+ set(results.F.generic_method.__type_params__)
+ )
+ self.assertNotEqual(
+ set(results.F_meth_annotations.values()),
+ set(results.F.__type_params__)
+ )
+ self.assertEqual(
+ set(results.F_meth_annotations.values()).intersection(results.F.__type_params__),
+ set()
+ )
+
+ self.assertEqual(results.G_annotations, {"x": str})
+
+ self.assertEqual(
+ set(results.generic_func_annotations.values()),
+ set(results.generic_func.__type_params__)
+ )