Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Two-process terminal frontend #708

Closed
wants to merge 23 commits into from

4 participants

Min RK Paul Ivanov Omar Andres Zapata Mesa Thomas Kluyver
Min RK
Owner

This extends #433 to use TerminalInteractiveShell/TerminalIPythonApp subclasses, so all our configuration, etc. is inherited as you would expect.

Not ready for merge because, among other things, ctrl-C crashes the kernel. Mostly posted as a starting point for someone else to take over finishing this client.

As far as I can tell, it's already quite functional as long as you never hit ctrl-C :).

omazapa and others added some commits
Omar Andres Zapata Mesa omazapa basic kernelmanager and frontend wrote, support indentation but it do…
…nt run code yet
55a32de
Omar Andres Zapata Mesa omazapa working in handlers 0a7161e
Omar Andres Zapata Mesa omazapa working in tab completion 1e1f981
Omar Andres Zapata Mesa omazapa completer not working fine 83f4e6f
Omar Andres Zapata Mesa omazapa little bug fixed in kernelmanager's queues eb57363
Omar Andres Zapata Mesa omazapa raw_input captured and working fine 6051b91
Omar Andres Zapata Mesa omazapa -mworking in tab-completion 22843e0
Omar Andres Zapata Mesa omazapa tab completion is not working yet, unknow error bfc9430
Thomas Kluyver takluyver Make readline tab-completion work in two-process terminal frontend. 2e6f2ac
Omar Andres Zapata Mesa omazapa little bug fixed in pyout message print. cd21306
Omar Andres Zapata Mesa omazapa bug fixed prompt count using differents clients 79048f7
Omar Andres Zapata Mesa omazapa traceback support added be18348
Omar Andres Zapata Mesa omazapa bug fixed in prompt count after traceback cb7e461
Thomas Kluyver takluyver Replace tabs with spaces 020805c
Thomas Kluyver takluyver Separate out frontend.zmqterminal package. 127d459
Thomas Kluyver takluyver zmqterminal frontend now uses IPythonInputSplitter, and non-ascii cha…
…racters work.
22a8fe3
Thomas Kluyver takluyver Minor tidying up of zmqterminal.frontend ef40f1f
Thomas Kluyver takluyver Minor tidy up of zmqterminal.completer 8d4a68b
Thomas Kluyver takluyver Refactor and simplification of zmqterminal. 3d4b21e
Thomas Kluyver takluyver Simplify handling of messaging in zmqterminal. 3b16045
Thomas Kluyver takluyver Nicer prompt formatting in zmqterminal, and use print_function. e3f9bc7
Min RK minrk rebased and updated to master d8d6bdf
Min RK minrk zmqterminal subclasses TerminalInteractiveShell/IPApp
this does a lot of the boiler plate for us, so Omar's communication
code is used in place of run_cell / complete.

Now colors, prompts, indentation, cl-args, etc. are all inherited
from regular existing code.

keyboard interrupts don't work yet, the kernel just dies...
9bb377b
Paul Ivanov
Owner

this would be really nice to get back plain terminal ipython with the two-process connection capability. For one, it would enable Django-shell embedding to work with vim-ipython in a simlpe and straightfoward manner - which was recently requested in ivanov/vim-ipython#11 and I pointed them to this PR.

Paul Ivanov
Owner

closing this one, since #864 will supersede it as

Paul Ivanov ivanov closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 17, 2011
  1. Omar Andres Zapata Mesa Min RK

    basic kernelmanager and frontend wrote, support indentation but it do…

    omazapa authored minrk committed
    …nt run code yet
  2. Omar Andres Zapata Mesa Min RK

    working in handlers

    omazapa authored minrk committed
  3. Omar Andres Zapata Mesa Min RK

    working in tab completion

    omazapa authored minrk committed
  4. Omar Andres Zapata Mesa Min RK

    completer not working fine

    omazapa authored minrk committed
  5. Omar Andres Zapata Mesa Min RK

    little bug fixed in kernelmanager's queues

    omazapa authored minrk committed
  6. Omar Andres Zapata Mesa Min RK

    raw_input captured and working fine

    omazapa authored minrk committed
  7. Omar Andres Zapata Mesa Min RK

    -mworking in tab-completion

    omazapa authored minrk committed
  8. Omar Andres Zapata Mesa Min RK

    tab completion is not working yet, unknow error

    omazapa authored minrk committed
  9. Thomas Kluyver Min RK
  10. Omar Andres Zapata Mesa Min RK

    little bug fixed in pyout message print.

    omazapa authored minrk committed
  11. Omar Andres Zapata Mesa Min RK

    bug fixed prompt count using differents clients

    omazapa authored minrk committed
  12. Omar Andres Zapata Mesa Min RK

    traceback support added

    omazapa authored minrk committed
  13. Omar Andres Zapata Mesa Min RK

    bug fixed in prompt count after traceback

    omazapa authored minrk committed
  14. Thomas Kluyver Min RK

    Replace tabs with spaces

    takluyver authored minrk committed
  15. Thomas Kluyver Min RK

    Separate out frontend.zmqterminal package.

    takluyver authored minrk committed
  16. Thomas Kluyver Min RK

    zmqterminal frontend now uses IPythonInputSplitter, and non-ascii cha…

    takluyver authored minrk committed
    …racters work.
  17. Thomas Kluyver Min RK

    Minor tidying up of zmqterminal.frontend

    takluyver authored minrk committed
  18. Thomas Kluyver Min RK

    Minor tidy up of zmqterminal.completer

    takluyver authored minrk committed
  19. Thomas Kluyver Min RK

    Refactor and simplification of zmqterminal.

    takluyver authored minrk committed
  20. Thomas Kluyver Min RK

    Simplify handling of messaging in zmqterminal.

    takluyver authored minrk committed
  21. Thomas Kluyver Min RK
  22. Min RK

    rebased and updated to master

    minrk authored
  23. Min RK

    zmqterminal subclasses TerminalInteractiveShell/IPApp

    minrk authored
    this does a lot of the boiler plate for us, so Omar's communication
    code is used in place of run_cell / complete.
    
    Now colors, prompts, indentation, cl-args, etc. are all inherited
    from regular existing code.
    
    keyboard interrupts don't work yet, the kernel just dies...
This page is out of date. Refresh to see the latest.
3  IPython/frontend/terminal/ipapp.py
View
@@ -210,6 +210,9 @@ def _classes_default(self):
kernel = ("IPython.zmq.ipkernel.IPKernelApp",
"Start a kernel without an attached frontend."
),
+ zmq=('IPython.frontend.zmqterminal.app.ZMQTerminalIPythonApp',
+ """Launch two-process Terminal session with 0MQ."""
+ ),
))
# *do* autocreate requested profile, but don't create the config file.
0  IPython/frontend/zmqterminal/__init__.py
View
No changes.
161 IPython/frontend/zmqterminal/app.py
View
@@ -0,0 +1,161 @@
+from __future__ import print_function
+
+import signal
+import sys
+import time
+
+from IPython.frontend.terminal.ipapp import TerminalIPythonApp
+
+from IPython.utils.traitlets import (
+ Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
+)
+from IPython.zmq.ipkernel import (
+ flags as ipkernel_flags,
+ aliases as ipkernel_aliases,
+ IPKernelApp
+)
+from IPython.zmq.session import Session
+from IPython.zmq.zmqshell import ZMQInteractiveShell
+from IPython.zmq.blockingkernelmanager import BlockingKernelManager
+from IPython.zmq.ipkernel import (
+ flags as ipkernel_flags,
+ aliases as ipkernel_aliases,
+ IPKernelApp
+)
+from IPython.frontend.zmqterminal.interactiveshell import ZMQTerminalInteractiveShell
+
+#-----------------------------------------------------------------------------
+# Network Constants
+#-----------------------------------------------------------------------------
+
+from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
+
+#-----------------------------------------------------------------------------
+# Flags and Aliases
+#-----------------------------------------------------------------------------
+
+
+flags = dict(ipkernel_flags)
+frontend_flags = {
+ 'existing' : ({'ZMQTerminalIPythonApp' : {'existing' : True}},
+ "Connect to an existing kernel."),
+}
+flags.update(frontend_flags)
+# the flags that are specific to the frontend
+# these must be scrubbed before being passed to the kernel,
+# or it will raise an error on unrecognized flags
+frontend_flags = frontend_flags.keys()
+
+aliases = dict(ipkernel_aliases)
+
+frontend_aliases = dict(
+ hb = 'ZMQTerminalIPythonApp.hb_port',
+ shell = 'ZMQTerminalIPythonApp.shell_port',
+ iopub = 'ZMQTerminalIPythonApp.iopub_port',
+ stdin = 'ZMQTerminalIPythonApp.stdin_port',
+ ip = 'ZMQTerminalIPythonApp.ip',
+)
+aliases.update(frontend_aliases)
+# also scrub aliases from the frontend
+frontend_flags.extend(frontend_aliases.keys())
+
+#-----------------------------------------------------------------------------
+# Classes
+#-----------------------------------------------------------------------------
+
+
+class ZMQTerminalIPythonApp(TerminalIPythonApp):
+ """Start a terminal frontend to the IPython zmq kernel."""
+
+ kernel_argv = List(Unicode)
+ flags = Dict(flags)
+ aliases = Dict(aliases)
+ classes = List([IPKernelApp, ZMQTerminalInteractiveShell])
+
+ # connection info:
+ ip = Unicode(LOCALHOST, config=True,
+ help="""Set the kernel\'s IP address [default localhost].
+ If the IP address is something other than localhost, then
+ Consoles on other machines will be able to connect
+ to the Kernel, so be careful!"""
+ )
+ pure = False
+ hb_port = Int(0, config=True,
+ help="set the heartbeat port [default: random]")
+ shell_port = Int(0, config=True,
+ help="set the shell (XREP) port [default: random]")
+ iopub_port = Int(0, config=True,
+ help="set the iopub (PUB) port [default: random]")
+ stdin_port = Int(0, config=True,
+ help="set the stdin (XREQ) port [default: random]")
+
+ existing = CBool(False, config=True,
+ help="Whether to connect to an already running Kernel.")
+
+ # from qtconsoleapp:
+ def parse_command_line(self, argv=None):
+ super(ZMQTerminalIPythonApp, self).parse_command_line(argv)
+ if argv is None:
+ argv = sys.argv[1:]
+
+ self.kernel_argv = list(argv) # copy
+ # kernel should inherit default config file from frontend
+ self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
+ # scrub frontend-specific flags
+ for a in argv:
+
+ if a.startswith('-'):
+ key = a.lstrip('-').split('=')[0]
+ if key in frontend_flags:
+ self.kernel_argv.remove(a)
+
+ def init_kernel_manager(self):
+ """init kernel manager (from qtconsole)"""
+ # Don't let Qt or ZMQ swallow KeyboardInterupts.
+ # signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+ # Create a KernelManager and start a kernel.
+ self.kernel_manager = BlockingKernelManager(
+ shell_address=(self.ip, self.shell_port),
+ sub_address=(self.ip, self.iopub_port),
+ stdin_address=(self.ip, self.stdin_port),
+ hb_address=(self.ip, self.hb_port),
+ config=self.config
+ )
+ # start the kernel
+ if not self.existing:
+ kwargs = dict(ip=self.ip, ipython=not self.pure)
+ kwargs['extra_arguments'] = self.kernel_argv
+ self.kernel_manager.start_kernel(**kwargs)
+ # wait for kernel to start
+ time.sleep(0.5)
+ self.kernel_manager.start_channels()
+ # relay sigint to kernel
+ signal.signal(signal.SIGINT, self.handle_sigint)
+
+ def init_shell(self):
+ self.init_kernel_manager()
+ self.shell = ZMQTerminalInteractiveShell.instance(config=self.config,
+ display_banner=False, profile_dir=self.profile_dir,
+ ipython_dir=self.ipython_dir, kernel_manager=self.kernel_manager)
+
+ def handle_sigint(self, *args):
+ # FIXME: this doesn't work, the kernel just dies every time
+ self.shell.write('KeyboardInterrupt\n')
+ self.kernel_manager.interrupt_kernel()
+
+ def init_code(self):
+ # no-op in the frontend, code gets run in the backend
+ pass
+
+
+def launch_new_instance():
+ """Create and run a full blown IPython instance"""
+ app = ZMQTerminalIPythonApp.instance()
+ app.initialize()
+ app.start()
+
+
+if __name__ == '__main__':
+ launch_new_instance()
+
44 IPython/frontend/zmqterminal/completer.py
View
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+import readline
+from Queue import Empty
+
+class ZMQCompleter(object):
+ """Client-side completion machinery.
+
+ How it works: self.complete will be called multiple times, with
+ state=0,1,2,... When state=0 it should compute ALL the completion matches,
+ and then return them for each value of state."""
+
+ def __init__(self, shell, km):
+ self.shell = shell
+ self.km = km
+ self.matches = []
+
+ def complete_request(self,text):
+ line = readline.get_line_buffer()
+ cursor_pos = readline.get_endidx()
+
+ # send completion request to kernel
+ # Give the kernel up to 0.5s to respond
+ msg_id = self.km.shell_channel.complete(text=text, line=line,
+ cursor_pos=cursor_pos)
+
+ msg = self.km.shell_channel.get_msg(timeout=0.5)
+ if msg['parent_header']['msg_id'] == msg_id:
+ return msg["content"]["matches"]
+ return []
+
+ def rlcomplete(self, text, state):
+ if state == 0:
+ try:
+ self.matches = self.complete_request(text)
+ except Empty:
+ print('WARNING: Kernel timeout on tab completion.')
+
+ try:
+ return self.matches[state]
+ except IndexError:
+ return None
+
+ def complete(self, text, line, cursor_pos=None):
+ return self.rlcomplete(text, 0)
253 IPython/frontend/zmqterminal/frontend.py
View
@@ -0,0 +1,253 @@
+# -*- coding: utf-8 -*-
+"""Frontend of ipython working with python-zmq
+
+Ipython's frontend, is a ipython interface that send request to kernel and proccess the kernel's outputs.
+
+For more details, see the ipython-zmq design
+"""
+#-----------------------------------------------------------------------------
+# Copyright (C) 2010 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
+#-----------------------------------------------------------------------------
+from __future__ import print_function
+
+import __builtin__
+import sys
+import os
+from Queue import Empty
+import readline
+import rlcompleter
+
+#-----------------------------------------------------------------------------
+# Imports from ipython
+#-----------------------------------------------------------------------------
+from IPython.external.argparse import ArgumentParser
+from IPython.core.inputsplitter import IPythonInputSplitter
+from IPython.zmq.blockingkernelmanager import BlockingKernelManager as KernelManager
+from IPython.frontend.zmqterminal.completer import ClientCompleter2p
+
+#-----------------------------------------------------------------------------
+# Network Constants
+#-----------------------------------------------------------------------------
+
+from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
+class Frontend(object):
+ """This class is a simple frontend to ipython-zmq
+
+ NOTE: this class uses kernelmanager to manipulate sockets
+
+ Parameters:
+ -----------
+ kernelmanager : object
+ instantiated object from class KernelManager in module kernelmanager
+
+ """
+
+ def __init__(self, kernelmanager):
+ self.km = kernelmanager
+ self.session_id = self.km.session.session
+ self.completer = ClientCompleter2p(self, self.km)
+ readline.parse_and_bind("tab: complete")
+ readline.parse_and_bind('set show-all-if-ambiguous on')
+ readline.set_completer(self.completer.complete)
+
+ history_path = os.path.expanduser('~/.ipython/history')
+ if os.path.isfile(history_path):
+ rlcompleter.readline.read_history_file(history_path)
+ else:
+ print("history file cannot be read.")
+
+ self.messages = {}
+
+ self._splitter = IPythonInputSplitter()
+ self.code = ""
+
+ self.prompt_count = 0
+ self._get_initial_prompt()
+
+ def _get_initial_prompt(self):
+ self._execute('', hidden=True)
+
+ def interact(self):
+ """Gets input from console using inputsplitter, then
+ while you enter code it can indent and set index id to any input
+ """
+
+ try:
+ print()
+ self._splitter.push(raw_input('In [%i]: '%self.prompt_count+self.code))
+ while self._splitter.push_accepts_more():
+ self.code = raw_input('.....: '+' '*self._splitter.indent_spaces)
+ self._splitter.push(' '*self._splitter.indent_spaces+self.code)
+ self._execute(self._splitter.source,False)
+ self._splitter.reset()
+ except KeyboardInterrupt:
+ print('\nKeyboardInterrupt\n')
+ pass
+
+
+ def start(self):
+ """Start the interaction loop, calling the .interact() method for each
+ input cell.
+ """
+ while True:
+ try:
+ self.interact()
+ except KeyboardInterrupt:
+ print('\nKeyboardInterrupt\n')
+ pass
+ except EOFError:
+ answer = ''
+ while True:
+ answer = raw_input('\nDo you really want to exit ([y]/n)?')
+ if answer == 'y' or answer == '' :
+ self.km.shutdown_kernel()
+ sys.exit()
+ elif answer == 'n':
+ break
+
+ def _execute(self, source, hidden = True):
+ """ Execute 'source'. If 'hidden', do not show any output.
+
+ See parent class :meth:`execute` docstring for full details.
+ """
+ msg_id = self.km.shell_channel.execute(source, hidden)
+ while not self.km.shell_channel.msg_ready():
+ try:
+ self.handle_stdin_channel(timeout=0.1)
+ except Empty:
+ pass
+ self.handle_execute_reply(msg_id)
+
+ def handle_execute_reply(self, msg_id):
+ msg = self.km.shell_channel.get_msg()
+ if msg["parent_header"]["msg_id"] == msg_id:
+ if msg["content"]["status"] == 'ok' :
+ self.handle_sub_channel()
+
+ elif msg["content"]["status"] == 'error':
+ for frame in msg["content"]["traceback"]:
+ print(frame, file=sys.stderr)
+
+ self.prompt_count = msg["content"]["execution_count"] + 1
+
+
+ def handle_sub_channel(self):
+ """ Method to procces subscribe channel's messages
+
+ This method reads a message and processes the content in different
+ outputs like stdout, stderr, pyout and status
+
+ Arguments:
+ sub_msg: message receive from kernel in the sub socket channel
+ capture by kernel manager.
+ """
+ while self.km.sub_channel.msg_ready():
+ sub_msg = self.km.sub_channel.get_msg()
+ if self.session_id == sub_msg['parent_header']['session']:
+ if sub_msg['msg_type'] == 'status' :
+ if sub_msg["content"]["execution_state"] == "busy" :
+ pass
+
+ elif sub_msg['msg_type'] == 'stream' :
+ if sub_msg["content"]["name"] == "stdout":
+ print(sub_msg["content"]["data"], file=sys.stdout, end="")
+ sys.stdout.flush()
+ elif sub_msg["content"]["name"] == "stderr" :
+ print(sub_msg["content"]["data"], file=sys.stderr, end="")
+ sys.stderr.flush()
+
+ elif sub_msg['msg_type'] == 'pyout' :
+ print("Out[%i]:"%sub_msg["content"]["execution_count"],
+ sub_msg["content"]["data"]["text/plain"],
+ file=sys.stdout)
+ sys.stdout.flush()
+
+ def handle_stdin_channel(self, timeout=0.1):
+ """ Method to capture raw_input
+ """
+ msg_rep = self.km.stdin_channel.get_msg(timeout=timeout)
+ if self.session_id == msg_rep["parent_header"]["session"] :
+ raw_data = raw_input(msg_rep["content"]["prompt"])
+ self.km.stdin_channel.input(raw_data)
+
+
+
+
+def start_frontend():
+ """ Entry point for application.
+
+ """
+ # Parse command line arguments.
+ parser = ArgumentParser()
+ kgroup = parser.add_argument_group('kernel options')
+ kgroup.add_argument('-e', '--existing', action='store_true',
+ help='connect to an existing kernel')
+ kgroup.add_argument('--ip', type=str, default=LOCALHOST,
+ help=\
+ "set the kernel\'s IP address [default localhost].\
+ If the IP address is something other than localhost, then \
+ Consoles on other machines will be able to connect\
+ to the Kernel, so be careful!")
+ kgroup.add_argument('--shell', type=int, metavar='PORT', default=0,
+ help='set the XREQ channel port [default random]')
+ kgroup.add_argument('--iopub', type=int, metavar='PORT', default=0,
+ help='set the SUB channel port [default random]')
+ kgroup.add_argument('--stdin', type=int, metavar='PORT', default=0,
+ help='set the REP channel port [default random]')
+ kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
+ help='set the heartbeat port [default random]')
+
+ egroup = kgroup.add_mutually_exclusive_group()
+ egroup.add_argument('--pure', action='store_true', help = \
+ 'use a pure Python kernel instead of an IPython kernel')
+ egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
+ const='auto', help = \
+ "Pre-load matplotlib and numpy for interactive use. If GUI is not \
+ given, the GUI backend is matplotlib's, otherwise use one of: \
+ ['tk', 'gtk', 'qt', 'wx', 'inline'].")
+ egroup.add_argument('--colors', type=str,
+ help="Set the color scheme (LightBG,Linux,NoColor). This is guessed\
+ based on the pygments style if not set.")
+
+ args = parser.parse_args()
+
+ # parse the colors arg down to current known labels
+ if args.colors:
+ colors=args.colors.lower()
+ if colors in ('lightbg', 'light'):
+ colors='lightbg'
+ elif colors in ('dark', 'linux'):
+ colors='linux'
+ else:
+ colors='nocolor'
+ else:
+ colors=None
+
+ # Create a KernelManager and start a kernel.
+ kernel_manager = KernelManager(shell_address=(args.ip, args.shell),
+ sub_address=(args.ip, args.iopub),
+ stdin_address=(args.ip, args.stdin),
+ hb_address=(args.ip, args.hb))
+ if not args.existing:
+ # if not args.ip in LOCAL_IPS+ALL_ALIAS:
+ # raise ValueError("Must bind a local ip, such as: %s"%LOCAL_IPS)
+
+ kwargs = dict(ip=args.ip, ipython=True)
+ kernel_manager.start_kernel(**kwargs)
+
+
+ kernel_manager.start_channels()
+
+ frontend=Frontend(kernel_manager)
+ return frontend
+
+if __name__ == "__main__" :
+ frontend=start_frontend()
+ frontend.start()
252 IPython/frontend/zmqterminal/interactiveshell.py
View
@@ -0,0 +1,252 @@
+# -*- coding: utf-8 -*-
+"""Frontend of ipython working with python-zmq
+
+Ipython's frontend, is a ipython interface that send request to kernel and proccess the kernel's outputs.
+
+For more details, see the ipython-zmq design
+"""
+#-----------------------------------------------------------------------------
+# Copyright (C) 2011 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
+#-----------------------------------------------------------------------------
+from __future__ import print_function
+
+import bdb
+import sys
+
+from Queue import Empty
+
+from IPython.core.alias import AliasManager, AliasError
+from IPython.utils.warn import warn, error, fatal
+from IPython.utils import io
+
+from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
+from IPython.frontend.zmqterminal.completer import ZMQCompleter
+
+
+class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
+ """A subclass of TerminalInteractiveShell that """
+
+ def __init__(self, *args, **kwargs):
+ self.km = kwargs.pop('kernel_manager')
+ self.session_id = self.km.session.session
+ super(ZMQTerminalInteractiveShell, self).__init__(*args, **kwargs)
+
+ def init_completer(self):
+ """Initialize the completion machinery.
+
+ This creates completion machinery that can be used by client code,
+ either interactively in-process (typically triggered by the readline
+ library), programatically (such as in test suites) or out-of-prcess
+ (typically over the network by remote frontends).
+ """
+ from IPython.core.completerlib import (module_completer,
+ magic_run_completer, cd_completer)
+
+ self.Completer = ZMQCompleter(self, self.km)
+
+
+ self.set_hook('complete_command', module_completer, str_key = 'import')
+ self.set_hook('complete_command', module_completer, str_key = 'from')
+ self.set_hook('complete_command', magic_run_completer, str_key = '%run')
+ self.set_hook('complete_command', cd_completer, str_key = '%cd')
+
+ # Only configure readline if we truly are using readline. IPython can
+ # do tab-completion over the network, in GUIs, etc, where readline
+ # itself may be absent
+ if self.has_readline:
+ self.set_readline_completer()
+
+ def run_cell(self, cell, store_history=True):
+ """Run a complete IPython cell.
+
+ Parameters
+ ----------
+ cell : str
+ The code (including IPython code such as %magic functions) to run.
+ store_history : bool
+ If True, the raw and translated cell will be stored in IPython's
+ history. For user code calling back into IPython's machinery, this
+ should be set to False.
+ """
+ if (not cell) or cell.isspace():
+ return
+
+ # shell_channel.execute takes 'hidden', which is the inverse of store_hist
+ msg_id = self.km.shell_channel.execute(cell, not store_history)
+ while not self.km.shell_channel.msg_ready():
+ try:
+ self.handle_stdin_request(timeout=0.05)
+ except Empty:
+ pass
+ self.handle_execute_reply(msg_id)
+
+ #-----------------
+ # message handlers
+ #-----------------
+
+ def handle_execute_reply(self, msg_id):
+ msg = self.km.shell_channel.get_msg()
+ if msg["parent_header"]["msg_id"] == msg_id:
+ if msg["content"]["status"] == 'ok' :
+ self.handle_iopub()
+
+ elif msg["content"]["status"] == 'error':
+ for frame in msg["content"]["traceback"]:
+ print(frame, file=io.stderr)
+
+ self.execution_count = msg["content"]["execution_count"] + 1
+
+
+ def handle_iopub(self):
+ """ Method to procces subscribe channel's messages
+
+ This method reads a message and processes the content in different
+ outputs like stdout, stderr, pyout and status
+
+ Arguments:
+ sub_msg: message receive from kernel in the sub socket channel
+ capture by kernel manager.
+ """
+ while self.km.sub_channel.msg_ready():
+ sub_msg = self.km.sub_channel.get_msg()
+ msg_type = sub_msg['header']['msg_type']
+ if self.session_id == sub_msg['parent_header']['session']:
+ if msg_type == 'status' :
+ if sub_msg["content"]["execution_state"] == "busy" :
+ pass
+
+ elif msg_type == 'stream' :
+ if sub_msg["content"]["name"] == "stdout":
+ print(sub_msg["content"]["data"], file=io.stdout, end="")
+ io.stdout.flush()
+ elif sub_msg["content"]["name"] == "stderr" :
+ print(sub_msg["content"]["data"], file=io.stderr, end="")
+ io.stderr.flush()
+
+ elif msg_type == 'pyout':
+ format_dict = sub_msg["content"]["data"]
+ # taken from DisplayHook.__call__:
+ hook = self.displayhook
+ hook.start_displayhook()
+ hook.write_output_prompt()
+ hook.write_format_data(format_dict)
+ hook.log_output(format_dict)
+ hook.finish_displayhook()
+
+ def handle_stdin_request(self, timeout=0.1):
+ """ Method to capture raw_input
+ """
+ msg_rep = self.km.stdin_channel.get_msg(timeout=timeout)
+ if self.session_id == msg_rep["parent_header"]["session"] :
+ raw_data = raw_input(msg_rep["content"]["prompt"])
+ self.km.stdin_channel.input(raw_data)
+
+ def mainloop(self, display_banner=False):
+ while True:
+ try:
+ self.interact(display_banner=display_banner)
+ #self.interact_with_readline()
+ # XXX for testing of a readline-decoupled repl loop, call
+ # interact_with_readline above
+ break
+ except KeyboardInterrupt:
+ # this should not be necessary, but KeyboardInterrupt
+ # handling seems rather unpredictable...
+ self.write("\nKeyboardInterrupt in interact()\n")
+
+ def interact(self, display_banner=None):
+ """Closely emulate the interactive Python console."""
+
+ # batch run -> do not interact
+ if self.exit_now:
+ return
+
+ if display_banner is None:
+ display_banner = self.display_banner
+
+ if isinstance(display_banner, basestring):
+ self.show_banner(display_banner)
+ elif display_banner:
+ self.show_banner()
+
+ more = False
+
+ if self.has_readline:
+ self.readline_startup_hook(self.pre_readline)
+ # exit_now is set by a call to %Exit or %Quit, through the
+ # ask_exit callback.
+
+ while not self.exit_now:
+ if not self.km.is_alive:
+ ans = self.raw_input("kernel died, restart (y/n)?")
+ if not ans.lower().startswith('n'):
+ self.km.restart_kernel(True)
+ else:
+ self.exit_now=True
+ continue
+ self.hooks.pre_prompt_hook()
+ if more:
+ try:
+ prompt = self.hooks.generate_prompt(True)
+ except:
+ self.showtraceback()
+ if self.autoindent:
+ self.rl_do_indent = True
+
+ else:
+ try:
+ prompt = self.hooks.generate_prompt(False)
+ except:
+ self.showtraceback()
+ try:
+ line = self.raw_input(prompt)
+ if self.exit_now:
+ # quick exit on sys.std[in|out] close
+ break
+ if self.autoindent:
+ self.rl_do_indent = False
+
+ except KeyboardInterrupt:
+ #double-guard against keyboardinterrupts during kbdint handling
+ try:
+ self.write('\nKeyboardInterrupt\n')
+ self.input_splitter.reset()
+ more = False
+ except KeyboardInterrupt:
+ pass
+ except EOFError:
+ if self.autoindent:
+ self.rl_do_indent = False
+ if self.has_readline:
+ self.readline_startup_hook(None)
+ self.write('\n')
+ self.exit()
+ except bdb.BdbQuit:
+ warn('The Python debugger has exited with a BdbQuit exception.\n'
+ 'Because of how pdb handles the stack, it is impossible\n'
+ 'for IPython to properly format this particular exception.\n'
+ 'IPython will resume normal operation.')
+ except:
+ # exceptions here are VERY RARE, but they can be triggered
+ # asynchronously by signal handlers, for example.
+ self.showtraceback()
+ else:
+ self.input_splitter.push(line)
+ more = self.input_splitter.push_accepts_more()
+ if (self.SyntaxTB.last_syntax_error and
+ self.autoedit_syntax):
+ self.edit_syntax_error()
+ if not more:
+ source_raw = self.input_splitter.source_reset()
+ self.run_cell(source_raw)
+
+
+ # Turn off the exit flag, so the mainloop can be restarted if desired
+ self.exit_now = False
25 IPython/zmq/blockingkernelmanager.py
View
@@ -16,6 +16,7 @@
# Stdlib
from Queue import Queue, Empty
+from threading import Event
# Our own
from IPython.utils import io
@@ -104,9 +105,31 @@ def get_msgs(self):
class BlockingStdInSocketChannel(StdInSocketChannel):
+ def __init__(self, context, session, address=None):
+ super(BlockingStdInSocketChannel, self).__init__(context, session, address)
+ self._in_queue = Queue()
+
def call_handlers(self, msg):
#io.rprint('[[Rep]]', msg) # dbg
- pass
+ self._in_queue.put(msg)
+
+ def get_msg(self, block=True, timeout=None):
+ "Gets a message if there is one that is ready."
+ return self._in_queue.get(block, timeout)
+
+ def get_msgs(self):
+ """Get all messages that are currently ready."""
+ msgs = []
+ while True:
+ try:
+ msgs.append(self.get_msg(block=False))
+ except Empty:
+ break
+ return msgs
+
+ def msg_ready(self):
+ "Is there a message that has been received?"
+ return not self._in_queue.empty()
class BlockingHBSocketChannel(HBSocketChannel):
Something went wrong with that request. Please try again.