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