##// END OF EJS Templates
Refactor and tweak async-in-function tests...
Paul Ganssle -
Show More
@@ -1,234 +1,252 b''
1 """
1 """
2 Test for async helpers.
2 Test for async helpers.
3
3
4 Should only trigger on python 3.5+ or will have syntax errors.
4 Should only trigger on python 3.5+ or will have syntax errors.
5 """
5 """
6
6
7 import sys
7 import sys
8 from itertools import chain, repeat
8 import nose.tools as nt
9 import nose.tools as nt
9 from textwrap import dedent, indent
10 from textwrap import dedent, indent
10 from unittest import TestCase
11 from unittest import TestCase
11
12
12 ip = get_ipython()
13 ip = get_ipython()
13 iprc = lambda x: ip.run_cell(dedent(x)).raise_error()
14 iprc = lambda x: ip.run_cell(dedent(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
17
18
18 class AsyncTest(TestCase):
19 class AsyncTest(TestCase):
19 def test_should_be_async(self):
20 def test_should_be_async(self):
20 nt.assert_false(_should_be_async("False"))
21 nt.assert_false(_should_be_async("False"))
21 nt.assert_true(_should_be_async("await bar()"))
22 nt.assert_true(_should_be_async("await bar()"))
22 nt.assert_true(_should_be_async("x = await bar()"))
23 nt.assert_true(_should_be_async("x = await bar()"))
23 nt.assert_false(
24 nt.assert_false(
24 _should_be_async(
25 _should_be_async(
25 dedent(
26 dedent(
26 """
27 """
27 async def awaitable():
28 async def awaitable():
28 pass
29 pass
29 """
30 """
30 )
31 )
31 )
32 )
32 )
33 )
33
34
34 def _get_top_level_cases(self):
35 def _get_top_level_cases(self):
35 # These are test cases that should be valid in a function
36 # These are test cases that should be valid in a function
36 # but invalid outside of a function.
37 # but invalid outside of a function.
37 test_cases = []
38 test_cases = []
38 test_cases.append(('basic', "{val}"))
39 test_cases.append(('basic', "{val}"))
39
40
40 # Note, in all conditional cases, I use True instead of
41 # Note, in all conditional cases, I use True instead of
41 # False so that the peephole optimizer won't optimize away
42 # False so that the peephole optimizer won't optimize away
42 # the return, so CPython will see this as a syntax error:
43 # the return, so CPython will see this as a syntax error:
43 #
44 #
44 # while True:
45 # while True:
45 # break
46 # break
46 # return
47 # return
47 #
48 #
48 # But not this:
49 # But not this:
49 #
50 #
50 # while False:
51 # while False:
51 # return
52 # return
52 #
53 #
53 # See https://bugs.python.org/issue1875
54 # See https://bugs.python.org/issue1875
54
55
55 test_cases.append(('if', dedent("""
56 test_cases.append(('if', dedent("""
56 if True:
57 if True:
57 {val}
58 {val}
58 """)))
59 """)))
59
60
60 test_cases.append(('while', dedent("""
61 test_cases.append(('while', dedent("""
61 while True:
62 while True:
62 {val}
63 {val}
63 break
64 break
64 """)))
65 """)))
65
66
66 test_cases.append(('try', dedent("""
67 test_cases.append(('try', dedent("""
67 try:
68 try:
68 {val}
69 {val}
69 except:
70 except:
70 pass
71 pass
71 """)))
72 """)))
72
73
73 test_cases.append(('except', dedent("""
74 test_cases.append(('except', dedent("""
74 try:
75 try:
75 pass
76 pass
76 except:
77 except:
77 {val}
78 {val}
78 """)))
79 """)))
79
80
80 test_cases.append(('finally', dedent("""
81 test_cases.append(('finally', dedent("""
81 try:
82 try:
82 pass
83 pass
83 except:
84 except:
84 pass
85 pass
85 finally:
86 finally:
86 {val}
87 {val}
87 """)))
88 """)))
88
89
89 test_cases.append(('for', dedent("""
90 test_cases.append(('for', dedent("""
90 for _ in range(4):
91 for _ in range(4):
91 {val}
92 {val}
92 """)))
93 """)))
93
94
94
95
95 test_cases.append(('nested', dedent("""
96 test_cases.append(('nested', dedent("""
96 if True:
97 if True:
97 while True:
98 while True:
98 {val}
99 {val}
99 break
100 break
100 """)))
101 """)))
101
102
102 test_cases.append(('deep-nested', dedent("""
103 test_cases.append(('deep-nested', dedent("""
103 if True:
104 if True:
104 while True:
105 while True:
105 break
106 break
106 for x in range(3):
107 for x in range(3):
107 if True:
108 if True:
108 while True:
109 while True:
109 for x in range(3):
110 for x in range(3):
110 {val}
111 {val}
111 """)))
112 """)))
112
113
113 return test_cases
114 return test_cases
114
115
115 def _get_ry_syntax_errors(self):
116 def _get_ry_syntax_errors(self):
116 # This is a mix of tests that should be a syntax error if
117 # This is a mix of tests that should be a syntax error if
117 # return or yield whether or not they are in a function
118 # return or yield whether or not they are in a function
118
119
119 test_cases = []
120 test_cases = []
120
121
121 test_cases.append(('class', dedent("""
122 test_cases.append(('class', dedent("""
122 class V:
123 class V:
123 {val}
124 {val}
124 """)))
125 """)))
125
126
126 test_cases.append(('nested-class', dedent("""
127 test_cases.append(('nested-class', dedent("""
127 class V:
128 class V:
128 class C:
129 class C:
129 {val}
130 {val}
130 """)))
131 """)))
131
132
132 return test_cases
133 return test_cases
133
134
134
135
135 def test_top_level_return_error(self):
136 def test_top_level_return_error(self):
136 tl_err_test_cases = self._get_top_level_cases()
137 tl_err_test_cases = self._get_top_level_cases()
137 tl_err_test_cases.extend(self._get_ry_syntax_errors())
138 tl_err_test_cases.extend(self._get_ry_syntax_errors())
138
139
139 vals = ('return', 'yield', 'yield from (_ for _ in range(3))')
140 vals = ('return', 'yield', 'yield from (_ for _ in range(3))')
140
141
141 for test_name, test_case in tl_err_test_cases:
142 for test_name, test_case in tl_err_test_cases:
142 # This example should work if 'pass' is used as the value
143 # This example should work if 'pass' is used as the value
143 with self.subTest((test_name, 'pass')):
144 with self.subTest((test_name, 'pass')):
144 iprc(test_case.format(val='pass'))
145 iprc(test_case.format(val='pass'))
145
146
146 # It should fail with all the values
147 # It should fail with all the values
147 for val in vals:
148 for val in vals:
148 with self.subTest((test_name, val)):
149 with self.subTest((test_name, val)):
149 msg = "Syntax error not raised for %s, %s" % (test_name, val)
150 msg = "Syntax error not raised for %s, %s" % (test_name, val)
150 with self.assertRaises(SyntaxError, msg=msg):
151 with self.assertRaises(SyntaxError, msg=msg):
151 iprc(test_case.format(val=val))
152 iprc(test_case.format(val=val))
152
153
153 def test_in_func_no_error(self):
154 def test_in_func_no_error(self):
154 # Test that the implementation of top-level return/yield
155 # Test that the implementation of top-level return/yield
155 # detection isn't *too* aggressive, and works inside a function
156 # detection isn't *too* aggressive, and works inside a function
156 func_contexts = []
157 func_contexts = []
157
158
158 func_contexts.append(('func', dedent("""
159 func_contexts.append(('func', False, dedent("""
159 def f():""")))
160 def f():""")))
160
161
161 func_contexts.append(('method', dedent("""
162 func_contexts.append(('method', False, dedent("""
162 class MyClass:
163 class MyClass:
163 def __init__(self):
164 def __init__(self):
164 """)))
165 """)))
165
166
166 func_contexts.append(('async-func', dedent("""
167 func_contexts.append(('async-func', True, dedent("""
167 async def f():""")))
168 async def f():""")))
168
169
169 func_contexts.append(('closure', dedent("""
170 func_contexts.append(('async-method', True, dedent("""
171 class MyClass:
172 async def f(self):""")))
173
174 func_contexts.append(('closure', False, dedent("""
170 def f():
175 def f():
171 def g():
176 def g():
172 """)))
177 """)))
173
178
174 def nest_case(context, case):
179 def nest_case(context, case):
175 # Detect indentation
180 # Detect indentation
176 lines = context.strip().splitlines()
181 lines = context.strip().splitlines()
177 prefix_len = 0
182 prefix_len = 0
178 for c in lines[-1]:
183 for c in lines[-1]:
179 if c != ' ':
184 if c != ' ':
180 break
185 break
181 prefix_len += 1
186 prefix_len += 1
182
187
183 indented_case = indent(case, ' ' * (prefix_len + 4))
188 indented_case = indent(case, ' ' * (prefix_len + 4))
184 return context + '\n' + indented_case
189 return context + '\n' + indented_case
185
190
186 # Gather and run the tests
191 # Gather and run the tests
187 vals = ('return', 'yield')
188
192
189 success_tests = self._get_top_level_cases()
193 # yield is allowed in async functions, starting in Python 3.6,
190 failure_tests = self._get_ry_syntax_errors()
194 # and yield from is not allowed in any version
195 vals = ('return', 'yield', 'yield from (_ for _ in range(3))')
196 async_safe = (True,
197 sys.version_info >= (3, 6),
198 False)
199 vals = tuple(zip(vals, async_safe))
191
200
192 for context_name, context in func_contexts:
201 success_tests = zip(self._get_top_level_cases(), repeat(False))
193 # These tests should now successfully run
202 failure_tests = zip(self._get_ry_syntax_errors(), repeat(True))
194 for test_name, test_case in success_tests:
195 nested_case = nest_case(context, test_case)
196
203
197 for val in vals:
204 tests = chain(success_tests, failure_tests)
198 with self.subTest((test_name, context_name, val)):
199 iprc(nested_case.format(val=val))
200
205
201 # These tests should still raise a SyntaxError
206 for context_name, async_func, context in func_contexts:
202 for test_name, test_case in failure_tests:
207 for (test_name, test_case), should_fail in tests:
203 nested_case = nest_case(context, test_case)
208 nested_case = nest_case(context, test_case)
204
209
205 for val in vals:
210 for val, async_safe in vals:
206 with self.subTest((test_name, context_name, val)):
211 val_should_fail = (should_fail or
207 with self.assertRaises(SyntaxError):
212 (async_func and not async_safe))
208 iprc(nested_case.format(val=val))
213
214 test_id = (context_name, test_name, val)
215 cell = nested_case.format(val=val)
216
217 with self.subTest(test_id):
218 if val_should_fail:
219 msg = ("SyntaxError not raised for %s" %
220 str(test_id))
221 with self.assertRaises(SyntaxError, msg=msg):
222 iprc(cell)
223
224 print(cell)
225 else:
226 iprc(cell)
209
227
210
228
211 def test_execute(self):
229 def test_execute(self):
212 iprc("""
230 iprc("""
213 import asyncio
231 import asyncio
214 await asyncio.sleep(0.001)
232 await asyncio.sleep(0.001)
215 """
233 """
216 )
234 )
217
235
218 def test_autoawait(self):
236 def test_autoawait(self):
219 iprc("%autoawait False")
237 iprc("%autoawait False")
220 iprc("%autoawait True")
238 iprc("%autoawait True")
221 iprc("""
239 iprc("""
222 from asyncio import sleep
240 from asyncio import sleep
223 await sleep(0.1)
241 await sleep(0.1)
224 """
242 """
225 )
243 )
226
244
227 def test_autoawait_curio(self):
245 def test_autoawait_curio(self):
228 iprc("%autoawait curio")
246 iprc("%autoawait curio")
229
247
230 def test_autoawait_trio(self):
248 def test_autoawait_trio(self):
231 iprc("%autoawait trio")
249 iprc("%autoawait trio")
232
250
233 def tearDown(self):
251 def tearDown(self):
234 ip.loop_runner = "asyncio"
252 ip.loop_runner = "asyncio"
General Comments 0
You need to be logged in to leave comments. Login now