first comit
This commit is contained in:
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,82 @@
|
||||
"""Test embedding of IPython"""
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (C) 2013 The IPython Development Team
|
||||
#
|
||||
# Distributed under the terms of the BSD License. The full license is in
|
||||
# the file COPYING, distributed as part of this software.
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Imports
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
from IPython.testing.decorators import skip_win32
|
||||
from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Tests
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
@skip_win32
|
||||
def test_debug_magic_passes_through_generators():
|
||||
"""
|
||||
This test that we can correctly pass through frames of a generator post-mortem.
|
||||
"""
|
||||
import pexpect
|
||||
import re
|
||||
in_prompt = re.compile(br'In ?\[\d+\]:')
|
||||
ipdb_prompt = 'ipdb>'
|
||||
env = os.environ.copy()
|
||||
child = pexpect.spawn(sys.executable, ['-m', 'IPython', '--colors=nocolor', '--simple-prompt'],
|
||||
env=env)
|
||||
child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
|
||||
|
||||
child.expect(in_prompt)
|
||||
|
||||
child.timeout = 2 * IPYTHON_TESTING_TIMEOUT_SCALE
|
||||
|
||||
child.sendline("def f(x):")
|
||||
child.sendline(" raise Exception")
|
||||
child.sendline("")
|
||||
|
||||
child.expect(in_prompt)
|
||||
child.sendline("gen = (f(x) for x in [0])")
|
||||
child.sendline("")
|
||||
|
||||
child.expect(in_prompt)
|
||||
child.sendline("for x in gen:")
|
||||
child.sendline(" pass")
|
||||
child.sendline("")
|
||||
|
||||
child.timeout = 10 * IPYTHON_TESTING_TIMEOUT_SCALE
|
||||
|
||||
child.expect('Exception:')
|
||||
|
||||
child.expect(in_prompt)
|
||||
child.sendline(r'%debug')
|
||||
child.expect('----> 2 raise Exception')
|
||||
|
||||
child.expect(ipdb_prompt)
|
||||
child.sendline('u')
|
||||
child.expect_exact(r'----> 1 gen = (f(x) for x in [0])')
|
||||
|
||||
child.expect(ipdb_prompt)
|
||||
child.sendline('u')
|
||||
child.expect_exact('----> 1 for x in gen:')
|
||||
|
||||
child.expect(ipdb_prompt)
|
||||
child.sendline("u")
|
||||
child.expect_exact(
|
||||
"*** all frames above hidden, use `skip_hidden False` to get get into those."
|
||||
)
|
||||
|
||||
child.expect(ipdb_prompt)
|
||||
child.sendline('exit')
|
||||
|
||||
child.expect(in_prompt)
|
||||
child.sendline('exit')
|
||||
|
||||
child.close()
|
||||
@@ -0,0 +1,138 @@
|
||||
"""Test embedding of IPython"""
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (C) 2013 The IPython Development Team
|
||||
#
|
||||
# Distributed under the terms of the BSD License. The full license is in
|
||||
# the file COPYING, distributed as part of this software.
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Imports
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from IPython.utils.tempdir import NamedFileInTemporaryDirectory
|
||||
from IPython.testing.decorators import skip_win32
|
||||
from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Tests
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
_sample_embed = b"""
|
||||
import IPython
|
||||
|
||||
a = 3
|
||||
b = 14
|
||||
print(a, '.', b)
|
||||
|
||||
IPython.embed()
|
||||
|
||||
print('bye!')
|
||||
"""
|
||||
|
||||
_exit = b"exit\r"
|
||||
|
||||
def test_ipython_embed():
|
||||
"""test that `IPython.embed()` works"""
|
||||
with NamedFileInTemporaryDirectory('file_with_embed.py') as f:
|
||||
f.write(_sample_embed)
|
||||
f.flush()
|
||||
f.close() # otherwise msft won't be able to read the file
|
||||
|
||||
# run `python file_with_embed.py`
|
||||
cmd = [sys.executable, f.name]
|
||||
env = os.environ.copy()
|
||||
env['IPY_TEST_SIMPLE_PROMPT'] = '1'
|
||||
|
||||
p = subprocess.Popen(cmd, env=env, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
out, err = p.communicate(_exit)
|
||||
std = out.decode('UTF-8')
|
||||
|
||||
assert p.returncode == 0
|
||||
assert "3 . 14" in std
|
||||
if os.name != "nt":
|
||||
# TODO: Fix up our different stdout references, see issue gh-14
|
||||
assert "IPython" in std
|
||||
assert "bye!" in std
|
||||
|
||||
|
||||
@skip_win32
|
||||
def test_nest_embed():
|
||||
"""test that `IPython.embed()` is nestable"""
|
||||
import pexpect
|
||||
ipy_prompt = r']:' #ansi color codes give problems matching beyond this
|
||||
env = os.environ.copy()
|
||||
env['IPY_TEST_SIMPLE_PROMPT'] = '1'
|
||||
|
||||
|
||||
child = pexpect.spawn(sys.executable, ['-m', 'IPython', '--colors=nocolor'],
|
||||
env=env)
|
||||
child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
|
||||
child.expect(ipy_prompt)
|
||||
child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
|
||||
child.sendline("import IPython")
|
||||
child.expect(ipy_prompt)
|
||||
child.sendline("ip0 = get_ipython()")
|
||||
#enter first nested embed
|
||||
child.sendline("IPython.embed()")
|
||||
#skip the banner until we get to a prompt
|
||||
try:
|
||||
prompted = -1
|
||||
while prompted != 0:
|
||||
prompted = child.expect([ipy_prompt, '\r\n'])
|
||||
except pexpect.TIMEOUT as e:
|
||||
print(e)
|
||||
#child.interact()
|
||||
child.sendline("embed1 = get_ipython()")
|
||||
child.expect(ipy_prompt)
|
||||
child.sendline("print('true' if embed1 is not ip0 else 'false')")
|
||||
assert(child.expect(['true\r\n', 'false\r\n']) == 0)
|
||||
child.expect(ipy_prompt)
|
||||
child.sendline("print('true' if IPython.get_ipython() is embed1 else 'false')")
|
||||
assert(child.expect(['true\r\n', 'false\r\n']) == 0)
|
||||
child.expect(ipy_prompt)
|
||||
#enter second nested embed
|
||||
child.sendline("IPython.embed()")
|
||||
#skip the banner until we get to a prompt
|
||||
try:
|
||||
prompted = -1
|
||||
while prompted != 0:
|
||||
prompted = child.expect([ipy_prompt, '\r\n'])
|
||||
except pexpect.TIMEOUT as e:
|
||||
print(e)
|
||||
#child.interact()
|
||||
child.sendline("embed2 = get_ipython()")
|
||||
child.expect(ipy_prompt)
|
||||
child.sendline("print('true' if embed2 is not embed1 else 'false')")
|
||||
assert(child.expect(['true\r\n', 'false\r\n']) == 0)
|
||||
child.expect(ipy_prompt)
|
||||
child.sendline("print('true' if embed2 is IPython.get_ipython() else 'false')")
|
||||
assert(child.expect(['true\r\n', 'false\r\n']) == 0)
|
||||
child.expect(ipy_prompt)
|
||||
child.sendline('exit')
|
||||
#back at first embed
|
||||
child.expect(ipy_prompt)
|
||||
child.sendline("print('true' if get_ipython() is embed1 else 'false')")
|
||||
assert(child.expect(['true\r\n', 'false\r\n']) == 0)
|
||||
child.expect(ipy_prompt)
|
||||
child.sendline("print('true' if IPython.get_ipython() is embed1 else 'false')")
|
||||
assert(child.expect(['true\r\n', 'false\r\n']) == 0)
|
||||
child.expect(ipy_prompt)
|
||||
child.sendline('exit')
|
||||
#back at launching scope
|
||||
child.expect(ipy_prompt)
|
||||
child.sendline("print('true' if get_ipython() is ip0 else 'false')")
|
||||
assert(child.expect(['true\r\n', 'false\r\n']) == 0)
|
||||
child.expect(ipy_prompt)
|
||||
child.sendline("print('true' if IPython.get_ipython() is ip0 else 'false')")
|
||||
assert(child.expect(['true\r\n', 'false\r\n']) == 0)
|
||||
child.expect(ipy_prompt)
|
||||
child.sendline('exit')
|
||||
child.close()
|
||||
@@ -0,0 +1,30 @@
|
||||
"""Test help output of various IPython entry points"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import pytest
|
||||
import IPython.testing.tools as tt
|
||||
|
||||
|
||||
def test_ipython_help():
|
||||
tt.help_all_output_test()
|
||||
|
||||
def test_profile_help():
|
||||
tt.help_all_output_test("profile")
|
||||
|
||||
def test_profile_list_help():
|
||||
tt.help_all_output_test("profile list")
|
||||
|
||||
def test_profile_create_help():
|
||||
tt.help_all_output_test("profile create")
|
||||
|
||||
def test_locate_help():
|
||||
tt.help_all_output_test("locate")
|
||||
|
||||
def test_locate_profile_help():
|
||||
tt.help_all_output_test("locate profile")
|
||||
|
||||
def test_trust_help():
|
||||
pytest.importorskip("nbformat")
|
||||
tt.help_all_output_test("trust")
|
||||
@@ -0,0 +1,255 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Tests for the TerminalInteractiveShell and related pieces."""
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
import os
|
||||
|
||||
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
||||
|
||||
|
||||
from IPython.testing import tools as tt
|
||||
|
||||
from IPython.terminal.ptutils import _elide, _adjust_completion_text_based_on_context
|
||||
from IPython.terminal.shortcuts.auto_suggest import NavigableAutoSuggestFromHistory
|
||||
|
||||
|
||||
class TestAutoSuggest(unittest.TestCase):
|
||||
def test_changing_provider(self):
|
||||
ip = get_ipython()
|
||||
ip.autosuggestions_provider = None
|
||||
self.assertEqual(ip.auto_suggest, None)
|
||||
ip.autosuggestions_provider = "AutoSuggestFromHistory"
|
||||
self.assertIsInstance(ip.auto_suggest, AutoSuggestFromHistory)
|
||||
ip.autosuggestions_provider = "NavigableAutoSuggestFromHistory"
|
||||
self.assertIsInstance(ip.auto_suggest, NavigableAutoSuggestFromHistory)
|
||||
|
||||
|
||||
class TestElide(unittest.TestCase):
|
||||
def test_elide(self):
|
||||
_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_string = (
|
||||
os.sep + "a" + "\N{HORIZONTAL ELLIPSIS}" + "b" + os.sep + 10 * "c"
|
||||
)
|
||||
self.assertEqual(_elide(test_string, ""), expect_string)
|
||||
|
||||
def test_elide_typed_normal(self):
|
||||
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",
|
||||
)
|
||||
|
||||
def test_elide_typed_short_match(self):
|
||||
"""
|
||||
if the match is too short we don't elide.
|
||||
avoid the "the...the"
|
||||
"""
|
||||
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",
|
||||
)
|
||||
|
||||
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
|
||||
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",
|
||||
)
|
||||
|
||||
|
||||
class TestContextAwareCompletion(unittest.TestCase):
|
||||
def test_adjust_completion_text_based_on_context(self):
|
||||
# Adjusted case
|
||||
self.assertEqual(
|
||||
_adjust_completion_text_based_on_context("arg1=", "func1(a=)", 7), "arg1"
|
||||
)
|
||||
|
||||
# Untouched cases
|
||||
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"
|
||||
)
|
||||
|
||||
|
||||
# Decorator for interaction loop tests -----------------------------------------
|
||||
|
||||
|
||||
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):
|
||||
self.orig_prompt_for_code = self.ip.prompt_for_code
|
||||
self.ip.prompt_for_code = self.fake_input
|
||||
return self
|
||||
|
||||
def __exit__(self, etype, value, tb):
|
||||
self.ip.prompt_for_code = self.orig_prompt_for_code
|
||||
|
||||
def fake_input(self):
|
||||
try:
|
||||
return next(self.testgen)
|
||||
except StopIteration:
|
||||
self.ip.keep_running = False
|
||||
return u''
|
||||
except:
|
||||
self.exception = sys.exc_info()
|
||||
self.ip.keep_running = False
|
||||
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:
|
||||
mih.ip.interact()
|
||||
|
||||
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 -----------------------------------------------------------------
|
||||
|
||||
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)]
|
||||
|
||||
@mock_input
|
||||
def test_inputtransformer_syntaxerror(self):
|
||||
ip = get_ipython()
|
||||
ip.input_transformers_post.append(syntax_error_transformer)
|
||||
|
||||
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:
|
||||
ip.input_transformers_post.remove(syntax_error_transformer)
|
||||
|
||||
def test_repl_not_plain_text(self):
|
||||
ip = get_ipython()
|
||||
formatter = ip.display_formatter
|
||||
assert formatter.active_types == ['text/plain']
|
||||
|
||||
# terminal may have arbitrary mimetype handler to open external viewer
|
||||
# or inline images.
|
||||
assert formatter.ipython_display_formatter.enabled
|
||||
|
||||
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):
|
||||
from IPython.display import display, HTML
|
||||
|
||||
display(HTML("<custom>"))
|
||||
|
||||
# verify that mimehandlers are called
|
||||
called = False
|
||||
|
||||
def handler(data, metadata):
|
||||
print("Handler called")
|
||||
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
|
||||
try:
|
||||
obj = Test()
|
||||
display(obj)
|
||||
finally:
|
||||
ip.display_formatter.formatters["text/html"].enabled = False
|
||||
del ip.mime_renderers["text/html"]
|
||||
|
||||
assert called == True
|
||||
|
||||
|
||||
def syntax_error_transformer(lines):
|
||||
"""Transformer that throws SyntaxError if 'syntaxerror' is in the code."""
|
||||
for line in lines:
|
||||
pos = line.find('syntaxerror')
|
||||
if pos >= 0:
|
||||
e = SyntaxError('input contains "syntaxerror"')
|
||||
e.text = line
|
||||
e.offset = pos + 1
|
||||
raise e
|
||||
return lines
|
||||
|
||||
|
||||
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)
|
||||
@@ -0,0 +1,50 @@
|
||||
import os
|
||||
import importlib
|
||||
|
||||
import pytest
|
||||
|
||||
from IPython.terminal.pt_inputhooks import set_qt_api, get_inputhook_name_and_func
|
||||
|
||||
|
||||
guis_avail = []
|
||||
|
||||
|
||||
def _get_qt_vers():
|
||||
"""If any version of Qt is available, this will populate `guis_avail` with 'qt' and 'qtx'. Due
|
||||
to the import mechanism, we can't import multiple versions of Qt in one session."""
|
||||
for gui in ["qt", "qt6", "qt5"]:
|
||||
print(f"Trying {gui}")
|
||||
try:
|
||||
set_qt_api(gui)
|
||||
importlib.import_module("IPython.terminal.pt_inputhooks.qt")
|
||||
guis_avail.append(gui)
|
||||
if "QT_API" in os.environ.keys():
|
||||
del os.environ["QT_API"]
|
||||
except ImportError:
|
||||
pass # that version of Qt isn't available.
|
||||
except RuntimeError:
|
||||
pass # the version of IPython doesn't know what to do with this Qt version.
|
||||
|
||||
|
||||
_get_qt_vers()
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
len(guis_avail) == 0, reason="No viable version of PyQt or PySide installed."
|
||||
)
|
||||
def test_inputhook_qt():
|
||||
# Choose the "best" Qt version.
|
||||
gui_ret, _ = get_inputhook_name_and_func("qt")
|
||||
|
||||
assert gui_ret != "qt" # you get back the specific version that was loaded.
|
||||
assert gui_ret in guis_avail
|
||||
|
||||
if len(guis_avail) > 2:
|
||||
# ...and now we're stuck with this version of Qt for good; can't switch.
|
||||
for not_gui in ["qt6", "qt5"]:
|
||||
if not_gui != gui_ret:
|
||||
break
|
||||
# Try to import the other gui; it won't work.
|
||||
gui_ret2, _ = get_inputhook_name_and_func(not_gui)
|
||||
assert gui_ret2 == gui_ret
|
||||
assert gui_ret2 != not_gui
|
||||
@@ -0,0 +1,485 @@
|
||||
import pytest
|
||||
from IPython.terminal.shortcuts.auto_suggest import (
|
||||
accept,
|
||||
accept_or_jump_to_end,
|
||||
accept_token,
|
||||
accept_character,
|
||||
accept_word,
|
||||
accept_and_keep_cursor,
|
||||
discard,
|
||||
NavigableAutoSuggestFromHistory,
|
||||
swap_autosuggestion_up,
|
||||
swap_autosuggestion_down,
|
||||
)
|
||||
from IPython.terminal.shortcuts.auto_match import skip_over
|
||||
from IPython.terminal.shortcuts import create_ipython_shortcuts, reset_search_buffer
|
||||
|
||||
from prompt_toolkit.history import InMemoryHistory
|
||||
from prompt_toolkit.buffer import Buffer
|
||||
from prompt_toolkit.document import Document
|
||||
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
||||
from prompt_toolkit.enums import DEFAULT_BUFFER
|
||||
|
||||
from unittest.mock import patch, Mock
|
||||
|
||||
|
||||
def test_deprected():
|
||||
import IPython.terminal.shortcuts.auto_suggest as iptsa
|
||||
|
||||
with pytest.warns(DeprecationWarning, match=r"8\.12.+accept_or_jump_to_end"):
|
||||
iptsa.accept_in_vi_insert_mode
|
||||
|
||||
|
||||
def make_event(text, cursor, suggestion):
|
||||
event = Mock()
|
||||
event.current_buffer = Mock()
|
||||
event.current_buffer.suggestion = Mock()
|
||||
event.current_buffer.text = text
|
||||
event.current_buffer.cursor_position = cursor
|
||||
event.current_buffer.suggestion.text = suggestion
|
||||
event.current_buffer.document = Document(text=text, cursor_position=cursor)
|
||||
return event
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"text, suggestion, expected",
|
||||
[
|
||||
("", "def out(tag: str, n=50):", "def out(tag: str, n=50):"),
|
||||
("def ", "out(tag: str, n=50):", "out(tag: str, n=50):"),
|
||||
],
|
||||
)
|
||||
def test_accept(text, suggestion, expected):
|
||||
event = make_event(text, len(text), suggestion)
|
||||
buffer = event.current_buffer
|
||||
buffer.insert_text = Mock()
|
||||
accept(event)
|
||||
assert buffer.insert_text.called
|
||||
assert buffer.insert_text.call_args[0] == (expected,)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"text, suggestion",
|
||||
[
|
||||
("", "def out(tag: str, n=50):"),
|
||||
("def ", "out(tag: str, n=50):"),
|
||||
],
|
||||
)
|
||||
def test_discard(text, suggestion):
|
||||
event = make_event(text, len(text), suggestion)
|
||||
buffer = event.current_buffer
|
||||
buffer.insert_text = Mock()
|
||||
discard(event)
|
||||
assert not buffer.insert_text.called
|
||||
assert buffer.suggestion is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"text, cursor, suggestion, called",
|
||||
[
|
||||
("123456", 6, "123456789", True),
|
||||
("123456", 3, "123456789", False),
|
||||
("123456 \n789", 6, "123456789", True),
|
||||
],
|
||||
)
|
||||
def test_autosuggest_at_EOL(text, cursor, suggestion, called):
|
||||
"""
|
||||
test that autosuggest is only applied at end of line.
|
||||
"""
|
||||
|
||||
event = make_event(text, cursor, suggestion)
|
||||
event.current_buffer.insert_text = Mock()
|
||||
accept_or_jump_to_end(event)
|
||||
if called:
|
||||
event.current_buffer.insert_text.assert_called()
|
||||
else:
|
||||
event.current_buffer.insert_text.assert_not_called()
|
||||
# event.current_buffer.document.get_end_of_line_position.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"text, suggestion, expected",
|
||||
[
|
||||
("", "def out(tag: str, n=50):", "def "),
|
||||
("d", "ef out(tag: str, n=50):", "ef "),
|
||||
("de ", "f out(tag: str, n=50):", "f "),
|
||||
("def", " out(tag: str, n=50):", " "),
|
||||
("def ", "out(tag: str, n=50):", "out("),
|
||||
("def o", "ut(tag: str, n=50):", "ut("),
|
||||
("def ou", "t(tag: str, n=50):", "t("),
|
||||
("def out", "(tag: str, n=50):", "("),
|
||||
("def out(", "tag: str, n=50):", "tag: "),
|
||||
("def out(t", "ag: str, n=50):", "ag: "),
|
||||
("def out(ta", "g: str, n=50):", "g: "),
|
||||
("def out(tag", ": str, n=50):", ": "),
|
||||
("def out(tag:", " str, n=50):", " "),
|
||||
("def out(tag: ", "str, n=50):", "str, "),
|
||||
("def out(tag: s", "tr, n=50):", "tr, "),
|
||||
("def out(tag: st", "r, n=50):", "r, "),
|
||||
("def out(tag: str", ", n=50):", ", n"),
|
||||
("def out(tag: str,", " n=50):", " n"),
|
||||
("def out(tag: str, ", "n=50):", "n="),
|
||||
("def out(tag: str, n", "=50):", "="),
|
||||
("def out(tag: str, n=", "50):", "50)"),
|
||||
("def out(tag: str, n=5", "0):", "0)"),
|
||||
("def out(tag: str, n=50", "):", "):"),
|
||||
("def out(tag: str, n=50)", ":", ":"),
|
||||
],
|
||||
)
|
||||
def test_autosuggest_token(text, suggestion, expected):
|
||||
event = make_event(text, len(text), suggestion)
|
||||
event.current_buffer.insert_text = Mock()
|
||||
accept_token(event)
|
||||
assert event.current_buffer.insert_text.called
|
||||
assert event.current_buffer.insert_text.call_args[0] == (expected,)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"text, suggestion, expected",
|
||||
[
|
||||
("", "def out(tag: str, n=50):", "d"),
|
||||
("d", "ef out(tag: str, n=50):", "e"),
|
||||
("de ", "f out(tag: str, n=50):", "f"),
|
||||
("def", " out(tag: str, n=50):", " "),
|
||||
],
|
||||
)
|
||||
def test_accept_character(text, suggestion, expected):
|
||||
event = make_event(text, len(text), suggestion)
|
||||
event.current_buffer.insert_text = Mock()
|
||||
accept_character(event)
|
||||
assert event.current_buffer.insert_text.called
|
||||
assert event.current_buffer.insert_text.call_args[0] == (expected,)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"text, suggestion, expected",
|
||||
[
|
||||
("", "def out(tag: str, n=50):", "def "),
|
||||
("d", "ef out(tag: str, n=50):", "ef "),
|
||||
("de", "f out(tag: str, n=50):", "f "),
|
||||
("def", " out(tag: str, n=50):", " "),
|
||||
# (this is why we also have accept_token)
|
||||
("def ", "out(tag: str, n=50):", "out(tag: "),
|
||||
],
|
||||
)
|
||||
def test_accept_word(text, suggestion, expected):
|
||||
event = make_event(text, len(text), suggestion)
|
||||
event.current_buffer.insert_text = Mock()
|
||||
accept_word(event)
|
||||
assert event.current_buffer.insert_text.called
|
||||
assert event.current_buffer.insert_text.call_args[0] == (expected,)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"text, suggestion, expected, cursor",
|
||||
[
|
||||
("", "def out(tag: str, n=50):", "def out(tag: str, n=50):", 0),
|
||||
("def ", "out(tag: str, n=50):", "out(tag: str, n=50):", 4),
|
||||
],
|
||||
)
|
||||
def test_accept_and_keep_cursor(text, suggestion, expected, cursor):
|
||||
event = make_event(text, cursor, suggestion)
|
||||
buffer = event.current_buffer
|
||||
buffer.insert_text = Mock()
|
||||
accept_and_keep_cursor(event)
|
||||
assert buffer.insert_text.called
|
||||
assert buffer.insert_text.call_args[0] == (expected,)
|
||||
assert buffer.cursor_position == cursor
|
||||
|
||||
|
||||
def test_autosuggest_token_empty():
|
||||
full = "def out(tag: str, n=50):"
|
||||
event = make_event(full, len(full), "")
|
||||
event.current_buffer.insert_text = Mock()
|
||||
|
||||
with patch(
|
||||
"prompt_toolkit.key_binding.bindings.named_commands.forward_word"
|
||||
) as forward_word:
|
||||
accept_token(event)
|
||||
assert not event.current_buffer.insert_text.called
|
||||
assert forward_word.called
|
||||
|
||||
|
||||
def test_reset_search_buffer():
|
||||
event_with_text = Mock()
|
||||
event_with_text.current_buffer.document.text = "some text"
|
||||
event_with_text.current_buffer.reset = Mock()
|
||||
|
||||
event_empty = Mock()
|
||||
event_empty.current_buffer.document.text = ""
|
||||
event_empty.app.layout.focus = Mock()
|
||||
|
||||
reset_search_buffer(event_with_text)
|
||||
event_with_text.current_buffer.reset.assert_called_once()
|
||||
|
||||
reset_search_buffer(event_empty)
|
||||
event_empty.app.layout.focus.assert_called_once_with(DEFAULT_BUFFER)
|
||||
|
||||
|
||||
def test_other_providers():
|
||||
"""Ensure that swapping autosuggestions does not break with other providers"""
|
||||
provider = AutoSuggestFromHistory()
|
||||
ip = get_ipython()
|
||||
ip.auto_suggest = provider
|
||||
event = Mock()
|
||||
event.current_buffer = Buffer()
|
||||
assert swap_autosuggestion_up(event) is None
|
||||
assert swap_autosuggestion_down(event) is None
|
||||
|
||||
|
||||
async def test_navigable_provider():
|
||||
provider = NavigableAutoSuggestFromHistory()
|
||||
history = InMemoryHistory(history_strings=["very_a", "very", "very_b", "very_c"])
|
||||
buffer = Buffer(history=history)
|
||||
ip = get_ipython()
|
||||
ip.auto_suggest = provider
|
||||
|
||||
async for _ in history.load():
|
||||
pass
|
||||
|
||||
buffer.cursor_position = 5
|
||||
buffer.text = "very"
|
||||
|
||||
up = swap_autosuggestion_up
|
||||
down = swap_autosuggestion_down
|
||||
|
||||
event = Mock()
|
||||
event.current_buffer = buffer
|
||||
|
||||
def get_suggestion():
|
||||
suggestion = provider.get_suggestion(buffer, buffer.document)
|
||||
buffer.suggestion = suggestion
|
||||
return suggestion
|
||||
|
||||
assert get_suggestion().text == "_c"
|
||||
|
||||
# should go up
|
||||
up(event)
|
||||
assert get_suggestion().text == "_b"
|
||||
|
||||
# should skip over 'very' which is identical to buffer content
|
||||
up(event)
|
||||
assert get_suggestion().text == "_a"
|
||||
|
||||
# should cycle back to beginning
|
||||
up(event)
|
||||
assert get_suggestion().text == "_c"
|
||||
|
||||
# should cycle back through end boundary
|
||||
down(event)
|
||||
assert get_suggestion().text == "_a"
|
||||
|
||||
down(event)
|
||||
assert get_suggestion().text == "_b"
|
||||
|
||||
down(event)
|
||||
assert get_suggestion().text == "_c"
|
||||
|
||||
down(event)
|
||||
assert get_suggestion().text == "_a"
|
||||
|
||||
|
||||
async def test_navigable_provider_multiline_entries():
|
||||
provider = NavigableAutoSuggestFromHistory()
|
||||
history = InMemoryHistory(history_strings=["very_a\nvery_b", "very_c"])
|
||||
buffer = Buffer(history=history)
|
||||
ip = get_ipython()
|
||||
ip.auto_suggest = provider
|
||||
|
||||
async for _ in history.load():
|
||||
pass
|
||||
|
||||
buffer.cursor_position = 5
|
||||
buffer.text = "very"
|
||||
up = swap_autosuggestion_up
|
||||
down = swap_autosuggestion_down
|
||||
|
||||
event = Mock()
|
||||
event.current_buffer = buffer
|
||||
|
||||
def get_suggestion():
|
||||
suggestion = provider.get_suggestion(buffer, buffer.document)
|
||||
buffer.suggestion = suggestion
|
||||
return suggestion
|
||||
|
||||
assert get_suggestion().text == "_c"
|
||||
|
||||
up(event)
|
||||
assert get_suggestion().text == "_b"
|
||||
|
||||
up(event)
|
||||
assert get_suggestion().text == "_a"
|
||||
|
||||
down(event)
|
||||
assert get_suggestion().text == "_b"
|
||||
|
||||
down(event)
|
||||
assert get_suggestion().text == "_c"
|
||||
|
||||
|
||||
def create_session_mock():
|
||||
session = Mock()
|
||||
session.default_buffer = Buffer()
|
||||
return session
|
||||
|
||||
|
||||
def test_navigable_provider_connection():
|
||||
provider = NavigableAutoSuggestFromHistory()
|
||||
provider.skip_lines = 1
|
||||
|
||||
session_1 = create_session_mock()
|
||||
provider.connect(session_1)
|
||||
|
||||
assert provider.skip_lines == 1
|
||||
session_1.default_buffer.on_text_insert.fire()
|
||||
assert provider.skip_lines == 0
|
||||
|
||||
session_2 = create_session_mock()
|
||||
provider.connect(session_2)
|
||||
provider.skip_lines = 2
|
||||
|
||||
assert provider.skip_lines == 2
|
||||
session_2.default_buffer.on_text_insert.fire()
|
||||
assert provider.skip_lines == 0
|
||||
|
||||
provider.skip_lines = 3
|
||||
provider.disconnect()
|
||||
session_1.default_buffer.on_text_insert.fire()
|
||||
session_2.default_buffer.on_text_insert.fire()
|
||||
assert provider.skip_lines == 3
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ipython_with_prompt():
|
||||
ip = get_ipython()
|
||||
ip.pt_app = Mock()
|
||||
ip.pt_app.key_bindings = create_ipython_shortcuts(ip)
|
||||
try:
|
||||
yield ip
|
||||
finally:
|
||||
ip.pt_app = None
|
||||
|
||||
|
||||
def find_bindings_by_command(command):
|
||||
ip = get_ipython()
|
||||
return [
|
||||
binding
|
||||
for binding in ip.pt_app.key_bindings.bindings
|
||||
if binding.handler == command
|
||||
]
|
||||
|
||||
|
||||
def test_modify_unique_shortcut(ipython_with_prompt):
|
||||
original = find_bindings_by_command(accept_token)
|
||||
assert len(original) == 1
|
||||
|
||||
ipython_with_prompt.shortcuts = [
|
||||
{"command": "IPython:auto_suggest.accept_token", "new_keys": ["a", "b", "c"]}
|
||||
]
|
||||
matched = find_bindings_by_command(accept_token)
|
||||
assert len(matched) == 1
|
||||
assert list(matched[0].keys) == ["a", "b", "c"]
|
||||
assert list(matched[0].keys) != list(original[0].keys)
|
||||
assert matched[0].filter == original[0].filter
|
||||
|
||||
ipython_with_prompt.shortcuts = [
|
||||
{"command": "IPython:auto_suggest.accept_token", "new_filter": "always"}
|
||||
]
|
||||
matched = find_bindings_by_command(accept_token)
|
||||
assert len(matched) == 1
|
||||
assert list(matched[0].keys) != ["a", "b", "c"]
|
||||
assert list(matched[0].keys) == list(original[0].keys)
|
||||
assert matched[0].filter != original[0].filter
|
||||
|
||||
|
||||
def test_disable_shortcut(ipython_with_prompt):
|
||||
matched = find_bindings_by_command(accept_token)
|
||||
assert len(matched) == 1
|
||||
|
||||
ipython_with_prompt.shortcuts = [
|
||||
{"command": "IPython:auto_suggest.accept_token", "new_keys": []}
|
||||
]
|
||||
matched = find_bindings_by_command(accept_token)
|
||||
assert len(matched) == 0
|
||||
|
||||
ipython_with_prompt.shortcuts = []
|
||||
matched = find_bindings_by_command(accept_token)
|
||||
assert len(matched) == 1
|
||||
|
||||
|
||||
def test_modify_shortcut_with_filters(ipython_with_prompt):
|
||||
matched = find_bindings_by_command(skip_over)
|
||||
matched_keys = {m.keys[0] for m in matched}
|
||||
assert matched_keys == {")", "]", "}", "'", '"'}
|
||||
|
||||
with pytest.raises(ValueError, match="Multiple shortcuts matching"):
|
||||
ipython_with_prompt.shortcuts = [
|
||||
{"command": "IPython:auto_match.skip_over", "new_keys": ["x"]}
|
||||
]
|
||||
|
||||
ipython_with_prompt.shortcuts = [
|
||||
{
|
||||
"command": "IPython:auto_match.skip_over",
|
||||
"new_keys": ["x"],
|
||||
"match_filter": "focused_insert & auto_match & followed_by_single_quote",
|
||||
}
|
||||
]
|
||||
matched = find_bindings_by_command(skip_over)
|
||||
matched_keys = {m.keys[0] for m in matched}
|
||||
assert matched_keys == {")", "]", "}", "x", '"'}
|
||||
|
||||
|
||||
def example_command():
|
||||
pass
|
||||
|
||||
|
||||
def test_add_shortcut_for_new_command(ipython_with_prompt):
|
||||
matched = find_bindings_by_command(example_command)
|
||||
assert len(matched) == 0
|
||||
|
||||
with pytest.raises(ValueError, match="example_command is not a known"):
|
||||
ipython_with_prompt.shortcuts = [
|
||||
{"command": "example_command", "new_keys": ["x"]}
|
||||
]
|
||||
matched = find_bindings_by_command(example_command)
|
||||
assert len(matched) == 0
|
||||
|
||||
|
||||
def test_modify_shortcut_failure(ipython_with_prompt):
|
||||
with pytest.raises(ValueError, match="No shortcuts matching"):
|
||||
ipython_with_prompt.shortcuts = [
|
||||
{
|
||||
"command": "IPython:auto_match.skip_over",
|
||||
"match_keys": ["x"],
|
||||
"new_keys": ["y"],
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_add_shortcut_for_existing_command(ipython_with_prompt):
|
||||
matched = find_bindings_by_command(skip_over)
|
||||
assert len(matched) == 5
|
||||
|
||||
with pytest.raises(ValueError, match="Cannot add a shortcut without keys"):
|
||||
ipython_with_prompt.shortcuts = [
|
||||
{"command": "IPython:auto_match.skip_over", "new_keys": [], "create": True}
|
||||
]
|
||||
|
||||
ipython_with_prompt.shortcuts = [
|
||||
{"command": "IPython:auto_match.skip_over", "new_keys": ["x"], "create": True}
|
||||
]
|
||||
matched = find_bindings_by_command(skip_over)
|
||||
assert len(matched) == 6
|
||||
|
||||
ipython_with_prompt.shortcuts = []
|
||||
matched = find_bindings_by_command(skip_over)
|
||||
assert len(matched) == 5
|
||||
|
||||
|
||||
def test_setting_shortcuts_before_pt_app_init():
|
||||
ipython = get_ipython()
|
||||
assert ipython.pt_app is None
|
||||
shortcuts = [
|
||||
{"command": "IPython:auto_match.skip_over", "new_keys": ["x"], "create": True}
|
||||
]
|
||||
ipython.shortcuts = shortcuts
|
||||
assert ipython.shortcuts == shortcuts
|
||||
Reference in New Issue
Block a user