test_interactivshell.py
242 lines
| 7.6 KiB
| text/x-python
|
PythonLexer
Julian Taylor
|
r5177 | # -*- coding: utf-8 -*- | ||
Thomas Kluyver
|
r22436 | """Tests for the TerminalInteractiveShell and related pieces.""" | ||
Min RK
|
r23307 | # Copyright (c) IPython Development Team. | ||
# Distributed under the terms of the Modified BSD License. | ||||
Julian Taylor
|
r5177 | |||
MinRK
|
r7683 | import sys | ||
Julian Taylor
|
r5177 | import unittest | ||
Inception95
|
r25476 | import os | ||
Julian Taylor
|
r5177 | |||
Thomas Kluyver
|
r13528 | from IPython.core.inputtransformer import InputTransformer | ||
mr.Shu
|
r8974 | from IPython.testing import tools as tt | ||
Min RK
|
r23307 | from IPython.utils.capture import capture_output | ||
Julian Taylor
|
r5177 | |||
Steve Bartz
|
r23622 | from IPython.terminal.ptutils import _elide, _adjust_completion_text_based_on_context | ||
Matthias Bussonnier
|
r23591 | |||
Samuel Gaist
|
r26917 | class TestElide(unittest.TestCase): | ||
Matthias Bussonnier
|
r23591 | def test_elide(self): | ||
Samuel Gaist
|
r26917 | _elide("concatenate((a1, a2, ...), axis", "") # do not raise | ||
_elide("concatenate((a1, a2, ..), . axis", "") # do not raise | ||||
self.assertEqual( | ||||
_elide("aaaa.bbbb.ccccc.dddddd.eeeee.fffff.gggggg.hhhhhh", ""), | ||||
"aaaa.b…g.hhhhhh", | ||||
) | ||||
test_string = os.sep.join(["", 10 * "a", 10 * "b", 10 * "c", ""]) | ||||
expect_stirng = ( | ||||
os.sep + "a" + "\N{HORIZONTAL ELLIPSIS}" + "b" + os.sep + 10 * "c" | ||||
) | ||||
self.assertEqual(_elide(test_string, ""), expect_stirng) | ||||
Matthias Bussonnier
|
r25690 | |||
def test_elide_typed_normal(self): | ||||
Samuel Gaist
|
r26917 | self.assertEqual( | ||
_elide( | ||||
"the quick brown fox jumped over the lazy dog", | ||||
"the quick brown fox", | ||||
min_elide=10, | ||||
), | ||||
"the…fox jumped over the lazy dog", | ||||
) | ||||
Matthias Bussonnier
|
r25690 | |||
def test_elide_typed_short_match(self): | ||||
""" | ||||
if the match is too short we don't elide. | ||||
avoid the "the...the" | ||||
""" | ||||
Samuel Gaist
|
r26917 | self.assertEqual( | ||
_elide("the quick brown fox jumped over the lazy dog", "the", min_elide=10), | ||||
"the quick brown fox jumped over the lazy dog", | ||||
) | ||||
Matthias Bussonnier
|
r23591 | |||
Matthias Bussonnier
|
r25690 | def test_elide_typed_no_match(self): | ||
""" | ||||
if the match is too short we don't elide. | ||||
avoid the "the...the" | ||||
""" | ||||
# here we typed red instead of brown | ||||
Samuel Gaist
|
r26917 | self.assertEqual( | ||
_elide( | ||||
"the quick brown fox jumped over the lazy dog", | ||||
"the quick red fox", | ||||
min_elide=10, | ||||
), | ||||
"the quick brown fox jumped over the lazy dog", | ||||
) | ||||
Steve Bartz
|
r23622 | |||
Samuel Gaist
|
r26917 | class TestContextAwareCompletion(unittest.TestCase): | ||
Steve Bartz
|
r23622 | def test_adjust_completion_text_based_on_context(self): | ||
# Adjusted case | ||||
Samuel Gaist
|
r26917 | self.assertEqual( | ||
_adjust_completion_text_based_on_context("arg1=", "func1(a=)", 7), "arg1" | ||||
) | ||||
Steve Bartz
|
r23622 | |||
# Untouched cases | ||||
Samuel Gaist
|
r26917 | self.assertEqual( | ||
_adjust_completion_text_based_on_context("arg1=", "func1(a)", 7), "arg1=" | ||||
) | ||||
self.assertEqual( | ||||
_adjust_completion_text_based_on_context("arg1=", "func1(a", 7), "arg1=" | ||||
) | ||||
self.assertEqual( | ||||
_adjust_completion_text_based_on_context("%magic", "func1(a=)", 7), "%magic" | ||||
) | ||||
self.assertEqual( | ||||
_adjust_completion_text_based_on_context("func2", "func1(a=)", 7), "func2" | ||||
) | ||||
Steve Bartz
|
r23622 | |||
Thomas Kluyver
|
r13528 | # Decorator for interaction loop tests ----------------------------------------- | ||
Samuel Gaist
|
r26917 | |||
Thomas Kluyver
|
r13528 | class mock_input_helper(object): | ||
"""Machinery for tests of the main interact loop. | ||||
Used by the mock_input decorator. | ||||
""" | ||||
def __init__(self, testgen): | ||||
self.testgen = testgen | ||||
self.exception = None | ||||
self.ip = get_ipython() | ||||
def __enter__(self): | ||||
Thomas Kluyver
|
r22436 | self.orig_prompt_for_code = self.ip.prompt_for_code | ||
self.ip.prompt_for_code = self.fake_input | ||||
Thomas Kluyver
|
r13528 | return self | ||
def __exit__(self, etype, value, tb): | ||||
Thomas Kluyver
|
r22436 | self.ip.prompt_for_code = self.orig_prompt_for_code | ||
Thomas Kluyver
|
r13528 | |||
Thomas Kluyver
|
r22436 | def fake_input(self): | ||
Thomas Kluyver
|
r13528 | try: | ||
return next(self.testgen) | ||||
except StopIteration: | ||||
Thomas Kluyver
|
r22436 | self.ip.keep_running = False | ||
Thomas Kluyver
|
r13528 | return u'' | ||
except: | ||||
self.exception = sys.exc_info() | ||||
Thomas Kluyver
|
r22436 | self.ip.keep_running = False | ||
Thomas Kluyver
|
r13528 | return u'' | ||
def mock_input(testfunc): | ||||
"""Decorator for tests of the main interact loop. | ||||
Write the test as a generator, yield-ing the input strings, which IPython | ||||
will see as if they were typed in at the prompt. | ||||
""" | ||||
def test_method(self): | ||||
testgen = testfunc(self) | ||||
with mock_input_helper(testgen) as mih: | ||||
Thomas Kluyver
|
r22436 | mih.ip.interact() | ||
Thomas Kluyver
|
r13528 | |||
if mih.exception is not None: | ||||
# Re-raise captured exception | ||||
etype, value, tb = mih.exception | ||||
import traceback | ||||
traceback.print_tb(tb, file=sys.stdout) | ||||
del tb # Avoid reference loop | ||||
raise value | ||||
return test_method | ||||
# Test classes ----------------------------------------------------------------- | ||||
Julian Taylor
|
r5177 | class InteractiveShellTestCase(unittest.TestCase): | ||
def rl_hist_entries(self, rl, n): | ||||
"""Get last n readline history entries as a list""" | ||||
return [rl.get_history_item(rl.get_current_history_length() - x) | ||||
for x in range(n - 1, -1, -1)] | ||||
mr.Shu
|
r8974 | |||
Thomas Kluyver
|
r13528 | @mock_input | ||
def test_inputtransformer_syntaxerror(self): | ||||
ip = get_ipython() | ||||
Thomas Kluyver
|
r24405 | ip.input_transformers_post.append(syntax_error_transformer) | ||
Thomas Kluyver
|
r13528 | |||
try: | ||||
#raise Exception | ||||
with tt.AssertPrints('4', suppress=False): | ||||
yield u'print(2*2)' | ||||
with tt.AssertPrints('SyntaxError: input contains', suppress=False): | ||||
yield u'print(2345) # syntaxerror' | ||||
with tt.AssertPrints('16', suppress=False): | ||||
yield u'print(4*4)' | ||||
finally: | ||||
Thomas Kluyver
|
r24405 | ip.input_transformers_post.remove(syntax_error_transformer) | ||
Thomas Kluyver
|
r13528 | |||
Matthias Bussonnier
|
r25728 | def test_repl_not_plain_text(self): | ||
Min RK
|
r22530 | ip = get_ipython() | ||
formatter = ip.display_formatter | ||||
assert formatter.active_types == ['text/plain'] | ||||
Matthias Bussonnier
|
r25728 | |||
# terminal may have arbitrary mimetype handler to open external viewer | ||||
# or inline images. | ||||
assert formatter.ipython_display_formatter.enabled | ||||
Min RK
|
r23307 | |||
class Test(object): | ||||
def __repr__(self): | ||||
return "<Test %i>" % id(self) | ||||
def _repr_html_(self): | ||||
return '<html>' | ||||
# verify that HTML repr isn't computed | ||||
obj = Test() | ||||
data, _ = formatter.format(obj) | ||||
self.assertEqual(data, {'text/plain': repr(obj)}) | ||||
class Test2(Test): | ||||
def _ipython_display_(self): | ||||
Matthias Bussonnier
|
r25728 | from IPython.display import display, HTML | ||
Matthias Bussonnier
|
r27370 | |||
display(HTML("<custom>")) | ||||
Min RK
|
r23307 | |||
Matthias Bussonnier
|
r25728 | # verify that mimehandlers are called | ||
called = False | ||||
Min RK
|
r23307 | |||
Matthias Bussonnier
|
r25728 | def handler(data, metadata): | ||
Matthias Bussonnier
|
r27370 | print("Handler called") | ||
Matthias Bussonnier
|
r25728 | nonlocal called | ||
called = True | ||||
ip.display_formatter.active_types.append("text/html") | ||||
ip.display_formatter.formatters["text/html"].enabled = True | ||||
ip.mime_renderers["text/html"] = handler | ||||
Matthias Bussonnier
|
r27371 | try: | ||
obj = Test() | ||||
display(obj) | ||||
finally: | ||||
ip.display_formatter.formatters["text/html"].enabled = False | ||||
del ip.mime_renderers["text/html"] | ||||
Matthias Bussonnier
|
r25728 | |||
assert called == True | ||||
Matthias Bussonnier
|
r27370 | |||
Min RK
|
r23307 | |||
Thomas Kluyver
|
r24170 | def syntax_error_transformer(lines): | ||
"""Transformer that throws SyntaxError if 'syntaxerror' is in the code.""" | ||||
for line in lines: | ||||
Thomas Kluyver
|
r13528 | pos = line.find('syntaxerror') | ||
if pos >= 0: | ||||
e = SyntaxError('input contains "syntaxerror"') | ||||
e.text = line | ||||
e.offset = pos + 1 | ||||
raise e | ||||
Thomas Kluyver
|
r24170 | return lines | ||
Thomas Kluyver
|
r13528 | |||
Thomas Kluyver
|
r10752 | class TerminalMagicsTestCase(unittest.TestCase): | ||
def test_paste_magics_blankline(self): | ||||
"""Test that code with a blank line doesn't get split (gh-3246).""" | ||||
ip = get_ipython() | ||||
s = ('def pasted_func(a):\n' | ||||
' b = a+1\n' | ||||
'\n' | ||||
' return b') | ||||
tm = ip.magics_manager.registry['TerminalMagics'] | ||||
tm.store_or_execute(s, name=None) | ||||
self.assertEqual(ip.user_ns['pasted_func'](54), 55) | ||||