test_inputsplitter.py
845 lines
| 29.0 KiB
| text/x-python
|
PythonLexer
Fernando Perez
|
r2780 | # -*- coding: utf-8 -*- | ||
Fernando Perez
|
r2663 | """Tests for the inputsplitter module. | ||
Fernando Perez
|
r3297 | |||
Authors | ||||
------- | ||||
* Fernando Perez | ||||
* Robert Kern | ||||
Fernando Perez
|
r2633 | """ | ||
#----------------------------------------------------------------------------- | ||||
Matthias BUSSONNIER
|
r5390 | # Copyright (C) 2010-2011 The IPython Development Team | ||
Fernando Perez
|
r2633 | # | ||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
# stdlib | ||||
import unittest | ||||
Fernando Perez
|
r2718 | import sys | ||
Fernando Perez
|
r2633 | |||
# Third party | ||||
import nose.tools as nt | ||||
# Our own | ||||
Fernando Perez
|
r2663 | from IPython.core import inputsplitter as isp | ||
Thomas Kluyver
|
r4080 | from IPython.testing import tools as tt | ||
Thomas Kluyver
|
r4895 | from IPython.utils import py3compat | ||
Fernando Perez
|
r2663 | |||
#----------------------------------------------------------------------------- | ||||
# Semi-complete examples (also used as tests) | ||||
#----------------------------------------------------------------------------- | ||||
Fernando Perez
|
r2780 | |||
# Note: at the bottom, there's a slightly more complete version of this that | ||||
# can be useful during development of code here. | ||||
Thomas Kluyver
|
r3119 | def mini_interactive_loop(input_func): | ||
Fernando Perez
|
r2663 | """Minimal example of the logic of an interactive interpreter loop. | ||
This serves as an example, and it is used by the test system with a fake | ||||
raw_input that simulates interactive input.""" | ||||
from IPython.core.inputsplitter import InputSplitter | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2663 | isp = InputSplitter() | ||
# In practice, this input loop would be wrapped in an outside loop to read | ||||
# input indefinitely, until some exit/quit command was issued. Here we | ||||
# only illustrate the basic inner loop. | ||||
while isp.push_accepts_more(): | ||||
indent = ' '*isp.indent_spaces | ||||
prompt = '>>> ' + indent | ||||
Thomas Kluyver
|
r3119 | line = indent + input_func(prompt) | ||
Fernando Perez
|
r2663 | isp.push(line) | ||
# Here we just return input so we can use it in a test suite, but a real | ||||
# interpreter would instead send it for execution somewhere. | ||||
src = isp.source_reset() | ||||
Fernando Perez
|
r2780 | #print 'Input source was:\n', src # dbg | ||
Fernando Perez
|
r2663 | return src | ||
Fernando Perez
|
r2633 | |||
#----------------------------------------------------------------------------- | ||||
Fernando Perez
|
r2645 | # Test utilities, just for local use | ||
#----------------------------------------------------------------------------- | ||||
def assemble(block): | ||||
"""Assemble a block into multi-line sub-blocks.""" | ||||
return ['\n'.join(sub_block)+'\n' for sub_block in block] | ||||
Fernando Perez
|
r2663 | |||
def pseudo_input(lines): | ||||
"""Return a function that acts like raw_input but feeds the input list.""" | ||||
ilines = iter(lines) | ||||
def raw_in(prompt): | ||||
try: | ||||
return next(ilines) | ||||
except StopIteration: | ||||
return '' | ||||
return raw_in | ||||
Fernando Perez
|
r2645 | #----------------------------------------------------------------------------- | ||
Fernando Perez
|
r2633 | # Tests | ||
#----------------------------------------------------------------------------- | ||||
def test_spaces(): | ||||
tests = [('', 0), | ||||
(' ', 1), | ||||
('\n', 0), | ||||
(' \n', 1), | ||||
('x', 0), | ||||
(' x', 1), | ||||
(' x',2), | ||||
(' x',4), | ||||
# Note: tabs are counted as a single whitespace! | ||||
('\tx', 1), | ||||
('\t x', 2), | ||||
] | ||||
Thomas Kluyver
|
r4080 | tt.check_pairs(isp.num_ini_spaces, tests) | ||
Fernando Perez
|
r2633 | |||
def test_remove_comments(): | ||||
tests = [('text', 'text'), | ||||
('text # comment', 'text '), | ||||
('text # comment\n', 'text \n'), | ||||
('text # comment \n', 'text \n'), | ||||
('line # c \nline\n','line \nline\n'), | ||||
('line # c \nline#c2 \nline\nline #c\n\n', | ||||
'line \nline\nline\nline \n\n'), | ||||
] | ||||
Thomas Kluyver
|
r4080 | tt.check_pairs(isp.remove_comments, tests) | ||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r4080 | def test_has_comment(): | ||
tests = [('text', False), | ||||
('text #comment', True), | ||||
('text #comment\n', True), | ||||
('#comment', True), | ||||
('#comment\n', True), | ||||
('a = "#string"', False), | ||||
('a = "#string" # comment', True), | ||||
('a #comment not "string"', True), | ||||
] | ||||
tt.check_pairs(isp.has_comment, tests) | ||||
Fernando Perez
|
r2633 | |||
def test_get_input_encoding(): | ||||
Fernando Perez
|
r2663 | encoding = isp.get_input_encoding() | ||
Fernando Perez
|
r2633 | nt.assert_true(isinstance(encoding, basestring)) | ||
# simple-minded check that at least encoding a simple string works with the | ||||
# encoding we got. | ||||
Thomas Kluyver
|
r4731 | nt.assert_equal(u'test'.encode(encoding), b'test') | ||
Fernando Perez
|
r2633 | |||
Fernando Perez
|
r2718 | class NoInputEncodingTestCase(unittest.TestCase): | ||
def setUp(self): | ||||
self.old_stdin = sys.stdin | ||||
class X: pass | ||||
fake_stdin = X() | ||||
sys.stdin = fake_stdin | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2718 | def test(self): | ||
# Verify that if sys.stdin has no 'encoding' attribute we do the right | ||||
# thing | ||||
enc = isp.get_input_encoding() | ||||
self.assertEqual(enc, 'ascii') | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2718 | def tearDown(self): | ||
sys.stdin = self.old_stdin | ||||
Fernando Perez
|
r2663 | class InputSplitterTestCase(unittest.TestCase): | ||
Fernando Perez
|
r2633 | def setUp(self): | ||
Fernando Perez
|
r2663 | self.isp = isp.InputSplitter() | ||
Fernando Perez
|
r2633 | |||
def test_reset(self): | ||||
Fernando Perez
|
r2663 | isp = self.isp | ||
isp.push('x=1') | ||||
isp.reset() | ||||
self.assertEqual(isp._buffer, []) | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
self.assertEqual(isp.source, '') | ||||
self.assertEqual(isp.code, None) | ||||
self.assertEqual(isp._is_complete, False) | ||||
Fernando Perez
|
r2633 | |||
def test_source(self): | ||||
Fernando Perez
|
r2663 | self.isp._store('1') | ||
self.isp._store('2') | ||||
self.assertEqual(self.isp.source, '1\n2\n') | ||||
self.assertTrue(len(self.isp._buffer)>0) | ||||
self.assertEqual(self.isp.source_reset(), '1\n2\n') | ||||
self.assertEqual(self.isp._buffer, []) | ||||
self.assertEqual(self.isp.source, '') | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2633 | def test_indent(self): | ||
Fernando Perez
|
r2663 | isp = self.isp # shorthand | ||
isp.push('x=1') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
isp.push('if 1:\n x=1') | ||||
self.assertEqual(isp.indent_spaces, 4) | ||||
isp.push('y=2\n') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
Fernando Perez
|
r3085 | |||
def test_indent2(self): | ||||
# In cell mode, inputs must be fed in whole blocks, so skip this test | ||||
if self.isp.input_mode == 'cell': return | ||||
isp = self.isp | ||||
Fernando Perez
|
r2663 | isp.push('if 1:') | ||
self.assertEqual(isp.indent_spaces, 4) | ||||
isp.push(' x=1') | ||||
self.assertEqual(isp.indent_spaces, 4) | ||||
Fernando Perez
|
r2633 | # Blank lines shouldn't change the indent level | ||
Fernando Perez
|
r2663 | isp.push(' '*2) | ||
self.assertEqual(isp.indent_spaces, 4) | ||||
Fernando Perez
|
r2633 | |||
Fernando Perez
|
r3085 | def test_indent3(self): | ||
# In cell mode, inputs must be fed in whole blocks, so skip this test | ||||
if self.isp.input_mode == 'cell': return | ||||
Fernando Perez
|
r2663 | isp = self.isp | ||
Fernando Perez
|
r2633 | # When a multiline statement contains parens or multiline strings, we | ||
# shouldn't get confused. | ||||
Fernando Perez
|
r2663 | isp.push("if 1:") | ||
isp.push(" x = (1+\n 2)") | ||||
self.assertEqual(isp.indent_spaces, 4) | ||||
Bernardo B. Marques
|
r4872 | |||
Paul Ivanov
|
r4204 | def test_indent4(self): | ||
# In cell mode, inputs must be fed in whole blocks, so skip this test | ||||
if self.isp.input_mode == 'cell': return | ||||
isp = self.isp | ||||
# whitespace after ':' should not screw up indent level | ||||
isp.push('if 1: \n x=1') | ||||
self.assertEqual(isp.indent_spaces, 4) | ||||
isp.push('y=2\n') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
isp.push('if 1:\t\n x=1') | ||||
self.assertEqual(isp.indent_spaces, 4) | ||||
isp.push('y=2\n') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
Fernando Perez
|
r2633 | |||
David Warde-Farley
|
r3693 | def test_dedent_pass(self): | ||
Fernando Perez
|
r2663 | isp = self.isp # shorthand | ||
David Warde-Farley
|
r3693 | # should NOT cause dedent | ||
isp.push('if 1:\n passes = 5') | ||||
Fernando Perez
|
r2663 | self.assertEqual(isp.indent_spaces, 4) | ||
David Warde-Farley
|
r3693 | isp.push('if 1:\n pass') | ||
Fernando Perez
|
r2663 | self.assertEqual(isp.indent_spaces, 0) | ||
David Warde-Farley
|
r3693 | isp.push('if 1:\n pass ') | ||
self.assertEqual(isp.indent_spaces, 0) | ||||
Aaron Meurer
|
r7824 | def test_dedent_break(self): | ||
isp = self.isp # shorthand | ||||
# should NOT cause dedent | ||||
isp.push('while 1:\n breaks = 5') | ||||
self.assertEqual(isp.indent_spaces, 4) | ||||
isp.push('while 1:\n break') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
isp.push('while 1:\n break ') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
def test_dedent_continue(self): | ||||
isp = self.isp # shorthand | ||||
# should NOT cause dedent | ||||
isp.push('while 1:\n continues = 5') | ||||
self.assertEqual(isp.indent_spaces, 4) | ||||
isp.push('while 1:\n continue') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
isp.push('while 1:\n continue ') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
David Warde-Farley
|
r3693 | def test_dedent_raise(self): | ||
isp = self.isp # shorthand | ||||
# should NOT cause dedent | ||||
isp.push('if 1:\n raised = 4') | ||||
self.assertEqual(isp.indent_spaces, 4) | ||||
isp.push('if 1:\n raise TypeError()') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
isp.push('if 1:\n raise') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
isp.push('if 1:\n raise ') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
def test_dedent_return(self): | ||||
isp = self.isp # shorthand | ||||
# should NOT cause dedent | ||||
isp.push('if 1:\n returning = 4') | ||||
self.assertEqual(isp.indent_spaces, 4) | ||||
isp.push('if 1:\n return 5 + 493') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
isp.push('if 1:\n return') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
isp.push('if 1:\n return ') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
isp.push('if 1:\n return(0)') | ||||
self.assertEqual(isp.indent_spaces, 0) | ||||
Fernando Perez
|
r2633 | def test_push(self): | ||
Fernando Perez
|
r2663 | isp = self.isp | ||
self.assertTrue(isp.push('x=1')) | ||||
Fernando Perez
|
r2633 | |||
def test_push2(self): | ||||
Fernando Perez
|
r2663 | isp = self.isp | ||
self.assertFalse(isp.push('if 1:')) | ||||
Fernando Perez
|
r2633 | for line in [' x=1', '# a comment', ' y=2']: | ||
Fernando Perez
|
r2663 | self.assertTrue(isp.push(line)) | ||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r3747 | def test_push3(self): | ||
isp = self.isp | ||||
isp.push('if True:') | ||||
isp.push(' a = 1') | ||||
self.assertFalse(isp.push('b = [1,')) | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2634 | def test_replace_mode(self): | ||
Fernando Perez
|
r2663 | isp = self.isp | ||
Fernando Perez
|
r3004 | isp.input_mode = 'cell' | ||
Fernando Perez
|
r2663 | isp.push('x=1') | ||
self.assertEqual(isp.source, 'x=1\n') | ||||
isp.push('x=2') | ||||
self.assertEqual(isp.source, 'x=2\n') | ||||
def test_push_accepts_more(self): | ||||
isp = self.isp | ||||
isp.push('x=1') | ||||
self.assertFalse(isp.push_accepts_more()) | ||||
def test_push_accepts_more2(self): | ||||
Fernando Perez
|
r3085 | # In cell mode, inputs must be fed in whole blocks, so skip this test | ||
if self.isp.input_mode == 'cell': return | ||||
Fernando Perez
|
r2663 | isp = self.isp | ||
isp.push('if 1:') | ||||
self.assertTrue(isp.push_accepts_more()) | ||||
isp.push(' x=1') | ||||
self.assertTrue(isp.push_accepts_more()) | ||||
isp.push('') | ||||
self.assertFalse(isp.push_accepts_more()) | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2663 | def test_push_accepts_more3(self): | ||
isp = self.isp | ||||
isp.push("x = (2+\n3)") | ||||
self.assertFalse(isp.push_accepts_more()) | ||||
Fernando Perez
|
r2633 | |||
Fernando Perez
|
r2663 | def test_push_accepts_more4(self): | ||
Fernando Perez
|
r3085 | # In cell mode, inputs must be fed in whole blocks, so skip this test | ||
if self.isp.input_mode == 'cell': return | ||||
Fernando Perez
|
r2663 | isp = self.isp | ||
Fernando Perez
|
r2633 | # When a multiline statement contains parens or multiline strings, we | ||
# shouldn't get confused. | ||||
# FIXME: we should be able to better handle de-dents in statements like | ||||
# multiline strings and multiline expressions (continued with \ or | ||||
# parens). Right now we aren't handling the indentation tracking quite | ||||
# correctly with this, though in practice it may not be too much of a | ||||
# problem. We'll need to see. | ||||
Fernando Perez
|
r2663 | isp.push("if 1:") | ||
isp.push(" x = (2+") | ||||
isp.push(" 3)") | ||||
self.assertTrue(isp.push_accepts_more()) | ||||
isp.push(" y = 3") | ||||
self.assertTrue(isp.push_accepts_more()) | ||||
isp.push('') | ||||
self.assertFalse(isp.push_accepts_more()) | ||||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r3461 | def test_push_accepts_more5(self): | ||
# In cell mode, inputs must be fed in whole blocks, so skip this test | ||||
if self.isp.input_mode == 'cell': return | ||||
isp = self.isp | ||||
isp.push('try:') | ||||
isp.push(' a = 5') | ||||
isp.push('except:') | ||||
isp.push(' raise') | ||||
self.assertTrue(isp.push_accepts_more()) | ||||
Fernando Perez
|
r3013 | |||
def test_continuation(self): | ||||
isp = self.isp | ||||
isp.push("import os, \\") | ||||
self.assertTrue(isp.push_accepts_more()) | ||||
isp.push("sys") | ||||
self.assertFalse(isp.push_accepts_more()) | ||||
Fernando Perez
|
r2635 | |||
def test_syntax_error(self): | ||||
Fernando Perez
|
r2663 | isp = self.isp | ||
Fernando Perez
|
r2635 | # Syntax errors immediately produce a 'ready' block, so the invalid | ||
# Python can be sent to the kernel for evaluation with possible ipython | ||||
# special-syntax conversion. | ||||
Fernando Perez
|
r2663 | isp.push('run foo') | ||
self.assertFalse(isp.push_accepts_more()) | ||||
Fernando Perez
|
r2645 | |||
Fernando Perez
|
r3126 | def test_unicode(self): | ||
self.isp.push(u"Pérez") | ||||
self.isp.push(u'\xc3\xa9') | ||||
Thomas Kluyver
|
r3455 | self.isp.push(u"u'\xc3\xa9'") | ||
Fernando Perez
|
r2663 | |||
Aaron Meurer
|
r7823 | def test_line_continuation(self): | ||
""" Test issue #2108.""" | ||||
isp = self.isp | ||||
# A blank line after a line continuation should not accept more | ||||
isp.push("1 \\\n\n") | ||||
self.assertFalse(isp.push_accepts_more()) | ||||
# Whitespace after a \ is a SyntaxError. The only way to test that | ||||
# here is to test that push doesn't accept more (as with | ||||
# test_syntax_error() above). | ||||
isp.push(r"1 \ ") | ||||
self.assertFalse(isp.push_accepts_more()) | ||||
# Even if the line is continuable (c.f. the regular Python | ||||
# interpreter) | ||||
isp.push(r"(1 \ ") | ||||
self.assertFalse(isp.push_accepts_more()) | ||||
Fernando Perez
|
r2663 | class InteractiveLoopTestCase(unittest.TestCase): | ||
"""Tests for an interactive loop like a python shell. | ||||
""" | ||||
def check_ns(self, lines, ns): | ||||
"""Validate that the given input lines produce the resulting namespace. | ||||
Note: the input lines are given exactly as they would be typed in an | ||||
auto-indenting environment, as mini_interactive_loop above already does | ||||
auto-indenting and prepends spaces to the input. | ||||
""" | ||||
src = mini_interactive_loop(pseudo_input(lines)) | ||||
test_ns = {} | ||||
exec src in test_ns | ||||
# We can't check that the provided ns is identical to the test_ns, | ||||
# because Python fills test_ns with extra keys (copyright, etc). But | ||||
# we can check that the given dict is *contained* in test_ns | ||||
Thomas Kluyver
|
r3114 | for k,v in ns.iteritems(): | ||
Fernando Perez
|
r2663 | self.assertEqual(test_ns[k], v) | ||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2663 | def test_simple(self): | ||
self.check_ns(['x=1'], dict(x=1)) | ||||
def test_simple2(self): | ||||
self.check_ns(['if 1:', 'x=2'], dict(x=2)) | ||||
def test_xy(self): | ||||
self.check_ns(['x=1; y=2'], dict(x=1, y=2)) | ||||
def test_abc(self): | ||||
self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3)) | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2663 | def test_multi(self): | ||
self.check_ns(['x =(1+','1+','2)'], dict(x=4)) | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2719 | |||
Fernando Perez
|
r2780 | def test_LineInfo(): | ||
"""Simple test for LineInfo construction and str()""" | ||||
linfo = isp.LineInfo(' %cd /home') | ||||
Bradley M. Froehle
|
r7875 | nt.assert_equal(str(linfo), 'LineInfo [ |%|cd|/home]') | ||
Fernando Perez
|
r2780 | |||
Fernando Perez
|
r2719 | # Transformer tests | ||
def transform_checker(tests, func): | ||||
"""Utility to loop over test inputs""" | ||||
for inp, tr in tests: | ||||
Bradley M. Froehle
|
r7875 | nt.assert_equal(func(inp), tr) | ||
Fernando Perez
|
r2780 | |||
# Data for all the syntax tests in the form of lists of pairs of | ||||
# raw/transformed input. We store it here as a global dict so that we can use | ||||
# it both within single-function tests and also to validate the behavior of the | ||||
# larger objects | ||||
syntax = \ | ||||
dict(assign_system = | ||||
Thomas Kluyver
|
r4895 | [(i,py3compat.u_format(o)) for i,o in \ | ||
Thomas Kluyver
|
r5350 | [(u'a =! ls', "a = get_ipython().getoutput({u}'ls')"), | ||
(u'b = !ls', "b = get_ipython().getoutput({u}'ls')"), | ||||
Fernando Perez
|
r2780 | ('x=1', 'x=1'), # normal input is unmodified | ||
(' ',' '), # blank lines are kept intact | ||||
Thomas Kluyver
|
r4895 | ]], | ||
Fernando Perez
|
r2780 | |||
assign_magic = | ||||
Thomas Kluyver
|
r4895 | [(i,py3compat.u_format(o)) for i,o in \ | ||
Thomas Kluyver
|
r5350 | [(u'a =% who', "a = get_ipython().magic({u}'who')"), | ||
(u'b = %who', "b = get_ipython().magic({u}'who')"), | ||||
Fernando Perez
|
r2780 | ('x=1', 'x=1'), # normal input is unmodified | ||
(' ',' '), # blank lines are kept intact | ||||
Thomas Kluyver
|
r4895 | ]], | ||
Fernando Perez
|
r2780 | |||
classic_prompt = | ||||
[('>>> x=1', 'x=1'), | ||||
('x=1', 'x=1'), # normal input is unmodified | ||||
Fernando Perez
|
r2861 | (' ', ' '), # blank lines are kept intact | ||
('... ', ''), # continuation prompts | ||||
Fernando Perez
|
r2780 | ], | ||
ipy_prompt = | ||||
[('In [1]: x=1', 'x=1'), | ||||
('x=1', 'x=1'), # normal input is unmodified | ||||
(' ',' '), # blank lines are kept intact | ||||
Fernando Perez
|
r2861 | (' ....: ', ''), # continuation prompts | ||
Fernando Perez
|
r2780 | ], | ||
# Tests for the escape transformer to leave normal code alone | ||||
escaped_noesc = | ||||
[ (' ', ' '), | ||||
('x=1', 'x=1'), | ||||
], | ||||
# System calls | ||||
escaped_shell = | ||||
Thomas Kluyver
|
r4895 | [(i,py3compat.u_format(o)) for i,o in \ | ||
Thomas Kluyver
|
r5350 | [ (u'!ls', "get_ipython().system({u}'ls')"), | ||
Fernando Perez
|
r2780 | # Double-escape shell, this means to capture the output of the | ||
# subprocess and return it | ||||
Thomas Kluyver
|
r5350 | (u'!!ls', "get_ipython().getoutput({u}'ls')"), | ||
Thomas Kluyver
|
r4895 | ]], | ||
Fernando Perez
|
r2780 | |||
# Help/object info | ||||
escaped_help = | ||||
Thomas Kluyver
|
r4895 | [(i,py3compat.u_format(o)) for i,o in \ | ||
Thomas Kluyver
|
r5350 | [ (u'?', 'get_ipython().show_usage()'), | ||
(u'?x1', "get_ipython().magic({u}'pinfo x1')"), | ||||
(u'??x2', "get_ipython().magic({u}'pinfo2 x2')"), | ||||
(u'?a.*s', "get_ipython().magic({u}'psearch a.*s')"), | ||||
Fernando Perez
|
r6999 | (u'?%hist1', "get_ipython().magic({u}'pinfo %hist1')"), | ||
(u'?%%hist2', "get_ipython().magic({u}'pinfo %%hist2')"), | ||||
Thomas Kluyver
|
r5350 | (u'?abc = qwe', "get_ipython().magic({u}'pinfo abc')"), | ||
Thomas Kluyver
|
r4895 | ]], | ||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r4076 | end_help = | ||
Thomas Kluyver
|
r4895 | [(i,py3compat.u_format(o)) for i,o in \ | ||
Thomas Kluyver
|
r5350 | [ (u'x3?', "get_ipython().magic({u}'pinfo x3')"), | ||
(u'x4??', "get_ipython().magic({u}'pinfo2 x4')"), | ||||
Fernando Perez
|
r6999 | (u'%hist1?', "get_ipython().magic({u}'pinfo %hist1')"), | ||
(u'%hist2??', "get_ipython().magic({u}'pinfo2 %hist2')"), | ||||
(u'%%hist3?', "get_ipython().magic({u}'pinfo %%hist3')"), | ||||
(u'%%hist4??', "get_ipython().magic({u}'pinfo2 %%hist4')"), | ||||
Thomas Kluyver
|
r5350 | (u'f*?', "get_ipython().magic({u}'psearch f*')"), | ||
(u'ax.*aspe*?', "get_ipython().magic({u}'psearch ax.*aspe*')"), | ||||
Fernando Perez
|
r6974 | (u'a = abc?', "get_ipython().set_next_input({u}'a = abc');" | ||
"get_ipython().magic({u}'pinfo abc')"), | ||||
(u'a = abc.qe??', "get_ipython().set_next_input({u}'a = abc.qe');" | ||||
"get_ipython().magic({u}'pinfo2 abc.qe')"), | ||||
(u'a = *.items?', "get_ipython().set_next_input({u}'a = *.items');" | ||||
"get_ipython().magic({u}'psearch *.items')"), | ||||
(u'plot(a?', "get_ipython().set_next_input({u}'plot(a');" | ||||
"get_ipython().magic({u}'pinfo a')"), | ||||
Thomas Kluyver
|
r5350 | (u'a*2 #comment?', 'a*2 #comment?'), | ||
Thomas Kluyver
|
r4895 | ]], | ||
Fernando Perez
|
r2780 | |||
# Explicit magic calls | ||||
escaped_magic = | ||||
Thomas Kluyver
|
r4895 | [(i,py3compat.u_format(o)) for i,o in \ | ||
Thomas Kluyver
|
r5350 | [ (u'%cd', "get_ipython().magic({u}'cd')"), | ||
(u'%cd /home', "get_ipython().magic({u}'cd /home')"), | ||||
# Backslashes need to be escaped. | ||||
(u'%cd C:\\User', "get_ipython().magic({u}'cd C:\\\\User')"), | ||||
(u' %magic', " get_ipython().magic({u}'magic')"), | ||||
Thomas Kluyver
|
r4895 | ]], | ||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2780 | # Quoting with separate arguments | ||
escaped_quote = | ||||
[ (',f', 'f("")'), | ||||
(',f x', 'f("x")'), | ||||
(' ,f y', ' f("y")'), | ||||
(',f a b', 'f("a", "b")'), | ||||
], | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2780 | # Quoting with single argument | ||
Bernardo B. Marques
|
r4872 | escaped_quote2 = | ||
Fernando Perez
|
r2780 | [ (';f', 'f("")'), | ||
(';f x', 'f("x")'), | ||||
(' ;f y', ' f("y")'), | ||||
(';f a b', 'f("a b")'), | ||||
], | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2780 | # Simply apply parens | ||
Bernardo B. Marques
|
r4872 | escaped_paren = | ||
Fernando Perez
|
r2780 | [ ('/f', 'f()'), | ||
('/f x', 'f(x)'), | ||||
(' /f y', ' f(y)'), | ||||
('/f a b', 'f(a, b)'), | ||||
], | ||||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r4078 | # Check that we transform prompts before other transforms | ||
mixed = | ||||
Thomas Kluyver
|
r4895 | [(i,py3compat.u_format(o)) for i,o in \ | ||
Thomas Kluyver
|
r5350 | [ (u'In [1]: %lsmagic', "get_ipython().magic({u}'lsmagic')"), | ||
(u'>>> %lsmagic', "get_ipython().magic({u}'lsmagic')"), | ||||
(u'In [2]: !ls', "get_ipython().system({u}'ls')"), | ||||
(u'In [3]: abs?', "get_ipython().magic({u}'pinfo abs')"), | ||||
(u'In [4]: b = %who', "b = get_ipython().magic({u}'who')"), | ||||
Thomas Kluyver
|
r4895 | ]], | ||
Fernando Perez
|
r2780 | ) | ||
# multiline syntax examples. Each of these should be a list of lists, with | ||||
# each entry itself having pairs of raw/transformed input. The union (with | ||||
# '\n'.join() of the transformed inputs is what the splitter should produce | ||||
# when fed the raw lines one at a time via push. | ||||
syntax_ml = \ | ||||
dict(classic_prompt = | ||||
[ [('>>> for i in range(10):','for i in range(10):'), | ||||
('... print i',' print i'), | ||||
('... ', ''), | ||||
], | ||||
], | ||||
ipy_prompt = | ||||
[ [('In [24]: for i in range(10):','for i in range(10):'), | ||||
(' ....: print i',' print i'), | ||||
(' ....: ', ''), | ||||
], | ||||
], | ||||
Daniel Velkov
|
r5088 | |||
multiline_datastructure = | ||||
[ [('>>> a = [1,','a = [1,'), | ||||
('... 2]','2]'), | ||||
], | ||||
], | ||||
Fernando Perez
|
r2780 | ) | ||
Fernando Perez
|
r2719 | |||
def test_assign_system(): | ||||
Thomas Kluyver
|
r4080 | tt.check_pairs(isp.transform_assign_system, syntax['assign_system']) | ||
Fernando Perez
|
r2719 | |||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2719 | def test_assign_magic(): | ||
Thomas Kluyver
|
r4080 | tt.check_pairs(isp.transform_assign_magic, syntax['assign_magic']) | ||
Fernando Perez
|
r2719 | |||
def test_classic_prompt(): | ||||
Fernando Perez
|
r2780 | transform_checker(syntax['classic_prompt'], isp.transform_classic_prompt) | ||
for example in syntax_ml['classic_prompt']: | ||||
transform_checker(example, isp.transform_classic_prompt) | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2719 | |||
def test_ipy_prompt(): | ||||
Fernando Perez
|
r2780 | transform_checker(syntax['ipy_prompt'], isp.transform_ipy_prompt) | ||
for example in syntax_ml['ipy_prompt']: | ||||
transform_checker(example, isp.transform_ipy_prompt) | ||||
Thomas Kluyver
|
r4076 | |||
def test_end_help(): | ||||
Thomas Kluyver
|
r4080 | tt.check_pairs(isp.transform_help_end, syntax['end_help']) | ||
Fernando Perez
|
r2780 | |||
def test_escaped_noesc(): | ||||
Thomas Kluyver
|
r4080 | tt.check_pairs(isp.transform_escaped, syntax['escaped_noesc']) | ||
Fernando Perez
|
r2780 | |||
def test_escaped_shell(): | ||||
Thomas Kluyver
|
r4080 | tt.check_pairs(isp.transform_escaped, syntax['escaped_shell']) | ||
Fernando Perez
|
r2780 | |||
def test_escaped_help(): | ||||
Thomas Kluyver
|
r4080 | tt.check_pairs(isp.transform_escaped, syntax['escaped_help']) | ||
Fernando Perez
|
r2780 | |||
def test_escaped_magic(): | ||||
Thomas Kluyver
|
r4080 | tt.check_pairs(isp.transform_escaped, syntax['escaped_magic']) | ||
Fernando Perez
|
r2780 | |||
def test_escaped_quote(): | ||||
Thomas Kluyver
|
r4080 | tt.check_pairs(isp.transform_escaped, syntax['escaped_quote']) | ||
Fernando Perez
|
r2780 | |||
def test_escaped_quote2(): | ||||
Thomas Kluyver
|
r4080 | tt.check_pairs(isp.transform_escaped, syntax['escaped_quote2']) | ||
Fernando Perez
|
r2780 | |||
def test_escaped_paren(): | ||||
Thomas Kluyver
|
r4080 | tt.check_pairs(isp.transform_escaped, syntax['escaped_paren']) | ||
Fernando Perez
|
r2780 | |||
class IPythonInputTestCase(InputSplitterTestCase): | ||||
"""By just creating a new class whose .isp is a different instance, we | ||||
re-run the same test battery on the new input splitter. | ||||
In addition, this runs the tests over the syntax and syntax_ml dicts that | ||||
were tested by individual functions, as part of the OO interface. | ||||
Fernando Perez
|
r3080 | |||
It also makes some checks on the raw buffer storage. | ||||
Fernando Perez
|
r2780 | """ | ||
Fernando Perez
|
r2861 | |||
Fernando Perez
|
r2780 | def setUp(self): | ||
Fernando Perez
|
r2862 | self.isp = isp.IPythonInputSplitter(input_mode='line') | ||
Fernando Perez
|
r2780 | |||
def test_syntax(self): | ||||
"""Call all single-line syntax tests from the main object""" | ||||
isp = self.isp | ||||
for example in syntax.itervalues(): | ||||
for raw, out_t in example: | ||||
if raw.startswith(' '): | ||||
continue | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r7000 | isp.push(raw+'\n') | ||
Fernando Perez
|
r3080 | out, out_raw = isp.source_raw_reset() | ||
Thomas Kluyver
|
r4746 | self.assertEqual(out.rstrip(), out_t, | ||
tt.pair_fail_msg.format("inputsplitter",raw, out_t, out)) | ||||
Fernando Perez
|
r3080 | self.assertEqual(out_raw.rstrip(), raw.rstrip()) | ||
Fernando Perez
|
r2861 | |||
Fernando Perez
|
r2780 | def test_syntax_multiline(self): | ||
isp = self.isp | ||||
for example in syntax_ml.itervalues(): | ||||
out_t_parts = [] | ||||
Fernando Perez
|
r3080 | raw_parts = [] | ||
Fernando Perez
|
r2780 | for line_pairs in example: | ||
Fernando Perez
|
r3080 | for lraw, out_t_part in line_pairs: | ||
isp.push(lraw) | ||||
Fernando Perez
|
r2780 | out_t_parts.append(out_t_part) | ||
Fernando Perez
|
r3080 | raw_parts.append(lraw) | ||
Fernando Perez
|
r2780 | |||
Fernando Perez
|
r3080 | out, out_raw = isp.source_raw_reset() | ||
Fernando Perez
|
r2780 | out_t = '\n'.join(out_t_parts).rstrip() | ||
Fernando Perez
|
r3080 | raw = '\n'.join(raw_parts).rstrip() | ||
self.assertEqual(out.rstrip(), out_t) | ||||
self.assertEqual(out_raw.rstrip(), raw) | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2861 | |||
class BlockIPythonInputTestCase(IPythonInputTestCase): | ||||
# Deactivate tests that don't make sense for the block mode | ||||
test_push3 = test_split = lambda s: None | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2861 | def setUp(self): | ||
Fernando Perez
|
r3004 | self.isp = isp.IPythonInputSplitter(input_mode='cell') | ||
Fernando Perez
|
r2861 | |||
def test_syntax_multiline(self): | ||||
isp = self.isp | ||||
for example in syntax_ml.itervalues(): | ||||
raw_parts = [] | ||||
out_t_parts = [] | ||||
for line_pairs in example: | ||||
for raw, out_t_part in line_pairs: | ||||
raw_parts.append(raw) | ||||
out_t_parts.append(out_t_part) | ||||
raw = '\n'.join(raw_parts) | ||||
out_t = '\n'.join(out_t_parts) | ||||
isp.push(raw) | ||||
Fernando Perez
|
r3080 | out, out_raw = isp.source_raw_reset() | ||
Fernando Perez
|
r2861 | # Match ignoring trailing whitespace | ||
self.assertEqual(out.rstrip(), out_t.rstrip()) | ||||
Fernando Perez
|
r3080 | self.assertEqual(out_raw.rstrip(), raw.rstrip()) | ||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r7488 | def test_syntax_multiline_cell(self): | ||
isp = self.isp | ||||
for example in syntax_ml.itervalues(): | ||||
Aaron Meurer
|
r7823 | |||
Fernando Perez
|
r7488 | out_t_parts = [] | ||
for line_pairs in example: | ||||
raw = '\n'.join(r for r, _ in line_pairs) | ||||
out_t = '\n'.join(t for _,t in line_pairs) | ||||
out = isp.transform_cell(raw) | ||||
# Match ignoring trailing whitespace | ||||
self.assertEqual(out.rstrip(), out_t.rstrip()) | ||||
Fernando Perez
|
r2861 | |||
Fernando Perez
|
r2780 | #----------------------------------------------------------------------------- | ||
Fernando Perez
|
r2861 | # Main - use as a script, mostly for developer experiments | ||
Fernando Perez
|
r2780 | #----------------------------------------------------------------------------- | ||
if __name__ == '__main__': | ||||
# A simple demo for interactive experimentation. This code will not get | ||||
Fernando Perez
|
r2861 | # picked up by any test suite. | ||
Fernando Perez
|
r2780 | from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter | ||
Fernando Perez
|
r2782 | |||
# configure here the syntax to use, prompt and whether to autoindent | ||||
Fernando Perez
|
r2780 | #isp, start_prompt = InputSplitter(), '>>> ' | ||
isp, start_prompt = IPythonInputSplitter(), 'In> ' | ||||
autoindent = True | ||||
#autoindent = False | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2780 | try: | ||
while True: | ||||
prompt = start_prompt | ||||
while isp.push_accepts_more(): | ||||
indent = ' '*isp.indent_spaces | ||||
if autoindent: | ||||
line = indent + raw_input(prompt+indent) | ||||
else: | ||||
line = raw_input(prompt) | ||||
isp.push(line) | ||||
prompt = '... ' | ||||
# Here we just return input so we can use it in a test suite, but a | ||||
# real interpreter would instead send it for execution somewhere. | ||||
Fernando Perez
|
r2828 | #src = isp.source; raise EOFError # dbg | ||
Fernando Perez
|
r3080 | src, raw = isp.source_raw_reset() | ||
Fernando Perez
|
r2782 | print 'Input source was:\n', src | ||
Fernando Perez
|
r3080 | print 'Raw source was:\n', raw | ||
Fernando Perez
|
r2780 | except EOFError: | ||
print 'Bye' | ||||
Fernando Perez
|
r6985 | |||
# Tests for cell magics support | ||||
def test_last_blank(): | ||||
nt.assert_false(isp.last_blank('')) | ||||
nt.assert_false(isp.last_blank('abc')) | ||||
nt.assert_false(isp.last_blank('abc\n')) | ||||
nt.assert_false(isp.last_blank('abc\na')) | ||||
nt.assert_true(isp.last_blank('\n')) | ||||
nt.assert_true(isp.last_blank('\n ')) | ||||
nt.assert_true(isp.last_blank('abc\n ')) | ||||
nt.assert_true(isp.last_blank('abc\n\n')) | ||||
nt.assert_true(isp.last_blank('abc\nd\n\n')) | ||||
nt.assert_true(isp.last_blank('abc\nd\ne\n\n')) | ||||
nt.assert_true(isp.last_blank('abc \n \n \n\n')) | ||||
def test_last_two_blanks(): | ||||
nt.assert_false(isp.last_two_blanks('')) | ||||
nt.assert_false(isp.last_two_blanks('abc')) | ||||
nt.assert_false(isp.last_two_blanks('abc\n')) | ||||
nt.assert_false(isp.last_two_blanks('abc\n\na')) | ||||
nt.assert_false(isp.last_two_blanks('abc\n \n')) | ||||
nt.assert_false(isp.last_two_blanks('abc\n\n')) | ||||
nt.assert_true(isp.last_two_blanks('\n\n')) | ||||
nt.assert_true(isp.last_two_blanks('\n\n ')) | ||||
nt.assert_true(isp.last_two_blanks('\n \n')) | ||||
nt.assert_true(isp.last_two_blanks('abc\n\n ')) | ||||
nt.assert_true(isp.last_two_blanks('abc\n\n\n')) | ||||
nt.assert_true(isp.last_two_blanks('abc\n\n \n')) | ||||
nt.assert_true(isp.last_two_blanks('abc\n\n \n ')) | ||||
nt.assert_true(isp.last_two_blanks('abc\n\n \n \n')) | ||||
nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n')) | ||||
nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n')) | ||||
Fernando Perez
|
r7004 | class CellMagicsCommon(object): | ||
Aaron Meurer
|
r7823 | |||
Fernando Perez
|
r6985 | def test_whole_cell(self): | ||
src = "%%cellm line\nbody\n" | ||||
sp = self.sp | ||||
sp.push(src) | ||||
nt.assert_equal(sp.cell_magic_parts, ['body\n']) | ||||
out = sp.source | ||||
Fernando Perez
|
r7004 | ref = u"get_ipython()._run_cached_cell_magic({u}'cellm', {u}'line')\n" | ||
nt.assert_equal(out, py3compat.u_format(ref)) | ||||
def tearDown(self): | ||||
self.sp.reset() | ||||
class CellModeCellMagics(CellMagicsCommon, unittest.TestCase): | ||||
sp = isp.IPythonInputSplitter(input_mode='cell') | ||||
Fernando Perez
|
r6985 | |||
def test_incremental(self): | ||||
sp = self.sp | ||||
src = '%%cellm line2\n' | ||||
sp.push(src) | ||||
nt.assert_true(sp.push_accepts_more()) #1 | ||||
src += '\n' | ||||
sp.push(src) | ||||
# Note: if we ever change the logic to allow full blank lines (see | ||||
# _handle_cell_magic), then the following test should change to true | ||||
nt.assert_false(sp.push_accepts_more()) #2 | ||||
# By now, even with full blanks allowed, a second blank should signal | ||||
# the end. For now this test is only a redundancy safety, but don't | ||||
# delete it in case we change our mind and the previous one goes to | ||||
# true. | ||||
src += '\n' | ||||
sp.push(src) | ||||
nt.assert_false(sp.push_accepts_more()) #3 | ||||
Fernando Perez
|
r7004 | class LineModeCellMagics(CellMagicsCommon, unittest.TestCase): | ||
Fernando Perez
|
r6985 | sp = isp.IPythonInputSplitter(input_mode='line') | ||
def test_incremental(self): | ||||
sp = self.sp | ||||
sp.push('%%cellm line2\n') | ||||
nt.assert_true(sp.push_accepts_more()) #1 | ||||
sp.push('\n') | ||||
nt.assert_false(sp.push_accepts_more()) #2 | ||||