##// END OF EJS Templates
Remove split_blocks method and function, and the associated tests. Test coverage of IPython.core.inputsplitter remains 100%.
Thomas Kluyver -
Show More
@@ -166,78 +166,6 b' def get_input_encoding():'
166 # Classes and functions for normal Python syntax handling
166 # Classes and functions for normal Python syntax handling
167 #-----------------------------------------------------------------------------
167 #-----------------------------------------------------------------------------
168
168
169 # HACK! This implementation, written by Robert K a while ago using the
170 # compiler module, is more robust than the other one below, but it expects its
171 # input to be pure python (no ipython syntax). For now we're using it as a
172 # second-pass splitter after the first pass transforms the input to pure
173 # python.
174
175 def split_blocks(python):
176 """ Split multiple lines of code into discrete commands that can be
177 executed singly.
178
179 Parameters
180 ----------
181 python : str
182 Pure, exec'able Python code.
183
184 Returns
185 -------
186 commands : list of str
187 Separate commands that can be exec'ed independently.
188 """
189 # compiler.parse treats trailing spaces after a newline as a
190 # SyntaxError. This is different than codeop.CommandCompiler, which
191 # will compile the trailng spaces just fine. We simply strip any
192 # trailing whitespace off. Passing a string with trailing whitespace
193 # to exec will fail however. There seems to be some inconsistency in
194 # how trailing whitespace is handled, but this seems to work.
195 python_ori = python # save original in case we bail on error
196 python = python.strip()
197
198 # The compiler module will parse the code into an abstract syntax tree.
199 # This has a bug with str("a\nb"), but not str("""a\nb""")!!!
200 try:
201 code_ast = ast.parse(python)
202 except:
203 return [python_ori]
204
205 # Uncomment to help debug the ast tree
206 # for n in code_ast.body:
207 # print n.lineno,'->',n
208
209 # Each separate command is available by iterating over ast.node. The
210 # lineno attribute is the line number (1-indexed) beginning the commands
211 # suite.
212 # lines ending with ";" yield a Discard Node that doesn't have a lineno
213 # attribute. These nodes can and should be discarded. But there are
214 # other situations that cause Discard nodes that shouldn't be discarded.
215 # We might eventually discover other cases where lineno is None and have
216 # to put in a more sophisticated test.
217 linenos = [x.lineno-1 for x in code_ast.body if x.lineno is not None]
218
219 # When we finally get the slices, we will need to slice all the way to
220 # the end even though we don't have a line number for it. Fortunately,
221 # None does the job nicely.
222 linenos.append(None)
223
224 # Same problem at the other end: sometimes the ast tree has its
225 # first complete statement not starting on line 0. In this case
226 # we might miss part of it. This fixes ticket 266993. Thanks Gael!
227 linenos[0] = 0
228
229 lines = python.splitlines()
230
231 # Create a list of atomic commands.
232 cmds = []
233 for i, j in zip(linenos[:-1], linenos[1:]):
234 cmd = lines[i:j]
235 if cmd:
236 cmds.append('\n'.join(cmd)+'\n')
237
238 return cmds
239
240
241 class InputSplitter(object):
169 class InputSplitter(object):
242 """An object that can split Python source input in executable blocks.
170 """An object that can split Python source input in executable blocks.
243
171
@@ -457,88 +385,6 b' class InputSplitter(object):'
457 # line at the end.
385 # line at the end.
458 last_line = self.source.splitlines()[-1]
386 last_line = self.source.splitlines()[-1]
459 return bool(last_line and not last_line.isspace())
387 return bool(last_line and not last_line.isspace())
460
461 def split_blocks(self, lines):
462 """Split a multiline string into multiple input blocks.
463
464 Note: this method starts by performing a full reset().
465
466 Parameters
467 ----------
468 lines : str
469 A possibly multiline string.
470
471 Returns
472 -------
473 blocks : list
474 A list of strings, each possibly multiline. Each string corresponds
475 to a single block that can be compiled in 'single' mode (unless it
476 has a syntax error)."""
477
478 # This code is fairly delicate. If you make any changes here, make
479 # absolutely sure that you do run the full test suite and ALL tests
480 # pass.
481
482 self.reset()
483 blocks = []
484
485 # Reversed copy so we can use pop() efficiently and consume the input
486 # as a stack
487 lines = lines.splitlines()[::-1]
488 # Outer loop over all input
489 while lines:
490 #print 'Current lines:', lines # dbg
491 # Inner loop to build each block
492 while True:
493 # Safety exit from inner loop
494 if not lines:
495 break
496 # Grab next line but don't push it yet
497 next_line = lines.pop()
498 # Blank/empty lines are pushed as-is
499 if not next_line or next_line.isspace():
500 self.push(next_line)
501 continue
502
503 # Check indentation changes caused by the *next* line
504 indent_spaces, _full_dedent = self._find_indent(next_line)
505
506 # If the next line causes a dedent, it can be for two differnt
507 # reasons: either an explicit de-dent by the user or a
508 # return/raise/pass statement. These MUST be handled
509 # separately:
510 #
511 # 1. the first case is only detected when the actual explicit
512 # dedent happens, and that would be the *first* line of a *new*
513 # block. Thus, we must put the line back into the input buffer
514 # so that it starts a new block on the next pass.
515 #
516 # 2. the second case is detected in the line before the actual
517 # dedent happens, so , we consume the line and we can break out
518 # to start a new block.
519
520 # Case 1, explicit dedent causes a break.
521 # Note: check that we weren't on the very last line, else we'll
522 # enter an infinite loop adding/removing the last line.
523 if _full_dedent and lines and not next_line.startswith(' '):
524 lines.append(next_line)
525 break
526
527 # Otherwise any line is pushed
528 self.push(next_line)
529
530 # Case 2, full dedent with full block ready:
531 if _full_dedent or \
532 self.indent_spaces==0 and not self.push_accepts_more():
533 break
534 # Form the new block with the current source input
535 blocks.append(self.source_reset())
536
537 #return blocks
538 # HACK!!! Now that our input is in blocks but guaranteed to be pure
539 # python syntax, feed it back a second time through the AST-based
540 # splitter, which is more accurate than ours.
541 return split_blocks(''.join(blocks))
542
388
543 #------------------------------------------------------------------------
389 #------------------------------------------------------------------------
544 # Private interface
390 # Private interface
@@ -286,97 +286,6 b' class InputSplitterTestCase(unittest.TestCase):'
286 isp.push('run foo')
286 isp.push('run foo')
287 self.assertFalse(isp.push_accepts_more())
287 self.assertFalse(isp.push_accepts_more())
288
288
289 def check_split(self, block_lines, compile=True):
290 blocks = assemble(block_lines)
291 lines = ''.join(blocks)
292 oblock = self.isp.split_blocks(lines)
293 self.assertEqual(oblock, blocks)
294 if compile:
295 for block in blocks:
296 self.isp._compile(block)
297
298 def test_split(self):
299 # All blocks of input we want to test in a list. The format for each
300 # block is a list of lists, with each inner lists consisting of all the
301 # lines (as single-lines) that should make up a sub-block.
302
303 # Note: do NOT put here sub-blocks that don't compile, as the
304 # check_split() routine makes a final verification pass to check that
305 # each sub_block, as returned by split_blocks(), does compile
306 # correctly.
307 all_blocks = [ [['x=1']],
308
309 [['x=1'],
310 ['y=2']],
311
312 [['x=1',
313 '# a comment'],
314 ['y=11']],
315
316 [['if 1:',
317 ' x=1'],
318 ['y=3']],
319
320 [['def f(x):',
321 ' return x'],
322 ['x=1']],
323
324 [['def f(x):',
325 ' x+=1',
326 ' ',
327 ' return x'],
328 ['x=1']],
329
330 [['def f(x):',
331 ' if x>0:',
332 ' y=1',
333 ' # a comment',
334 ' else:',
335 ' y=4',
336 ' ',
337 ' return y'],
338 ['x=1'],
339 ['if 1:',
340 ' y=11'] ],
341
342 [['for i in range(10):'
343 ' x=i**2']],
344
345 [['for i in range(10):',
346 ' x=i**2'],
347 ['z = 1']],
348
349 [['for i in range(10):',
350 ' x=i**2'],
351 ['z = 1'],
352 ['a = None']],
353
354 [['"asdf"']],
355
356 [['"asdf"'],
357 ['10'],
358 ],
359
360 [['"""foo',
361 'bar"""']],
362 ]
363 for block_lines in all_blocks:
364 self.check_split(block_lines)
365
366 def test_split_syntax_errors(self):
367 # Block splitting with invalid syntax
368 all_blocks = [ [['a syntax error']],
369
370 [['x=1',
371 'another syntax error']],
372
373 [['for i in range(10):'
374 ' yet another error']],
375
376 ]
377 for block_lines in all_blocks:
378 self.check_split(block_lines, compile=False)
379
380 def test_unicode(self):
289 def test_unicode(self):
381 self.isp.push(u"Pérez")
290 self.isp.push(u"Pérez")
382 self.isp.push(u'\xc3\xa9')
291 self.isp.push(u'\xc3\xa9')
General Comments 0
You need to be logged in to leave comments. Login now