10
10
import re
11
11
import sys
12
12
13
+ TOP_LEVEL_MSG = 'from __future__ imports must occur at the beginning of the file'
14
+
13
15
rx = re .compile (r'\((\S+).py, line (\d+)' )
14
16
15
17
def get_error_location (msg ):
@@ -18,21 +20,48 @@ def get_error_location(msg):
18
20
19
21
class FutureTest (unittest .TestCase ):
20
22
21
- def check_syntax_error (self , err , basename , lineno , offset = 1 ):
22
- self .assertIn ('%s.py, line %d' % (basename , lineno ), str (err ))
23
- self .assertEqual (os .path .basename (err .filename ), basename + '.py' )
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 )
24
32
self .assertEqual (err .lineno , lineno )
25
33
self .assertEqual (err .offset , offset )
26
34
27
- def test_future1 (self ):
28
- with import_helper .CleanImport ('test.test_future_stmt.future_test1' ):
29
- from test .test_future_stmt import future_test1
30
- self .assertEqual (future_test1 .result , 6 )
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 )
31
60
32
- def test_future2 (self ):
33
- with import_helper .CleanImport ('test.test_future_stmt.future_test2 ' ):
34
- from test .test_future_stmt import future_test2
35
- self .assertEqual (future_test2 .result , 6 )
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 )
36
65
37
66
def test_future_single_import (self ):
38
67
with import_helper .CleanImport (
@@ -52,45 +81,80 @@ def test_future_multiple_features(self):
52
81
):
53
82
from test .test_future_stmt import test_future_multiple_features
54
83
55
- def test_badfuture3 (self ):
56
- with self .assertRaises (SyntaxError ) as cm :
57
- from test .test_future_stmt import badsyntax_future3
58
- self .check_syntax_error (cm .exception , "badsyntax_future3" , 3 )
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' ,
92
+ )
59
93
60
- def test_badfuture4 (self ):
61
- with self .assertRaises (SyntaxError ) as cm :
62
- from test .test_future_stmt import badsyntax_future4
63
- self .check_syntax_error (cm .exception , "badsyntax_future4" , 3 )
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 )
64
100
65
- def test_badfuture5 (self ):
66
- with self .assertRaises (SyntaxError ) as cm :
67
- from test .test_future_stmt import badsyntax_future5
68
- self .check_syntax_error (cm .exception , "badsyntax_future5" , 4 )
101
+ code = """
102
+ import __future__
103
+ from __future__ import annotations
104
+ """
105
+ self .assertSyntaxError (code , lineno = 2 )
69
106
70
- def test_badfuture6 (self ):
71
- with self .assertRaises (SyntaxError ) as cm :
72
- from test .test_future_stmt import badsyntax_future6
73
- self .check_syntax_error (cm .exception , "badsyntax_future6" , 3 )
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 )
74
113
75
- def test_badfuture7 (self ):
76
- with self .assertRaises (SyntaxError ) as cm :
77
- from test .test_future_stmt import badsyntax_future7
78
- self .check_syntax_error (cm .exception , "badsyntax_future7" , 3 , 54 )
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 )
79
121
80
- def test_badfuture8 (self ):
81
- with self .assertRaises (SyntaxError ) as cm :
82
- from test .test_future_stmt import badsyntax_future8
83
- self .check_syntax_error (cm .exception , "badsyntax_future8" , 3 )
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 )
84
129
85
- def test_badfuture9 (self ):
86
- with self .assertRaises (SyntaxError ) as cm :
87
- from test .test_future_stmt import badsyntax_future9
88
- self .check_syntax_error (cm .exception , "badsyntax_future9" , 3 )
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' )
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' )
148
+
149
+ code = """
150
+ from __future__ import nested_scopes, braces
151
+ """
152
+ self .assertSyntaxError (code , message = 'not a chance' )
89
153
90
- def test_badfuture10 (self ):
154
+ def test_module_with_future_import_not_on_top (self ):
91
155
with self .assertRaises (SyntaxError ) as cm :
92
- from test .test_future_stmt import badsyntax_future10
93
- self .check_syntax_error (cm .exception , "badsyntax_future10 " , 3 )
156
+ from test .test_future_stmt import badsyntax_future
157
+ self .check_syntax_error (cm .exception , "badsyntax_future " , lineno = 3 )
94
158
95
159
def test_ensure_flags_dont_clash (self ):
96
160
# bpo-39562: test that future flags and compiler flags doesn't clash
0 commit comments