test_inputsplitter.py
643 lines
| 22.6 KiB
| text/x-python
|
PythonLexer
|
r2780 | # -*- coding: utf-8 -*- | ||
|
r17813 | """Tests for the inputsplitter module.""" | ||
|
r3297 | |||
|
r2633 | |||
|
r17813 | # Copyright (c) IPython Development Team. | ||
# Distributed under the terms of the Modified BSD License. | ||||
|
r2633 | import unittest | ||
|
r27105 | import pytest | ||
|
r2718 | import sys | ||
|
r2633 | |||
|
r27107 | with pytest.warns(DeprecationWarning, match="inputsplitter"): | ||
from IPython.core import inputsplitter as isp | ||||
|
r17813 | from IPython.core.inputtransformer import InputTransformer | ||
|
r10095 | from IPython.core.tests.test_inputtransformer import syntax, syntax_ml | ||
|
r4080 | from IPython.testing import tools as tt | ||
|
r2663 | |||
#----------------------------------------------------------------------------- | ||||
# Semi-complete examples (also used as tests) | ||||
#----------------------------------------------------------------------------- | ||||
|
r2780 | |||
# Note: at the bottom, there's a slightly more complete version of this that | ||||
# can be useful during development of code here. | ||||
|
r3119 | def mini_interactive_loop(input_func): | ||
|
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 | ||||
|
r4872 | |||
|
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(): | ||||
|
r24048 | indent = ' '*isp.get_indent_spaces() | ||
|
r2663 | prompt = '>>> ' + indent | ||
|
r3119 | line = indent + input_func(prompt) | ||
|
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() | ||||
|
r28756 | # print('Input source was:\n', src) # dbg | ||
|
r2663 | return src | ||
|
r2633 | |||
#----------------------------------------------------------------------------- | ||||
|
r2645 | # Test utilities, just for local use | ||
#----------------------------------------------------------------------------- | ||||
|
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 | ||||
|
r2645 | #----------------------------------------------------------------------------- | ||
|
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), | ||||
] | ||||
|
r28487 | with pytest.warns(PendingDeprecationWarning): | ||
tt.check_pairs(isp.num_ini_spaces, tests) | ||||
|
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'), | ||||
] | ||||
|
r4080 | tt.check_pairs(isp.remove_comments, tests) | ||
|
r4872 | |||
|
r2633 | |||
def test_get_input_encoding(): | ||||
|
r2663 | encoding = isp.get_input_encoding() | ||
|
r26898 | assert isinstance(encoding, str) | ||
|
r2633 | # simple-minded check that at least encoding a simple string works with the | ||
# encoding we got. | ||||
|
r26898 | assert "test".encode(encoding) == b"test" | ||
|
r2633 | |||
|
r2718 | class NoInputEncodingTestCase(unittest.TestCase): | ||
def setUp(self): | ||||
self.old_stdin = sys.stdin | ||||
class X: pass | ||||
fake_stdin = X() | ||||
sys.stdin = fake_stdin | ||||
|
r4872 | |||
|
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') | ||||
|
r4872 | |||
|
r2718 | def tearDown(self): | ||
sys.stdin = self.old_stdin | ||||
|
r2663 | class InputSplitterTestCase(unittest.TestCase): | ||
|
r2633 | def setUp(self): | ||
|
r2663 | self.isp = isp.InputSplitter() | ||
|
r2633 | |||
def test_reset(self): | ||||
|
r2663 | isp = self.isp | ||
isp.push('x=1') | ||||
isp.reset() | ||||
self.assertEqual(isp._buffer, []) | ||||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r2663 | self.assertEqual(isp.source, '') | ||
self.assertEqual(isp.code, None) | ||||
self.assertEqual(isp._is_complete, False) | ||||
|
r2633 | |||
def test_source(self): | ||||
|
r2663 | self.isp._store('1') | ||
self.isp._store('2') | ||||
self.assertEqual(self.isp.source, '1\n2\n') | ||||
|
r22968 | self.assertEqual(len(self.isp._buffer)>0, True) | ||
|
r2663 | self.assertEqual(self.isp.source_reset(), '1\n2\n') | ||
self.assertEqual(self.isp._buffer, []) | ||||
self.assertEqual(self.isp.source, '') | ||||
|
r4872 | |||
|
r2633 | def test_indent(self): | ||
|
r2663 | isp = self.isp # shorthand | ||
isp.push('x=1') | ||||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r2663 | isp.push('if 1:\n x=1') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 4) | ||
|
r2663 | isp.push('y=2\n') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r3085 | |||
def test_indent2(self): | ||||
isp = self.isp | ||||
|
r2663 | isp.push('if 1:') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 4) | ||
|
r2663 | isp.push(' x=1') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 4) | ||
|
r2633 | # Blank lines shouldn't change the indent level | ||
|
r2663 | isp.push(' '*2) | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 4) | ||
|
r2633 | |||
|
r3085 | def test_indent3(self): | ||
|
r2663 | isp = self.isp | ||
|
r2633 | # When a multiline statement contains parens or multiline strings, we | ||
# shouldn't get confused. | ||||
|
r2663 | isp.push("if 1:") | ||
isp.push(" x = (1+\n 2)") | ||||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 4) | ||
|
r4872 | |||
|
r4204 | def test_indent4(self): | ||
isp = self.isp | ||||
# whitespace after ':' should not screw up indent level | ||||
isp.push('if 1: \n x=1') | ||||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 4) | ||
|
r4204 | isp.push('y=2\n') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r4204 | isp.push('if 1:\t\n x=1') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 4) | ||
|
r4204 | isp.push('y=2\n') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r2633 | |||
|
r3693 | def test_dedent_pass(self): | ||
|
r2663 | isp = self.isp # shorthand | ||
|
r3693 | # should NOT cause dedent | ||
isp.push('if 1:\n passes = 5') | ||||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 4) | ||
|
r3693 | isp.push('if 1:\n pass') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r3693 | isp.push('if 1:\n pass ') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r3693 | |||
|
r7824 | def test_dedent_break(self): | ||
isp = self.isp # shorthand | ||||
# should NOT cause dedent | ||||
isp.push('while 1:\n breaks = 5') | ||||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 4) | ||
|
r7824 | isp.push('while 1:\n break') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r7824 | isp.push('while 1:\n break ') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r7824 | |||
def test_dedent_continue(self): | ||||
isp = self.isp # shorthand | ||||
# should NOT cause dedent | ||||
isp.push('while 1:\n continues = 5') | ||||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 4) | ||
|
r7824 | isp.push('while 1:\n continue') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r7824 | isp.push('while 1:\n continue ') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r7824 | |||
|
r3693 | def test_dedent_raise(self): | ||
isp = self.isp # shorthand | ||||
# should NOT cause dedent | ||||
isp.push('if 1:\n raised = 4') | ||||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 4) | ||
|
r3693 | isp.push('if 1:\n raise TypeError()') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r3693 | isp.push('if 1:\n raise') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r3693 | isp.push('if 1:\n raise ') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r3693 | |||
def test_dedent_return(self): | ||||
isp = self.isp # shorthand | ||||
# should NOT cause dedent | ||||
isp.push('if 1:\n returning = 4') | ||||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 4) | ||
|
r3693 | isp.push('if 1:\n return 5 + 493') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r3693 | isp.push('if 1:\n return') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r3693 | isp.push('if 1:\n return ') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r3693 | isp.push('if 1:\n return(0)') | ||
|
r24048 | self.assertEqual(isp.get_indent_spaces(), 0) | ||
|
r3693 | |||
|
r2633 | def test_push(self): | ||
|
r2663 | isp = self.isp | ||
|
r22968 | self.assertEqual(isp.push('x=1'), True) | ||
|
r2633 | |||
def test_push2(self): | ||||
|
r2663 | isp = self.isp | ||
|
r22968 | self.assertEqual(isp.push('if 1:'), False) | ||
|
r2633 | for line in [' x=1', '# a comment', ' y=2']: | ||
|
r10106 | print(line) | ||
|
r22968 | self.assertEqual(isp.push(line), True) | ||
|
r4872 | |||
|
r3747 | def test_push3(self): | ||
isp = self.isp | ||||
isp.push('if True:') | ||||
isp.push(' a = 1') | ||||
|
r22968 | self.assertEqual(isp.push('b = [1,'), False) | ||
|
r4872 | |||
|
r2663 | def test_push_accepts_more(self): | ||
isp = self.isp | ||||
isp.push('x=1') | ||||
|
r22968 | self.assertEqual(isp.push_accepts_more(), False) | ||
|
r2663 | |||
def test_push_accepts_more2(self): | ||||
isp = self.isp | ||||
isp.push('if 1:') | ||||
|
r22968 | self.assertEqual(isp.push_accepts_more(), True) | ||
|
r2663 | isp.push(' x=1') | ||
|
r22968 | self.assertEqual(isp.push_accepts_more(), True) | ||
|
r2663 | isp.push('') | ||
|
r22968 | self.assertEqual(isp.push_accepts_more(), False) | ||
|
r4872 | |||
|
r2663 | def test_push_accepts_more3(self): | ||
isp = self.isp | ||||
isp.push("x = (2+\n3)") | ||||
|
r22968 | self.assertEqual(isp.push_accepts_more(), False) | ||
|
r2633 | |||
|
r2663 | def test_push_accepts_more4(self): | ||
isp = self.isp | ||||
|
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. | ||||
|
r2663 | isp.push("if 1:") | ||
isp.push(" x = (2+") | ||||
isp.push(" 3)") | ||||
|
r22968 | self.assertEqual(isp.push_accepts_more(), True) | ||
|
r2663 | isp.push(" y = 3") | ||
|
r22968 | self.assertEqual(isp.push_accepts_more(), True) | ||
|
r2663 | isp.push('') | ||
|
r22968 | self.assertEqual(isp.push_accepts_more(), False) | ||
|
r4872 | |||
|
r3461 | def test_push_accepts_more5(self): | ||
isp = self.isp | ||||
isp.push('try:') | ||||
isp.push(' a = 5') | ||||
isp.push('except:') | ||||
isp.push(' raise') | ||||
|
r10251 | # We want to be able to add an else: block at this point, so it should | ||
# wait for a blank line. | ||||
|
r22968 | self.assertEqual(isp.push_accepts_more(), True) | ||
|
r3013 | |||
def test_continuation(self): | ||||
isp = self.isp | ||||
isp.push("import os, \\") | ||||
|
r22968 | self.assertEqual(isp.push_accepts_more(), True) | ||
|
r3013 | isp.push("sys") | ||
|
r22968 | self.assertEqual(isp.push_accepts_more(), False) | ||
|
r2635 | |||
def test_syntax_error(self): | ||||
|
r2663 | isp = self.isp | ||
|
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. | ||||
|
r2663 | isp.push('run foo') | ||
|
r22968 | self.assertEqual(isp.push_accepts_more(), False) | ||
|
r2645 | |||
|
r3126 | def test_unicode(self): | ||
self.isp.push(u"Pérez") | ||||
self.isp.push(u'\xc3\xa9') | ||||
|
r3455 | self.isp.push(u"u'\xc3\xa9'") | ||
|
r2663 | |||
|
r27105 | @pytest.mark.xfail( | ||
reason="Bug in python 3.9.8 – bpo 45738", | ||||
|
r28529 | condition=sys.version_info in [(3, 11, 0, "alpha", 2)], | ||
|
r27105 | raises=SystemError, | ||
strict=True, | ||||
) | ||||
|
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") | ||||
|
r22968 | self.assertEqual(isp.push_accepts_more(), False) | ||
|
r7823 | # 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 \ ") | ||||
|
r22968 | self.assertEqual(isp.push_accepts_more(), False) | ||
|
r7823 | # Even if the line is continuable (c.f. the regular Python | ||
# interpreter) | ||||
isp.push(r"(1 \ ") | ||||
|
r22968 | self.assertEqual(isp.push_accepts_more(), False) | ||
|
r7823 | |||
|
r17804 | def test_check_complete(self): | ||
|
r17624 | isp = self.isp | ||
|
r17804 | self.assertEqual(isp.check_complete("a = 1"), ('complete', None)) | ||
self.assertEqual(isp.check_complete("for a in range(5):"), ('incomplete', 4)) | ||||
self.assertEqual(isp.check_complete("raise = 2"), ('invalid', None)) | ||||
self.assertEqual(isp.check_complete("a = [1,\n2,"), ('incomplete', 0)) | ||||
|
r18850 | self.assertEqual(isp.check_complete("def a():\n x=1\n global x"), ('invalid', None)) | ||
|
r17624 | |||
|
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 = {} | ||||
|
r13350 | exec(src, test_ns) | ||
|
r2663 | # 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 | ||||
|
r13361 | for k,v in ns.items(): | ||
|
r2663 | self.assertEqual(test_ns[k], v) | ||
|
r4872 | |||
|
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)) | ||||
|
r4872 | |||
|
r2663 | def test_multi(self): | ||
self.check_ns(['x =(1+','1+','2)'], dict(x=4)) | ||||
|
r4872 | |||
|
r2719 | |||
|
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. | ||||
|
r3080 | |||
It also makes some checks on the raw buffer storage. | ||||
|
r2780 | """ | ||
|
r2861 | |||
|
r2780 | def setUp(self): | ||
|
r10251 | self.isp = isp.IPythonInputSplitter() | ||
|
r2780 | |||
def test_syntax(self): | ||||
"""Call all single-line syntax tests from the main object""" | ||||
isp = self.isp | ||||
|
r13361 | for example in syntax.values(): | ||
|
r2780 | for raw, out_t in example: | ||
if raw.startswith(' '): | ||||
continue | ||||
|
r4872 | |||
|
r7000 | isp.push(raw+'\n') | ||
|
r13912 | out_raw = isp.source_raw | ||
out = isp.source_reset() | ||||
|
r4746 | self.assertEqual(out.rstrip(), out_t, | ||
tt.pair_fail_msg.format("inputsplitter",raw, out_t, out)) | ||||
|
r3080 | self.assertEqual(out_raw.rstrip(), raw.rstrip()) | ||
|
r2861 | |||
|
r2780 | def test_syntax_multiline(self): | ||
isp = self.isp | ||||
|
r13361 | for example in syntax_ml.values(): | ||
|
r2780 | for line_pairs in example: | ||
|
r10097 | out_t_parts = [] | ||
raw_parts = [] | ||||
|
r3080 | for lraw, out_t_part in line_pairs: | ||
|
r10097 | if out_t_part is not None: | ||
out_t_parts.append(out_t_part) | ||||
|
r26898 | |||
|
r10097 | if lraw is not None: | ||
isp.push(lraw) | ||||
raw_parts.append(lraw) | ||||
|
r2780 | |||
|
r13912 | out_raw = isp.source_raw | ||
out = isp.source_reset() | ||||
|
r2780 | out_t = '\n'.join(out_t_parts).rstrip() | ||
|
r3080 | raw = '\n'.join(raw_parts).rstrip() | ||
self.assertEqual(out.rstrip(), out_t) | ||||
self.assertEqual(out_raw.rstrip(), raw) | ||||
|
r4872 | |||
|
r7488 | def test_syntax_multiline_cell(self): | ||
isp = self.isp | ||||
|
r13361 | for example in syntax_ml.values(): | ||
|
r7823 | |||
|
r7488 | out_t_parts = [] | ||
for line_pairs in example: | ||||
|
r10097 | raw = '\n'.join(r for r, _ in line_pairs if r is not None) | ||
out_t = '\n'.join(t for _,t in line_pairs if t is not None) | ||||
|
r7488 | out = isp.transform_cell(raw) | ||
# Match ignoring trailing whitespace | ||||
self.assertEqual(out.rstrip(), out_t.rstrip()) | ||||
|
r26898 | |||
|
r11459 | def test_cellmagic_preempt(self): | ||
isp = self.isp | ||||
for raw, name, line, cell in [ | ||||
("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'), | ||||
|
r17038 | ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'), | ||
(">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'), | ||||
|
r21954 | ("%%cellm \n>>> hi", u'cellm', u'', u'>>> hi'), | ||
|
r11459 | ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'), | ||
("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'), | ||||
]: | ||||
expected = "get_ipython().run_cell_magic(%r, %r, %r)" % ( | ||||
name, line, cell | ||||
) | ||||
out = isp.transform_cell(raw) | ||||
self.assertEqual(out.rstrip(), expected.rstrip()) | ||||
|
r17813 | |||
def test_multiline_passthrough(self): | ||||
isp = self.isp | ||||
class CommentTransformer(InputTransformer): | ||||
def __init__(self): | ||||
self._lines = [] | ||||
|
r26898 | |||
|
r17813 | def push(self, line): | ||
self._lines.append(line + '#') | ||||
|
r26898 | |||
|
r17813 | def reset(self): | ||
text = '\n'.join(self._lines) | ||||
self._lines = [] | ||||
return text | ||||
|
r26898 | |||
|
r17813 | isp.physical_line_transforms.insert(0, CommentTransformer()) | ||
|
r26898 | |||
|
r17813 | for raw, expected in [ | ||
("a=5", "a=5#"), | ||||
|
r23752 | ("%ls foo", "get_ipython().run_line_magic(%r, %r)" % (u'ls', u'foo#')), | ||
("!ls foo\n%ls bar", "get_ipython().system(%r)\nget_ipython().run_line_magic(%r, %r)" % ( | ||||
u'ls foo#', u'ls', u'bar#' | ||||
|
r17813 | )), | ||
|
r23752 | ("1\n2\n3\n%ls foo\n4\n5", "1#\n2#\n3#\nget_ipython().run_line_magic(%r, %r)\n4#\n5#" % (u'ls', u'foo#')), | ||
|
r17813 | ]: | ||
out = isp.transform_cell(raw) | ||||
self.assertEqual(out.rstrip(), expected.rstrip()) | ||||
|
r2861 | |||
|
r2780 | #----------------------------------------------------------------------------- | ||
|
r2861 | # Main - use as a script, mostly for developer experiments | ||
|
r2780 | #----------------------------------------------------------------------------- | ||
if __name__ == '__main__': | ||||
# A simple demo for interactive experimentation. This code will not get | ||||
|
r2861 | # picked up by any test suite. | ||
|
r21693 | from IPython.core.inputsplitter import IPythonInputSplitter | ||
|
r2782 | |||
# configure here the syntax to use, prompt and whether to autoindent | ||||
|
r2780 | #isp, start_prompt = InputSplitter(), '>>> ' | ||
isp, start_prompt = IPythonInputSplitter(), 'In> ' | ||||
autoindent = True | ||||
#autoindent = False | ||||
|
r4872 | |||
|
r2780 | try: | ||
while True: | ||||
prompt = start_prompt | ||||
while isp.push_accepts_more(): | ||||
|
r24048 | indent = ' '*isp.get_indent_spaces() | ||
|
r2780 | if autoindent: | ||
|
r13355 | line = indent + input(prompt+indent) | ||
|
r2780 | else: | ||
|
r13355 | line = input(prompt) | ||
|
r2780 | 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. | ||||
|
r2828 | #src = isp.source; raise EOFError # dbg | ||
|
r13912 | raw = isp.source_raw | ||
src = isp.source_reset() | ||||
|
r13348 | print('Input source was:\n', src) | ||
print('Raw source was:\n', raw) | ||||
|
r2780 | except EOFError: | ||
|
r13348 | print('Bye') | ||
|
r6985 | |||
# Tests for cell magics support | ||||
def test_last_blank(): | ||||
|
r26898 | assert isp.last_blank("") is False | ||
assert isp.last_blank("abc") is False | ||||
assert isp.last_blank("abc\n") is False | ||||
assert isp.last_blank("abc\na") is False | ||||
|
r6985 | |||
|
r26898 | assert isp.last_blank("\n") is True | ||
assert isp.last_blank("\n ") is True | ||||
assert isp.last_blank("abc\n ") is True | ||||
assert isp.last_blank("abc\n\n") is True | ||||
assert isp.last_blank("abc\nd\n\n") is True | ||||
assert isp.last_blank("abc\nd\ne\n\n") is True | ||||
assert isp.last_blank("abc \n \n \n\n") is True | ||||
|
r6985 | |||
def test_last_two_blanks(): | ||||
|
r26898 | assert isp.last_two_blanks("") is False | ||
assert isp.last_two_blanks("abc") is False | ||||
assert isp.last_two_blanks("abc\n") is False | ||||
assert isp.last_two_blanks("abc\n\na") is False | ||||
assert isp.last_two_blanks("abc\n \n") is False | ||||
assert isp.last_two_blanks("abc\n\n") is False | ||||
assert isp.last_two_blanks("\n\n") is True | ||||
assert isp.last_two_blanks("\n\n ") is True | ||||
assert isp.last_two_blanks("\n \n") is True | ||||
assert isp.last_two_blanks("abc\n\n ") is True | ||||
assert isp.last_two_blanks("abc\n\n\n") is True | ||||
assert isp.last_two_blanks("abc\n\n \n") is True | ||||
assert isp.last_two_blanks("abc\n\n \n ") is True | ||||
assert isp.last_two_blanks("abc\n\n \n \n") is True | ||||
assert isp.last_two_blanks("abc\nd\n\n\n") is True | ||||
assert isp.last_two_blanks("abc\nd\ne\nf\n\n\n") is True | ||||
|
r6985 | |||
|
r7004 | class CellMagicsCommon(object): | ||
|
r7823 | |||
|
r6985 | def test_whole_cell(self): | ||
src = "%%cellm line\nbody\n" | ||||
|
r13912 | out = self.sp.transform_cell(src) | ||
|
r25355 | ref = "get_ipython().run_cell_magic('cellm', 'line', 'body')\n" | ||
|
r26898 | assert out == ref | ||
|
r10252 | def test_cellmagic_help(self): | ||
self.sp.push('%%cellm?') | ||||
|
r26898 | assert self.sp.push_accepts_more() is False | ||
|
r7004 | |||
def tearDown(self): | ||||
self.sp.reset() | ||||
class CellModeCellMagics(CellMagicsCommon, unittest.TestCase): | ||||
|
r10252 | sp = isp.IPythonInputSplitter(line_input_checker=False) | ||
|
r6985 | |||
def test_incremental(self): | ||||
sp = self.sp | ||||
|
r26898 | sp.push("%%cellm firstline\n") | ||
assert sp.push_accepts_more() is True # 1 | ||||
sp.push("line2\n") | ||||
assert sp.push_accepts_more() is True # 2 | ||||
sp.push("\n") | ||||
|
r10252 | # This should accept a blank line and carry on until the cell is reset | ||
|
r26898 | assert sp.push_accepts_more() is True # 3 | ||
|
r21693 | def test_no_strip_coding(self): | ||
src = '\n'.join([ | ||||
'%%writefile foo.py', | ||||
'# coding: utf-8', | ||||
'print(u"üñîçø∂é")', | ||||
]) | ||||
out = self.sp.transform_cell(src) | ||||
|
r26898 | assert "# coding: utf-8" in out | ||
|
r21693 | |||
|
r6985 | |||
|
r7004 | class LineModeCellMagics(CellMagicsCommon, unittest.TestCase): | ||
|
r10252 | sp = isp.IPythonInputSplitter(line_input_checker=True) | ||
|
r6985 | |||
def test_incremental(self): | ||||
sp = self.sp | ||||
|
r26898 | sp.push("%%cellm line2\n") | ||
assert sp.push_accepts_more() is True # 1 | ||||
sp.push("\n") | ||||
|
r10252 | # In this case, a blank line should end the cell magic | ||
|
r26898 | assert sp.push_accepts_more() is False # 2 | ||
|
r23331 | |||
indentation_samples = [ | ||||
('a = 1', 0), | ||||
('for a in b:', 4), | ||||
('def f():', 4), | ||||
('def f(): #comment', 4), | ||||
('a = ":#not a comment"', 0), | ||||
('def f():\n a = 1', 4), | ||||
('def f():\n return 1', 0), | ||||
('for a in b:\n' | ||||
' if a < 0:' | ||||
' continue', 3), | ||||
('a = {', 4), | ||||
('a = {\n' | ||||
' 1,', 5), | ||||
('b = """123', 0), | ||||
('', 0), | ||||
|
r23344 | ('def f():\n pass', 0), | ||
('class Bar:\n def f():\n pass', 4), | ||||
('class Bar:\n def f():\n raise', 4), | ||||
|
r23331 | ] | ||
def test_find_next_indent(): | ||||
for code, exp in indentation_samples: | ||||
res = isp.find_next_indent(code) | ||||
msg = "{!r} != {!r} (expected)\n Code: {!r}".format(res, exp, code) | ||||
assert res == exp, msg | ||||