2018-02-21 16:37:46 +00:00
|
|
|
|
|
|
|
import logging
|
|
|
|
import threading
|
2018-02-23 15:24:19 +00:00
|
|
|
import signal
|
|
|
|
import cutter_internal
|
2018-02-23 16:42:54 +00:00
|
|
|
import zmq
|
2018-02-21 16:37:46 +00:00
|
|
|
from ipykernel.kernelapp import IPKernelApp
|
|
|
|
from ipykernel.ipkernel import IPythonKernel
|
|
|
|
|
|
|
|
|
|
|
|
class IPyKernelInterfaceKernel:
|
2018-02-23 15:24:19 +00:00
|
|
|
def __init__(self, thread, app):
|
|
|
|
self._thread = thread
|
|
|
|
self._app = app
|
|
|
|
|
2018-02-23 12:04:53 +00:00
|
|
|
def send_signal(self, signum):
|
2018-02-23 15:24:19 +00:00
|
|
|
if not self._thread.is_alive():
|
|
|
|
return
|
|
|
|
|
|
|
|
if signum == signal.SIGKILL or signum == signal.SIGTERM:
|
|
|
|
self._app.io_loop.stop()
|
|
|
|
elif signum == signal.SIGINT and self._app.kernel.interruptable:
|
|
|
|
self._app.log.debug("Sending KeyboardInterrupt to ioloop thread.")
|
|
|
|
cutter_internal.thread_set_async_exc(self._thread.ident, KeyboardInterrupt())
|
2018-02-22 21:08:06 +00:00
|
|
|
|
2018-02-21 16:37:46 +00:00
|
|
|
def poll(self):
|
2018-02-23 15:24:19 +00:00
|
|
|
if self._thread.is_alive():
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return 0
|
2018-02-21 16:37:46 +00:00
|
|
|
|
2018-02-23 16:42:54 +00:00
|
|
|
def cleanup(self):
|
|
|
|
self._app.heartbeat.context.destroy()
|
|
|
|
self._thread.join()
|
|
|
|
self._app.heartbeat.join()
|
|
|
|
self._app.iopub_thread.stop()
|
2018-03-06 18:40:24 +00:00
|
|
|
try:
|
|
|
|
self._app.kernel.shell.history_manager.save_thread.stop()
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
2018-02-23 16:42:54 +00:00
|
|
|
zmq.Context.instance().destroy()
|
|
|
|
# successful if only the main thread remains
|
|
|
|
return len(threading.enumerate()) == 1
|
|
|
|
|
2018-02-21 16:37:46 +00:00
|
|
|
|
|
|
|
class CutterIPythonKernel(IPythonKernel):
|
2018-02-23 15:24:19 +00:00
|
|
|
def __init__(self, **kwargs):
|
|
|
|
super().__init__(**kwargs)
|
|
|
|
self.interruptable = False
|
|
|
|
|
2018-02-21 16:37:46 +00:00
|
|
|
def pre_handler_hook(self):
|
2018-02-23 15:24:19 +00:00
|
|
|
self.interruptable = True
|
2018-02-21 16:37:46 +00:00
|
|
|
|
|
|
|
def post_handler_hook(self):
|
2018-02-23 15:24:19 +00:00
|
|
|
self.interruptable = False
|
2018-02-21 16:37:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
class CutterIPKernelApp(IPKernelApp):
|
|
|
|
def init_signal(self):
|
2018-02-23 15:24:19 +00:00
|
|
|
# This would call signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
|
|
# Not needed in supinterpreter.
|
|
|
|
pass
|
|
|
|
|
|
|
|
def log_connection_info(self):
|
|
|
|
# Just skip this. It would only pollute Cutter's output.
|
2018-02-21 16:37:46 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def launch_ipykernel(argv):
|
2018-02-23 15:24:19 +00:00
|
|
|
app = CutterIPKernelApp.instance()
|
|
|
|
|
2018-02-21 16:37:46 +00:00
|
|
|
def run_kernel():
|
2018-03-06 18:40:24 +00:00
|
|
|
import asyncio
|
|
|
|
asyncio.set_event_loop(asyncio.new_event_loop())
|
2018-02-21 16:37:46 +00:00
|
|
|
app.kernel_class = CutterIPythonKernel
|
2018-02-23 16:42:54 +00:00
|
|
|
# app.log_level = logging.DEBUG
|
2018-02-21 16:37:46 +00:00
|
|
|
app.initialize(argv[3:])
|
|
|
|
app.start()
|
|
|
|
|
|
|
|
thread = threading.Thread(target=run_kernel)
|
|
|
|
thread.start()
|
|
|
|
|
2018-02-23 15:24:19 +00:00
|
|
|
return IPyKernelInterfaceKernel(thread, app)
|
2018-02-21 16:37:46 +00:00
|
|
|
|