##// END OF EJS Templates
Add test for inputsplitter bug.
Thomas Kluyver -
Show More
@@ -1,647 +1,653 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the inputsplitter module.
2 """Tests for the inputsplitter module.
3
3
4 Authors
4 Authors
5 -------
5 -------
6 * Fernando Perez
6 * Fernando Perez
7 * Robert Kern
7 * Robert Kern
8 """
8 """
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2010 The IPython Development Team
10 # Copyright (C) 2010 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # stdlib
19 # stdlib
20 import unittest
20 import unittest
21 import sys
21 import sys
22
22
23 # Third party
23 # Third party
24 import nose.tools as nt
24 import nose.tools as nt
25
25
26 # Our own
26 # Our own
27 from IPython.core import inputsplitter as isp
27 from IPython.core import inputsplitter as isp
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Semi-complete examples (also used as tests)
30 # Semi-complete examples (also used as tests)
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 # Note: at the bottom, there's a slightly more complete version of this that
33 # Note: at the bottom, there's a slightly more complete version of this that
34 # can be useful during development of code here.
34 # can be useful during development of code here.
35
35
36 def mini_interactive_loop(input_func):
36 def mini_interactive_loop(input_func):
37 """Minimal example of the logic of an interactive interpreter loop.
37 """Minimal example of the logic of an interactive interpreter loop.
38
38
39 This serves as an example, and it is used by the test system with a fake
39 This serves as an example, and it is used by the test system with a fake
40 raw_input that simulates interactive input."""
40 raw_input that simulates interactive input."""
41
41
42 from IPython.core.inputsplitter import InputSplitter
42 from IPython.core.inputsplitter import InputSplitter
43
43
44 isp = InputSplitter()
44 isp = InputSplitter()
45 # In practice, this input loop would be wrapped in an outside loop to read
45 # In practice, this input loop would be wrapped in an outside loop to read
46 # input indefinitely, until some exit/quit command was issued. Here we
46 # input indefinitely, until some exit/quit command was issued. Here we
47 # only illustrate the basic inner loop.
47 # only illustrate the basic inner loop.
48 while isp.push_accepts_more():
48 while isp.push_accepts_more():
49 indent = ' '*isp.indent_spaces
49 indent = ' '*isp.indent_spaces
50 prompt = '>>> ' + indent
50 prompt = '>>> ' + indent
51 line = indent + input_func(prompt)
51 line = indent + input_func(prompt)
52 isp.push(line)
52 isp.push(line)
53
53
54 # Here we just return input so we can use it in a test suite, but a real
54 # Here we just return input so we can use it in a test suite, but a real
55 # interpreter would instead send it for execution somewhere.
55 # interpreter would instead send it for execution somewhere.
56 src = isp.source_reset()
56 src = isp.source_reset()
57 #print 'Input source was:\n', src # dbg
57 #print 'Input source was:\n', src # dbg
58 return src
58 return src
59
59
60 #-----------------------------------------------------------------------------
60 #-----------------------------------------------------------------------------
61 # Test utilities, just for local use
61 # Test utilities, just for local use
62 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
63
63
64 def assemble(block):
64 def assemble(block):
65 """Assemble a block into multi-line sub-blocks."""
65 """Assemble a block into multi-line sub-blocks."""
66 return ['\n'.join(sub_block)+'\n' for sub_block in block]
66 return ['\n'.join(sub_block)+'\n' for sub_block in block]
67
67
68
68
69 def pseudo_input(lines):
69 def pseudo_input(lines):
70 """Return a function that acts like raw_input but feeds the input list."""
70 """Return a function that acts like raw_input but feeds the input list."""
71 ilines = iter(lines)
71 ilines = iter(lines)
72 def raw_in(prompt):
72 def raw_in(prompt):
73 try:
73 try:
74 return next(ilines)
74 return next(ilines)
75 except StopIteration:
75 except StopIteration:
76 return ''
76 return ''
77 return raw_in
77 return raw_in
78
78
79 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
80 # Tests
80 # Tests
81 #-----------------------------------------------------------------------------
81 #-----------------------------------------------------------------------------
82 def test_spaces():
82 def test_spaces():
83 tests = [('', 0),
83 tests = [('', 0),
84 (' ', 1),
84 (' ', 1),
85 ('\n', 0),
85 ('\n', 0),
86 (' \n', 1),
86 (' \n', 1),
87 ('x', 0),
87 ('x', 0),
88 (' x', 1),
88 (' x', 1),
89 (' x',2),
89 (' x',2),
90 (' x',4),
90 (' x',4),
91 # Note: tabs are counted as a single whitespace!
91 # Note: tabs are counted as a single whitespace!
92 ('\tx', 1),
92 ('\tx', 1),
93 ('\t x', 2),
93 ('\t x', 2),
94 ]
94 ]
95
95
96 for s, nsp in tests:
96 for s, nsp in tests:
97 nt.assert_equal(isp.num_ini_spaces(s), nsp)
97 nt.assert_equal(isp.num_ini_spaces(s), nsp)
98
98
99
99
100 def test_remove_comments():
100 def test_remove_comments():
101 tests = [('text', 'text'),
101 tests = [('text', 'text'),
102 ('text # comment', 'text '),
102 ('text # comment', 'text '),
103 ('text # comment\n', 'text \n'),
103 ('text # comment\n', 'text \n'),
104 ('text # comment \n', 'text \n'),
104 ('text # comment \n', 'text \n'),
105 ('line # c \nline\n','line \nline\n'),
105 ('line # c \nline\n','line \nline\n'),
106 ('line # c \nline#c2 \nline\nline #c\n\n',
106 ('line # c \nline#c2 \nline\nline #c\n\n',
107 'line \nline\nline\nline \n\n'),
107 'line \nline\nline\nline \n\n'),
108 ]
108 ]
109
109
110 for inp, out in tests:
110 for inp, out in tests:
111 nt.assert_equal(isp.remove_comments(inp), out)
111 nt.assert_equal(isp.remove_comments(inp), out)
112
112
113
113
114 def test_get_input_encoding():
114 def test_get_input_encoding():
115 encoding = isp.get_input_encoding()
115 encoding = isp.get_input_encoding()
116 nt.assert_true(isinstance(encoding, basestring))
116 nt.assert_true(isinstance(encoding, basestring))
117 # simple-minded check that at least encoding a simple string works with the
117 # simple-minded check that at least encoding a simple string works with the
118 # encoding we got.
118 # encoding we got.
119 nt.assert_equal('test'.encode(encoding), 'test')
119 nt.assert_equal('test'.encode(encoding), 'test')
120
120
121
121
122 class NoInputEncodingTestCase(unittest.TestCase):
122 class NoInputEncodingTestCase(unittest.TestCase):
123 def setUp(self):
123 def setUp(self):
124 self.old_stdin = sys.stdin
124 self.old_stdin = sys.stdin
125 class X: pass
125 class X: pass
126 fake_stdin = X()
126 fake_stdin = X()
127 sys.stdin = fake_stdin
127 sys.stdin = fake_stdin
128
128
129 def test(self):
129 def test(self):
130 # Verify that if sys.stdin has no 'encoding' attribute we do the right
130 # Verify that if sys.stdin has no 'encoding' attribute we do the right
131 # thing
131 # thing
132 enc = isp.get_input_encoding()
132 enc = isp.get_input_encoding()
133 self.assertEqual(enc, 'ascii')
133 self.assertEqual(enc, 'ascii')
134
134
135 def tearDown(self):
135 def tearDown(self):
136 sys.stdin = self.old_stdin
136 sys.stdin = self.old_stdin
137
137
138
138
139 class InputSplitterTestCase(unittest.TestCase):
139 class InputSplitterTestCase(unittest.TestCase):
140 def setUp(self):
140 def setUp(self):
141 self.isp = isp.InputSplitter()
141 self.isp = isp.InputSplitter()
142
142
143 def test_reset(self):
143 def test_reset(self):
144 isp = self.isp
144 isp = self.isp
145 isp.push('x=1')
145 isp.push('x=1')
146 isp.reset()
146 isp.reset()
147 self.assertEqual(isp._buffer, [])
147 self.assertEqual(isp._buffer, [])
148 self.assertEqual(isp.indent_spaces, 0)
148 self.assertEqual(isp.indent_spaces, 0)
149 self.assertEqual(isp.source, '')
149 self.assertEqual(isp.source, '')
150 self.assertEqual(isp.code, None)
150 self.assertEqual(isp.code, None)
151 self.assertEqual(isp._is_complete, False)
151 self.assertEqual(isp._is_complete, False)
152
152
153 def test_source(self):
153 def test_source(self):
154 self.isp._store('1')
154 self.isp._store('1')
155 self.isp._store('2')
155 self.isp._store('2')
156 self.assertEqual(self.isp.source, '1\n2\n')
156 self.assertEqual(self.isp.source, '1\n2\n')
157 self.assertTrue(len(self.isp._buffer)>0)
157 self.assertTrue(len(self.isp._buffer)>0)
158 self.assertEqual(self.isp.source_reset(), '1\n2\n')
158 self.assertEqual(self.isp.source_reset(), '1\n2\n')
159 self.assertEqual(self.isp._buffer, [])
159 self.assertEqual(self.isp._buffer, [])
160 self.assertEqual(self.isp.source, '')
160 self.assertEqual(self.isp.source, '')
161
161
162 def test_indent(self):
162 def test_indent(self):
163 isp = self.isp # shorthand
163 isp = self.isp # shorthand
164 isp.push('x=1')
164 isp.push('x=1')
165 self.assertEqual(isp.indent_spaces, 0)
165 self.assertEqual(isp.indent_spaces, 0)
166 isp.push('if 1:\n x=1')
166 isp.push('if 1:\n x=1')
167 self.assertEqual(isp.indent_spaces, 4)
167 self.assertEqual(isp.indent_spaces, 4)
168 isp.push('y=2\n')
168 isp.push('y=2\n')
169 self.assertEqual(isp.indent_spaces, 0)
169 self.assertEqual(isp.indent_spaces, 0)
170
170
171 def test_indent2(self):
171 def test_indent2(self):
172 # In cell mode, inputs must be fed in whole blocks, so skip this test
172 # In cell mode, inputs must be fed in whole blocks, so skip this test
173 if self.isp.input_mode == 'cell': return
173 if self.isp.input_mode == 'cell': return
174
174
175 isp = self.isp
175 isp = self.isp
176 isp.push('if 1:')
176 isp.push('if 1:')
177 self.assertEqual(isp.indent_spaces, 4)
177 self.assertEqual(isp.indent_spaces, 4)
178 isp.push(' x=1')
178 isp.push(' x=1')
179 self.assertEqual(isp.indent_spaces, 4)
179 self.assertEqual(isp.indent_spaces, 4)
180 # Blank lines shouldn't change the indent level
180 # Blank lines shouldn't change the indent level
181 isp.push(' '*2)
181 isp.push(' '*2)
182 self.assertEqual(isp.indent_spaces, 4)
182 self.assertEqual(isp.indent_spaces, 4)
183
183
184 def test_indent3(self):
184 def test_indent3(self):
185 # In cell mode, inputs must be fed in whole blocks, so skip this test
185 # In cell mode, inputs must be fed in whole blocks, so skip this test
186 if self.isp.input_mode == 'cell': return
186 if self.isp.input_mode == 'cell': return
187
187
188 isp = self.isp
188 isp = self.isp
189 # When a multiline statement contains parens or multiline strings, we
189 # When a multiline statement contains parens or multiline strings, we
190 # shouldn't get confused.
190 # shouldn't get confused.
191 isp.push("if 1:")
191 isp.push("if 1:")
192 isp.push(" x = (1+\n 2)")
192 isp.push(" x = (1+\n 2)")
193 self.assertEqual(isp.indent_spaces, 4)
193 self.assertEqual(isp.indent_spaces, 4)
194
194
195 def test_dedent_pass(self):
195 def test_dedent_pass(self):
196 isp = self.isp # shorthand
196 isp = self.isp # shorthand
197 # should NOT cause dedent
197 # should NOT cause dedent
198 isp.push('if 1:\n passes = 5')
198 isp.push('if 1:\n passes = 5')
199 self.assertEqual(isp.indent_spaces, 4)
199 self.assertEqual(isp.indent_spaces, 4)
200 isp.push('if 1:\n pass')
200 isp.push('if 1:\n pass')
201 self.assertEqual(isp.indent_spaces, 0)
201 self.assertEqual(isp.indent_spaces, 0)
202 isp.push('if 1:\n pass ')
202 isp.push('if 1:\n pass ')
203 self.assertEqual(isp.indent_spaces, 0)
203 self.assertEqual(isp.indent_spaces, 0)
204
204
205 def test_dedent_raise(self):
205 def test_dedent_raise(self):
206 isp = self.isp # shorthand
206 isp = self.isp # shorthand
207 # should NOT cause dedent
207 # should NOT cause dedent
208 isp.push('if 1:\n raised = 4')
208 isp.push('if 1:\n raised = 4')
209 self.assertEqual(isp.indent_spaces, 4)
209 self.assertEqual(isp.indent_spaces, 4)
210 isp.push('if 1:\n raise TypeError()')
210 isp.push('if 1:\n raise TypeError()')
211 self.assertEqual(isp.indent_spaces, 0)
211 self.assertEqual(isp.indent_spaces, 0)
212 isp.push('if 1:\n raise')
212 isp.push('if 1:\n raise')
213 self.assertEqual(isp.indent_spaces, 0)
213 self.assertEqual(isp.indent_spaces, 0)
214 isp.push('if 1:\n raise ')
214 isp.push('if 1:\n raise ')
215 self.assertEqual(isp.indent_spaces, 0)
215 self.assertEqual(isp.indent_spaces, 0)
216
216
217 def test_dedent_return(self):
217 def test_dedent_return(self):
218 isp = self.isp # shorthand
218 isp = self.isp # shorthand
219 # should NOT cause dedent
219 # should NOT cause dedent
220 isp.push('if 1:\n returning = 4')
220 isp.push('if 1:\n returning = 4')
221 self.assertEqual(isp.indent_spaces, 4)
221 self.assertEqual(isp.indent_spaces, 4)
222 isp.push('if 1:\n return 5 + 493')
222 isp.push('if 1:\n return 5 + 493')
223 self.assertEqual(isp.indent_spaces, 0)
223 self.assertEqual(isp.indent_spaces, 0)
224 isp.push('if 1:\n return')
224 isp.push('if 1:\n return')
225 self.assertEqual(isp.indent_spaces, 0)
225 self.assertEqual(isp.indent_spaces, 0)
226 isp.push('if 1:\n return ')
226 isp.push('if 1:\n return ')
227 self.assertEqual(isp.indent_spaces, 0)
227 self.assertEqual(isp.indent_spaces, 0)
228 isp.push('if 1:\n return(0)')
228 isp.push('if 1:\n return(0)')
229 self.assertEqual(isp.indent_spaces, 0)
229 self.assertEqual(isp.indent_spaces, 0)
230
230
231 def test_push(self):
231 def test_push(self):
232 isp = self.isp
232 isp = self.isp
233 self.assertTrue(isp.push('x=1'))
233 self.assertTrue(isp.push('x=1'))
234
234
235 def test_push2(self):
235 def test_push2(self):
236 isp = self.isp
236 isp = self.isp
237 self.assertFalse(isp.push('if 1:'))
237 self.assertFalse(isp.push('if 1:'))
238 for line in [' x=1', '# a comment', ' y=2']:
238 for line in [' x=1', '# a comment', ' y=2']:
239 self.assertTrue(isp.push(line))
239 self.assertTrue(isp.push(line))
240
240
241 def test_push3(self):
242 isp = self.isp
243 isp.push('if True:')
244 isp.push(' a = 1')
245 self.assertFalse(isp.push('b = [1,'))
246
241 def test_replace_mode(self):
247 def test_replace_mode(self):
242 isp = self.isp
248 isp = self.isp
243 isp.input_mode = 'cell'
249 isp.input_mode = 'cell'
244 isp.push('x=1')
250 isp.push('x=1')
245 self.assertEqual(isp.source, 'x=1\n')
251 self.assertEqual(isp.source, 'x=1\n')
246 isp.push('x=2')
252 isp.push('x=2')
247 self.assertEqual(isp.source, 'x=2\n')
253 self.assertEqual(isp.source, 'x=2\n')
248
254
249 def test_push_accepts_more(self):
255 def test_push_accepts_more(self):
250 isp = self.isp
256 isp = self.isp
251 isp.push('x=1')
257 isp.push('x=1')
252 self.assertFalse(isp.push_accepts_more())
258 self.assertFalse(isp.push_accepts_more())
253
259
254 def test_push_accepts_more2(self):
260 def test_push_accepts_more2(self):
255 # In cell mode, inputs must be fed in whole blocks, so skip this test
261 # In cell mode, inputs must be fed in whole blocks, so skip this test
256 if self.isp.input_mode == 'cell': return
262 if self.isp.input_mode == 'cell': return
257
263
258 isp = self.isp
264 isp = self.isp
259 isp.push('if 1:')
265 isp.push('if 1:')
260 self.assertTrue(isp.push_accepts_more())
266 self.assertTrue(isp.push_accepts_more())
261 isp.push(' x=1')
267 isp.push(' x=1')
262 self.assertTrue(isp.push_accepts_more())
268 self.assertTrue(isp.push_accepts_more())
263 isp.push('')
269 isp.push('')
264 self.assertFalse(isp.push_accepts_more())
270 self.assertFalse(isp.push_accepts_more())
265
271
266 def test_push_accepts_more3(self):
272 def test_push_accepts_more3(self):
267 isp = self.isp
273 isp = self.isp
268 isp.push("x = (2+\n3)")
274 isp.push("x = (2+\n3)")
269 self.assertFalse(isp.push_accepts_more())
275 self.assertFalse(isp.push_accepts_more())
270
276
271 def test_push_accepts_more4(self):
277 def test_push_accepts_more4(self):
272 # In cell mode, inputs must be fed in whole blocks, so skip this test
278 # In cell mode, inputs must be fed in whole blocks, so skip this test
273 if self.isp.input_mode == 'cell': return
279 if self.isp.input_mode == 'cell': return
274
280
275 isp = self.isp
281 isp = self.isp
276 # When a multiline statement contains parens or multiline strings, we
282 # When a multiline statement contains parens or multiline strings, we
277 # shouldn't get confused.
283 # shouldn't get confused.
278 # FIXME: we should be able to better handle de-dents in statements like
284 # FIXME: we should be able to better handle de-dents in statements like
279 # multiline strings and multiline expressions (continued with \ or
285 # multiline strings and multiline expressions (continued with \ or
280 # parens). Right now we aren't handling the indentation tracking quite
286 # parens). Right now we aren't handling the indentation tracking quite
281 # correctly with this, though in practice it may not be too much of a
287 # correctly with this, though in practice it may not be too much of a
282 # problem. We'll need to see.
288 # problem. We'll need to see.
283 isp.push("if 1:")
289 isp.push("if 1:")
284 isp.push(" x = (2+")
290 isp.push(" x = (2+")
285 isp.push(" 3)")
291 isp.push(" 3)")
286 self.assertTrue(isp.push_accepts_more())
292 self.assertTrue(isp.push_accepts_more())
287 isp.push(" y = 3")
293 isp.push(" y = 3")
288 self.assertTrue(isp.push_accepts_more())
294 self.assertTrue(isp.push_accepts_more())
289 isp.push('')
295 isp.push('')
290 self.assertFalse(isp.push_accepts_more())
296 self.assertFalse(isp.push_accepts_more())
291
297
292 def test_push_accepts_more5(self):
298 def test_push_accepts_more5(self):
293 # In cell mode, inputs must be fed in whole blocks, so skip this test
299 # In cell mode, inputs must be fed in whole blocks, so skip this test
294 if self.isp.input_mode == 'cell': return
300 if self.isp.input_mode == 'cell': return
295
301
296 isp = self.isp
302 isp = self.isp
297 isp.push('try:')
303 isp.push('try:')
298 isp.push(' a = 5')
304 isp.push(' a = 5')
299 isp.push('except:')
305 isp.push('except:')
300 isp.push(' raise')
306 isp.push(' raise')
301 self.assertTrue(isp.push_accepts_more())
307 self.assertTrue(isp.push_accepts_more())
302
308
303 def test_continuation(self):
309 def test_continuation(self):
304 isp = self.isp
310 isp = self.isp
305 isp.push("import os, \\")
311 isp.push("import os, \\")
306 self.assertTrue(isp.push_accepts_more())
312 self.assertTrue(isp.push_accepts_more())
307 isp.push("sys")
313 isp.push("sys")
308 self.assertFalse(isp.push_accepts_more())
314 self.assertFalse(isp.push_accepts_more())
309
315
310 def test_syntax_error(self):
316 def test_syntax_error(self):
311 isp = self.isp
317 isp = self.isp
312 # Syntax errors immediately produce a 'ready' block, so the invalid
318 # Syntax errors immediately produce a 'ready' block, so the invalid
313 # Python can be sent to the kernel for evaluation with possible ipython
319 # Python can be sent to the kernel for evaluation with possible ipython
314 # special-syntax conversion.
320 # special-syntax conversion.
315 isp.push('run foo')
321 isp.push('run foo')
316 self.assertFalse(isp.push_accepts_more())
322 self.assertFalse(isp.push_accepts_more())
317
323
318 def test_unicode(self):
324 def test_unicode(self):
319 self.isp.push(u"PΓ©rez")
325 self.isp.push(u"PΓ©rez")
320 self.isp.push(u'\xc3\xa9')
326 self.isp.push(u'\xc3\xa9')
321 self.isp.push(u"u'\xc3\xa9'")
327 self.isp.push(u"u'\xc3\xa9'")
322
328
323 class InteractiveLoopTestCase(unittest.TestCase):
329 class InteractiveLoopTestCase(unittest.TestCase):
324 """Tests for an interactive loop like a python shell.
330 """Tests for an interactive loop like a python shell.
325 """
331 """
326 def check_ns(self, lines, ns):
332 def check_ns(self, lines, ns):
327 """Validate that the given input lines produce the resulting namespace.
333 """Validate that the given input lines produce the resulting namespace.
328
334
329 Note: the input lines are given exactly as they would be typed in an
335 Note: the input lines are given exactly as they would be typed in an
330 auto-indenting environment, as mini_interactive_loop above already does
336 auto-indenting environment, as mini_interactive_loop above already does
331 auto-indenting and prepends spaces to the input.
337 auto-indenting and prepends spaces to the input.
332 """
338 """
333 src = mini_interactive_loop(pseudo_input(lines))
339 src = mini_interactive_loop(pseudo_input(lines))
334 test_ns = {}
340 test_ns = {}
335 exec src in test_ns
341 exec src in test_ns
336 # We can't check that the provided ns is identical to the test_ns,
342 # We can't check that the provided ns is identical to the test_ns,
337 # because Python fills test_ns with extra keys (copyright, etc). But
343 # because Python fills test_ns with extra keys (copyright, etc). But
338 # we can check that the given dict is *contained* in test_ns
344 # we can check that the given dict is *contained* in test_ns
339 for k,v in ns.iteritems():
345 for k,v in ns.iteritems():
340 self.assertEqual(test_ns[k], v)
346 self.assertEqual(test_ns[k], v)
341
347
342 def test_simple(self):
348 def test_simple(self):
343 self.check_ns(['x=1'], dict(x=1))
349 self.check_ns(['x=1'], dict(x=1))
344
350
345 def test_simple2(self):
351 def test_simple2(self):
346 self.check_ns(['if 1:', 'x=2'], dict(x=2))
352 self.check_ns(['if 1:', 'x=2'], dict(x=2))
347
353
348 def test_xy(self):
354 def test_xy(self):
349 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
355 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
350
356
351 def test_abc(self):
357 def test_abc(self):
352 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
358 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
353
359
354 def test_multi(self):
360 def test_multi(self):
355 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
361 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
356
362
357
363
358 def test_LineInfo():
364 def test_LineInfo():
359 """Simple test for LineInfo construction and str()"""
365 """Simple test for LineInfo construction and str()"""
360 linfo = isp.LineInfo(' %cd /home')
366 linfo = isp.LineInfo(' %cd /home')
361 nt.assert_equals(str(linfo), 'LineInfo [ |%|cd|/home]')
367 nt.assert_equals(str(linfo), 'LineInfo [ |%|cd|/home]')
362
368
363
369
364 def test_split_user_input():
370 def test_split_user_input():
365 """Unicode test - split_user_input already has good doctests"""
371 """Unicode test - split_user_input already has good doctests"""
366 line = u"PΓ©rez Fernando"
372 line = u"PΓ©rez Fernando"
367 parts = isp.split_user_input(line)
373 parts = isp.split_user_input(line)
368 parts_expected = (u'', u'', u'', line)
374 parts_expected = (u'', u'', u'', line)
369 nt.assert_equal(parts, parts_expected)
375 nt.assert_equal(parts, parts_expected)
370
376
371
377
372 # Transformer tests
378 # Transformer tests
373 def transform_checker(tests, func):
379 def transform_checker(tests, func):
374 """Utility to loop over test inputs"""
380 """Utility to loop over test inputs"""
375 for inp, tr in tests:
381 for inp, tr in tests:
376 nt.assert_equals(func(inp), tr)
382 nt.assert_equals(func(inp), tr)
377
383
378 # Data for all the syntax tests in the form of lists of pairs of
384 # Data for all the syntax tests in the form of lists of pairs of
379 # raw/transformed input. We store it here as a global dict so that we can use
385 # raw/transformed input. We store it here as a global dict so that we can use
380 # it both within single-function tests and also to validate the behavior of the
386 # it both within single-function tests and also to validate the behavior of the
381 # larger objects
387 # larger objects
382
388
383 syntax = \
389 syntax = \
384 dict(assign_system =
390 dict(assign_system =
385 [('a =! ls', 'a = get_ipython().getoutput(u"ls")'),
391 [('a =! ls', 'a = get_ipython().getoutput(u"ls")'),
386 ('b = !ls', 'b = get_ipython().getoutput(u"ls")'),
392 ('b = !ls', 'b = get_ipython().getoutput(u"ls")'),
387 ('x=1', 'x=1'), # normal input is unmodified
393 ('x=1', 'x=1'), # normal input is unmodified
388 (' ',' '), # blank lines are kept intact
394 (' ',' '), # blank lines are kept intact
389 ],
395 ],
390
396
391 assign_magic =
397 assign_magic =
392 [('a =% who', 'a = get_ipython().magic(u"who")'),
398 [('a =% who', 'a = get_ipython().magic(u"who")'),
393 ('b = %who', 'b = get_ipython().magic(u"who")'),
399 ('b = %who', 'b = get_ipython().magic(u"who")'),
394 ('x=1', 'x=1'), # normal input is unmodified
400 ('x=1', 'x=1'), # normal input is unmodified
395 (' ',' '), # blank lines are kept intact
401 (' ',' '), # blank lines are kept intact
396 ],
402 ],
397
403
398 classic_prompt =
404 classic_prompt =
399 [('>>> x=1', 'x=1'),
405 [('>>> x=1', 'x=1'),
400 ('x=1', 'x=1'), # normal input is unmodified
406 ('x=1', 'x=1'), # normal input is unmodified
401 (' ', ' '), # blank lines are kept intact
407 (' ', ' '), # blank lines are kept intact
402 ('... ', ''), # continuation prompts
408 ('... ', ''), # continuation prompts
403 ],
409 ],
404
410
405 ipy_prompt =
411 ipy_prompt =
406 [('In [1]: x=1', 'x=1'),
412 [('In [1]: x=1', 'x=1'),
407 ('x=1', 'x=1'), # normal input is unmodified
413 ('x=1', 'x=1'), # normal input is unmodified
408 (' ',' '), # blank lines are kept intact
414 (' ',' '), # blank lines are kept intact
409 (' ....: ', ''), # continuation prompts
415 (' ....: ', ''), # continuation prompts
410 ],
416 ],
411
417
412 # Tests for the escape transformer to leave normal code alone
418 # Tests for the escape transformer to leave normal code alone
413 escaped_noesc =
419 escaped_noesc =
414 [ (' ', ' '),
420 [ (' ', ' '),
415 ('x=1', 'x=1'),
421 ('x=1', 'x=1'),
416 ],
422 ],
417
423
418 # System calls
424 # System calls
419 escaped_shell =
425 escaped_shell =
420 [ ('!ls', 'get_ipython().system(u"ls")'),
426 [ ('!ls', 'get_ipython().system(u"ls")'),
421 # Double-escape shell, this means to capture the output of the
427 # Double-escape shell, this means to capture the output of the
422 # subprocess and return it
428 # subprocess and return it
423 ('!!ls', 'get_ipython().getoutput(u"ls")'),
429 ('!!ls', 'get_ipython().getoutput(u"ls")'),
424 ],
430 ],
425
431
426 # Help/object info
432 # Help/object info
427 escaped_help =
433 escaped_help =
428 [ ('?', 'get_ipython().show_usage()'),
434 [ ('?', 'get_ipython().show_usage()'),
429 ('?x1', 'get_ipython().magic(u"pinfo x1")'),
435 ('?x1', 'get_ipython().magic(u"pinfo x1")'),
430 ('??x2', 'get_ipython().magic(u"pinfo2 x2")'),
436 ('??x2', 'get_ipython().magic(u"pinfo2 x2")'),
431 ('x3?', 'get_ipython().magic(u"pinfo x3")'),
437 ('x3?', 'get_ipython().magic(u"pinfo x3")'),
432 ('x4??', 'get_ipython().magic(u"pinfo2 x4")'),
438 ('x4??', 'get_ipython().magic(u"pinfo2 x4")'),
433 ('%hist?', 'get_ipython().magic(u"pinfo %hist")'),
439 ('%hist?', 'get_ipython().magic(u"pinfo %hist")'),
434 ('f*?', 'get_ipython().magic(u"psearch f*")'),
440 ('f*?', 'get_ipython().magic(u"psearch f*")'),
435 ('ax.*aspe*?', 'get_ipython().magic(u"psearch ax.*aspe*")'),
441 ('ax.*aspe*?', 'get_ipython().magic(u"psearch ax.*aspe*")'),
436 ],
442 ],
437
443
438 # Explicit magic calls
444 # Explicit magic calls
439 escaped_magic =
445 escaped_magic =
440 [ ('%cd', 'get_ipython().magic(u"cd")'),
446 [ ('%cd', 'get_ipython().magic(u"cd")'),
441 ('%cd /home', 'get_ipython().magic(u"cd /home")'),
447 ('%cd /home', 'get_ipython().magic(u"cd /home")'),
442 (' %magic', ' get_ipython().magic(u"magic")'),
448 (' %magic', ' get_ipython().magic(u"magic")'),
443 ],
449 ],
444
450
445 # Quoting with separate arguments
451 # Quoting with separate arguments
446 escaped_quote =
452 escaped_quote =
447 [ (',f', 'f("")'),
453 [ (',f', 'f("")'),
448 (',f x', 'f("x")'),
454 (',f x', 'f("x")'),
449 (' ,f y', ' f("y")'),
455 (' ,f y', ' f("y")'),
450 (',f a b', 'f("a", "b")'),
456 (',f a b', 'f("a", "b")'),
451 ],
457 ],
452
458
453 # Quoting with single argument
459 # Quoting with single argument
454 escaped_quote2 =
460 escaped_quote2 =
455 [ (';f', 'f("")'),
461 [ (';f', 'f("")'),
456 (';f x', 'f("x")'),
462 (';f x', 'f("x")'),
457 (' ;f y', ' f("y")'),
463 (' ;f y', ' f("y")'),
458 (';f a b', 'f("a b")'),
464 (';f a b', 'f("a b")'),
459 ],
465 ],
460
466
461 # Simply apply parens
467 # Simply apply parens
462 escaped_paren =
468 escaped_paren =
463 [ ('/f', 'f()'),
469 [ ('/f', 'f()'),
464 ('/f x', 'f(x)'),
470 ('/f x', 'f(x)'),
465 (' /f y', ' f(y)'),
471 (' /f y', ' f(y)'),
466 ('/f a b', 'f(a, b)'),
472 ('/f a b', 'f(a, b)'),
467 ],
473 ],
468
474
469 )
475 )
470
476
471 # multiline syntax examples. Each of these should be a list of lists, with
477 # multiline syntax examples. Each of these should be a list of lists, with
472 # each entry itself having pairs of raw/transformed input. The union (with
478 # each entry itself having pairs of raw/transformed input. The union (with
473 # '\n'.join() of the transformed inputs is what the splitter should produce
479 # '\n'.join() of the transformed inputs is what the splitter should produce
474 # when fed the raw lines one at a time via push.
480 # when fed the raw lines one at a time via push.
475 syntax_ml = \
481 syntax_ml = \
476 dict(classic_prompt =
482 dict(classic_prompt =
477 [ [('>>> for i in range(10):','for i in range(10):'),
483 [ [('>>> for i in range(10):','for i in range(10):'),
478 ('... print i',' print i'),
484 ('... print i',' print i'),
479 ('... ', ''),
485 ('... ', ''),
480 ],
486 ],
481 ],
487 ],
482
488
483 ipy_prompt =
489 ipy_prompt =
484 [ [('In [24]: for i in range(10):','for i in range(10):'),
490 [ [('In [24]: for i in range(10):','for i in range(10):'),
485 (' ....: print i',' print i'),
491 (' ....: print i',' print i'),
486 (' ....: ', ''),
492 (' ....: ', ''),
487 ],
493 ],
488 ],
494 ],
489 )
495 )
490
496
491
497
492 def test_assign_system():
498 def test_assign_system():
493 transform_checker(syntax['assign_system'], isp.transform_assign_system)
499 transform_checker(syntax['assign_system'], isp.transform_assign_system)
494
500
495
501
496 def test_assign_magic():
502 def test_assign_magic():
497 transform_checker(syntax['assign_magic'], isp.transform_assign_magic)
503 transform_checker(syntax['assign_magic'], isp.transform_assign_magic)
498
504
499
505
500 def test_classic_prompt():
506 def test_classic_prompt():
501 transform_checker(syntax['classic_prompt'], isp.transform_classic_prompt)
507 transform_checker(syntax['classic_prompt'], isp.transform_classic_prompt)
502 for example in syntax_ml['classic_prompt']:
508 for example in syntax_ml['classic_prompt']:
503 transform_checker(example, isp.transform_classic_prompt)
509 transform_checker(example, isp.transform_classic_prompt)
504
510
505
511
506 def test_ipy_prompt():
512 def test_ipy_prompt():
507 transform_checker(syntax['ipy_prompt'], isp.transform_ipy_prompt)
513 transform_checker(syntax['ipy_prompt'], isp.transform_ipy_prompt)
508 for example in syntax_ml['ipy_prompt']:
514 for example in syntax_ml['ipy_prompt']:
509 transform_checker(example, isp.transform_ipy_prompt)
515 transform_checker(example, isp.transform_ipy_prompt)
510
516
511
517
512 def test_escaped_noesc():
518 def test_escaped_noesc():
513 transform_checker(syntax['escaped_noesc'], isp.transform_escaped)
519 transform_checker(syntax['escaped_noesc'], isp.transform_escaped)
514
520
515
521
516 def test_escaped_shell():
522 def test_escaped_shell():
517 transform_checker(syntax['escaped_shell'], isp.transform_escaped)
523 transform_checker(syntax['escaped_shell'], isp.transform_escaped)
518
524
519
525
520 def test_escaped_help():
526 def test_escaped_help():
521 transform_checker(syntax['escaped_help'], isp.transform_escaped)
527 transform_checker(syntax['escaped_help'], isp.transform_escaped)
522
528
523
529
524 def test_escaped_magic():
530 def test_escaped_magic():
525 transform_checker(syntax['escaped_magic'], isp.transform_escaped)
531 transform_checker(syntax['escaped_magic'], isp.transform_escaped)
526
532
527
533
528 def test_escaped_quote():
534 def test_escaped_quote():
529 transform_checker(syntax['escaped_quote'], isp.transform_escaped)
535 transform_checker(syntax['escaped_quote'], isp.transform_escaped)
530
536
531
537
532 def test_escaped_quote2():
538 def test_escaped_quote2():
533 transform_checker(syntax['escaped_quote2'], isp.transform_escaped)
539 transform_checker(syntax['escaped_quote2'], isp.transform_escaped)
534
540
535
541
536 def test_escaped_paren():
542 def test_escaped_paren():
537 transform_checker(syntax['escaped_paren'], isp.transform_escaped)
543 transform_checker(syntax['escaped_paren'], isp.transform_escaped)
538
544
539
545
540 class IPythonInputTestCase(InputSplitterTestCase):
546 class IPythonInputTestCase(InputSplitterTestCase):
541 """By just creating a new class whose .isp is a different instance, we
547 """By just creating a new class whose .isp is a different instance, we
542 re-run the same test battery on the new input splitter.
548 re-run the same test battery on the new input splitter.
543
549
544 In addition, this runs the tests over the syntax and syntax_ml dicts that
550 In addition, this runs the tests over the syntax and syntax_ml dicts that
545 were tested by individual functions, as part of the OO interface.
551 were tested by individual functions, as part of the OO interface.
546
552
547 It also makes some checks on the raw buffer storage.
553 It also makes some checks on the raw buffer storage.
548 """
554 """
549
555
550 def setUp(self):
556 def setUp(self):
551 self.isp = isp.IPythonInputSplitter(input_mode='line')
557 self.isp = isp.IPythonInputSplitter(input_mode='line')
552
558
553 def test_syntax(self):
559 def test_syntax(self):
554 """Call all single-line syntax tests from the main object"""
560 """Call all single-line syntax tests from the main object"""
555 isp = self.isp
561 isp = self.isp
556 for example in syntax.itervalues():
562 for example in syntax.itervalues():
557 for raw, out_t in example:
563 for raw, out_t in example:
558 if raw.startswith(' '):
564 if raw.startswith(' '):
559 continue
565 continue
560
566
561 isp.push(raw)
567 isp.push(raw)
562 out, out_raw = isp.source_raw_reset()
568 out, out_raw = isp.source_raw_reset()
563 self.assertEqual(out.rstrip(), out_t)
569 self.assertEqual(out.rstrip(), out_t)
564 self.assertEqual(out_raw.rstrip(), raw.rstrip())
570 self.assertEqual(out_raw.rstrip(), raw.rstrip())
565
571
566 def test_syntax_multiline(self):
572 def test_syntax_multiline(self):
567 isp = self.isp
573 isp = self.isp
568 for example in syntax_ml.itervalues():
574 for example in syntax_ml.itervalues():
569 out_t_parts = []
575 out_t_parts = []
570 raw_parts = []
576 raw_parts = []
571 for line_pairs in example:
577 for line_pairs in example:
572 for lraw, out_t_part in line_pairs:
578 for lraw, out_t_part in line_pairs:
573 isp.push(lraw)
579 isp.push(lraw)
574 out_t_parts.append(out_t_part)
580 out_t_parts.append(out_t_part)
575 raw_parts.append(lraw)
581 raw_parts.append(lraw)
576
582
577 out, out_raw = isp.source_raw_reset()
583 out, out_raw = isp.source_raw_reset()
578 out_t = '\n'.join(out_t_parts).rstrip()
584 out_t = '\n'.join(out_t_parts).rstrip()
579 raw = '\n'.join(raw_parts).rstrip()
585 raw = '\n'.join(raw_parts).rstrip()
580 self.assertEqual(out.rstrip(), out_t)
586 self.assertEqual(out.rstrip(), out_t)
581 self.assertEqual(out_raw.rstrip(), raw)
587 self.assertEqual(out_raw.rstrip(), raw)
582
588
583
589
584 class BlockIPythonInputTestCase(IPythonInputTestCase):
590 class BlockIPythonInputTestCase(IPythonInputTestCase):
585
591
586 # Deactivate tests that don't make sense for the block mode
592 # Deactivate tests that don't make sense for the block mode
587 test_push3 = test_split = lambda s: None
593 test_push3 = test_split = lambda s: None
588
594
589 def setUp(self):
595 def setUp(self):
590 self.isp = isp.IPythonInputSplitter(input_mode='cell')
596 self.isp = isp.IPythonInputSplitter(input_mode='cell')
591
597
592 def test_syntax_multiline(self):
598 def test_syntax_multiline(self):
593 isp = self.isp
599 isp = self.isp
594 for example in syntax_ml.itervalues():
600 for example in syntax_ml.itervalues():
595 raw_parts = []
601 raw_parts = []
596 out_t_parts = []
602 out_t_parts = []
597 for line_pairs in example:
603 for line_pairs in example:
598 for raw, out_t_part in line_pairs:
604 for raw, out_t_part in line_pairs:
599 raw_parts.append(raw)
605 raw_parts.append(raw)
600 out_t_parts.append(out_t_part)
606 out_t_parts.append(out_t_part)
601
607
602 raw = '\n'.join(raw_parts)
608 raw = '\n'.join(raw_parts)
603 out_t = '\n'.join(out_t_parts)
609 out_t = '\n'.join(out_t_parts)
604
610
605 isp.push(raw)
611 isp.push(raw)
606 out, out_raw = isp.source_raw_reset()
612 out, out_raw = isp.source_raw_reset()
607 # Match ignoring trailing whitespace
613 # Match ignoring trailing whitespace
608 self.assertEqual(out.rstrip(), out_t.rstrip())
614 self.assertEqual(out.rstrip(), out_t.rstrip())
609 self.assertEqual(out_raw.rstrip(), raw.rstrip())
615 self.assertEqual(out_raw.rstrip(), raw.rstrip())
610
616
611
617
612 #-----------------------------------------------------------------------------
618 #-----------------------------------------------------------------------------
613 # Main - use as a script, mostly for developer experiments
619 # Main - use as a script, mostly for developer experiments
614 #-----------------------------------------------------------------------------
620 #-----------------------------------------------------------------------------
615
621
616 if __name__ == '__main__':
622 if __name__ == '__main__':
617 # A simple demo for interactive experimentation. This code will not get
623 # A simple demo for interactive experimentation. This code will not get
618 # picked up by any test suite.
624 # picked up by any test suite.
619 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
625 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
620
626
621 # configure here the syntax to use, prompt and whether to autoindent
627 # configure here the syntax to use, prompt and whether to autoindent
622 #isp, start_prompt = InputSplitter(), '>>> '
628 #isp, start_prompt = InputSplitter(), '>>> '
623 isp, start_prompt = IPythonInputSplitter(), 'In> '
629 isp, start_prompt = IPythonInputSplitter(), 'In> '
624
630
625 autoindent = True
631 autoindent = True
626 #autoindent = False
632 #autoindent = False
627
633
628 try:
634 try:
629 while True:
635 while True:
630 prompt = start_prompt
636 prompt = start_prompt
631 while isp.push_accepts_more():
637 while isp.push_accepts_more():
632 indent = ' '*isp.indent_spaces
638 indent = ' '*isp.indent_spaces
633 if autoindent:
639 if autoindent:
634 line = indent + raw_input(prompt+indent)
640 line = indent + raw_input(prompt+indent)
635 else:
641 else:
636 line = raw_input(prompt)
642 line = raw_input(prompt)
637 isp.push(line)
643 isp.push(line)
638 prompt = '... '
644 prompt = '... '
639
645
640 # Here we just return input so we can use it in a test suite, but a
646 # Here we just return input so we can use it in a test suite, but a
641 # real interpreter would instead send it for execution somewhere.
647 # real interpreter would instead send it for execution somewhere.
642 #src = isp.source; raise EOFError # dbg
648 #src = isp.source; raise EOFError # dbg
643 src, raw = isp.source_raw_reset()
649 src, raw = isp.source_raw_reset()
644 print 'Input source was:\n', src
650 print 'Input source was:\n', src
645 print 'Raw source was:\n', raw
651 print 'Raw source was:\n', raw
646 except EOFError:
652 except EOFError:
647 print 'Bye'
653 print 'Bye'
General Comments 0
You need to be logged in to leave comments. Login now