diff options
Diffstat (limited to 'Lib/test')
-rw-r--r-- | Lib/test/dataclass_module_1.py | 6 | ||||
-rw-r--r-- | Lib/test/dataclass_module_1_str.py | 32 | ||||
-rw-r--r-- | Lib/test/dataclass_module_2.py | 6 | ||||
-rw-r--r-- | Lib/test/dataclass_module_2_str.py | 32 | ||||
-rw-r--r-- | Lib/test/dataclass_textanno.py | 2 | ||||
-rw-r--r-- | Lib/test/test_annotations.py | 228 | ||||
-rw-r--r-- | Lib/test/test_coroutines.py | 8 | ||||
-rw-r--r-- | Lib/test/test_dataclasses.py | 77 | ||||
-rw-r--r-- | Lib/test/test_dis.py | 38 | ||||
-rw-r--r-- | Lib/test/test_functools.py | 4 | ||||
-rw-r--r-- | Lib/test/test_grammar.py | 56 | ||||
-rw-r--r-- | Lib/test/test_inspect.py | 52 | ||||
-rw-r--r-- | Lib/test/test_opcodes.py | 2 | ||||
-rw-r--r-- | Lib/test/test_positional_only_arg.py | 17 | ||||
-rw-r--r-- | Lib/test/test_pydoc.py | 4 | ||||
-rw-r--r-- | Lib/test/test_syntax.py | 8 | ||||
-rw-r--r-- | Lib/test/test_types.py | 4 | ||||
-rw-r--r-- | Lib/test/test_typing.py | 22 |
18 files changed, 253 insertions, 345 deletions
diff --git a/Lib/test/dataclass_module_1.py b/Lib/test/dataclass_module_1.py index 9f0aeda67f9..87a33f8191d 100644 --- a/Lib/test/dataclass_module_1.py +++ b/Lib/test/dataclass_module_1.py @@ -1,3 +1,9 @@ +#from __future__ import annotations +USING_STRINGS = False + +# dataclass_module_1.py and dataclass_module_1_str.py are identical +# except only the latter uses string annotations. + import dataclasses import typing diff --git a/Lib/test/dataclass_module_1_str.py b/Lib/test/dataclass_module_1_str.py new file mode 100644 index 00000000000..6de490b7ad7 --- /dev/null +++ b/Lib/test/dataclass_module_1_str.py @@ -0,0 +1,32 @@ +from __future__ import annotations +USING_STRINGS = True + +# dataclass_module_1.py and dataclass_module_1_str.py are identical +# except only the latter uses string annotations. + +import dataclasses +import typing + +T_CV2 = typing.ClassVar[int] +T_CV3 = typing.ClassVar + +T_IV2 = dataclasses.InitVar[int] +T_IV3 = dataclasses.InitVar + +@dataclasses.dataclass +class CV: + T_CV4 = typing.ClassVar + cv0: typing.ClassVar[int] = 20 + cv1: typing.ClassVar = 30 + cv2: T_CV2 + cv3: T_CV3 + not_cv4: T_CV4 # When using string annotations, this field is not recognized as a ClassVar. + +@dataclasses.dataclass +class IV: + T_IV4 = dataclasses.InitVar + iv0: dataclasses.InitVar[int] + iv1: dataclasses.InitVar + iv2: T_IV2 + iv3: T_IV3 + not_iv4: T_IV4 # When using string annotations, this field is not recognized as an InitVar. diff --git a/Lib/test/dataclass_module_2.py b/Lib/test/dataclass_module_2.py index 8d120d181bd..68fb733e299 100644 --- a/Lib/test/dataclass_module_2.py +++ b/Lib/test/dataclass_module_2.py @@ -1,3 +1,9 @@ +#from __future__ import annotations +USING_STRINGS = False + +# dataclass_module_2.py and dataclass_module_2_str.py are identical +# except only the latter uses string annotations. + from dataclasses import dataclass, InitVar from typing import ClassVar diff --git a/Lib/test/dataclass_module_2_str.py b/Lib/test/dataclass_module_2_str.py new file mode 100644 index 00000000000..b363d17c176 --- /dev/null +++ b/Lib/test/dataclass_module_2_str.py @@ -0,0 +1,32 @@ +from __future__ import annotations +USING_STRINGS = True + +# dataclass_module_2.py and dataclass_module_2_str.py are identical +# except only the latter uses string annotations. + +from dataclasses import dataclass, InitVar +from typing import ClassVar + +T_CV2 = ClassVar[int] +T_CV3 = ClassVar + +T_IV2 = InitVar[int] +T_IV3 = InitVar + +@dataclass +class CV: + T_CV4 = ClassVar + cv0: ClassVar[int] = 20 + cv1: ClassVar = 30 + cv2: T_CV2 + cv3: T_CV3 + not_cv4: T_CV4 # When using string annotations, this field is not recognized as a ClassVar. + +@dataclass +class IV: + T_IV4 = InitVar + iv0: InitVar[int] + iv1: InitVar + iv2: T_IV2 + iv3: T_IV3 + not_iv4: T_IV4 # When using string annotations, this field is not recognized as an InitVar. diff --git a/Lib/test/dataclass_textanno.py b/Lib/test/dataclass_textanno.py index 589b60f0cd6..3eb6c943d4c 100644 --- a/Lib/test/dataclass_textanno.py +++ b/Lib/test/dataclass_textanno.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import dataclasses diff --git a/Lib/test/test_annotations.py b/Lib/test/test_annotations.py deleted file mode 100644 index 3e6b709fb4f..00000000000 --- a/Lib/test/test_annotations.py +++ /dev/null @@ -1,228 +0,0 @@ -import unittest -import sys -from textwrap import dedent - -class PostponedAnnotationsTestCase(unittest.TestCase): - template = dedent( - """ - def f() -> {ann}: - ... - def g(arg: {ann}) -> None: - ... - async def f2() -> {ann}: - ... - async def g2(arg: {ann}) -> None: - ... - var: {ann} - var2: {ann} = None - """ - ) - - def getActual(self, annotation): - scope = {} - exec(self.template.format(ann=annotation), {}, scope) - func_ret_ann = scope['f'].__annotations__['return'] - func_arg_ann = scope['g'].__annotations__['arg'] - async_func_ret_ann = scope['f2'].__annotations__['return'] - async_func_arg_ann = scope['g2'].__annotations__['arg'] - var_ann1 = scope['__annotations__']['var'] - var_ann2 = scope['__annotations__']['var2'] - self.assertEqual(func_ret_ann, func_arg_ann) - self.assertEqual(func_ret_ann, async_func_ret_ann) - self.assertEqual(func_ret_ann, async_func_arg_ann) - self.assertEqual(func_ret_ann, var_ann1) - self.assertEqual(func_ret_ann, var_ann2) - return func_ret_ann - - def assertAnnotationEqual( - self, annotation, expected=None, drop_parens=False, is_tuple=False, - ): - actual = self.getActual(annotation) - if expected is None: - expected = annotation if not is_tuple else annotation[1:-1] - if drop_parens: - self.assertNotEqual(actual, expected) - actual = actual.replace("(", "").replace(")", "") - - self.assertEqual(actual, expected) - - def test_annotations(self): - eq = self.assertAnnotationEqual - eq('...') - eq("'some_string'") - eq("u'some_string'") - eq("b'\\xa3'") - eq('Name') - eq('None') - eq('True') - eq('False') - eq('1') - eq('1.0') - eq('1j') - eq('True or False') - eq('True or False or None') - eq('True and False') - eq('True and False and None') - eq('Name1 and Name2 or Name3') - eq('Name1 and (Name2 or Name3)') - eq('Name1 or Name2 and Name3') - eq('(Name1 or Name2) and Name3') - eq('Name1 and Name2 or Name3 and Name4') - eq('Name1 or Name2 and Name3 or Name4') - eq('a + b + (c + d)') - eq('a * b * (c * d)') - eq('(a ** b) ** c ** d') - eq('v1 << 2') - eq('1 >> v2') - eq('1 % finished') - eq('1 + v2 - v3 * 4 ^ 5 ** v6 / 7 // 8') - eq('not great') - eq('not not great') - eq('~great') - eq('+value') - eq('++value') - eq('-1') - eq('~int and not v1 ^ 123 + v2 | True') - eq('a + (not b)') - eq('lambda: None') - eq('lambda arg: None') - eq('lambda a=True: a') - eq('lambda a, b, c=True: a') - eq("lambda a, b, c=True, *, d=1 << v2, e='str': a") - eq("lambda a, b, c=True, *vararg, d, e='str', **kwargs: a + b") - eq("lambda a, /, b, c=True, *vararg, d, e='str', **kwargs: a + b") - eq('lambda x, /: x') - eq('lambda x=1, /: x') - eq('lambda x, /, y: x + y') - eq('lambda x=1, /, y=2: x + y') - eq('lambda x, /, y=1: x + y') - eq('lambda x, /, y=1, *, z=3: x + y + z') - eq('lambda x=1, /, y=2, *, z=3: x + y + z') - eq('lambda x=1, /, y=2, *, z: x + y + z') - eq('lambda x=1, y=2, z=3, /, w=4, *, l, l2: x + y + z + w + l + l2') - eq('lambda x=1, y=2, z=3, /, w=4, *, l, l2, **kwargs: x + y + z + w + l + l2') - eq('lambda x, /, y=1, *, z: x + y + z') - eq('lambda x: lambda y: x + y') - eq('1 if True else 2') - eq('str or None if int or True else str or bytes or None') - eq('str or None if (1 if True else 2) else str or bytes or None') - eq("0 if not x else 1 if x > 0 else -1") - eq("(1 if x > 0 else -1) if x else 0") - eq("{'2.7': dead, '3.7': long_live or die_hard}") - eq("{'2.7': dead, '3.7': long_live or die_hard, **{'3.6': verygood}}") - eq("{**a, **b, **c}") - eq("{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}") - eq("{*a, *b, *c}") - eq("({'a': 'b'}, True or False, +value, 'string', b'bytes') or None") - eq("()") - eq("(a,)") - eq("(a, b)") - eq("(a, b, c)") - eq("(*a, *b, *c)") - eq("[]") - eq("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]") - eq("[*a, *b, *c]") - eq("{i for i in (1, 2, 3)}") - eq("{i ** 2 for i in (1, 2, 3)}") - eq("{i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))}") - eq("{i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3)}") - eq("[i for i in (1, 2, 3)]") - eq("[i ** 2 for i in (1, 2, 3)]") - eq("[i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))]") - eq("[i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3)]") - eq("(i for i in (1, 2, 3))") - eq("(i ** 2 for i in (1, 2, 3))") - eq("(i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c')))") - eq("(i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3))") - eq("{i: 0 for i in (1, 2, 3)}") - eq("{i: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))}") - eq("[(x, y) for x, y in (a, b)]") - eq("[(x,) for x, in (a,)]") - eq("Python3 > Python2 > COBOL") - eq("Life is Life") - eq("call()") - eq("call(arg)") - eq("call(kwarg='hey')") - eq("call(arg, kwarg='hey')") - eq("call(arg, *args, another, kwarg='hey')") - eq("call(arg, another, kwarg='hey', **kwargs, kwarg2='ho')") - eq("lukasz.langa.pl") - eq("call.me(maybe)") - eq("1 .real") - eq("1.0.real") - eq("....__class__") - eq("list[str]") - eq("dict[str, int]") - eq("set[str,]") - eq("tuple[str, ...]") - eq("tuple[(str, *types)]") - eq("tuple[str, int, (str, int)]") - eq("tuple[(*int, str, str, (str, int))]") - eq("tuple[str, int, float, dict[str, int]]") - eq("slice[0]") - eq("slice[0:1]") - eq("slice[0:1:2]") - eq("slice[:]") - eq("slice[:-1]") - eq("slice[1:]") - eq("slice[::-1]") - eq("slice[:,]") - eq("slice[1:2,]") - eq("slice[1:2:3,]") - eq("slice[1:2, 1]") - eq("slice[1:2, 2, 3]") - eq("slice[()]") - eq("slice[a, b:c, d:e:f]") - eq("slice[(x for x in a)]") - eq('str or None if sys.version_info[0] > (3,) else str or bytes or None') - eq("f'f-string without formatted values is just a string'") - eq("f'{{NOT a formatted value}}'") - eq("f'some f-string with {a} {few():.2f} {formatted.values!r}'") - eq('''f"{f'{nested} inner'} outer"''') - eq("f'space between opening braces: { {a for a in (1, 2, 3)}}'") - eq("f'{(lambda x: x)}'") - eq("f'{(None if a else lambda x: x)}'") - eq("f'{x}'") - eq("f'{x!r}'") - eq("f'{x!a}'") - eq('(yield from outside_of_generator)') - eq('(yield)') - eq('(yield a + b)') - eq('await some.complicated[0].call(with_args=True or 1 is not 1)') - eq('[x for x in (a if b else c)]') - eq('[x for x in a if (b if c else d)]') - eq('f(x for x in a)') - eq('f(1, (x for x in a))') - eq('f((x for x in a), 2)') - eq('(((a)))', 'a') - eq('(((a, b)))', '(a, b)') - eq("(x := 10)") - eq("f'{(x := 10):=10}'") - eq("1 + 2") - eq("1 + 2 + 3") - - def test_fstring_debug_annotations(self): - # f-strings with '=' don't round trip very well, so set the expected - # result explicitely. - self.assertAnnotationEqual("f'{x=!r}'", expected="f'x={x!r}'") - self.assertAnnotationEqual("f'{x=:}'", expected="f'x={x:}'") - self.assertAnnotationEqual("f'{x=:.2f}'", expected="f'x={x:.2f}'") - self.assertAnnotationEqual("f'{x=!r}'", expected="f'x={x!r}'") - self.assertAnnotationEqual("f'{x=!a}'", expected="f'x={x!a}'") - self.assertAnnotationEqual("f'{x=!s:*^20}'", expected="f'x={x!s:*^20}'") - - def test_infinity_numbers(self): - inf = "1e" + repr(sys.float_info.max_10_exp + 1) - infj = f"{inf}j" - self.assertAnnotationEqual("1e1000", expected=inf) - self.assertAnnotationEqual("1e1000j", expected=infj) - self.assertAnnotationEqual("-1e1000", expected=f"-{inf}") - self.assertAnnotationEqual("3+1e1000j", expected=f"3 + {infj}") - self.assertAnnotationEqual("(1e1000, 1e1000j)", expected=f"({inf}, {infj})") - self.assertAnnotationEqual("'inf'") - self.assertAnnotationEqual("('inf', 1e1000, 'infxxx', 1e1000j)", expected=f"('inf', {inf}, 'infxxx', {infj})") - self.assertAnnotationEqual("(1e1000, (1e1000j,))", expected=f"({inf}, ({infj},))") - - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 40c2eb8d232..145adb67781 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -91,6 +91,10 @@ class AsyncBadSyntaxTest(unittest.TestCase): pass """, + """async def foo(a:await something()): + pass + """, + """async def foo(): def bar(): [i async for i in els] @@ -295,6 +299,10 @@ class AsyncBadSyntaxTest(unittest.TestCase): pass """, + """async def foo(a:await b): + pass + """, + """def baz(): async def foo(a=await b): pass diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index 4beed69e45b..f35f466125d 100644 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -9,7 +9,6 @@ import pickle import inspect import builtins import unittest -from textwrap import dedent from unittest.mock import Mock from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional from typing import get_type_hints @@ -563,17 +562,17 @@ class TestCase(unittest.TestCase): self.assertEqual(len(the_fields), 3) self.assertEqual(the_fields[0].name, 'x') - self.assertEqual(the_fields[0].type, 'int') + self.assertEqual(the_fields[0].type, int) self.assertFalse(hasattr(C, 'x')) self.assertTrue (the_fields[0].init) self.assertTrue (the_fields[0].repr) self.assertEqual(the_fields[1].name, 'y') - self.assertEqual(the_fields[1].type, 'str') + self.assertEqual(the_fields[1].type, str) self.assertIsNone(getattr(C, 'y')) self.assertFalse(the_fields[1].init) self.assertTrue (the_fields[1].repr) self.assertEqual(the_fields[2].name, 'z') - self.assertEqual(the_fields[2].type, 'str') + self.assertEqual(the_fields[2].type, str) self.assertFalse(hasattr(C, 'z')) self.assertTrue (the_fields[2].init) self.assertFalse(the_fields[2].repr) @@ -759,11 +758,11 @@ class TestCase(unittest.TestCase): def validate_class(cls): # First, check __annotations__, even though they're not # function annotations. - self.assertEqual(cls.__annotations__['i'], 'int') - self.assertEqual(cls.__annotations__['j'], 'str') - self.assertEqual(cls.__annotations__['k'], 'F') - self.assertEqual(cls.__annotations__['l'], 'float') - self.assertEqual(cls.__annotations__['z'], 'complex') + self.assertEqual(cls.__annotations__['i'], int) + self.assertEqual(cls.__annotations__['j'], str) + self.assertEqual(cls.__annotations__['k'], F) + self.assertEqual(cls.__annotations__['l'], float) + self.assertEqual(cls.__annotations__['z'], complex) # Verify __init__. @@ -778,22 +777,22 @@ class TestCase(unittest.TestCase): self.assertEqual(param.name, 'self') param = next(params) self.assertEqual(param.name, 'i') - self.assertIs (param.annotation, 'int') + self.assertIs (param.annotation, int) self.assertEqual(param.default, inspect.Parameter.empty) self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) param = next(params) self.assertEqual(param.name, 'j') - self.assertIs (param.annotation, 'str') + self.assertIs (param.annotation, str) self.assertEqual(param.default, inspect.Parameter.empty) self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) param = next(params) self.assertEqual(param.name, 'k') - self.assertIs (param.annotation, 'F') + self.assertIs (param.annotation, F) # Don't test for the default, since it's set to MISSING. self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) param = next(params) self.assertEqual(param.name, 'l') - self.assertIs (param.annotation, 'float') + self.assertIs (param.annotation, float) # Don't test for the default, since it's set to MISSING. self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) self.assertRaises(StopIteration, next, params) @@ -2855,6 +2854,9 @@ class TestDescriptors(unittest.TestCase): class TestStringAnnotations(unittest.TestCase): def test_classvar(self): + # Some expressions recognized as ClassVar really aren't. But + # if you're using string annotations, it's not an exact + # science. # These tests assume that both "import typing" and "from # typing import *" have been run in this file. for typestr in ('ClassVar[int]', @@ -2869,15 +2871,17 @@ class TestStringAnnotations(unittest.TestCase): 'typing. ClassVar[str]', 'typing.ClassVar [str]', 'typing.ClassVar [ str]', - # Double stringified - '"typing.ClassVar[int]"', + # Not syntactically valid, but these will - # be treated as ClassVars. + # be treated as ClassVars. 'typing.ClassVar.[int]', 'typing.ClassVar+', ): with self.subTest(typestr=typestr): - C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) + @dataclass + class C: + x: typestr + # x is a ClassVar, so C() takes no args. C() @@ -2898,7 +2902,9 @@ class TestStringAnnotations(unittest.TestCase): 'typingxClassVar[str]', ): with self.subTest(typestr=typestr): - C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) + @dataclass + class C: + x: typestr # x is not a ClassVar, so C() takes one arg. self.assertEqual(C(10).x, 10) @@ -2918,16 +2924,16 @@ class TestStringAnnotations(unittest.TestCase): 'dataclasses. InitVar[str]', 'dataclasses.InitVar [str]', 'dataclasses.InitVar [ str]', - # Double stringified - '"dataclasses.InitVar[int]"', + # Not syntactically valid, but these will # be treated as InitVars. 'dataclasses.InitVar.[int]', 'dataclasses.InitVar+', ): with self.subTest(typestr=typestr): - C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) - + @dataclass + class C: + x: typestr # x is an InitVar, so doesn't create a member. with self.assertRaisesRegex(AttributeError, @@ -2941,22 +2947,30 @@ class TestStringAnnotations(unittest.TestCase): 'typing.xInitVar[int]', ): with self.subTest(typestr=typestr): - C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) + @dataclass + class C: + x: typestr # x is not an InitVar, so there will be a member x. self.assertEqual(C(10).x, 10) def test_classvar_module_level_import(self): from test import dataclass_module_1 + from test import dataclass_module_1_str from test import dataclass_module_2 + from test import dataclass_module_2_str - for m in (dataclass_module_1, - dataclass_module_2): + for m in (dataclass_module_1, dataclass_module_1_str, + dataclass_module_2, dataclass_module_2_str, + ): with self.subTest(m=m): # There's a difference in how the ClassVars are # interpreted when using string annotations or # not. See the imported modules for details. - c = m.CV(10) + if m.USING_STRINGS: + c = m.CV(10) + else: + c = m.CV() self.assertEqual(c.cv0, 20) @@ -2972,9 +2986,14 @@ class TestStringAnnotations(unittest.TestCase): # not an instance field. getattr(c, field_name) - # iv4 is interpreted as a normal field. - self.assertIn('not_iv4', c.__dict__) - self.assertEqual(c.not_iv4, 4) + if m.USING_STRINGS: + # iv4 is interpreted as a normal field. + self.assertIn('not_iv4', c.__dict__) + self.assertEqual(c.not_iv4, 4) + else: + # iv4 is interpreted as an InitVar, so it + # won't exist on the instance. + self.assertNotIn('not_iv4', c.__dict__) def test_text_annotations(self): from test import dataclass_textanno diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 19e5c0f6335..8e36ae266b9 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -237,26 +237,28 @@ dis_annot_stmt_str = """\ 2 0 SETUP_ANNOTATIONS 2 LOAD_CONST 0 (1) 4 STORE_NAME 0 (x) - 6 LOAD_CONST 1 ('int') - 8 LOAD_NAME 1 (__annotations__) - 10 LOAD_CONST 2 ('x') + 6 LOAD_NAME 1 (int) + 8 LOAD_NAME 2 (__annotations__) + 10 LOAD_CONST 1 ('x') 12 STORE_SUBSCR - 3 14 LOAD_CONST 3 ('fun(1)') - 16 LOAD_NAME 1 (__annotations__) - 18 LOAD_CONST 4 ('y') - 20 STORE_SUBSCR - - 4 22 LOAD_CONST 0 (1) - 24 LOAD_NAME 2 (lst) - 26 LOAD_NAME 3 (fun) - 28 LOAD_CONST 5 (0) - 30 CALL_FUNCTION 1 - 32 STORE_SUBSCR - 34 LOAD_NAME 4 (int) - 36 POP_TOP - 38 LOAD_CONST 6 (None) - 40 RETURN_VALUE + 3 14 LOAD_NAME 3 (fun) + 16 LOAD_CONST 0 (1) + 18 CALL_FUNCTION 1 + 20 LOAD_NAME 2 (__annotations__) + 22 LOAD_CONST 2 ('y') + 24 STORE_SUBSCR + + 4 26 LOAD_CONST 0 (1) + 28 LOAD_NAME 4 (lst) + 30 LOAD_NAME 3 (fun) + 32 LOAD_CONST 3 (0) + 34 CALL_FUNCTION 1 + 36 STORE_SUBSCR + 38 LOAD_NAME 1 (int) + 40 POP_TOP + 42 LOAD_CONST 4 (None) + 44 RETURN_VALUE """ compound_stmt_str = """\ diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index caeeb2712a1..fba9281deb3 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -617,7 +617,7 @@ class TestUpdateWrapper(unittest.TestCase): def _default_update(self): - def f(a: int): + def f(a:'This is a new annotation'): """This is a test""" pass f.attr = 'This is also a test' @@ -634,7 +634,7 @@ class TestUpdateWrapper(unittest.TestCase): self.assertEqual(wrapper.__name__, 'f') self.assertEqual(wrapper.__qualname__, f.__qualname__) self.assertEqual(wrapper.attr, 'This is also a test') - self.assertEqual(wrapper.__annotations__['a'], 'int') + self.assertEqual(wrapper.__annotations__['a'], 'This is a new annotation') self.assertNotIn('b', wrapper.__annotations__) @unittest.skipIf(sys.flags.optimize >= 2, diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index c15f10b8bc7..6f79e19a544 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -363,7 +363,7 @@ class GrammarTests(unittest.TestCase): z = 2 def __init__(self, x): self.x: int = x - self.assertEqual(C.__annotations__, {'_C__foo': 'int', 's': 'str'}) + self.assertEqual(C.__annotations__, {'_C__foo': int, 's': str}) with self.assertRaises(NameError): class CBad: no_such_name_defined.attr: int = 0 @@ -379,15 +379,15 @@ class GrammarTests(unittest.TestCase): return {'__annotations__': CNS()} class CC(metaclass=CMeta): XX: 'ANNOT' - self.assertEqual(CC.__annotations__['xx'], repr('ANNOT')) + self.assertEqual(CC.__annotations__['xx'], 'ANNOT') def test_var_annot_module_semantics(self): with self.assertRaises(AttributeError): print(test.__annotations__) self.assertEqual(ann_module.__annotations__, - {1: 2, 'x': 'int', 'y': 'str', 'f': 'Tuple[int, int]'}) + {1: 2, 'x': int, 'y': str, 'f': typing.Tuple[int, int]}) self.assertEqual(ann_module.M.__annotations__, - {'123': 123, 'o': 'type'}) + {'123': 123, 'o': type}) self.assertEqual(ann_module2.__annotations__, {}) def test_var_annot_in_module(self): @@ -406,7 +406,7 @@ class GrammarTests(unittest.TestCase): exec("'docstring'\n" "__annotations__[1] = 2\n" "x: int = 5\n", gns, lns) - self.assertEqual(lns["__annotations__"], {1: 2, 'x': 'int'}) + self.assertEqual(lns["__annotations__"], {1: 2, 'x': int}) with self.assertRaises(KeyError): gns['__annotations__'] @@ -414,8 +414,8 @@ class GrammarTests(unittest.TestCase): # 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(ns['__annotations__']['x'], int) + self.assertEqual(ns['__annotations__']['z'], str) with self.assertRaises(KeyError): ns['__annotations__']['w'] nonloc_ns = {} @@ -429,7 +429,7 @@ class GrammarTests(unittest.TestCase): def __getitem__(self, item): return self._dct[item] exec('x: int = 1', {}, CNS2()) - self.assertEqual(nonloc_ns['__annotations__']['x'], 'int') + self.assertEqual(nonloc_ns['__annotations__']['x'], int) def test_var_annot_refleak(self): # complex case: custom locals plus custom __annotations__ @@ -446,7 +446,7 @@ class GrammarTests(unittest.TestCase): def __getitem__(self, item): return self._dct[item] exec('X: str', {}, CNS2()) - self.assertEqual(nonloc_ns['__annotations__']['x'], 'str') + self.assertEqual(nonloc_ns['__annotations__']['x'], str) def test_var_annot_rhs(self): ns = {} @@ -626,46 +626,50 @@ class GrammarTests(unittest.TestCase): # argument annotation tests def f(x) -> list: pass - self.assertEqual(f.__annotations__, {'return': 'list'}) + self.assertEqual(f.__annotations__, {'return': list}) def f(x: int): pass - self.assertEqual(f.__annotations__, {'x': 'int'}) + self.assertEqual(f.__annotations__, {'x': int}) def f(x: int, /): pass - self.assertEqual(f.__annotations__, {'x': 'int'}) + self.assertEqual(f.__annotations__, {'x': int}) def f(x: int = 34, /): pass - self.assertEqual(f.__annotations__, {'x': 'int'}) + self.assertEqual(f.__annotations__, {'x': int}) def f(*x: str): pass - self.assertEqual(f.__annotations__, {'x': 'str'}) + self.assertEqual(f.__annotations__, {'x': str}) def f(**x: float): pass - self.assertEqual(f.__annotations__, {'x': 'float'}) + self.assertEqual(f.__annotations__, {'x': float}) + def f(x, y: 1+2): pass + self.assertEqual(f.__annotations__, {'y': 3}) + def f(x, y: 1+2, /): pass + self.assertEqual(f.__annotations__, {'y': 3}) def f(a, b: 1, c: 2, d): pass - self.assertEqual(f.__annotations__, {'b': '1', 'c': '2'}) + self.assertEqual(f.__annotations__, {'b': 1, 'c': 2}) def f(a, b: 1, /, c: 2, d): pass - self.assertEqual(f.__annotations__, {'b': '1', 'c': '2'}) + self.assertEqual(f.__annotations__, {'b': 1, 'c': 2}) def f(a, b: 1, c: 2, d, e: 3 = 4, f=5, *g: 6): pass self.assertEqual(f.__annotations__, - {'b': '1', 'c': '2', 'e': '3', 'g': '6'}) + {'b': 1, 'c': 2, 'e': 3, 'g': 6}) def f(a, b: 1, c: 2, d, e: 3 = 4, f=5, *g: 6, h: 7, i=8, j: 9 = 10, **k: 11) -> 12: pass self.assertEqual(f.__annotations__, - {'b': '1', 'c': '2', 'e': '3', 'g': '6', 'h': '7', 'j': '9', - 'k': '11', 'return': '12'}) + {'b': 1, 'c': 2, 'e': 3, 'g': 6, 'h': 7, 'j': 9, + 'k': 11, 'return': 12}) def f(a, b: 1, c: 2, d, e: 3 = 4, f: int = 5, /, *g: 6, h: 7, i=8, j: 9 = 10, **k: 11) -> 12: pass self.assertEqual(f.__annotations__, - {'b': '1', 'c': '2', 'e': '3', 'f': 'int', 'g': '6', 'h': '7', 'j': '9', - 'k': '11', 'return': '12'}) + {'b': 1, 'c': 2, 'e': 3, 'f': int, 'g': 6, 'h': 7, 'j': 9, + 'k': 11, 'return': 12}) # Check for issue #20625 -- annotations mangling class Spam: def f(self, *, __kw: 1): pass class Ham(Spam): pass - self.assertEqual(Spam.f.__annotations__, {'_Spam__kw': '1'}) - self.assertEqual(Ham.f.__annotations__, {'_Spam__kw': '1'}) + self.assertEqual(Spam.f.__annotations__, {'_Spam__kw': 1}) + self.assertEqual(Ham.f.__annotations__, {'_Spam__kw': 1}) # Check for SF Bug #1697248 - mixing decorators and a return annotation def null(x): return x @null def f(x) -> list: pass - self.assertEqual(f.__annotations__, {'return': 'list'}) + self.assertEqual(f.__annotations__, {'return': list}) # Test expressions as decorators (PEP 614): @False or null @@ -1113,6 +1117,8 @@ class GrammarTests(unittest.TestCase): # Not allowed at class scope check_syntax_error(self, "class foo:yield 1") check_syntax_error(self, "class foo:yield from ()") + # Check annotation refleak on SyntaxError + check_syntax_error(self, "def g(a:(yield)): pass") def test_yield_in_comprehensions(self): # Check yield in comprehensions diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 35ad0b93e7d..b32b3d37577 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -888,7 +888,7 @@ class TestClassesAndFunctions(unittest.TestCase): self.assertFullArgSpecEquals(mod2.annotated, ['arg1'], ann_e={'arg1' : list}, - formatted="(arg1: list)") + formatted='(arg1: list)') self.assertFullArgSpecEquals(mod2.keyword_only_arg, [], kwonlyargs_e=['arg'], formatted='(*, arg)') @@ -2237,8 +2237,8 @@ class TestSignatureObject(unittest.TestCase): pass self.assertEqual(self.signature(test), ((('a', ..., ..., "positional_or_keyword"), - ('b', ..., repr('foo'), "positional_or_keyword")), - '123')) + ('b', ..., 'foo', "positional_or_keyword")), + 123)) def test_signature_on_wkwonly(self): def test(*, a:float, b:str) -> int: @@ -2253,11 +2253,11 @@ class TestSignatureObject(unittest.TestCase): pass self.assertEqual(self.signature(test), ((('a', ..., ..., "positional_or_keyword"), - ('b', 10, repr('foo'), "positional_or_keyword"), - ('args', ..., repr('bar'), "var_positional"), - ('spam', ..., repr('baz'), "keyword_only"), + ('b', 10, 'foo', "positional_or_keyword"), + ('args', ..., 'bar', "var_positional"), + ('spam', ..., 'baz', "keyword_only"), ('ham', 123, ..., "keyword_only"), - ('kwargs', ..., 'int', "var_keyword")), + ('kwargs', ..., int, "var_keyword")), ...)) def test_signature_without_self(self): @@ -2666,12 +2666,12 @@ class TestSignatureObject(unittest.TestCase): self.assertEqual(self.signature(partial(partial(test, 1))), ((('b', ..., ..., "positional_or_keyword"), - ('c', ..., 'int', "positional_or_keyword")), - '42')) + ('c', ..., int, "positional_or_keyword")), + 42)) self.assertEqual(self.signature(partial(partial(test, 1), 2)), - ((('c', ..., 'int', "positional_or_keyword"),), - '42')) + ((('c', ..., int, "positional_or_keyword"),), + 42)) psig = inspect.signature(partial(partial(test, 1), 2)) @@ -2790,12 +2790,12 @@ class TestSignatureObject(unittest.TestCase): ((('it', ..., ..., 'positional_or_keyword'), ('a', ..., ..., 'positional_or_keyword'), ('c', 1, ..., 'keyword_only')), - repr('spam'))) + 'spam')) self.assertEqual(self.signature(Spam().ham), ((('a', ..., ..., 'positional_or_keyword'), ('c', 1, ..., 'keyword_only')), - repr('spam'))) + 'spam')) class Spam: def test(self: 'anno', x): @@ -2804,7 +2804,7 @@ class TestSignatureObject(unittest.TestCase): g = partialmethod(test, 1) self.assertEqual(self.signature(Spam.g), - ((('self', ..., repr('anno'), 'positional_or_keyword'),), + ((('self', ..., 'anno', 'positional_or_keyword'),), ...)) def test_signature_on_fake_partialmethod(self): @@ -3142,16 +3142,20 @@ class TestSignatureObject(unittest.TestCase): with self.assertRaisesRegex(TypeError, 'unhashable type'): hash(inspect.signature(foo)) + def foo(a) -> {}: pass + with self.assertRaisesRegex(TypeError, 'unhashable type'): + hash(inspect.signature(foo)) + def test_signature_str(self): def foo(a:int=1, *, b, c=None, **kwargs) -> 42: pass self.assertEqual(str(inspect.signature(foo)), - '(a: \'int\' = 1, *, b, c=None, **kwargs) -> \'42\'') + '(a: int = 1, *, b, c=None, **kwargs) -> 42') def foo(a:int=1, *args, b, c=None, **kwargs) -> 42: pass self.assertEqual(str(inspect.signature(foo)), - '(a: \'int\' = 1, *args, b, c=None, **kwargs) -> \'42\'') + '(a: int = 1, *args, b, c=None, **kwargs) -> 42') def foo(): pass @@ -3194,8 +3198,8 @@ class TestSignatureObject(unittest.TestCase): self.assertIs(sig.return_annotation, None) sig = sig.replace(return_annotation=sig.empty) self.assertIs(sig.return_annotation, sig.empty) - sig = sig.replace(return_annotation='42') - self.assertEqual(sig.return_annotation, '42') + sig = sig.replace(return_annotation=42) + self.assertEqual(sig.return_annotation, 42) self.assertEqual(sig, inspect.signature(test)) def test_signature_on_mangled_parameters(self): @@ -3207,8 +3211,8 @@ class TestSignatureObject(unittest.TestCase): self.assertEqual(self.signature(Spam.foo), ((('self', ..., ..., "positional_or_keyword"), - ('_Spam__p1', 2, '1', "positional_or_keyword"), - ('_Spam__p2', 3, '2', "keyword_only")), + ('_Spam__p1', 2, 1, "positional_or_keyword"), + ('_Spam__p2', 3, 2, "keyword_only")), ...)) self.assertEqual(self.signature(Spam.foo), @@ -3253,13 +3257,13 @@ class TestSignatureObject(unittest.TestCase): def test_signature_annotations_with_local_namespaces(self): class Foo: ... def func(foo: Foo) -> int: pass - def func2(foo: Foo, bar: Bar) -> int: pass + def func2(foo: Foo, bar: 'Bar') -> int: pass for signature_func in (inspect.signature, inspect.Signature.from_callable): with self.subTest(signature_func = signature_func): sig1 = signature_func(func) - self.assertEqual(sig1.return_annotation, 'int') - self.assertEqual(sig1.parameters['foo'].annotation, 'Foo') + self.assertEqual(sig1.return_annotation, int) + self.assertEqual(sig1.parameters['foo'].annotation, Foo) sig2 = signature_func(func, localns=locals()) self.assertEqual(sig2.return_annotation, int) @@ -3268,7 +3272,7 @@ class TestSignatureObject(unittest.TestCase): sig3 = signature_func(func2, globalns={'Bar': int}, localns=locals()) self.assertEqual(sig3.return_annotation, int) self.assertEqual(sig3.parameters['foo'].annotation, Foo) - self.assertEqual(sig3.parameters['bar'].annotation, int) + self.assertEqual(sig3.parameters['bar'].annotation, 'Bar') class TestParameterObject(unittest.TestCase): diff --git a/Lib/test/test_opcodes.py b/Lib/test/test_opcodes.py index 4be78a4e62b..d43a8303b17 100644 --- a/Lib/test/test_opcodes.py +++ b/Lib/test/test_opcodes.py @@ -39,7 +39,7 @@ 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__'], {'x': int, 1: 2}) def test_do_not_recreate_annotations(self): # Don't rely on the existence of the '__annotations__' global. diff --git a/Lib/test/test_positional_only_arg.py b/Lib/test/test_positional_only_arg.py index 1fe8256d46e..0a9503e2025 100644 --- a/Lib/test/test_positional_only_arg.py +++ b/Lib/test/test_positional_only_arg.py @@ -302,14 +302,14 @@ class PositionalOnlyTestCase(unittest.TestCase): def f(x: int, /): ... return f - assert inner_has_pos_only().__annotations__ == {'x': 'int'} + assert inner_has_pos_only().__annotations__ == {'x': int} class Something: def method(self): def f(x: int, /): ... return f - assert Something().method().__annotations__ == {'x': 'int'} + assert Something().method().__annotations__ == {'x': int} def multiple_levels(): def inner_has_pos_only(): @@ -317,7 +317,7 @@ class PositionalOnlyTestCase(unittest.TestCase): return f return inner_has_pos_only() - assert multiple_levels().__annotations__ == {'x': 'int'} + assert multiple_levels().__annotations__ == {'x': int} def test_same_keyword_as_positional_with_kwargs(self): def f(something,/,**kwargs): @@ -429,6 +429,17 @@ class PositionalOnlyTestCase(unittest.TestCase): self.assertEqual(C().method(), sentinel) + def test_annotations_constant_fold(self): + def g(): + def f(x: not (int is int), /): ... + + # 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)] + self.assertNotIn(('UNARY_NOT', None), codes) + self.assertIn(('IS_OP', 1), codes) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 9bde0c75bc9..c862a805bbc 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -81,7 +81,7 @@ CLASSES |\x20\x20 | NO_MEANING = 'eggs' |\x20\x20 - | __annotations__ = {'NO_MEANING': 'str'} + | __annotations__ = {'NO_MEANING': <class 'str'>} \x20\x20\x20\x20 class C(builtins.object) | Methods defined here: @@ -194,7 +194,7 @@ Data descriptors defined here:<br> Data and other attributes defined here:<br> <dl><dt><strong>NO_MEANING</strong> = 'eggs'</dl> -<dl><dt><strong>__annotations__</strong> = {'NO_MEANING': 'str'}</dl> +<dl><dt><strong>__annotations__</strong> = {'NO_MEANING': <class 'str'>}</dl> </td></tr></table> <p> <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 4d8198e5db6..5f622b092f9 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -1018,6 +1018,14 @@ Corner-cases that used to fail to raise the correct error: Traceback (most recent call last): SyntaxError: cannot assign to __debug__ + >>> def f(*args:(lambda __debug__:0)): pass + Traceback (most recent call last): + SyntaxError: cannot assign to __debug__ + + >>> def f(**kwargs:(lambda __debug__:0)): pass + Traceback (most recent call last): + SyntaxError: cannot assign to __debug__ + >>> with (lambda *:0): pass Traceback (most recent call last): SyntaxError: named arguments must follow bare * diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index d8a48ce36f6..25ebec5fa55 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -671,8 +671,8 @@ class TypesTests(unittest.TestCase): ForwardBefore = 'Forward' | T def forward_after(x: ForwardAfter[int]) -> None: ... def forward_before(x: ForwardBefore[int]) -> None: ... - assert typing.get_args(typing.get_type_hints(forward_after, localns=locals())['x']) == (int, Forward) - assert typing.get_args(typing.get_type_hints(forward_before, localns=locals())['x']) == (int, Forward) + assert typing.get_args(typing.get_type_hints(forward_after)['x']) == (int, Forward) + assert typing.get_args(typing.get_type_hints(forward_before)['x']) == (int, Forward) def test_or_type_operator_with_Protocol(self): class Proto(typing.Protocol): diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 50723c4df19..99417d7d364 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -363,7 +363,7 @@ class UnionTests(BaseTestCase): def test_no_eval_union(self): u = Union[int, str] def f(x: u): ... - self.assertIs(get_type_hints(f, globals(), locals())['x'], u) + self.assertIs(get_type_hints(f)['x'], u) def test_function_repr_union(self): def fun() -> int: ... @@ -2876,7 +2876,7 @@ class GetTypeHintTests(BaseTestCase): self.assertEqual(gth(HasForeignBaseClass), {'some_xrepr': XRepr, 'other_a': mod_generics_cache.A, 'some_b': mod_generics_cache.B}) - self.assertEqual(gth(XRepr), + self.assertEqual(gth(XRepr.__new__), {'x': int, 'y': int}) self.assertEqual(gth(mod_generics_cache.B), {'my_inner_a1': mod_generics_cache.B.A, @@ -3689,7 +3689,7 @@ class NamedTupleTests(BaseTestCase): self.assertEqual(tim.cool, 9000) self.assertEqual(CoolEmployee.__name__, 'CoolEmployee') self.assertEqual(CoolEmployee._fields, ('name', 'cool')) - self.assertEqual(gth(CoolEmployee), + self.assertEqual(CoolEmployee.__annotations__, collections.OrderedDict(name=str, cool=int)) def test_annotation_usage_with_default(self): @@ -3703,7 +3703,7 @@ class NamedTupleTests(BaseTestCase): self.assertEqual(CoolEmployeeWithDefault.__name__, 'CoolEmployeeWithDefault') self.assertEqual(CoolEmployeeWithDefault._fields, ('name', 'cool')) - self.assertEqual(gth(CoolEmployeeWithDefault), + self.assertEqual(CoolEmployeeWithDefault.__annotations__, dict(name=str, cool=int)) self.assertEqual(CoolEmployeeWithDefault._field_defaults, dict(cool=0)) @@ -3871,7 +3871,7 @@ class TypedDictTests(BaseTestCase): def test_py36_class_syntax_usage(self): self.assertEqual(LabelPoint2D.__name__, 'LabelPoint2D') self.assertEqual(LabelPoint2D.__module__, __name__) - self.assertEqual(gth(LabelPoint2D), {'x': int, 'y': int, 'label': str}) + self.assertEqual(LabelPoint2D.__annotations__, {'x': int, 'y': int, 'label': str}) self.assertEqual(LabelPoint2D.__bases__, (dict,)) self.assertEqual(LabelPoint2D.__total__, True) self.assertNotIsSubclass(LabelPoint2D, typing.Sequence) @@ -3934,11 +3934,11 @@ class TypedDictTests(BaseTestCase): assert BaseAnimal.__required_keys__ == frozenset(['name']) assert BaseAnimal.__optional_keys__ == frozenset([]) - assert gth(BaseAnimal) == {'name': str} + assert BaseAnimal.__annotations__ == {'name': str} assert Animal.__required_keys__ == frozenset(['name']) assert Animal.__optional_keys__ == frozenset(['tail', 'voice']) - assert gth(Animal) == { + assert Animal.__annotations__ == { 'name': str, 'tail': bool, 'voice': str, @@ -3946,7 +3946,7 @@ class TypedDictTests(BaseTestCase): assert Cat.__required_keys__ == frozenset(['name', 'fur_color']) assert Cat.__optional_keys__ == frozenset(['tail', 'voice']) - assert gth(Cat) == { + assert Cat.__annotations__ == { 'fur_color': str, 'name': str, 'tail': bool, @@ -3967,7 +3967,7 @@ class IOTests(BaseTestCase): def stuff(a: IO) -> AnyStr: return a.readline() - a = gth(stuff)['a'] + a = stuff.__annotations__['a'] self.assertEqual(a.__parameters__, (AnyStr,)) def test_textio(self): @@ -3975,7 +3975,7 @@ class IOTests(BaseTestCase): def stuff(a: TextIO) -> str: return a.readline() - a = gth(stuff)['a'] + a = stuff.__annotations__['a'] self.assertEqual(a.__parameters__, ()) def test_binaryio(self): @@ -3983,7 +3983,7 @@ class IOTests(BaseTestCase): def stuff(a: BinaryIO) -> bytes: return a.readline() - a = gth(stuff)['a'] + a = stuff.__annotations__['a'] self.assertEqual(a.__parameters__, ()) def test_io_submodule(self): |