• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Test various flavors of legal and illegal future statements
2
3import __future__
4import ast
5import unittest
6from test.support import import_helper
7from test.support.script_helper import spawn_python, kill_python
8from textwrap import dedent
9import os
10import re
11import sys
12
13TOP_LEVEL_MSG = 'from __future__ imports must occur at the beginning of the file'
14
15rx = re.compile(r'\((\S+).py, line (\d+)')
16
17def get_error_location(msg):
18    mo = rx.search(str(msg))
19    return mo.group(1, 2)
20
21class FutureTest(unittest.TestCase):
22
23    def check_syntax_error(self, err, basename,
24                           *,
25                           lineno,
26                           message=TOP_LEVEL_MSG, offset=1):
27        if basename != '<string>':
28            basename += '.py'
29
30        self.assertEqual(f'{message} ({basename}, line {lineno})', str(err))
31        self.assertEqual(os.path.basename(err.filename), basename)
32        self.assertEqual(err.lineno, lineno)
33        self.assertEqual(err.offset, offset)
34
35    def assertSyntaxError(self, code,
36                          *,
37                          lineno=1,
38                          message=TOP_LEVEL_MSG, offset=1,
39                          parametrize_docstring=True):
40        code = dedent(code.lstrip('\n'))
41        for add_docstring in ([False, True] if parametrize_docstring else [False]):
42            with self.subTest(code=code, add_docstring=add_docstring):
43                if add_docstring:
44                    code = '"""Docstring"""\n' + code
45                    lineno += 1
46                with self.assertRaises(SyntaxError) as cm:
47                    exec(code)
48                self.check_syntax_error(cm.exception, "<string>",
49                                        lineno=lineno,
50                                        message=message,
51                                        offset=offset)
52
53    def test_import_nested_scope_twice(self):
54        # Import the name nested_scopes twice to trigger SF bug #407394
55        with import_helper.CleanImport(
56            'test.test_future_stmt.import_nested_scope_twice',
57        ):
58            from test.test_future_stmt import import_nested_scope_twice
59        self.assertEqual(import_nested_scope_twice.result, 6)
60
61    def test_nested_scope(self):
62        with import_helper.CleanImport('test.test_future_stmt.nested_scope'):
63            from test.test_future_stmt import nested_scope
64        self.assertEqual(nested_scope.result, 6)
65
66    def test_future_single_import(self):
67        with import_helper.CleanImport(
68            'test.test_future_stmt.test_future_single_import',
69        ):
70            from test.test_future_stmt import test_future_single_import
71
72    def test_future_multiple_imports(self):
73        with import_helper.CleanImport(
74            'test.test_future_stmt.test_future_multiple_imports',
75        ):
76            from test.test_future_stmt import test_future_multiple_imports
77
78    def test_future_multiple_features(self):
79        with import_helper.CleanImport(
80            "test.test_future_stmt.test_future_multiple_features",
81        ):
82            from test.test_future_stmt import test_future_multiple_features
83
84    def test_unknown_future_flag(self):
85        code = """
86            from __future__ import nested_scopes
87            from __future__ import rested_snopes  # typo error here: nested => rested
88        """
89        self.assertSyntaxError(
90            code, lineno=2,
91            message='future feature rested_snopes is not defined', offset=24,
92        )
93
94    def test_future_import_not_on_top(self):
95        code = """
96            import some_module
97            from __future__ import annotations
98        """
99        self.assertSyntaxError(code, lineno=2)
100
101        code = """
102            import __future__
103            from __future__ import annotations
104        """
105        self.assertSyntaxError(code, lineno=2)
106
107        code = """
108            from __future__ import absolute_import
109            "spam, bar, blah"
110            from __future__ import print_function
111        """
112        self.assertSyntaxError(code, lineno=3)
113
114    def test_future_import_with_extra_string(self):
115        code = """
116            '''Docstring'''
117            "this isn't a doc string"
118            from __future__ import nested_scopes
119        """
120        self.assertSyntaxError(code, lineno=3, parametrize_docstring=False)
121
122    def test_multiple_import_statements_on_same_line(self):
123        # With `\`:
124        code = """
125            from __future__ import nested_scopes; import string; from __future__ import \
126        nested_scopes
127        """
128        self.assertSyntaxError(code, offset=54)
129
130        # Without `\`:
131        code = """
132            from __future__ import nested_scopes; import string; from __future__ import  nested_scopes
133        """
134        self.assertSyntaxError(code, offset=54)
135
136    def test_future_import_star(self):
137        code = """
138            from __future__ import *
139        """
140        self.assertSyntaxError(code, message='future feature * is not defined', offset=24)
141
142    def test_future_import_braces(self):
143        code = """
144            from __future__ import braces
145        """
146        # Congrats, you found an easter egg!
147        self.assertSyntaxError(code, message='not a chance', offset=24)
148
149        code = """
150            from __future__ import nested_scopes, braces
151        """
152        self.assertSyntaxError(code, message='not a chance', offset=39)
153
154    def test_module_with_future_import_not_on_top(self):
155        with self.assertRaises(SyntaxError) as cm:
156            from test.test_future_stmt import badsyntax_future
157        self.check_syntax_error(cm.exception, "badsyntax_future", lineno=3)
158
159    def test_ensure_flags_dont_clash(self):
160        # bpo-39562: test that future flags and compiler flags doesn't clash
161
162        # obtain future flags (CO_FUTURE_***) from the __future__ module
163        flags = {
164            f"CO_FUTURE_{future.upper()}": getattr(__future__, future).compiler_flag
165            for future in __future__.all_feature_names
166        }
167        # obtain some of the exported compiler flags (PyCF_***) from the ast module
168        flags |= {
169            flag: getattr(ast, flag)
170            for flag in dir(ast) if flag.startswith("PyCF_")
171        }
172        self.assertCountEqual(set(flags.values()), flags.values())
173
174    def test_unicode_literals_exec(self):
175        scope = {}
176        exec("from __future__ import unicode_literals; x = ''", {}, scope)
177        self.assertIsInstance(scope["x"], str)
178
179    def test_syntactical_future_repl(self):
180        p = spawn_python('-i')
181        p.stdin.write(b"from __future__ import barry_as_FLUFL\n")
182        p.stdin.write(b"2 <> 3\n")
183        out = kill_python(p)
184        self.assertNotIn(b'SyntaxError: invalid syntax', out)
185
186    def test_future_dotted_import(self):
187        with self.assertRaises(ImportError):
188            exec("from .__future__ import spam")
189
190        code = dedent(
191            """
192            from __future__ import print_function
193            from ...__future__ import ham
194            """
195        )
196        with self.assertRaises(ImportError):
197            exec(code)
198
199        code = """
200            from .__future__ import nested_scopes
201            from __future__ import barry_as_FLUFL
202        """
203        self.assertSyntaxError(code, lineno=2)
204
205class AnnotationsFutureTestCase(unittest.TestCase):
206    template = dedent(
207        """
208        from __future__ import annotations
209        def f() -> {ann}:
210            ...
211        def g(arg: {ann}) -> None:
212            ...
213        async def f2() -> {ann}:
214            ...
215        async def g2(arg: {ann}) -> None:
216            ...
217        class H:
218            var: {ann}
219            object.attr: {ann}
220        var: {ann}
221        var2: {ann} = None
222        object.attr: {ann}
223        """
224    )
225
226    def getActual(self, annotation):
227        scope = {}
228        exec(self.template.format(ann=annotation), {}, scope)
229        func_ret_ann = scope['f'].__annotations__['return']
230        func_arg_ann = scope['g'].__annotations__['arg']
231        async_func_ret_ann = scope['f2'].__annotations__['return']
232        async_func_arg_ann = scope['g2'].__annotations__['arg']
233        var_ann1 = scope['__annotations__']['var']
234        var_ann2 = scope['__annotations__']['var2']
235        self.assertEqual(func_ret_ann, func_arg_ann)
236        self.assertEqual(func_ret_ann, async_func_ret_ann)
237        self.assertEqual(func_ret_ann, async_func_arg_ann)
238        self.assertEqual(func_ret_ann, var_ann1)
239        self.assertEqual(func_ret_ann, var_ann2)
240        return func_ret_ann
241
242    def assertAnnotationEqual(
243        self, annotation, expected=None, drop_parens=False, is_tuple=False,
244    ):
245        actual = self.getActual(annotation)
246        if expected is None:
247            expected = annotation if not is_tuple else annotation[1:-1]
248        if drop_parens:
249            self.assertNotEqual(actual, expected)
250            actual = actual.replace("(", "").replace(")", "")
251
252        self.assertEqual(actual, expected)
253
254    def _exec_future(self, code):
255        scope = {}
256        exec(
257            "from __future__ import annotations\n"
258            + code, scope
259        )
260        return scope
261
262    def test_annotations(self):
263        eq = self.assertAnnotationEqual
264        eq('...')
265        eq("'some_string'")
266        eq("u'some_string'")
267        eq("b'\\xa3'")
268        eq('Name')
269        eq('None')
270        eq('True')
271        eq('False')
272        eq('1')
273        eq('1.0')
274        eq('1j')
275        eq('True or False')
276        eq('True or False or None')
277        eq('True and False')
278        eq('True and False and None')
279        eq('Name1 and Name2 or Name3')
280        eq('Name1 and (Name2 or Name3)')
281        eq('Name1 or Name2 and Name3')
282        eq('(Name1 or Name2) and Name3')
283        eq('Name1 and Name2 or Name3 and Name4')
284        eq('Name1 or Name2 and Name3 or Name4')
285        eq('a + b + (c + d)')
286        eq('a * b * (c * d)')
287        eq('(a ** b) ** c ** d')
288        eq('v1 << 2')
289        eq('1 >> v2')
290        eq('1 % finished')
291        eq('1 + v2 - v3 * 4 ^ 5 ** v6 / 7 // 8')
292        eq('not great')
293        eq('not not great')
294        eq('~great')
295        eq('+value')
296        eq('++value')
297        eq('-1')
298        eq('~int and not v1 ^ 123 + v2 | True')
299        eq('a + (not b)')
300        eq('lambda: None')
301        eq('lambda arg: None')
302        eq('lambda a=True: a')
303        eq('lambda a, b, c=True: a')
304        eq("lambda a, b, c=True, *, d=1 << v2, e='str': a")
305        eq("lambda a, b, c=True, *vararg, d, e='str', **kwargs: a + b")
306        eq("lambda a, /, b, c=True, *vararg, d, e='str', **kwargs: a + b")
307        eq('lambda x, /: x')
308        eq('lambda x=1, /: x')
309        eq('lambda x, /, y: x + y')
310        eq('lambda x=1, /, y=2: x + y')
311        eq('lambda x, /, y=1: x + y')
312        eq('lambda x, /, y=1, *, z=3: x + y + z')
313        eq('lambda x=1, /, y=2, *, z=3: x + y + z')
314        eq('lambda x=1, /, y=2, *, z: x + y + z')
315        eq('lambda x=1, y=2, z=3, /, w=4, *, l, l2: x + y + z + w + l + l2')
316        eq('lambda x=1, y=2, z=3, /, w=4, *, l, l2, **kwargs: x + y + z + w + l + l2')
317        eq('lambda x, /, y=1, *, z: x + y + z')
318        eq('lambda x: lambda y: x + y')
319        eq('1 if True else 2')
320        eq('str or None if int or True else str or bytes or None')
321        eq('str or None if (1 if True else 2) else str or bytes or None')
322        eq("0 if not x else 1 if x > 0 else -1")
323        eq("(1 if x > 0 else -1) if x else 0")
324        eq("{'2.7': dead, '3.7': long_live or die_hard}")
325        eq("{'2.7': dead, '3.7': long_live or die_hard, **{'3.6': verygood}}")
326        eq("{**a, **b, **c}")
327        eq("{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}")
328        eq("{*a, *b, *c}")
329        eq("({'a': 'b'}, True or False, +value, 'string', b'bytes') or None")
330        eq("()")
331        eq("(a,)")
332        eq("(a, b)")
333        eq("(a, b, c)")
334        eq("(*a, *b, *c)")
335        eq("[]")
336        eq("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]")
337        eq("[*a, *b, *c]")
338        eq("{i for i in (1, 2, 3)}")
339        eq("{i ** 2 for i in (1, 2, 3)}")
340        eq("{i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))}")
341        eq("{i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3)}")
342        eq("[i for i in (1, 2, 3)]")
343        eq("[i ** 2 for i in (1, 2, 3)]")
344        eq("[i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))]")
345        eq("[i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3)]")
346        eq("(i for i in (1, 2, 3))")
347        eq("(i ** 2 for i in (1, 2, 3))")
348        eq("(i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c')))")
349        eq("(i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3))")
350        eq("{i: 0 for i in (1, 2, 3)}")
351        eq("{i: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))}")
352        eq("[(x, y) for x, y in (a, b)]")
353        eq("[(x,) for x, in (a,)]")
354        eq("Python3 > Python2 > COBOL")
355        eq("Life is Life")
356        eq("call()")
357        eq("call(arg)")
358        eq("call(kwarg='hey')")
359        eq("call(arg, kwarg='hey')")
360        eq("call(arg, *args, another, kwarg='hey')")
361        eq("call(arg, another, kwarg='hey', **kwargs, kwarg2='ho')")
362        eq("lukasz.langa.pl")
363        eq("call.me(maybe)")
364        eq("1 .real")
365        eq("1.0.real")
366        eq("....__class__")
367        eq("list[str]")
368        eq("dict[str, int]")
369        eq("set[str,]")
370        eq("tuple[()]")
371        eq("tuple[str, ...]")
372        eq("tuple[str, *types]")
373        eq("tuple[str, int, (str, int)]")
374        eq("tuple[*int, str, str, (str, int)]")
375        eq("tuple[str, int, float, dict[str, int]]")
376        eq("slice[0]")
377        eq("slice[0:1]")
378        eq("slice[0:1:2]")
379        eq("slice[:]")
380        eq("slice[:-1]")
381        eq("slice[1:]")
382        eq("slice[::-1]")
383        eq("slice[:,]")
384        eq("slice[1:2,]")
385        eq("slice[1:2:3,]")
386        eq("slice[1:2, 1]")
387        eq("slice[1:2, 2, 3]")
388        eq("slice[()]")
389        # Note that `slice[*Ts]`, `slice[*Ts,]`, and `slice[(*Ts,)]` all have
390        # the same AST, but only `slice[*Ts,]` passes this test, because that's
391        # what the unparser produces.
392        eq("slice[*Ts,]")
393        eq("slice[1, *Ts]")
394        eq("slice[*Ts, 2]")
395        eq("slice[1, *Ts, 2]")
396        eq("slice[*Ts, *Ts]")
397        eq("slice[1, *Ts, *Ts]")
398        eq("slice[*Ts, 1, *Ts]")
399        eq("slice[*Ts, *Ts, 1]")
400        eq("slice[1, *Ts, *Ts, 2]")
401        eq("slice[1:2, *Ts]")
402        eq("slice[*Ts, 1:2]")
403        eq("slice[1:2, *Ts, 3:4]")
404        eq("slice[a, b:c, d:e:f]")
405        eq("slice[(x for x in a)]")
406        eq('str or None if sys.version_info[0] > (3,) else str or bytes or None')
407        eq("f'f-string without formatted values is just a string'")
408        eq("f'{{NOT a formatted value}}'")
409        eq("f'some f-string with {a} {few():.2f} {formatted.values!r}'")
410        eq('''f"{f'{nested} inner'} outer"''')
411        eq("f'space between opening braces: { {a for a in (1, 2, 3)}}'")
412        eq("f'{(lambda x: x)}'")
413        eq("f'{(None if a else lambda x: x)}'")
414        eq("f'{x}'")
415        eq("f'{x!r}'")
416        eq("f'{x!a}'")
417        eq('[x for x in (a if b else c)]')
418        eq('[x for x in a if (b if c else d)]')
419        eq('f(x for x in a)')
420        eq('f(1, (x for x in a))')
421        eq('f((x for x in a), 2)')
422        eq('(((a)))', 'a')
423        eq('(((a, b)))', '(a, b)')
424        eq("1 + 2 + 3")
425
426    def test_fstring_debug_annotations(self):
427        # f-strings with '=' don't round trip very well, so set the expected
428        # result explicitly.
429        self.assertAnnotationEqual("f'{x=!r}'", expected="f'x={x!r}'")
430        self.assertAnnotationEqual("f'{x=:}'", expected="f'x={x:}'")
431        self.assertAnnotationEqual("f'{x=:.2f}'", expected="f'x={x:.2f}'")
432        self.assertAnnotationEqual("f'{x=!r}'", expected="f'x={x!r}'")
433        self.assertAnnotationEqual("f'{x=!a}'", expected="f'x={x!a}'")
434        self.assertAnnotationEqual("f'{x=!s:*^20}'", expected="f'x={x!s:*^20}'")
435
436    def test_infinity_numbers(self):
437        inf = "1e" + repr(sys.float_info.max_10_exp + 1)
438        infj = f"{inf}j"
439        self.assertAnnotationEqual("1e1000", expected=inf)
440        self.assertAnnotationEqual("1e1000j", expected=infj)
441        self.assertAnnotationEqual("-1e1000", expected=f"-{inf}")
442        self.assertAnnotationEqual("3+1e1000j", expected=f"3 + {infj}")
443        self.assertAnnotationEqual("(1e1000, 1e1000j)", expected=f"({inf}, {infj})")
444        self.assertAnnotationEqual("'inf'")
445        self.assertAnnotationEqual("('inf', 1e1000, 'infxxx', 1e1000j)", expected=f"('inf', {inf}, 'infxxx', {infj})")
446        self.assertAnnotationEqual("(1e1000, (1e1000j,))", expected=f"({inf}, ({infj},))")
447
448    def test_annotation_with_complex_target(self):
449        with self.assertRaises(SyntaxError):
450            exec(
451                "from __future__ import annotations\n"
452                "object.__debug__: int"
453            )
454
455    def test_annotations_symbol_table_pass(self):
456        namespace = self._exec_future(dedent("""
457        from __future__ import annotations
458
459        def foo():
460            outer = 1
461            def bar():
462                inner: outer = 1
463            return bar
464        """))
465
466        foo = namespace.pop("foo")
467        self.assertIsNone(foo().__closure__)
468        self.assertEqual(foo.__code__.co_cellvars, ())
469        self.assertEqual(foo().__code__.co_freevars, ())
470
471    def test_annotations_forbidden(self):
472        with self.assertRaises(SyntaxError):
473            self._exec_future("test: (yield)")
474
475        with self.assertRaises(SyntaxError):
476            self._exec_future("test.test: (yield a + b)")
477
478        with self.assertRaises(SyntaxError):
479            self._exec_future("test[something]: (yield from x)")
480
481        with self.assertRaises(SyntaxError):
482            self._exec_future("def func(test: (yield from outside_of_generator)): pass")
483
484        with self.assertRaises(SyntaxError):
485            self._exec_future("def test() -> (await y): pass")
486
487        with self.assertRaises(SyntaxError):
488            self._exec_future("async def test() -> something((a := b)): pass")
489
490        with self.assertRaises(SyntaxError):
491            self._exec_future("test: await some.complicated[0].call(with_args=True or 1 is not 1)")
492
493        with self.assertRaises(SyntaxError):
494            self._exec_future("test: f'{(x := 10):=10}'")
495
496        with self.assertRaises(SyntaxError):
497            self._exec_future(dedent("""\
498            def foo():
499                def bar(arg: (yield)): pass
500            """))
501
502    def test_get_type_hints_on_func_with_variadic_arg(self):
503        # `typing.get_type_hints` might break on a function with a variadic
504        # annotation (e.g. `f(*args: *Ts)`) if `from __future__ import
505        # annotations`, because it could try to evaluate `*Ts` as an expression,
506        # which on its own isn't value syntax.
507        namespace = self._exec_future(dedent("""\
508        class StarredC: pass
509        class C:
510          def __iter__(self):
511            yield StarredC()
512        c = C()
513        def f(*args: *c): pass
514        import typing
515        hints = typing.get_type_hints(f)
516        """))
517
518        hints = namespace.pop('hints')
519        self.assertIsInstance(hints['args'], namespace['StarredC'])
520
521
522if __name__ == "__main__":
523    unittest.main()
524