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