##// END OF EJS Templates
Completed first pass of inputsplitter with IPython syntax....
Fernando Perez -
Show More
@@ -6,6 +6,11 b" into standalone blocks that can be executed by Python as 'single' statements"
6 (thus triggering sys.displayhook).
6 (thus triggering sys.displayhook).
7
7
8 For more details, see the class docstring below.
8 For more details, see the class docstring below.
9
10 Authors
11
12 * Fernando Perez
13 * Brian Granger
9 """
14 """
10 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
11 # Copyright (C) 2010 The IPython Development Team
16 # Copyright (C) 2010 The IPython Development Team
@@ -26,10 +31,32 b' import sys'
26 from IPython.utils.text import make_quoted_expr
31 from IPython.utils.text import make_quoted_expr
27
32
28 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Globals
35 #-----------------------------------------------------------------------------
36
37 # The escape sequences that define the syntax transformations IPython will
38 # apply to user input. These can NOT be just changed here: many regular
39 # expressions and other parts of the code may use their hardcoded values, and
40 # for all intents and purposes they constitute the 'IPython syntax', so they
41 # should be considered fixed.
42
43 ESC_SHELL = '!'
44 ESC_SH_CAP = '!!'
45 ESC_HELP = '?'
46 ESC_HELP2 = '??'
47 ESC_MAGIC = '%'
48 ESC_QUOTE = ','
49 ESC_QUOTE2 = ';'
50 ESC_PAREN = '/'
51
52 #-----------------------------------------------------------------------------
29 # Utilities
53 # Utilities
30 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
31
55
32 # FIXME: move these utilities to the general ward...
56 # FIXME: These are general-purpose utilities that later can be moved to the
57 # general ward. Kept here for now because we're being very strict about test
58 # coverage with this code, and this lets us ensure that we keep 100% coverage
59 # while developing.
33
60
34 # compiled regexps for autoindent management
61 # compiled regexps for autoindent management
35 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
62 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
@@ -88,7 +115,7 b' def get_input_encoding():'
88 return encoding
115 return encoding
89
116
90 #-----------------------------------------------------------------------------
117 #-----------------------------------------------------------------------------
91 # Classes and functions
118 # Classes and functions for normal Python syntax handling
92 #-----------------------------------------------------------------------------
119 #-----------------------------------------------------------------------------
93
120
94 class InputSplitter(object):
121 class InputSplitter(object):
@@ -425,14 +452,140 b' class InputSplitter(object):'
425
452
426
453
427 #-----------------------------------------------------------------------------
454 #-----------------------------------------------------------------------------
428 # IPython-specific syntactic support
455 # Functions and classes for IPython-specific syntactic support
429 #-----------------------------------------------------------------------------
456 #-----------------------------------------------------------------------------
430
457
431 # We implement things, as much as possible, as standalone functions that can be
458 # RegExp for splitting line contents into pre-char//first word-method//rest.
432 # tested and validated in isolation.
459 # For clarity, each group in on one line.
460
461 line_split = re.compile("""
462 ^(\s*) # any leading space
463 ([,;/%]|!!?|\?\??) # escape character or characters
464 \s*([\w\.]*) # function/method part (mix of \w and '.')
465 (\s+.*$|$) # rest of line
466 """, re.VERBOSE)
467
468
469 def split_user_input(line):
470 """Split user input into early whitespace, esc-char, function part and rest.
471
472 This is currently handles lines with '=' in them in a very inconsistent
473 manner.
474
475 Examples
476 ========
477 >>> split_user_input('x=1')
478 ('', '', 'x=1', '')
479 >>> split_user_input('?')
480 ('', '?', '', '')
481 >>> split_user_input('??')
482 ('', '??', '', '')
483 >>> split_user_input(' ?')
484 (' ', '?', '', '')
485 >>> split_user_input(' ??')
486 (' ', '??', '', '')
487 >>> split_user_input('??x')
488 ('', '??', 'x', '')
489 >>> split_user_input('?x=1')
490 ('', '', '?x=1', '')
491 >>> split_user_input('!ls')
492 ('', '!', 'ls', '')
493 >>> split_user_input(' !ls')
494 (' ', '!', 'ls', '')
495 >>> split_user_input('!!ls')
496 ('', '!!', 'ls', '')
497 >>> split_user_input(' !!ls')
498 (' ', '!!', 'ls', '')
499 >>> split_user_input(',ls')
500 ('', ',', 'ls', '')
501 >>> split_user_input(';ls')
502 ('', ';', 'ls', '')
503 >>> split_user_input(' ;ls')
504 (' ', ';', 'ls', '')
505 >>> split_user_input('f.g(x)')
506 ('', '', 'f.g(x)', '')
507 >>> split_user_input('f.g (x)')
508 ('', '', 'f.g', '(x)')
509 """
510 match = line_split.match(line)
511 if match:
512 lspace, esc, fpart, rest = match.groups()
513 else:
514 # print "match failed for line '%s'" % line
515 try:
516 fpart, rest = line.split(None,1)
517 except ValueError:
518 # print "split failed for line '%s'" % line
519 fpart, rest = line,''
520 lspace = re.match('^(\s*)(.*)',line).groups()[0]
521 esc = ''
522
523 # fpart has to be a valid python identifier, so it better be only pure
524 # ascii, no unicode:
525 try:
526 fpart = fpart.encode('ascii')
527 except UnicodeEncodeError:
528 lspace = unicode(lspace)
529 rest = fpart + u' ' + rest
530 fpart = u''
531
532 #print 'line:<%s>' % line # dbg
533 #print 'esc <%s> fpart <%s> rest <%s>' % (esc,fpart.strip(),rest) # dbg
534 return lspace, esc, fpart.strip(), rest.lstrip()
535
536
537 # The escaped translators ALL receive a line where their own escape has been
538 # stripped. Only '?' is valid at the end of the line, all others can only be
539 # placed at the start.
540
541 class LineInfo(object):
542 """A single line of input and associated info.
543
544 This is a utility class that mostly wraps the output of
545 :func:`split_user_input` into a convenient object to be passed around
546 during input transformations.
547
548 Includes the following as properties:
549
550 line
551 The original, raw line
552
553 lspace
554 Any early whitespace before actual text starts.
555
556 esc
557 The initial esc character (or characters, for double-char escapes like
558 '??' or '!!').
559
560 pre_char
561 The escape character(s) in esc or the empty string if there isn't one.
562
563 fpart
564 The 'function part', which is basically the maximal initial sequence
565 of valid python identifiers and the '.' character. This is what is
566 checked for alias and magic transformations, used for auto-calling,
567 etc.
568
569 rest
570 Everything else on the line.
571 """
572 def __init__(self, line):
573 self.line = line
574 self.lspace, self.esc, self.fpart, self.rest = \
575 split_user_input(line)
576
577 def __str__(self):
578 return "LineInfo [%s|%s|%s|%s]" % (self.lspace, self.esc,
579 self.fpart, self.rest)
580
581
582 # Transformations of the special syntaxes that don't rely on an explicit escape
583 # character but instead on patterns on the input line
584
585 # The core transformations are implemented as standalone functions that can be
586 # tested and validated in isolation. Each of these uses a regexp, we
587 # pre-compile these and keep them close to each function definition for clarity
433
588
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\.]+)*))'
589 _assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
437 r'\s*=\s*!\s*(?P<cmd>.*)')
590 r'\s*=\s*!\s*(?P<cmd>.*)')
438
591
@@ -467,16 +620,13 b' def transform_assign_magic(line):'
467 return line
620 return line
468
621
469
622
470 _classic_prompt_re = re.compile(r'(^[ \t]*>>> |^[ \t]*\.\.\. )')
623 _classic_prompt_re = re.compile(r'^([ \t]*>>> |^[ \t]*\.\.\. )')
471
624
472 def transform_classic_prompt(line):
625 def transform_classic_prompt(line):
473 """Handle inputs that start with '>>> ' syntax."""
626 """Handle inputs that start with '>>> ' syntax."""
474
627
475 if not line or line.isspace() or line.strip() == '...':
628 if not line or line.isspace():
476 # This allows us to recognize multiple input prompts separated by
629 return line
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)
630 m = _classic_prompt_re.match(line)
481 if m:
631 if m:
482 return line[len(m.group(0)):]
632 return line[len(m.group(0)):]
@@ -484,16 +634,13 b' def transform_classic_prompt(line):'
484 return line
634 return line
485
635
486
636
487 _ipy_prompt_re = re.compile(r'(^[ \t]*In \[\d+\]: |^[ \t]*\ \ \ \.\.\.+: )')
637 _ipy_prompt_re = re.compile(r'^([ \t]*In \[\d+\]: |^[ \t]*\ \ \ \.\.\.+: )')
488
638
489 def transform_ipy_prompt(line):
639 def transform_ipy_prompt(line):
490 """Handle inputs that start classic IPython prompt syntax."""
640 """Handle inputs that start classic IPython prompt syntax."""
491
641
492 if not line or line.isspace() or line.strip() == '...':
642 if not line or line.isspace():
493 # This allows us to recognize multiple input prompts separated by
643 return line
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)
644 m = _ipy_prompt_re.match(line)
498 if m:
645 if m:
499 return line[len(m.group(0)):]
646 return line[len(m.group(0)):]
@@ -501,21 +648,151 b' def transform_ipy_prompt(line):'
501 return line
648 return line
502
649
503
650
504 # Warning, these cannot be changed unless various regular expressions
651 def transform_unescaped(line):
505 # are updated in a number of places. Not great, but at least we told you.
652 """Transform lines that are explicitly escaped out.
506 ESC_SHELL = '!'
653
507 ESC_SH_CAP = '!!'
654 This calls to the above transform_* functions for the actual line
508 ESC_HELP = '?'
655 translations.
509 ESC_MAGIC = '%'
656
510 ESC_QUOTE = ','
657 Parameters
511 ESC_QUOTE2 = ';'
658 ----------
512 ESC_PAREN = '/'
659 line : str
660 A single line of input to be transformed.
661
662 Returns
663 -------
664 new_line : str
665 Transformed line, which may be identical to the original."""
666
667 if not line or line.isspace():
668 return line
669
670 new_line = line
671 for f in [transform_assign_system, transform_assign_magic,
672 transform_classic_prompt, transform_ipy_prompt ] :
673 new_line = f(new_line)
674 return new_line
675
676 # Support for syntax transformations that use explicit escapes typed by the
677 # user at the beginning of a line
678
679 def tr_system(line_info):
680 "Translate lines escaped with: !"
681 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
682 return '%sget_ipython().system(%s)' % (line_info.lspace,
683 make_quoted_expr(cmd))
684
685
686 def tr_system2(line_info):
687 "Translate lines escaped with: !!"
688 cmd = line_info.line.lstrip()[2:]
689 return '%sget_ipython().getoutput(%s)' % (line_info.lspace,
690 make_quoted_expr(cmd))
691
692
693 def tr_help(line_info):
694 "Translate lines escaped with: ?/??"
695 # A naked help line should just fire the intro help screen
696 if not line_info.line[1:]:
697 return 'get_ipython().show_usage()'
698
699 # There may be one or two '?' at the end, move them to the front so that
700 # the rest of the logic can assume escapes are at the start
701 line = line_info.line
702 if line.endswith('?'):
703 line = line[-1] + line[:-1]
704 if line.endswith('?'):
705 line = line[-1] + line[:-1]
706 line_info = LineInfo(line)
707
708 # From here on, simply choose which level of detail to get.
709 if line_info.esc == '?':
710 pinfo = 'pinfo'
711 elif line_info.esc == '??':
712 pinfo = 'pinfo2'
713
714 tpl = '%sget_ipython().magic("%s %s")'
715 return tpl % (line_info.lspace, pinfo,
716 ' '.join([line_info.fpart, line_info.rest]).strip())
717
718
719 def tr_magic(line_info):
720 "Translate lines escaped with: %"
721 tpl = '%sget_ipython().magic(%s)'
722 cmd = make_quoted_expr(' '.join([line_info.fpart,
723 line_info.rest])).strip()
724 return tpl % (line_info.lspace, cmd)
725
726
727 def tr_quote(line_info):
728 "Translate lines escaped with: ,"
729 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
730 '", "'.join(line_info.rest.split()) )
731
732
733 def tr_quote2(line_info):
734 "Translate lines escaped with: ;"
735 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
736 line_info.rest)
737
738
739 def tr_paren(line_info):
740 "Translate lines escaped with: /"
741 return '%s%s(%s)' % (line_info.lspace, line_info.fpart,
742 ", ".join(line_info.rest.split()))
743
744
745 def transform_escaped(line):
746 """Transform lines that are explicitly escaped out.
747
748 This calls to the above tr_* functions for the actual line translations."""
749
750 tr = { ESC_SHELL : tr_system,
751 ESC_SH_CAP : tr_system2,
752 ESC_HELP : tr_help,
753 ESC_HELP2 : tr_help,
754 ESC_MAGIC : tr_magic,
755 ESC_QUOTE : tr_quote,
756 ESC_QUOTE2 : tr_quote2,
757 ESC_PAREN : tr_paren }
758
759 # Empty lines just get returned unmodified
760 if not line or line.isspace():
761 return line
762
763 # Get line endpoints, where the escapes can be
764 line_info = LineInfo(line)
765
766 # If the escape is not at the start, only '?' needs to be special-cased.
767 # All other escapes are only valid at the start
768 if not line_info.esc in tr:
769 if line.endswith(ESC_HELP):
770 return tr_help(line_info)
771 else:
772 # If we don't recognize the escape, don't modify the line
773 return line
774
775 return tr[line_info.esc](line_info)
776
513
777
514 class IPythonInputSplitter(InputSplitter):
778 class IPythonInputSplitter(InputSplitter):
515 """An input splitter that recognizes all of IPython's special syntax."""
779 """An input splitter that recognizes all of IPython's special syntax."""
516
780
517
518 def push(self, lines):
781 def push(self, lines):
519 """Push one or more lines of IPython input.
782 """Push one or more lines of IPython input.
520 """
783 """
521 return super(IPythonInputSplitter, self).push(lines)
784 # We only apply the line transformers to the input if we have either no
785 # input yet, or complete input. This prevents the accidental
786 # transformation of escapes inside multiline expressions like
787 # triple-quoted strings or parenthesized expressions.
788 lines_list = lines.splitlines()
789 if self._is_complete or not self._buffer:
790
791 new_list = map(transform_escaped, lines_list)
792 else:
793 new_list = lines_list
794
795 # Now apply the unescaped transformations to each input line
796 new_list = map(transform_unescaped, new_list)
797 newlines = '\n'.join(new_list)
798 return super(IPythonInputSplitter, self).push(newlines)
@@ -1,3 +1,4 b''
1 # -*- coding: utf-8 -*-
1 """Tests for the inputsplitter module.
2 """Tests for the inputsplitter module.
2 """
3 """
3 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
@@ -23,6 +24,10 b' from IPython.core import inputsplitter as isp'
23 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
24 # Semi-complete examples (also used as tests)
25 # Semi-complete examples (also used as tests)
25 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
28 # Note: at the bottom, there's a slightly more complete version of this that
29 # can be useful during development of code here.
30
26 def mini_interactive_loop(raw_input):
31 def mini_interactive_loop(raw_input):
27 """Minimal example of the logic of an interactive interpreter loop.
32 """Minimal example of the logic of an interactive interpreter loop.
28
33
@@ -44,7 +49,7 b' def mini_interactive_loop(raw_input):'
44 # Here we just return input so we can use it in a test suite, but a real
49 # Here we just return input so we can use it in a test suite, but a real
45 # interpreter would instead send it for execution somewhere.
50 # interpreter would instead send it for execution somewhere.
46 src = isp.source_reset()
51 src = isp.source_reset()
47 print 'Input source was:\n', src
52 #print 'Input source was:\n', src # dbg
48 return src
53 return src
49
54
50 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
@@ -363,9 +368,18 b' class InteractiveLoopTestCase(unittest.TestCase):'
363 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
368 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
364
369
365
370
366 class IPythonInputTestCase(InputSplitterTestCase):
371 def test_LineInfo():
367 def setUp(self):
372 """Simple test for LineInfo construction and str()"""
368 self.isp = isp.IPythonInputSplitter()
373 linfo = isp.LineInfo(' %cd /home')
374 nt.assert_equals(str(linfo), 'LineInfo [ |%|cd|/home]')
375
376
377 def test_split_user_input():
378 """Unicode test - split_user_input already has good doctests"""
379 line = u"Pérez Fernando"
380 parts = isp.split_user_input(line)
381 parts_expected = (u'', u'', u'', line)
382 nt.assert_equal(parts, parts_expected)
369
383
370
384
371 # Transformer tests
385 # Transformer tests
@@ -373,39 +387,237 b' def transform_checker(tests, func):'
373 """Utility to loop over test inputs"""
387 """Utility to loop over test inputs"""
374 for inp, tr in tests:
388 for inp, tr in tests:
375 nt.assert_equals(func(inp), tr)
389 nt.assert_equals(func(inp), tr)
376
390
391 # Data for all the syntax tests in the form of lists of pairs of
392 # raw/transformed input. We store it here as a global dict so that we can use
393 # it both within single-function tests and also to validate the behavior of the
394 # larger objects
395
396 syntax = \
397 dict(assign_system =
398 [('a =! ls', 'a = get_ipython().magic("sc -l = ls")'),
399 ('b = !ls', 'b = get_ipython().magic("sc -l = ls")'),
400 ('x=1', 'x=1'), # normal input is unmodified
401 (' ',' '), # blank lines are kept intact
402 ],
403
404 assign_magic =
405 [('a =% who', 'a = get_ipython().magic("who")'),
406 ('b = %who', 'b = get_ipython().magic("who")'),
407 ('x=1', 'x=1'), # normal input is unmodified
408 (' ',' '), # blank lines are kept intact
409 ],
410
411 classic_prompt =
412 [('>>> x=1', 'x=1'),
413 ('x=1', 'x=1'), # normal input is unmodified
414 (' ',' '), # blank lines are kept intact
415 ],
416
417 ipy_prompt =
418 [('In [1]: x=1', 'x=1'),
419 ('x=1', 'x=1'), # normal input is unmodified
420 (' ',' '), # blank lines are kept intact
421 ],
422
423 # Tests for the escape transformer to leave normal code alone
424 escaped_noesc =
425 [ (' ', ' '),
426 ('x=1', 'x=1'),
427 ],
428
429 # System calls
430 escaped_shell =
431 [ ('!ls', 'get_ipython().system("ls")'),
432 # Double-escape shell, this means to capture the output of the
433 # subprocess and return it
434 ('!!ls', 'get_ipython().getoutput("ls")'),
435 ],
436
437 # Help/object info
438 escaped_help =
439 [ ('?', 'get_ipython().show_usage()'),
440 ('?x1', 'get_ipython().magic("pinfo x1")'),
441 ('??x2', 'get_ipython().magic("pinfo2 x2")'),
442 ('x3?', 'get_ipython().magic("pinfo x3")'),
443 ('x4??', 'get_ipython().magic("pinfo2 x4")'),
444 ],
445
446 # Explicit magic calls
447 escaped_magic =
448 [ ('%cd', 'get_ipython().magic("cd")'),
449 ('%cd /home', 'get_ipython().magic("cd /home")'),
450 (' %magic', ' get_ipython().magic("magic")'),
451 ],
452
453 # Quoting with separate arguments
454 escaped_quote =
455 [ (',f', 'f("")'),
456 (',f x', 'f("x")'),
457 (' ,f y', ' f("y")'),
458 (',f a b', 'f("a", "b")'),
459 ],
460
461 # Quoting with single argument
462 escaped_quote2 =
463 [ (';f', 'f("")'),
464 (';f x', 'f("x")'),
465 (' ;f y', ' f("y")'),
466 (';f a b', 'f("a b")'),
467 ],
468
469 # Simply apply parens
470 escaped_paren =
471 [ ('/f', 'f()'),
472 ('/f x', 'f(x)'),
473 (' /f y', ' f(y)'),
474 ('/f a b', 'f(a, b)'),
475 ],
476
477 # More complex multiline tests
478 ## escaped_multiline =
479 ## [()],
480 )
481
482 # multiline syntax examples. Each of these should be a list of lists, with
483 # each entry itself having pairs of raw/transformed input. The union (with
484 # '\n'.join() of the transformed inputs is what the splitter should produce
485 # when fed the raw lines one at a time via push.
486 syntax_ml = \
487 dict(classic_prompt =
488 [ [('>>> for i in range(10):','for i in range(10):'),
489 ('... print i',' print i'),
490 ('... ', ''),
491 ],
492 ],
493
494 ipy_prompt =
495 [ [('In [24]: for i in range(10):','for i in range(10):'),
496 (' ....: print i',' print i'),
497 (' ....: ', ''),
498 ],
499 ],
500 )
501
377
502
378 def test_assign_system():
503 def test_assign_system():
379 tests = [('a =! ls', 'a = get_ipython().magic("sc -l = ls")'),
504 transform_checker(syntax['assign_system'], isp.transform_assign_system)
380 ('b = !ls', 'b = get_ipython().magic("sc -l = ls")'),
381 ('x=1','x=1')]
382 transform_checker(tests, isp.transform_assign_system)
383
505
384
506
385 def test_assign_magic():
507 def test_assign_magic():
386 tests = [('a =% who', 'a = get_ipython().magic("who")'),
508 transform_checker(syntax['assign_magic'], isp.transform_assign_magic)
387 ('b = %who', 'b = get_ipython().magic("who")'),
388 ('x=1','x=1')]
389 transform_checker(tests, isp.transform_assign_magic)
390
509
391
510
392 def test_classic_prompt():
511 def test_classic_prompt():
393 tests = [('>>> x=1', 'x=1'),
512 transform_checker(syntax['classic_prompt'], isp.transform_classic_prompt)
394 ('>>> for i in range(10):','for i in range(10):'),
513 for example in syntax_ml['classic_prompt']:
395 ('... print i',' print i'),
514 transform_checker(example, isp.transform_classic_prompt)
396 ('...', ''),
397 ('x=1','x=1')
398 ]
399 transform_checker(tests, isp.transform_classic_prompt)
400
515
401
516
402 def test_ipy_prompt():
517 def test_ipy_prompt():
403 tests = [('In [1]: x=1', 'x=1'),
518 transform_checker(syntax['ipy_prompt'], isp.transform_ipy_prompt)
404 ('In [24]: for i in range(10):','for i in range(10):'),
519 for example in syntax_ml['ipy_prompt']:
405 (' ....: print i',' print i'),
520 transform_checker(example, isp.transform_ipy_prompt)
406 (' ....: ', ''),
521
407 ('x=1', 'x=1'), # normal input is unmodified
522
408 (' ','') # blank lines are just collapsed
523 def test_escaped_noesc():
409 ]
524 transform_checker(syntax['escaped_noesc'], isp.transform_escaped)
410 transform_checker(tests, isp.transform_ipy_prompt)
525
526
527 def test_escaped_shell():
528 transform_checker(syntax['escaped_shell'], isp.transform_escaped)
529
530
531 def test_escaped_help():
532 transform_checker(syntax['escaped_help'], isp.transform_escaped)
533
534
535 def test_escaped_magic():
536 transform_checker(syntax['escaped_magic'], isp.transform_escaped)
537
538
539 def test_escaped_quote():
540 transform_checker(syntax['escaped_quote'], isp.transform_escaped)
541
542
543 def test_escaped_quote2():
544 transform_checker(syntax['escaped_quote2'], isp.transform_escaped)
545
546
547 def test_escaped_paren():
548 transform_checker(syntax['escaped_paren'], isp.transform_escaped)
549
550
551 class IPythonInputTestCase(InputSplitterTestCase):
552 """By just creating a new class whose .isp is a different instance, we
553 re-run the same test battery on the new input splitter.
554
555 In addition, this runs the tests over the syntax and syntax_ml dicts that
556 were tested by individual functions, as part of the OO interface.
557 """
558 def setUp(self):
559 self.isp = isp.IPythonInputSplitter()
560
561 def test_syntax(self):
562 """Call all single-line syntax tests from the main object"""
563 isp = self.isp
564 for example in syntax.itervalues():
565 for raw, out_t in example:
566 if raw.startswith(' '):
567 continue
568
569 isp.push(raw)
570 out = isp.source_reset().rstrip()
571 self.assertEqual(out, out_t)
572
573 def test_syntax_multiline(self):
574 isp = self.isp
575 for example in syntax_ml.itervalues():
576 out_t_parts = []
577 for line_pairs in example:
578 for raw, out_t_part in line_pairs:
579 isp.push(raw)
580 out_t_parts.append(out_t_part)
581
582 out = isp.source_reset().rstrip()
583 out_t = '\n'.join(out_t_parts).rstrip()
584 self.assertEqual(out, out_t)
585
586
587 #-----------------------------------------------------------------------------
588 # Main - use as a script
589 #-----------------------------------------------------------------------------
590
591 if __name__ == '__main__':
592 # A simple demo for interactive experimentation. This code will not get
593 # picked up by any test suite. Useful mostly for illustration and during
594 # development.
595 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
411
596
597 #isp, start_prompt = InputSplitter(), '>>> '
598 isp, start_prompt = IPythonInputSplitter(), 'In> '
599
600 autoindent = True
601 #autoindent = False
602
603 # In practice, this input loop would be wrapped in an outside loop to read
604 # input indefinitely, until some exit/quit command was issued. Here we
605 # only illustrate the basic inner loop.
606 try:
607 while True:
608 prompt = start_prompt
609 while isp.push_accepts_more():
610 indent = ' '*isp.indent_spaces
611 if autoindent:
612 line = indent + raw_input(prompt+indent)
613 else:
614 line = raw_input(prompt)
615 isp.push(line)
616 prompt = '... '
617
618 # Here we just return input so we can use it in a test suite, but a
619 # real interpreter would instead send it for execution somewhere.
620 src = isp.source_reset()
621 print 'Input source was:\n', src # dbg
622 except EOFError:
623 print 'Bye'
@@ -295,8 +295,9 b' def make_quoted_expr(s):'
295 quote = "'''"
295 quote = "'''"
296 else:
296 else:
297 # give up, backslash-escaped string will do
297 # give up, backslash-escaped string will do
298 return '"%s"' % esc_quotes(s)
298 return '"%s"' % esc_quotes(s).strip()
299 res = raw + quote + s + tailpadding + quote + tail
299 txt = (s + tailpadding).strip()
300 res = raw + quote + txt + quote + tail
300 return res
301 return res
301
302
302
303
General Comments 0
You need to be logged in to leave comments. Login now