##// END OF EJS Templates
Add tests for SyntaxError with top-level return
Paul Ganssle -
Show More
@@ -6,11 +6,12 Should only trigger on python 3.5+ or will have syntax errors.
6
6
7 import sys
7 import sys
8 import nose.tools as nt
8 import nose.tools as nt
9 from textwrap import dedent
9 from textwrap import dedent, indent
10 from unittest import TestCase
10 from unittest import TestCase
11
11
12 ip = get_ipython()
12 ip = get_ipython()
13 iprc = lambda x: ip.run_cell(dedent(x))
13 iprc = lambda x: ip.run_cell(dedent(x))
14 iprc_err = lambda x: iprc(x).raise_error()
14
15
15 if sys.version_info > (3, 5):
16 if sys.version_info > (3, 5):
16 from IPython.core.async_helpers import _should_be_async
17 from IPython.core.async_helpers import _should_be_async
@@ -31,6 +32,183 if sys.version_info > (3, 5):
31 )
32 )
32 )
33 )
33
34
35 def _get_top_level_cases(self):
36 # These are test cases that should be valid in a function
37 # but invalid outside of a function.
38 test_cases = []
39 test_cases.append(('basic', "{val}"))
40
41 # Note, in all conditional cases, I use True instead of
42 # False so that the peephole optimizer won't optimize away
43 # the return, so CPython will see this as a syntax error:
44 #
45 # while True:
46 # break
47 # return
48 #
49 # But not this:
50 #
51 # while False:
52 # return
53 #
54 # See https://bugs.python.org/issue1875
55
56 test_cases.append(('if', dedent("""
57 if True:
58 {val}
59 """)))
60
61 test_cases.append(('while', dedent("""
62 while True:
63 {val}
64 break
65 """)))
66
67 test_cases.append(('try', dedent("""
68 try:
69 {val}
70 except:
71 pass
72 """)))
73
74 test_cases.append(('except', dedent("""
75 try:
76 pass
77 except:
78 {val}
79 """)))
80
81 test_cases.append(('finally', dedent("""
82 try:
83 pass
84 except:
85 pass
86 finally:
87 {val}
88 """)))
89
90 test_cases.append(('for', dedent("""
91 for _ in range(4):
92 {val}
93 """)))
94
95
96 test_cases.append(('nested', dedent("""
97 if True:
98 while True:
99 {val}
100 break
101 """)))
102
103 test_cases.append(('deep-nested', dedent("""
104 if True:
105 while True:
106 break
107 for x in range(3):
108 if True:
109 while True:
110 for x in range(3):
111 {val}
112 """)))
113
114 return test_cases
115
116 def _get_ry_syntax_errors(self):
117 # This is a mix of tests that should be a syntax error if
118 # return or yield whether or not they are in a function
119
120 test_cases = []
121
122 test_cases.append(('class', dedent("""
123 class V:
124 {val}
125 """)))
126
127 test_cases.append(('nested-class', dedent("""
128 class V:
129 class C:
130 {val}
131 """)))
132
133 return test_cases
134
135
136 def test_top_level_return_error(self):
137 tl_err_test_cases = self._get_top_level_cases()
138 tl_err_test_cases.extend(self._get_ry_syntax_errors())
139
140 vals = ('return', 'yield', 'yield from (_ for _ in range(3))')
141
142 for test_name, test_case in tl_err_test_cases:
143 # This example should work if 'pass' is used as the value
144 with self.subTest((test_name, 'pass')):
145 iprc_err(test_case.format(val='pass'))
146
147 # It should fail with all the values
148 for val in vals:
149 with self.subTest((test_name, val)):
150 msg = "Syntax error not raised for %s, %s" % (test_name, val)
151 with self.assertRaises(SyntaxError, msg=msg):
152 iprc_err(test_case.format(val=val))
153
154 def test_in_func_no_error(self):
155 # Test that the implementation of top-level return/yield
156 # detection isn't *too* aggressive, and works inside a function
157 func_contexts = []
158
159 func_contexts.append(('func', dedent("""
160 def f():""")))
161
162 func_contexts.append(('method', dedent("""
163 class MyClass:
164 def __init__(self):
165 """)))
166
167 func_contexts.append(('async-func', dedent("""
168 async def f():""")))
169
170 func_contexts.append(('closure', dedent("""
171 def f():
172 def g():
173 """)))
174
175 def nest_case(context, case):
176 # Detect indentation
177 lines = context.strip().splitlines()
178 prefix_len = 0
179 for c in lines[-1]:
180 if c != ' ':
181 break
182 prefix_len += 1
183
184 indented_case = indent(case, ' ' * (prefix_len + 4))
185 return context + '\n' + indented_case
186
187 # Gather and run the tests
188 vals = ('return', 'yield')
189
190 success_tests = self._get_top_level_cases()
191 failure_tests = self._get_ry_syntax_errors()
192
193 for context_name, context in func_contexts:
194 # These tests should now successfully run
195 for test_name, test_case in success_tests:
196 nested_case = nest_case(context, test_case)
197
198 for val in vals:
199 with self.subTest((test_name, context_name, val)):
200 iprc_err(nested_case.format(val=val))
201
202 # These tests should still raise a SyntaxError
203 for test_name, test_case in failure_tests:
204 nested_case = nest_case(context, test_case)
205
206 for val in vals:
207 with self.subTest((test_name, context_name, val)):
208 with self.assertRaises(SyntaxError):
209 iprc_err(nested_case.format(val=val))
210
211
34 def test_execute(self):
212 def test_execute(self):
35 iprc(
213 iprc(
36 """
214 """
General Comments 0
You need to be logged in to leave comments. Login now