kernelmanager.py
314 lines
| 10.8 KiB
| text/x-python
|
PythonLexer
epatters
|
r8471 | """ A kernel manager for in-process kernels. """ | ||
epatters
|
r8408 | |||
#----------------------------------------------------------------------------- | ||||
# 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. | ||||
Brian Granger
|
r9122 | from IPython.config.configurable import Configurable | ||
from IPython.utils.traitlets import Any, Instance, Type | ||||
MinRK
|
r9376 | from IPython.kernel.kernelmanagerabc import ( | ||
Brian Granger
|
r9121 | ShellChannelABC, IOPubChannelABC, | ||
HBChannelABC, StdInChannelABC, | ||||
KernelManagerABC | ||||
) | ||||
epatters
|
r8408 | |||
MinRK
|
r9375 | from .socket import DummySocket | ||
epatters
|
r8408 | #----------------------------------------------------------------------------- | ||
# Channel classes | ||||
#----------------------------------------------------------------------------- | ||||
epatters
|
r8471 | class InProcessChannel(object): | ||
Brian Granger
|
r9128 | """Base class for in-process channels.""" | ||
epatters
|
r8408 | |||
def __init__(self, manager): | ||||
epatters
|
r8471 | super(InProcessChannel, self).__init__() | ||
epatters
|
r8408 | 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.') | ||||
#-------------------------------------------------------------------------- | ||||
epatters
|
r8471 | # InProcessChannel interface | ||
epatters
|
r8408 | #-------------------------------------------------------------------------- | ||
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 | ||||
Brian Granger
|
r9120 | class InProcessShellChannel(InProcessChannel): | ||
MinRK
|
r9370 | """See `IPython.kernel.kernelmanager.ShellChannel` for docstrings.""" | ||
epatters
|
r8408 | |||
# flag for whether execute requests should be allowed to call raw_input | ||||
allow_stdin = True | ||||
epatters
|
r8411 | #-------------------------------------------------------------------------- | ||
# ShellChannel interface | ||||
#-------------------------------------------------------------------------- | ||||
epatters
|
r8408 | def execute(self, code, silent=False, store_history=True, | ||
user_variables=[], user_expressions={}, allow_stdin=None): | ||||
epatters
|
r8411 | 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'] | ||||
epatters
|
r8408 | |||
def complete(self, text, line, cursor_pos, block=None): | ||||
epatters
|
r8411 | 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'] | ||||
epatters
|
r8408 | |||
def object_info(self, oname, detail_level=0): | ||||
epatters
|
r8411 | 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'] | ||||
epatters
|
r8408 | |||
def history(self, raw=True, output=False, hist_access_type='range', **kwds): | ||||
epatters
|
r8411 | 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'] | ||||
epatters
|
r8408 | |||
def shutdown(self, restart=False): | ||||
# FIXME: What to do here? | ||||
epatters
|
r8471 | raise NotImplementedError('Cannot shutdown in-process kernel') | ||
epatters
|
r8408 | |||
epatters
|
r8411 | #-------------------------------------------------------------------------- | ||
# 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) | ||||
epatters
|
r8408 | |||
Brian Granger
|
r9120 | class InProcessIOPubChannel(InProcessChannel): | ||
MinRK
|
r9370 | """See `IPython.kernel.kernelmanager.IOPubChannel` for docstrings.""" | ||
epatters
|
r8408 | |||
def flush(self, timeout=1.0): | ||||
pass | ||||
Brian Granger
|
r9120 | class InProcessStdInChannel(InProcessChannel): | ||
MinRK
|
r9370 | """See `IPython.kernel.kernelmanager.StdInChannel` for docstrings.""" | ||
epatters
|
r8408 | |||
def input(self, string): | ||||
epatters
|
r8411 | kernel = self.manager.kernel | ||
if kernel is None: | ||||
raise RuntimeError('Cannot send input reply. No kernel exists.') | ||||
kernel.raw_input_str = string | ||||
epatters
|
r8408 | |||
Brian Granger
|
r9120 | class InProcessHBChannel(InProcessChannel): | ||
MinRK
|
r9370 | """See `IPython.kernel.kernelmanager.HBChannel` for docstrings.""" | ||
epatters
|
r8408 | |||
time_to_dead = 3.0 | ||||
def __init__(self, *args, **kwds): | ||||
Brian Granger
|
r9120 | super(InProcessHBChannel, self).__init__(*args, **kwds) | ||
epatters
|
r8408 | self._pause = True | ||
def pause(self): | ||||
self._pause = True | ||||
def unpause(self): | ||||
self._pause = False | ||||
def is_beating(self): | ||||
return not self._pause | ||||
#----------------------------------------------------------------------------- | ||||
# Main kernel manager class | ||||
#----------------------------------------------------------------------------- | ||||
Brian Granger
|
r9122 | class InProcessKernelManager(Configurable): | ||
Brian Granger
|
r9128 | """A manager for an in-process kernel. | ||
epatters
|
r8408 | |||
Brian Granger
|
r9128 | This class implements the interface of | ||
MinRK
|
r9354 | `IPython.kernel.kernelmanagerabc.KernelManagerABC` and allows | ||
Brian Granger
|
r9128 | (asynchronous) frontends to be used seamlessly with an in-process kernel. | ||
MinRK
|
r9370 | See `IPython.kernel.kernelmanager.KernelManager` for docstrings. | ||
epatters
|
r8408 | """ | ||
# The Session to use for building messages. | ||||
MinRK
|
r9372 | session = Instance('IPython.kernel.zmq.session.Session') | ||
epatters
|
r8408 | def _session_default(self): | ||
MinRK
|
r9372 | from IPython.kernel.zmq.session import Session | ||
epatters
|
r8408 | return Session(config=self.config) | ||
# The kernel process with which the KernelManager is communicating. | ||||
MinRK
|
r9375 | kernel = Instance('IPython.kernel.inprocess.ipkernel.InProcessKernel') | ||
epatters
|
r8408 | |||
# The classes to use for the various channels. | ||||
Brian Granger
|
r9120 | shell_channel_class = Type(InProcessShellChannel) | ||
iopub_channel_class = Type(InProcessIOPubChannel) | ||||
stdin_channel_class = Type(InProcessStdInChannel) | ||||
hb_channel_class = Type(InProcessHBChannel) | ||||
epatters
|
r8408 | |||
# Protected traits. | ||||
_shell_channel = Any | ||||
Brian Granger
|
r9120 | _iopub_channel = Any | ||
epatters
|
r8408 | _stdin_channel = Any | ||
_hb_channel = Any | ||||
#-------------------------------------------------------------------------- | ||||
Brian Granger
|
r9121 | # Channel management methods. | ||
epatters
|
r8408 | #-------------------------------------------------------------------------- | ||
Brian Granger
|
r9121 | def start_channels(self, shell=True, iopub=True, stdin=True, hb=True): | ||
epatters
|
r8408 | if shell: | ||
self.shell_channel.start() | ||||
Brian Granger
|
r9121 | if iopub: | ||
Brian Granger
|
r9120 | self.iopub_channel.start() | ||
epatters
|
r8408 | 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): | ||||
if self.shell_channel.is_alive(): | ||||
self.shell_channel.stop() | ||||
Brian Granger
|
r9120 | if self.iopub_channel.is_alive(): | ||
self.iopub_channel.stop() | ||||
epatters
|
r8408 | 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): | ||||
Brian Granger
|
r9120 | return (self.shell_channel.is_alive() or self.iopub_channel.is_alive() or | ||
epatters
|
r8408 | self.stdin_channel.is_alive() or self.hb_channel.is_alive()) | ||
Brian Granger
|
r9121 | @property | ||
def shell_channel(self): | ||||
if self._shell_channel is None: | ||||
self._shell_channel = self.shell_channel_class(self) | ||||
return self._shell_channel | ||||
@property | ||||
def iopub_channel(self): | ||||
if self._iopub_channel is None: | ||||
self._iopub_channel = self.iopub_channel_class(self) | ||||
return self._iopub_channel | ||||
@property | ||||
def stdin_channel(self): | ||||
if self._stdin_channel is None: | ||||
self._stdin_channel = self.stdin_channel_class(self) | ||||
return self._stdin_channel | ||||
@property | ||||
def hb_channel(self): | ||||
if self._hb_channel is None: | ||||
self._hb_channel = self.hb_channel_class(self) | ||||
return self._hb_channel | ||||
epatters
|
r8408 | #-------------------------------------------------------------------------- | ||
# Kernel management methods: | ||||
#-------------------------------------------------------------------------- | ||||
def start_kernel(self, **kwds): | ||||
MinRK
|
r9375 | from IPython.kernel.inprocess.ipkernel import InProcessKernel | ||
epatters
|
r8471 | self.kernel = InProcessKernel() | ||
epatters
|
r8408 | self.kernel.frontends.append(self) | ||
def shutdown_kernel(self): | ||||
Brian E. Granger
|
r9130 | self._kill_kernel() | ||
epatters
|
r8408 | |||
def restart_kernel(self, now=False, **kwds): | ||||
self.shutdown_kernel() | ||||
self.start_kernel(**kwds) | ||||
@property | ||||
def has_kernel(self): | ||||
return self.kernel is not None | ||||
Brian E. Granger
|
r9130 | def _kill_kernel(self): | ||
epatters
|
r8408 | self.kernel.frontends.remove(self) | ||
self.kernel = None | ||||
def interrupt_kernel(self): | ||||
epatters
|
r8471 | raise NotImplementedError("Cannot interrupt in-process kernel.") | ||
epatters
|
r8408 | |||
def signal_kernel(self, signum): | ||||
epatters
|
r8471 | raise NotImplementedError("Cannot signal in-process kernel.") | ||
epatters
|
r8408 | |||
@property | ||||
def is_alive(self): | ||||
return True | ||||
Brian Granger
|
r9121 | #----------------------------------------------------------------------------- | ||
# ABC Registration | ||||
#----------------------------------------------------------------------------- | ||||
epatters
|
r8408 | |||
Brian Granger
|
r9121 | ShellChannelABC.register(InProcessShellChannel) | ||
IOPubChannelABC.register(InProcessIOPubChannel) | ||||
HBChannelABC.register(InProcessHBChannel) | ||||
StdInChannelABC.register(InProcessStdInChannel) | ||||
KernelManagerABC.register(InProcessKernelManager) | ||||