2018-02-21 16:37:46 +00:00
|
|
|
|
2018-03-06 18:40:24 +00:00
|
|
|
import asyncio
|
|
|
|
import queue
|
2018-02-21 16:37:46 +00:00
|
|
|
from jupyter_client.ioloop import IOLoopKernelManager
|
2018-02-09 15:48:02 +00:00
|
|
|
from notebook.notebookapp import *
|
2018-02-23 12:04:53 +00:00
|
|
|
import cutter_internal
|
2018-02-21 16:37:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
class IPyKernelInterfaceJupyter:
|
2018-02-22 21:08:06 +00:00
|
|
|
def __init__(self, id):
|
|
|
|
self._id = id
|
|
|
|
|
2018-02-23 12:04:53 +00:00
|
|
|
def send_signal(self, signum):
|
|
|
|
cutter_internal.kernel_interface_send_signal(self._id, signum)
|
|
|
|
|
2018-02-22 21:08:06 +00:00
|
|
|
def kill(self):
|
2018-02-23 12:04:53 +00:00
|
|
|
self.send_signal(signal.SIGKILL)
|
|
|
|
|
|
|
|
def terminate(self):
|
|
|
|
self.send_signal(signal.SIGTERM)
|
2018-02-22 21:08:06 +00:00
|
|
|
|
2018-02-21 16:37:46 +00:00
|
|
|
def poll(self):
|
2018-02-23 12:04:53 +00:00
|
|
|
return cutter_internal.kernel_interface_poll(self._id)
|
|
|
|
|
|
|
|
def wait(self, timeout=None):
|
2018-02-23 15:24:19 +00:00
|
|
|
if timeout is not None:
|
|
|
|
start_time = time.process_time()
|
|
|
|
else:
|
|
|
|
start_time = None
|
|
|
|
while timeout is None or time.process_time() - start_time < timeout:
|
|
|
|
if self.poll() is not None:
|
|
|
|
return
|
|
|
|
time.sleep(0.1)
|
2018-02-21 16:37:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
class CutterInternalIPyKernelManager(IOLoopKernelManager):
|
|
|
|
def start_kernel(self, **kw):
|
|
|
|
self.write_connection_file()
|
|
|
|
|
|
|
|
self._launch_args = kw.copy()
|
2018-02-23 15:24:19 +00:00
|
|
|
|
2018-02-21 16:37:46 +00:00
|
|
|
extra_arguments = kw.pop('extra_arguments', [])
|
|
|
|
kernel_cmd = self.format_kernel_cmd(extra_arguments=extra_arguments)
|
|
|
|
env = kw.pop('env', os.environ).copy()
|
|
|
|
# Don't allow PYTHONEXECUTABLE to be passed to kernel process.
|
|
|
|
# If set, it can bork all the things.
|
|
|
|
env.pop('PYTHONEXECUTABLE', None)
|
|
|
|
if not self.kernel_cmd:
|
|
|
|
# If kernel_cmd has been set manually, don't refer to a kernel spec
|
|
|
|
# Environment variables from kernel spec are added to os.environ
|
|
|
|
env.update(self.kernel_spec.env or {})
|
|
|
|
|
|
|
|
# launch the kernel subprocess
|
2018-02-22 21:08:06 +00:00
|
|
|
id = cutter_internal.launch_ipykernel(kernel_cmd, env=env, **kw)
|
|
|
|
self.kernel = IPyKernelInterfaceJupyter(id)
|
2018-02-23 12:04:53 +00:00
|
|
|
# self._launch_kernel(kernel_cmd, env=env, **kw)
|
2018-02-21 16:37:46 +00:00
|
|
|
|
|
|
|
self.start_restarter()
|
|
|
|
self._connect_control_socket()
|
|
|
|
|
2018-02-23 12:04:53 +00:00
|
|
|
def signal_kernel(self, signum):
|
|
|
|
self.kernel.send_signal(signum)
|
|
|
|
|
2018-02-21 16:37:46 +00:00
|
|
|
|
|
|
|
def kernel_manager_factory(kernel_name, **kwargs):
|
|
|
|
if kernel_name in {"python", "python2", "python3"}:
|
|
|
|
return CutterInternalIPyKernelManager(kernel_name=kernel_name, **kwargs)
|
|
|
|
else:
|
|
|
|
return IOLoopKernelManager(kernel_name=kernel_name, **kwargs)
|
2018-02-09 15:48:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
class CutterNotebookApp(NotebookApp):
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
self.thread = None
|
2018-03-06 18:40:24 +00:00
|
|
|
self.io_loop = None
|
|
|
|
super().__init__(**kwargs)
|
2018-02-09 15:48:02 +00:00
|
|
|
|
|
|
|
def start(self):
|
|
|
|
""" see NotebookApp.start() """
|
2018-02-21 16:37:46 +00:00
|
|
|
self.kernel_manager.kernel_manager_factory = kernel_manager_factory
|
|
|
|
|
2018-02-09 15:48:02 +00:00
|
|
|
super(NotebookApp, self).start()
|
|
|
|
|
|
|
|
self.write_server_info_file()
|
|
|
|
|
|
|
|
self.io_loop = ioloop.IOLoop.current()
|
|
|
|
if sys.platform.startswith('win'):
|
|
|
|
# add no-op to wake every 5s
|
|
|
|
# to handle signals that may be ignored by the inner loop
|
|
|
|
pc = ioloop.PeriodicCallback(lambda: None, 5000)
|
|
|
|
pc.start()
|
|
|
|
try:
|
|
|
|
self.io_loop.start()
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
self.log.info(_("Interrupted..."))
|
|
|
|
finally:
|
|
|
|
self.remove_server_info_file()
|
|
|
|
self.cleanup_kernels()
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
super().stop()
|
2018-04-21 10:13:36 +00:00
|
|
|
if self.thread is not None:
|
|
|
|
self.thread.join()
|
2018-02-09 15:48:02 +00:00
|
|
|
|
2018-03-06 18:40:24 +00:00
|
|
|
def init_signal(self):
|
|
|
|
# This would call signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
|
|
# Not needed in supinterpreter.
|
|
|
|
pass
|
|
|
|
|
2018-02-09 15:48:02 +00:00
|
|
|
@property
|
|
|
|
def url_with_token(self):
|
|
|
|
return url_concat(self.connection_url, {'token': self.token})
|
|
|
|
|
|
|
|
|
|
|
|
def start_jupyter():
|
2018-03-06 18:40:24 +00:00
|
|
|
q = queue.Queue()
|
|
|
|
|
|
|
|
def start_jupyter_async():
|
|
|
|
asyncio.set_event_loop(asyncio.new_event_loop())
|
|
|
|
app = CutterNotebookApp()
|
|
|
|
# app.log_level = logging.DEBUG
|
2018-04-21 10:13:36 +00:00
|
|
|
app.thread = threading.current_thread()
|
2018-03-06 18:40:24 +00:00
|
|
|
app.initialize()
|
|
|
|
q.put(app)
|
|
|
|
app.start()
|
|
|
|
|
|
|
|
threading.Thread(target=start_jupyter_async).start()
|
|
|
|
return q.get()
|
2018-02-21 16:37:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
app = start_jupyter()
|
|
|
|
print("Started " + app.url_with_token)
|