##// END OF EJS Templates
New tests related to issue #142.
David Warde-Farley -
Show More
@@ -1,618 +1,647 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(self):
195 def test_dedent_pass(self):
196 isp = self.isp # shorthand
196 isp = self.isp # shorthand
197 isp.push('if 1:')
197 # should NOT cause dedent
198 isp.push('if 1:\n passes = 5')
199 self.assertEqual(isp.indent_spaces, 4)
200 isp.push('if 1:\n pass')
201 self.assertEqual(isp.indent_spaces, 0)
202 isp.push('if 1:\n pass ')
203 self.assertEqual(isp.indent_spaces, 0)
204
205 def test_dedent_raise(self):
206 isp = self.isp # shorthand
207 # should NOT cause dedent
208 isp.push('if 1:\n raised = 4')
198 self.assertEqual(isp.indent_spaces, 4)
209 self.assertEqual(isp.indent_spaces, 4)
199 isp.push(' pass')
210 isp.push('if 1:\n raise TypeError()')
211 self.assertEqual(isp.indent_spaces, 0)
212 isp.push('if 1:\n raise')
213 self.assertEqual(isp.indent_spaces, 0)
214 isp.push('if 1:\n raise ')
215 self.assertEqual(isp.indent_spaces, 0)
216
217 def test_dedent_return(self):
218 isp = self.isp # shorthand
219 # should NOT cause dedent
220 isp.push('if 1:\n returning = 4')
221 self.assertEqual(isp.indent_spaces, 4)
222 isp.push('if 1:\n return 5 + 493')
223 self.assertEqual(isp.indent_spaces, 0)
224 isp.push('if 1:\n return')
225 self.assertEqual(isp.indent_spaces, 0)
226 isp.push('if 1:\n return ')
227 self.assertEqual(isp.indent_spaces, 0)
228 isp.push('if 1:\n return(0)')
200 self.assertEqual(isp.indent_spaces, 0)
229 self.assertEqual(isp.indent_spaces, 0)
201
230
202 def test_push(self):
231 def test_push(self):
203 isp = self.isp
232 isp = self.isp
204 self.assertTrue(isp.push('x=1'))
233 self.assertTrue(isp.push('x=1'))
205
234
206 def test_push2(self):
235 def test_push2(self):
207 isp = self.isp
236 isp = self.isp
208 self.assertFalse(isp.push('if 1:'))
237 self.assertFalse(isp.push('if 1:'))
209 for line in [' x=1', '# a comment', ' y=2']:
238 for line in [' x=1', '# a comment', ' y=2']:
210 self.assertTrue(isp.push(line))
239 self.assertTrue(isp.push(line))
211
240
212 def test_replace_mode(self):
241 def test_replace_mode(self):
213 isp = self.isp
242 isp = self.isp
214 isp.input_mode = 'cell'
243 isp.input_mode = 'cell'
215 isp.push('x=1')
244 isp.push('x=1')
216 self.assertEqual(isp.source, 'x=1\n')
245 self.assertEqual(isp.source, 'x=1\n')
217 isp.push('x=2')
246 isp.push('x=2')
218 self.assertEqual(isp.source, 'x=2\n')
247 self.assertEqual(isp.source, 'x=2\n')
219
248
220 def test_push_accepts_more(self):
249 def test_push_accepts_more(self):
221 isp = self.isp
250 isp = self.isp
222 isp.push('x=1')
251 isp.push('x=1')
223 self.assertFalse(isp.push_accepts_more())
252 self.assertFalse(isp.push_accepts_more())
224
253
225 def test_push_accepts_more2(self):
254 def test_push_accepts_more2(self):
226 # In cell mode, inputs must be fed in whole blocks, so skip this test
255 # In cell mode, inputs must be fed in whole blocks, so skip this test
227 if self.isp.input_mode == 'cell': return
256 if self.isp.input_mode == 'cell': return
228
257
229 isp = self.isp
258 isp = self.isp
230 isp.push('if 1:')
259 isp.push('if 1:')
231 self.assertTrue(isp.push_accepts_more())
260 self.assertTrue(isp.push_accepts_more())
232 isp.push(' x=1')
261 isp.push(' x=1')
233 self.assertTrue(isp.push_accepts_more())
262 self.assertTrue(isp.push_accepts_more())
234 isp.push('')
263 isp.push('')
235 self.assertFalse(isp.push_accepts_more())
264 self.assertFalse(isp.push_accepts_more())
236
265
237 def test_push_accepts_more3(self):
266 def test_push_accepts_more3(self):
238 isp = self.isp
267 isp = self.isp
239 isp.push("x = (2+\n3)")
268 isp.push("x = (2+\n3)")
240 self.assertFalse(isp.push_accepts_more())
269 self.assertFalse(isp.push_accepts_more())
241
270
242 def test_push_accepts_more4(self):
271 def test_push_accepts_more4(self):
243 # In cell mode, inputs must be fed in whole blocks, so skip this test
272 # In cell mode, inputs must be fed in whole blocks, so skip this test
244 if self.isp.input_mode == 'cell': return
273 if self.isp.input_mode == 'cell': return
245
274
246 isp = self.isp
275 isp = self.isp
247 # When a multiline statement contains parens or multiline strings, we
276 # When a multiline statement contains parens or multiline strings, we
248 # shouldn't get confused.
277 # shouldn't get confused.
249 # FIXME: we should be able to better handle de-dents in statements like
278 # FIXME: we should be able to better handle de-dents in statements like
250 # multiline strings and multiline expressions (continued with \ or
279 # multiline strings and multiline expressions (continued with \ or
251 # parens). Right now we aren't handling the indentation tracking quite
280 # parens). Right now we aren't handling the indentation tracking quite
252 # correctly with this, though in practice it may not be too much of a
281 # correctly with this, though in practice it may not be too much of a
253 # problem. We'll need to see.
282 # problem. We'll need to see.
254 isp.push("if 1:")
283 isp.push("if 1:")
255 isp.push(" x = (2+")
284 isp.push(" x = (2+")
256 isp.push(" 3)")
285 isp.push(" 3)")
257 self.assertTrue(isp.push_accepts_more())
286 self.assertTrue(isp.push_accepts_more())
258 isp.push(" y = 3")
287 isp.push(" y = 3")
259 self.assertTrue(isp.push_accepts_more())
288 self.assertTrue(isp.push_accepts_more())
260 isp.push('')
289 isp.push('')
261 self.assertFalse(isp.push_accepts_more())
290 self.assertFalse(isp.push_accepts_more())
262
291
263 def test_push_accepts_more5(self):
292 def test_push_accepts_more5(self):
264 # In cell mode, inputs must be fed in whole blocks, so skip this test
293 # In cell mode, inputs must be fed in whole blocks, so skip this test
265 if self.isp.input_mode == 'cell': return
294 if self.isp.input_mode == 'cell': return
266
295
267 isp = self.isp
296 isp = self.isp
268 isp.push('try:')
297 isp.push('try:')
269 isp.push(' a = 5')
298 isp.push(' a = 5')
270 isp.push('except:')
299 isp.push('except:')
271 isp.push(' raise')
300 isp.push(' raise')
272 self.assertTrue(isp.push_accepts_more())
301 self.assertTrue(isp.push_accepts_more())
273
302
274 def test_continuation(self):
303 def test_continuation(self):
275 isp = self.isp
304 isp = self.isp
276 isp.push("import os, \\")
305 isp.push("import os, \\")
277 self.assertTrue(isp.push_accepts_more())
306 self.assertTrue(isp.push_accepts_more())
278 isp.push("sys")
307 isp.push("sys")
279 self.assertFalse(isp.push_accepts_more())
308 self.assertFalse(isp.push_accepts_more())
280
309
281 def test_syntax_error(self):
310 def test_syntax_error(self):
282 isp = self.isp
311 isp = self.isp
283 # Syntax errors immediately produce a 'ready' block, so the invalid
312 # Syntax errors immediately produce a 'ready' block, so the invalid
284 # Python can be sent to the kernel for evaluation with possible ipython
313 # Python can be sent to the kernel for evaluation with possible ipython
285 # special-syntax conversion.
314 # special-syntax conversion.
286 isp.push('run foo')
315 isp.push('run foo')
287 self.assertFalse(isp.push_accepts_more())
316 self.assertFalse(isp.push_accepts_more())
288
317
289 def test_unicode(self):
318 def test_unicode(self):
290 self.isp.push(u"PΓ©rez")
319 self.isp.push(u"PΓ©rez")
291 self.isp.push(u'\xc3\xa9')
320 self.isp.push(u'\xc3\xa9')
292 self.isp.push(u"u'\xc3\xa9'")
321 self.isp.push(u"u'\xc3\xa9'")
293
322
294 class InteractiveLoopTestCase(unittest.TestCase):
323 class InteractiveLoopTestCase(unittest.TestCase):
295 """Tests for an interactive loop like a python shell.
324 """Tests for an interactive loop like a python shell.
296 """
325 """
297 def check_ns(self, lines, ns):
326 def check_ns(self, lines, ns):
298 """Validate that the given input lines produce the resulting namespace.
327 """Validate that the given input lines produce the resulting namespace.
299
328
300 Note: the input lines are given exactly as they would be typed in an
329 Note: the input lines are given exactly as they would be typed in an
301 auto-indenting environment, as mini_interactive_loop above already does
330 auto-indenting environment, as mini_interactive_loop above already does
302 auto-indenting and prepends spaces to the input.
331 auto-indenting and prepends spaces to the input.
303 """
332 """
304 src = mini_interactive_loop(pseudo_input(lines))
333 src = mini_interactive_loop(pseudo_input(lines))
305 test_ns = {}
334 test_ns = {}
306 exec src in test_ns
335 exec src in test_ns
307 # We can't check that the provided ns is identical to the test_ns,
336 # We can't check that the provided ns is identical to the test_ns,
308 # because Python fills test_ns with extra keys (copyright, etc). But
337 # because Python fills test_ns with extra keys (copyright, etc). But
309 # we can check that the given dict is *contained* in test_ns
338 # we can check that the given dict is *contained* in test_ns
310 for k,v in ns.iteritems():
339 for k,v in ns.iteritems():
311 self.assertEqual(test_ns[k], v)
340 self.assertEqual(test_ns[k], v)
312
341
313 def test_simple(self):
342 def test_simple(self):
314 self.check_ns(['x=1'], dict(x=1))
343 self.check_ns(['x=1'], dict(x=1))
315
344
316 def test_simple2(self):
345 def test_simple2(self):
317 self.check_ns(['if 1:', 'x=2'], dict(x=2))
346 self.check_ns(['if 1:', 'x=2'], dict(x=2))
318
347
319 def test_xy(self):
348 def test_xy(self):
320 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
349 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
321
350
322 def test_abc(self):
351 def test_abc(self):
323 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
352 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
324
353
325 def test_multi(self):
354 def test_multi(self):
326 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
355 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
327
356
328
357
329 def test_LineInfo():
358 def test_LineInfo():
330 """Simple test for LineInfo construction and str()"""
359 """Simple test for LineInfo construction and str()"""
331 linfo = isp.LineInfo(' %cd /home')
360 linfo = isp.LineInfo(' %cd /home')
332 nt.assert_equals(str(linfo), 'LineInfo [ |%|cd|/home]')
361 nt.assert_equals(str(linfo), 'LineInfo [ |%|cd|/home]')
333
362
334
363
335 def test_split_user_input():
364 def test_split_user_input():
336 """Unicode test - split_user_input already has good doctests"""
365 """Unicode test - split_user_input already has good doctests"""
337 line = u"PΓ©rez Fernando"
366 line = u"PΓ©rez Fernando"
338 parts = isp.split_user_input(line)
367 parts = isp.split_user_input(line)
339 parts_expected = (u'', u'', u'', line)
368 parts_expected = (u'', u'', u'', line)
340 nt.assert_equal(parts, parts_expected)
369 nt.assert_equal(parts, parts_expected)
341
370
342
371
343 # Transformer tests
372 # Transformer tests
344 def transform_checker(tests, func):
373 def transform_checker(tests, func):
345 """Utility to loop over test inputs"""
374 """Utility to loop over test inputs"""
346 for inp, tr in tests:
375 for inp, tr in tests:
347 nt.assert_equals(func(inp), tr)
376 nt.assert_equals(func(inp), tr)
348
377
349 # Data for all the syntax tests in the form of lists of pairs of
378 # Data for all the syntax tests in the form of lists of pairs of
350 # raw/transformed input. We store it here as a global dict so that we can use
379 # raw/transformed input. We store it here as a global dict so that we can use
351 # it both within single-function tests and also to validate the behavior of the
380 # it both within single-function tests and also to validate the behavior of the
352 # larger objects
381 # larger objects
353
382
354 syntax = \
383 syntax = \
355 dict(assign_system =
384 dict(assign_system =
356 [('a =! ls', 'a = get_ipython().getoutput(u"ls")'),
385 [('a =! ls', 'a = get_ipython().getoutput(u"ls")'),
357 ('b = !ls', 'b = get_ipython().getoutput(u"ls")'),
386 ('b = !ls', 'b = get_ipython().getoutput(u"ls")'),
358 ('x=1', 'x=1'), # normal input is unmodified
387 ('x=1', 'x=1'), # normal input is unmodified
359 (' ',' '), # blank lines are kept intact
388 (' ',' '), # blank lines are kept intact
360 ],
389 ],
361
390
362 assign_magic =
391 assign_magic =
363 [('a =% who', 'a = get_ipython().magic(u"who")'),
392 [('a =% who', 'a = get_ipython().magic(u"who")'),
364 ('b = %who', 'b = get_ipython().magic(u"who")'),
393 ('b = %who', 'b = get_ipython().magic(u"who")'),
365 ('x=1', 'x=1'), # normal input is unmodified
394 ('x=1', 'x=1'), # normal input is unmodified
366 (' ',' '), # blank lines are kept intact
395 (' ',' '), # blank lines are kept intact
367 ],
396 ],
368
397
369 classic_prompt =
398 classic_prompt =
370 [('>>> x=1', 'x=1'),
399 [('>>> x=1', 'x=1'),
371 ('x=1', 'x=1'), # normal input is unmodified
400 ('x=1', 'x=1'), # normal input is unmodified
372 (' ', ' '), # blank lines are kept intact
401 (' ', ' '), # blank lines are kept intact
373 ('... ', ''), # continuation prompts
402 ('... ', ''), # continuation prompts
374 ],
403 ],
375
404
376 ipy_prompt =
405 ipy_prompt =
377 [('In [1]: x=1', 'x=1'),
406 [('In [1]: x=1', 'x=1'),
378 ('x=1', 'x=1'), # normal input is unmodified
407 ('x=1', 'x=1'), # normal input is unmodified
379 (' ',' '), # blank lines are kept intact
408 (' ',' '), # blank lines are kept intact
380 (' ....: ', ''), # continuation prompts
409 (' ....: ', ''), # continuation prompts
381 ],
410 ],
382
411
383 # Tests for the escape transformer to leave normal code alone
412 # Tests for the escape transformer to leave normal code alone
384 escaped_noesc =
413 escaped_noesc =
385 [ (' ', ' '),
414 [ (' ', ' '),
386 ('x=1', 'x=1'),
415 ('x=1', 'x=1'),
387 ],
416 ],
388
417
389 # System calls
418 # System calls
390 escaped_shell =
419 escaped_shell =
391 [ ('!ls', 'get_ipython().system(u"ls")'),
420 [ ('!ls', 'get_ipython().system(u"ls")'),
392 # Double-escape shell, this means to capture the output of the
421 # Double-escape shell, this means to capture the output of the
393 # subprocess and return it
422 # subprocess and return it
394 ('!!ls', 'get_ipython().getoutput(u"ls")'),
423 ('!!ls', 'get_ipython().getoutput(u"ls")'),
395 ],
424 ],
396
425
397 # Help/object info
426 # Help/object info
398 escaped_help =
427 escaped_help =
399 [ ('?', 'get_ipython().show_usage()'),
428 [ ('?', 'get_ipython().show_usage()'),
400 ('?x1', 'get_ipython().magic(u"pinfo x1")'),
429 ('?x1', 'get_ipython().magic(u"pinfo x1")'),
401 ('??x2', 'get_ipython().magic(u"pinfo2 x2")'),
430 ('??x2', 'get_ipython().magic(u"pinfo2 x2")'),
402 ('x3?', 'get_ipython().magic(u"pinfo x3")'),
431 ('x3?', 'get_ipython().magic(u"pinfo x3")'),
403 ('x4??', 'get_ipython().magic(u"pinfo2 x4")'),
432 ('x4??', 'get_ipython().magic(u"pinfo2 x4")'),
404 ('%hist?', 'get_ipython().magic(u"pinfo %hist")'),
433 ('%hist?', 'get_ipython().magic(u"pinfo %hist")'),
405 ('f*?', 'get_ipython().magic(u"psearch f*")'),
434 ('f*?', 'get_ipython().magic(u"psearch f*")'),
406 ('ax.*aspe*?', 'get_ipython().magic(u"psearch ax.*aspe*")'),
435 ('ax.*aspe*?', 'get_ipython().magic(u"psearch ax.*aspe*")'),
407 ],
436 ],
408
437
409 # Explicit magic calls
438 # Explicit magic calls
410 escaped_magic =
439 escaped_magic =
411 [ ('%cd', 'get_ipython().magic(u"cd")'),
440 [ ('%cd', 'get_ipython().magic(u"cd")'),
412 ('%cd /home', 'get_ipython().magic(u"cd /home")'),
441 ('%cd /home', 'get_ipython().magic(u"cd /home")'),
413 (' %magic', ' get_ipython().magic(u"magic")'),
442 (' %magic', ' get_ipython().magic(u"magic")'),
414 ],
443 ],
415
444
416 # Quoting with separate arguments
445 # Quoting with separate arguments
417 escaped_quote =
446 escaped_quote =
418 [ (',f', 'f("")'),
447 [ (',f', 'f("")'),
419 (',f x', 'f("x")'),
448 (',f x', 'f("x")'),
420 (' ,f y', ' f("y")'),
449 (' ,f y', ' f("y")'),
421 (',f a b', 'f("a", "b")'),
450 (',f a b', 'f("a", "b")'),
422 ],
451 ],
423
452
424 # Quoting with single argument
453 # Quoting with single argument
425 escaped_quote2 =
454 escaped_quote2 =
426 [ (';f', 'f("")'),
455 [ (';f', 'f("")'),
427 (';f x', 'f("x")'),
456 (';f x', 'f("x")'),
428 (' ;f y', ' f("y")'),
457 (' ;f y', ' f("y")'),
429 (';f a b', 'f("a b")'),
458 (';f a b', 'f("a b")'),
430 ],
459 ],
431
460
432 # Simply apply parens
461 # Simply apply parens
433 escaped_paren =
462 escaped_paren =
434 [ ('/f', 'f()'),
463 [ ('/f', 'f()'),
435 ('/f x', 'f(x)'),
464 ('/f x', 'f(x)'),
436 (' /f y', ' f(y)'),
465 (' /f y', ' f(y)'),
437 ('/f a b', 'f(a, b)'),
466 ('/f a b', 'f(a, b)'),
438 ],
467 ],
439
468
440 )
469 )
441
470
442 # multiline syntax examples. Each of these should be a list of lists, with
471 # multiline syntax examples. Each of these should be a list of lists, with
443 # each entry itself having pairs of raw/transformed input. The union (with
472 # each entry itself having pairs of raw/transformed input. The union (with
444 # '\n'.join() of the transformed inputs is what the splitter should produce
473 # '\n'.join() of the transformed inputs is what the splitter should produce
445 # when fed the raw lines one at a time via push.
474 # when fed the raw lines one at a time via push.
446 syntax_ml = \
475 syntax_ml = \
447 dict(classic_prompt =
476 dict(classic_prompt =
448 [ [('>>> for i in range(10):','for i in range(10):'),
477 [ [('>>> for i in range(10):','for i in range(10):'),
449 ('... print i',' print i'),
478 ('... print i',' print i'),
450 ('... ', ''),
479 ('... ', ''),
451 ],
480 ],
452 ],
481 ],
453
482
454 ipy_prompt =
483 ipy_prompt =
455 [ [('In [24]: for i in range(10):','for i in range(10):'),
484 [ [('In [24]: for i in range(10):','for i in range(10):'),
456 (' ....: print i',' print i'),
485 (' ....: print i',' print i'),
457 (' ....: ', ''),
486 (' ....: ', ''),
458 ],
487 ],
459 ],
488 ],
460 )
489 )
461
490
462
491
463 def test_assign_system():
492 def test_assign_system():
464 transform_checker(syntax['assign_system'], isp.transform_assign_system)
493 transform_checker(syntax['assign_system'], isp.transform_assign_system)
465
494
466
495
467 def test_assign_magic():
496 def test_assign_magic():
468 transform_checker(syntax['assign_magic'], isp.transform_assign_magic)
497 transform_checker(syntax['assign_magic'], isp.transform_assign_magic)
469
498
470
499
471 def test_classic_prompt():
500 def test_classic_prompt():
472 transform_checker(syntax['classic_prompt'], isp.transform_classic_prompt)
501 transform_checker(syntax['classic_prompt'], isp.transform_classic_prompt)
473 for example in syntax_ml['classic_prompt']:
502 for example in syntax_ml['classic_prompt']:
474 transform_checker(example, isp.transform_classic_prompt)
503 transform_checker(example, isp.transform_classic_prompt)
475
504
476
505
477 def test_ipy_prompt():
506 def test_ipy_prompt():
478 transform_checker(syntax['ipy_prompt'], isp.transform_ipy_prompt)
507 transform_checker(syntax['ipy_prompt'], isp.transform_ipy_prompt)
479 for example in syntax_ml['ipy_prompt']:
508 for example in syntax_ml['ipy_prompt']:
480 transform_checker(example, isp.transform_ipy_prompt)
509 transform_checker(example, isp.transform_ipy_prompt)
481
510
482
511
483 def test_escaped_noesc():
512 def test_escaped_noesc():
484 transform_checker(syntax['escaped_noesc'], isp.transform_escaped)
513 transform_checker(syntax['escaped_noesc'], isp.transform_escaped)
485
514
486
515
487 def test_escaped_shell():
516 def test_escaped_shell():
488 transform_checker(syntax['escaped_shell'], isp.transform_escaped)
517 transform_checker(syntax['escaped_shell'], isp.transform_escaped)
489
518
490
519
491 def test_escaped_help():
520 def test_escaped_help():
492 transform_checker(syntax['escaped_help'], isp.transform_escaped)
521 transform_checker(syntax['escaped_help'], isp.transform_escaped)
493
522
494
523
495 def test_escaped_magic():
524 def test_escaped_magic():
496 transform_checker(syntax['escaped_magic'], isp.transform_escaped)
525 transform_checker(syntax['escaped_magic'], isp.transform_escaped)
497
526
498
527
499 def test_escaped_quote():
528 def test_escaped_quote():
500 transform_checker(syntax['escaped_quote'], isp.transform_escaped)
529 transform_checker(syntax['escaped_quote'], isp.transform_escaped)
501
530
502
531
503 def test_escaped_quote2():
532 def test_escaped_quote2():
504 transform_checker(syntax['escaped_quote2'], isp.transform_escaped)
533 transform_checker(syntax['escaped_quote2'], isp.transform_escaped)
505
534
506
535
507 def test_escaped_paren():
536 def test_escaped_paren():
508 transform_checker(syntax['escaped_paren'], isp.transform_escaped)
537 transform_checker(syntax['escaped_paren'], isp.transform_escaped)
509
538
510
539
511 class IPythonInputTestCase(InputSplitterTestCase):
540 class IPythonInputTestCase(InputSplitterTestCase):
512 """By just creating a new class whose .isp is a different instance, we
541 """By just creating a new class whose .isp is a different instance, we
513 re-run the same test battery on the new input splitter.
542 re-run the same test battery on the new input splitter.
514
543
515 In addition, this runs the tests over the syntax and syntax_ml dicts that
544 In addition, this runs the tests over the syntax and syntax_ml dicts that
516 were tested by individual functions, as part of the OO interface.
545 were tested by individual functions, as part of the OO interface.
517
546
518 It also makes some checks on the raw buffer storage.
547 It also makes some checks on the raw buffer storage.
519 """
548 """
520
549
521 def setUp(self):
550 def setUp(self):
522 self.isp = isp.IPythonInputSplitter(input_mode='line')
551 self.isp = isp.IPythonInputSplitter(input_mode='line')
523
552
524 def test_syntax(self):
553 def test_syntax(self):
525 """Call all single-line syntax tests from the main object"""
554 """Call all single-line syntax tests from the main object"""
526 isp = self.isp
555 isp = self.isp
527 for example in syntax.itervalues():
556 for example in syntax.itervalues():
528 for raw, out_t in example:
557 for raw, out_t in example:
529 if raw.startswith(' '):
558 if raw.startswith(' '):
530 continue
559 continue
531
560
532 isp.push(raw)
561 isp.push(raw)
533 out, out_raw = isp.source_raw_reset()
562 out, out_raw = isp.source_raw_reset()
534 self.assertEqual(out.rstrip(), out_t)
563 self.assertEqual(out.rstrip(), out_t)
535 self.assertEqual(out_raw.rstrip(), raw.rstrip())
564 self.assertEqual(out_raw.rstrip(), raw.rstrip())
536
565
537 def test_syntax_multiline(self):
566 def test_syntax_multiline(self):
538 isp = self.isp
567 isp = self.isp
539 for example in syntax_ml.itervalues():
568 for example in syntax_ml.itervalues():
540 out_t_parts = []
569 out_t_parts = []
541 raw_parts = []
570 raw_parts = []
542 for line_pairs in example:
571 for line_pairs in example:
543 for lraw, out_t_part in line_pairs:
572 for lraw, out_t_part in line_pairs:
544 isp.push(lraw)
573 isp.push(lraw)
545 out_t_parts.append(out_t_part)
574 out_t_parts.append(out_t_part)
546 raw_parts.append(lraw)
575 raw_parts.append(lraw)
547
576
548 out, out_raw = isp.source_raw_reset()
577 out, out_raw = isp.source_raw_reset()
549 out_t = '\n'.join(out_t_parts).rstrip()
578 out_t = '\n'.join(out_t_parts).rstrip()
550 raw = '\n'.join(raw_parts).rstrip()
579 raw = '\n'.join(raw_parts).rstrip()
551 self.assertEqual(out.rstrip(), out_t)
580 self.assertEqual(out.rstrip(), out_t)
552 self.assertEqual(out_raw.rstrip(), raw)
581 self.assertEqual(out_raw.rstrip(), raw)
553
582
554
583
555 class BlockIPythonInputTestCase(IPythonInputTestCase):
584 class BlockIPythonInputTestCase(IPythonInputTestCase):
556
585
557 # Deactivate tests that don't make sense for the block mode
586 # Deactivate tests that don't make sense for the block mode
558 test_push3 = test_split = lambda s: None
587 test_push3 = test_split = lambda s: None
559
588
560 def setUp(self):
589 def setUp(self):
561 self.isp = isp.IPythonInputSplitter(input_mode='cell')
590 self.isp = isp.IPythonInputSplitter(input_mode='cell')
562
591
563 def test_syntax_multiline(self):
592 def test_syntax_multiline(self):
564 isp = self.isp
593 isp = self.isp
565 for example in syntax_ml.itervalues():
594 for example in syntax_ml.itervalues():
566 raw_parts = []
595 raw_parts = []
567 out_t_parts = []
596 out_t_parts = []
568 for line_pairs in example:
597 for line_pairs in example:
569 for raw, out_t_part in line_pairs:
598 for raw, out_t_part in line_pairs:
570 raw_parts.append(raw)
599 raw_parts.append(raw)
571 out_t_parts.append(out_t_part)
600 out_t_parts.append(out_t_part)
572
601
573 raw = '\n'.join(raw_parts)
602 raw = '\n'.join(raw_parts)
574 out_t = '\n'.join(out_t_parts)
603 out_t = '\n'.join(out_t_parts)
575
604
576 isp.push(raw)
605 isp.push(raw)
577 out, out_raw = isp.source_raw_reset()
606 out, out_raw = isp.source_raw_reset()
578 # Match ignoring trailing whitespace
607 # Match ignoring trailing whitespace
579 self.assertEqual(out.rstrip(), out_t.rstrip())
608 self.assertEqual(out.rstrip(), out_t.rstrip())
580 self.assertEqual(out_raw.rstrip(), raw.rstrip())
609 self.assertEqual(out_raw.rstrip(), raw.rstrip())
581
610
582
611
583 #-----------------------------------------------------------------------------
612 #-----------------------------------------------------------------------------
584 # Main - use as a script, mostly for developer experiments
613 # Main - use as a script, mostly for developer experiments
585 #-----------------------------------------------------------------------------
614 #-----------------------------------------------------------------------------
586
615
587 if __name__ == '__main__':
616 if __name__ == '__main__':
588 # A simple demo for interactive experimentation. This code will not get
617 # A simple demo for interactive experimentation. This code will not get
589 # picked up by any test suite.
618 # picked up by any test suite.
590 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
619 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
591
620
592 # configure here the syntax to use, prompt and whether to autoindent
621 # configure here the syntax to use, prompt and whether to autoindent
593 #isp, start_prompt = InputSplitter(), '>>> '
622 #isp, start_prompt = InputSplitter(), '>>> '
594 isp, start_prompt = IPythonInputSplitter(), 'In> '
623 isp, start_prompt = IPythonInputSplitter(), 'In> '
595
624
596 autoindent = True
625 autoindent = True
597 #autoindent = False
626 #autoindent = False
598
627
599 try:
628 try:
600 while True:
629 while True:
601 prompt = start_prompt
630 prompt = start_prompt
602 while isp.push_accepts_more():
631 while isp.push_accepts_more():
603 indent = ' '*isp.indent_spaces
632 indent = ' '*isp.indent_spaces
604 if autoindent:
633 if autoindent:
605 line = indent + raw_input(prompt+indent)
634 line = indent + raw_input(prompt+indent)
606 else:
635 else:
607 line = raw_input(prompt)
636 line = raw_input(prompt)
608 isp.push(line)
637 isp.push(line)
609 prompt = '... '
638 prompt = '... '
610
639
611 # Here we just return input so we can use it in a test suite, but a
640 # Here we just return input so we can use it in a test suite, but a
612 # real interpreter would instead send it for execution somewhere.
641 # real interpreter would instead send it for execution somewhere.
613 #src = isp.source; raise EOFError # dbg
642 #src = isp.source; raise EOFError # dbg
614 src, raw = isp.source_raw_reset()
643 src, raw = isp.source_raw_reset()
615 print 'Input source was:\n', src
644 print 'Input source was:\n', src
616 print 'Raw source was:\n', raw
645 print 'Raw source was:\n', raw
617 except EOFError:
646 except EOFError:
618 print 'Bye'
647 print 'Bye'
General Comments 0
You need to be logged in to leave comments. Login now