first comit
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
from .blocking import BlockingInProcessKernelClient
|
||||
from .channels import InProcessChannel, InProcessHBChannel
|
||||
from .client import InProcessKernelClient
|
||||
from .manager import InProcessKernelManager
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,108 @@
|
||||
""" Implements a fully blocking kernel client.
|
||||
|
||||
Useful for test suites and blocking terminal interfaces.
|
||||
"""
|
||||
import sys
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (C) 2012 The IPython Development Team
|
||||
#
|
||||
# Distributed under the terms of the BSD License. The full license is in
|
||||
# the file LICENSE, distributed as part of this software.
|
||||
# -----------------------------------------------------------------------------
|
||||
from queue import Empty, Queue
|
||||
|
||||
# IPython imports
|
||||
from traitlets import Type
|
||||
|
||||
# Local imports
|
||||
from .channels import InProcessChannel
|
||||
from .client import InProcessKernelClient
|
||||
|
||||
|
||||
class BlockingInProcessChannel(InProcessChannel):
|
||||
"""A blocking in-process channel."""
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
"""Initialize the channel."""
|
||||
super().__init__(*args, **kwds)
|
||||
self._in_queue: Queue[object] = Queue()
|
||||
|
||||
def call_handlers(self, msg):
|
||||
"""Call the handlers for a message."""
|
||||
self._in_queue.put(msg)
|
||||
|
||||
def get_msg(self, block=True, timeout=None):
|
||||
"""Gets a message if there is one that is ready."""
|
||||
if timeout is None:
|
||||
# Queue.get(timeout=None) has stupid uninteruptible
|
||||
# behavior, so wait for a week instead
|
||||
timeout = 604800
|
||||
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 BlockingInProcessStdInChannel(BlockingInProcessChannel):
|
||||
"""A blocking in-process stdin channel."""
|
||||
|
||||
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.client.kernel._sys_raw_input
|
||||
prompt = msg["content"]["prompt"]
|
||||
print(prompt, end="", file=sys.__stdout__)
|
||||
sys.__stdout__.flush()
|
||||
self.client.input(_raw_input())
|
||||
|
||||
|
||||
class BlockingInProcessKernelClient(InProcessKernelClient):
|
||||
"""A blocking in-process kernel client."""
|
||||
|
||||
# The classes to use for the various channels.
|
||||
shell_channel_class = Type(BlockingInProcessChannel) # type:ignore[arg-type]
|
||||
iopub_channel_class = Type(BlockingInProcessChannel) # type:ignore[arg-type]
|
||||
stdin_channel_class = Type(BlockingInProcessStdInChannel) # type:ignore[arg-type]
|
||||
|
||||
def wait_for_ready(self):
|
||||
"""Wait for kernel info reply on shell channel."""
|
||||
while True:
|
||||
self.kernel_info()
|
||||
try:
|
||||
msg = self.shell_channel.get_msg(block=True, timeout=1)
|
||||
except Empty:
|
||||
pass
|
||||
else:
|
||||
if msg["msg_type"] == "kernel_info_reply":
|
||||
# Checking that IOPub is connected. If it is not connected, start over.
|
||||
try:
|
||||
self.iopub_channel.get_msg(block=True, timeout=0.2)
|
||||
except Empty:
|
||||
pass
|
||||
else:
|
||||
self._handle_kernel_info_reply(msg)
|
||||
break
|
||||
|
||||
# Flush IOPub channel
|
||||
while True:
|
||||
try:
|
||||
msg = self.iopub_channel.get_msg(block=True, timeout=0.2)
|
||||
print(msg["msg_type"])
|
||||
except Empty:
|
||||
break
|
||||
@@ -0,0 +1,109 @@
|
||||
"""A kernel client for in-process kernels."""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from typing import List
|
||||
|
||||
from jupyter_client.channelsabc import HBChannelABC
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Channel classes
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class InProcessChannel:
|
||||
"""Base class for in-process channels."""
|
||||
|
||||
proxy_methods: List[object] = []
|
||||
|
||||
def __init__(self, client=None):
|
||||
"""Initialize the channel."""
|
||||
super().__init__()
|
||||
self.client = client
|
||||
self._is_alive = False
|
||||
|
||||
def is_alive(self):
|
||||
"""Test if the channel is alive."""
|
||||
return self._is_alive
|
||||
|
||||
def start(self):
|
||||
"""Start the channel."""
|
||||
self._is_alive = True
|
||||
|
||||
def stop(self):
|
||||
"""Stop the channel."""
|
||||
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.
|
||||
"""
|
||||
msg = "call_handlers must be defined in a subclass."
|
||||
raise NotImplementedError(msg)
|
||||
|
||||
def flush(self, timeout=1.0):
|
||||
"""Flush the channel."""
|
||||
|
||||
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 InProcessHBChannel:
|
||||
"""A dummy heartbeat channel interface for in-process kernels.
|
||||
|
||||
Normally we use the heartbeat to check that the kernel process is alive.
|
||||
When the kernel is in-process, that doesn't make sense, but clients still
|
||||
expect this interface.
|
||||
"""
|
||||
|
||||
time_to_dead = 3.0
|
||||
|
||||
def __init__(self, client=None):
|
||||
"""Initialize the channel."""
|
||||
super().__init__()
|
||||
self.client = client
|
||||
self._is_alive = False
|
||||
self._pause = True
|
||||
|
||||
def is_alive(self):
|
||||
"""Test if the channel is alive."""
|
||||
return self._is_alive
|
||||
|
||||
def start(self):
|
||||
"""Start the channel."""
|
||||
self._is_alive = True
|
||||
|
||||
def stop(self):
|
||||
"""Stop the channel."""
|
||||
self._is_alive = False
|
||||
|
||||
def pause(self):
|
||||
"""Pause the channel."""
|
||||
self._pause = True
|
||||
|
||||
def unpause(self):
|
||||
"""Unpause the channel."""
|
||||
self._pause = False
|
||||
|
||||
def is_beating(self):
|
||||
"""Test if the channel is beating."""
|
||||
return not self._pause
|
||||
|
||||
|
||||
HBChannelABC.register(InProcessHBChannel)
|
||||
223
venv/lib/python3.10/site-packages/ipykernel/inprocess/client.py
Normal file
223
venv/lib/python3.10/site-packages/ipykernel/inprocess/client.py
Normal file
@@ -0,0 +1,223 @@
|
||||
"""A client 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 LICENSE, distributed as part of this software.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Imports
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
import asyncio
|
||||
|
||||
from jupyter_client.client import KernelClient
|
||||
from jupyter_client.clientabc import KernelClientABC
|
||||
from jupyter_core.utils import run_sync
|
||||
|
||||
# IPython imports
|
||||
from traitlets import Instance, Type, default
|
||||
|
||||
# Local imports
|
||||
from .channels import InProcessChannel, InProcessHBChannel
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Main kernel Client class
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class InProcessKernelClient(KernelClient):
|
||||
"""A client for an in-process kernel.
|
||||
|
||||
This class implements the interface of
|
||||
`jupyter_client.clientabc.KernelClientABC` and allows
|
||||
(asynchronous) frontends to be used seamlessly with an in-process kernel.
|
||||
|
||||
See `jupyter_client.client.KernelClient` for docstrings.
|
||||
"""
|
||||
|
||||
# The classes to use for the various channels.
|
||||
shell_channel_class = Type(InProcessChannel) # type:ignore[arg-type]
|
||||
iopub_channel_class = Type(InProcessChannel) # type:ignore[arg-type]
|
||||
stdin_channel_class = Type(InProcessChannel) # type:ignore[arg-type]
|
||||
control_channel_class = Type(InProcessChannel) # type:ignore[arg-type]
|
||||
hb_channel_class = Type(InProcessHBChannel) # type:ignore[arg-type]
|
||||
|
||||
kernel = Instance("ipykernel.inprocess.ipkernel.InProcessKernel", allow_none=True)
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Channel management methods
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
@default("blocking_class")
|
||||
def _default_blocking_class(self):
|
||||
from .blocking import BlockingInProcessKernelClient
|
||||
|
||||
return BlockingInProcessKernelClient
|
||||
|
||||
def get_connection_info(self):
|
||||
"""Get the connection info for the client."""
|
||||
d = super().get_connection_info()
|
||||
d["kernel"] = self.kernel # type:ignore[assignment]
|
||||
return d
|
||||
|
||||
def start_channels(self, *args, **kwargs):
|
||||
"""Start the channels on the client."""
|
||||
super().start_channels()
|
||||
if self.kernel:
|
||||
self.kernel.frontends.append(self)
|
||||
|
||||
@property
|
||||
def shell_channel(self):
|
||||
if self._shell_channel is None:
|
||||
self._shell_channel = self.shell_channel_class(self) # type:ignore[abstract,call-arg]
|
||||
return self._shell_channel
|
||||
|
||||
@property
|
||||
def iopub_channel(self):
|
||||
if self._iopub_channel is None:
|
||||
self._iopub_channel = self.iopub_channel_class(self) # type:ignore[abstract,call-arg]
|
||||
return self._iopub_channel
|
||||
|
||||
@property
|
||||
def stdin_channel(self):
|
||||
if self._stdin_channel is None:
|
||||
self._stdin_channel = self.stdin_channel_class(self) # type:ignore[abstract,call-arg]
|
||||
return self._stdin_channel
|
||||
|
||||
@property
|
||||
def control_channel(self):
|
||||
if self._control_channel is None:
|
||||
self._control_channel = self.control_channel_class(self) # type:ignore[abstract,call-arg]
|
||||
return self._control_channel
|
||||
|
||||
@property
|
||||
def hb_channel(self):
|
||||
if self._hb_channel is None:
|
||||
self._hb_channel = self.hb_channel_class(self) # type:ignore[abstract,call-arg]
|
||||
return self._hb_channel
|
||||
|
||||
# Methods for sending specific messages
|
||||
# -------------------------------------
|
||||
|
||||
def execute(
|
||||
self, code, silent=False, store_history=True, user_expressions=None, allow_stdin=None
|
||||
):
|
||||
"""Execute code on the client."""
|
||||
if allow_stdin is None:
|
||||
allow_stdin = self.allow_stdin
|
||||
content = dict(
|
||||
code=code,
|
||||
silent=silent,
|
||||
store_history=store_history,
|
||||
user_expressions=user_expressions or {},
|
||||
allow_stdin=allow_stdin,
|
||||
)
|
||||
msg = self.session.msg("execute_request", content)
|
||||
self._dispatch_to_kernel(msg)
|
||||
return msg["header"]["msg_id"]
|
||||
|
||||
def complete(self, code, cursor_pos=None):
|
||||
"""Get code completion."""
|
||||
if cursor_pos is None:
|
||||
cursor_pos = len(code)
|
||||
content = dict(code=code, cursor_pos=cursor_pos)
|
||||
msg = self.session.msg("complete_request", content)
|
||||
self._dispatch_to_kernel(msg)
|
||||
return msg["header"]["msg_id"]
|
||||
|
||||
def inspect(self, code, cursor_pos=None, detail_level=0):
|
||||
"""Get code inspection."""
|
||||
if cursor_pos is None:
|
||||
cursor_pos = len(code)
|
||||
content = dict(
|
||||
code=code,
|
||||
cursor_pos=cursor_pos,
|
||||
detail_level=detail_level,
|
||||
)
|
||||
msg = self.session.msg("inspect_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 code history."""
|
||||
content = dict(raw=raw, output=output, hist_access_type=hist_access_type, **kwds)
|
||||
msg = self.session.msg("history_request", content)
|
||||
self._dispatch_to_kernel(msg)
|
||||
return msg["header"]["msg_id"]
|
||||
|
||||
def shutdown(self, restart=False):
|
||||
"""Handle shutdown."""
|
||||
# FIXME: What to do here?
|
||||
msg = "Cannot shutdown in-process kernel"
|
||||
raise NotImplementedError(msg)
|
||||
|
||||
def kernel_info(self):
|
||||
"""Request kernel info."""
|
||||
msg = self.session.msg("kernel_info_request")
|
||||
self._dispatch_to_kernel(msg)
|
||||
return msg["header"]["msg_id"]
|
||||
|
||||
def comm_info(self, target_name=None):
|
||||
"""Request a dictionary of valid comms and their targets."""
|
||||
content = {} if target_name is None else dict(target_name=target_name)
|
||||
msg = self.session.msg("comm_info_request", content)
|
||||
self._dispatch_to_kernel(msg)
|
||||
return msg["header"]["msg_id"]
|
||||
|
||||
def input(self, string):
|
||||
"""Handle kernel input."""
|
||||
if self.kernel is None:
|
||||
msg = "Cannot send input reply. No kernel exists."
|
||||
raise RuntimeError(msg)
|
||||
self.kernel.raw_input_str = string
|
||||
|
||||
def is_complete(self, code):
|
||||
"""Handle an is_complete request."""
|
||||
msg = self.session.msg("is_complete_request", {"code": code})
|
||||
self._dispatch_to_kernel(msg)
|
||||
return msg["header"]["msg_id"]
|
||||
|
||||
def _dispatch_to_kernel(self, msg):
|
||||
"""Send a message to the kernel and handle a reply."""
|
||||
kernel = self.kernel
|
||||
if kernel is None:
|
||||
msg = "Cannot send request. No kernel exists."
|
||||
raise RuntimeError(msg)
|
||||
|
||||
stream = kernel.shell_stream
|
||||
self.session.send(stream, msg)
|
||||
msg_parts = stream.recv_multipart()
|
||||
if run_sync is not None:
|
||||
dispatch_shell = run_sync(kernel.dispatch_shell)
|
||||
dispatch_shell(msg_parts)
|
||||
else:
|
||||
loop = asyncio.get_event_loop() # type:ignore[unreachable]
|
||||
loop.run_until_complete(kernel.dispatch_shell(msg_parts))
|
||||
idents, reply_msg = self.session.recv(stream, copy=False)
|
||||
self.shell_channel.call_handlers_later(reply_msg)
|
||||
|
||||
def get_shell_msg(self, block=True, timeout=None):
|
||||
"""Get a shell message."""
|
||||
return self.shell_channel.get_msg(block, timeout)
|
||||
|
||||
def get_iopub_msg(self, block=True, timeout=None):
|
||||
"""Get an iopub message."""
|
||||
return self.iopub_channel.get_msg(block, timeout)
|
||||
|
||||
def get_stdin_msg(self, block=True, timeout=None):
|
||||
"""Get a stdin message."""
|
||||
return self.stdin_channel.get_msg(block, timeout)
|
||||
|
||||
def get_control_msg(self, block=True, timeout=None):
|
||||
"""Get a control message."""
|
||||
return self.control_channel.get_msg(block, timeout)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# ABC Registration
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
KernelClientABC.register(InProcessKernelClient)
|
||||
@@ -0,0 +1,8 @@
|
||||
"""Shared constants.
|
||||
"""
|
||||
|
||||
# Because inprocess communication is not networked, we can use a common Session
|
||||
# key everywhere. This is not just the empty bytestring to avoid tripping
|
||||
# certain security checks in the rest of Jupyter that assumes that empty keys
|
||||
# are insecure.
|
||||
INPROCESS_KEY = b"inprocess"
|
||||
@@ -0,0 +1,203 @@
|
||||
"""An in-process kernel"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import logging
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
|
||||
from IPython.core.interactiveshell import InteractiveShellABC
|
||||
from traitlets import Any, Enum, Instance, List, Type, default
|
||||
|
||||
from ipykernel.ipkernel import IPythonKernel
|
||||
from ipykernel.jsonutil import json_clean
|
||||
from ipykernel.zmqshell import ZMQInteractiveShell
|
||||
|
||||
from ..iostream import BackgroundSocket, IOPubThread, OutStream
|
||||
from .constants import INPROCESS_KEY
|
||||
from .socket import DummySocket
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Main kernel class
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class InProcessKernel(IPythonKernel):
|
||||
"""An in-process kernel."""
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# InProcessKernel interface
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# The frontends connected to this kernel.
|
||||
frontends = List(Instance("ipykernel.inprocess.client.InProcessKernelClient", allow_none=True))
|
||||
|
||||
# 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(allow_none=True) # type:ignore[assignment]
|
||||
_underlying_iopub_socket = Instance(DummySocket, ())
|
||||
iopub_thread: IOPubThread = Instance(IOPubThread) # type:ignore[assignment]
|
||||
|
||||
shell_stream = Instance(DummySocket, ()) # type:ignore[arg-type]
|
||||
|
||||
@default("iopub_thread")
|
||||
def _default_iopub_thread(self):
|
||||
thread = IOPubThread(self._underlying_iopub_socket)
|
||||
thread.start()
|
||||
return thread
|
||||
|
||||
iopub_socket: BackgroundSocket = Instance(BackgroundSocket) # type:ignore[assignment]
|
||||
|
||||
@default("iopub_socket")
|
||||
def _default_iopub_socket(self):
|
||||
return self.iopub_thread.background_socket
|
||||
|
||||
stdin_socket = Instance(DummySocket, ()) # type:ignore[assignment]
|
||||
|
||||
def __init__(self, **traits):
|
||||
"""Initialize the kernel."""
|
||||
super().__init__(**traits)
|
||||
|
||||
self._underlying_iopub_socket.observe(self._io_dispatch, names=["message_sent"])
|
||||
if self.shell:
|
||||
self.shell.kernel = self
|
||||
|
||||
async def execute_request(self, stream, ident, parent):
|
||||
"""Override for temporary IO redirection."""
|
||||
with self._redirected_io():
|
||||
await super().execute_request(stream, ident, parent)
|
||||
|
||||
def start(self):
|
||||
"""Override registration of dispatchers for streams."""
|
||||
if self.shell:
|
||||
self.shell.exit_now = False
|
||||
|
||||
def _abort_queues(self):
|
||||
"""The in-process kernel doesn't abort requests."""
|
||||
|
||||
async def _flush_control_queue(self):
|
||||
"""No need to flush control queues for in-process"""
|
||||
|
||||
def _input_request(self, prompt, ident, parent, password=False):
|
||||
# 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, password=password))
|
||||
assert self.session is not None
|
||||
msg = self.session.msg("input_request", content, parent)
|
||||
for frontend in self.frontends:
|
||||
assert frontend is not None
|
||||
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 ""
|
||||
|
||||
# Await a response.
|
||||
while self.raw_input_str is None:
|
||||
frontend.stdin_channel.process_events()
|
||||
return self.raw_input_str # type:ignore[unreachable]
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Protected interface
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@contextmanager
|
||||
def _redirected_io(self):
|
||||
"""Temporarily redirect IO to the kernel."""
|
||||
sys_stdout, sys_stderr = sys.stdout, sys.stderr
|
||||
try:
|
||||
sys.stdout, sys.stderr = self.stdout, self.stderr
|
||||
yield
|
||||
finally:
|
||||
sys.stdout, sys.stderr = sys_stdout, sys_stderr
|
||||
|
||||
# ------ Trait change handlers --------------------------------------------
|
||||
|
||||
def _io_dispatch(self, change):
|
||||
"""Called when a message is sent to the IO socket."""
|
||||
assert self.iopub_socket.io_thread is not None
|
||||
assert self.session is not None
|
||||
ident, msg = self.session.recv(self.iopub_socket.io_thread.socket, copy=False)
|
||||
for frontend in self.frontends:
|
||||
assert frontend is not None
|
||||
frontend.iopub_channel.call_handlers(msg)
|
||||
|
||||
# ------ Trait initializers -----------------------------------------------
|
||||
|
||||
@default("log")
|
||||
def _default_log(self):
|
||||
return logging.getLogger(__name__)
|
||||
|
||||
@default("session")
|
||||
def _default_session(self):
|
||||
from jupyter_client.session import Session
|
||||
|
||||
return Session(parent=self, key=INPROCESS_KEY)
|
||||
|
||||
@default("shell_class")
|
||||
def _default_shell_class(self):
|
||||
return InProcessInteractiveShell
|
||||
|
||||
@default("stdout")
|
||||
def _default_stdout(self):
|
||||
return OutStream(self.session, self.iopub_thread, "stdout", watchfd=False)
|
||||
|
||||
@default("stderr")
|
||||
def _default_stderr(self):
|
||||
return OutStream(self.session, self.iopub_thread, "stderr", watchfd=False)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Interactive shell subclass
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class InProcessInteractiveShell(ZMQInteractiveShell):
|
||||
"""An in-process interactive shell."""
|
||||
|
||||
kernel: InProcessKernel = Instance(
|
||||
"ipykernel.inprocess.ipkernel.InProcessKernel", allow_none=True
|
||||
) # type:ignore[assignment]
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# InteractiveShell interface
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def enable_gui(self, gui=None):
|
||||
"""Enable GUI integration for the kernel."""
|
||||
if not gui:
|
||||
gui = self.kernel.gui
|
||||
self.active_eventloop = gui
|
||||
|
||||
def enable_matplotlib(self, gui=None):
|
||||
"""Enable matplotlib integration for the kernel."""
|
||||
if not gui:
|
||||
gui = self.kernel.gui
|
||||
return super().enable_matplotlib(gui)
|
||||
|
||||
def enable_pylab(self, gui=None, import_all=True, welcome_message=False):
|
||||
"""Activate pylab support at runtime."""
|
||||
if not gui:
|
||||
gui = self.kernel.gui
|
||||
return super().enable_pylab(gui, import_all, welcome_message)
|
||||
|
||||
|
||||
InteractiveShellABC.register(InProcessInteractiveShell)
|
||||
@@ -0,0 +1,92 @@
|
||||
"""A kernel manager for in-process kernels."""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from jupyter_client.manager import KernelManager
|
||||
from jupyter_client.managerabc import KernelManagerABC
|
||||
from jupyter_client.session import Session
|
||||
from traitlets import DottedObjectName, Instance, default
|
||||
|
||||
from .constants import INPROCESS_KEY
|
||||
|
||||
|
||||
class InProcessKernelManager(KernelManager):
|
||||
"""A manager for an in-process kernel.
|
||||
|
||||
This class implements the interface of
|
||||
`jupyter_client.kernelmanagerabc.KernelManagerABC` and allows
|
||||
(asynchronous) frontends to be used seamlessly with an in-process kernel.
|
||||
|
||||
See `jupyter_client.kernelmanager.KernelManager` for docstrings.
|
||||
"""
|
||||
|
||||
# The kernel process with which the KernelManager is communicating.
|
||||
kernel = Instance("ipykernel.inprocess.ipkernel.InProcessKernel", allow_none=True)
|
||||
# the client class for KM.client() shortcut
|
||||
client_class = DottedObjectName("ipykernel.inprocess.BlockingInProcessKernelClient")
|
||||
|
||||
@default("blocking_class")
|
||||
def _default_blocking_class(self):
|
||||
from .blocking import BlockingInProcessKernelClient
|
||||
|
||||
return BlockingInProcessKernelClient
|
||||
|
||||
@default("session")
|
||||
def _default_session(self):
|
||||
# don't sign in-process messages
|
||||
return Session(key=INPROCESS_KEY, parent=self)
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Kernel management methods
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
def start_kernel(self, **kwds):
|
||||
"""Start the kernel."""
|
||||
from ipykernel.inprocess.ipkernel import InProcessKernel
|
||||
|
||||
self.kernel = InProcessKernel(parent=self, session=self.session)
|
||||
|
||||
def shutdown_kernel(self):
|
||||
"""Shutdown the kernel."""
|
||||
if self.kernel:
|
||||
self.kernel.iopub_thread.stop()
|
||||
self._kill_kernel()
|
||||
|
||||
def restart_kernel(self, now=False, **kwds):
|
||||
"""Restart the kernel."""
|
||||
self.shutdown_kernel()
|
||||
self.start_kernel(**kwds)
|
||||
|
||||
@property
|
||||
def has_kernel(self):
|
||||
return self.kernel is not None
|
||||
|
||||
def _kill_kernel(self):
|
||||
self.kernel = None
|
||||
|
||||
def interrupt_kernel(self):
|
||||
"""Interrupt the kernel."""
|
||||
msg = "Cannot interrupt in-process kernel."
|
||||
raise NotImplementedError(msg)
|
||||
|
||||
def signal_kernel(self, signum):
|
||||
"""Send a signal to the kernel."""
|
||||
msg = "Cannot signal in-process kernel."
|
||||
raise NotImplementedError(msg)
|
||||
|
||||
def is_alive(self):
|
||||
"""Test if the kernel is alive."""
|
||||
return self.kernel is not None
|
||||
|
||||
def client(self, **kwargs):
|
||||
"""Get a client for the kernel."""
|
||||
kwargs["kernel"] = self.kernel
|
||||
return super().client(**kwargs)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# ABC Registration
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
KernelManagerABC.register(InProcessKernelManager)
|
||||
@@ -0,0 +1,41 @@
|
||||
""" Defines a dummy socket implementing (part of) the zmq.Socket interface. """
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from queue import Queue
|
||||
|
||||
import zmq
|
||||
from traitlets import HasTraits, Instance, Int
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Dummy socket class
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class DummySocket(HasTraits):
|
||||
"""A dummy socket implementing (part of) the zmq.Socket interface."""
|
||||
|
||||
queue = Instance(Queue, ())
|
||||
message_sent = Int(0) # Should be an Event
|
||||
context = Instance(zmq.Context)
|
||||
|
||||
def _context_default(self):
|
||||
return zmq.Context()
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Socket interface
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def recv_multipart(self, flags=0, copy=True, track=False):
|
||||
"""Recv a multipart message."""
|
||||
return self.queue.get_nowait()
|
||||
|
||||
def send_multipart(self, msg_parts, flags=0, copy=True, track=False):
|
||||
"""Send a multipart message."""
|
||||
msg_parts = list(map(zmq.Message, msg_parts))
|
||||
self.queue.put_nowait(msg_parts)
|
||||
self.message_sent += 1
|
||||
|
||||
def flush(self, timeout=1.0):
|
||||
"""no-op to comply with stream API"""
|
||||
Reference in New Issue
Block a user