##// END OF EJS Templates
Add Xfail test on 3.9.8...
Matthias Bussonnier -
Show More
@@ -1,638 +1,640 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the inputsplitter module."""
2 """Tests for the inputsplitter module."""
3
3
4
4
5 # Copyright (c) IPython Development Team.
5 # Copyright (c) IPython Development Team.
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7
7
8 import unittest
8 import unittest
9 import sys
9 import sys
10
10
11 from IPython.core import inputsplitter as isp
11 from IPython.core import inputsplitter as isp
12 from IPython.core.inputtransformer import InputTransformer
12 from IPython.core.inputtransformer import InputTransformer
13 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
13 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
14 from IPython.testing import tools as tt
14 from IPython.testing import tools as tt
15 from IPython.testing.decorators import skipif
15
16
16 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
17 # Semi-complete examples (also used as tests)
18 # Semi-complete examples (also used as tests)
18 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
19
20
20 # Note: at the bottom, there's a slightly more complete version of this that
21 # Note: at the bottom, there's a slightly more complete version of this that
21 # can be useful during development of code here.
22 # can be useful during development of code here.
22
23
23 def mini_interactive_loop(input_func):
24 def mini_interactive_loop(input_func):
24 """Minimal example of the logic of an interactive interpreter loop.
25 """Minimal example of the logic of an interactive interpreter loop.
25
26
26 This serves as an example, and it is used by the test system with a fake
27 This serves as an example, and it is used by the test system with a fake
27 raw_input that simulates interactive input."""
28 raw_input that simulates interactive input."""
28
29
29 from IPython.core.inputsplitter import InputSplitter
30 from IPython.core.inputsplitter import InputSplitter
30
31
31 isp = InputSplitter()
32 isp = InputSplitter()
32 # In practice, this input loop would be wrapped in an outside loop to read
33 # In practice, this input loop would be wrapped in an outside loop to read
33 # input indefinitely, until some exit/quit command was issued. Here we
34 # input indefinitely, until some exit/quit command was issued. Here we
34 # only illustrate the basic inner loop.
35 # only illustrate the basic inner loop.
35 while isp.push_accepts_more():
36 while isp.push_accepts_more():
36 indent = ' '*isp.get_indent_spaces()
37 indent = ' '*isp.get_indent_spaces()
37 prompt = '>>> ' + indent
38 prompt = '>>> ' + indent
38 line = indent + input_func(prompt)
39 line = indent + input_func(prompt)
39 isp.push(line)
40 isp.push(line)
40
41
41 # Here we just return input so we can use it in a test suite, but a real
42 # Here we just return input so we can use it in a test suite, but a real
42 # interpreter would instead send it for execution somewhere.
43 # interpreter would instead send it for execution somewhere.
43 src = isp.source_reset()
44 src = isp.source_reset()
44 #print 'Input source was:\n', src # dbg
45 #print 'Input source was:\n', src # dbg
45 return src
46 return src
46
47
47 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
48 # Test utilities, just for local use
49 # Test utilities, just for local use
49 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
50
51
51 def assemble(block):
52 def assemble(block):
52 """Assemble a block into multi-line sub-blocks."""
53 """Assemble a block into multi-line sub-blocks."""
53 return ['\n'.join(sub_block)+'\n' for sub_block in block]
54 return ['\n'.join(sub_block)+'\n' for sub_block in block]
54
55
55
56
56 def pseudo_input(lines):
57 def pseudo_input(lines):
57 """Return a function that acts like raw_input but feeds the input list."""
58 """Return a function that acts like raw_input but feeds the input list."""
58 ilines = iter(lines)
59 ilines = iter(lines)
59 def raw_in(prompt):
60 def raw_in(prompt):
60 try:
61 try:
61 return next(ilines)
62 return next(ilines)
62 except StopIteration:
63 except StopIteration:
63 return ''
64 return ''
64 return raw_in
65 return raw_in
65
66
66 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
67 # Tests
68 # Tests
68 #-----------------------------------------------------------------------------
69 #-----------------------------------------------------------------------------
69 def test_spaces():
70 def test_spaces():
70 tests = [('', 0),
71 tests = [('', 0),
71 (' ', 1),
72 (' ', 1),
72 ('\n', 0),
73 ('\n', 0),
73 (' \n', 1),
74 (' \n', 1),
74 ('x', 0),
75 ('x', 0),
75 (' x', 1),
76 (' x', 1),
76 (' x',2),
77 (' x',2),
77 (' x',4),
78 (' x',4),
78 # Note: tabs are counted as a single whitespace!
79 # Note: tabs are counted as a single whitespace!
79 ('\tx', 1),
80 ('\tx', 1),
80 ('\t x', 2),
81 ('\t x', 2),
81 ]
82 ]
82 tt.check_pairs(isp.num_ini_spaces, tests)
83 tt.check_pairs(isp.num_ini_spaces, tests)
83
84
84
85
85 def test_remove_comments():
86 def test_remove_comments():
86 tests = [('text', 'text'),
87 tests = [('text', 'text'),
87 ('text # comment', 'text '),
88 ('text # comment', 'text '),
88 ('text # comment\n', 'text \n'),
89 ('text # comment\n', 'text \n'),
89 ('text # comment \n', 'text \n'),
90 ('text # comment \n', 'text \n'),
90 ('line # c \nline\n','line \nline\n'),
91 ('line # c \nline\n','line \nline\n'),
91 ('line # c \nline#c2 \nline\nline #c\n\n',
92 ('line # c \nline#c2 \nline\nline #c\n\n',
92 'line \nline\nline\nline \n\n'),
93 'line \nline\nline\nline \n\n'),
93 ]
94 ]
94 tt.check_pairs(isp.remove_comments, tests)
95 tt.check_pairs(isp.remove_comments, tests)
95
96
96
97
97 def test_get_input_encoding():
98 def test_get_input_encoding():
98 encoding = isp.get_input_encoding()
99 encoding = isp.get_input_encoding()
99 assert isinstance(encoding, str)
100 assert isinstance(encoding, str)
100 # simple-minded check that at least encoding a simple string works with the
101 # simple-minded check that at least encoding a simple string works with the
101 # encoding we got.
102 # encoding we got.
102 assert "test".encode(encoding) == b"test"
103 assert "test".encode(encoding) == b"test"
103
104
104
105
105 class NoInputEncodingTestCase(unittest.TestCase):
106 class NoInputEncodingTestCase(unittest.TestCase):
106 def setUp(self):
107 def setUp(self):
107 self.old_stdin = sys.stdin
108 self.old_stdin = sys.stdin
108 class X: pass
109 class X: pass
109 fake_stdin = X()
110 fake_stdin = X()
110 sys.stdin = fake_stdin
111 sys.stdin = fake_stdin
111
112
112 def test(self):
113 def test(self):
113 # Verify that if sys.stdin has no 'encoding' attribute we do the right
114 # Verify that if sys.stdin has no 'encoding' attribute we do the right
114 # thing
115 # thing
115 enc = isp.get_input_encoding()
116 enc = isp.get_input_encoding()
116 self.assertEqual(enc, 'ascii')
117 self.assertEqual(enc, 'ascii')
117
118
118 def tearDown(self):
119 def tearDown(self):
119 sys.stdin = self.old_stdin
120 sys.stdin = self.old_stdin
120
121
121
122
122 class InputSplitterTestCase(unittest.TestCase):
123 class InputSplitterTestCase(unittest.TestCase):
123 def setUp(self):
124 def setUp(self):
124 self.isp = isp.InputSplitter()
125 self.isp = isp.InputSplitter()
125
126
126 def test_reset(self):
127 def test_reset(self):
127 isp = self.isp
128 isp = self.isp
128 isp.push('x=1')
129 isp.push('x=1')
129 isp.reset()
130 isp.reset()
130 self.assertEqual(isp._buffer, [])
131 self.assertEqual(isp._buffer, [])
131 self.assertEqual(isp.get_indent_spaces(), 0)
132 self.assertEqual(isp.get_indent_spaces(), 0)
132 self.assertEqual(isp.source, '')
133 self.assertEqual(isp.source, '')
133 self.assertEqual(isp.code, None)
134 self.assertEqual(isp.code, None)
134 self.assertEqual(isp._is_complete, False)
135 self.assertEqual(isp._is_complete, False)
135
136
136 def test_source(self):
137 def test_source(self):
137 self.isp._store('1')
138 self.isp._store('1')
138 self.isp._store('2')
139 self.isp._store('2')
139 self.assertEqual(self.isp.source, '1\n2\n')
140 self.assertEqual(self.isp.source, '1\n2\n')
140 self.assertEqual(len(self.isp._buffer)>0, True)
141 self.assertEqual(len(self.isp._buffer)>0, True)
141 self.assertEqual(self.isp.source_reset(), '1\n2\n')
142 self.assertEqual(self.isp.source_reset(), '1\n2\n')
142 self.assertEqual(self.isp._buffer, [])
143 self.assertEqual(self.isp._buffer, [])
143 self.assertEqual(self.isp.source, '')
144 self.assertEqual(self.isp.source, '')
144
145
145 def test_indent(self):
146 def test_indent(self):
146 isp = self.isp # shorthand
147 isp = self.isp # shorthand
147 isp.push('x=1')
148 isp.push('x=1')
148 self.assertEqual(isp.get_indent_spaces(), 0)
149 self.assertEqual(isp.get_indent_spaces(), 0)
149 isp.push('if 1:\n x=1')
150 isp.push('if 1:\n x=1')
150 self.assertEqual(isp.get_indent_spaces(), 4)
151 self.assertEqual(isp.get_indent_spaces(), 4)
151 isp.push('y=2\n')
152 isp.push('y=2\n')
152 self.assertEqual(isp.get_indent_spaces(), 0)
153 self.assertEqual(isp.get_indent_spaces(), 0)
153
154
154 def test_indent2(self):
155 def test_indent2(self):
155 isp = self.isp
156 isp = self.isp
156 isp.push('if 1:')
157 isp.push('if 1:')
157 self.assertEqual(isp.get_indent_spaces(), 4)
158 self.assertEqual(isp.get_indent_spaces(), 4)
158 isp.push(' x=1')
159 isp.push(' x=1')
159 self.assertEqual(isp.get_indent_spaces(), 4)
160 self.assertEqual(isp.get_indent_spaces(), 4)
160 # Blank lines shouldn't change the indent level
161 # Blank lines shouldn't change the indent level
161 isp.push(' '*2)
162 isp.push(' '*2)
162 self.assertEqual(isp.get_indent_spaces(), 4)
163 self.assertEqual(isp.get_indent_spaces(), 4)
163
164
164 def test_indent3(self):
165 def test_indent3(self):
165 isp = self.isp
166 isp = self.isp
166 # When a multiline statement contains parens or multiline strings, we
167 # When a multiline statement contains parens or multiline strings, we
167 # shouldn't get confused.
168 # shouldn't get confused.
168 isp.push("if 1:")
169 isp.push("if 1:")
169 isp.push(" x = (1+\n 2)")
170 isp.push(" x = (1+\n 2)")
170 self.assertEqual(isp.get_indent_spaces(), 4)
171 self.assertEqual(isp.get_indent_spaces(), 4)
171
172
172 def test_indent4(self):
173 def test_indent4(self):
173 isp = self.isp
174 isp = self.isp
174 # whitespace after ':' should not screw up indent level
175 # whitespace after ':' should not screw up indent level
175 isp.push('if 1: \n x=1')
176 isp.push('if 1: \n x=1')
176 self.assertEqual(isp.get_indent_spaces(), 4)
177 self.assertEqual(isp.get_indent_spaces(), 4)
177 isp.push('y=2\n')
178 isp.push('y=2\n')
178 self.assertEqual(isp.get_indent_spaces(), 0)
179 self.assertEqual(isp.get_indent_spaces(), 0)
179 isp.push('if 1:\t\n x=1')
180 isp.push('if 1:\t\n x=1')
180 self.assertEqual(isp.get_indent_spaces(), 4)
181 self.assertEqual(isp.get_indent_spaces(), 4)
181 isp.push('y=2\n')
182 isp.push('y=2\n')
182 self.assertEqual(isp.get_indent_spaces(), 0)
183 self.assertEqual(isp.get_indent_spaces(), 0)
183
184
184 def test_dedent_pass(self):
185 def test_dedent_pass(self):
185 isp = self.isp # shorthand
186 isp = self.isp # shorthand
186 # should NOT cause dedent
187 # should NOT cause dedent
187 isp.push('if 1:\n passes = 5')
188 isp.push('if 1:\n passes = 5')
188 self.assertEqual(isp.get_indent_spaces(), 4)
189 self.assertEqual(isp.get_indent_spaces(), 4)
189 isp.push('if 1:\n pass')
190 isp.push('if 1:\n pass')
190 self.assertEqual(isp.get_indent_spaces(), 0)
191 self.assertEqual(isp.get_indent_spaces(), 0)
191 isp.push('if 1:\n pass ')
192 isp.push('if 1:\n pass ')
192 self.assertEqual(isp.get_indent_spaces(), 0)
193 self.assertEqual(isp.get_indent_spaces(), 0)
193
194
194 def test_dedent_break(self):
195 def test_dedent_break(self):
195 isp = self.isp # shorthand
196 isp = self.isp # shorthand
196 # should NOT cause dedent
197 # should NOT cause dedent
197 isp.push('while 1:\n breaks = 5')
198 isp.push('while 1:\n breaks = 5')
198 self.assertEqual(isp.get_indent_spaces(), 4)
199 self.assertEqual(isp.get_indent_spaces(), 4)
199 isp.push('while 1:\n break')
200 isp.push('while 1:\n break')
200 self.assertEqual(isp.get_indent_spaces(), 0)
201 self.assertEqual(isp.get_indent_spaces(), 0)
201 isp.push('while 1:\n break ')
202 isp.push('while 1:\n break ')
202 self.assertEqual(isp.get_indent_spaces(), 0)
203 self.assertEqual(isp.get_indent_spaces(), 0)
203
204
204 def test_dedent_continue(self):
205 def test_dedent_continue(self):
205 isp = self.isp # shorthand
206 isp = self.isp # shorthand
206 # should NOT cause dedent
207 # should NOT cause dedent
207 isp.push('while 1:\n continues = 5')
208 isp.push('while 1:\n continues = 5')
208 self.assertEqual(isp.get_indent_spaces(), 4)
209 self.assertEqual(isp.get_indent_spaces(), 4)
209 isp.push('while 1:\n continue')
210 isp.push('while 1:\n continue')
210 self.assertEqual(isp.get_indent_spaces(), 0)
211 self.assertEqual(isp.get_indent_spaces(), 0)
211 isp.push('while 1:\n continue ')
212 isp.push('while 1:\n continue ')
212 self.assertEqual(isp.get_indent_spaces(), 0)
213 self.assertEqual(isp.get_indent_spaces(), 0)
213
214
214 def test_dedent_raise(self):
215 def test_dedent_raise(self):
215 isp = self.isp # shorthand
216 isp = self.isp # shorthand
216 # should NOT cause dedent
217 # should NOT cause dedent
217 isp.push('if 1:\n raised = 4')
218 isp.push('if 1:\n raised = 4')
218 self.assertEqual(isp.get_indent_spaces(), 4)
219 self.assertEqual(isp.get_indent_spaces(), 4)
219 isp.push('if 1:\n raise TypeError()')
220 isp.push('if 1:\n raise TypeError()')
220 self.assertEqual(isp.get_indent_spaces(), 0)
221 self.assertEqual(isp.get_indent_spaces(), 0)
221 isp.push('if 1:\n raise')
222 isp.push('if 1:\n raise')
222 self.assertEqual(isp.get_indent_spaces(), 0)
223 self.assertEqual(isp.get_indent_spaces(), 0)
223 isp.push('if 1:\n raise ')
224 isp.push('if 1:\n raise ')
224 self.assertEqual(isp.get_indent_spaces(), 0)
225 self.assertEqual(isp.get_indent_spaces(), 0)
225
226
226 def test_dedent_return(self):
227 def test_dedent_return(self):
227 isp = self.isp # shorthand
228 isp = self.isp # shorthand
228 # should NOT cause dedent
229 # should NOT cause dedent
229 isp.push('if 1:\n returning = 4')
230 isp.push('if 1:\n returning = 4')
230 self.assertEqual(isp.get_indent_spaces(), 4)
231 self.assertEqual(isp.get_indent_spaces(), 4)
231 isp.push('if 1:\n return 5 + 493')
232 isp.push('if 1:\n return 5 + 493')
232 self.assertEqual(isp.get_indent_spaces(), 0)
233 self.assertEqual(isp.get_indent_spaces(), 0)
233 isp.push('if 1:\n return')
234 isp.push('if 1:\n return')
234 self.assertEqual(isp.get_indent_spaces(), 0)
235 self.assertEqual(isp.get_indent_spaces(), 0)
235 isp.push('if 1:\n return ')
236 isp.push('if 1:\n return ')
236 self.assertEqual(isp.get_indent_spaces(), 0)
237 self.assertEqual(isp.get_indent_spaces(), 0)
237 isp.push('if 1:\n return(0)')
238 isp.push('if 1:\n return(0)')
238 self.assertEqual(isp.get_indent_spaces(), 0)
239 self.assertEqual(isp.get_indent_spaces(), 0)
239
240
240 def test_push(self):
241 def test_push(self):
241 isp = self.isp
242 isp = self.isp
242 self.assertEqual(isp.push('x=1'), True)
243 self.assertEqual(isp.push('x=1'), True)
243
244
244 def test_push2(self):
245 def test_push2(self):
245 isp = self.isp
246 isp = self.isp
246 self.assertEqual(isp.push('if 1:'), False)
247 self.assertEqual(isp.push('if 1:'), False)
247 for line in [' x=1', '# a comment', ' y=2']:
248 for line in [' x=1', '# a comment', ' y=2']:
248 print(line)
249 print(line)
249 self.assertEqual(isp.push(line), True)
250 self.assertEqual(isp.push(line), True)
250
251
251 def test_push3(self):
252 def test_push3(self):
252 isp = self.isp
253 isp = self.isp
253 isp.push('if True:')
254 isp.push('if True:')
254 isp.push(' a = 1')
255 isp.push(' a = 1')
255 self.assertEqual(isp.push('b = [1,'), False)
256 self.assertEqual(isp.push('b = [1,'), False)
256
257
257 def test_push_accepts_more(self):
258 def test_push_accepts_more(self):
258 isp = self.isp
259 isp = self.isp
259 isp.push('x=1')
260 isp.push('x=1')
260 self.assertEqual(isp.push_accepts_more(), False)
261 self.assertEqual(isp.push_accepts_more(), False)
261
262
262 def test_push_accepts_more2(self):
263 def test_push_accepts_more2(self):
263 isp = self.isp
264 isp = self.isp
264 isp.push('if 1:')
265 isp.push('if 1:')
265 self.assertEqual(isp.push_accepts_more(), True)
266 self.assertEqual(isp.push_accepts_more(), True)
266 isp.push(' x=1')
267 isp.push(' x=1')
267 self.assertEqual(isp.push_accepts_more(), True)
268 self.assertEqual(isp.push_accepts_more(), True)
268 isp.push('')
269 isp.push('')
269 self.assertEqual(isp.push_accepts_more(), False)
270 self.assertEqual(isp.push_accepts_more(), False)
270
271
271 def test_push_accepts_more3(self):
272 def test_push_accepts_more3(self):
272 isp = self.isp
273 isp = self.isp
273 isp.push("x = (2+\n3)")
274 isp.push("x = (2+\n3)")
274 self.assertEqual(isp.push_accepts_more(), False)
275 self.assertEqual(isp.push_accepts_more(), False)
275
276
276 def test_push_accepts_more4(self):
277 def test_push_accepts_more4(self):
277 isp = self.isp
278 isp = self.isp
278 # When a multiline statement contains parens or multiline strings, we
279 # When a multiline statement contains parens or multiline strings, we
279 # shouldn't get confused.
280 # shouldn't get confused.
280 # FIXME: we should be able to better handle de-dents in statements like
281 # FIXME: we should be able to better handle de-dents in statements like
281 # multiline strings and multiline expressions (continued with \ or
282 # multiline strings and multiline expressions (continued with \ or
282 # parens). Right now we aren't handling the indentation tracking quite
283 # parens). Right now we aren't handling the indentation tracking quite
283 # correctly with this, though in practice it may not be too much of a
284 # correctly with this, though in practice it may not be too much of a
284 # problem. We'll need to see.
285 # problem. We'll need to see.
285 isp.push("if 1:")
286 isp.push("if 1:")
286 isp.push(" x = (2+")
287 isp.push(" x = (2+")
287 isp.push(" 3)")
288 isp.push(" 3)")
288 self.assertEqual(isp.push_accepts_more(), True)
289 self.assertEqual(isp.push_accepts_more(), True)
289 isp.push(" y = 3")
290 isp.push(" y = 3")
290 self.assertEqual(isp.push_accepts_more(), True)
291 self.assertEqual(isp.push_accepts_more(), True)
291 isp.push('')
292 isp.push('')
292 self.assertEqual(isp.push_accepts_more(), False)
293 self.assertEqual(isp.push_accepts_more(), False)
293
294
294 def test_push_accepts_more5(self):
295 def test_push_accepts_more5(self):
295 isp = self.isp
296 isp = self.isp
296 isp.push('try:')
297 isp.push('try:')
297 isp.push(' a = 5')
298 isp.push(' a = 5')
298 isp.push('except:')
299 isp.push('except:')
299 isp.push(' raise')
300 isp.push(' raise')
300 # We want to be able to add an else: block at this point, so it should
301 # We want to be able to add an else: block at this point, so it should
301 # wait for a blank line.
302 # wait for a blank line.
302 self.assertEqual(isp.push_accepts_more(), True)
303 self.assertEqual(isp.push_accepts_more(), True)
303
304
304 def test_continuation(self):
305 def test_continuation(self):
305 isp = self.isp
306 isp = self.isp
306 isp.push("import os, \\")
307 isp.push("import os, \\")
307 self.assertEqual(isp.push_accepts_more(), True)
308 self.assertEqual(isp.push_accepts_more(), True)
308 isp.push("sys")
309 isp.push("sys")
309 self.assertEqual(isp.push_accepts_more(), False)
310 self.assertEqual(isp.push_accepts_more(), False)
310
311
311 def test_syntax_error(self):
312 def test_syntax_error(self):
312 isp = self.isp
313 isp = self.isp
313 # Syntax errors immediately produce a 'ready' block, so the invalid
314 # Syntax errors immediately produce a 'ready' block, so the invalid
314 # Python can be sent to the kernel for evaluation with possible ipython
315 # Python can be sent to the kernel for evaluation with possible ipython
315 # special-syntax conversion.
316 # special-syntax conversion.
316 isp.push('run foo')
317 isp.push('run foo')
317 self.assertEqual(isp.push_accepts_more(), False)
318 self.assertEqual(isp.push_accepts_more(), False)
318
319
319 def test_unicode(self):
320 def test_unicode(self):
320 self.isp.push(u"Pérez")
321 self.isp.push(u"Pérez")
321 self.isp.push(u'\xc3\xa9')
322 self.isp.push(u'\xc3\xa9')
322 self.isp.push(u"u'\xc3\xa9'")
323 self.isp.push(u"u'\xc3\xa9'")
323
324
325 @skipif(sys.version_info[:3] == (3, 9, 8))
324 def test_line_continuation(self):
326 def test_line_continuation(self):
325 """ Test issue #2108."""
327 """ Test issue #2108."""
326 isp = self.isp
328 isp = self.isp
327 # A blank line after a line continuation should not accept more
329 # A blank line after a line continuation should not accept more
328 isp.push("1 \\\n\n")
330 isp.push("1 \\\n\n")
329 self.assertEqual(isp.push_accepts_more(), False)
331 self.assertEqual(isp.push_accepts_more(), False)
330 # Whitespace after a \ is a SyntaxError. The only way to test that
332 # Whitespace after a \ is a SyntaxError. The only way to test that
331 # here is to test that push doesn't accept more (as with
333 # here is to test that push doesn't accept more (as with
332 # test_syntax_error() above).
334 # test_syntax_error() above).
333 isp.push(r"1 \ ")
335 isp.push(r"1 \ ")
334 self.assertEqual(isp.push_accepts_more(), False)
336 self.assertEqual(isp.push_accepts_more(), False)
335 # Even if the line is continuable (c.f. the regular Python
337 # Even if the line is continuable (c.f. the regular Python
336 # interpreter)
338 # interpreter)
337 isp.push(r"(1 \ ")
339 isp.push(r"(1 \ ")
338 self.assertEqual(isp.push_accepts_more(), False)
340 self.assertEqual(isp.push_accepts_more(), False)
339
341
340 def test_check_complete(self):
342 def test_check_complete(self):
341 isp = self.isp
343 isp = self.isp
342 self.assertEqual(isp.check_complete("a = 1"), ('complete', None))
344 self.assertEqual(isp.check_complete("a = 1"), ('complete', None))
343 self.assertEqual(isp.check_complete("for a in range(5):"), ('incomplete', 4))
345 self.assertEqual(isp.check_complete("for a in range(5):"), ('incomplete', 4))
344 self.assertEqual(isp.check_complete("raise = 2"), ('invalid', None))
346 self.assertEqual(isp.check_complete("raise = 2"), ('invalid', None))
345 self.assertEqual(isp.check_complete("a = [1,\n2,"), ('incomplete', 0))
347 self.assertEqual(isp.check_complete("a = [1,\n2,"), ('incomplete', 0))
346 self.assertEqual(isp.check_complete("def a():\n x=1\n global x"), ('invalid', None))
348 self.assertEqual(isp.check_complete("def a():\n x=1\n global x"), ('invalid', None))
347
349
348 class InteractiveLoopTestCase(unittest.TestCase):
350 class InteractiveLoopTestCase(unittest.TestCase):
349 """Tests for an interactive loop like a python shell.
351 """Tests for an interactive loop like a python shell.
350 """
352 """
351 def check_ns(self, lines, ns):
353 def check_ns(self, lines, ns):
352 """Validate that the given input lines produce the resulting namespace.
354 """Validate that the given input lines produce the resulting namespace.
353
355
354 Note: the input lines are given exactly as they would be typed in an
356 Note: the input lines are given exactly as they would be typed in an
355 auto-indenting environment, as mini_interactive_loop above already does
357 auto-indenting environment, as mini_interactive_loop above already does
356 auto-indenting and prepends spaces to the input.
358 auto-indenting and prepends spaces to the input.
357 """
359 """
358 src = mini_interactive_loop(pseudo_input(lines))
360 src = mini_interactive_loop(pseudo_input(lines))
359 test_ns = {}
361 test_ns = {}
360 exec(src, test_ns)
362 exec(src, test_ns)
361 # We can't check that the provided ns is identical to the test_ns,
363 # We can't check that the provided ns is identical to the test_ns,
362 # because Python fills test_ns with extra keys (copyright, etc). But
364 # because Python fills test_ns with extra keys (copyright, etc). But
363 # we can check that the given dict is *contained* in test_ns
365 # we can check that the given dict is *contained* in test_ns
364 for k,v in ns.items():
366 for k,v in ns.items():
365 self.assertEqual(test_ns[k], v)
367 self.assertEqual(test_ns[k], v)
366
368
367 def test_simple(self):
369 def test_simple(self):
368 self.check_ns(['x=1'], dict(x=1))
370 self.check_ns(['x=1'], dict(x=1))
369
371
370 def test_simple2(self):
372 def test_simple2(self):
371 self.check_ns(['if 1:', 'x=2'], dict(x=2))
373 self.check_ns(['if 1:', 'x=2'], dict(x=2))
372
374
373 def test_xy(self):
375 def test_xy(self):
374 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
376 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
375
377
376 def test_abc(self):
378 def test_abc(self):
377 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
379 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
378
380
379 def test_multi(self):
381 def test_multi(self):
380 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
382 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
381
383
382
384
383 class IPythonInputTestCase(InputSplitterTestCase):
385 class IPythonInputTestCase(InputSplitterTestCase):
384 """By just creating a new class whose .isp is a different instance, we
386 """By just creating a new class whose .isp is a different instance, we
385 re-run the same test battery on the new input splitter.
387 re-run the same test battery on the new input splitter.
386
388
387 In addition, this runs the tests over the syntax and syntax_ml dicts that
389 In addition, this runs the tests over the syntax and syntax_ml dicts that
388 were tested by individual functions, as part of the OO interface.
390 were tested by individual functions, as part of the OO interface.
389
391
390 It also makes some checks on the raw buffer storage.
392 It also makes some checks on the raw buffer storage.
391 """
393 """
392
394
393 def setUp(self):
395 def setUp(self):
394 self.isp = isp.IPythonInputSplitter()
396 self.isp = isp.IPythonInputSplitter()
395
397
396 def test_syntax(self):
398 def test_syntax(self):
397 """Call all single-line syntax tests from the main object"""
399 """Call all single-line syntax tests from the main object"""
398 isp = self.isp
400 isp = self.isp
399 for example in syntax.values():
401 for example in syntax.values():
400 for raw, out_t in example:
402 for raw, out_t in example:
401 if raw.startswith(' '):
403 if raw.startswith(' '):
402 continue
404 continue
403
405
404 isp.push(raw+'\n')
406 isp.push(raw+'\n')
405 out_raw = isp.source_raw
407 out_raw = isp.source_raw
406 out = isp.source_reset()
408 out = isp.source_reset()
407 self.assertEqual(out.rstrip(), out_t,
409 self.assertEqual(out.rstrip(), out_t,
408 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
410 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
409 self.assertEqual(out_raw.rstrip(), raw.rstrip())
411 self.assertEqual(out_raw.rstrip(), raw.rstrip())
410
412
411 def test_syntax_multiline(self):
413 def test_syntax_multiline(self):
412 isp = self.isp
414 isp = self.isp
413 for example in syntax_ml.values():
415 for example in syntax_ml.values():
414 for line_pairs in example:
416 for line_pairs in example:
415 out_t_parts = []
417 out_t_parts = []
416 raw_parts = []
418 raw_parts = []
417 for lraw, out_t_part in line_pairs:
419 for lraw, out_t_part in line_pairs:
418 if out_t_part is not None:
420 if out_t_part is not None:
419 out_t_parts.append(out_t_part)
421 out_t_parts.append(out_t_part)
420
422
421 if lraw is not None:
423 if lraw is not None:
422 isp.push(lraw)
424 isp.push(lraw)
423 raw_parts.append(lraw)
425 raw_parts.append(lraw)
424
426
425 out_raw = isp.source_raw
427 out_raw = isp.source_raw
426 out = isp.source_reset()
428 out = isp.source_reset()
427 out_t = '\n'.join(out_t_parts).rstrip()
429 out_t = '\n'.join(out_t_parts).rstrip()
428 raw = '\n'.join(raw_parts).rstrip()
430 raw = '\n'.join(raw_parts).rstrip()
429 self.assertEqual(out.rstrip(), out_t)
431 self.assertEqual(out.rstrip(), out_t)
430 self.assertEqual(out_raw.rstrip(), raw)
432 self.assertEqual(out_raw.rstrip(), raw)
431
433
432 def test_syntax_multiline_cell(self):
434 def test_syntax_multiline_cell(self):
433 isp = self.isp
435 isp = self.isp
434 for example in syntax_ml.values():
436 for example in syntax_ml.values():
435
437
436 out_t_parts = []
438 out_t_parts = []
437 for line_pairs in example:
439 for line_pairs in example:
438 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
440 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
439 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
441 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
440 out = isp.transform_cell(raw)
442 out = isp.transform_cell(raw)
441 # Match ignoring trailing whitespace
443 # Match ignoring trailing whitespace
442 self.assertEqual(out.rstrip(), out_t.rstrip())
444 self.assertEqual(out.rstrip(), out_t.rstrip())
443
445
444 def test_cellmagic_preempt(self):
446 def test_cellmagic_preempt(self):
445 isp = self.isp
447 isp = self.isp
446 for raw, name, line, cell in [
448 for raw, name, line, cell in [
447 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
449 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
448 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
450 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
449 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
451 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
450 ("%%cellm \n>>> hi", u'cellm', u'', u'>>> hi'),
452 ("%%cellm \n>>> hi", u'cellm', u'', u'>>> hi'),
451 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
453 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
452 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
454 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
453 ]:
455 ]:
454 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
456 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
455 name, line, cell
457 name, line, cell
456 )
458 )
457 out = isp.transform_cell(raw)
459 out = isp.transform_cell(raw)
458 self.assertEqual(out.rstrip(), expected.rstrip())
460 self.assertEqual(out.rstrip(), expected.rstrip())
459
461
460 def test_multiline_passthrough(self):
462 def test_multiline_passthrough(self):
461 isp = self.isp
463 isp = self.isp
462 class CommentTransformer(InputTransformer):
464 class CommentTransformer(InputTransformer):
463 def __init__(self):
465 def __init__(self):
464 self._lines = []
466 self._lines = []
465
467
466 def push(self, line):
468 def push(self, line):
467 self._lines.append(line + '#')
469 self._lines.append(line + '#')
468
470
469 def reset(self):
471 def reset(self):
470 text = '\n'.join(self._lines)
472 text = '\n'.join(self._lines)
471 self._lines = []
473 self._lines = []
472 return text
474 return text
473
475
474 isp.physical_line_transforms.insert(0, CommentTransformer())
476 isp.physical_line_transforms.insert(0, CommentTransformer())
475
477
476 for raw, expected in [
478 for raw, expected in [
477 ("a=5", "a=5#"),
479 ("a=5", "a=5#"),
478 ("%ls foo", "get_ipython().run_line_magic(%r, %r)" % (u'ls', u'foo#')),
480 ("%ls foo", "get_ipython().run_line_magic(%r, %r)" % (u'ls', u'foo#')),
479 ("!ls foo\n%ls bar", "get_ipython().system(%r)\nget_ipython().run_line_magic(%r, %r)" % (
481 ("!ls foo\n%ls bar", "get_ipython().system(%r)\nget_ipython().run_line_magic(%r, %r)" % (
480 u'ls foo#', u'ls', u'bar#'
482 u'ls foo#', u'ls', u'bar#'
481 )),
483 )),
482 ("1\n2\n3\n%ls foo\n4\n5", "1#\n2#\n3#\nget_ipython().run_line_magic(%r, %r)\n4#\n5#" % (u'ls', u'foo#')),
484 ("1\n2\n3\n%ls foo\n4\n5", "1#\n2#\n3#\nget_ipython().run_line_magic(%r, %r)\n4#\n5#" % (u'ls', u'foo#')),
483 ]:
485 ]:
484 out = isp.transform_cell(raw)
486 out = isp.transform_cell(raw)
485 self.assertEqual(out.rstrip(), expected.rstrip())
487 self.assertEqual(out.rstrip(), expected.rstrip())
486
488
487 #-----------------------------------------------------------------------------
489 #-----------------------------------------------------------------------------
488 # Main - use as a script, mostly for developer experiments
490 # Main - use as a script, mostly for developer experiments
489 #-----------------------------------------------------------------------------
491 #-----------------------------------------------------------------------------
490
492
491 if __name__ == '__main__':
493 if __name__ == '__main__':
492 # A simple demo for interactive experimentation. This code will not get
494 # A simple demo for interactive experimentation. This code will not get
493 # picked up by any test suite.
495 # picked up by any test suite.
494 from IPython.core.inputsplitter import IPythonInputSplitter
496 from IPython.core.inputsplitter import IPythonInputSplitter
495
497
496 # configure here the syntax to use, prompt and whether to autoindent
498 # configure here the syntax to use, prompt and whether to autoindent
497 #isp, start_prompt = InputSplitter(), '>>> '
499 #isp, start_prompt = InputSplitter(), '>>> '
498 isp, start_prompt = IPythonInputSplitter(), 'In> '
500 isp, start_prompt = IPythonInputSplitter(), 'In> '
499
501
500 autoindent = True
502 autoindent = True
501 #autoindent = False
503 #autoindent = False
502
504
503 try:
505 try:
504 while True:
506 while True:
505 prompt = start_prompt
507 prompt = start_prompt
506 while isp.push_accepts_more():
508 while isp.push_accepts_more():
507 indent = ' '*isp.get_indent_spaces()
509 indent = ' '*isp.get_indent_spaces()
508 if autoindent:
510 if autoindent:
509 line = indent + input(prompt+indent)
511 line = indent + input(prompt+indent)
510 else:
512 else:
511 line = input(prompt)
513 line = input(prompt)
512 isp.push(line)
514 isp.push(line)
513 prompt = '... '
515 prompt = '... '
514
516
515 # Here we just return input so we can use it in a test suite, but a
517 # Here we just return input so we can use it in a test suite, but a
516 # real interpreter would instead send it for execution somewhere.
518 # real interpreter would instead send it for execution somewhere.
517 #src = isp.source; raise EOFError # dbg
519 #src = isp.source; raise EOFError # dbg
518 raw = isp.source_raw
520 raw = isp.source_raw
519 src = isp.source_reset()
521 src = isp.source_reset()
520 print('Input source was:\n', src)
522 print('Input source was:\n', src)
521 print('Raw source was:\n', raw)
523 print('Raw source was:\n', raw)
522 except EOFError:
524 except EOFError:
523 print('Bye')
525 print('Bye')
524
526
525 # Tests for cell magics support
527 # Tests for cell magics support
526
528
527 def test_last_blank():
529 def test_last_blank():
528 assert isp.last_blank("") is False
530 assert isp.last_blank("") is False
529 assert isp.last_blank("abc") is False
531 assert isp.last_blank("abc") is False
530 assert isp.last_blank("abc\n") is False
532 assert isp.last_blank("abc\n") is False
531 assert isp.last_blank("abc\na") is False
533 assert isp.last_blank("abc\na") is False
532
534
533 assert isp.last_blank("\n") is True
535 assert isp.last_blank("\n") is True
534 assert isp.last_blank("\n ") is True
536 assert isp.last_blank("\n ") is True
535 assert isp.last_blank("abc\n ") is True
537 assert isp.last_blank("abc\n ") is True
536 assert isp.last_blank("abc\n\n") is True
538 assert isp.last_blank("abc\n\n") is True
537 assert isp.last_blank("abc\nd\n\n") is True
539 assert isp.last_blank("abc\nd\n\n") is True
538 assert isp.last_blank("abc\nd\ne\n\n") is True
540 assert isp.last_blank("abc\nd\ne\n\n") is True
539 assert isp.last_blank("abc \n \n \n\n") is True
541 assert isp.last_blank("abc \n \n \n\n") is True
540
542
541
543
542 def test_last_two_blanks():
544 def test_last_two_blanks():
543 assert isp.last_two_blanks("") is False
545 assert isp.last_two_blanks("") is False
544 assert isp.last_two_blanks("abc") is False
546 assert isp.last_two_blanks("abc") is False
545 assert isp.last_two_blanks("abc\n") is False
547 assert isp.last_two_blanks("abc\n") is False
546 assert isp.last_two_blanks("abc\n\na") is False
548 assert isp.last_two_blanks("abc\n\na") is False
547 assert isp.last_two_blanks("abc\n \n") is False
549 assert isp.last_two_blanks("abc\n \n") is False
548 assert isp.last_two_blanks("abc\n\n") is False
550 assert isp.last_two_blanks("abc\n\n") is False
549
551
550 assert isp.last_two_blanks("\n\n") is True
552 assert isp.last_two_blanks("\n\n") is True
551 assert isp.last_two_blanks("\n\n ") is True
553 assert isp.last_two_blanks("\n\n ") is True
552 assert isp.last_two_blanks("\n \n") is True
554 assert isp.last_two_blanks("\n \n") is True
553 assert isp.last_two_blanks("abc\n\n ") is True
555 assert isp.last_two_blanks("abc\n\n ") is True
554 assert isp.last_two_blanks("abc\n\n\n") is True
556 assert isp.last_two_blanks("abc\n\n\n") is True
555 assert isp.last_two_blanks("abc\n\n \n") is True
557 assert isp.last_two_blanks("abc\n\n \n") is True
556 assert isp.last_two_blanks("abc\n\n \n ") is True
558 assert isp.last_two_blanks("abc\n\n \n ") is True
557 assert isp.last_two_blanks("abc\n\n \n \n") is True
559 assert isp.last_two_blanks("abc\n\n \n \n") is True
558 assert isp.last_two_blanks("abc\nd\n\n\n") is True
560 assert isp.last_two_blanks("abc\nd\n\n\n") is True
559 assert isp.last_two_blanks("abc\nd\ne\nf\n\n\n") is True
561 assert isp.last_two_blanks("abc\nd\ne\nf\n\n\n") is True
560
562
561
563
562 class CellMagicsCommon(object):
564 class CellMagicsCommon(object):
563
565
564 def test_whole_cell(self):
566 def test_whole_cell(self):
565 src = "%%cellm line\nbody\n"
567 src = "%%cellm line\nbody\n"
566 out = self.sp.transform_cell(src)
568 out = self.sp.transform_cell(src)
567 ref = "get_ipython().run_cell_magic('cellm', 'line', 'body')\n"
569 ref = "get_ipython().run_cell_magic('cellm', 'line', 'body')\n"
568 assert out == ref
570 assert out == ref
569
571
570 def test_cellmagic_help(self):
572 def test_cellmagic_help(self):
571 self.sp.push('%%cellm?')
573 self.sp.push('%%cellm?')
572 assert self.sp.push_accepts_more() is False
574 assert self.sp.push_accepts_more() is False
573
575
574 def tearDown(self):
576 def tearDown(self):
575 self.sp.reset()
577 self.sp.reset()
576
578
577
579
578 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
580 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
579 sp = isp.IPythonInputSplitter(line_input_checker=False)
581 sp = isp.IPythonInputSplitter(line_input_checker=False)
580
582
581 def test_incremental(self):
583 def test_incremental(self):
582 sp = self.sp
584 sp = self.sp
583 sp.push("%%cellm firstline\n")
585 sp.push("%%cellm firstline\n")
584 assert sp.push_accepts_more() is True # 1
586 assert sp.push_accepts_more() is True # 1
585 sp.push("line2\n")
587 sp.push("line2\n")
586 assert sp.push_accepts_more() is True # 2
588 assert sp.push_accepts_more() is True # 2
587 sp.push("\n")
589 sp.push("\n")
588 # This should accept a blank line and carry on until the cell is reset
590 # This should accept a blank line and carry on until the cell is reset
589 assert sp.push_accepts_more() is True # 3
591 assert sp.push_accepts_more() is True # 3
590
592
591 def test_no_strip_coding(self):
593 def test_no_strip_coding(self):
592 src = '\n'.join([
594 src = '\n'.join([
593 '%%writefile foo.py',
595 '%%writefile foo.py',
594 '# coding: utf-8',
596 '# coding: utf-8',
595 'print(u"üñîçø∂é")',
597 'print(u"üñîçø∂é")',
596 ])
598 ])
597 out = self.sp.transform_cell(src)
599 out = self.sp.transform_cell(src)
598 assert "# coding: utf-8" in out
600 assert "# coding: utf-8" in out
599
601
600
602
601 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
603 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
602 sp = isp.IPythonInputSplitter(line_input_checker=True)
604 sp = isp.IPythonInputSplitter(line_input_checker=True)
603
605
604 def test_incremental(self):
606 def test_incremental(self):
605 sp = self.sp
607 sp = self.sp
606 sp.push("%%cellm line2\n")
608 sp.push("%%cellm line2\n")
607 assert sp.push_accepts_more() is True # 1
609 assert sp.push_accepts_more() is True # 1
608 sp.push("\n")
610 sp.push("\n")
609 # In this case, a blank line should end the cell magic
611 # In this case, a blank line should end the cell magic
610 assert sp.push_accepts_more() is False # 2
612 assert sp.push_accepts_more() is False # 2
611
613
612
614
613 indentation_samples = [
615 indentation_samples = [
614 ('a = 1', 0),
616 ('a = 1', 0),
615 ('for a in b:', 4),
617 ('for a in b:', 4),
616 ('def f():', 4),
618 ('def f():', 4),
617 ('def f(): #comment', 4),
619 ('def f(): #comment', 4),
618 ('a = ":#not a comment"', 0),
620 ('a = ":#not a comment"', 0),
619 ('def f():\n a = 1', 4),
621 ('def f():\n a = 1', 4),
620 ('def f():\n return 1', 0),
622 ('def f():\n return 1', 0),
621 ('for a in b:\n'
623 ('for a in b:\n'
622 ' if a < 0:'
624 ' if a < 0:'
623 ' continue', 3),
625 ' continue', 3),
624 ('a = {', 4),
626 ('a = {', 4),
625 ('a = {\n'
627 ('a = {\n'
626 ' 1,', 5),
628 ' 1,', 5),
627 ('b = """123', 0),
629 ('b = """123', 0),
628 ('', 0),
630 ('', 0),
629 ('def f():\n pass', 0),
631 ('def f():\n pass', 0),
630 ('class Bar:\n def f():\n pass', 4),
632 ('class Bar:\n def f():\n pass', 4),
631 ('class Bar:\n def f():\n raise', 4),
633 ('class Bar:\n def f():\n raise', 4),
632 ]
634 ]
633
635
634 def test_find_next_indent():
636 def test_find_next_indent():
635 for code, exp in indentation_samples:
637 for code, exp in indentation_samples:
636 res = isp.find_next_indent(code)
638 res = isp.find_next_indent(code)
637 msg = "{!r} != {!r} (expected)\n Code: {!r}".format(res, exp, code)
639 msg = "{!r} != {!r} (expected)\n Code: {!r}".format(res, exp, code)
638 assert res == exp, msg
640 assert res == exp, msg
@@ -1,359 +1,387 b''
1 """Tests for the token-based transformers in IPython.core.inputtransformer2
1 """Tests for the token-based transformers in IPython.core.inputtransformer2
2
2
3 Line-based transformers are the simpler ones; token-based transformers are
3 Line-based transformers are the simpler ones; token-based transformers are
4 more complex. See test_inputtransformer2_line for tests for line-based
4 more complex. See test_inputtransformer2_line for tests for line-based
5 transformations.
5 transformations.
6 """
6 """
7 import string
7 import string
8 import sys
9 from textwrap import dedent
8
10
9 from IPython.core import inputtransformer2 as ipt2
11 import pytest
10 from IPython.core.inputtransformer2 import make_tokens_by_line, _find_assign_op
11
12
12 from textwrap import dedent
13 from IPython.core import inputtransformer2 as ipt2
14 from IPython.core.inputtransformer2 import _find_assign_op, make_tokens_by_line
15 from IPython.testing.decorators import skip_iptest_but_not_pytest
13
16
14 MULTILINE_MAGIC = ("""\
17 MULTILINE_MAGIC = ("""\
15 a = f()
18 a = f()
16 %foo \\
19 %foo \\
17 bar
20 bar
18 g()
21 g()
19 """.splitlines(keepends=True), (2, 0), """\
22 """.splitlines(keepends=True), (2, 0), """\
20 a = f()
23 a = f()
21 get_ipython().run_line_magic('foo', ' bar')
24 get_ipython().run_line_magic('foo', ' bar')
22 g()
25 g()
23 """.splitlines(keepends=True))
26 """.splitlines(keepends=True))
24
27
25 INDENTED_MAGIC = ("""\
28 INDENTED_MAGIC = ("""\
26 for a in range(5):
29 for a in range(5):
27 %ls
30 %ls
28 """.splitlines(keepends=True), (2, 4), """\
31 """.splitlines(keepends=True), (2, 4), """\
29 for a in range(5):
32 for a in range(5):
30 get_ipython().run_line_magic('ls', '')
33 get_ipython().run_line_magic('ls', '')
31 """.splitlines(keepends=True))
34 """.splitlines(keepends=True))
32
35
33 CRLF_MAGIC = ([
36 CRLF_MAGIC = ([
34 "a = f()\n",
37 "a = f()\n",
35 "%ls\r\n",
38 "%ls\r\n",
36 "g()\n"
39 "g()\n"
37 ], (2, 0), [
40 ], (2, 0), [
38 "a = f()\n",
41 "a = f()\n",
39 "get_ipython().run_line_magic('ls', '')\n",
42 "get_ipython().run_line_magic('ls', '')\n",
40 "g()\n"
43 "g()\n"
41 ])
44 ])
42
45
43 MULTILINE_MAGIC_ASSIGN = ("""\
46 MULTILINE_MAGIC_ASSIGN = ("""\
44 a = f()
47 a = f()
45 b = %foo \\
48 b = %foo \\
46 bar
49 bar
47 g()
50 g()
48 """.splitlines(keepends=True), (2, 4), """\
51 """.splitlines(keepends=True), (2, 4), """\
49 a = f()
52 a = f()
50 b = get_ipython().run_line_magic('foo', ' bar')
53 b = get_ipython().run_line_magic('foo', ' bar')
51 g()
54 g()
52 """.splitlines(keepends=True))
55 """.splitlines(keepends=True))
53
56
54 MULTILINE_SYSTEM_ASSIGN = ("""\
57 MULTILINE_SYSTEM_ASSIGN = ("""\
55 a = f()
58 a = f()
56 b = !foo \\
59 b = !foo \\
57 bar
60 bar
58 g()
61 g()
59 """.splitlines(keepends=True), (2, 4), """\
62 """.splitlines(keepends=True), (2, 4), """\
60 a = f()
63 a = f()
61 b = get_ipython().getoutput('foo bar')
64 b = get_ipython().getoutput('foo bar')
62 g()
65 g()
63 """.splitlines(keepends=True))
66 """.splitlines(keepends=True))
64
67
65 #####
68 #####
66
69
67 MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT = ("""\
70 MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT = ("""\
68 def test():
71 def test():
69 for i in range(1):
72 for i in range(1):
70 print(i)
73 print(i)
71 res =! ls
74 res =! ls
72 """.splitlines(keepends=True), (4, 7), '''\
75 """.splitlines(keepends=True), (4, 7), '''\
73 def test():
76 def test():
74 for i in range(1):
77 for i in range(1):
75 print(i)
78 print(i)
76 res =get_ipython().getoutput(\' ls\')
79 res =get_ipython().getoutput(\' ls\')
77 '''.splitlines(keepends=True))
80 '''.splitlines(keepends=True))
78
81
79 ######
82 ######
80
83
81 AUTOCALL_QUOTE = (
84 AUTOCALL_QUOTE = (
82 [",f 1 2 3\n"], (1, 0),
85 [",f 1 2 3\n"], (1, 0),
83 ['f("1", "2", "3")\n']
86 ['f("1", "2", "3")\n']
84 )
87 )
85
88
86 AUTOCALL_QUOTE2 = (
89 AUTOCALL_QUOTE2 = (
87 [";f 1 2 3\n"], (1, 0),
90 [";f 1 2 3\n"], (1, 0),
88 ['f("1 2 3")\n']
91 ['f("1 2 3")\n']
89 )
92 )
90
93
91 AUTOCALL_PAREN = (
94 AUTOCALL_PAREN = (
92 ["/f 1 2 3\n"], (1, 0),
95 ["/f 1 2 3\n"], (1, 0),
93 ['f(1, 2, 3)\n']
96 ['f(1, 2, 3)\n']
94 )
97 )
95
98
96 SIMPLE_HELP = (
99 SIMPLE_HELP = (
97 ["foo?\n"], (1, 0),
100 ["foo?\n"], (1, 0),
98 ["get_ipython().run_line_magic('pinfo', 'foo')\n"]
101 ["get_ipython().run_line_magic('pinfo', 'foo')\n"]
99 )
102 )
100
103
101 DETAILED_HELP = (
104 DETAILED_HELP = (
102 ["foo??\n"], (1, 0),
105 ["foo??\n"], (1, 0),
103 ["get_ipython().run_line_magic('pinfo2', 'foo')\n"]
106 ["get_ipython().run_line_magic('pinfo2', 'foo')\n"]
104 )
107 )
105
108
106 MAGIC_HELP = (
109 MAGIC_HELP = (
107 ["%foo?\n"], (1, 0),
110 ["%foo?\n"], (1, 0),
108 ["get_ipython().run_line_magic('pinfo', '%foo')\n"]
111 ["get_ipython().run_line_magic('pinfo', '%foo')\n"]
109 )
112 )
110
113
111 HELP_IN_EXPR = (
114 HELP_IN_EXPR = (
112 ["a = b + c?\n"], (1, 0),
115 ["a = b + c?\n"], (1, 0),
113 ["get_ipython().set_next_input('a = b + c');"
116 ["get_ipython().set_next_input('a = b + c');"
114 "get_ipython().run_line_magic('pinfo', 'c')\n"]
117 "get_ipython().run_line_magic('pinfo', 'c')\n"]
115 )
118 )
116
119
117 HELP_CONTINUED_LINE = ("""\
120 HELP_CONTINUED_LINE = ("""\
118 a = \\
121 a = \\
119 zip?
122 zip?
120 """.splitlines(keepends=True), (1, 0),
123 """.splitlines(keepends=True), (1, 0),
121 [r"get_ipython().set_next_input('a = \\\nzip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"]
124 [r"get_ipython().set_next_input('a = \\\nzip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"]
122 )
125 )
123
126
124 HELP_MULTILINE = ("""\
127 HELP_MULTILINE = ("""\
125 (a,
128 (a,
126 b) = zip?
129 b) = zip?
127 """.splitlines(keepends=True), (1, 0),
130 """.splitlines(keepends=True), (1, 0),
128 [r"get_ipython().set_next_input('(a,\nb) = zip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"]
131 [r"get_ipython().set_next_input('(a,\nb) = zip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"]
129 )
132 )
130
133
131 HELP_UNICODE = (
134 HELP_UNICODE = (
132 ["π.foo?\n"], (1, 0),
135 ["π.foo?\n"], (1, 0),
133 ["get_ipython().run_line_magic('pinfo', 'π.foo')\n"]
136 ["get_ipython().run_line_magic('pinfo', 'π.foo')\n"]
134 )
137 )
135
138
136
139
137 def null_cleanup_transformer(lines):
140 def null_cleanup_transformer(lines):
138 """
141 """
139 A cleanup transform that returns an empty list.
142 A cleanup transform that returns an empty list.
140 """
143 """
141 return []
144 return []
142
145
143 def check_make_token_by_line_never_ends_empty():
146 def check_make_token_by_line_never_ends_empty():
144 """
147 """
145 Check that not sequence of single or double characters ends up leading to en empty list of tokens
148 Check that not sequence of single or double characters ends up leading to en empty list of tokens
146 """
149 """
147 from string import printable
150 from string import printable
148 for c in printable:
151 for c in printable:
149 assert make_tokens_by_line(c)[-1] != []
152 assert make_tokens_by_line(c)[-1] != []
150 for k in printable:
153 for k in printable:
151 assert make_tokens_by_line(c + k)[-1] != []
154 assert make_tokens_by_line(c + k)[-1] != []
152
155
153
156
154 def check_find(transformer, case, match=True):
157 def check_find(transformer, case, match=True):
155 sample, expected_start, _ = case
158 sample, expected_start, _ = case
156 tbl = make_tokens_by_line(sample)
159 tbl = make_tokens_by_line(sample)
157 res = transformer.find(tbl)
160 res = transformer.find(tbl)
158 if match:
161 if match:
159 # start_line is stored 0-indexed, expected values are 1-indexed
162 # start_line is stored 0-indexed, expected values are 1-indexed
160 assert (res.start_line + 1, res.start_col) == expected_start
163 assert (res.start_line + 1, res.start_col) == expected_start
161 return res
164 return res
162 else:
165 else:
163 assert res is None
166 assert res is None
164
167
165 def check_transform(transformer_cls, case):
168 def check_transform(transformer_cls, case):
166 lines, start, expected = case
169 lines, start, expected = case
167 transformer = transformer_cls(start)
170 transformer = transformer_cls(start)
168 assert transformer.transform(lines) == expected
171 assert transformer.transform(lines) == expected
169
172
170 def test_continued_line():
173 def test_continued_line():
171 lines = MULTILINE_MAGIC_ASSIGN[0]
174 lines = MULTILINE_MAGIC_ASSIGN[0]
172 assert ipt2.find_end_of_continued_line(lines, 1) == 2
175 assert ipt2.find_end_of_continued_line(lines, 1) == 2
173
176
174 assert ipt2.assemble_continued_line(lines, (1, 5), 2) == "foo bar"
177 assert ipt2.assemble_continued_line(lines, (1, 5), 2) == "foo bar"
175
178
176 def test_find_assign_magic():
179 def test_find_assign_magic():
177 check_find(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN)
180 check_find(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN)
178 check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN, match=False)
181 check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN, match=False)
179 check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT, match=False)
182 check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT, match=False)
180
183
181 def test_transform_assign_magic():
184 def test_transform_assign_magic():
182 check_transform(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN)
185 check_transform(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN)
183
186
184 def test_find_assign_system():
187 def test_find_assign_system():
185 check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN)
188 check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN)
186 check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT)
189 check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT)
187 check_find(ipt2.SystemAssign, (["a = !ls\n"], (1, 5), None))
190 check_find(ipt2.SystemAssign, (["a = !ls\n"], (1, 5), None))
188 check_find(ipt2.SystemAssign, (["a=!ls\n"], (1, 2), None))
191 check_find(ipt2.SystemAssign, (["a=!ls\n"], (1, 2), None))
189 check_find(ipt2.SystemAssign, MULTILINE_MAGIC_ASSIGN, match=False)
192 check_find(ipt2.SystemAssign, MULTILINE_MAGIC_ASSIGN, match=False)
190
193
191 def test_transform_assign_system():
194 def test_transform_assign_system():
192 check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN)
195 check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN)
193 check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT)
196 check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT)
194
197
195 def test_find_magic_escape():
198 def test_find_magic_escape():
196 check_find(ipt2.EscapedCommand, MULTILINE_MAGIC)
199 check_find(ipt2.EscapedCommand, MULTILINE_MAGIC)
197 check_find(ipt2.EscapedCommand, INDENTED_MAGIC)
200 check_find(ipt2.EscapedCommand, INDENTED_MAGIC)
198 check_find(ipt2.EscapedCommand, MULTILINE_MAGIC_ASSIGN, match=False)
201 check_find(ipt2.EscapedCommand, MULTILINE_MAGIC_ASSIGN, match=False)
199
202
200 def test_transform_magic_escape():
203 def test_transform_magic_escape():
201 check_transform(ipt2.EscapedCommand, MULTILINE_MAGIC)
204 check_transform(ipt2.EscapedCommand, MULTILINE_MAGIC)
202 check_transform(ipt2.EscapedCommand, INDENTED_MAGIC)
205 check_transform(ipt2.EscapedCommand, INDENTED_MAGIC)
203 check_transform(ipt2.EscapedCommand, CRLF_MAGIC)
206 check_transform(ipt2.EscapedCommand, CRLF_MAGIC)
204
207
205 def test_find_autocalls():
208 def test_find_autocalls():
206 for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]:
209 for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]:
207 print("Testing %r" % case[0])
210 print("Testing %r" % case[0])
208 check_find(ipt2.EscapedCommand, case)
211 check_find(ipt2.EscapedCommand, case)
209
212
210 def test_transform_autocall():
213 def test_transform_autocall():
211 for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]:
214 for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]:
212 print("Testing %r" % case[0])
215 print("Testing %r" % case[0])
213 check_transform(ipt2.EscapedCommand, case)
216 check_transform(ipt2.EscapedCommand, case)
214
217
215 def test_find_help():
218 def test_find_help():
216 for case in [SIMPLE_HELP, DETAILED_HELP, MAGIC_HELP, HELP_IN_EXPR]:
219 for case in [SIMPLE_HELP, DETAILED_HELP, MAGIC_HELP, HELP_IN_EXPR]:
217 check_find(ipt2.HelpEnd, case)
220 check_find(ipt2.HelpEnd, case)
218
221
219 tf = check_find(ipt2.HelpEnd, HELP_CONTINUED_LINE)
222 tf = check_find(ipt2.HelpEnd, HELP_CONTINUED_LINE)
220 assert tf.q_line == 1
223 assert tf.q_line == 1
221 assert tf.q_col == 3
224 assert tf.q_col == 3
222
225
223 tf = check_find(ipt2.HelpEnd, HELP_MULTILINE)
226 tf = check_find(ipt2.HelpEnd, HELP_MULTILINE)
224 assert tf.q_line == 1
227 assert tf.q_line == 1
225 assert tf.q_col == 8
228 assert tf.q_col == 8
226
229
227 # ? in a comment does not trigger help
230 # ? in a comment does not trigger help
228 check_find(ipt2.HelpEnd, (["foo # bar?\n"], None, None), match=False)
231 check_find(ipt2.HelpEnd, (["foo # bar?\n"], None, None), match=False)
229 # Nor in a string
232 # Nor in a string
230 check_find(ipt2.HelpEnd, (["foo = '''bar?\n"], None, None), match=False)
233 check_find(ipt2.HelpEnd, (["foo = '''bar?\n"], None, None), match=False)
231
234
232 def test_transform_help():
235 def test_transform_help():
233 tf = ipt2.HelpEnd((1, 0), (1, 9))
236 tf = ipt2.HelpEnd((1, 0), (1, 9))
234 assert tf.transform(HELP_IN_EXPR[0]) == HELP_IN_EXPR[2]
237 assert tf.transform(HELP_IN_EXPR[0]) == HELP_IN_EXPR[2]
235
238
236 tf = ipt2.HelpEnd((1, 0), (2, 3))
239 tf = ipt2.HelpEnd((1, 0), (2, 3))
237 assert tf.transform(HELP_CONTINUED_LINE[0]) == HELP_CONTINUED_LINE[2]
240 assert tf.transform(HELP_CONTINUED_LINE[0]) == HELP_CONTINUED_LINE[2]
238
241
239 tf = ipt2.HelpEnd((1, 0), (2, 8))
242 tf = ipt2.HelpEnd((1, 0), (2, 8))
240 assert tf.transform(HELP_MULTILINE[0]) == HELP_MULTILINE[2]
243 assert tf.transform(HELP_MULTILINE[0]) == HELP_MULTILINE[2]
241
244
242 tf = ipt2.HelpEnd((1, 0), (1, 0))
245 tf = ipt2.HelpEnd((1, 0), (1, 0))
243 assert tf.transform(HELP_UNICODE[0]) == HELP_UNICODE[2]
246 assert tf.transform(HELP_UNICODE[0]) == HELP_UNICODE[2]
244
247
245 def test_find_assign_op_dedent():
248 def test_find_assign_op_dedent():
246 """
249 """
247 be careful that empty token like dedent are not counted as parens
250 be careful that empty token like dedent are not counted as parens
248 """
251 """
249 class Tk:
252 class Tk:
250 def __init__(self, s):
253 def __init__(self, s):
251 self.string = s
254 self.string = s
252
255
253 assert _find_assign_op([Tk(s) for s in ("", "a", "=", "b")]) == 2
256 assert _find_assign_op([Tk(s) for s in ("", "a", "=", "b")]) == 2
254 assert (
257 assert (
255 _find_assign_op([Tk(s) for s in ("", "(", "a", "=", "b", ")", "=", "5")]) == 6
258 _find_assign_op([Tk(s) for s in ("", "(", "a", "=", "b", ")", "=", "5")]) == 6
256 )
259 )
257
260
258
261
262 examples = [
263 pytest.param("a = 1", "complete", None),
264 pytest.param("for a in range(5):", "incomplete", 4),
265 pytest.param("for a in range(5):\n if a > 0:", "incomplete", 8),
266 pytest.param("raise = 2", "invalid", None),
267 pytest.param("a = [1,\n2,", "incomplete", 0),
268 pytest.param("(\n))", "incomplete", 0),
269 pytest.param("\\\r\n", "incomplete", 0),
270 pytest.param("a = '''\n hi", "incomplete", 3),
271 pytest.param("def a():\n x=1\n global x", "invalid", None),
272 pytest.param(
273 "a \\ ",
274 "invalid",
275 None,
276 marks=pytest.mark.xfail(
277 reason="Bug in python 3.9.8 – bpo 45738",
278 condition=sys.version_info[:3] == (3, 9, 8),
279 raises=SystemError,
280 strict=True,
281 ),
282 ), # Nothing allowed after backslash,
283 pytest.param("1\\\n+2", "complete", None),
284 ]
285
286
287 @skip_iptest_but_not_pytest
288 @pytest.mark.parametrize("code, expected, number", examples)
289 def test_check_complete_param(code, expected, number):
290 cc = ipt2.TransformerManager().check_complete
291 assert cc(code) == (expected, number)
292
293
294 @skip_iptest_but_not_pytest
295 @pytest.mark.xfail(
296 reason="Bug in python 3.9.8 – bpo 45738",
297 condition=sys.version_info[:3] == (3, 9, 8),
298 )
259 def test_check_complete():
299 def test_check_complete():
260 cc = ipt2.TransformerManager().check_complete
300 cc = ipt2.TransformerManager().check_complete
261 assert cc("a = 1") == ("complete", None)
262 assert cc("for a in range(5):") == ("incomplete", 4)
263 assert cc("for a in range(5):\n if a > 0:") == ("incomplete", 8)
264 assert cc("raise = 2") == ("invalid", None)
265 assert cc("a = [1,\n2,") == ("incomplete", 0)
266 assert cc("(\n))") == ("incomplete", 0)
267 assert cc("\\\r\n") == ("incomplete", 0)
268 assert cc("a = '''\n hi") == ("incomplete", 3)
269 assert cc("def a():\n x=1\n global x") == ("invalid", None)
270 assert cc("a \\ ") == ("invalid", None) # Nothing allowed after backslash
271 assert cc("1\\\n+2") == ("complete", None)
272 assert cc("exit") == ("complete", None)
273
301
274 example = dedent("""
302 example = dedent("""
275 if True:
303 if True:
276 a=1""" )
304 a=1""" )
277
305
278 assert cc(example) == ("incomplete", 4)
306 assert cc(example) == ("incomplete", 4)
279 assert cc(example + "\n") == ("complete", None)
307 assert cc(example + "\n") == ("complete", None)
280 assert cc(example + "\n ") == ("complete", None)
308 assert cc(example + "\n ") == ("complete", None)
281
309
282 # no need to loop on all the letters/numbers.
310 # no need to loop on all the letters/numbers.
283 short = '12abAB'+string.printable[62:]
311 short = '12abAB'+string.printable[62:]
284 for c in short:
312 for c in short:
285 # test does not raise:
313 # test does not raise:
286 cc(c)
314 cc(c)
287 for k in short:
315 for k in short:
288 cc(c+k)
316 cc(c+k)
289
317
290 assert cc("def f():\n x=0\n \\\n ") == ("incomplete", 2)
318 assert cc("def f():\n x=0\n \\\n ") == ("incomplete", 2)
291
319
292
320
293 def test_check_complete_II():
321 def test_check_complete_II():
294 """
322 """
295 Test that multiple line strings are properly handled.
323 Test that multiple line strings are properly handled.
296
324
297 Separate test function for convenience
325 Separate test function for convenience
298
326
299 """
327 """
300 cc = ipt2.TransformerManager().check_complete
328 cc = ipt2.TransformerManager().check_complete
301 assert cc('''def foo():\n """''') == ("incomplete", 4)
329 assert cc('''def foo():\n """''') == ("incomplete", 4)
302
330
303
331
304 def test_check_complete_invalidates_sunken_brackets():
332 def test_check_complete_invalidates_sunken_brackets():
305 """
333 """
306 Test that a single line with more closing brackets than the opening ones is
334 Test that a single line with more closing brackets than the opening ones is
307 interpreted as invalid
335 interpreted as invalid
308 """
336 """
309 cc = ipt2.TransformerManager().check_complete
337 cc = ipt2.TransformerManager().check_complete
310 assert cc(")") == ("invalid", None)
338 assert cc(")") == ("invalid", None)
311 assert cc("]") == ("invalid", None)
339 assert cc("]") == ("invalid", None)
312 assert cc("}") == ("invalid", None)
340 assert cc("}") == ("invalid", None)
313 assert cc(")(") == ("invalid", None)
341 assert cc(")(") == ("invalid", None)
314 assert cc("][") == ("invalid", None)
342 assert cc("][") == ("invalid", None)
315 assert cc("}{") == ("invalid", None)
343 assert cc("}{") == ("invalid", None)
316 assert cc("]()(") == ("invalid", None)
344 assert cc("]()(") == ("invalid", None)
317 assert cc("())(") == ("invalid", None)
345 assert cc("())(") == ("invalid", None)
318 assert cc(")[](") == ("invalid", None)
346 assert cc(")[](") == ("invalid", None)
319 assert cc("()](") == ("invalid", None)
347 assert cc("()](") == ("invalid", None)
320
348
321
349
322 def test_null_cleanup_transformer():
350 def test_null_cleanup_transformer():
323 manager = ipt2.TransformerManager()
351 manager = ipt2.TransformerManager()
324 manager.cleanup_transforms.insert(0, null_cleanup_transformer)
352 manager.cleanup_transforms.insert(0, null_cleanup_transformer)
325 assert manager.transform_cell("") == ""
353 assert manager.transform_cell("") == ""
326
354
327
355
328
356
329
357
330 def test_side_effects_I():
358 def test_side_effects_I():
331 count = 0
359 count = 0
332 def counter(lines):
360 def counter(lines):
333 nonlocal count
361 nonlocal count
334 count += 1
362 count += 1
335 return lines
363 return lines
336
364
337 counter.has_side_effects = True
365 counter.has_side_effects = True
338
366
339 manager = ipt2.TransformerManager()
367 manager = ipt2.TransformerManager()
340 manager.cleanup_transforms.insert(0, counter)
368 manager.cleanup_transforms.insert(0, counter)
341 assert manager.check_complete("a=1\n") == ('complete', None)
369 assert manager.check_complete("a=1\n") == ('complete', None)
342 assert count == 0
370 assert count == 0
343
371
344
372
345
373
346
374
347 def test_side_effects_II():
375 def test_side_effects_II():
348 count = 0
376 count = 0
349 def counter(lines):
377 def counter(lines):
350 nonlocal count
378 nonlocal count
351 count += 1
379 count += 1
352 return lines
380 return lines
353
381
354 counter.has_side_effects = True
382 counter.has_side_effects = True
355
383
356 manager = ipt2.TransformerManager()
384 manager = ipt2.TransformerManager()
357 manager.line_transforms.insert(0, counter)
385 manager.line_transforms.insert(0, counter)
358 assert manager.check_complete("b=1\n") == ('complete', None)
386 assert manager.check_complete("b=1\n") == ('complete', None)
359 assert count == 0
387 assert count == 0
General Comments 0
You need to be logged in to leave comments. Login now