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

@minrk
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
@omazapa omazapa basic kernelmanager and frontend wrote, support indentation but it do…
…nt run code yet
55a32de
@omazapa omazapa working in handlers 0a7161e
@omazapa omazapa working in tab completion 1e1f981
@omazapa omazapa completer not working fine 83f4e6f
@omazapa omazapa little bug fixed in kernelmanager's queues eb57363
@omazapa omazapa raw_input captured and working fine 6051b91
@omazapa omazapa -mworking in tab-completion 22843e0
@omazapa omazapa tab completion is not working yet, unknow error bfc9430
@takluyver takluyver Make readline tab-completion work in two-process terminal frontend. 2e6f2ac
@omazapa omazapa little bug fixed in pyout message print. cd21306
@omazapa omazapa bug fixed prompt count using differents clients 79048f7
@omazapa omazapa traceback support added be18348
@omazapa omazapa bug fixed in prompt count after traceback cb7e461
@takluyver takluyver Replace tabs with spaces 020805c
@takluyver takluyver Separate out frontend.zmqterminal package. 127d459
@takluyver takluyver zmqterminal frontend now uses IPythonInputSplitter, and non-ascii cha…
…racters work.
22a8fe3
@takluyver takluyver Minor tidying up of zmqterminal.frontend ef40f1f
@takluyver takluyver Minor tidy up of zmqterminal.completer 8d4a68b
@takluyver takluyver Refactor and simplification of zmqterminal. 3d4b21e
@takluyver takluyver Simplify handling of messaging in zmqterminal. 3b16045
@takluyver takluyver Nicer prompt formatting in zmqterminal, and use print_function. e3f9bc7
@minrk minrk rebased and updated to master d8d6bdf
@minrk 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
@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.

@ivanov
Owner

closing this one, since #864 will supersede it as

@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. @omazapa @minrk

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

    omazapa authored minrk committed
    …nt run code yet
  2. @omazapa @minrk

    working in handlers

    omazapa authored minrk committed
  3. @omazapa @minrk

    working in tab completion

    omazapa authored minrk committed
  4. @omazapa @minrk

    completer not working fine

    omazapa authored minrk committed
  5. @omazapa @minrk

    little bug fixed in kernelmanager's queues

    omazapa authored minrk committed
  6. @omazapa @minrk

    raw_input captured and working fine

    omazapa authored minrk committed
  7. @omazapa @minrk

    -mworking in tab-completion

    omazapa authored minrk committed
  8. @omazapa @minrk

    tab completion is not working yet, unknow error

    omazapa authored minrk committed
  9. @takluyver @minrk
  10. @omazapa @minrk

    little bug fixed in pyout message print.

    omazapa authored minrk committed
  11. @omazapa @minrk

    bug fixed prompt count using differents clients

    omazapa authored minrk committed
  12. @omazapa @minrk

    traceback support added

    omazapa authored minrk committed
  13. @omazapa @minrk

    bug fixed in prompt count after traceback

    omazapa authored minrk committed
  14. @takluyver @minrk

    Replace tabs with spaces

    takluyver authored minrk committed
  15. @takluyver @minrk

    Separate out frontend.zmqterminal package.

    takluyver authored minrk committed
  16. @takluyver @minrk

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

    takluyver authored minrk committed
    …racters work.
  17. @takluyver @minrk

    Minor tidying up of zmqterminal.frontend

    takluyver authored minrk committed
  18. @takluyver @minrk

    Minor tidy up of zmqterminal.completer

    takluyver authored minrk committed
  19. @takluyver @minrk

    Refactor and simplification of zmqterminal.

    takluyver authored minrk committed
  20. @takluyver @minrk

    Simplify handling of messaging in zmqterminal.

    takluyver authored minrk committed
  21. @takluyver @minrk
  22. @minrk

    rebased and updated to master

    minrk authored
  23. @minrk

    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.
View
3  IPython/frontend/terminal/ipapp.py
@@ -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.
View
0  IPython/frontend/zmqterminal/__init__.py
No changes.
View
161 IPython/frontend/zmqterminal/app.py
@@ -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()
+
View
44 IPython/frontend/zmqterminal/completer.py
@@ -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)
View
253 IPython/frontend/zmqterminal/frontend.py
@@ -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()
View
252 IPython/frontend/zmqterminal/interactiveshell.py
@@ -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
View
25 IPython/zmq/blockingkernelmanager.py
@@ -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.