-
Notifications
You must be signed in to change notification settings - Fork 287
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #240 from takluyver/jupyter-kernel-cmd
Add 'jupyter kernel' command
- Loading branch information
Showing
4 changed files
with
157 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import os | ||
import signal | ||
import uuid | ||
|
||
from jupyter_core.application import JupyterApp, base_flags | ||
from tornado.ioloop import IOLoop | ||
from traitlets import Unicode | ||
|
||
from . import __version__ | ||
from .kernelspec import KernelSpecManager, NATIVE_KERNEL_NAME | ||
from .manager import KernelManager | ||
|
||
class KernelApp(JupyterApp): | ||
"""Launch a kernel by name in a local subprocess. | ||
""" | ||
version = __version__ | ||
description = "Run a kernel locally in a subprocess" | ||
|
||
classes = [KernelManager, KernelSpecManager] | ||
|
||
aliases = { | ||
'kernel': 'KernelApp.kernel_name', | ||
'ip': 'KernelManager.ip', | ||
} | ||
flags = {'debug': base_flags['debug']} | ||
|
||
kernel_name = Unicode(NATIVE_KERNEL_NAME, | ||
help = 'The name of a kernel type to start' | ||
).tag(config=True) | ||
|
||
def initialize(self, argv=None): | ||
super(KernelApp, self).initialize(argv) | ||
self.km = KernelManager(kernel_name=self.kernel_name, | ||
config=self.config) | ||
cf_basename = 'kernel-%s.json' % uuid.uuid4() | ||
self.km.connection_file = os.path.join(self.runtime_dir, cf_basename) | ||
self.loop = IOLoop.current() | ||
self.loop.add_callback(self._record_started) | ||
|
||
def setup_signals(self): | ||
"""Shutdown on SIGTERM or SIGINT (Ctrl-C)""" | ||
if os.name == 'nt': | ||
return | ||
|
||
def shutdown_handler(signo, frame): | ||
self.loop.add_callback_from_signal(self.shutdown, signo) | ||
for sig in [signal.SIGTERM, signal.SIGINT]: | ||
signal.signal(sig, shutdown_handler) | ||
|
||
def shutdown(self, signo): | ||
self.log.info('Shutting down on signal %d' % signo) | ||
self.km.shutdown_kernel() | ||
self.loop.stop() | ||
|
||
def log_connection_info(self): | ||
cf = self.km.connection_file | ||
self.log.info('Connection file: %s', cf) | ||
self.log.info("To connect a client: --existing %s", os.path.basename(cf)) | ||
|
||
def _record_started(self): | ||
"""For tests, create a file to indicate that we've started | ||
Do not rely on this except in our own tests! | ||
""" | ||
fn = os.environ.get('JUPYTER_CLIENT_TEST_RECORD_STARTUP_PRIVATE') | ||
if fn is not None: | ||
with open(fn, 'wb'): | ||
pass | ||
|
||
def start(self): | ||
self.log.info('Starting kernel %r', self.kernel_name) | ||
try: | ||
self.km.start_kernel() | ||
self.log_connection_info() | ||
self.setup_signals() | ||
self.loop.start() | ||
finally: | ||
self.km.cleanup() | ||
|
||
|
||
main = KernelApp.launch_instance |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
from __future__ import division | ||
|
||
import os | ||
import shutil | ||
from subprocess import Popen, PIPE | ||
import sys | ||
from tempfile import mkdtemp | ||
import time | ||
|
||
PY3 = sys.version_info[0] >= 3 | ||
|
||
def _launch(extra_env): | ||
env = os.environ.copy() | ||
env.update(extra_env) | ||
return Popen([sys.executable, '-c', | ||
'from jupyter_client.kernelapp import main; main()'], | ||
env=env, stderr=(PIPE if PY3 else None)) | ||
|
||
WAIT_TIME = 10 | ||
POLL_FREQ = 10 | ||
|
||
def hacky_wait(p): | ||
"""Python 2 subprocess doesn't have timeouts :-(""" | ||
for _ in range(WAIT_TIME * POLL_FREQ): | ||
if p.poll() is not None: | ||
return p.returncode | ||
time.sleep(1 / POLL_FREQ) | ||
else: | ||
raise AssertionError("Process didn't exit in {} seconds" | ||
.format(WAIT_TIME)) | ||
|
||
def test_kernelapp_lifecycle(): | ||
# Check that 'jupyter kernel' starts and terminates OK. | ||
runtime_dir = mkdtemp() | ||
startup_dir = mkdtemp() | ||
started = os.path.join(startup_dir, 'started') | ||
try: | ||
p = _launch({'JUPYTER_RUNTIME_DIR': runtime_dir, | ||
'JUPYTER_CLIENT_TEST_RECORD_STARTUP_PRIVATE': started, | ||
}) | ||
# Wait for start | ||
for _ in range(WAIT_TIME * POLL_FREQ): | ||
if os.path.isfile(started): | ||
break | ||
time.sleep(1 / POLL_FREQ) | ||
else: | ||
raise AssertionError("No started file created in {} seconds" | ||
.format(WAIT_TIME)) | ||
|
||
# Connection file should be there by now | ||
files = os.listdir(runtime_dir) | ||
assert len(files) == 1 | ||
cf = files[0] | ||
assert cf.startswith('kernel') | ||
assert cf.endswith('.json') | ||
|
||
# Send SIGTERM to shut down | ||
p.terminate() | ||
if PY3: | ||
_, stderr = p.communicate(timeout=WAIT_TIME) | ||
assert cf in stderr.decode('utf-8', 'replace') | ||
else: | ||
hacky_wait(p) | ||
finally: | ||
shutil.rmtree(runtime_dir) | ||
shutil.rmtree(startup_dir) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#!/usr/bin/env python | ||
from jupyter_client.kernelapp import main | ||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters