Show More
@@ -22,6 +22,9 b' import codeop' | |||
|
22 | 22 | import re |
|
23 | 23 | import sys |
|
24 | 24 | |
|
25 | # IPython modules | |
|
26 | from IPython.utils.text import make_quoted_expr | |
|
27 | ||
|
25 | 28 | #----------------------------------------------------------------------------- |
|
26 | 29 | # Utilities |
|
27 | 30 | #----------------------------------------------------------------------------- |
@@ -419,3 +422,100 b' class InputSplitter(object):' | |||
|
419 | 422 | |
|
420 | 423 | def _set_source(self): |
|
421 | 424 | self.source = ''.join(self._buffer).encode(self.encoding) |
|
425 | ||
|
426 | ||
|
427 | #----------------------------------------------------------------------------- | |
|
428 | # IPython-specific syntactic support | |
|
429 | #----------------------------------------------------------------------------- | |
|
430 | ||
|
431 | # We implement things, as much as possible, as standalone functions that can be | |
|
432 | # tested and validated in isolation. | |
|
433 | ||
|
434 | # Each of these uses a regexp, we pre-compile these and keep them close to each | |
|
435 | # function definition for clarity | |
|
436 | _assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))' | |
|
437 | r'\s*=\s*!\s*(?P<cmd>.*)') | |
|
438 | ||
|
439 | def transform_assign_system(line): | |
|
440 | """Handle the `files = !ls` syntax.""" | |
|
441 | # FIXME: This transforms the line to use %sc, but we've listed that magic | |
|
442 | # as deprecated. We should then implement this functionality in a | |
|
443 | # standalone api that we can transform to, without going through a | |
|
444 | # deprecated magic. | |
|
445 | m = _assign_system_re.match(line) | |
|
446 | if m is not None: | |
|
447 | cmd = m.group('cmd') | |
|
448 | lhs = m.group('lhs') | |
|
449 | expr = make_quoted_expr("sc -l = %s" % cmd) | |
|
450 | new_line = '%s = get_ipython().magic(%s)' % (lhs, expr) | |
|
451 | return new_line | |
|
452 | return line | |
|
453 | ||
|
454 | ||
|
455 | _assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))' | |
|
456 | r'\s*=\s*%\s*(?P<cmd>.*)') | |
|
457 | ||
|
458 | def transform_assign_magic(line): | |
|
459 | """Handle the `a = %who` syntax.""" | |
|
460 | m = _assign_magic_re.match(line) | |
|
461 | if m is not None: | |
|
462 | cmd = m.group('cmd') | |
|
463 | lhs = m.group('lhs') | |
|
464 | expr = make_quoted_expr(cmd) | |
|
465 | new_line = '%s = get_ipython().magic(%s)' % (lhs, expr) | |
|
466 | return new_line | |
|
467 | return line | |
|
468 | ||
|
469 | ||
|
470 | _classic_prompt_re = re.compile(r'(^[ \t]*>>> |^[ \t]*\.\.\. )') | |
|
471 | ||
|
472 | def transform_classic_prompt(line): | |
|
473 | """Handle inputs that start with '>>> ' syntax.""" | |
|
474 | ||
|
475 | if not line or line.isspace() or line.strip() == '...': | |
|
476 | # This allows us to recognize multiple input prompts separated by | |
|
477 | # blank lines and pasted in a single chunk, very common when | |
|
478 | # pasting doctests or long tutorial passages. | |
|
479 | return '' | |
|
480 | m = _classic_prompt_re.match(line) | |
|
481 | if m: | |
|
482 | return line[len(m.group(0)):] | |
|
483 | else: | |
|
484 | return line | |
|
485 | ||
|
486 | ||
|
487 | _ipy_prompt_re = re.compile(r'(^[ \t]*In \[\d+\]: |^[ \t]*\ \ \ \.\.\.+: )') | |
|
488 | ||
|
489 | def transform_ipy_prompt(line): | |
|
490 | """Handle inputs that start classic IPython prompt syntax.""" | |
|
491 | ||
|
492 | if not line or line.isspace() or line.strip() == '...': | |
|
493 | # This allows us to recognize multiple input prompts separated by | |
|
494 | # blank lines and pasted in a single chunk, very common when | |
|
495 | # pasting doctests or long tutorial passages. | |
|
496 | return '' | |
|
497 | m = _ipy_prompt_re.match(line) | |
|
498 | if m: | |
|
499 | return line[len(m.group(0)):] | |
|
500 | else: | |
|
501 | return line | |
|
502 | ||
|
503 | ||
|
504 | # Warning, these cannot be changed unless various regular expressions | |
|
505 | # are updated in a number of places. Not great, but at least we told you. | |
|
506 | ESC_SHELL = '!' | |
|
507 | ESC_SH_CAP = '!!' | |
|
508 | ESC_HELP = '?' | |
|
509 | ESC_MAGIC = '%' | |
|
510 | ESC_QUOTE = ',' | |
|
511 | ESC_QUOTE2 = ';' | |
|
512 | ESC_PAREN = '/' | |
|
513 | ||
|
514 | class IPythonInputSplitter(InputSplitter): | |
|
515 | """An input splitter that recognizes all of IPython's special syntax.""" | |
|
516 | ||
|
517 | ||
|
518 | def push(self, lines): | |
|
519 | """Push one or more lines of IPython input. | |
|
520 | """ | |
|
521 | return super(IPythonInputSplitter, self).push(lines) |
@@ -362,3 +362,50 b' class InteractiveLoopTestCase(unittest.TestCase):' | |||
|
362 | 362 | def test_multi(self): |
|
363 | 363 | self.check_ns(['x =(1+','1+','2)'], dict(x=4)) |
|
364 | 364 | |
|
365 | ||
|
366 | class IPythonInputTestCase(InputSplitterTestCase): | |
|
367 | def setUp(self): | |
|
368 | self.isp = isp.IPythonInputSplitter() | |
|
369 | ||
|
370 | ||
|
371 | # Transformer tests | |
|
372 | def transform_checker(tests, func): | |
|
373 | """Utility to loop over test inputs""" | |
|
374 | for inp, tr in tests: | |
|
375 | nt.assert_equals(func(inp), tr) | |
|
376 | ||
|
377 | ||
|
378 | def test_assign_system(): | |
|
379 | tests = [('a =! ls', 'a = get_ipython().magic("sc -l = ls")'), | |
|
380 | ('b = !ls', 'b = get_ipython().magic("sc -l = ls")'), | |
|
381 | ('x=1','x=1')] | |
|
382 | transform_checker(tests, isp.transform_assign_system) | |
|
383 | ||
|
384 | ||
|
385 | def test_assign_magic(): | |
|
386 | tests = [('a =% who', 'a = get_ipython().magic("who")'), | |
|
387 | ('b = %who', 'b = get_ipython().magic("who")'), | |
|
388 | ('x=1','x=1')] | |
|
389 | transform_checker(tests, isp.transform_assign_magic) | |
|
390 | ||
|
391 | ||
|
392 | def test_classic_prompt(): | |
|
393 | tests = [('>>> x=1', 'x=1'), | |
|
394 | ('>>> for i in range(10):','for i in range(10):'), | |
|
395 | ('... print i',' print i'), | |
|
396 | ('...', ''), | |
|
397 | ('x=1','x=1') | |
|
398 | ] | |
|
399 | transform_checker(tests, isp.transform_classic_prompt) | |
|
400 | ||
|
401 | ||
|
402 | def test_ipy_prompt(): | |
|
403 | tests = [('In [1]: x=1', 'x=1'), | |
|
404 | ('In [24]: for i in range(10):','for i in range(10):'), | |
|
405 | (' ....: print i',' print i'), | |
|
406 | (' ....: ', ''), | |
|
407 | ('x=1', 'x=1'), # normal input is unmodified | |
|
408 | (' ','') # blank lines are just collapsed | |
|
409 | ] | |
|
410 | transform_checker(tests, isp.transform_ipy_prompt) | |
|
411 |
General Comments 0
You need to be logged in to leave comments.
Login now