mirror of
				https://github.com/python/cpython.git
				synced 2025-10-29 20:51:26 +00:00 
			
		
		
		
	
		
			
	
	
		
			229 lines
		
	
	
	
		
			8.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			229 lines
		
	
	
	
		
			8.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | 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() |