|
| 1 | +import unittest |
| 2 | +from textwrap import dedent |
| 3 | +import sys |
| 4 | + |
| 5 | +class AnnotationsFutureTestCase(unittest.TestCase): |
| 6 | + template = dedent( |
| 7 | + """ |
| 8 | + from __future__ import annotations |
| 9 | + def f() -> {ann}: |
| 10 | + ... |
| 11 | + def g(arg: {ann}) -> None: |
| 12 | + ... |
| 13 | + async def f2() -> {ann}: |
| 14 | + ... |
| 15 | + async def g2(arg: {ann}) -> None: |
| 16 | + ... |
| 17 | + var: {ann} |
| 18 | + var2: {ann} = None |
| 19 | + """ |
| 20 | + ) |
| 21 | + |
| 22 | + def getActual(self, annotation): |
| 23 | + scope = {} |
| 24 | + exec(self.template.format(ann=annotation), {}, scope) |
| 25 | + func_ret_ann = scope['f'].__annotations__['return'] |
| 26 | + func_arg_ann = scope['g'].__annotations__['arg'] |
| 27 | + async_func_ret_ann = scope['f2'].__annotations__['return'] |
| 28 | + async_func_arg_ann = scope['g2'].__annotations__['arg'] |
| 29 | + var_ann1 = scope['__annotations__']['var'] |
| 30 | + var_ann2 = scope['__annotations__']['var2'] |
| 31 | + self.assertEqual(func_ret_ann, func_arg_ann) |
| 32 | + self.assertEqual(func_ret_ann, async_func_ret_ann) |
| 33 | + self.assertEqual(func_ret_ann, async_func_arg_ann) |
| 34 | + self.assertEqual(func_ret_ann, var_ann1) |
| 35 | + self.assertEqual(func_ret_ann, var_ann2) |
| 36 | + return func_ret_ann |
| 37 | + |
| 38 | + def assertAnnotationEqual( |
| 39 | + self, annotation, expected=None, drop_parens=False, is_tuple=False, |
| 40 | + ): |
| 41 | + actual = self.getActual(annotation) |
| 42 | + if expected is None: |
| 43 | + expected = annotation if not is_tuple else annotation[1:-1] |
| 44 | + if drop_parens: |
| 45 | + self.assertNotEqual(actual, expected) |
| 46 | + actual = actual.replace("(", "").replace(")", "") |
| 47 | + |
| 48 | + self.assertEqual(actual, expected) |
| 49 | + |
| 50 | + def test_annotations(self): |
| 51 | + eq = self.assertAnnotationEqual |
| 52 | + eq('...') |
| 53 | + eq("'some_string'") |
| 54 | + eq("u'some_string'") |
| 55 | + eq("b'\\xa3'") |
| 56 | + eq('Name') |
| 57 | + eq('None') |
| 58 | + eq('True') |
| 59 | + eq('False') |
| 60 | + eq('1') |
| 61 | + eq('1.0') |
| 62 | + eq('1j') |
| 63 | + eq('True or False') |
| 64 | + eq('True or False or None') |
| 65 | + eq('True and False') |
| 66 | + eq('True and False and None') |
| 67 | + eq('Name1 and Name2 or Name3') |
| 68 | + eq('Name1 and (Name2 or Name3)') |
| 69 | + eq('Name1 or Name2 and Name3') |
| 70 | + eq('(Name1 or Name2) and Name3') |
| 71 | + eq('Name1 and Name2 or Name3 and Name4') |
| 72 | + eq('Name1 or Name2 and Name3 or Name4') |
| 73 | + eq('a + b + (c + d)') |
| 74 | + eq('a * b * (c * d)') |
| 75 | + eq('(a ** b) ** c ** d') |
| 76 | + eq('v1 << 2') |
| 77 | + eq('1 >> v2') |
| 78 | + eq('1 % finished') |
| 79 | + eq('1 + v2 - v3 * 4 ^ 5 ** v6 / 7 // 8') |
| 80 | + eq('not great') |
| 81 | + eq('not not great') |
| 82 | + eq('~great') |
| 83 | + eq('+value') |
| 84 | + eq('++value') |
| 85 | + eq('-1') |
| 86 | + eq('~int and not v1 ^ 123 + v2 | True') |
| 87 | + eq('a + (not b)') |
| 88 | + eq('lambda: None') |
| 89 | + eq('lambda arg: None') |
| 90 | + eq('lambda a=True: a') |
| 91 | + eq('lambda a, b, c=True: a') |
| 92 | + eq("lambda a, b, c=True, *, d=1 << v2, e='str': a") |
| 93 | + eq("lambda a, b, c=True, *vararg, d, e='str', **kwargs: a + b") |
| 94 | + eq("lambda a, /, b, c=True, *vararg, d, e='str', **kwargs: a + b") |
| 95 | + eq('lambda x, /: x') |
| 96 | + eq('lambda x=1, /: x') |
| 97 | + eq('lambda x, /, y: x + y') |
| 98 | + eq('lambda x=1, /, y=2: x + y') |
| 99 | + eq('lambda x, /, y=1: x + y') |
| 100 | + eq('lambda x, /, y=1, *, z=3: x + y + z') |
| 101 | + eq('lambda x=1, /, y=2, *, z=3: x + y + z') |
| 102 | + eq('lambda x=1, /, y=2, *, z: x + y + z') |
| 103 | + eq('lambda x=1, y=2, z=3, /, w=4, *, l, l2: x + y + z + w + l + l2') |
| 104 | + eq('lambda x=1, y=2, z=3, /, w=4, *, l, l2, **kwargs: x + y + z + w + l + l2') |
| 105 | + eq('lambda x, /, y=1, *, z: x + y + z') |
| 106 | + eq('lambda x: lambda y: x + y') |
| 107 | + eq('1 if True else 2') |
| 108 | + eq('str or None if int or True else str or bytes or None') |
| 109 | + eq('str or None if (1 if True else 2) else str or bytes or None') |
| 110 | + eq("0 if not x else 1 if x > 0 else -1") |
| 111 | + eq("(1 if x > 0 else -1) if x else 0") |
| 112 | + eq("{'2.7': dead, '3.7': long_live or die_hard}") |
| 113 | + eq("{'2.7': dead, '3.7': long_live or die_hard, **{'3.6': verygood}}") |
| 114 | + eq("{**a, **b, **c}") |
| 115 | + eq("{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}") |
| 116 | + eq("{*a, *b, *c}") |
| 117 | + eq("({'a': 'b'}, True or False, +value, 'string', b'bytes') or None") |
| 118 | + eq("()") |
| 119 | + eq("(a,)") |
| 120 | + eq("(a, b)") |
| 121 | + eq("(a, b, c)") |
| 122 | + eq("(*a, *b, *c)") |
| 123 | + eq("[]") |
| 124 | + eq("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]") |
| 125 | + eq("[*a, *b, *c]") |
| 126 | + eq("{i for i in (1, 2, 3)}") |
| 127 | + eq("{i ** 2 for i in (1, 2, 3)}") |
| 128 | + eq("{i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))}") |
| 129 | + eq("{i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3)}") |
| 130 | + eq("[i for i in (1, 2, 3)]") |
| 131 | + eq("[i ** 2 for i in (1, 2, 3)]") |
| 132 | + eq("[i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))]") |
| 133 | + eq("[i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3)]") |
| 134 | + eq("(i for i in (1, 2, 3))") |
| 135 | + eq("(i ** 2 for i in (1, 2, 3))") |
| 136 | + eq("(i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c')))") |
| 137 | + eq("(i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3))") |
| 138 | + eq("{i: 0 for i in (1, 2, 3)}") |
| 139 | + eq("{i: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))}") |
| 140 | + eq("[(x, y) for x, y in (a, b)]") |
| 141 | + eq("[(x,) for x, in (a,)]") |
| 142 | + eq("Python3 > Python2 > COBOL") |
| 143 | + eq("Life is Life") |
| 144 | + eq("call()") |
| 145 | + eq("call(arg)") |
| 146 | + eq("call(kwarg='hey')") |
| 147 | + eq("call(arg, kwarg='hey')") |
| 148 | + eq("call(arg, *args, another, kwarg='hey')") |
| 149 | + eq("call(arg, another, kwarg='hey', **kwargs, kwarg2='ho')") |
| 150 | + eq("lukasz.langa.pl") |
| 151 | + eq("call.me(maybe)") |
| 152 | + eq("1 .real") |
| 153 | + eq("1.0.real") |
| 154 | + eq("....__class__") |
| 155 | + eq("list[str]") |
| 156 | + eq("dict[str, int]") |
| 157 | + eq("set[str,]") |
| 158 | + eq("tuple[str, ...]") |
| 159 | + eq("tuple[(str, *types)]") |
| 160 | + eq("tuple[str, int, (str, int)]") |
| 161 | + eq("tuple[(*int, str, str, (str, int))]") |
| 162 | + eq("tuple[str, int, float, dict[str, int]]") |
| 163 | + eq("slice[0]") |
| 164 | + eq("slice[0:1]") |
| 165 | + eq("slice[0:1:2]") |
| 166 | + eq("slice[:]") |
| 167 | + eq("slice[:-1]") |
| 168 | + eq("slice[1:]") |
| 169 | + eq("slice[::-1]") |
| 170 | + eq("slice[:,]") |
| 171 | + eq("slice[1:2,]") |
| 172 | + eq("slice[1:2:3,]") |
| 173 | + eq("slice[1:2, 1]") |
| 174 | + eq("slice[1:2, 2, 3]") |
| 175 | + eq("slice[()]") |
| 176 | + eq("slice[a, b:c, d:e:f]") |
| 177 | + eq("slice[(x for x in a)]") |
| 178 | + eq('str or None if sys.version_info[0] > (3,) else str or bytes or None') |
| 179 | + eq("f'f-string without formatted values is just a string'") |
| 180 | + eq("f'{{NOT a formatted value}}'") |
| 181 | + eq("f'some f-string with {a} {few():.2f} {formatted.values!r}'") |
| 182 | + eq('''f"{f'{nested} inner'} outer"''') |
| 183 | + eq("f'space between opening braces: { {a for a in (1, 2, 3)}}'") |
| 184 | + eq("f'{(lambda x: x)}'") |
| 185 | + eq("f'{(None if a else lambda x: x)}'") |
| 186 | + eq("f'{x}'") |
| 187 | + eq("f'{x!r}'") |
| 188 | + eq("f'{x!a}'") |
| 189 | + eq('(yield from outside_of_generator)') |
| 190 | + eq('(yield)') |
| 191 | + eq('(yield a + b)') |
| 192 | + eq('await some.complicated[0].call(with_args=True or 1 is not 1)') |
| 193 | + eq('[x for x in (a if b else c)]') |
| 194 | + eq('[x for x in a if (b if c else d)]') |
| 195 | + eq('f(x for x in a)') |
| 196 | + eq('f(1, (x for x in a))') |
| 197 | + eq('f((x for x in a), 2)') |
| 198 | + eq('(((a)))', 'a') |
| 199 | + eq('(((a, b)))', '(a, b)') |
| 200 | + eq("(x := 10)") |
| 201 | + eq("f'{(x := 10):=10}'") |
| 202 | + eq("1 + 2 + 3") |
| 203 | + |
| 204 | + def test_fstring_debug_annotations(self): |
| 205 | + # f-strings with '=' don't round trip very well, so set the expected |
| 206 | + # result explicitely. |
| 207 | + self.assertAnnotationEqual("f'{x=!r}'", expected="f'x={x!r}'") |
| 208 | + self.assertAnnotationEqual("f'{x=:}'", expected="f'x={x:}'") |
| 209 | + self.assertAnnotationEqual("f'{x=:.2f}'", expected="f'x={x:.2f}'") |
| 210 | + self.assertAnnotationEqual("f'{x=!r}'", expected="f'x={x!r}'") |
| 211 | + self.assertAnnotationEqual("f'{x=!a}'", expected="f'x={x!a}'") |
| 212 | + self.assertAnnotationEqual("f'{x=!s:*^20}'", expected="f'x={x!s:*^20}'") |
| 213 | + |
| 214 | + def test_infinity_numbers(self): |
| 215 | + inf = "1e" + repr(sys.float_info.max_10_exp + 1) |
| 216 | + infj = f"{inf}j" |
| 217 | + self.assertAnnotationEqual("1e1000", expected=inf) |
| 218 | + self.assertAnnotationEqual("1e1000j", expected=infj) |
| 219 | + self.assertAnnotationEqual("-1e1000", expected=f"-{inf}") |
| 220 | + self.assertAnnotationEqual("3+1e1000j", expected=f"3 + {infj}") |
| 221 | + self.assertAnnotationEqual("(1e1000, 1e1000j)", expected=f"({inf}, {infj})") |
| 222 | + self.assertAnnotationEqual("'inf'") |
| 223 | + self.assertAnnotationEqual("('inf', 1e1000, 'infxxx', 1e1000j)", expected=f"('inf', {inf}, 'infxxx', {infj})") |
| 224 | + self.assertAnnotationEqual("(1e1000, (1e1000j,))", expected=f"({inf}, ({infj},))") |
| 225 | + |
| 226 | + |
| 227 | +if __name__ == "__main__": |
| 228 | + unittest.main() |
0 commit comments