|
"""An Application for launching a kernel""" |
|
|
|
|
|
|
|
from __future__ import annotations |
|
|
|
import atexit |
|
import errno |
|
import logging |
|
import os |
|
import signal |
|
import sys |
|
import traceback |
|
import typing as t |
|
from functools import partial |
|
from io import FileIO, TextIOWrapper |
|
from logging import StreamHandler |
|
from pathlib import Path |
|
|
|
import zmq |
|
from IPython.core.application import ( |
|
BaseIPythonApplication, |
|
base_aliases, |
|
base_flags, |
|
catch_config_error, |
|
) |
|
from IPython.core.profiledir import ProfileDir |
|
from IPython.core.shellapp import InteractiveShellApp, shell_aliases, shell_flags |
|
from jupyter_client.connect import ConnectionFileMixin |
|
from jupyter_client.session import Session, session_aliases, session_flags |
|
from jupyter_core.paths import jupyter_runtime_dir |
|
from tornado import ioloop |
|
from traitlets.traitlets import ( |
|
Any, |
|
Bool, |
|
Dict, |
|
DottedObjectName, |
|
Instance, |
|
Integer, |
|
Type, |
|
Unicode, |
|
default, |
|
) |
|
from traitlets.utils import filefind |
|
from traitlets.utils.importstring import import_item |
|
from zmq.eventloop.zmqstream import ZMQStream |
|
|
|
from .connect import get_connection_info, write_connection_file |
|
|
|
|
|
from .control import ControlThread |
|
from .heartbeat import Heartbeat |
|
from .iostream import IOPubThread |
|
from .ipkernel import IPythonKernel |
|
from .parentpoller import ParentPollerUnix, ParentPollerWindows |
|
from .zmqshell import ZMQInteractiveShell |
|
|
|
|
|
|
|
|
|
|
|
kernel_aliases = dict(base_aliases) |
|
kernel_aliases.update( |
|
{ |
|
"ip": "IPKernelApp.ip", |
|
"hb": "IPKernelApp.hb_port", |
|
"shell": "IPKernelApp.shell_port", |
|
"iopub": "IPKernelApp.iopub_port", |
|
"stdin": "IPKernelApp.stdin_port", |
|
"control": "IPKernelApp.control_port", |
|
"f": "IPKernelApp.connection_file", |
|
"transport": "IPKernelApp.transport", |
|
} |
|
) |
|
|
|
kernel_flags = dict(base_flags) |
|
kernel_flags.update( |
|
{ |
|
"no-stdout": ({"IPKernelApp": {"no_stdout": True}}, "redirect stdout to the null device"), |
|
"no-stderr": ({"IPKernelApp": {"no_stderr": True}}, "redirect stderr to the null device"), |
|
"pylab": ( |
|
{"IPKernelApp": {"pylab": "auto"}}, |
|
"""Pre-load matplotlib and numpy for interactive use with |
|
the default matplotlib backend.""", |
|
), |
|
"trio-loop": ( |
|
{"InteractiveShell": {"trio_loop": False}}, |
|
"Enable Trio as main event loop.", |
|
), |
|
} |
|
) |
|
|
|
|
|
kernel_aliases.update(shell_aliases) |
|
kernel_flags.update(shell_flags) |
|
|
|
|
|
kernel_aliases.update(session_aliases) |
|
kernel_flags.update(session_flags) |
|
|
|
_ctrl_c_message = """\ |
|
NOTE: When using the `ipython kernel` entry point, Ctrl-C will not work. |
|
|
|
To exit, you will have to explicitly quit this process, by either sending |
|
"quit" from a client, or using Ctrl-\\ in UNIX-like environments. |
|
|
|
To read more about this, see https://github.com/ipython/ipython/issues/2049 |
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
class IPKernelApp(BaseIPythonApplication, InteractiveShellApp, ConnectionFileMixin): |
|
"""The IPYKernel application class.""" |
|
|
|
name = "ipython-kernel" |
|
aliases = Dict(kernel_aliases) |
|
flags = Dict(kernel_flags) |
|
classes = [IPythonKernel, ZMQInteractiveShell, ProfileDir, Session] |
|
|
|
kernel_class = Type( |
|
"ipykernel.ipkernel.IPythonKernel", |
|
klass="ipykernel.kernelbase.Kernel", |
|
help="""The Kernel subclass to be used. |
|
|
|
This should allow easy reuse of the IPKernelApp entry point |
|
to configure and launch kernels other than IPython's own. |
|
""", |
|
).tag(config=True) |
|
kernel = Any() |
|
poller = Any() |
|
heartbeat = Instance(Heartbeat, allow_none=True) |
|
|
|
context: zmq.Context[t.Any] | None = Any() |
|
shell_socket = Any() |
|
control_socket = Any() |
|
debugpy_socket = Any() |
|
debug_shell_socket = Any() |
|
stdin_socket = Any() |
|
iopub_socket = Any() |
|
iopub_thread = Any() |
|
control_thread = Any() |
|
|
|
_ports = Dict() |
|
|
|
subcommands = { |
|
"install": ( |
|
"ipykernel.kernelspec.InstallIPythonKernelSpecApp", |
|
"Install the IPython kernel", |
|
), |
|
} |
|
|
|
|
|
connection_dir = Unicode() |
|
|
|
@default("connection_dir") |
|
def _default_connection_dir(self): |
|
return jupyter_runtime_dir() |
|
|
|
@property |
|
def abs_connection_file(self): |
|
if Path(self.connection_file).name == self.connection_file and self.connection_dir: |
|
return str(Path(str(self.connection_dir)) / self.connection_file) |
|
return self.connection_file |
|
|
|
|
|
no_stdout = Bool(False, help="redirect stdout to the null device").tag(config=True) |
|
no_stderr = Bool(False, help="redirect stderr to the null device").tag(config=True) |
|
trio_loop = Bool(False, help="Set main event loop.").tag(config=True) |
|
quiet = Bool(True, help="Only send stdout/stderr to output stream").tag(config=True) |
|
outstream_class = DottedObjectName( |
|
"ipykernel.iostream.OutStream", |
|
help="The importstring for the OutStream factory", |
|
allow_none=True, |
|
).tag(config=True) |
|
displayhook_class = DottedObjectName( |
|
"ipykernel.displayhook.ZMQDisplayHook", help="The importstring for the DisplayHook factory" |
|
).tag(config=True) |
|
|
|
capture_fd_output = Bool( |
|
True, |
|
help="""Attempt to capture and forward low-level output, e.g. produced by Extension libraries. |
|
""", |
|
).tag(config=True) |
|
|
|
|
|
parent_handle = Integer( |
|
int(os.environ.get("JPY_PARENT_PID") or 0), |
|
help="""kill this process if its parent dies. On Windows, the argument |
|
specifies the HANDLE of the parent process, otherwise it is simply boolean. |
|
""", |
|
).tag(config=True) |
|
interrupt = Integer( |
|
int(os.environ.get("JPY_INTERRUPT_EVENT") or 0), |
|
help="""ONLY USED ON WINDOWS |
|
Interrupt this process when the parent is signaled. |
|
""", |
|
).tag(config=True) |
|
|
|
def init_crash_handler(self): |
|
"""Initialize the crash handler.""" |
|
sys.excepthook = self.excepthook |
|
|
|
def excepthook(self, etype, evalue, tb): |
|
"""Handle an exception.""" |
|
|
|
traceback.print_exception(etype, evalue, tb, file=sys.__stderr__) |
|
|
|
def init_poller(self): |
|
"""Initialize the poller.""" |
|
if sys.platform == "win32": |
|
if self.interrupt or self.parent_handle: |
|
self.poller = ParentPollerWindows(self.interrupt, self.parent_handle) |
|
elif self.parent_handle and self.parent_handle != 1: |
|
|
|
|
|
|
|
self.poller = ParentPollerUnix() |
|
|
|
def _try_bind_socket(self, s, port): |
|
iface = f"{self.transport}://{self.ip}" |
|
if self.transport == "tcp": |
|
if port <= 0: |
|
port = s.bind_to_random_port(iface) |
|
else: |
|
s.bind("tcp://%s:%i" % (self.ip, port)) |
|
elif self.transport == "ipc": |
|
if port <= 0: |
|
port = 1 |
|
path = "%s-%i" % (self.ip, port) |
|
while Path(path).exists(): |
|
port = port + 1 |
|
path = "%s-%i" % (self.ip, port) |
|
else: |
|
path = "%s-%i" % (self.ip, port) |
|
s.bind("ipc://%s" % path) |
|
return port |
|
|
|
def _bind_socket(self, s, port): |
|
try: |
|
win_in_use = errno.WSAEADDRINUSE |
|
except AttributeError: |
|
win_in_use = None |
|
|
|
|
|
|
|
max_attempts = 1 if port else 100 |
|
for attempt in range(max_attempts): |
|
try: |
|
return self._try_bind_socket(s, port) |
|
except zmq.ZMQError as ze: |
|
|
|
if ze.errno != errno.EADDRINUSE and ze.errno != win_in_use: |
|
raise |
|
if attempt == max_attempts - 1: |
|
raise |
|
return None |
|
|
|
def write_connection_file(self): |
|
"""write connection info to JSON file""" |
|
cf = self.abs_connection_file |
|
connection_info = dict( |
|
ip=self.ip, |
|
key=self.session.key, |
|
transport=self.transport, |
|
shell_port=self.shell_port, |
|
stdin_port=self.stdin_port, |
|
hb_port=self.hb_port, |
|
iopub_port=self.iopub_port, |
|
control_port=self.control_port, |
|
) |
|
if Path(cf).exists(): |
|
|
|
|
|
|
|
existing_connection_info = get_connection_info(cf, unpack=True) |
|
assert isinstance(existing_connection_info, dict) |
|
connection_info = dict(existing_connection_info, **connection_info) |
|
if connection_info == existing_connection_info: |
|
self.log.debug("Connection file %s with current information already exists", cf) |
|
return |
|
|
|
self.log.debug("Writing connection file: %s", cf) |
|
|
|
write_connection_file(cf, **connection_info) |
|
|
|
def cleanup_connection_file(self): |
|
"""Clean up our connection file.""" |
|
cf = self.abs_connection_file |
|
self.log.debug("Cleaning up connection file: %s", cf) |
|
try: |
|
Path(cf).unlink() |
|
except OSError: |
|
pass |
|
|
|
self.cleanup_ipc_files() |
|
|
|
def init_connection_file(self): |
|
"""Initialize our connection file.""" |
|
if not self.connection_file: |
|
self.connection_file = "kernel-%s.json" % os.getpid() |
|
try: |
|
self.connection_file = filefind(self.connection_file, [".", self.connection_dir]) |
|
except OSError: |
|
self.log.debug("Connection file not found: %s", self.connection_file) |
|
|
|
Path(self.abs_connection_file).parent.mkdir(mode=0o700, exist_ok=True, parents=True) |
|
|
|
atexit.register(self.cleanup_connection_file) |
|
return |
|
try: |
|
self.load_connection_file() |
|
except Exception: |
|
self.log.error( |
|
"Failed to load connection file: %r", self.connection_file, exc_info=True |
|
) |
|
self.exit(1) |
|
|
|
def init_sockets(self): |
|
"""Create a context, a session, and the kernel sockets.""" |
|
self.log.info("Starting the kernel at pid: %i", os.getpid()) |
|
assert self.context is None, "init_sockets cannot be called twice!" |
|
self.context = context = zmq.Context() |
|
atexit.register(self.close) |
|
|
|
self.shell_socket = context.socket(zmq.ROUTER) |
|
self.shell_socket.linger = 1000 |
|
self.shell_port = self._bind_socket(self.shell_socket, self.shell_port) |
|
self.log.debug("shell ROUTER Channel on port: %i" % self.shell_port) |
|
|
|
self.stdin_socket = context.socket(zmq.ROUTER) |
|
self.stdin_socket.linger = 1000 |
|
self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port) |
|
self.log.debug("stdin ROUTER Channel on port: %i" % self.stdin_port) |
|
|
|
if hasattr(zmq, "ROUTER_HANDOVER"): |
|
|
|
|
|
|
|
self.shell_socket.router_handover = self.stdin_socket.router_handover = 1 |
|
|
|
self.init_control(context) |
|
self.init_iopub(context) |
|
|
|
def init_control(self, context): |
|
"""Initialize the control channel.""" |
|
self.control_socket = context.socket(zmq.ROUTER) |
|
self.control_socket.linger = 1000 |
|
self.control_port = self._bind_socket(self.control_socket, self.control_port) |
|
self.log.debug("control ROUTER Channel on port: %i" % self.control_port) |
|
|
|
self.debugpy_socket = context.socket(zmq.STREAM) |
|
self.debugpy_socket.linger = 1000 |
|
|
|
self.debug_shell_socket = context.socket(zmq.DEALER) |
|
self.debug_shell_socket.linger = 1000 |
|
if self.shell_socket.getsockopt(zmq.LAST_ENDPOINT): |
|
self.debug_shell_socket.connect(self.shell_socket.getsockopt(zmq.LAST_ENDPOINT)) |
|
|
|
if hasattr(zmq, "ROUTER_HANDOVER"): |
|
|
|
|
|
|
|
self.control_socket.router_handover = 1 |
|
|
|
self.control_thread = ControlThread(daemon=True) |
|
|
|
def init_iopub(self, context): |
|
"""Initialize the iopub channel.""" |
|
self.iopub_socket = context.socket(zmq.PUB) |
|
self.iopub_socket.linger = 1000 |
|
self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port) |
|
self.log.debug("iopub PUB Channel on port: %i" % self.iopub_port) |
|
self.configure_tornado_logger() |
|
self.iopub_thread = IOPubThread(self.iopub_socket, pipe=True) |
|
self.iopub_thread.start() |
|
|
|
self.iopub_socket = self.iopub_thread.background_socket |
|
|
|
def init_heartbeat(self): |
|
"""start the heart beating""" |
|
|
|
|
|
hb_ctx = zmq.Context() |
|
self.heartbeat = Heartbeat(hb_ctx, (self.transport, self.ip, self.hb_port)) |
|
self.hb_port = self.heartbeat.port |
|
self.log.debug("Heartbeat REP Channel on port: %i" % self.hb_port) |
|
self.heartbeat.start() |
|
|
|
def close(self): |
|
"""Close zmq sockets in an orderly fashion""" |
|
|
|
self.reset_io() |
|
self.log.info("Cleaning up sockets") |
|
if self.heartbeat: |
|
self.log.debug("Closing heartbeat channel") |
|
self.heartbeat.context.term() |
|
if self.iopub_thread: |
|
self.log.debug("Closing iopub channel") |
|
self.iopub_thread.stop() |
|
self.iopub_thread.close() |
|
if self.control_thread and self.control_thread.is_alive(): |
|
self.log.debug("Closing control thread") |
|
self.control_thread.stop() |
|
self.control_thread.join() |
|
|
|
if self.debugpy_socket and not self.debugpy_socket.closed: |
|
self.debugpy_socket.close() |
|
if self.debug_shell_socket and not self.debug_shell_socket.closed: |
|
self.debug_shell_socket.close() |
|
|
|
for channel in ("shell", "control", "stdin"): |
|
self.log.debug("Closing %s channel", channel) |
|
socket = getattr(self, channel + "_socket", None) |
|
if socket and not socket.closed: |
|
socket.close() |
|
self.log.debug("Terminating zmq context") |
|
if self.context: |
|
self.context.term() |
|
self.log.debug("Terminated zmq context") |
|
|
|
def log_connection_info(self): |
|
"""display connection info, and store ports""" |
|
basename = Path(self.connection_file).name |
|
if ( |
|
basename == self.connection_file |
|
or str(Path(self.connection_file).parent) == self.connection_dir |
|
): |
|
|
|
tail = basename |
|
else: |
|
tail = self.connection_file |
|
lines = [ |
|
"To connect another client to this kernel, use:", |
|
" --existing %s" % tail, |
|
] |
|
|
|
|
|
|
|
|
|
for line in lines: |
|
self.log.info(line) |
|
|
|
|
|
if not self.parent_handle and int(self.log_level) < logging.CRITICAL: |
|
print(_ctrl_c_message, file=sys.__stdout__) |
|
for line in lines: |
|
print(line, file=sys.__stdout__) |
|
|
|
self._ports = dict( |
|
shell=self.shell_port, |
|
iopub=self.iopub_port, |
|
stdin=self.stdin_port, |
|
hb=self.hb_port, |
|
control=self.control_port, |
|
) |
|
|
|
def init_blackhole(self): |
|
"""redirects stdout/stderr to devnull if necessary""" |
|
if self.no_stdout or self.no_stderr: |
|
blackhole = open(os.devnull, "w") |
|
if self.no_stdout: |
|
sys.stdout = sys.__stdout__ = blackhole |
|
if self.no_stderr: |
|
sys.stderr = sys.__stderr__ = blackhole |
|
|
|
def init_io(self): |
|
"""Redirect input streams and set a display hook.""" |
|
if self.outstream_class: |
|
outstream_factory = import_item(str(self.outstream_class)) |
|
if sys.stdout is not None: |
|
sys.stdout.flush() |
|
|
|
e_stdout = None if self.quiet else sys.__stdout__ |
|
e_stderr = None if self.quiet else sys.__stderr__ |
|
|
|
if not self.capture_fd_output: |
|
outstream_factory = partial(outstream_factory, watchfd=False) |
|
|
|
sys.stdout = outstream_factory(self.session, self.iopub_thread, "stdout", echo=e_stdout) |
|
if sys.stderr is not None: |
|
sys.stderr.flush() |
|
sys.stderr = outstream_factory(self.session, self.iopub_thread, "stderr", echo=e_stderr) |
|
if hasattr(sys.stderr, "_original_stdstream_copy"): |
|
for handler in self.log.handlers: |
|
if isinstance(handler, StreamHandler) and (handler.stream.buffer.fileno() == 2): |
|
self.log.debug("Seeing logger to stderr, rerouting to raw filedescriptor.") |
|
|
|
handler.stream = TextIOWrapper( |
|
FileIO( |
|
sys.stderr._original_stdstream_copy, |
|
"w", |
|
) |
|
) |
|
if self.displayhook_class: |
|
displayhook_factory = import_item(str(self.displayhook_class)) |
|
self.displayhook = displayhook_factory(self.session, self.iopub_socket) |
|
sys.displayhook = self.displayhook |
|
|
|
self.patch_io() |
|
|
|
def reset_io(self): |
|
"""restore original io |
|
|
|
restores state after init_io |
|
""" |
|
sys.stdout = sys.__stdout__ |
|
sys.stderr = sys.__stderr__ |
|
sys.displayhook = sys.__displayhook__ |
|
|
|
def patch_io(self): |
|
"""Patch important libraries that can't handle sys.stdout forwarding""" |
|
try: |
|
import faulthandler |
|
except ImportError: |
|
pass |
|
else: |
|
|
|
|
|
|
|
|
|
|
|
faulthandler_enable = faulthandler.enable |
|
|
|
def enable(file=sys.__stderr__, all_threads=True, **kwargs): |
|
return faulthandler_enable(file=file, all_threads=all_threads, **kwargs) |
|
|
|
faulthandler.enable = enable |
|
|
|
if hasattr(faulthandler, "register"): |
|
faulthandler_register = faulthandler.register |
|
|
|
def register(signum, file=sys.__stderr__, all_threads=True, chain=False, **kwargs): |
|
return faulthandler_register( |
|
signum, file=file, all_threads=all_threads, chain=chain, **kwargs |
|
) |
|
|
|
faulthandler.register = register |
|
|
|
def init_signal(self): |
|
"""Initialize the signal handler.""" |
|
signal.signal(signal.SIGINT, signal.SIG_IGN) |
|
|
|
def init_kernel(self): |
|
"""Create the Kernel object itself""" |
|
shell_stream = ZMQStream(self.shell_socket) |
|
control_stream = ZMQStream(self.control_socket, self.control_thread.io_loop) |
|
debugpy_stream = ZMQStream(self.debugpy_socket, self.control_thread.io_loop) |
|
self.control_thread.start() |
|
kernel_factory = self.kernel_class.instance |
|
|
|
kernel = kernel_factory( |
|
parent=self, |
|
session=self.session, |
|
control_stream=control_stream, |
|
debugpy_stream=debugpy_stream, |
|
debug_shell_socket=self.debug_shell_socket, |
|
shell_stream=shell_stream, |
|
control_thread=self.control_thread, |
|
iopub_thread=self.iopub_thread, |
|
iopub_socket=self.iopub_socket, |
|
stdin_socket=self.stdin_socket, |
|
log=self.log, |
|
profile_dir=self.profile_dir, |
|
user_ns=self.user_ns, |
|
) |
|
kernel.record_ports({name + "_port": port for name, port in self._ports.items()}) |
|
self.kernel = kernel |
|
|
|
|
|
self.displayhook.get_execution_count = lambda: kernel.execution_count |
|
|
|
def init_gui_pylab(self): |
|
"""Enable GUI event loop integration, taking pylab into account.""" |
|
|
|
|
|
|
|
|
|
|
|
if not os.environ.get("MPLBACKEND"): |
|
os.environ["MPLBACKEND"] = "module://matplotlib_inline.backend_inline" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
shell = self.shell |
|
assert shell is not None |
|
_showtraceback = shell._showtraceback |
|
try: |
|
|
|
def print_tb(etype, evalue, stb): |
|
print("GUI event loop or pylab initialization failed", file=sys.stderr) |
|
assert shell is not None |
|
print(shell.InteractiveTB.stb2text(stb), file=sys.stderr) |
|
|
|
shell._showtraceback = print_tb |
|
InteractiveShellApp.init_gui_pylab(self) |
|
finally: |
|
shell._showtraceback = _showtraceback |
|
|
|
def init_shell(self): |
|
"""Initialize the shell channel.""" |
|
self.shell = getattr(self.kernel, "shell", None) |
|
if self.shell: |
|
self.shell.configurables.append(self) |
|
|
|
def configure_tornado_logger(self): |
|
"""Configure the tornado logging.Logger. |
|
|
|
Must set up the tornado logger or else tornado will call |
|
basicConfig for the root logger which makes the root logger |
|
go to the real sys.stderr instead of the capture streams. |
|
This function mimics the setup of logging.basicConfig. |
|
""" |
|
logger = logging.getLogger("tornado") |
|
handler = logging.StreamHandler() |
|
formatter = logging.Formatter(logging.BASIC_FORMAT) |
|
handler.setFormatter(formatter) |
|
logger.addHandler(handler) |
|
|
|
def _init_asyncio_patch(self): |
|
"""set default asyncio policy to be compatible with tornado |
|
|
|
Tornado 6 (at least) is not compatible with the default |
|
asyncio implementation on Windows |
|
|
|
Pick the older SelectorEventLoopPolicy on Windows |
|
if the known-incompatible default policy is in use. |
|
|
|
Support for Proactor via a background thread is available in tornado 6.1, |
|
but it is still preferable to run the Selector in the main thread |
|
instead of the background. |
|
|
|
do this as early as possible to make it a low priority and overridable |
|
|
|
ref: https://github.com/tornadoweb/tornado/issues/2608 |
|
|
|
FIXME: if/when tornado supports the defaults in asyncio without threads, |
|
remove and bump tornado requirement for py38. |
|
Most likely, this will mean a new Python version |
|
where asyncio.ProactorEventLoop supports add_reader and friends. |
|
|
|
""" |
|
if sys.platform.startswith("win") and sys.version_info >= (3, 8): |
|
import asyncio |
|
|
|
try: |
|
from asyncio import WindowsProactorEventLoopPolicy, WindowsSelectorEventLoopPolicy |
|
except ImportError: |
|
pass |
|
|
|
else: |
|
if type(asyncio.get_event_loop_policy()) is WindowsProactorEventLoopPolicy: |
|
|
|
|
|
asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy()) |
|
|
|
def init_pdb(self): |
|
"""Replace pdb with IPython's version that is interruptible. |
|
|
|
With the non-interruptible version, stopping pdb() locks up the kernel in a |
|
non-recoverable state. |
|
""" |
|
import pdb |
|
|
|
from IPython.core import debugger |
|
|
|
if hasattr(debugger, "InterruptiblePdb"): |
|
|
|
debugger.Pdb = debugger.InterruptiblePdb |
|
pdb.Pdb = debugger.Pdb |
|
pdb.set_trace = debugger.set_trace |
|
|
|
@catch_config_error |
|
def initialize(self, argv=None): |
|
"""Initialize the application.""" |
|
self._init_asyncio_patch() |
|
super().initialize(argv) |
|
if self.subapp is not None: |
|
return |
|
|
|
self.init_pdb() |
|
self.init_blackhole() |
|
self.init_connection_file() |
|
self.init_poller() |
|
self.init_sockets() |
|
self.init_heartbeat() |
|
|
|
self.write_connection_file() |
|
|
|
|
|
self.log_connection_info() |
|
self.init_io() |
|
try: |
|
self.init_signal() |
|
except Exception: |
|
|
|
|
|
if int(self.log_level) < logging.CRITICAL: |
|
self.log.error("Unable to initialize signal:", exc_info=True) |
|
self.init_kernel() |
|
|
|
self.init_path() |
|
self.init_shell() |
|
if self.shell: |
|
self.init_gui_pylab() |
|
self.init_extensions() |
|
self.init_code() |
|
|
|
|
|
sys.stdout.flush() |
|
sys.stderr.flush() |
|
|
|
def start(self): |
|
"""Start the application.""" |
|
if self.subapp is not None: |
|
return self.subapp.start() |
|
if self.poller is not None: |
|
self.poller.start() |
|
self.kernel.start() |
|
self.io_loop = ioloop.IOLoop.current() |
|
if self.trio_loop: |
|
from ipykernel.trio_runner import TrioRunner |
|
|
|
tr = TrioRunner() |
|
tr.initialize(self.kernel, self.io_loop) |
|
try: |
|
tr.run() |
|
except KeyboardInterrupt: |
|
pass |
|
else: |
|
try: |
|
self.io_loop.start() |
|
except KeyboardInterrupt: |
|
pass |
|
|
|
|
|
launch_new_instance = IPKernelApp.launch_instance |
|
|
|
|
|
def main(): |
|
"""Run an IPKernel as an application""" |
|
app = IPKernelApp.instance() |
|
app.initialize() |
|
app.start() |
|
|
|
|
|
if __name__ == "__main__": |
|
main() |
|
|