Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #2724 from pberkes/embedded-ipython-v2

In-process kernel support (take 3)
  • Loading branch information...
commit 98e3fdd6dca45d8402fc469b1ac22fd8fcc340cd 2 parents ff7178b + 775571c
@ellisonbg ellisonbg authored
View
260 IPython/frontend/qt/base_kernelmanager.py
@@ -0,0 +1,260 @@
+""" Defines a KernelManager that provides signals and slots.
+"""
+
+# System library imports.
+from IPython.external.qt import QtCore
+
+# IPython imports.
+from IPython.utils.traitlets import HasTraits, Type
+from util import MetaQObjectHasTraits, SuperQObject
+
+
+class ChannelQObject(SuperQObject):
+
+ # Emitted when the channel is started.
+ started = QtCore.Signal()
+
+ # Emitted when the channel is stopped.
+ stopped = QtCore.Signal()
+
+ #---------------------------------------------------------------------------
+ # Channel interface
+ #---------------------------------------------------------------------------
+
+ def start(self):
+ """ Reimplemented to emit signal.
+ """
+ super(ChannelQObject, self).start()
+ self.started.emit()
+
+ def stop(self):
+ """ Reimplemented to emit signal.
+ """
+ super(ChannelQObject, self).stop()
+ self.stopped.emit()
+
+ #---------------------------------------------------------------------------
+ # InProcessChannel interface
+ #---------------------------------------------------------------------------
+
+ def call_handlers_later(self, *args, **kwds):
+ """ Call the message handlers later.
+ """
+ do_later = lambda: self.call_handlers(*args, **kwds)
+ QtCore.QTimer.singleShot(0, do_later)
+
+ def process_events(self):
+ """ Process any pending GUI events.
+ """
+ QtCore.QCoreApplication.instance().processEvents()
+
+
+class QtShellChannelMixin(ChannelQObject):
+
+ # Emitted when any message is received.
+ message_received = QtCore.Signal(object)
+
+ # Emitted when a reply has been received for the corresponding request
+ # type.
+ execute_reply = QtCore.Signal(object)
+ complete_reply = QtCore.Signal(object)
+ object_info_reply = QtCore.Signal(object)
+ history_reply = QtCore.Signal(object)
+
+ # Emitted when the first reply comes back.
+ first_reply = QtCore.Signal()
+
+ # Used by the first_reply signal logic to determine if a reply is the
+ # first.
+ _handlers_called = False
+
+ #---------------------------------------------------------------------------
+ # 'ShellSocketChannel' interface
+ #---------------------------------------------------------------------------
+
+ def call_handlers(self, msg):
+ """ Reimplemented to emit signals instead of making callbacks.
+ """
+ # Emit the generic signal.
+ self.message_received.emit(msg)
+
+ # Emit signals for specialized message types.
+ msg_type = msg['header']['msg_type']
+ signal = getattr(self, msg_type, None)
+ if signal:
+ signal.emit(msg)
+
+ if not self._handlers_called:
+ self.first_reply.emit()
+ self._handlers_called = True
+
+ #---------------------------------------------------------------------------
+ # 'QtShellChannelMixin' interface
+ #---------------------------------------------------------------------------
+
+ def reset_first_reply(self):
+ """ Reset the first_reply signal to fire again on the next reply.
+ """
+ self._handlers_called = False
+
+
+class QtSubChannelMixin(ChannelQObject):
+
+ # Emitted when any message is received.
+ message_received = QtCore.Signal(object)
+
+ # Emitted when a message of type 'stream' is received.
+ stream_received = QtCore.Signal(object)
+
+ # Emitted when a message of type 'pyin' is received.
+ pyin_received = QtCore.Signal(object)
+
+ # Emitted when a message of type 'pyout' is received.
+ pyout_received = QtCore.Signal(object)
+
+ # Emitted when a message of type 'pyerr' is received.
+ pyerr_received = QtCore.Signal(object)
+
+ # Emitted when a message of type 'display_data' is received
+ display_data_received = QtCore.Signal(object)
+
+ # Emitted when a crash report message is received from the kernel's
+ # last-resort sys.excepthook.
+ crash_received = QtCore.Signal(object)
+
+ # Emitted when a shutdown is noticed.
+ shutdown_reply_received = QtCore.Signal(object)
+
+ #---------------------------------------------------------------------------
+ # 'SubSocketChannel' interface
+ #---------------------------------------------------------------------------
+
+ def call_handlers(self, msg):
+ """ Reimplemented to emit signals instead of making callbacks.
+ """
+ # Emit the generic signal.
+ self.message_received.emit(msg)
+ # Emit signals for specialized message types.
+ msg_type = msg['header']['msg_type']
+ signal = getattr(self, msg_type + '_received', None)
+ if signal:
+ signal.emit(msg)
+ elif msg_type in ('stdout', 'stderr'):
+ self.stream_received.emit(msg)
+
+ def flush(self):
+ """ Reimplemented to ensure that signals are dispatched immediately.
+ """
+ super(QtSubChannelMixin, self).flush()
+ QtCore.QCoreApplication.instance().processEvents()
+
+
+class QtStdInChannelMixin(ChannelQObject):
+
+ # Emitted when any message is received.
+ message_received = QtCore.Signal(object)
+
+ # Emitted when an input request is received.
+ input_requested = QtCore.Signal(object)
+
+ #---------------------------------------------------------------------------
+ # 'StdInSocketChannel' interface
+ #---------------------------------------------------------------------------
+
+ def call_handlers(self, msg):
+ """ Reimplemented to emit signals instead of making callbacks.
+ """
+ # Emit the generic signal.
+ self.message_received.emit(msg)
+
+ # Emit signals for specialized message types.
+ msg_type = msg['header']['msg_type']
+ if msg_type == 'input_request':
+ self.input_requested.emit(msg)
+
+
+class QtHBChannelMixin(ChannelQObject):
+
+ # Emitted when the kernel has died.
+ kernel_died = QtCore.Signal(object)
+
+ #---------------------------------------------------------------------------
+ # 'HBSocketChannel' interface
+ #---------------------------------------------------------------------------
+
+ def call_handlers(self, since_last_heartbeat):
+ """ Reimplemented to emit signals instead of making callbacks.
+ """
+ # Emit the generic signal.
+ self.kernel_died.emit(since_last_heartbeat)
+
+
+class QtKernelManagerMixin(HasTraits, SuperQObject):
+ """ A KernelManager that provides signals and slots.
+ """
+
+ __metaclass__ = MetaQObjectHasTraits
+
+ # Emitted when the kernel manager has started listening.
+ started_kernel = QtCore.Signal()
+
+ # Emitted when the kernel manager has started listening.
+ started_channels = QtCore.Signal()
+
+ # Emitted when the kernel manager has stopped listening.
+ stopped_channels = QtCore.Signal()
+
+ # Use Qt-specific channel classes that emit signals.
+ sub_channel_class = Type(QtSubChannelMixin)
+ shell_channel_class = Type(QtShellChannelMixin)
+ stdin_channel_class = Type(QtStdInChannelMixin)
+ hb_channel_class = Type(QtHBChannelMixin)
+
+ #---------------------------------------------------------------------------
+ # 'KernelManager' interface
+ #---------------------------------------------------------------------------
+
+ #------ Kernel process management ------------------------------------------
+
+ def start_kernel(self, *args, **kw):
+ """ Reimplemented for proper heartbeat management.
+ """
+ if self._shell_channel is not None:
+ self._shell_channel.reset_first_reply()
+ super(QtKernelManagerMixin, self).start_kernel(*args, **kw)
+ self.started_kernel.emit()
+
+ #------ Channel management -------------------------------------------------
+
+ def start_channels(self, *args, **kw):
+ """ Reimplemented to emit signal.
+ """
+ super(QtKernelManagerMixin, self).start_channels(*args, **kw)
+ self.started_channels.emit()
+
+ def stop_channels(self):
+ """ Reimplemented to emit signal.
+ """
+ super(QtKernelManagerMixin, self).stop_channels()
+ self.stopped_channels.emit()
+
+ @property
+ def shell_channel(self):
+ """ Reimplemented for proper heartbeat management.
+ """
+ if self._shell_channel is None:
+ self._shell_channel = super(QtKernelManagerMixin,self).shell_channel
+ self._shell_channel.first_reply.connect(self._first_reply)
+ return self._shell_channel
+
+ #---------------------------------------------------------------------------
+ # Protected interface
+ #---------------------------------------------------------------------------
+
+ def _first_reply(self):
+ """ Unpauses the heartbeat channel when the first reply is received on
+ the execute channel. Note that this will *not* start the heartbeat
+ channel if it is not already running!
+ """
+ if self._hb_channel is not None:
+ self._hb_channel.unpause()
View
33 IPython/frontend/qt/inprocess_kernelmanager.py
@@ -0,0 +1,33 @@
+""" Defines an in-process KernelManager with signals and slots.
+"""
+
+# Local imports.
+from IPython.inprocess.kernelmanager import \
+ ShellInProcessChannel, SubInProcessChannel, StdInInProcessChannel, \
+ HBInProcessChannel, InProcessKernelManager
+from IPython.utils.traitlets import Type
+from base_kernelmanager import QtShellChannelMixin, QtSubChannelMixin, \
+ QtStdInChannelMixin, QtHBChannelMixin, QtKernelManagerMixin
+
+
+class QtShellInProcessChannel(QtShellChannelMixin, ShellInProcessChannel):
+ pass
+
+class QtSubInProcessChannel(QtSubChannelMixin, SubInProcessChannel):
+ pass
+
+class QtStdInInProcessChannel(QtStdInChannelMixin, StdInInProcessChannel):
+ pass
+
+class QtHBInProcessChannel(QtHBChannelMixin, HBInProcessChannel):
+ pass
+
+
+class QtInProcessKernelManager(QtKernelManagerMixin, InProcessKernelManager):
+ """ An in-process KernelManager with signals and slots.
+ """
+
+ sub_channel_class = Type(QtSubInProcessChannel)
+ shell_channel_class = Type(QtShellInProcessChannel)
+ stdin_channel_class = Type(QtStdInInProcessChannel)
+ hb_channel_class = Type(QtHBInProcessChannel)
View
243 IPython/frontend/qt/kernelmanager.py
@@ -1,247 +1,32 @@
""" Defines a KernelManager that provides signals and slots.
"""
-# System library imports.
-from IPython.external.qt import QtCore
-
-# IPython imports.
+# Local imports.
from IPython.utils.traitlets import Type
-from IPython.zmq.kernelmanager import KernelManager, SubSocketChannel, \
- ShellSocketChannel, StdInSocketChannel, HBSocketChannel
-from util import MetaQObjectHasTraits, SuperQObject
-
-
-class SocketChannelQObject(SuperQObject):
-
- # Emitted when the channel is started.
- started = QtCore.Signal()
-
- # Emitted when the channel is stopped.
- stopped = QtCore.Signal()
-
- #---------------------------------------------------------------------------
- # 'ZMQSocketChannel' interface
- #---------------------------------------------------------------------------
-
- def start(self):
- """ Reimplemented to emit signal.
- """
- super(SocketChannelQObject, self).start()
- self.started.emit()
-
- def stop(self):
- """ Reimplemented to emit signal.
- """
- super(SocketChannelQObject, self).stop()
- self.stopped.emit()
-
-
-class QtShellSocketChannel(SocketChannelQObject, ShellSocketChannel):
-
- # Emitted when any message is received.
- message_received = QtCore.Signal(object)
-
- # Emitted when a reply has been received for the corresponding request
- # type.
- execute_reply = QtCore.Signal(object)
- complete_reply = QtCore.Signal(object)
- object_info_reply = QtCore.Signal(object)
- history_reply = QtCore.Signal(object)
-
- # Emitted when the first reply comes back.
- first_reply = QtCore.Signal()
-
- # Used by the first_reply signal logic to determine if a reply is the
- # first.
- _handlers_called = False
-
- #---------------------------------------------------------------------------
- # 'ShellSocketChannel' interface
- #---------------------------------------------------------------------------
-
- def call_handlers(self, msg):
- """ Reimplemented to emit signals instead of making callbacks.
- """
- # Emit the generic signal.
- self.message_received.emit(msg)
-
- # Emit signals for specialized message types.
- msg_type = msg['header']['msg_type']
- signal = getattr(self, msg_type, None)
- if signal:
- signal.emit(msg)
-
- if not self._handlers_called:
- self.first_reply.emit()
- self._handlers_called = True
-
- #---------------------------------------------------------------------------
- # 'QtShellSocketChannel' interface
- #---------------------------------------------------------------------------
-
- def reset_first_reply(self):
- """ Reset the first_reply signal to fire again on the next reply.
- """
- self._handlers_called = False
-
-
-class QtSubSocketChannel(SocketChannelQObject, SubSocketChannel):
-
- # Emitted when any message is received.
- message_received = QtCore.Signal(object)
-
- # Emitted when a message of type 'stream' is received.
- stream_received = QtCore.Signal(object)
-
- # Emitted when a message of type 'pyin' is received.
- pyin_received = QtCore.Signal(object)
-
- # Emitted when a message of type 'pyout' is received.
- pyout_received = QtCore.Signal(object)
-
- # Emitted when a message of type 'pyerr' is received.
- pyerr_received = QtCore.Signal(object)
+from IPython.zmq.kernelmanager import ShellSocketChannel, SubSocketChannel, \
+ StdInSocketChannel, HBSocketChannel, KernelManager
+from base_kernelmanager import QtShellChannelMixin, QtSubChannelMixin, \
+ QtStdInChannelMixin, QtHBChannelMixin, QtKernelManagerMixin
- # Emitted when a message of type 'display_data' is received
- display_data_received = QtCore.Signal(object)
- # Emitted when a crash report message is received from the kernel's
- # last-resort sys.excepthook.
- crash_received = QtCore.Signal(object)
+class QtShellSocketChannel(QtShellChannelMixin, ShellSocketChannel):
+ pass
- # Emitted when a shutdown is noticed.
- shutdown_reply_received = QtCore.Signal(object)
+class QtSubSocketChannel(QtSubChannelMixin, SubSocketChannel):
+ pass
- #---------------------------------------------------------------------------
- # 'SubSocketChannel' interface
- #---------------------------------------------------------------------------
+class QtStdInSocketChannel(QtStdInChannelMixin, StdInSocketChannel):
+ pass
- def call_handlers(self, msg):
- """ Reimplemented to emit signals instead of making callbacks.
- """
- # Emit the generic signal.
- self.message_received.emit(msg)
- # Emit signals for specialized message types.
- msg_type = msg['header']['msg_type']
- signal = getattr(self, msg_type + '_received', None)
- if signal:
- signal.emit(msg)
- elif msg_type in ('stdout', 'stderr'):
- self.stream_received.emit(msg)
+class QtHBSocketChannel(QtHBChannelMixin, HBSocketChannel):
+ pass
- def flush(self):
- """ Reimplemented to ensure that signals are dispatched immediately.
- """
- super(QtSubSocketChannel, self).flush()
- QtCore.QCoreApplication.instance().processEvents()
-
-class QtStdInSocketChannel(SocketChannelQObject, StdInSocketChannel):
-
- # Emitted when any message is received.
- message_received = QtCore.Signal(object)
-
- # Emitted when an input request is received.
- input_requested = QtCore.Signal(object)
-
- #---------------------------------------------------------------------------
- # 'StdInSocketChannel' interface
- #---------------------------------------------------------------------------
-
- def call_handlers(self, msg):
- """ Reimplemented to emit signals instead of making callbacks.
- """
- # Emit the generic signal.
- self.message_received.emit(msg)
-
- # Emit signals for specialized message types.
- msg_type = msg['header']['msg_type']
- if msg_type == 'input_request':
- self.input_requested.emit(msg)
-
-
-class QtHBSocketChannel(SocketChannelQObject, HBSocketChannel):
-
- # Emitted when the kernel has died.
- kernel_died = QtCore.Signal(object)
-
- #---------------------------------------------------------------------------
- # 'HBSocketChannel' interface
- #---------------------------------------------------------------------------
-
- def call_handlers(self, since_last_heartbeat):
- """ Reimplemented to emit signals instead of making callbacks.
- """
- # Emit the generic signal.
- self.kernel_died.emit(since_last_heartbeat)
-
-
-class QtKernelManager(KernelManager, SuperQObject):
+class QtKernelManager(QtKernelManagerMixin, KernelManager):
""" A KernelManager that provides signals and slots.
"""
- __metaclass__ = MetaQObjectHasTraits
-
- # Emitted when the kernel manager has started listening.
- started_kernel = QtCore.Signal()
-
- # Emitted when the kernel manager has started listening.
- started_channels = QtCore.Signal()
-
- # Emitted when the kernel manager has stopped listening.
- stopped_channels = QtCore.Signal()
-
- # Use Qt-specific channel classes that emit signals.
sub_channel_class = Type(QtSubSocketChannel)
shell_channel_class = Type(QtShellSocketChannel)
stdin_channel_class = Type(QtStdInSocketChannel)
hb_channel_class = Type(QtHBSocketChannel)
-
- #---------------------------------------------------------------------------
- # 'KernelManager' interface
- #---------------------------------------------------------------------------
-
- #------ Kernel process management ------------------------------------------
-
- def start_kernel(self, *args, **kw):
- """ Reimplemented for proper heartbeat management.
- """
- if self._shell_channel is not None:
- self._shell_channel.reset_first_reply()
- super(QtKernelManager, self).start_kernel(*args, **kw)
- self.started_kernel.emit()
-
- #------ Channel management -------------------------------------------------
-
- def start_channels(self, *args, **kw):
- """ Reimplemented to emit signal.
- """
- super(QtKernelManager, self).start_channels(*args, **kw)
- self.started_channels.emit()
-
- def stop_channels(self):
- """ Reimplemented to emit signal.
- """
- super(QtKernelManager, self).stop_channels()
- self.stopped_channels.emit()
-
- @property
- def shell_channel(self):
- """ Reimplemented for proper heartbeat management.
- """
- if self._shell_channel is None:
- self._shell_channel = super(QtKernelManager, self).shell_channel
- self._shell_channel.first_reply.connect(self._first_reply)
- return self._shell_channel
-
- #---------------------------------------------------------------------------
- # Protected interface
- #---------------------------------------------------------------------------
-
- def _first_reply(self):
- """ Unpauses the heartbeat channel when the first reply is received on
- the execute channel. Note that this will *not* start the heartbeat
- channel if it is not already running!
- """
- if self._hb_channel is not None:
- self._hb_channel.unpause()
View
2  IPython/frontend/qt/util.py
@@ -56,7 +56,7 @@ class SuperQObject(QtCore.QObject):
they don't inherit QObject.)
This class is primarily useful for attaching signals to existing non-Qt
- classes. See QtKernelManager for an example.
+ classes. See QtKernelManagerMixin for an example.
"""
def __new__(cls, *args, **kw):
View
0  IPython/inprocess/__init__.py
No changes.
View
87 IPython/inprocess/blockingkernelmanager.py
@@ -0,0 +1,87 @@
+""" Implements a fully blocking kernel manager.
+
+Useful for test suites and blocking terminal interfaces.
+"""
+#-----------------------------------------------------------------------------
+# Copyright (C) 2012 The IPython Development Team
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING.txt, distributed as part of this software.
+#-----------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+from __future__ import print_function
+
+# Standard library imports.
+import Queue
+from threading import Event
+
+# Local imports.
+from IPython.utils.io import raw_print
+from IPython.utils.traitlets import Type
+from kernelmanager import InProcessKernelManager, ShellInProcessChannel, \
+ SubInProcessChannel, StdInInProcessChannel
+
+#-----------------------------------------------------------------------------
+# Utility classes
+#-----------------------------------------------------------------------------
+
+class BlockingChannelMixin(object):
+
+ def __init__(self, *args, **kwds):
+ super(BlockingChannelMixin, self).__init__(*args, **kwds)
+ self._in_queue = Queue.Queue()
+
+ def call_handlers(self, msg):
+ 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 Queue.Empty:
+ break
+ return msgs
+
+ def msg_ready(self):
+ """ Is there a message that has been received? """
+ return not self._in_queue.empty()
+
+#-----------------------------------------------------------------------------
+# Blocking kernel manager
+#-----------------------------------------------------------------------------
+
+class BlockingShellInProcessChannel(BlockingChannelMixin, ShellInProcessChannel):
+ pass
+
+class BlockingSubInProcessChannel(BlockingChannelMixin, SubInProcessChannel):
+ pass
+
+class BlockingStdInInProcessChannel(BlockingChannelMixin, StdInInProcessChannel):
+
+ def call_handlers(self, msg):
+ """ Overridden for the in-process channel.
+
+ This methods simply calls raw_input directly.
+ """
+ msg_type = msg['header']['msg_type']
+ if msg_type == 'input_request':
+ _raw_input = self.manager.kernel._sys_raw_input
+ prompt = msg['content']['prompt']
+ raw_print(prompt, end='')
+ self.input(_raw_input())
+
+class BlockingInProcessKernelManager(InProcessKernelManager):
+
+ # The classes to use for the various channels.
+ shell_channel_class = Type(BlockingShellInProcessChannel)
+ sub_channel_class = Type(BlockingSubInProcessChannel)
+ stdin_channel_class = Type(BlockingStdInInProcessChannel)
View
176 IPython/inprocess/ipkernel.py
@@ -0,0 +1,176 @@
+""" An in-process kernel. """
+
+#-----------------------------------------------------------------------------
+# Copyright (C) 2012 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
+#-----------------------------------------------------------------------------
+
+# Standard library imports
+from contextlib import contextmanager
+import logging
+import sys
+
+# Local imports.
+from IPython.core.interactiveshell import InteractiveShellABC
+from IPython.inprocess.socket import DummySocket
+from IPython.utils.jsonutil import json_clean
+from IPython.utils.traitlets import Any, Enum, Instance, List, Type
+from IPython.zmq.ipkernel import Kernel
+from IPython.zmq.zmqshell import ZMQInteractiveShell
+
+#-----------------------------------------------------------------------------
+# Main kernel class
+#-----------------------------------------------------------------------------
+
+class InProcessKernel(Kernel):
+
+ #-------------------------------------------------------------------------
+ # InProcessKernel interface
+ #-------------------------------------------------------------------------
+
+ # The frontends connected to this kernel.
+ frontends = List(
+ Instance('IPython.inprocess.kernelmanager.InProcessKernelManager'))
+
+ # The GUI environment that the kernel is running under. This need not be
+ # specified for the normal operation for the kernel, but is required for
+ # IPython's GUI support (including pylab). The default is 'inline' because
+ # it is safe under all GUI toolkits.
+ gui = Enum(('tk', 'gtk', 'wx', 'qt', 'qt4', 'inline'),
+ default_value='inline')
+
+ raw_input_str = Any()
+ stdout = Any()
+ stderr = Any()
+
+ #-------------------------------------------------------------------------
+ # Kernel interface
+ #-------------------------------------------------------------------------
+
+ shell_class = Type()
+ shell_streams = List()
+ control_stream = Any()
+ iopub_socket = Instance(DummySocket, ())
+ stdin_socket = Instance(DummySocket, ())
+
+ def __init__(self, **traits):
+ # When an InteractiveShell is instantiated by our base class, it binds
+ # the current values of sys.stdout and sys.stderr.
+ with self._redirected_io():
+ super(InProcessKernel, self).__init__(**traits)
+
+ self.iopub_socket.on_trait_change(self._io_dispatch, 'message_sent')
+ self.shell.kernel = self
+
+ def execute_request(self, stream, ident, parent):
+ """ Override for temporary IO redirection. """
+ with self._redirected_io():
+ super(InProcessKernel, self).execute_request(stream, ident, parent)
+
+ def start(self):
+ """ Override registration of dispatchers for streams. """
+ self.shell.exit_now = False
+
+ def _abort_queue(self, stream):
+ """ The in-process kernel doesn't abort requests. """
+ pass
+
+ def _raw_input(self, prompt, ident, parent):
+ # Flush output before making the request.
+ self.raw_input_str = None
+ sys.stderr.flush()
+ sys.stdout.flush()
+
+ # Send the input request.
+ content = json_clean(dict(prompt=prompt))
+ msg = self.session.msg(u'input_request', content, parent)
+ for frontend in self.frontends:
+ if frontend.session.session == parent['header']['session']:
+ frontend.stdin_channel.call_handlers(msg)
+ break
+ else:
+ logging.error('No frontend found for raw_input request')
+ return str()
+
+ # Await a response.
+ while self.raw_input_str is None:
+ frontend.stdin_channel.process_events()
+ return self.raw_input_str
+
+ #-------------------------------------------------------------------------
+ # Protected interface
+ #-------------------------------------------------------------------------
+
+ @contextmanager
+ def _redirected_io(self):
+ """ Temporarily redirect IO to the kernel.
+ """
+ sys_stdout, sys_stderr = sys.stdout, sys.stderr
+ sys.stdout, sys.stderr = self.stdout, self.stderr
+ yield
+ sys.stdout, sys.stderr = sys_stdout, sys_stderr
+
+ #------ Trait change handlers --------------------------------------------
+
+ def _io_dispatch(self):
+ """ Called when a message is sent to the IO socket.
+ """
+ ident, msg = self.session.recv(self.iopub_socket, copy=False)
+ for frontend in self.frontends:
+ frontend.sub_channel.call_handlers(msg)
+
+ #------ Trait initializers -----------------------------------------------
+
+ def _log_default(self):
+ return logging.getLogger(__name__)
+
+ def _session_default(self):
+ from IPython.zmq.session import Session
+ return Session(config=self.config)
+
+ def _shell_class_default(self):
+ return InProcessInteractiveShell
+
+ def _stdout_default(self):
+ from IPython.zmq.iostream import OutStream
+ return OutStream(self.session, self.iopub_socket, u'stdout')
+
+ def _stderr_default(self):
+ from IPython.zmq.iostream import OutStream
+ return OutStream(self.session, self.iopub_socket, u'stderr')
+
+#-----------------------------------------------------------------------------
+# Interactive shell subclass
+#-----------------------------------------------------------------------------
+
+class InProcessInteractiveShell(ZMQInteractiveShell):
+
+ kernel = Instance('IPython.inprocess.ipkernel.InProcessKernel')
+
+ #-------------------------------------------------------------------------
+ # InteractiveShell interface
+ #-------------------------------------------------------------------------
+
+ def enable_gui(self, gui=None):
+ """ Enable GUI integration for the kernel.
+ """
+ from IPython.zmq.eventloops import enable_gui
+ if not gui:
+ gui = self.kernel.gui
+ enable_gui(gui, kernel=self.kernel)
+
+ def enable_pylab(self, gui=None, import_all=True, welcome_message=False):
+ """ Activate pylab support at runtime.
+ """
+ if not gui:
+ gui = self.kernel.gui
+ super(InProcessInteractiveShell, self).enable_pylab(gui, import_all,
+ welcome_message)
+
+InteractiveShellABC.register(InProcessInteractiveShell)
View
443 IPython/inprocess/kernelmanager.py
@@ -0,0 +1,443 @@
+""" A kernel manager for in-process kernels. """
+
+#-----------------------------------------------------------------------------
+# Copyright (C) 2012 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
+#-----------------------------------------------------------------------------
+
+# Local imports.
+from IPython.config.loader import Config
+from IPython.inprocess.socket import DummySocket
+from IPython.utils.traitlets import HasTraits, Any, Instance, Type
+
+#-----------------------------------------------------------------------------
+# Channel classes
+#-----------------------------------------------------------------------------
+
+class InProcessChannel(object):
+ """ Base class for in-process channels.
+ """
+
+ def __init__(self, manager):
+ super(InProcessChannel, self).__init__()
+ self.manager = manager
+ self._is_alive = False
+
+ #--------------------------------------------------------------------------
+ # Channel interface
+ #--------------------------------------------------------------------------
+
+ def is_alive(self):
+ return self._is_alive
+
+ def start(self):
+ self._is_alive = True
+
+ def stop(self):
+ self._is_alive = False
+
+ def call_handlers(self, msg):
+ """ This method is called in the main thread when a message arrives.
+
+ Subclasses should override this method to handle incoming messages.
+ """
+ raise NotImplementedError('call_handlers must be defined in a subclass.')
+
+ #--------------------------------------------------------------------------
+ # InProcessChannel interface
+ #--------------------------------------------------------------------------
+
+ def call_handlers_later(self, *args, **kwds):
+ """ Call the message handlers later.
+
+ The default implementation just calls the handlers immediately, but this
+ method exists so that GUI toolkits can defer calling the handlers until
+ after the event loop has run, as expected by GUI frontends.
+ """
+ self.call_handlers(*args, **kwds)
+
+ def process_events(self):
+ """ Process any pending GUI events.
+
+ This method will be never be called from a frontend without an event
+ loop (e.g., a terminal frontend).
+ """
+ raise NotImplementedError
+
+
+class ShellInProcessChannel(InProcessChannel):
+ """The DEALER channel for issues request/replies to the kernel.
+ """
+
+ # flag for whether execute requests should be allowed to call raw_input
+ allow_stdin = True
+
+ #--------------------------------------------------------------------------
+ # ShellChannel interface
+ #--------------------------------------------------------------------------
+
+ def execute(self, code, silent=False, store_history=True,
+ user_variables=[], user_expressions={}, allow_stdin=None):
+ """Execute code in the kernel.
+
+ Parameters
+ ----------
+ code : str
+ A string of Python code.
+
+ silent : bool, optional (default False)
+ If set, the kernel will execute the code as quietly possible, and
+ will force store_history to be False.
+
+ store_history : bool, optional (default True)
+ If set, the kernel will store command history. This is forced
+ to be False if silent is True.
+
+ user_variables : list, optional
+ A list of variable names to pull from the user's namespace. They
+ will come back as a dict with these names as keys and their
+ :func:`repr` as values.
+
+ user_expressions : dict, optional
+ A dict mapping names to expressions to be evaluated in the user's
+ dict. The expression values are returned as strings formatted using
+ :func:`repr`.
+
+ allow_stdin : bool, optional (default self.allow_stdin)
+ Flag for whether the kernel can send stdin requests to frontends.
+
+ Some frontends (e.g. the Notebook) do not support stdin requests.
+ If raw_input is called from code executed from such a frontend, a
+ StdinNotImplementedError will be raised.
+
+ Returns
+ -------
+ The msg_id of the message sent.
+ """
+ if allow_stdin is None:
+ allow_stdin = self.allow_stdin
+ content = dict(code=code, silent=silent, store_history=store_history,
+ user_variables=user_variables,
+ user_expressions=user_expressions,
+ allow_stdin=allow_stdin)
+ msg = self.manager.session.msg('execute_request', content)
+ self._dispatch_to_kernel(msg)
+ return msg['header']['msg_id']
+
+ def complete(self, text, line, cursor_pos, block=None):
+ """Tab complete text in the kernel's namespace.
+
+ Parameters
+ ----------
+ text : str
+ The text to complete.
+ line : str
+ The full line of text that is the surrounding context for the
+ text to complete.
+ cursor_pos : int
+ The position of the cursor in the line where the completion was
+ requested.
+ block : str, optional
+ The full block of code in which the completion is being requested.
+
+ Returns
+ -------
+ The msg_id of the message sent.
+ """
+ content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
+ msg = self.manager.session.msg('complete_request', content)
+ self._dispatch_to_kernel(msg)
+ return msg['header']['msg_id']
+
+ def object_info(self, oname, detail_level=0):
+ """Get metadata information about an object.
+
+ Parameters
+ ----------
+ oname : str
+ A string specifying the object name.
+ detail_level : int, optional
+ The level of detail for the introspection (0-2)
+
+ Returns
+ -------
+ The msg_id of the message sent.
+ """
+ content = dict(oname=oname, detail_level=detail_level)
+ msg = self.manager.session.msg('object_info_request', content)
+ self._dispatch_to_kernel(msg)
+ return msg['header']['msg_id']
+
+ def history(self, raw=True, output=False, hist_access_type='range', **kwds):
+ """Get entries from the history list.
+
+ Parameters
+ ----------
+ raw : bool
+ If True, return the raw input.
+ output : bool
+ If True, then return the output as well.
+ hist_access_type : str
+ 'range' (fill in session, start and stop params), 'tail' (fill in n)
+ or 'search' (fill in pattern param).
+
+ session : int
+ For a range request, the session from which to get lines. Session
+ numbers are positive integers; negative ones count back from the
+ current session.
+ start : int
+ The first line number of a history range.
+ stop : int
+ The final (excluded) line number of a history range.
+
+ n : int
+ The number of lines of history to get for a tail request.
+
+ pattern : str
+ The glob-syntax pattern for a search request.
+
+ Returns
+ -------
+ The msg_id of the message sent.
+ """
+ content = dict(raw=raw, output=output,
+ hist_access_type=hist_access_type, **kwds)
+ msg = self.manager.session.msg('history_request', content)
+ self._dispatch_to_kernel(msg)
+ return msg['header']['msg_id']
+
+ def shutdown(self, restart=False):
+ """ Request an immediate kernel shutdown.
+
+ A dummy method for the in-process kernel.
+ """
+ # FIXME: What to do here?
+ raise NotImplementedError('Cannot shutdown in-process kernel')
+
+ #--------------------------------------------------------------------------
+ # Protected interface
+ #--------------------------------------------------------------------------
+
+ def _dispatch_to_kernel(self, msg):
+ """ Send a message to the kernel and handle a reply.
+ """
+ kernel = self.manager.kernel
+ if kernel is None:
+ raise RuntimeError('Cannot send request. No kernel exists.')
+
+ stream = DummySocket()
+ self.manager.session.send(stream, msg)
+ msg_parts = stream.recv_multipart()
+ kernel.dispatch_shell(stream, msg_parts)
+
+ idents, reply_msg = self.manager.session.recv(stream, copy=False)
+ self.call_handlers_later(reply_msg)
+
+
+class SubInProcessChannel(InProcessChannel):
+ """The SUB channel which listens for messages that the kernel publishes.
+ """
+
+ def flush(self, timeout=1.0):
+ """ Immediately processes all pending messages on the SUB channel.
+
+ A dummy method for the in-process kernel.
+ """
+ pass
+
+
+class StdInInProcessChannel(InProcessChannel):
+ """ A reply channel to handle raw_input requests that the kernel makes. """
+
+ def input(self, string):
+ """ Send a string of raw input to the kernel.
+ """
+ kernel = self.manager.kernel
+ if kernel is None:
+ raise RuntimeError('Cannot send input reply. No kernel exists.')
+ kernel.raw_input_str = string
+
+
+class HBInProcessChannel(InProcessChannel):
+ """ A dummy heartbeat channel. """
+
+ time_to_dead = 3.0
+
+ def __init__(self, *args, **kwds):
+ super(HBInProcessChannel, self).__init__(*args, **kwds)
+ self._pause = True
+
+ def pause(self):
+ """ Pause the heartbeat. """
+ self._pause = True
+
+ def unpause(self):
+ """ Unpause the heartbeat. """
+ self._pause = False
+
+ def is_beating(self):
+ """ Is the heartbeat running and responsive (and not paused). """
+ return not self._pause
+
+
+#-----------------------------------------------------------------------------
+# Main kernel manager class
+#-----------------------------------------------------------------------------
+
+class InProcessKernelManager(HasTraits):
+ """ A manager for an in-process kernel.
+
+ This class implements most of the interface of
+ ``IPython.zmq.kernelmanager.KernelManager`` and allows (asynchronous)
+ frontends to be used seamlessly with an in-process kernel.
+ """
+ # Config object for passing to child configurables
+ config = Instance(Config)
+
+ # The Session to use for building messages.
+ session = Instance('IPython.zmq.session.Session')
+ def _session_default(self):
+ from IPython.zmq.session import Session
+ return Session(config=self.config)
+
+ # The kernel process with which the KernelManager is communicating.
+ kernel = Instance('IPython.inprocess.ipkernel.InProcessKernel')
+
+ # The classes to use for the various channels.
+ shell_channel_class = Type(ShellInProcessChannel)
+ sub_channel_class = Type(SubInProcessChannel)
+ stdin_channel_class = Type(StdInInProcessChannel)
+ hb_channel_class = Type(HBInProcessChannel)
+
+ # Protected traits.
+ _shell_channel = Any
+ _sub_channel = Any
+ _stdin_channel = Any
+ _hb_channel = Any
+
+ #--------------------------------------------------------------------------
+ # Channel management methods:
+ #--------------------------------------------------------------------------
+
+ def start_channels(self, shell=True, sub=True, stdin=True, hb=True):
+ """ Starts the channels for this kernel.
+ """
+ if shell:
+ self.shell_channel.start()
+ if sub:
+ self.sub_channel.start()
+ if stdin:
+ self.stdin_channel.start()
+ self.shell_channel.allow_stdin = True
+ else:
+ self.shell_channel.allow_stdin = False
+ if hb:
+ self.hb_channel.start()
+
+ def stop_channels(self):
+ """ Stops all the running channels for this kernel.
+ """
+ if self.shell_channel.is_alive():
+ self.shell_channel.stop()
+ if self.sub_channel.is_alive():
+ self.sub_channel.stop()
+ if self.stdin_channel.is_alive():
+ self.stdin_channel.stop()
+ if self.hb_channel.is_alive():
+ self.hb_channel.stop()
+
+ @property
+ def channels_running(self):
+ """ Are any of the channels created and running? """
+ return (self.shell_channel.is_alive() or self.sub_channel.is_alive() or
+ self.stdin_channel.is_alive() or self.hb_channel.is_alive())
+
+ #--------------------------------------------------------------------------
+ # Kernel management methods:
+ #--------------------------------------------------------------------------
+
+ def start_kernel(self, **kwds):
+ """ Starts a kernel process and configures the manager to use it.
+ """
+ from IPython.inprocess.ipkernel import InProcessKernel
+ self.kernel = InProcessKernel()
+ self.kernel.frontends.append(self)
+
+ def shutdown_kernel(self):
+ """ Attempts to the stop the kernel process cleanly. If the kernel
+ cannot be stopped and the kernel is local, it is killed.
+ """
+ self.kill_kernel()
+
+ def restart_kernel(self, now=False, **kwds):
+ """ Restarts a kernel with the arguments that were used to launch it.
+
+ The 'now' parameter is ignored.
+ """
+ self.shutdown_kernel()
+ self.start_kernel(**kwds)
+
+ @property
+ def has_kernel(self):
+ """ Returns whether a kernel process has been specified for the kernel
+ manager.
+ """
+ return self.kernel is not None
+
+ def kill_kernel(self):
+ """ Kill the running kernel.
+ """
+ self.kernel.frontends.remove(self)
+ self.kernel = None
+
+ def interrupt_kernel(self):
+ """ Interrupts the kernel. """
+ raise NotImplementedError("Cannot interrupt in-process kernel.")
+
+ def signal_kernel(self, signum):
+ """ Sends a signal to the kernel. """
+ raise NotImplementedError("Cannot signal in-process kernel.")
+
+ @property
+ def is_alive(self):
+ """ Is the kernel process still running? """
+ return True
+
+ #--------------------------------------------------------------------------
+ # Channels used for communication with the kernel:
+ #--------------------------------------------------------------------------
+
+ @property
+ def shell_channel(self):
+ """Get the REQ socket channel object to make requests of the kernel."""
+ if self._shell_channel is None:
+ self._shell_channel = self.shell_channel_class(self)
+ return self._shell_channel
+
+ @property
+ def sub_channel(self):
+ """Get the SUB socket channel object."""
+ if self._sub_channel is None:
+ self._sub_channel = self.sub_channel_class(self)
+ return self._sub_channel
+
+ @property
+ def stdin_channel(self):
+ """Get the REP socket channel object to handle stdin (raw_input)."""
+ if self._stdin_channel is None:
+ self._stdin_channel = self.stdin_channel_class(self)
+ return self._stdin_channel
+
+ @property
+ def hb_channel(self):
+ """Get the heartbeat socket channel object to check that the
+ kernel is alive."""
+ if self._hb_channel is None:
+ self._hb_channel = self.hb_channel_class(self)
+ return self._hb_channel
View
63 IPython/inprocess/socket.py
@@ -0,0 +1,63 @@
+""" Defines a dummy socket implementing (part of) the zmq.Socket interface. """
+
+#-----------------------------------------------------------------------------
+# Copyright (C) 2012 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
+#-----------------------------------------------------------------------------
+
+# Standard library imports.
+import abc
+import Queue
+
+# System library imports.
+import zmq
+
+# Local imports.
+from IPython.utils.traitlets import HasTraits, Instance, Int
+
+#-----------------------------------------------------------------------------
+# Generic socket interface
+#-----------------------------------------------------------------------------
+
+class SocketABC(object):
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractmethod
+ def recv_multipart(self, flags=0, copy=True, track=False):
+ raise NotImplementedError
+
+ @abc.abstractmethod
+ def send_multipart(self, msg_parts, flags=0, copy=True, track=False):
+ raise NotImplementedError
+
+SocketABC.register(zmq.Socket)
+
+#-----------------------------------------------------------------------------
+# Dummy socket class
+#-----------------------------------------------------------------------------
+
+class DummySocket(HasTraits):
+ """ A dummy socket implementing (part of) the zmq.Socket interface. """
+
+ queue = Instance(Queue.Queue, ())
+ message_sent = Int(0) # Should be an Event
+
+ #-------------------------------------------------------------------------
+ # Socket interface
+ #-------------------------------------------------------------------------
+
+ def recv_multipart(self, flags=0, copy=True, track=False):
+ return self.queue.get_nowait()
+
+ def send_multipart(self, msg_parts, flags=0, copy=True, track=False):
+ msg_parts = map(zmq.Message, msg_parts)
+ self.queue.put_nowait(msg_parts)
+ self.message_sent += 1
+
+SocketABC.register(DummySocket)
View
0  IPython/inprocess/tests/__init__.py
No changes.
View
89 IPython/inprocess/tests/test_kernel.py
@@ -0,0 +1,89 @@
+#-------------------------------------------------------------------------------
+# Copyright (C) 2012 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
+
+# Standard library imports
+from StringIO import StringIO
+import sys
+import unittest
+
+# Local imports
+from IPython.inprocess.blockingkernelmanager import \
+ BlockingInProcessKernelManager
+from IPython.inprocess.ipkernel import InProcessKernel
+from IPython.testing.decorators import skipif_not_matplotlib
+from IPython.utils.io import capture_output
+from IPython.utils import py3compat
+
+#-----------------------------------------------------------------------------
+# Test case
+#-----------------------------------------------------------------------------
+
+class InProcessKernelTestCase(unittest.TestCase):
+
+ @skipif_not_matplotlib
+ def test_pylab(self):
+ """ Does pylab work in the in-process kernel?
+ """
+ km = BlockingInProcessKernelManager()
+ km.start_kernel()
+ km.shell_channel.execute('%pylab')
+ msg = get_stream_message(km)
+ self.assert_('Welcome to pylab' in msg['content']['data'])
+
+ def test_raw_input(self):
+ """ Does the in-process kernel handle raw_input correctly?
+ """
+ km = BlockingInProcessKernelManager()
+ km.start_kernel()
+
+ io = StringIO('foobar\n')
+ sys_stdin = sys.stdin
+ sys.stdin = io
+ try:
+ if py3compat.PY3:
+ km.shell_channel.execute('x = input()')
+ else:
+ km.shell_channel.execute('x = raw_input()')
+ finally:
+ sys.stdin = sys_stdin
+ self.assertEqual(km.kernel.shell.user_ns.get('x'), 'foobar')
+
+ def test_stdout(self):
+ """ Does the in-process kernel correctly capture IO?
+ """
+ kernel = InProcessKernel()
+
+ with capture_output() as io:
+ kernel.shell.run_cell('print("foo")')
+ self.assertEqual(io.stdout, 'foo\n')
+
+ km = BlockingInProcessKernelManager(kernel=kernel)
+ kernel.frontends.append(km)
+ km.shell_channel.execute('print("bar")')
+ msg = get_stream_message(km)
+ self.assertEqual(msg['content']['data'], 'bar\n')
+
+#-----------------------------------------------------------------------------
+# Utility functions
+#-----------------------------------------------------------------------------
+
+def get_stream_message(kernel_manager, timeout=5):
+ """ Gets a single stream message synchronously from the sub channel.
+ """
+ while True:
+ msg = kernel_manager.sub_channel.get_msg(timeout=timeout)
+ if msg['header']['msg_type'] == 'stream':
+ return msg
+
+
+if __name__ == '__main__':
+ unittest.main()
View
102 IPython/inprocess/tests/test_kernelmanager.py
@@ -0,0 +1,102 @@
+#-------------------------------------------------------------------------------
+# Copyright (C) 2012 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
+
+# Standard library imports
+import unittest
+
+# Local imports
+from IPython.inprocess.blockingkernelmanager import \
+ BlockingInProcessKernelManager
+from IPython.inprocess.ipkernel import InProcessKernel
+
+#-----------------------------------------------------------------------------
+# Test case
+#-----------------------------------------------------------------------------
+
+class InProcessKernelManagerTestCase(unittest.TestCase):
+
+ def test_inteface(self):
+ """ Does the in-process kernel manager implement the basic KM interface?
+ """
+ km = BlockingInProcessKernelManager()
+ self.assert_(not km.channels_running)
+ self.assert_(not km.has_kernel)
+
+ km.start_channels()
+ self.assert_(km.channels_running)
+
+ km.start_kernel()
+ self.assert_(km.has_kernel)
+ self.assert_(km.kernel is not None)
+
+ old_kernel = km.kernel
+ km.restart_kernel()
+ self.assert_(km.kernel is not None)
+ self.assertNotEquals(km.kernel, old_kernel)
+
+ km.shutdown_kernel()
+ self.assert_(not km.has_kernel)
+
+ self.assertRaises(NotImplementedError, km.interrupt_kernel)
+ self.assertRaises(NotImplementedError, km.signal_kernel, 9)
+
+ km.stop_channels()
+ self.assert_(not km.channels_running)
+
+ def test_execute(self):
+ """ Does executing code in an in-process kernel work?
+ """
+ km = BlockingInProcessKernelManager()
+ km.start_kernel()
+ km.shell_channel.execute('foo = 1')
+ self.assertEquals(km.kernel.shell.user_ns['foo'], 1)
+
+ def test_complete(self):
+ """ Does requesting completion from an in-process kernel work?
+ """
+ km = BlockingInProcessKernelManager()
+ km.start_kernel()
+ km.kernel.shell.push({'my_bar': 0, 'my_baz': 1})
+ km.shell_channel.complete('my_ba', 'my_ba', 5)
+ msg = km.shell_channel.get_msg()
+ self.assertEquals(msg['header']['msg_type'], 'complete_reply')
+ self.assertEquals(sorted(msg['content']['matches']),
+ ['my_bar', 'my_baz'])
+
+ def test_object_info(self):
+ """ Does requesting object information from an in-process kernel work?
+ """
+ km = BlockingInProcessKernelManager()
+ km.start_kernel()
+ km.kernel.shell.user_ns['foo'] = 1
+ km.shell_channel.object_info('foo')
+ msg = km.shell_channel.get_msg()
+ self.assertEquals(msg['header']['msg_type'], 'object_info_reply')
+ self.assertEquals(msg['content']['name'], 'foo')
+ self.assertEquals(msg['content']['type_name'], 'int')
+
+ def test_history(self):
+ """ Does requesting history from an in-process kernel work?
+ """
+ km = BlockingInProcessKernelManager()
+ km.start_kernel()
+ km.shell_channel.execute('%who')
+ km.shell_channel.history(hist_access_type='tail', n=1)
+ msg = km.shell_channel.get_msgs()[-1]
+ self.assertEquals(msg['header']['msg_type'], 'history_reply')
+ history = msg['content']['history']
+ self.assertEquals(len(history), 1)
+ self.assertEquals(history[0][2], '%who')
+
+
+if __name__ == '__main__':
+ unittest.main()
View
15 IPython/testing/iptest.py
@@ -441,7 +441,7 @@ def make_runners(inc_slow=False):
# Packages to be tested via nose, that only depend on the stdlib
nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib',
- 'testing', 'utils', 'nbformat' ]
+ 'testing', 'utils', 'nbformat', 'inprocess' ]
if have['zmq']:
nose_pkg_names.append('zmq')
@@ -504,8 +504,17 @@ def run_iptest():
# use our plugin for doctesting. It will remove the standard doctest plugin
# if it finds it enabled
plugins = [IPythonDoctest(make_exclude()), KnownFailure()]
- # We need a global ipython running in this process
- globalipapp.start_ipython()
+
+ # We need a global ipython running in this process, but the special
+ # in-process group spawns its own IPython kernels, so for *that* group we
+ # must avoid also opening the global one (otherwise there's a conflict of
+ # singletons). Ultimately the solution to this problem is to refactor our
+ # assumptions about what needs to be a singleton and what doesn't (app
+ # objects should, individual shells shouldn't). But for now, this
+ # workaround allows the test suite for the inprocess module to complete.
+ if not 'IPython.inprocess' in sys.argv:
+ globalipapp.start_ipython()
+
# Now nose can run
TestProgram(argv=argv, addplugins=plugins)
View
129 IPython/zmq/blockingkernelmanager.py
@@ -1,9 +1,9 @@
-"""Implement a fully blocking kernel manager.
+""" Implements a fully blocking kernel manager.
Useful for test suites and blocking terminal interfaces.
"""
#-----------------------------------------------------------------------------
-# Copyright (C) 2010-2011 The IPython Development Team
+# Copyright (C) 2010-2012 The IPython Development Team
#
# Distributed under the terms of the BSD License. The full license is in
# the file COPYING.txt, distributed as part of this software.
@@ -12,125 +12,25 @@
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
-from __future__ import print_function
-# Stdlib
-from Queue import Queue, Empty
-from threading import Event
-
-# Our own
-from IPython.utils import io
+# Local imports.
+from IPython.inprocess.blockingkernelmanager import BlockingChannelMixin
from IPython.utils.traitlets import Type
-
-from .kernelmanager import (KernelManager, SubSocketChannel, HBSocketChannel,
- ShellSocketChannel, StdInSocketChannel)
+from kernelmanager import KernelManager, SubSocketChannel, HBSocketChannel, \
+ ShellSocketChannel, StdInSocketChannel
#-----------------------------------------------------------------------------
-# Functions and classes
+# Blocking kernel manager
#-----------------------------------------------------------------------------
-class BlockingSubSocketChannel(SubSocketChannel):
-
- def __init__(self, context, session, address=None):
- super(BlockingSubSocketChannel, self).__init__(context, session,
- address)
- self._in_queue = Queue()
-
- def call_handlers(self, msg):
- #io.rprint('[[Sub]]', msg) # dbg
- self._in_queue.put(msg)
-
- def msg_ready(self):
- """Is there a message that has been received?"""
- if self._in_queue.qsize() == 0:
- return False
- else:
- return True
-
- def get_msg(self, block=True, timeout=None):
- """Get a message if there is one that is ready."""
- if block and timeout is None:
- # never use timeout=None, because get
- # becomes uninterruptible
- timeout = 1e6
- 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
-
-
-class BlockingShellSocketChannel(ShellSocketChannel):
+class BlockingSubSocketChannel(BlockingChannelMixin, SubSocketChannel):
+ pass
- def __init__(self, context, session, address=None):
- super(BlockingShellSocketChannel, self).__init__(context, session,
- address)
- self._in_queue = Queue()
-
- def call_handlers(self, msg):
- #io.rprint('[[Shell]]', msg) # dbg
- self._in_queue.put(msg)
-
- def msg_ready(self):
- """Is there a message that has been received?"""
- if self._in_queue.qsize() == 0:
- return False
- else:
- return True
-
- def get_msg(self, block=True, timeout=None):
- """Get a message if there is one that is ready."""
- if block and timeout is None:
- # never use timeout=None, because get
- # becomes uninterruptible
- timeout = 1e6
- 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
-
-
-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
- 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 BlockingShellSocketChannel(BlockingChannelMixin, ShellSocketChannel):
+ pass
+class BlockingStdInSocketChannel(BlockingChannelMixin, StdInSocketChannel):
+ pass
class BlockingHBSocketChannel(HBSocketChannel):
@@ -140,10 +40,9 @@ class BlockingHBSocketChannel(HBSocketChannel):
time_to_dead = 1.
def call_handlers(self, since_last_heartbeat):
- """pause beating on missed heartbeat"""
+ """ Pause beating on missed heartbeat. """
pass
-
class BlockingKernelManager(KernelManager):
# The classes to use for the various channels.
View
5 IPython/zmq/datapub.py
@@ -13,10 +13,9 @@
#-----------------------------------------------------------------------------
from IPython.config import Configurable
-
+from IPython.inprocess.socket import SocketABC
from IPython.utils.jsonutil import json_clean
from IPython.utils.traitlets import Instance, Dict, CBytes
-
from IPython.zmq.serialize import serialize_object
from IPython.zmq.session import Session, extract_header
@@ -29,7 +28,7 @@ class ZMQDataPublisher(Configurable):
topic = topic = CBytes(b'datapub')
session = Instance(Session)
- pub_socket = Instance('zmq.Socket')
+ pub_socket = Instance(SocketABC)
parent_header = Dict({})
def set_parent(self, parent):
View
3  IPython/zmq/displayhook.py
@@ -2,6 +2,7 @@
import sys
from IPython.core.displayhook import DisplayHook
+from IPython.inprocess.socket import SocketABC
from IPython.utils.jsonutil import encode_images
from IPython.utils.traitlets import Instance, Dict
from session import extract_header, Session
@@ -37,7 +38,7 @@ class ZMQShellDisplayHook(DisplayHook):
topic=None
session = Instance(Session)
- pub_socket = Instance('zmq.Socket')
+ pub_socket = Instance(SocketABC)
parent_header = Dict({})
def set_parent(self, parent):
View
52 IPython/zmq/ipkernel.py
@@ -48,7 +48,8 @@
from IPython.utils.frame import extract_module_locals
from IPython.utils.jsonutil import json_clean
from IPython.utils.traitlets import (
- Any, Instance, Float, Dict, CaselessStrEnum, List, Set, Integer, Unicode
+ Any, Instance, Float, Dict, CaselessStrEnum, List, Set, Integer, Unicode,
+ Type
)
from entry_point import base_launch_kernel
@@ -81,6 +82,8 @@ def _eventloop_changed(self, name, old, new):
loop.add_timeout(time.time()+0.1, self.enter_eventloop)
shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
+ shell_class = Type(ZMQInteractiveShell)
+
session = Instance(Session)
profile_dir = Instance('IPython.core.profiledir.ProfileDir')
shell_streams = List()
@@ -134,7 +137,11 @@ def _ident_default(self):
# This is a dict of port number that the kernel is listening on. It is set
# by record_ports and used by connect_request.
_recorded_ports = Dict()
-
+
+ # A reference to the Python builtin 'raw_input' function.
+ # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
+ _sys_raw_input = Any()
+
# set of aborted msg_ids
aborted = Set()
@@ -143,7 +150,7 @@ def __init__(self, **kwargs):
super(Kernel, self).__init__(**kwargs)
# Initialize the InteractiveShell subclass
- self.shell = ZMQInteractiveShell.instance(config=self.config,
+ self.shell = self.shell_class.instance(config=self.config,
profile_dir = self.profile_dir,
user_module = self.user_module,
user_ns = self.user_ns,
@@ -356,8 +363,10 @@ def execute_request(self, stream, ident, parent):
raw_input = lambda prompt='' : self._no_raw_input()
if py3compat.PY3:
+ self._sys_raw_input = __builtin__.input
__builtin__.input = raw_input
else:
+ self._sys_raw_input = __builtin__.raw_input
__builtin__.raw_input = raw_input
# Set the parent message of the display hook and out streams.
@@ -389,6 +398,12 @@ def execute_request(self, stream, ident, parent):
reply_content.update(shell._showtraceback(etype, evalue, tb_list))
else:
status = u'ok'
+ finally:
+ # Restore raw_input.
+ if py3compat.PY3:
+ __builtin__.input = self._sys_raw_input
+ else:
+ __builtin__.raw_input = self._sys_raw_input
reply_content[u'status'] = status
@@ -663,7 +678,6 @@ def clear_request(self, stream, idents, parent):
# Protected interface
#---------------------------------------------------------------------------
-
def _wrap_exception(self, method=None):
# import here, because _wrap_exception is only used in parallel,
# and parallel has higher min pyzmq version
@@ -758,36 +772,6 @@ def _complete(self, msg):
cpos = len(c['line'])
return self.shell.complete(c['text'], c['line'], cpos)
- def _object_info(self, context):
- symbol, leftover = self._symbol_from_context(context)
- if symbol is not None and not leftover:
- doc = getattr(symbol, '__doc__', '')
- else:
- doc = ''
- object_info = dict(docstring = doc)
- return object_info
-
- def _symbol_from_context(self, context):
- if not context:
- return None, context
-
- base_symbol_string = context[0]
- symbol = self.shell.user_ns.get(base_symbol_string, None)
- if symbol is None:
- symbol = __builtin__.__dict__.get(base_symbol_string, None)
- if symbol is None:
- return None, context
-
- context = context[1:]
- for i, name in enumerate(context):
- new_symbol = getattr(symbol, name, None)
- if new_symbol is None:
- return symbol, context[i:]
- else:
- symbol = new_symbol
-
- return symbol, []
-
def _at_shutdown(self):
"""Actions taken at shutdown by the kernel, called by python's atexit.
"""
View
8 IPython/zmq/kernelmanager.py
@@ -659,6 +659,8 @@ def _context_default(self):
# The Session to use for communication with the kernel.
session = Instance(Session)
+ def _session_default(self):
+ return Session(config=self.config)
# The kernel process with which the KernelManager is communicating.
kernel = Instance(Popen)
@@ -691,16 +693,10 @@ def _ip_changed(self, name, old, new):
_stdin_channel = Any
_hb_channel = Any
_connection_file_written=Bool(False)
-
- def __init__(self, **kwargs):
- super(KernelManager, self).__init__(**kwargs)
- if self.session is None:
- self.session = Session(config=self.config)
def __del__(self):
self.cleanup_connection_file()
-
#--------------------------------------------------------------------------
# Channel management methods:
#--------------------------------------------------------------------------
View
8 IPython/zmq/session.py
@@ -557,11 +557,9 @@ def send(self, stream, msg_or_type, content=None, parent=None, ident=None,
msg : dict
The constructed message.
"""
-
- if not isinstance(stream, (zmq.Socket, ZMQStream)):
- raise TypeError("stream must be Socket or ZMQStream, not %r"%type(stream))
- elif track and isinstance(stream, ZMQStream):
- raise TypeError("ZMQStream cannot track messages")
+ if not isinstance(stream, zmq.Socket):
+ # ZMQStreams and dummy sockets do not support tracking.
+ track = False
if isinstance(msg_or_type, (Message, dict)):
# We got a Message or message dict, not a msg_type so don't
View
3  IPython/zmq/zmqshell.py
@@ -34,6 +34,7 @@
from IPython.core.magics import MacroToEdit, CodeMagics
from IPython.core.magic import magics_class, line_magic, Magics
from IPython.core.payloadpage import install_payload_page
+from IPython.inprocess.socket import SocketABC
from IPython.lib.kernel import (
get_connection_file, get_connection_info, connect_qtconsole
)
@@ -57,7 +58,7 @@ class ZMQDisplayPublisher(DisplayPublisher):
"""A display publisher that publishes data using a ZeroMQ PUB socket."""
session = Instance(Session)
- pub_socket = Instance('zmq.Socket')
+ pub_socket = Instance(SocketABC)
parent_header = Dict({})
topic = CBytes(b'displaypub')
View
58 docs/examples/frontend/inprocess_qtconsole.py
@@ -0,0 +1,58 @@
+""" A simple example of using the Qt console with an in-process kernel.
+
+We shall see how to create the frontend widget, create an in-process kernel,
+push Python objects into the kernel's namespace, and execute code in the
+kernel, both directly and via the frontend widget.
+"""
+
+from IPython.inprocess.ipkernel import InProcessKernel
+from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
+from IPython.frontend.qt.inprocess_kernelmanager import QtInProcessKernelManager
+from IPython.lib import guisupport
+
+
+def main():
+ app = guisupport.get_app_qt4()
+
+ # Create a kernel.
+ #
+ # Setting the GUI is not necessary for the normal operation of the kernel,
+ # but it is used for IPython GUI's integration, particularly in pylab. By
+ # default, the inline backend is used, which is safe under all toolkits.
+ #
+ # WARNING: Under no circumstances should another GUI toolkit, like wx, be
+ # used when running a Qt application. This will lead to unexpected behavior,
+ # including segfaults.
+ kernel = InProcessKernel(gui='qt4')
+
+ # Populate the kernel's namespace.
+ kernel.shell.push({'x': 0, 'y': 1, 'z': 2})
+
+ # Create a kernel manager for the frontend and register it with the kernel.
+ km = QtInProcessKernelManager(kernel=kernel)
+ km.start_channels()
+ kernel.frontends.append(km)
+
+ # Create the Qt console frontend.
+ control = RichIPythonWidget()
+ control.exit_requested.connect(app.quit)
+ control.kernel_manager = km
+ control.show()
+
+ # Execute some code directly. Note where the output appears.
+ kernel.shell.run_cell('print "x=%r, y=%r, z=%r" % (x,y,z)')
+
+ # Execute some code through the frontend (once the event loop is
+ # running). Again, note where the output appears.
+ do_later(control.execute, '%who')
+
+ guisupport.start_event_loop_qt4(app)
+
+
+def do_later(func, *args, **kwds):
+ from IPython.external.qt import QtCore
+ QtCore.QTimer.singleShot(0, lambda: func(*args, **kwds))
+
+
+if __name__ == '__main__':
+ main()
View
11 docs/source/whatsnew/development.txt
@@ -12,6 +12,17 @@ especially intenting/deindenting blocks that is now bound to Ctrl+] and ctr+[
``_render_traceback_()`` method which returns a list of strings, each
containing one line of the traceback.
+In-process kernels
+------------------
+
+The Python-language frontends, particularly the Qt console, may now communicate
+with in-process kernels, in addition to the traditional out-of-process
+kernels. An in-process kernel permits direct access to the kernel namespace,
+which is necessary in some applications. It should be understood, however, that
+the in-process kernel is not robust to bad user input and will block the main
+(GUI) thread while executing. Developers must decide on a case-by-case basis
+whether this tradeoff is appropriate for their application.
+
Backwards incompatible changes
------------------------------
Please sign in to comment.
Something went wrong with that request. Please try again.