Uploaded Test files

This commit is contained in:
Batuhan Berk Başoğlu 2020-11-12 11:05:57 -05:00
parent f584ad9d97
commit 2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions

View file

@ -0,0 +1,19 @@
"""
Shim to maintain backwards compatibility with old IPython.terminal.console imports.
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
import sys
from warnings import warn
from IPython.utils.shimmodule import ShimModule, ShimWarning
warn("The `IPython.terminal.console` package has been deprecated since IPython 4.0. "
"You should import from jupyter_console instead.", ShimWarning)
# Unconditionally insert the shim into sys.modules so that further import calls
# trigger the custom attribute access above
sys.modules['IPython.terminal.console'] = ShimModule(
src='IPython.terminal.console', mirror='jupyter_console')

View file

@ -0,0 +1,151 @@
import asyncio
import signal
import sys
import threading
from IPython.core.debugger import Pdb
from IPython.core.completer import IPCompleter
from .ptutils import IPythonPTCompleter
from .shortcuts import create_ipython_shortcuts, suspend_to_bg, cursor_in_leading_ws
from prompt_toolkit.enums import DEFAULT_BUFFER
from prompt_toolkit.filters import (Condition, has_focus, has_selection,
vi_insert_mode, emacs_insert_mode)
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
from pygments.token import Token
from prompt_toolkit.shortcuts.prompt import PromptSession
from prompt_toolkit.enums import EditingMode
from prompt_toolkit.formatted_text import PygmentsTokens
from prompt_toolkit import __version__ as ptk_version
PTK3 = ptk_version.startswith('3.')
class TerminalPdb(Pdb):
"""Standalone IPython debugger."""
def __init__(self, *args, **kwargs):
Pdb.__init__(self, *args, **kwargs)
self._ptcomp = None
self.pt_init()
def pt_init(self):
def get_prompt_tokens():
return [(Token.Prompt, self.prompt)]
if self._ptcomp is None:
compl = IPCompleter(shell=self.shell,
namespace={},
global_namespace={},
parent=self.shell,
)
# add a completer for all the do_ methods
methods_names = [m[3:] for m in dir(self) if m.startswith("do_")]
def gen_comp(self, text):
return [m for m in methods_names if m.startswith(text)]
import types
newcomp = types.MethodType(gen_comp, compl)
compl.custom_matchers.insert(0, newcomp)
# end add completer.
self._ptcomp = IPythonPTCompleter(compl)
options = dict(
message=(lambda: PygmentsTokens(get_prompt_tokens())),
editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
key_bindings=create_ipython_shortcuts(self.shell),
history=self.shell.debugger_history,
completer=self._ptcomp,
enable_history_search=True,
mouse_support=self.shell.mouse_support,
complete_style=self.shell.pt_complete_style,
style=self.shell.style,
color_depth=self.shell.color_depth,
)
if not PTK3:
options['inputhook'] = self.shell.inputhook
self.pt_loop = asyncio.new_event_loop()
self.pt_app = PromptSession(**options)
def cmdloop(self, intro=None):
"""Repeatedly issue a prompt, accept input, parse an initial prefix
off the received input, and dispatch to action methods, passing them
the remainder of the line as argument.
override the same methods from cmd.Cmd to provide prompt toolkit replacement.
"""
if not self.use_rawinput:
raise ValueError('Sorry ipdb does not support use_rawinput=False')
# In order to make sure that prompt, which uses asyncio doesn't
# interfere with applications in which it's used, we always run the
# prompt itself in a different thread (we can't start an event loop
# within an event loop). This new thread won't have any event loop
# running, and here we run our prompt-loop.
self.preloop()
try:
if intro is not None:
self.intro = intro
if self.intro:
self.stdout.write(str(self.intro)+"\n")
stop = None
while not stop:
if self.cmdqueue:
line = self.cmdqueue.pop(0)
else:
self._ptcomp.ipy_completer.namespace = self.curframe_locals
self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
# Run the prompt in a different thread.
line = ''
keyboard_interrupt = False
def in_thread():
nonlocal line, keyboard_interrupt
try:
line = self.pt_app.prompt()
except EOFError:
line = 'EOF'
except KeyboardInterrupt:
keyboard_interrupt = True
th = threading.Thread(target=in_thread)
th.start()
th.join()
if keyboard_interrupt:
raise KeyboardInterrupt
line = self.precmd(line)
stop = self.onecmd(line)
stop = self.postcmd(stop, line)
self.postloop()
except Exception:
raise
def set_trace(frame=None):
"""
Start debugging from `frame`.
If frame is not specified, debugging starts from caller's frame.
"""
TerminalPdb().set_trace(frame or sys._getframe().f_back)
if __name__ == '__main__':
import pdb
# IPython.core.debugger.Pdb.trace_dispatch shall not catch
# bdb.BdbQuit. When started through __main__ and an exception
# happened after hitting "c", this is needed in order to
# be able to quit the debugging session (see #9950).
old_trace_dispatch = pdb.Pdb.trace_dispatch
pdb.Pdb = TerminalPdb
pdb.Pdb.trace_dispatch = old_trace_dispatch
pdb.main()

View file

@ -0,0 +1,399 @@
# encoding: utf-8
"""
An embedded IPython shell.
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
import sys
import warnings
from IPython.core import ultratb, compilerop
from IPython.core import magic_arguments
from IPython.core.magic import Magics, magics_class, line_magic
from IPython.core.interactiveshell import DummyMod, InteractiveShell
from IPython.terminal.interactiveshell import TerminalInteractiveShell
from IPython.terminal.ipapp import load_default_config
from traitlets import Bool, CBool, Unicode
from IPython.utils.io import ask_yes_no
class KillEmbedded(Exception):pass
# kept for backward compatibility as IPython 6 was released with
# the typo. See https://github.com/ipython/ipython/pull/10706
KillEmbeded = KillEmbedded
# This is an additional magic that is exposed in embedded shells.
@magics_class
class EmbeddedMagics(Magics):
@line_magic
@magic_arguments.magic_arguments()
@magic_arguments.argument('-i', '--instance', action='store_true',
help='Kill instance instead of call location')
@magic_arguments.argument('-x', '--exit', action='store_true',
help='Also exit the current session')
@magic_arguments.argument('-y', '--yes', action='store_true',
help='Do not ask confirmation')
def kill_embedded(self, parameter_s=''):
"""%kill_embedded : deactivate for good the current embedded IPython
This function (after asking for confirmation) sets an internal flag so
that an embedded IPython will never activate again for the given call
location. This is useful to permanently disable a shell that is being
called inside a loop: once you've figured out what you needed from it,
you may then kill it and the program will then continue to run without
the interactive shell interfering again.
Kill Instance Option:
If for some reasons you need to kill the location where the instance
is created and not called, for example if you create a single
instance in one place and debug in many locations, you can use the
``--instance`` option to kill this specific instance. Like for the
``call location`` killing an "instance" should work even if it is
recreated within a loop.
.. note::
This was the default behavior before IPython 5.2
"""
args = magic_arguments.parse_argstring(self.kill_embedded, parameter_s)
print(args)
if args.instance:
# let no ask
if not args.yes:
kill = ask_yes_no(
"Are you sure you want to kill this embedded instance? [y/N] ", 'n')
else:
kill = True
if kill:
self.shell._disable_init_location()
print("This embedded IPython instance will not reactivate anymore "
"once you exit.")
else:
if not args.yes:
kill = ask_yes_no(
"Are you sure you want to kill this embedded call_location? [y/N] ", 'n')
else:
kill = True
if kill:
self.shell.embedded_active = False
print("This embedded IPython call location will not reactivate anymore "
"once you exit.")
if args.exit:
# Ask-exit does not really ask, it just set internals flags to exit
# on next loop.
self.shell.ask_exit()
@line_magic
def exit_raise(self, parameter_s=''):
"""%exit_raise Make the current embedded kernel exit and raise and exception.
This function sets an internal flag so that an embedded IPython will
raise a `IPython.terminal.embed.KillEmbedded` Exception on exit, and then exit the current I. This is
useful to permanently exit a loop that create IPython embed instance.
"""
self.shell.should_raise = True
self.shell.ask_exit()
class InteractiveShellEmbed(TerminalInteractiveShell):
dummy_mode = Bool(False)
exit_msg = Unicode('')
embedded = CBool(True)
should_raise = CBool(False)
# Like the base class display_banner is not configurable, but here it
# is True by default.
display_banner = CBool(True)
exit_msg = Unicode()
# When embedding, by default we don't change the terminal title
term_title = Bool(False,
help="Automatically set the terminal title"
).tag(config=True)
_inactive_locations = set()
@property
def embedded_active(self):
return (self._call_location_id not in InteractiveShellEmbed._inactive_locations)\
and (self._init_location_id not in InteractiveShellEmbed._inactive_locations)
def _disable_init_location(self):
"""Disable the current Instance creation location"""
InteractiveShellEmbed._inactive_locations.add(self._init_location_id)
@embedded_active.setter
def embedded_active(self, value):
if value:
InteractiveShellEmbed._inactive_locations.discard(
self._call_location_id)
InteractiveShellEmbed._inactive_locations.discard(
self._init_location_id)
else:
InteractiveShellEmbed._inactive_locations.add(
self._call_location_id)
def __init__(self, **kw):
if kw.get('user_global_ns', None) is not None:
raise DeprecationWarning(
"Key word argument `user_global_ns` has been replaced by `user_module` since IPython 4.0.")
clid = kw.pop('_init_location_id', None)
if not clid:
frame = sys._getframe(1)
clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
self._init_location_id = clid
super(InteractiveShellEmbed,self).__init__(**kw)
# don't use the ipython crash handler so that user exceptions aren't
# trapped
sys.excepthook = ultratb.FormattedTB(color_scheme=self.colors,
mode=self.xmode,
call_pdb=self.pdb)
def init_sys_modules(self):
"""
Explicitly overwrite :mod:`IPython.core.interactiveshell` to do nothing.
"""
pass
def init_magics(self):
super(InteractiveShellEmbed, self).init_magics()
self.register_magics(EmbeddedMagics)
def __call__(self, header='', local_ns=None, module=None, dummy=None,
stack_depth=1, global_ns=None, compile_flags=None, **kw):
"""Activate the interactive interpreter.
__call__(self,header='',local_ns=None,module=None,dummy=None) -> Start
the interpreter shell with the given local and global namespaces, and
optionally print a header string at startup.
The shell can be globally activated/deactivated using the
dummy_mode attribute. This allows you to turn off a shell used
for debugging globally.
However, *each* time you call the shell you can override the current
state of dummy_mode with the optional keyword parameter 'dummy'. For
example, if you set dummy mode on with IPShell.dummy_mode = True, you
can still have a specific call work by making it as IPShell(dummy=False).
"""
# we are called, set the underlying interactiveshell not to exit.
self.keep_running = True
# If the user has turned it off, go away
clid = kw.pop('_call_location_id', None)
if not clid:
frame = sys._getframe(1)
clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
self._call_location_id = clid
if not self.embedded_active:
return
# Normal exits from interactive mode set this flag, so the shell can't
# re-enter (it checks this variable at the start of interactive mode).
self.exit_now = False
# Allow the dummy parameter to override the global __dummy_mode
if dummy or (dummy != 0 and self.dummy_mode):
return
# self.banner is auto computed
if header:
self.old_banner2 = self.banner2
self.banner2 = self.banner2 + '\n' + header + '\n'
else:
self.old_banner2 = ''
if self.display_banner:
self.show_banner()
# Call the embedding code with a stack depth of 1 so it can skip over
# our call and get the original caller's namespaces.
self.mainloop(local_ns, module, stack_depth=stack_depth,
global_ns=global_ns, compile_flags=compile_flags)
self.banner2 = self.old_banner2
if self.exit_msg is not None:
print(self.exit_msg)
if self.should_raise:
raise KillEmbedded('Embedded IPython raising error, as user requested.')
def mainloop(self, local_ns=None, module=None, stack_depth=0,
display_banner=None, global_ns=None, compile_flags=None):
"""Embeds IPython into a running python program.
Parameters
----------
local_ns, module
Working local namespace (a dict) and module (a module or similar
object). If given as None, they are automatically taken from the scope
where the shell was called, so that program variables become visible.
stack_depth : int
How many levels in the stack to go to looking for namespaces (when
local_ns or module is None). This allows an intermediate caller to
make sure that this function gets the namespace from the intended
level in the stack. By default (0) it will get its locals and globals
from the immediate caller.
compile_flags
A bit field identifying the __future__ features
that are enabled, as passed to the builtin :func:`compile` function.
If given as None, they are automatically taken from the scope where
the shell was called.
"""
if (global_ns is not None) and (module is None):
raise DeprecationWarning("'global_ns' keyword argument is deprecated, and has been removed in IPython 5.0 use `module` keyword argument instead.")
if (display_banner is not None):
warnings.warn("The display_banner parameter is deprecated since IPython 4.0", DeprecationWarning)
# Get locals and globals from caller
if ((local_ns is None or module is None or compile_flags is None)
and self.default_user_namespaces):
call_frame = sys._getframe(stack_depth).f_back
if local_ns is None:
local_ns = call_frame.f_locals
if module is None:
global_ns = call_frame.f_globals
try:
module = sys.modules[global_ns['__name__']]
except KeyError:
warnings.warn("Failed to get module %s" % \
global_ns.get('__name__', 'unknown module')
)
module = DummyMod()
module.__dict__ = global_ns
if compile_flags is None:
compile_flags = (call_frame.f_code.co_flags &
compilerop.PyCF_MASK)
# Save original namespace and module so we can restore them after
# embedding; otherwise the shell doesn't shut down correctly.
orig_user_module = self.user_module
orig_user_ns = self.user_ns
orig_compile_flags = self.compile.flags
# Update namespaces and fire up interpreter
# The global one is easy, we can just throw it in
if module is not None:
self.user_module = module
# But the user/local one is tricky: ipython needs it to store internal
# data, but we also need the locals. We'll throw our hidden variables
# like _ih and get_ipython() into the local namespace, but delete them
# later.
if local_ns is not None:
reentrant_local_ns = {k: v for (k, v) in local_ns.items() if k not in self.user_ns_hidden.keys()}
self.user_ns = reentrant_local_ns
self.init_user_ns()
# Compiler flags
if compile_flags is not None:
self.compile.flags = compile_flags
# make sure the tab-completer has the correct frame information, so it
# actually completes using the frame's locals/globals
self.set_completer_frame()
with self.builtin_trap, self.display_trap:
self.interact()
# now, purge out the local namespace of IPython's hidden variables.
if local_ns is not None:
local_ns.update({k: v for (k, v) in self.user_ns.items() if k not in self.user_ns_hidden.keys()})
# Restore original namespace so shell can shut down when we exit.
self.user_module = orig_user_module
self.user_ns = orig_user_ns
self.compile.flags = orig_compile_flags
def embed(**kwargs):
"""Call this to embed IPython at the current point in your program.
The first invocation of this will create an :class:`InteractiveShellEmbed`
instance and then call it. Consecutive calls just call the already
created instance.
If you don't want the kernel to initialize the namespace
from the scope of the surrounding function,
and/or you want to load full IPython configuration,
you probably want `IPython.start_ipython()` instead.
Here is a simple example::
from IPython import embed
a = 10
b = 20
embed(header='First time')
c = 30
d = 40
embed()
Full customization can be done by passing a :class:`Config` in as the
config argument.
"""
config = kwargs.get('config')
header = kwargs.pop('header', u'')
compile_flags = kwargs.pop('compile_flags', None)
if config is None:
config = load_default_config()
config.InteractiveShellEmbed = config.TerminalInteractiveShell
kwargs['config'] = config
using = kwargs.get('using', 'sync')
if using :
kwargs['config'].update({'TerminalInteractiveShell':{'loop_runner':using, 'colors':'NoColor', 'autoawait': using!='sync'}})
#save ps1/ps2 if defined
ps1 = None
ps2 = None
try:
ps1 = sys.ps1
ps2 = sys.ps2
except AttributeError:
pass
#save previous instance
saved_shell_instance = InteractiveShell._instance
if saved_shell_instance is not None:
cls = type(saved_shell_instance)
cls.clear_instance()
frame = sys._getframe(1)
shell = InteractiveShellEmbed.instance(_init_location_id='%s:%s' % (
frame.f_code.co_filename, frame.f_lineno), **kwargs)
shell(header=header, stack_depth=2, compile_flags=compile_flags,
_call_location_id='%s:%s' % (frame.f_code.co_filename, frame.f_lineno))
InteractiveShellEmbed.clear_instance()
#restore previous instance
if saved_shell_instance is not None:
cls = type(saved_shell_instance)
cls.clear_instance()
for subclass in cls._walk_mro():
subclass._instance = saved_shell_instance
if ps1 is not None:
sys.ps1 = ps1
sys.ps2 = ps2

View file

@ -0,0 +1,645 @@
"""IPython terminal interface using prompt_toolkit"""
import asyncio
import os
import sys
import warnings
from warnings import warn
from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
from IPython.utils import io
from IPython.utils.py3compat import input
from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title
from IPython.utils.process import abbrev_cwd
from traitlets import (
Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
Any, validate
)
from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
from prompt_toolkit.formatted_text import PygmentsTokens
from prompt_toolkit.history import InMemoryHistory
from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
from prompt_toolkit.output import ColorDepth
from prompt_toolkit.patch_stdout import patch_stdout
from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
from prompt_toolkit.styles import DynamicStyle, merge_styles
from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
from prompt_toolkit import __version__ as ptk_version
from pygments.styles import get_style_by_name
from pygments.style import Style
from pygments.token import Token
from .debugger import TerminalPdb, Pdb
from .magics import TerminalMagics
from .pt_inputhooks import get_inputhook_name_and_func
from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
from .ptutils import IPythonPTCompleter, IPythonPTLexer
from .shortcuts import create_ipython_shortcuts
DISPLAY_BANNER_DEPRECATED = object()
PTK3 = ptk_version.startswith('3.')
class _NoStyle(Style): pass
_style_overrides_light_bg = {
Token.Prompt: '#0000ff',
Token.PromptNum: '#0000ee bold',
Token.OutPrompt: '#cc0000',
Token.OutPromptNum: '#bb0000 bold',
}
_style_overrides_linux = {
Token.Prompt: '#00cc00',
Token.PromptNum: '#00bb00 bold',
Token.OutPrompt: '#cc0000',
Token.OutPromptNum: '#bb0000 bold',
}
def get_default_editor():
try:
return os.environ['EDITOR']
except KeyError:
pass
except UnicodeError:
warn("$EDITOR environment variable is not pure ASCII. Using platform "
"default editor.")
if os.name == 'posix':
return 'vi' # the only one guaranteed to be there!
else:
return 'notepad' # same in Windows!
# conservatively check for tty
# overridden streams can result in things like:
# - sys.stdin = None
# - no isatty method
for _name in ('stdin', 'stdout', 'stderr'):
_stream = getattr(sys, _name)
if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
_is_tty = False
break
else:
_is_tty = True
_use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
def black_reformat_handler(text_before_cursor):
import black
formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
if not text_before_cursor.endswith('\n') and formatted_text.endswith('\n'):
formatted_text = formatted_text[:-1]
return formatted_text
class TerminalInteractiveShell(InteractiveShell):
mime_renderers = Dict().tag(config=True)
space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
'to reserve for the tab completion menu, '
'search history, ...etc, the height of '
'these menus will at most this value. '
'Increase it is you prefer long and skinny '
'menus, decrease for short and wide.'
).tag(config=True)
pt_app = None
debugger_history = None
simple_prompt = Bool(_use_simple_prompt,
help="""Use `raw_input` for the REPL, without completion and prompt colors.
Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
IPython own testing machinery, and emacs inferior-shell integration through elpy.
This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
environment variable is set, or the current terminal is not a tty."""
).tag(config=True)
@property
def debugger_cls(self):
return Pdb if self.simple_prompt else TerminalPdb
confirm_exit = Bool(True,
help="""
Set to confirm when you try to exit IPython with an EOF (Control-D
in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
you can force a direct exit without any confirmation.""",
).tag(config=True)
editing_mode = Unicode('emacs',
help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
).tag(config=True)
autoformatter = Unicode(None,
help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
allow_none=True
).tag(config=True)
mouse_support = Bool(False,
help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
).tag(config=True)
# We don't load the list of styles for the help string, because loading
# Pygments plugins takes time and can cause unexpected errors.
highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
help="""The name or class of a Pygments style to use for syntax
highlighting. To see available styles, run `pygmentize -L styles`."""
).tag(config=True)
@validate('editing_mode')
def _validate_editing_mode(self, proposal):
if proposal['value'].lower() == 'vim':
proposal['value']= 'vi'
elif proposal['value'].lower() == 'default':
proposal['value']= 'emacs'
if hasattr(EditingMode, proposal['value'].upper()):
return proposal['value'].lower()
return self.editing_mode
@observe('editing_mode')
def _editing_mode(self, change):
u_mode = change.new.upper()
if self.pt_app:
self.pt_app.editing_mode = u_mode
@observe('autoformatter')
def _autoformatter_changed(self, change):
formatter = change.new
if formatter is None:
self.reformat_handler = lambda x:x
elif formatter == 'black':
self.reformat_handler = black_reformat_handler
else:
raise ValueError
@observe('highlighting_style')
@observe('colors')
def _highlighting_style_changed(self, change):
self.refresh_style()
def refresh_style(self):
self._style = self._make_style_from_name_or_cls(self.highlighting_style)
highlighting_style_overrides = Dict(
help="Override highlighting format for specific tokens"
).tag(config=True)
true_color = Bool(False,
help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
"If your terminal supports true color, the following command "
"should print 'TRUECOLOR' in orange: "
"printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
).tag(config=True)
editor = Unicode(get_default_editor(),
help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
).tag(config=True)
prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
prompts = Instance(Prompts)
@default('prompts')
def _prompts_default(self):
return self.prompts_class(self)
# @observe('prompts')
# def _(self, change):
# self._update_layout()
@default('displayhook_class')
def _displayhook_class_default(self):
return RichPromptDisplayHook
term_title = Bool(True,
help="Automatically set the terminal title"
).tag(config=True)
term_title_format = Unicode("IPython: {cwd}",
help="Customize the terminal title format. This is a python format string. " +
"Available substitutions are: {cwd}."
).tag(config=True)
display_completions = Enum(('column', 'multicolumn','readlinelike'),
help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
"'readlinelike'. These options are for `prompt_toolkit`, see "
"`prompt_toolkit` documentation for more information."
),
default_value='multicolumn').tag(config=True)
highlight_matching_brackets = Bool(True,
help="Highlight matching brackets.",
).tag(config=True)
extra_open_editor_shortcuts = Bool(False,
help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
"This is in addition to the F2 binding, which is always enabled."
).tag(config=True)
handle_return = Any(None,
help="Provide an alternative handler to be called when the user presses "
"Return. This is an advanced option intended for debugging, which "
"may be changed or removed in later releases."
).tag(config=True)
enable_history_search = Bool(True,
help="Allows to enable/disable the prompt toolkit history search"
).tag(config=True)
prompt_includes_vi_mode = Bool(True,
help="Display the current vi mode (when using vi editing mode)."
).tag(config=True)
@observe('term_title')
def init_term_title(self, change=None):
# Enable or disable the terminal title.
if self.term_title:
toggle_set_term_title(True)
set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
else:
toggle_set_term_title(False)
def restore_term_title(self):
if self.term_title:
restore_term_title()
def init_display_formatter(self):
super(TerminalInteractiveShell, self).init_display_formatter()
# terminal only supports plain text
self.display_formatter.active_types = ['text/plain']
# disable `_ipython_display_`
self.display_formatter.ipython_display_formatter.enabled = False
def init_prompt_toolkit_cli(self):
if self.simple_prompt:
# Fall back to plain non-interactive output for tests.
# This is very limited.
def prompt():
prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
lines = [input(prompt_text)]
prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
while self.check_complete('\n'.join(lines))[0] == 'incomplete':
lines.append( input(prompt_continuation) )
return '\n'.join(lines)
self.prompt_for_code = prompt
return
# Set up keyboard shortcuts
key_bindings = create_ipython_shortcuts(self)
# Pre-populate history from IPython's history database
history = InMemoryHistory()
last_cell = u""
for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
include_latest=True):
# Ignore blank lines and consecutive duplicates
cell = cell.rstrip()
if cell and (cell != last_cell):
history.append_string(cell)
last_cell = cell
self._style = self._make_style_from_name_or_cls(self.highlighting_style)
self.style = DynamicStyle(lambda: self._style)
editing_mode = getattr(EditingMode, self.editing_mode.upper())
self.pt_loop = asyncio.new_event_loop()
self.pt_app = PromptSession(
editing_mode=editing_mode,
key_bindings=key_bindings,
history=history,
completer=IPythonPTCompleter(shell=self),
enable_history_search = self.enable_history_search,
style=self.style,
include_default_pygments_style=False,
mouse_support=self.mouse_support,
enable_open_in_editor=self.extra_open_editor_shortcuts,
color_depth=self.color_depth,
tempfile_suffix=".py",
**self._extra_prompt_options())
def _make_style_from_name_or_cls(self, name_or_cls):
"""
Small wrapper that make an IPython compatible style from a style name
We need that to add style for prompt ... etc.
"""
style_overrides = {}
if name_or_cls == 'legacy':
legacy = self.colors.lower()
if legacy == 'linux':
style_cls = get_style_by_name('monokai')
style_overrides = _style_overrides_linux
elif legacy == 'lightbg':
style_overrides = _style_overrides_light_bg
style_cls = get_style_by_name('pastie')
elif legacy == 'neutral':
# The default theme needs to be visible on both a dark background
# and a light background, because we can't tell what the terminal
# looks like. These tweaks to the default theme help with that.
style_cls = get_style_by_name('default')
style_overrides.update({
Token.Number: '#007700',
Token.Operator: 'noinherit',
Token.String: '#BB6622',
Token.Name.Function: '#2080D0',
Token.Name.Class: 'bold #2080D0',
Token.Name.Namespace: 'bold #2080D0',
Token.Prompt: '#009900',
Token.PromptNum: '#ansibrightgreen bold',
Token.OutPrompt: '#990000',
Token.OutPromptNum: '#ansibrightred bold',
})
# Hack: Due to limited color support on the Windows console
# the prompt colors will be wrong without this
if os.name == 'nt':
style_overrides.update({
Token.Prompt: '#ansidarkgreen',
Token.PromptNum: '#ansigreen bold',
Token.OutPrompt: '#ansidarkred',
Token.OutPromptNum: '#ansired bold',
})
elif legacy =='nocolor':
style_cls=_NoStyle
style_overrides = {}
else :
raise ValueError('Got unknown colors: ', legacy)
else :
if isinstance(name_or_cls, str):
style_cls = get_style_by_name(name_or_cls)
else:
style_cls = name_or_cls
style_overrides = {
Token.Prompt: '#009900',
Token.PromptNum: '#ansibrightgreen bold',
Token.OutPrompt: '#990000',
Token.OutPromptNum: '#ansibrightred bold',
}
style_overrides.update(self.highlighting_style_overrides)
style = merge_styles([
style_from_pygments_cls(style_cls),
style_from_pygments_dict(style_overrides),
])
return style
@property
def pt_complete_style(self):
return {
'multicolumn': CompleteStyle.MULTI_COLUMN,
'column': CompleteStyle.COLUMN,
'readlinelike': CompleteStyle.READLINE_LIKE,
}[self.display_completions]
@property
def color_depth(self):
return (ColorDepth.TRUE_COLOR if self.true_color else None)
def _extra_prompt_options(self):
"""
Return the current layout option for the current Terminal InteractiveShell
"""
def get_message():
return PygmentsTokens(self.prompts.in_prompt_tokens())
if self.editing_mode == 'emacs':
# with emacs mode the prompt is (usually) static, so we call only
# the function once. With VI mode it can toggle between [ins] and
# [nor] so we can't precompute.
# here I'm going to favor the default keybinding which almost
# everybody uses to decrease CPU usage.
# if we have issues with users with custom Prompts we can see how to
# work around this.
get_message = get_message()
options = {
'complete_in_thread': False,
'lexer':IPythonPTLexer(),
'reserve_space_for_menu':self.space_for_menu,
'message': get_message,
'prompt_continuation': (
lambda width, lineno, is_soft_wrap:
PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
'multiline': True,
'complete_style': self.pt_complete_style,
# Highlight matching brackets, but only when this setting is
# enabled, and only when the DEFAULT_BUFFER has the focus.
'input_processors': [ConditionalProcessor(
processor=HighlightMatchingBracketProcessor(chars='[](){}'),
filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
Condition(lambda: self.highlight_matching_brackets))],
}
if not PTK3:
options['inputhook'] = self.inputhook
return options
def prompt_for_code(self):
if self.rl_next_input:
default = self.rl_next_input
self.rl_next_input = None
else:
default = ''
# In order to make sure that asyncio code written in the
# interactive shell doesn't interfere with the prompt, we run the
# prompt in a different event loop.
# If we don't do this, people could spawn coroutine with a
# while/true inside which will freeze the prompt.
try:
old_loop = asyncio.get_event_loop()
except RuntimeError:
# This happens when the user used `asyncio.run()`.
old_loop = None
asyncio.set_event_loop(self.pt_loop)
try:
with patch_stdout(raw=True):
text = self.pt_app.prompt(
default=default,
**self._extra_prompt_options())
finally:
# Restore the original event loop.
asyncio.set_event_loop(old_loop)
return text
def enable_win_unicode_console(self):
# Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
# console by default, so WUC shouldn't be needed.
from warnings import warn
warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
DeprecationWarning,
stacklevel=2)
def init_io(self):
if sys.platform not in {'win32', 'cli'}:
return
import colorama
colorama.init()
# For some reason we make these wrappers around stdout/stderr.
# For now, we need to reset them so all output gets coloured.
# https://github.com/ipython/ipython/issues/8669
# io.std* are deprecated, but don't show our own deprecation warnings
# during initialization of the deprecated API.
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
io.stdout = io.IOStream(sys.stdout)
io.stderr = io.IOStream(sys.stderr)
def init_magics(self):
super(TerminalInteractiveShell, self).init_magics()
self.register_magics(TerminalMagics)
def init_alias(self):
# The parent class defines aliases that can be safely used with any
# frontend.
super(TerminalInteractiveShell, self).init_alias()
# Now define aliases that only make sense on the terminal, because they
# need direct access to the console in a way that we can't emulate in
# GUI or web frontend
if os.name == 'posix':
for cmd in ('clear', 'more', 'less', 'man'):
self.alias_manager.soft_define_alias(cmd, cmd)
def __init__(self, *args, **kwargs):
super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
self.init_prompt_toolkit_cli()
self.init_term_title()
self.keep_running = True
self.debugger_history = InMemoryHistory()
def ask_exit(self):
self.keep_running = False
rl_next_input = None
def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
if display_banner is not DISPLAY_BANNER_DEPRECATED:
warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
self.keep_running = True
while self.keep_running:
print(self.separate_in, end='')
try:
code = self.prompt_for_code()
except EOFError:
if (not self.confirm_exit) \
or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
self.ask_exit()
else:
if code:
self.run_cell(code, store_history=True)
def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
# An extra layer of protection in case someone mashing Ctrl-C breaks
# out of our internal code.
if display_banner is not DISPLAY_BANNER_DEPRECATED:
warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
while True:
try:
self.interact()
break
except KeyboardInterrupt as e:
print("\n%s escaped interact()\n" % type(e).__name__)
finally:
# An interrupt during the eventloop will mess up the
# internal state of the prompt_toolkit library.
# Stopping the eventloop fixes this, see
# https://github.com/ipython/ipython/pull/9867
if hasattr(self, '_eventloop'):
self._eventloop.stop()
self.restore_term_title()
_inputhook = None
def inputhook(self, context):
if self._inputhook is not None:
self._inputhook(context)
active_eventloop = None
def enable_gui(self, gui=None):
if gui and (gui != 'inline') :
self.active_eventloop, self._inputhook =\
get_inputhook_name_and_func(gui)
else:
self.active_eventloop = self._inputhook = None
# For prompt_toolkit 3.0. We have to create an asyncio event loop with
# this inputhook.
if PTK3:
import asyncio
from prompt_toolkit.eventloop import new_eventloop_with_inputhook
if gui == 'asyncio':
# When we integrate the asyncio event loop, run the UI in the
# same event loop as the rest of the code. don't use an actual
# input hook. (Asyncio is not made for nesting event loops.)
self.pt_loop = asyncio.get_event_loop()
elif self._inputhook:
# If an inputhook was set, create a new asyncio event loop with
# this inputhook for the prompt.
self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
else:
# When there's no inputhook, run the prompt in a separate
# asyncio event loop.
self.pt_loop = asyncio.new_event_loop()
# Run !system commands directly, not through pipes, so terminal programs
# work correctly.
system = InteractiveShell.system_raw
def auto_rewrite_input(self, cmd):
"""Overridden from the parent class to use fancy rewriting prompt"""
if not self.show_rewritten_input:
return
tokens = self.prompts.rewrite_prompt_tokens()
if self.pt_app:
print_formatted_text(PygmentsTokens(tokens), end='',
style=self.pt_app.app.style)
print(cmd)
else:
prompt = ''.join(s for t, s in tokens)
print(prompt, cmd, sep='')
_prompts_before = None
def switch_doctest_mode(self, mode):
"""Switch prompts to classic for %doctest_mode"""
if mode:
self._prompts_before = self.prompts
self.prompts = ClassicPrompts(self)
elif self._prompts_before:
self.prompts = self._prompts_before
self._prompts_before = None
# self._update_layout()
InteractiveShellABC.register(TerminalInteractiveShell)
if __name__ == '__main__':
TerminalInteractiveShell.instance().interact()

View file

@ -0,0 +1,380 @@
#!/usr/bin/env python
# encoding: utf-8
"""
The :class:`~IPython.core.application.Application` object for the command
line :command:`ipython` program.
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
import logging
import os
import sys
import warnings
from traitlets.config.loader import Config
from traitlets.config.application import boolean_flag, catch_config_error
from IPython.core import release
from IPython.core import usage
from IPython.core.completer import IPCompleter
from IPython.core.crashhandler import CrashHandler
from IPython.core.formatters import PlainTextFormatter
from IPython.core.history import HistoryManager
from IPython.core.application import (
ProfileDir, BaseIPythonApplication, base_flags, base_aliases
)
from IPython.core.magics import (
ScriptMagics, LoggingMagics
)
from IPython.core.shellapp import (
InteractiveShellApp, shell_flags, shell_aliases
)
from IPython.extensions.storemagic import StoreMagics
from .interactiveshell import TerminalInteractiveShell
from IPython.paths import get_ipython_dir
from traitlets import (
Bool, List, default, observe, Type
)
#-----------------------------------------------------------------------------
# Globals, utilities and helpers
#-----------------------------------------------------------------------------
_examples = """
ipython --matplotlib # enable matplotlib integration
ipython --matplotlib=qt # enable matplotlib integration with qt4 backend
ipython --log-level=DEBUG # set logging to DEBUG
ipython --profile=foo # start with profile foo
ipython profile create foo # create profile foo w/ default config files
ipython help profile # show the help for the profile subcmd
ipython locate # print the path to the IPython directory
ipython locate profile foo # print the path to the directory for profile `foo`
"""
#-----------------------------------------------------------------------------
# Crash handler for this application
#-----------------------------------------------------------------------------
class IPAppCrashHandler(CrashHandler):
"""sys.excepthook for IPython itself, leaves a detailed report on disk."""
def __init__(self, app):
contact_name = release.author
contact_email = release.author_email
bug_tracker = 'https://github.com/ipython/ipython/issues'
super(IPAppCrashHandler,self).__init__(
app, contact_name, contact_email, bug_tracker
)
def make_report(self,traceback):
"""Return a string containing a crash report."""
sec_sep = self.section_sep
# Start with parent report
report = [super(IPAppCrashHandler, self).make_report(traceback)]
# Add interactive-specific info we may have
rpt_add = report.append
try:
rpt_add(sec_sep+"History of session input:")
for line in self.app.shell.user_ns['_ih']:
rpt_add(line)
rpt_add('\n*** Last line of input (may not be in above history):\n')
rpt_add(self.app.shell._last_input_line+'\n')
except:
pass
return ''.join(report)
#-----------------------------------------------------------------------------
# Aliases and Flags
#-----------------------------------------------------------------------------
flags = dict(base_flags)
flags.update(shell_flags)
frontend_flags = {}
addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
'Turn on auto editing of files with syntax errors.',
'Turn off auto editing of files with syntax errors.'
)
addflag('simple-prompt', 'TerminalInteractiveShell.simple_prompt',
"Force simple minimal prompt using `raw_input`",
"Use a rich interactive prompt with prompt_toolkit",
)
addflag('banner', 'TerminalIPythonApp.display_banner',
"Display a banner upon starting IPython.",
"Don't display a banner upon starting IPython."
)
addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
"""Set to confirm when you try to exit IPython with an EOF (Control-D
in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
you can force a direct exit without any confirmation.""",
"Don't prompt the user when exiting."
)
addflag('term-title', 'TerminalInteractiveShell.term_title',
"Enable auto setting the terminal title.",
"Disable auto setting the terminal title."
)
classic_config = Config()
classic_config.InteractiveShell.cache_size = 0
classic_config.PlainTextFormatter.pprint = False
classic_config.TerminalInteractiveShell.prompts_class='IPython.terminal.prompts.ClassicPrompts'
classic_config.InteractiveShell.separate_in = ''
classic_config.InteractiveShell.separate_out = ''
classic_config.InteractiveShell.separate_out2 = ''
classic_config.InteractiveShell.colors = 'NoColor'
classic_config.InteractiveShell.xmode = 'Plain'
frontend_flags['classic']=(
classic_config,
"Gives IPython a similar feel to the classic Python prompt."
)
# # log doesn't make so much sense this way anymore
# paa('--log','-l',
# action='store_true', dest='InteractiveShell.logstart',
# help="Start logging to the default log file (./ipython_log.py).")
#
# # quick is harder to implement
frontend_flags['quick']=(
{'TerminalIPythonApp' : {'quick' : True}},
"Enable quick startup with no config files."
)
frontend_flags['i'] = (
{'TerminalIPythonApp' : {'force_interact' : True}},
"""If running code from the command line, become interactive afterwards.
It is often useful to follow this with `--` to treat remaining flags as
script arguments.
"""
)
flags.update(frontend_flags)
aliases = dict(base_aliases)
aliases.update(shell_aliases)
#-----------------------------------------------------------------------------
# Main classes and functions
#-----------------------------------------------------------------------------
class LocateIPythonApp(BaseIPythonApplication):
description = """print the path to the IPython dir"""
subcommands = dict(
profile=('IPython.core.profileapp.ProfileLocate',
"print the path to an IPython profile directory",
),
)
def start(self):
if self.subapp is not None:
return self.subapp.start()
else:
print(self.ipython_dir)
class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
name = u'ipython'
description = usage.cl_usage
crash_handler_class = IPAppCrashHandler
examples = _examples
flags = flags
aliases = aliases
classes = List()
interactive_shell_class = Type(
klass=object, # use default_value otherwise which only allow subclasses.
default_value=TerminalInteractiveShell,
help="Class to use to instantiate the TerminalInteractiveShell object. Useful for custom Frontends"
).tag(config=True)
@default('classes')
def _classes_default(self):
"""This has to be in a method, for TerminalIPythonApp to be available."""
return [
InteractiveShellApp, # ShellApp comes before TerminalApp, because
self.__class__, # it will also affect subclasses (e.g. QtConsole)
TerminalInteractiveShell,
HistoryManager,
ProfileDir,
PlainTextFormatter,
IPCompleter,
ScriptMagics,
LoggingMagics,
StoreMagics,
]
deprecated_subcommands = dict(
qtconsole=('qtconsole.qtconsoleapp.JupyterQtConsoleApp',
"""DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter Qt Console."""
),
notebook=('notebook.notebookapp.NotebookApp',
"""DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter HTML Notebook Server."""
),
console=('jupyter_console.app.ZMQTerminalIPythonApp',
"""DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter terminal-based Console."""
),
nbconvert=('nbconvert.nbconvertapp.NbConvertApp',
"DEPRECATED, Will be removed in IPython 6.0 : Convert notebooks to/from other formats."
),
trust=('nbformat.sign.TrustNotebookApp',
"DEPRECATED, Will be removed in IPython 6.0 : Sign notebooks to trust their potentially unsafe contents at load."
),
kernelspec=('jupyter_client.kernelspecapp.KernelSpecApp',
"DEPRECATED, Will be removed in IPython 6.0 : Manage Jupyter kernel specifications."
),
)
subcommands = dict(
profile = ("IPython.core.profileapp.ProfileApp",
"Create and manage IPython profiles."
),
kernel = ("ipykernel.kernelapp.IPKernelApp",
"Start a kernel without an attached frontend."
),
locate=('IPython.terminal.ipapp.LocateIPythonApp',
LocateIPythonApp.description
),
history=('IPython.core.historyapp.HistoryApp',
"Manage the IPython history database."
),
)
deprecated_subcommands['install-nbextension'] = (
"notebook.nbextensions.InstallNBExtensionApp",
"DEPRECATED, Will be removed in IPython 6.0 : Install Jupyter notebook extension files"
)
subcommands.update(deprecated_subcommands)
# *do* autocreate requested profile, but don't create the config file.
auto_create=Bool(True)
# configurables
quick = Bool(False,
help="""Start IPython quickly by skipping the loading of config files."""
).tag(config=True)
@observe('quick')
def _quick_changed(self, change):
if change['new']:
self.load_config_file = lambda *a, **kw: None
display_banner = Bool(True,
help="Whether to display a banner upon starting IPython."
).tag(config=True)
# if there is code of files to run from the cmd line, don't interact
# unless the --i flag (App.force_interact) is true.
force_interact = Bool(False,
help="""If a command or file is given via the command-line,
e.g. 'ipython foo.py', start an interactive shell after executing the
file or command."""
).tag(config=True)
@observe('force_interact')
def _force_interact_changed(self, change):
if change['new']:
self.interact = True
@observe('file_to_run', 'code_to_run', 'module_to_run')
def _file_to_run_changed(self, change):
new = change['new']
if new:
self.something_to_run = True
if new and not self.force_interact:
self.interact = False
# internal, not-configurable
something_to_run=Bool(False)
def parse_command_line(self, argv=None):
"""override to allow old '-pylab' flag with deprecation warning"""
argv = sys.argv[1:] if argv is None else argv
if '-pylab' in argv:
# deprecated `-pylab` given,
# warn and transform into current syntax
argv = argv[:] # copy, don't clobber
idx = argv.index('-pylab')
warnings.warn("`-pylab` flag has been deprecated.\n"
" Use `--matplotlib <backend>` and import pylab manually.")
argv[idx] = '--pylab'
return super(TerminalIPythonApp, self).parse_command_line(argv)
@catch_config_error
def initialize(self, argv=None):
"""Do actions after construct, but before starting the app."""
super(TerminalIPythonApp, self).initialize(argv)
if self.subapp is not None:
# don't bother initializing further, starting subapp
return
# print self.extra_args
if self.extra_args and not self.something_to_run:
self.file_to_run = self.extra_args[0]
self.init_path()
# create the shell
self.init_shell()
# and draw the banner
self.init_banner()
# Now a variety of things that happen after the banner is printed.
self.init_gui_pylab()
self.init_extensions()
self.init_code()
def init_shell(self):
"""initialize the InteractiveShell instance"""
# Create an InteractiveShell instance.
# shell.display_banner should always be False for the terminal
# based app, because we call shell.show_banner() by hand below
# so the banner shows *before* all extension loading stuff.
self.shell = self.interactive_shell_class.instance(parent=self,
profile_dir=self.profile_dir,
ipython_dir=self.ipython_dir, user_ns=self.user_ns)
self.shell.configurables.append(self)
def init_banner(self):
"""optionally display the banner"""
if self.display_banner and self.interact:
self.shell.show_banner()
# Make sure there is a space below the banner.
if self.log_level <= logging.INFO: print()
def _pylab_changed(self, name, old, new):
"""Replace --pylab='inline' with --pylab='auto'"""
if new == 'inline':
warnings.warn("'inline' not available as pylab backend, "
"using 'auto' instead.")
self.pylab = 'auto'
def start(self):
if self.subapp is not None:
return self.subapp.start()
# perform any prexec steps:
if self.interact:
self.log.debug("Starting IPython's mainloop...")
self.shell.mainloop()
else:
self.log.debug("IPython not interactive...")
if not self.shell.last_execution_succeeded:
sys.exit(1)
def load_default_config(ipython_dir=None):
"""Load the default config file from the default ipython_dir.
This is useful for embedded shells.
"""
if ipython_dir is None:
ipython_dir = get_ipython_dir()
profile_dir = os.path.join(ipython_dir, 'profile_default')
app = TerminalIPythonApp()
app.config_file_paths.append(profile_dir)
app.load_config_file()
return app.config
launch_new_instance = TerminalIPythonApp.launch_instance
if __name__ == '__main__':
launch_new_instance()

View file

@ -0,0 +1,203 @@
"""Extra magics for terminal use."""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
from logging import error
import os
import sys
from IPython.core.error import TryNext, UsageError
from IPython.core.magic import Magics, magics_class, line_magic
from IPython.lib.clipboard import ClipboardEmpty
from IPython.utils.text import SList, strip_email_quotes
from IPython.utils import py3compat
def get_pasted_lines(sentinel, l_input=py3compat.input, quiet=False):
""" Yield pasted lines until the user enters the given sentinel value.
"""
if not quiet:
print("Pasting code; enter '%s' alone on the line to stop or use Ctrl-D." \
% sentinel)
prompt = ":"
else:
prompt = ""
while True:
try:
l = l_input(prompt)
if l == sentinel:
return
else:
yield l
except EOFError:
print('<EOF>')
return
@magics_class
class TerminalMagics(Magics):
def __init__(self, shell):
super(TerminalMagics, self).__init__(shell)
def store_or_execute(self, block, name):
""" Execute a block, or store it in a variable, per the user's request.
"""
if name:
# If storing it for further editing
self.shell.user_ns[name] = SList(block.splitlines())
print("Block assigned to '%s'" % name)
else:
b = self.preclean_input(block)
self.shell.user_ns['pasted_block'] = b
self.shell.using_paste_magics = True
try:
self.shell.run_cell(b)
finally:
self.shell.using_paste_magics = False
def preclean_input(self, block):
lines = block.splitlines()
while lines and not lines[0].strip():
lines = lines[1:]
return strip_email_quotes('\n'.join(lines))
def rerun_pasted(self, name='pasted_block'):
""" Rerun a previously pasted command.
"""
b = self.shell.user_ns.get(name)
# Sanity checks
if b is None:
raise UsageError('No previous pasted block available')
if not isinstance(b, str):
raise UsageError(
"Variable 'pasted_block' is not a string, can't execute")
print("Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)))
self.shell.run_cell(b)
@line_magic
def autoindent(self, parameter_s = ''):
"""Toggle autoindent on/off (deprecated)"""
self.shell.set_autoindent()
print("Automatic indentation is:",['OFF','ON'][self.shell.autoindent])
@line_magic
def cpaste(self, parameter_s=''):
"""Paste & execute a pre-formatted code block from clipboard.
You must terminate the block with '--' (two minus-signs) or Ctrl-D
alone on the line. You can also provide your own sentinel with '%paste
-s %%' ('%%' is the new sentinel for this operation).
The block is dedented prior to execution to enable execution of method
definitions. '>' and '+' characters at the beginning of a line are
ignored, to allow pasting directly from e-mails, diff files and
doctests (the '...' continuation prompt is also stripped). The
executed block is also assigned to variable named 'pasted_block' for
later editing with '%edit pasted_block'.
You can also pass a variable name as an argument, e.g. '%cpaste foo'.
This assigns the pasted block to variable 'foo' as string, without
dedenting or executing it (preceding >>> and + is still stripped)
'%cpaste -r' re-executes the block previously entered by cpaste.
'%cpaste -q' suppresses any additional output messages.
Do not be alarmed by garbled output on Windows (it's a readline bug).
Just press enter and type -- (and press enter again) and the block
will be what was just pasted.
IPython statements (magics, shell escapes) are not supported (yet).
See also
--------
paste: automatically pull code from clipboard.
Examples
--------
::
In [8]: %cpaste
Pasting code; enter '--' alone on the line to stop.
:>>> a = ["world!", "Hello"]
:>>> print " ".join(sorted(a))
:--
Hello world!
"""
opts, name = self.parse_options(parameter_s, 'rqs:', mode='string')
if 'r' in opts:
self.rerun_pasted()
return
quiet = ('q' in opts)
sentinel = opts.get('s', u'--')
block = '\n'.join(get_pasted_lines(sentinel, quiet=quiet))
self.store_or_execute(block, name)
@line_magic
def paste(self, parameter_s=''):
"""Paste & execute a pre-formatted code block from clipboard.
The text is pulled directly from the clipboard without user
intervention and printed back on the screen before execution (unless
the -q flag is given to force quiet mode).
The block is dedented prior to execution to enable execution of method
definitions. '>' and '+' characters at the beginning of a line are
ignored, to allow pasting directly from e-mails, diff files and
doctests (the '...' continuation prompt is also stripped). The
executed block is also assigned to variable named 'pasted_block' for
later editing with '%edit pasted_block'.
You can also pass a variable name as an argument, e.g. '%paste foo'.
This assigns the pasted block to variable 'foo' as string, without
executing it (preceding >>> and + is still stripped).
Options:
-r: re-executes the block previously entered by cpaste.
-q: quiet mode: do not echo the pasted text back to the terminal.
IPython statements (magics, shell escapes) are not supported (yet).
See also
--------
cpaste: manually paste code into terminal until you mark its end.
"""
opts, name = self.parse_options(parameter_s, 'rq', mode='string')
if 'r' in opts:
self.rerun_pasted()
return
try:
block = self.shell.hooks.clipboard_get()
except TryNext as clipboard_exc:
message = getattr(clipboard_exc, 'args')
if message:
error(message[0])
else:
error('Could not get text from the clipboard.')
return
except ClipboardEmpty:
raise UsageError("The clipboard appears to be empty")
# By default, echo back to terminal unless quiet mode is requested
if 'q' not in opts:
write = self.shell.write
write(self.shell.pycolorize(block))
if not block.endswith('\n'):
write('\n')
write("## -- End pasted text --\n")
self.store_or_execute(block, name)
# Class-level: add a '%cls' magic only on Windows
if sys.platform == 'win32':
@line_magic
def cls(self, s):
"""Clear screen.
"""
os.system("cls")

View file

@ -0,0 +1,108 @@
"""Terminal input and output prompts."""
from pygments.token import Token
import sys
from IPython.core.displayhook import DisplayHook
from prompt_toolkit.formatted_text import fragment_list_width, PygmentsTokens
from prompt_toolkit.shortcuts import print_formatted_text
from prompt_toolkit.enums import EditingMode
class Prompts(object):
def __init__(self, shell):
self.shell = shell
def vi_mode(self):
if (getattr(self.shell.pt_app, 'editing_mode', None) == EditingMode.VI
and self.shell.prompt_includes_vi_mode):
mode = str(self.shell.pt_app.app.vi_state.input_mode)
if mode.startswith('InputMode.'):
mode = mode[10:13].lower()
elif mode.startswith('vi-'):
mode = mode[3:6]
return '['+mode+'] '
return ''
def in_prompt_tokens(self):
return [
(Token.Prompt, self.vi_mode() ),
(Token.Prompt, 'In ['),
(Token.PromptNum, str(self.shell.execution_count)),
(Token.Prompt, ']: '),
]
def _width(self):
return fragment_list_width(self.in_prompt_tokens())
def continuation_prompt_tokens(self, width=None):
if width is None:
width = self._width()
return [
(Token.Prompt, (' ' * (width - 5)) + '...: '),
]
def rewrite_prompt_tokens(self):
width = self._width()
return [
(Token.Prompt, ('-' * (width - 2)) + '> '),
]
def out_prompt_tokens(self):
return [
(Token.OutPrompt, 'Out['),
(Token.OutPromptNum, str(self.shell.execution_count)),
(Token.OutPrompt, ']: '),
]
class ClassicPrompts(Prompts):
def in_prompt_tokens(self):
return [
(Token.Prompt, '>>> '),
]
def continuation_prompt_tokens(self, width=None):
return [
(Token.Prompt, '... ')
]
def rewrite_prompt_tokens(self):
return []
def out_prompt_tokens(self):
return []
class RichPromptDisplayHook(DisplayHook):
"""Subclass of base display hook using coloured prompt"""
def write_output_prompt(self):
sys.stdout.write(self.shell.separate_out)
# If we're not displaying a prompt, it effectively ends with a newline,
# because the output will be left-aligned.
self.prompt_end_newline = True
if self.do_full_cache:
tokens = self.shell.prompts.out_prompt_tokens()
prompt_txt = ''.join(s for t, s in tokens)
if prompt_txt and not prompt_txt.endswith('\n'):
# Ask for a newline before multiline output
self.prompt_end_newline = False
if self.shell.pt_app:
print_formatted_text(PygmentsTokens(tokens),
style=self.shell.pt_app.app.style, end='',
)
else:
sys.stdout.write(prompt_txt)
def write_format_data(self, format_dict, md_dict=None) -> None:
if self.shell.mime_renderers:
for mime, handler in self.shell.mime_renderers.items():
if mime in format_dict:
handler(format_dict[mime], None)
return
super().write_format_data(format_dict, md_dict)

View file

@ -0,0 +1,50 @@
import importlib
import os
aliases = {
'qt4': 'qt',
'gtk2': 'gtk',
}
backends = [
'qt', 'qt4', 'qt5',
'gtk', 'gtk2', 'gtk3',
'tk',
'wx',
'pyglet', 'glut',
'osx',
'asyncio'
]
registered = {}
def register(name, inputhook):
"""Register the function *inputhook* as an event loop integration."""
registered[name] = inputhook
class UnknownBackend(KeyError):
def __init__(self, name):
self.name = name
def __str__(self):
return ("No event loop integration for {!r}. "
"Supported event loops are: {}").format(self.name,
', '.join(backends + sorted(registered)))
def get_inputhook_name_and_func(gui):
if gui in registered:
return gui, registered[gui]
if gui not in backends:
raise UnknownBackend(gui)
if gui in aliases:
return get_inputhook_name_and_func(aliases[gui])
gui_mod = gui
if gui == 'qt5':
os.environ['QT_API'] = 'pyqt5'
gui_mod = 'qt'
mod = importlib.import_module('IPython.terminal.pt_inputhooks.'+gui_mod)
return gui, mod.inputhook

View file

@ -0,0 +1,64 @@
"""
Inputhook for running the original asyncio event loop while we're waiting for
input.
By default, in IPython, we run the prompt with a different asyncio event loop,
because otherwise we risk that people are freezing the prompt by scheduling bad
coroutines. E.g., a coroutine that does a while/true and never yield back
control to the loop. We can't cancel that.
However, sometimes we want the asyncio loop to keep running while waiting for
a prompt.
The following example will print the numbers from 1 to 10 above the prompt,
while we are waiting for input. (This works also because we use
prompt_toolkit`s `patch_stdout`)::
In [1]: import asyncio
In [2]: %gui asyncio
In [3]: async def f():
...: for i in range(10):
...: await asyncio.sleep(1)
...: print(i)
In [4]: asyncio.ensure_future(f())
"""
import asyncio
from prompt_toolkit import __version__ as ptk_version
PTK3 = ptk_version.startswith('3.')
# Keep reference to the original asyncio loop, because getting the event loop
# within the input hook would return the other loop.
loop = asyncio.get_event_loop()
def inputhook(context):
"""
Inputhook for asyncio event loop integration.
"""
# For prompt_toolkit 3.0, this input hook literally doesn't do anything.
# The event loop integration here is implemented in `interactiveshell.py`
# by running the prompt itself in the current asyncio loop. The main reason
# for this is that nesting asyncio event loops is unreliable.
if PTK3:
return
# For prompt_toolkit 2.0, we can run the current asyncio event loop,
# because prompt_toolkit 2.0 uses a different event loop internally.
def stop():
loop.stop()
fileno = context.fileno()
loop.add_reader(fileno, stop)
try:
loop.run_forever()
finally:
loop.remove_reader(fileno)

View file

@ -0,0 +1,140 @@
"""GLUT Input hook for interactive use with prompt_toolkit
"""
# GLUT is quite an old library and it is difficult to ensure proper
# integration within IPython since original GLUT does not allow to handle
# events one by one. Instead, it requires for the mainloop to be entered
# and never returned (there is not even a function to exit he
# mainloop). Fortunately, there are alternatives such as freeglut
# (available for linux and windows) and the OSX implementation gives
# access to a glutCheckLoop() function that blocks itself until a new
# event is received. This means we have to setup the idle callback to
# ensure we got at least one event that will unblock the function.
#
# Furthermore, it is not possible to install these handlers without a window
# being first created. We choose to make this window invisible. This means that
# display mode options are set at this level and user won't be able to change
# them later without modifying the code. This should probably be made available
# via IPython options system.
import sys
import time
import signal
import OpenGL.GLUT as glut
import OpenGL.platform as platform
from timeit import default_timer as clock
# Frame per second : 60
# Should probably be an IPython option
glut_fps = 60
# Display mode : double buffeed + rgba + depth
# Should probably be an IPython option
glut_display_mode = (glut.GLUT_DOUBLE |
glut.GLUT_RGBA |
glut.GLUT_DEPTH)
glutMainLoopEvent = None
if sys.platform == 'darwin':
try:
glutCheckLoop = platform.createBaseFunction(
'glutCheckLoop', dll=platform.GLUT, resultType=None,
argTypes=[],
doc='glutCheckLoop( ) -> None',
argNames=(),
)
except AttributeError:
raise RuntimeError(
'''Your glut implementation does not allow interactive sessions'''
'''Consider installing freeglut.''')
glutMainLoopEvent = glutCheckLoop
elif glut.HAVE_FREEGLUT:
glutMainLoopEvent = glut.glutMainLoopEvent
else:
raise RuntimeError(
'''Your glut implementation does not allow interactive sessions. '''
'''Consider installing freeglut.''')
def glut_display():
# Dummy display function
pass
def glut_idle():
# Dummy idle function
pass
def glut_close():
# Close function only hides the current window
glut.glutHideWindow()
glutMainLoopEvent()
def glut_int_handler(signum, frame):
# Catch sigint and print the defaultipyt message
signal.signal(signal.SIGINT, signal.default_int_handler)
print('\nKeyboardInterrupt')
# Need to reprint the prompt at this stage
# Initialisation code
glut.glutInit( sys.argv )
glut.glutInitDisplayMode( glut_display_mode )
# This is specific to freeglut
if bool(glut.glutSetOption):
glut.glutSetOption( glut.GLUT_ACTION_ON_WINDOW_CLOSE,
glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS )
glut.glutCreateWindow( b'ipython' )
glut.glutReshapeWindow( 1, 1 )
glut.glutHideWindow( )
glut.glutWMCloseFunc( glut_close )
glut.glutDisplayFunc( glut_display )
glut.glutIdleFunc( glut_idle )
def inputhook(context):
"""Run the pyglet event loop by processing pending events only.
This keeps processing pending events until stdin is ready. After
processing all pending events, a call to time.sleep is inserted. This is
needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
though for best performance.
"""
# We need to protect against a user pressing Control-C when IPython is
# idle and this is running. We trap KeyboardInterrupt and pass.
signal.signal(signal.SIGINT, glut_int_handler)
try:
t = clock()
# Make sure the default window is set after a window has been closed
if glut.glutGetWindow() == 0:
glut.glutSetWindow( 1 )
glutMainLoopEvent()
return 0
while not context.input_is_ready():
glutMainLoopEvent()
# We need to sleep at this point to keep the idle CPU load
# low. However, if sleep to long, GUI response is poor. As
# a compromise, we watch how often GUI events are being processed
# and switch between a short and long sleep time. Here are some
# stats useful in helping to tune this.
# time CPU load
# 0.001 13%
# 0.005 3%
# 0.01 1.5%
# 0.05 0.5%
used_time = clock() - t
if used_time > 10.0:
# print 'Sleep for 1 s' # dbg
time.sleep(1.0)
elif used_time > 0.1:
# Few GUI events coming in, so we can sleep longer
# print 'Sleep for 0.05 s' # dbg
time.sleep(0.05)
else:
# Many GUI events coming in, so sleep only very little
time.sleep(0.001)
except KeyboardInterrupt:
pass

View file

@ -0,0 +1,58 @@
# Code borrowed from python-prompt-toolkit examples
# https://github.com/jonathanslenders/python-prompt-toolkit/blob/77cdcfbc7f4b4c34a9d2f9a34d422d7152f16209/examples/inputhook.py
# Copyright (c) 2014, Jonathan Slenders
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice, this
# list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
#
# * Neither the name of the {organization} nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
PyGTK input hook for prompt_toolkit.
Listens on the pipe prompt_toolkit sets up for a notification that it should
return control to the terminal event loop.
"""
import gtk, gobject
# Enable threading in GTK. (Otherwise, GTK will keep the GIL.)
gtk.gdk.threads_init()
def inputhook(context):
"""
When the eventloop of prompt-toolkit is idle, call this inputhook.
This will run the GTK main loop until the file descriptor
`context.fileno()` becomes ready.
:param context: An `InputHookContext` instance.
"""
def _main_quit(*a, **kw):
gtk.main_quit()
return False
gobject.io_add_watch(context.fileno(), gobject.IO_IN, _main_quit)
gtk.main()

View file

@ -0,0 +1,12 @@
"""prompt_toolkit input hook for GTK 3
"""
from gi.repository import Gtk, GLib
def _main_quit(*args, **kwargs):
Gtk.main_quit()
return False
def inputhook(context):
GLib.io_add_watch(context.fileno(), GLib.PRIORITY_DEFAULT, GLib.IO_IN, _main_quit)
Gtk.main()

View file

@ -0,0 +1,137 @@
"""Inputhook for OS X
Calls NSApp / CoreFoundation APIs via ctypes.
"""
# obj-c boilerplate from appnope, used under BSD 2-clause
import ctypes
import ctypes.util
from threading import Event
objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('objc'))
void_p = ctypes.c_void_p
objc.objc_getClass.restype = void_p
objc.sel_registerName.restype = void_p
objc.objc_msgSend.restype = void_p
objc.objc_msgSend.argtypes = [void_p, void_p]
msg = objc.objc_msgSend
def _utf8(s):
"""ensure utf8 bytes"""
if not isinstance(s, bytes):
s = s.encode('utf8')
return s
def n(name):
"""create a selector name (for ObjC methods)"""
return objc.sel_registerName(_utf8(name))
def C(classname):
"""get an ObjC Class by name"""
return objc.objc_getClass(_utf8(classname))
# end obj-c boilerplate from appnope
# CoreFoundation C-API calls we will use:
CoreFoundation = ctypes.cdll.LoadLibrary(ctypes.util.find_library('CoreFoundation'))
CFFileDescriptorCreate = CoreFoundation.CFFileDescriptorCreate
CFFileDescriptorCreate.restype = void_p
CFFileDescriptorCreate.argtypes = [void_p, ctypes.c_int, ctypes.c_bool, void_p]
CFFileDescriptorGetNativeDescriptor = CoreFoundation.CFFileDescriptorGetNativeDescriptor
CFFileDescriptorGetNativeDescriptor.restype = ctypes.c_int
CFFileDescriptorGetNativeDescriptor.argtypes = [void_p]
CFFileDescriptorEnableCallBacks = CoreFoundation.CFFileDescriptorEnableCallBacks
CFFileDescriptorEnableCallBacks.restype = None
CFFileDescriptorEnableCallBacks.argtypes = [void_p, ctypes.c_ulong]
CFFileDescriptorCreateRunLoopSource = CoreFoundation.CFFileDescriptorCreateRunLoopSource
CFFileDescriptorCreateRunLoopSource.restype = void_p
CFFileDescriptorCreateRunLoopSource.argtypes = [void_p, void_p, void_p]
CFRunLoopGetCurrent = CoreFoundation.CFRunLoopGetCurrent
CFRunLoopGetCurrent.restype = void_p
CFRunLoopAddSource = CoreFoundation.CFRunLoopAddSource
CFRunLoopAddSource.restype = None
CFRunLoopAddSource.argtypes = [void_p, void_p, void_p]
CFRelease = CoreFoundation.CFRelease
CFRelease.restype = None
CFRelease.argtypes = [void_p]
CFFileDescriptorInvalidate = CoreFoundation.CFFileDescriptorInvalidate
CFFileDescriptorInvalidate.restype = None
CFFileDescriptorInvalidate.argtypes = [void_p]
# From CFFileDescriptor.h
kCFFileDescriptorReadCallBack = 1
kCFRunLoopCommonModes = void_p.in_dll(CoreFoundation, 'kCFRunLoopCommonModes')
def _NSApp():
"""Return the global NSApplication instance (NSApp)"""
return msg(C('NSApplication'), n('sharedApplication'))
def _wake(NSApp):
"""Wake the Application"""
event = msg(C('NSEvent'),
n('otherEventWithType:location:modifierFlags:'
'timestamp:windowNumber:context:subtype:data1:data2:'),
15, # Type
0, # location
0, # flags
0, # timestamp
0, # window
None, # context
0, # subtype
0, # data1
0, # data2
)
msg(NSApp, n('postEvent:atStart:'), void_p(event), True)
_triggered = Event()
def _input_callback(fdref, flags, info):
"""Callback to fire when there's input to be read"""
_triggered.set()
CFFileDescriptorInvalidate(fdref)
CFRelease(fdref)
NSApp = _NSApp()
msg(NSApp, n('stop:'), NSApp)
_wake(NSApp)
_c_callback_func_type = ctypes.CFUNCTYPE(None, void_p, void_p, void_p)
_c_input_callback = _c_callback_func_type(_input_callback)
def _stop_on_read(fd):
"""Register callback to stop eventloop when there's data on fd"""
_triggered.clear()
fdref = CFFileDescriptorCreate(None, fd, False, _c_input_callback, None)
CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack)
source = CFFileDescriptorCreateRunLoopSource(None, fdref, 0)
loop = CFRunLoopGetCurrent()
CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes)
CFRelease(source)
def inputhook(context):
"""Inputhook for Cocoa (NSApp)"""
NSApp = _NSApp()
_stop_on_read(context.fileno())
msg(NSApp, n('run'))
if not _triggered.is_set():
# app closed without firing callback,
# probably due to last window being closed.
# Run the loop manually in this case,
# since there may be events still to process (#9734)
CoreFoundation.CFRunLoopRun()

View file

@ -0,0 +1,66 @@
"""Enable pyglet to be used interactively with prompt_toolkit
"""
import sys
import time
from timeit import default_timer as clock
import pyglet
# On linux only, window.flip() has a bug that causes an AttributeError on
# window close. For details, see:
# http://groups.google.com/group/pyglet-users/browse_thread/thread/47c1aab9aa4a3d23/c22f9e819826799e?#c22f9e819826799e
if sys.platform.startswith('linux'):
def flip(window):
try:
window.flip()
except AttributeError:
pass
else:
def flip(window):
window.flip()
def inputhook(context):
"""Run the pyglet event loop by processing pending events only.
This keeps processing pending events until stdin is ready. After
processing all pending events, a call to time.sleep is inserted. This is
needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
though for best performance.
"""
# We need to protect against a user pressing Control-C when IPython is
# idle and this is running. We trap KeyboardInterrupt and pass.
try:
t = clock()
while not context.input_is_ready():
pyglet.clock.tick()
for window in pyglet.app.windows:
window.switch_to()
window.dispatch_events()
window.dispatch_event('on_draw')
flip(window)
# We need to sleep at this point to keep the idle CPU load
# low. However, if sleep to long, GUI response is poor. As
# a compromise, we watch how often GUI events are being processed
# and switch between a short and long sleep time. Here are some
# stats useful in helping to tune this.
# time CPU load
# 0.001 13%
# 0.005 3%
# 0.01 1.5%
# 0.05 0.5%
used_time = clock() - t
if used_time > 10.0:
# print 'Sleep for 1 s' # dbg
time.sleep(1.0)
elif used_time > 0.1:
# Few GUI events coming in, so we can sleep longer
# print 'Sleep for 0.05 s' # dbg
time.sleep(0.05)
else:
# Many GUI events coming in, so sleep only very little
time.sleep(0.001)
except KeyboardInterrupt:
pass

View file

@ -0,0 +1,54 @@
import sys
import os
from IPython.external.qt_for_kernel import QtCore, QtGui
# If we create a QApplication, keep a reference to it so that it doesn't get
# garbage collected.
_appref = None
_already_warned = False
def inputhook(context):
global _appref
app = QtCore.QCoreApplication.instance()
if not app:
if sys.platform == 'linux':
if not os.environ.get('DISPLAY') \
and not os.environ.get('WAYLAND_DISPLAY'):
import warnings
global _already_warned
if not _already_warned:
_already_warned = True
warnings.warn(
'The DISPLAY or WAYLAND_DISPLAY environment variable is '
'not set or empty and Qt5 requires this environment '
'variable. Deactivate Qt5 code.'
)
return
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
_appref = app = QtGui.QApplication([" "])
event_loop = QtCore.QEventLoop(app)
if sys.platform == 'win32':
# The QSocketNotifier method doesn't appear to work on Windows.
# Use polling instead.
timer = QtCore.QTimer()
timer.timeout.connect(event_loop.quit)
while not context.input_is_ready():
timer.start(50) # 50 ms
event_loop.exec_()
timer.stop()
else:
# On POSIX platforms, we can use a file descriptor to quit the event
# loop when there is input ready to read.
notifier = QtCore.QSocketNotifier(context.fileno(),
QtCore.QSocketNotifier.Read)
try:
# connect the callback we care about before we turn it on
notifier.activated.connect(lambda: event_loop.exit())
notifier.setEnabled(True)
# only start the event loop we are not already flipped
if not context.input_is_ready():
event_loop.exec_()
finally:
notifier.setEnabled(False)

View file

@ -0,0 +1,90 @@
# Code borrowed from ptpython
# https://github.com/jonathanslenders/ptpython/blob/86b71a89626114b18898a0af463978bdb32eeb70/ptpython/eventloop.py
# Copyright (c) 2015, Jonathan Slenders
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice, this
# list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
#
# * Neither the name of the {organization} nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
Wrapper around the eventloop that gives some time to the Tkinter GUI to process
events when it's loaded and while we are waiting for input at the REPL. This
way we don't block the UI of for instance ``turtle`` and other Tk libraries.
(Normally Tkinter registers it's callbacks in ``PyOS_InputHook`` to integrate
in readline. ``prompt-toolkit`` doesn't understand that input hook, but this
will fix it for Tk.)
"""
import time
import _tkinter
import tkinter
def inputhook(inputhook_context):
"""
Inputhook for Tk.
Run the Tk eventloop until prompt-toolkit needs to process the next input.
"""
# Get the current TK application.
root = tkinter._default_root
def wait_using_filehandler():
"""
Run the TK eventloop until the file handler that we got from the
inputhook becomes readable.
"""
# Add a handler that sets the stop flag when `prompt-toolkit` has input
# to process.
stop = [False]
def done(*a):
stop[0] = True
root.createfilehandler(inputhook_context.fileno(), _tkinter.READABLE, done)
# Run the TK event loop as long as we don't receive input.
while root.dooneevent(_tkinter.ALL_EVENTS):
if stop[0]:
break
root.deletefilehandler(inputhook_context.fileno())
def wait_using_polling():
"""
Windows TK doesn't support 'createfilehandler'.
So, run the TK eventloop and poll until input is ready.
"""
while not inputhook_context.input_is_ready():
while root.dooneevent(_tkinter.ALL_EVENTS | _tkinter.DONT_WAIT):
pass
# Sleep to make the CPU idle, but not too long, so that the UI
# stays responsive.
time.sleep(.01)
if root is not None:
if hasattr(root, 'createfilehandler'):
wait_using_filehandler()
else:
wait_using_polling()

View file

@ -0,0 +1,219 @@
"""Enable wxPython to be used interactively in prompt_toolkit
"""
import sys
import signal
import time
from timeit import default_timer as clock
import wx
def ignore_keyboardinterrupts(func):
"""Decorator which causes KeyboardInterrupt exceptions to be ignored during
execution of the decorated function.
This is used by the inputhook functions to handle the event where the user
presses CTRL+C while IPython is idle, and the inputhook loop is running. In
this case, we want to ignore interrupts.
"""
def wrapper(*args, **kwargs):
try:
func(*args, **kwargs)
except KeyboardInterrupt:
pass
return wrapper
@ignore_keyboardinterrupts
def inputhook_wx1(context):
"""Run the wx event loop by processing pending events only.
This approach seems to work, but its performance is not great as it
relies on having PyOS_InputHook called regularly.
"""
app = wx.GetApp()
if app is not None:
assert wx.Thread_IsMain()
# Make a temporary event loop and process system events until
# there are no more waiting, then allow idle events (which
# will also deal with pending or posted wx events.)
evtloop = wx.EventLoop()
ea = wx.EventLoopActivator(evtloop)
while evtloop.Pending():
evtloop.Dispatch()
app.ProcessIdle()
del ea
return 0
class EventLoopTimer(wx.Timer):
def __init__(self, func):
self.func = func
wx.Timer.__init__(self)
def Notify(self):
self.func()
class EventLoopRunner(object):
def Run(self, time, input_is_ready):
self.input_is_ready = input_is_ready
self.evtloop = wx.EventLoop()
self.timer = EventLoopTimer(self.check_stdin)
self.timer.Start(time)
self.evtloop.Run()
def check_stdin(self):
if self.input_is_ready():
self.timer.Stop()
self.evtloop.Exit()
@ignore_keyboardinterrupts
def inputhook_wx2(context):
"""Run the wx event loop, polling for stdin.
This version runs the wx eventloop for an undetermined amount of time,
during which it periodically checks to see if anything is ready on
stdin. If anything is ready on stdin, the event loop exits.
The argument to elr.Run controls how often the event loop looks at stdin.
This determines the responsiveness at the keyboard. A setting of 1000
enables a user to type at most 1 char per second. I have found that a
setting of 10 gives good keyboard response. We can shorten it further,
but eventually performance would suffer from calling select/kbhit too
often.
"""
app = wx.GetApp()
if app is not None:
assert wx.Thread_IsMain()
elr = EventLoopRunner()
# As this time is made shorter, keyboard response improves, but idle
# CPU load goes up. 10 ms seems like a good compromise.
elr.Run(time=10, # CHANGE time here to control polling interval
input_is_ready=context.input_is_ready)
return 0
@ignore_keyboardinterrupts
def inputhook_wx3(context):
"""Run the wx event loop by processing pending events only.
This is like inputhook_wx1, but it keeps processing pending events
until stdin is ready. After processing all pending events, a call to
time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%.
This sleep time should be tuned though for best performance.
"""
app = wx.GetApp()
if app is not None:
assert wx.Thread_IsMain()
# The import of wx on Linux sets the handler for signal.SIGINT
# to 0. This is a bug in wx or gtk. We fix by just setting it
# back to the Python default.
if not callable(signal.getsignal(signal.SIGINT)):
signal.signal(signal.SIGINT, signal.default_int_handler)
evtloop = wx.EventLoop()
ea = wx.EventLoopActivator(evtloop)
t = clock()
while not context.input_is_ready():
while evtloop.Pending():
t = clock()
evtloop.Dispatch()
app.ProcessIdle()
# We need to sleep at this point to keep the idle CPU load
# low. However, if sleep to long, GUI response is poor. As
# a compromise, we watch how often GUI events are being processed
# and switch between a short and long sleep time. Here are some
# stats useful in helping to tune this.
# time CPU load
# 0.001 13%
# 0.005 3%
# 0.01 1.5%
# 0.05 0.5%
used_time = clock() - t
if used_time > 10.0:
# print 'Sleep for 1 s' # dbg
time.sleep(1.0)
elif used_time > 0.1:
# Few GUI events coming in, so we can sleep longer
# print 'Sleep for 0.05 s' # dbg
time.sleep(0.05)
else:
# Many GUI events coming in, so sleep only very little
time.sleep(0.001)
del ea
return 0
@ignore_keyboardinterrupts
def inputhook_wxphoenix(context):
"""Run the wx event loop until the user provides more input.
This input hook is suitable for use with wxPython >= 4 (a.k.a. Phoenix).
It uses the same approach to that used in
ipykernel.eventloops.loop_wx. The wx.MainLoop is executed, and a wx.Timer
is used to periodically poll the context for input. As soon as input is
ready, the wx.MainLoop is stopped.
"""
app = wx.GetApp()
if app is None:
return
if context.input_is_ready():
return
assert wx.IsMainThread()
# Wx uses milliseconds
poll_interval = 100
# Use a wx.Timer to periodically check whether input is ready - as soon as
# it is, we exit the main loop
timer = wx.Timer()
def poll(ev):
if context.input_is_ready():
timer.Stop()
app.ExitMainLoop()
timer.Start(poll_interval)
timer.Bind(wx.EVT_TIMER, poll)
# The import of wx on Linux sets the handler for signal.SIGINT to 0. This
# is a bug in wx or gtk. We fix by just setting it back to the Python
# default.
if not callable(signal.getsignal(signal.SIGINT)):
signal.signal(signal.SIGINT, signal.default_int_handler)
# The SetExitOnFrameDelete call allows us to run the wx mainloop without
# having a frame open.
app.SetExitOnFrameDelete(False)
app.MainLoop()
# Get the major wx version number to figure out what input hook we should use.
major_version = 3
try:
major_version = int(wx.__version__[0])
except Exception:
pass
# Use the phoenix hook on all platforms for wxpython >= 4
if major_version >= 4:
inputhook = inputhook_wxphoenix
# On OSX, evtloop.Pending() always returns True, regardless of there being
# any events pending. As such we can't use implementations 1 or 3 of the
# inputhook as those depend on a pending/dispatch loop.
elif sys.platform == 'darwin':
inputhook = inputhook_wx2
else:
inputhook = inputhook_wx3

View file

@ -0,0 +1,8 @@
raise DeprecationWarning("""DEPRECATED:
After Popular request and decision from the BDFL:
`IPython.terminal.ptshell` has been moved back to `IPython.terminal.interactiveshell`
during the beta cycle (after IPython 5.0.beta3) Sorry about that.
This file will be removed in 5.0 rc or final.
""")

View file

@ -0,0 +1,192 @@
"""prompt-toolkit utilities
Everything in this module is a private API,
not to be used outside IPython.
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
import unicodedata
from wcwidth import wcwidth
from IPython.core.completer import (
provisionalcompleter, cursor_to_position,
_deduplicate_completions)
from prompt_toolkit.completion import Completer, Completion
from prompt_toolkit.lexers import Lexer
from prompt_toolkit.lexers import PygmentsLexer
from prompt_toolkit.patch_stdout import patch_stdout
import pygments.lexers as pygments_lexers
import os
_completion_sentinel = object()
def _elide_point(string:str, *, min_elide=30)->str:
"""
If a string is long enough, and has at least 3 dots,
replace the middle part with ellipses.
If a string naming a file is long enough, and has at least 3 slashes,
replace the middle part with ellipses.
If three consecutive dots, or two consecutive dots are encountered these are
replaced by the equivalents HORIZONTAL ELLIPSIS or TWO DOT LEADER unicode
equivalents
"""
string = string.replace('...','\N{HORIZONTAL ELLIPSIS}')
string = string.replace('..','\N{TWO DOT LEADER}')
if len(string) < min_elide:
return string
object_parts = string.split('.')
file_parts = string.split(os.sep)
if file_parts[-1] == '':
file_parts.pop()
if len(object_parts) > 3:
return '{}.{}\N{HORIZONTAL ELLIPSIS}{}.{}'.format(object_parts[0], object_parts[1][0], object_parts[-2][-1], object_parts[-1])
elif len(file_parts) > 3:
return ('{}' + os.sep + '{}\N{HORIZONTAL ELLIPSIS}{}' + os.sep + '{}').format(file_parts[0], file_parts[1][0], file_parts[-2][-1], file_parts[-1])
return string
def _elide_typed(string:str, typed:str, *, min_elide:int=30)->str:
"""
Elide the middle of a long string if the beginning has already been typed.
"""
if len(string) < min_elide:
return string
cut_how_much = len(typed)-3
if cut_how_much < 7:
return string
if string.startswith(typed) and len(string)> len(typed):
return f"{string[:3]}\N{HORIZONTAL ELLIPSIS}{string[cut_how_much:]}"
return string
def _elide(string:str, typed:str, min_elide=30)->str:
return _elide_typed(
_elide_point(string, min_elide=min_elide),
typed, min_elide=min_elide)
def _adjust_completion_text_based_on_context(text, body, offset):
if text.endswith('=') and len(body) > offset and body[offset] == '=':
return text[:-1]
else:
return text
class IPythonPTCompleter(Completer):
"""Adaptor to provide IPython completions to prompt_toolkit"""
def __init__(self, ipy_completer=None, shell=None):
if shell is None and ipy_completer is None:
raise TypeError("Please pass shell=an InteractiveShell instance.")
self._ipy_completer = ipy_completer
self.shell = shell
@property
def ipy_completer(self):
if self._ipy_completer:
return self._ipy_completer
else:
return self.shell.Completer
def get_completions(self, document, complete_event):
if not document.current_line.strip():
return
# Some bits of our completion system may print stuff (e.g. if a module
# is imported). This context manager ensures that doesn't interfere with
# the prompt.
with patch_stdout(), provisionalcompleter():
body = document.text
cursor_row = document.cursor_position_row
cursor_col = document.cursor_position_col
cursor_position = document.cursor_position
offset = cursor_to_position(body, cursor_row, cursor_col)
try:
yield from self._get_completions(body, offset, cursor_position, self.ipy_completer)
except Exception as e:
from traceback import print_tb
print_tb(e)
@staticmethod
def _get_completions(body, offset, cursor_position, ipyc):
"""
Private equivalent of get_completions() use only for unit_testing.
"""
debug = getattr(ipyc, 'debug', False)
completions = _deduplicate_completions(
body, ipyc.completions(body, offset))
for c in completions:
if not c.text:
# Guard against completion machinery giving us an empty string.
continue
text = unicodedata.normalize('NFC', c.text)
# When the first character of the completion has a zero length,
# then it's probably a decomposed unicode character. E.g. caused by
# the "\dot" completion. Try to compose again with the previous
# character.
if wcwidth(text[0]) == 0:
if cursor_position + c.start > 0:
char_before = body[c.start - 1]
fixed_text = unicodedata.normalize(
'NFC', char_before + text)
# Yield the modified completion instead, if this worked.
if wcwidth(text[0:1]) == 1:
yield Completion(fixed_text, start_position=c.start - offset - 1)
continue
# TODO: Use Jedi to determine meta_text
# (Jedi currently has a bug that results in incorrect information.)
# meta_text = ''
# yield Completion(m, start_position=start_pos,
# display_meta=meta_text)
display_text = c.text
adjusted_text = _adjust_completion_text_based_on_context(c.text, body, offset)
if c.type == 'function':
yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text+'()', body[c.start:c.end]), display_meta=c.type+c.signature)
else:
yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text, body[c.start:c.end]), display_meta=c.type)
class IPythonPTLexer(Lexer):
"""
Wrapper around PythonLexer and BashLexer.
"""
def __init__(self):
l = pygments_lexers
self.python_lexer = PygmentsLexer(l.Python3Lexer)
self.shell_lexer = PygmentsLexer(l.BashLexer)
self.magic_lexers = {
'HTML': PygmentsLexer(l.HtmlLexer),
'html': PygmentsLexer(l.HtmlLexer),
'javascript': PygmentsLexer(l.JavascriptLexer),
'js': PygmentsLexer(l.JavascriptLexer),
'perl': PygmentsLexer(l.PerlLexer),
'ruby': PygmentsLexer(l.RubyLexer),
'latex': PygmentsLexer(l.TexLexer),
}
def lex_document(self, document):
text = document.text.lstrip()
lexer = self.python_lexer
if text.startswith('!') or text.startswith('%%bash'):
lexer = self.shell_lexer
elif text.startswith('%%'):
for magic, l in self.magic_lexers.items():
if text.startswith('%%' + magic):
lexer = l
break
return lexer.lex_document(document)

View file

@ -0,0 +1,276 @@
"""
Module to define and register Terminal IPython shortcuts with
:mod:`prompt_toolkit`
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
import warnings
import signal
import sys
from typing import Callable
from prompt_toolkit.application.current import get_app
from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
from prompt_toolkit.filters import (has_focus, has_selection, Condition,
vi_insert_mode, emacs_insert_mode, has_completions, vi_mode)
from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
from prompt_toolkit.key_binding import KeyBindings
from IPython.utils.decorators import undoc
@undoc
@Condition
def cursor_in_leading_ws():
before = get_app().current_buffer.document.current_line_before_cursor
return (not before) or before.isspace()
def create_ipython_shortcuts(shell):
"""Set up the prompt_toolkit keyboard shortcuts for IPython"""
kb = KeyBindings()
insert_mode = vi_insert_mode | emacs_insert_mode
if getattr(shell, 'handle_return', None):
return_handler = shell.handle_return(shell)
else:
return_handler = newline_or_execute_outer(shell)
kb.add('enter', filter=(has_focus(DEFAULT_BUFFER)
& ~has_selection
& insert_mode
))(return_handler)
def reformat_and_execute(event):
reformat_text_before_cursor(event.current_buffer, event.current_buffer.document, shell)
event.current_buffer.validate_and_handle()
kb.add('escape', 'enter', filter=(has_focus(DEFAULT_BUFFER)
& ~has_selection
& insert_mode
))(reformat_and_execute)
kb.add('c-\\')(force_exit)
kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
)(previous_history_or_previous_completion)
kb.add('c-n', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
)(next_history_or_next_completion)
kb.add('c-g', filter=(has_focus(DEFAULT_BUFFER) & has_completions)
)(dismiss_completion)
kb.add('c-c', filter=has_focus(DEFAULT_BUFFER))(reset_buffer)
kb.add('c-c', filter=has_focus(SEARCH_BUFFER))(reset_search_buffer)
supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP'))
kb.add('c-z', filter=supports_suspend)(suspend_to_bg)
# Ctrl+I == Tab
kb.add('tab', filter=(has_focus(DEFAULT_BUFFER)
& ~has_selection
& insert_mode
& cursor_in_leading_ws
))(indent_buffer)
kb.add('c-o', filter=(has_focus(DEFAULT_BUFFER) & emacs_insert_mode)
)(newline_autoindent_outer(shell.input_transformer_manager))
kb.add('f2', filter=has_focus(DEFAULT_BUFFER))(open_input_in_editor)
if shell.display_completions == 'readlinelike':
kb.add('c-i', filter=(has_focus(DEFAULT_BUFFER)
& ~has_selection
& insert_mode
& ~cursor_in_leading_ws
))(display_completions_like_readline)
if sys.platform == 'win32':
kb.add('c-v', filter=(has_focus(DEFAULT_BUFFER) & ~vi_mode))(win_paste)
return kb
def reformat_text_before_cursor(buffer, document, shell):
text = buffer.delete_before_cursor(len(document.text[:document.cursor_position]))
try:
formatted_text = shell.reformat_handler(text)
buffer.insert_text(formatted_text)
except Exception as e:
buffer.insert_text(text)
def newline_or_execute_outer(shell):
def newline_or_execute(event):
"""When the user presses return, insert a newline or execute the code."""
b = event.current_buffer
d = b.document
if b.complete_state:
cc = b.complete_state.current_completion
if cc:
b.apply_completion(cc)
else:
b.cancel_completion()
return
# If there's only one line, treat it as if the cursor is at the end.
# See https://github.com/ipython/ipython/issues/10425
if d.line_count == 1:
check_text = d.text
else:
check_text = d.text[:d.cursor_position]
status, indent = shell.check_complete(check_text)
# if all we have after the cursor is whitespace: reformat current text
# before cursor
after_cursor = d.text[d.cursor_position:]
reformatted = False
if not after_cursor.strip():
reformat_text_before_cursor(b, d, shell)
reformatted = True
if not (d.on_last_line or
d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
):
if shell.autoindent:
b.insert_text('\n' + indent)
else:
b.insert_text('\n')
return
if (status != 'incomplete') and b.accept_handler:
if not reformatted:
reformat_text_before_cursor(b, d, shell)
b.validate_and_handle()
else:
if shell.autoindent:
b.insert_text('\n' + indent)
else:
b.insert_text('\n')
return newline_or_execute
def previous_history_or_previous_completion(event):
"""
Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
If completer is open this still select previous completion.
"""
event.current_buffer.auto_up()
def next_history_or_next_completion(event):
"""
Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
If completer is open this still select next completion.
"""
event.current_buffer.auto_down()
def dismiss_completion(event):
b = event.current_buffer
if b.complete_state:
b.cancel_completion()
def reset_buffer(event):
b = event.current_buffer
if b.complete_state:
b.cancel_completion()
else:
b.reset()
def reset_search_buffer(event):
if event.current_buffer.document.text:
event.current_buffer.reset()
else:
event.app.layout.focus(DEFAULT_BUFFER)
def suspend_to_bg(event):
event.app.suspend_to_background()
def force_exit(event):
"""
Force exit (with a non-zero return value)
"""
sys.exit("Quit")
def indent_buffer(event):
event.current_buffer.insert_text(' ' * 4)
@undoc
def newline_with_copy_margin(event):
"""
DEPRECATED since IPython 6.0
See :any:`newline_autoindent_outer` for a replacement.
Preserve margin and cursor position when using
Control-O to insert a newline in EMACS mode
"""
warnings.warn("`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
"see `newline_autoindent_outer(shell)(event)` for a replacement.",
DeprecationWarning, stacklevel=2)
b = event.current_buffer
cursor_start_pos = b.document.cursor_position_col
b.newline(copy_margin=True)
b.cursor_up(count=1)
cursor_end_pos = b.document.cursor_position_col
if cursor_start_pos != cursor_end_pos:
pos_diff = cursor_start_pos - cursor_end_pos
b.cursor_right(count=pos_diff)
def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
"""
Return a function suitable for inserting a indented newline after the cursor.
Fancier version of deprecated ``newline_with_copy_margin`` which should
compute the correct indentation of the inserted line. That is to say, indent
by 4 extra space after a function definition, class definition, context
manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
"""
def newline_autoindent(event):
"""insert a newline after the cursor indented appropriately."""
b = event.current_buffer
d = b.document
if b.complete_state:
b.cancel_completion()
text = d.text[:d.cursor_position] + '\n'
_, indent = inputsplitter.check_complete(text)
b.insert_text('\n' + (' ' * (indent or 0)), move_cursor=False)
return newline_autoindent
def open_input_in_editor(event):
event.app.current_buffer.open_in_editor()
if sys.platform == 'win32':
from IPython.core.error import TryNext
from IPython.lib.clipboard import (ClipboardEmpty,
win32_clipboard_get,
tkinter_clipboard_get)
@undoc
def win_paste(event):
try:
text = win32_clipboard_get()
except TryNext:
try:
text = tkinter_clipboard_get()
except (TryNext, ClipboardEmpty):
return
except ClipboardEmpty:
return
event.current_buffer.insert_text(text.replace('\t', ' ' * 4))

View file

@ -0,0 +1,78 @@
"""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.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('*** Oldest frame')
child.expect(ipdb_prompt)
child.sendline('exit')
child.expect(in_prompt)
child.sendline('exit')
child.close()

View file

@ -0,0 +1,136 @@
"""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
import nose.tools as nt
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')
nt.assert_equal(p.returncode, 0)
nt.assert_in('3 . 14', std)
if os.name != 'nt':
# TODO: Fix up our different stdout references, see issue gh-14
nt.assert_in('IPython', std)
nt.assert_in('bye!', 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 = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
child.expect(ipy_prompt)
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()

View file

@ -0,0 +1,28 @@
"""Test help output of various IPython entry points"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
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():
tt.help_all_output_test("trust")

View file

@ -0,0 +1,193 @@
# -*- 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 IPython.core.inputtransformer import InputTransformer
from IPython.testing import tools as tt
from IPython.utils.capture import capture_output
from IPython.terminal.ptutils import _elide, _adjust_completion_text_based_on_context
import nose.tools as nt
class TestElide(unittest.TestCase):
def test_elide(self):
_elide('concatenate((a1, a2, ...), axis', '') # do not raise
_elide('concatenate((a1, a2, ..), . axis', '') # do not raise
nt.assert_equal(_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'
nt.assert_equal(_elide(test_string, ''), expect_stirng)
def test_elide_typed_normal(self):
nt.assert_equal(_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"
"""
nt.assert_equal(_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
nt.assert_equal(_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
nt.assert_equal(_adjust_completion_text_based_on_context('arg1=', 'func1(a=)', 7), 'arg1')
# Untouched cases
nt.assert_equal(_adjust_completion_text_based_on_context('arg1=', 'func1(a)', 7), 'arg1=')
nt.assert_equal(_adjust_completion_text_based_on_context('arg1=', 'func1(a', 7), 'arg1=')
nt.assert_equal(_adjust_completion_text_based_on_context('%magic', 'func1(a=)', 7), '%magic')
nt.assert_equal(_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_plain_text_only(self):
ip = get_ipython()
formatter = ip.display_formatter
assert formatter.active_types == ['text/plain']
assert not 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
display('<custom>')
# verify that _ipython_display_ shortcut isn't called
obj = Test2()
with capture_output() as captured:
data, _ = formatter.format(obj)
self.assertEqual(data, {'text/plain': repr(obj)})
assert captured.stdout == ''
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)