summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/test_dis.py29
-rw-r--r--Lib/test/test_grammar.py69
-rw-r--r--Lib/test/test_module/__init__.py2
-rw-r--r--Lib/test/test_opcodes.py13
-rw-r--r--Lib/test/test_positional_only_arg.py5
-rw-r--r--Lib/test/test_pyclbr.py2
-rw-r--r--Lib/test/test_pydoc/test_pydoc.py11
-rw-r--r--Lib/test/test_pyrepl/test_interact.py2
-rw-r--r--Lib/test/test_symtable.py6
-rw-r--r--Lib/test/test_traceback.py5
-rw-r--r--Lib/test/test_type_annotations.py163
-rw-r--r--Lib/test/test_typing.py4
-rw-r--r--Lib/test/typinganndata/ann_module.py4
13 files changed, 208 insertions, 107 deletions
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index b1a1b77..b0ae128 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -352,32 +352,21 @@ lst[fun(0)]: int = 1
dis_annot_stmt_str = """\
0 RESUME 0
- 2 SETUP_ANNOTATIONS
- LOAD_CONST 0 (1)
+ 2 LOAD_CONST 0 (1)
STORE_NAME 0 (x)
- LOAD_NAME 1 (int)
- LOAD_NAME 2 (__annotations__)
- LOAD_CONST 1 ('x')
- STORE_SUBSCR
-
- 3 LOAD_NAME 3 (fun)
- PUSH_NULL
- LOAD_CONST 0 (1)
- CALL 1
- LOAD_NAME 2 (__annotations__)
- LOAD_CONST 2 ('y')
- STORE_SUBSCR
4 LOAD_CONST 0 (1)
- LOAD_NAME 4 (lst)
- LOAD_NAME 3 (fun)
+ LOAD_NAME 1 (lst)
+ LOAD_NAME 2 (fun)
PUSH_NULL
- LOAD_CONST 3 (0)
+ LOAD_CONST 1 (0)
CALL 1
STORE_SUBSCR
- LOAD_NAME 1 (int)
- POP_TOP
- RETURN_CONST 4 (None)
+
+ 2 LOAD_CONST 2 (<code object __annotate__ at 0x..., file "<dis>", line 2>)
+ MAKE_FUNCTION
+ STORE_NAME 3 (__annotate__)
+ RETURN_CONST 3 (None)
"""
compound_stmt_str = """\
diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py
index c72f438..5b7a639 100644
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -306,16 +306,6 @@ the \'lazy\' dog.\n\
var_annot_global: int # a global annotated is necessary for test_var_annot
-# custom namespace for testing __annotations__
-
-class CNS:
- def __init__(self):
- self._dct = {}
- def __setitem__(self, item, value):
- self._dct[item.lower()] = value
- def __getitem__(self, item):
- return self._dct[item]
-
class GrammarTests(unittest.TestCase):
@@ -446,22 +436,12 @@ class GrammarTests(unittest.TestCase):
self.assertEqual(E.__annotations__, {})
self.assertEqual(F.__annotations__, {})
-
- def test_var_annot_metaclass_semantics(self):
- class CMeta(type):
- @classmethod
- def __prepare__(metacls, name, bases, **kwds):
- return {'__annotations__': CNS()}
- class CC(metaclass=CMeta):
- XX: 'ANNOT'
- self.assertEqual(CC.__annotations__['xx'], 'ANNOT')
-
def test_var_annot_module_semantics(self):
self.assertEqual(test.__annotations__, {})
self.assertEqual(ann_module.__annotations__,
- {1: 2, 'x': int, 'y': str, 'f': typing.Tuple[int, int], 'u': int | float})
+ {'x': int, 'y': str, 'f': typing.Tuple[int, int], 'u': int | float})
self.assertEqual(ann_module.M.__annotations__,
- {'123': 123, 'o': type})
+ {'o': type})
self.assertEqual(ann_module2.__annotations__, {})
def test_var_annot_in_module(self):
@@ -476,51 +456,12 @@ class GrammarTests(unittest.TestCase):
ann_module3.D_bad_ann(5)
def test_var_annot_simple_exec(self):
- gns = {}; lns= {}
+ gns = {}; lns = {}
exec("'docstring'\n"
- "__annotations__[1] = 2\n"
"x: int = 5\n", gns, lns)
- self.assertEqual(lns["__annotations__"], {1: 2, 'x': int})
- with self.assertRaises(KeyError):
- gns['__annotations__']
-
- def test_var_annot_custom_maps(self):
- # tests with custom locals() and __annotations__
- ns = {'__annotations__': CNS()}
- exec('X: int; Z: str = "Z"; (w): complex = 1j', ns)
- self.assertEqual(ns['__annotations__']['x'], int)
- self.assertEqual(ns['__annotations__']['z'], str)
+ self.assertEqual(lns["__annotate__"](1), {'x': int})
with self.assertRaises(KeyError):
- ns['__annotations__']['w']
- nonloc_ns = {}
- class CNS2:
- def __init__(self):
- self._dct = {}
- def __setitem__(self, item, value):
- nonlocal nonloc_ns
- self._dct[item] = value
- nonloc_ns[item] = value
- def __getitem__(self, item):
- return self._dct[item]
- exec('x: int = 1', {}, CNS2())
- self.assertEqual(nonloc_ns['__annotations__']['x'], int)
-
- def test_var_annot_refleak(self):
- # complex case: custom locals plus custom __annotations__
- # this was causing refleak
- cns = CNS()
- nonloc_ns = {'__annotations__': cns}
- class CNS2:
- def __init__(self):
- self._dct = {'__annotations__': cns}
- def __setitem__(self, item, value):
- nonlocal nonloc_ns
- self._dct[item] = value
- nonloc_ns[item] = value
- def __getitem__(self, item):
- return self._dct[item]
- exec('X: str', {}, CNS2())
- self.assertEqual(nonloc_ns['__annotations__']['x'], str)
+ gns['__annotate__']
def test_var_annot_rhs(self):
ns = {}
diff --git a/Lib/test/test_module/__init__.py b/Lib/test/test_module/__init__.py
index 952ba43..56edd0c 100644
--- a/Lib/test/test_module/__init__.py
+++ b/Lib/test/test_module/__init__.py
@@ -360,6 +360,8 @@ a = A(destroyed)"""
ann_module4 = import_helper.import_fresh_module(
'test.typinganndata.ann_module4',
)
+ self.assertFalse("__annotations__" in ann_module4.__dict__)
+ self.assertEqual(ann_module4.__annotations__, {"a": int, "b": str})
self.assertTrue("__annotations__" in ann_module4.__dict__)
del ann_module4.__annotations__
self.assertFalse("__annotations__" in ann_module4.__dict__)
diff --git a/Lib/test/test_opcodes.py b/Lib/test/test_opcodes.py
index 72488b2..f7cc833 100644
--- a/Lib/test/test_opcodes.py
+++ b/Lib/test/test_opcodes.py
@@ -39,16 +39,19 @@ class OpcodeTest(unittest.TestCase):
def test_use_existing_annotations(self):
ns = {'__annotations__': {1: 2}}
exec('x: int', ns)
- self.assertEqual(ns['__annotations__'], {'x': int, 1: 2})
+ self.assertEqual(ns['__annotations__'], {1: 2})
def test_do_not_recreate_annotations(self):
# Don't rely on the existence of the '__annotations__' global.
with support.swap_item(globals(), '__annotations__', {}):
- del globals()['__annotations__']
+ globals().pop('__annotations__', None)
class C:
- del __annotations__
- with self.assertRaises(NameError):
- x: int
+ try:
+ del __annotations__
+ except NameError:
+ pass
+ x: int
+ self.assertEqual(C.__annotations__, {"x": int})
def test_raise_class_exceptions(self):
diff --git a/Lib/test/test_positional_only_arg.py b/Lib/test/test_positional_only_arg.py
index 1a19381..eea0625 100644
--- a/Lib/test/test_positional_only_arg.py
+++ b/Lib/test/test_positional_only_arg.py
@@ -2,6 +2,7 @@
import dis
import pickle
+import types
import unittest
from test.support import check_syntax_error
@@ -440,7 +441,9 @@ class PositionalOnlyTestCase(unittest.TestCase):
# without constant folding we end up with
# COMPARE_OP(is), IS_OP (0)
# with constant folding we should expect a IS_OP (1)
- codes = [(i.opname, i.argval) for i in dis.get_instructions(g)]
+ code_obj = next(const for const in g.__code__.co_consts
+ if isinstance(const, types.CodeType) and const.co_name == "__annotate__")
+ codes = [(i.opname, i.argval) for i in dis.get_instructions(code_obj)]
self.assertNotIn(('UNARY_NOT', None), codes)
self.assertIn(('IS_OP', 1), codes)
diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py
index 46206ac..0c12a30 100644
--- a/Lib/test/test_pyclbr.py
+++ b/Lib/test/test_pyclbr.py
@@ -109,6 +109,8 @@ class PyclbrTest(TestCase):
actualMethods = []
for m in py_item.__dict__.keys():
+ if m == "__annotate__":
+ continue
if ismethod(py_item, getattr(py_item, m), m):
actualMethods.append(m)
foundMethods = []
diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py
index 57e5b8e..a17c16c 100644
--- a/Lib/test/test_pydoc/test_pydoc.py
+++ b/Lib/test/test_pydoc/test_pydoc.py
@@ -77,6 +77,11 @@ CLASSES
| __weakref__%s
class B(builtins.object)
+ | Methods defined here:
+ |
+ | __annotate__(...)
+ |
+ | ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__%s
@@ -87,8 +92,6 @@ CLASSES
| Data and other attributes defined here:
|
| NO_MEANING = 'eggs'
- |
- | __annotations__ = {'NO_MEANING': <class 'str'>}
class C(builtins.object)
| Methods defined here:
@@ -176,6 +179,9 @@ class A(builtins.object)
list of weak references to the object
class B(builtins.object)
+ Methods defined here:
+ __annotate__(...)
+ ----------------------------------------------------------------------
Data descriptors defined here:
__dict__
dictionary for instance variables
@@ -184,7 +190,6 @@ class B(builtins.object)
----------------------------------------------------------------------
Data and other attributes defined here:
NO_MEANING = 'eggs'
- __annotations__ = {'NO_MEANING': <class 'str'>}
class C(builtins.object)
diff --git a/Lib/test/test_pyrepl/test_interact.py b/Lib/test/test_pyrepl/test_interact.py
index df97b13..31f08cd 100644
--- a/Lib/test/test_pyrepl/test_interact.py
+++ b/Lib/test/test_pyrepl/test_interact.py
@@ -105,7 +105,7 @@ class TestSimpleInteract(unittest.TestCase):
def test_no_active_future(self):
console = InteractiveColoredConsole()
- source = "x: int = 1; print(__annotations__)"
+ source = "x: int = 1; print(__annotate__(1))"
f = io.StringIO()
with contextlib.redirect_stdout(f):
result = console.runsource(source)
diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py
index ef2a228..a4b111e 100644
--- a/Lib/test/test_symtable.py
+++ b/Lib/test/test_symtable.py
@@ -205,12 +205,14 @@ class SymtableTest(unittest.TestCase):
def test_annotated(self):
st1 = symtable.symtable('def f():\n x: int\n', 'test', 'exec')
- st2 = st1.get_children()[0]
+ st2 = st1.get_children()[1]
+ self.assertEqual(st2.get_type(), "function")
self.assertTrue(st2.lookup('x').is_local())
self.assertTrue(st2.lookup('x').is_annotated())
self.assertFalse(st2.lookup('x').is_global())
st3 = symtable.symtable('def f():\n x = 1\n', 'test', 'exec')
- st4 = st3.get_children()[0]
+ st4 = st3.get_children()[1]
+ self.assertEqual(st4.get_type(), "function")
self.assertTrue(st4.lookup('x').is_local())
self.assertFalse(st4.lookup('x').is_annotated())
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 5035de1..1895c88 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -622,6 +622,7 @@ class TracebackErrorLocationCaretTestBase:
def f_with_type():
def foo(a: THIS_DOES_NOT_EXIST ) -> int:
return 0
+ foo.__annotations__
lineno_f = f_with_type.__code__.co_firstlineno
expected_f = (
@@ -629,7 +630,9 @@ class TracebackErrorLocationCaretTestBase:
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
' callable()\n'
' ~~~~~~~~^^\n'
- f' File "{__file__}", line {lineno_f+1}, in f_with_type\n'
+ f' File "{__file__}", line {lineno_f+3}, in f_with_type\n'
+ ' foo.__annotations__\n'
+ f' File "{__file__}", line {lineno_f+1}, in __annotate__\n'
' def foo(a: THIS_DOES_NOT_EXIST ) -> int:\n'
' ^^^^^^^^^^^^^^^^^^^\n'
)
diff --git a/Lib/test/test_type_annotations.py b/Lib/test/test_type_annotations.py
index 5e3c334..a9be1f5 100644
--- a/Lib/test/test_type_annotations.py
+++ b/Lib/test/test_type_annotations.py
@@ -1,7 +1,12 @@
import textwrap
import types
import unittest
-from test.support import run_code
+from test.support import run_code, check_syntax_error
+
+VALUE = 1
+FORWARDREF = 2
+SOURCE = 3
+
class TypeAnnotationTests(unittest.TestCase):
@@ -49,6 +54,7 @@ class TypeAnnotationTests(unittest.TestCase):
class C:
a:int=3
b:str=4
+ self.assertEqual(C.__annotations__, {"a": int, "b": str})
self.assertTrue("__annotations__" in C.__dict__)
del C.__annotations__
self.assertFalse("__annotations__" in C.__dict__)
@@ -106,6 +112,13 @@ class TypeAnnotationTests(unittest.TestCase):
self.assertEqual(D.__annotations__, {})
+def build_module(code: str, name: str = "top") -> types.ModuleType:
+ ns = run_code(code)
+ mod = types.ModuleType(name)
+ mod.__dict__.update(ns)
+ return mod
+
+
class TestSetupAnnotations(unittest.TestCase):
def check(self, code: str):
code = textwrap.dedent(code)
@@ -113,11 +126,10 @@ class TestSetupAnnotations(unittest.TestCase):
with self.subTest(scope=scope):
if scope == "class":
code = f"class C:\n{textwrap.indent(code, ' ')}"
- ns = run_code(code)
- if scope == "class":
+ ns = run_code(code)
annotations = ns["C"].__annotations__
else:
- annotations = ns["__annotations__"]
+ annotations = build_module(code).__annotations__
self.assertEqual(annotations, {"x": int})
def test_top_level(self):
@@ -256,3 +268,146 @@ class AnnotateTests(unittest.TestCase):
# Setting f.__annotations__ also clears __annotate__
f.__annotations__ = {"z": 43}
self.assertIs(f.__annotate__, None)
+
+
+class DeferredEvaluationTests(unittest.TestCase):
+ def test_function(self):
+ def func(x: undefined, /, y: undefined, *args: undefined, z: undefined, **kwargs: undefined) -> undefined:
+ pass
+
+ with self.assertRaises(NameError):
+ func.__annotations__
+
+ undefined = 1
+ self.assertEqual(func.__annotations__, {
+ "x": 1,
+ "y": 1,
+ "args": 1,
+ "z": 1,
+ "kwargs": 1,
+ "return": 1,
+ })
+
+ def test_async_function(self):
+ async def func(x: undefined, /, y: undefined, *args: undefined, z: undefined, **kwargs: undefined) -> undefined:
+ pass
+
+ with self.assertRaises(NameError):
+ func.__annotations__
+
+ undefined = 1
+ self.assertEqual(func.__annotations__, {
+ "x": 1,
+ "y": 1,
+ "args": 1,
+ "z": 1,
+ "kwargs": 1,
+ "return": 1,
+ })
+
+ def test_class(self):
+ class X:
+ a: undefined
+
+ with self.assertRaises(NameError):
+ X.__annotations__
+
+ undefined = 1
+ self.assertEqual(X.__annotations__, {"a": 1})
+
+ def test_module(self):
+ ns = run_code("x: undefined = 1")
+ anno = ns["__annotate__"]
+ with self.assertRaises(NotImplementedError):
+ anno(2)
+
+ with self.assertRaises(NameError):
+ anno(1)
+
+ ns["undefined"] = 1
+ self.assertEqual(anno(1), {"x": 1})
+
+ def test_class_scoping(self):
+ class Outer:
+ def meth(self, x: Nested): ...
+ x: Nested
+ class Nested: ...
+
+ self.assertEqual(Outer.meth.__annotations__, {"x": Outer.Nested})
+ self.assertEqual(Outer.__annotations__, {"x": Outer.Nested})
+
+ def test_no_exotic_expressions(self):
+ check_syntax_error(self, "def func(x: (yield)): ...", "yield expression cannot be used within an annotation")
+ check_syntax_error(self, "def func(x: (yield from x)): ...", "yield expression cannot be used within an annotation")
+ check_syntax_error(self, "def func(x: (y := 3)): ...", "named expression cannot be used within an annotation")
+ check_syntax_error(self, "def func(x: (await 42)): ...", "await expression cannot be used within an annotation")
+
+ def test_no_exotic_expressions_in_unevaluated_annotations(self):
+ preludes = [
+ "",
+ "class X: ",
+ "def f(): ",
+ "async def f(): ",
+ ]
+ for prelude in preludes:
+ with self.subTest(prelude=prelude):
+ check_syntax_error(self, prelude + "(x): (yield)", "yield expression cannot be used within an annotation")
+ check_syntax_error(self, prelude + "(x): (yield from x)", "yield expression cannot be used within an annotation")
+ check_syntax_error(self, prelude + "(x): (y := 3)", "named expression cannot be used within an annotation")
+ check_syntax_error(self, prelude + "(x): (await 42)", "await expression cannot be used within an annotation")
+
+ def test_ignore_non_simple_annotations(self):
+ ns = run_code("class X: (y): int")
+ self.assertEqual(ns["X"].__annotations__, {})
+ ns = run_code("class X: int.b: int")
+ self.assertEqual(ns["X"].__annotations__, {})
+ ns = run_code("class X: int[str]: int")
+ self.assertEqual(ns["X"].__annotations__, {})
+
+ def test_generated_annotate(self):
+ def func(x: int):
+ pass
+ class X:
+ x: int
+ mod = build_module("x: int")
+ for obj in (func, X, mod):
+ with self.subTest(obj=obj):
+ annotate = obj.__annotate__
+ self.assertIsInstance(annotate, types.FunctionType)
+ self.assertEqual(annotate.__name__, "__annotate__")
+ with self.assertRaises(NotImplementedError):
+ annotate(FORWARDREF)
+ with self.assertRaises(NotImplementedError):
+ annotate(SOURCE)
+ with self.assertRaises(NotImplementedError):
+ annotate(None)
+ self.assertEqual(annotate(VALUE), {"x": int})
+
+ def test_comprehension_in_annotation(self):
+ # This crashed in an earlier version of the code
+ ns = run_code("x: [y for y in range(10)]")
+ self.assertEqual(ns["__annotate__"](1), {"x": list(range(10))})
+
+ def test_future_annotations(self):
+ code = """
+ from __future__ import annotations
+
+ def f(x: int) -> int: pass
+ """
+ ns = run_code(code)
+ f = ns["f"]
+ self.assertIsInstance(f.__annotate__, types.FunctionType)
+ annos = {"x": "int", "return": "int"}
+ self.assertEqual(f.__annotate__(VALUE), annos)
+ self.assertEqual(f.__annotations__, annos)
+
+ def test_name_clash_with_format(self):
+ # this test would fail if __annotate__'s parameter was called "format"
+ code = """
+ class format: pass
+
+ def f(x: format): pass
+ """
+ ns = run_code(code)
+ f = ns["f"]
+ self.assertEqual(f.__annotations__, {"x": ns["format"]})
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index dac55ce..9800b3b 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -6634,7 +6634,7 @@ class GetTypeHintTests(BaseTestCase):
gth(None)
def test_get_type_hints_modules(self):
- ann_module_type_hints = {1: 2, 'f': Tuple[int, int], 'x': int, 'y': str, 'u': int | float}
+ ann_module_type_hints = {'f': Tuple[int, int], 'x': int, 'y': str, 'u': int | float}
self.assertEqual(gth(ann_module), ann_module_type_hints)
self.assertEqual(gth(ann_module2), {})
self.assertEqual(gth(ann_module3), {})
@@ -6652,7 +6652,7 @@ class GetTypeHintTests(BaseTestCase):
self.assertEqual(gth(ann_module.C), # gth will find the right globalns
{'y': Optional[ann_module.C]})
self.assertIsInstance(gth(ann_module.j_class), dict)
- self.assertEqual(gth(ann_module.M), {'123': 123, 'o': type})
+ self.assertEqual(gth(ann_module.M), {'o': type})
self.assertEqual(gth(ann_module.D),
{'j': str, 'k': str, 'y': Optional[ann_module.C]})
self.assertEqual(gth(ann_module.Y), {'z': int})
diff --git a/Lib/test/typinganndata/ann_module.py b/Lib/test/typinganndata/ann_module.py
index 5081e6b..e1a1792 100644
--- a/Lib/test/typinganndata/ann_module.py
+++ b/Lib/test/typinganndata/ann_module.py
@@ -8,8 +8,6 @@ Empty lines above are for good reason (testing for correct line numbers)
from typing import Optional
from functools import wraps
-__annotations__[1] = 2
-
class C:
x = 5; y: Optional['C'] = None
@@ -18,8 +16,6 @@ from typing import Tuple
x: int = 5; y: str = x; f: Tuple[int, int]
class M(type):
-
- __annotations__['123'] = 123
o: type = object
(pars): bool = True