160 lines
5 KiB
Python
160 lines
5 KiB
Python
|
""" A minimal application using the ZMQ-based terminal IPython frontend.
|
||
|
|
||
|
This is not a complete console app, as subprocess will not be able to receive
|
||
|
input, there is no real readline support, among other limitations.
|
||
|
"""
|
||
|
|
||
|
# Copyright (c) IPython Development Team.
|
||
|
# Distributed under the terms of the Modified BSD License.
|
||
|
|
||
|
from __future__ import print_function
|
||
|
|
||
|
import logging
|
||
|
import signal
|
||
|
import sys
|
||
|
|
||
|
from traitlets import (
|
||
|
Dict, Any
|
||
|
)
|
||
|
from traitlets.config import catch_config_error, boolean_flag
|
||
|
|
||
|
from jupyter_core.application import JupyterApp, base_aliases, base_flags, NoStart
|
||
|
from jupyter_client.consoleapp import (
|
||
|
JupyterConsoleApp, app_aliases, app_flags,
|
||
|
)
|
||
|
|
||
|
from jupyter_console.ptshell import ZMQTerminalInteractiveShell
|
||
|
from jupyter_console import __version__
|
||
|
|
||
|
#-----------------------------------------------------------------------------
|
||
|
# Globals
|
||
|
#-----------------------------------------------------------------------------
|
||
|
|
||
|
_examples = """
|
||
|
jupyter console # start the ZMQ-based console
|
||
|
jupyter console --existing # connect to an existing ipython session
|
||
|
"""
|
||
|
|
||
|
#-----------------------------------------------------------------------------
|
||
|
# Flags and Aliases
|
||
|
#-----------------------------------------------------------------------------
|
||
|
|
||
|
# copy flags from mixin:
|
||
|
flags = dict(base_flags)
|
||
|
# start with mixin frontend flags:
|
||
|
# update full dict with frontend flags:
|
||
|
flags.update(app_flags)
|
||
|
flags.update(boolean_flag(
|
||
|
'simple-prompt', 'ZMQTerminalInteractiveShell.simple_prompt',
|
||
|
"Force simple minimal prompt using `raw_input`",
|
||
|
"Use a rich interactive prompt with prompt_toolkit"
|
||
|
))
|
||
|
|
||
|
# copy flags from mixin
|
||
|
aliases = dict(base_aliases)
|
||
|
|
||
|
aliases.update(app_aliases)
|
||
|
|
||
|
frontend_aliases = set(app_aliases.keys())
|
||
|
frontend_flags = set(app_flags.keys())
|
||
|
|
||
|
|
||
|
#-----------------------------------------------------------------------------
|
||
|
# Classes
|
||
|
#-----------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
class ZMQTerminalIPythonApp(JupyterApp, JupyterConsoleApp):
|
||
|
name = "jupyter-console"
|
||
|
version = __version__
|
||
|
"""Start a terminal frontend to the IPython zmq kernel."""
|
||
|
|
||
|
description = """
|
||
|
The Jupyter terminal-based Console.
|
||
|
|
||
|
This launches a Console application inside a terminal.
|
||
|
|
||
|
The Console supports various extra features beyond the traditional
|
||
|
single-process Terminal IPython shell, such as connecting to an
|
||
|
existing ipython session, via:
|
||
|
|
||
|
jupyter console --existing
|
||
|
|
||
|
where the previous session could have been created by another ipython
|
||
|
console, an ipython qtconsole, or by opening an ipython notebook.
|
||
|
|
||
|
"""
|
||
|
examples = _examples
|
||
|
|
||
|
classes = [ZMQTerminalInteractiveShell] + JupyterConsoleApp.classes
|
||
|
flags = Dict(flags)
|
||
|
aliases = Dict(aliases)
|
||
|
frontend_aliases = Any(frontend_aliases)
|
||
|
frontend_flags = Any(frontend_flags)
|
||
|
|
||
|
subcommands = Dict()
|
||
|
|
||
|
force_interact = True
|
||
|
|
||
|
def parse_command_line(self, argv=None):
|
||
|
super(ZMQTerminalIPythonApp, self).parse_command_line(argv)
|
||
|
self.build_kernel_argv(self.extra_args)
|
||
|
|
||
|
def init_shell(self):
|
||
|
JupyterConsoleApp.initialize(self)
|
||
|
# relay sigint to kernel
|
||
|
signal.signal(signal.SIGINT, self.handle_sigint)
|
||
|
self.shell = ZMQTerminalInteractiveShell.instance(parent=self,
|
||
|
manager=self.kernel_manager,
|
||
|
client=self.kernel_client,
|
||
|
confirm_exit=self.confirm_exit,
|
||
|
)
|
||
|
self.shell.own_kernel = not self.existing
|
||
|
|
||
|
def init_gui_pylab(self):
|
||
|
# no-op, because we don't want to import matplotlib in the frontend.
|
||
|
pass
|
||
|
|
||
|
def handle_sigint(self, *args):
|
||
|
if self.shell._executing:
|
||
|
if self.kernel_manager:
|
||
|
self.kernel_manager.interrupt_kernel()
|
||
|
else:
|
||
|
print("ERROR: Cannot interrupt kernels we didn't start.",
|
||
|
file = sys.stderr)
|
||
|
else:
|
||
|
# raise the KeyboardInterrupt if we aren't waiting for execution,
|
||
|
# so that the interact loop advances, and prompt is redrawn, etc.
|
||
|
raise KeyboardInterrupt
|
||
|
|
||
|
@catch_config_error
|
||
|
def initialize(self, argv=None):
|
||
|
"""Do actions after construct, but before starting the app."""
|
||
|
super(ZMQTerminalIPythonApp, self).initialize(argv)
|
||
|
if self._dispatching:
|
||
|
return
|
||
|
# create the shell
|
||
|
self.init_shell()
|
||
|
# and draw the banner
|
||
|
self.init_banner()
|
||
|
|
||
|
def init_banner(self):
|
||
|
"""optionally display the banner"""
|
||
|
self.shell.show_banner()
|
||
|
# Make sure there is a space below the banner.
|
||
|
#if self.log_level <= logging.INFO: print()
|
||
|
|
||
|
def start(self):
|
||
|
# JupyterApp.start dispatches on NoStart
|
||
|
super(ZMQTerminalIPythonApp, self).start()
|
||
|
self.log.debug("Starting the jupyter console mainloop...")
|
||
|
self.shell.mainloop()
|
||
|
|
||
|
|
||
|
main = launch_new_instance = ZMQTerminalIPythonApp.launch_instance
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
main()
|
||
|
|