Skip to content

Commit 9817e71

Browse files
committed
Add support for async kernel management via subclassing
Introduced async subclasses that derive from synchronous classes, overriding appropriate methods with async support.
1 parent e813086 commit 9817e71

File tree

8 files changed

+734
-24
lines changed

8 files changed

+734
-24
lines changed

jupyter_client/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
from .connect import *
55
from .launcher import *
66
from .client import KernelClient
7-
from .manager import KernelManager, run_kernel
7+
from .manager import KernelManager, AsyncKernelManager, run_kernel
88
from .blocking import BlockingKernelClient
9-
from .multikernelmanager import MultiKernelManager
9+
from .multikernelmanager import MultiKernelManager, AsyncMultiKernelManager

jupyter_client/ioloop/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
from .manager import IOLoopKernelManager
2-
from .restarter import IOLoopKernelRestarter
1+
from .manager import IOLoopKernelManager, AsyncIOLoopKernelManager
2+
from .restarter import IOLoopKernelRestarter, AsyncIOLoopKernelRestarter

jupyter_client/ioloop/manager.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
Type,
1414
)
1515

16-
from jupyter_client.manager import KernelManager
17-
from .restarter import IOLoopKernelRestarter
16+
from jupyter_client.manager import KernelManager, AsyncKernelManager
17+
from .restarter import IOLoopKernelRestarter, AsyncIOLoopKernelRestarter
1818

1919

2020
def as_zmqstream(f):
@@ -23,9 +23,11 @@ def wrapped(self, *args, **kwargs):
2323
return ZMQStream(socket, self.loop)
2424
return wrapped
2525

26+
2627
class IOLoopKernelManager(KernelManager):
2728

2829
loop = Instance('tornado.ioloop.IOLoop')
30+
2931
def _loop_default(self):
3032
return ioloop.IOLoop.current()
3133

@@ -60,3 +62,43 @@ def stop_restarter(self):
6062
connect_iopub = as_zmqstream(KernelManager.connect_iopub)
6163
connect_stdin = as_zmqstream(KernelManager.connect_stdin)
6264
connect_hb = as_zmqstream(KernelManager.connect_hb)
65+
66+
67+
class AsyncIOLoopKernelManager(AsyncKernelManager):
68+
69+
loop = Instance('tornado.ioloop.IOLoop')
70+
71+
def _loop_default(self):
72+
return ioloop.IOLoop.current()
73+
74+
restarter_class = Type(
75+
default_value=AsyncIOLoopKernelRestarter,
76+
klass=AsyncIOLoopKernelRestarter,
77+
help=(
78+
'Type of KernelRestarter to use. '
79+
'Must be a subclass of IOLoopKernelRestarter.\n'
80+
'Override this to customize how kernel restarts are managed.'
81+
),
82+
config=True,
83+
)
84+
_restarter = Instance('jupyter_client.ioloop.AsyncIOLoopKernelRestarter', allow_none=True)
85+
86+
def start_restarter(self):
87+
if self.autorestart and self.has_kernel:
88+
if self._restarter is None:
89+
self._restarter = self.restarter_class(
90+
kernel_manager=self, loop=self.loop,
91+
parent=self, log=self.log
92+
)
93+
self._restarter.start()
94+
95+
def stop_restarter(self):
96+
if self.autorestart:
97+
if self._restarter is not None:
98+
self._restarter.stop()
99+
self._restarter = None
100+
101+
connect_shell = as_zmqstream(AsyncKernelManager.connect_shell)
102+
connect_iopub = as_zmqstream(AsyncKernelManager.connect_iopub)
103+
connect_stdin = as_zmqstream(AsyncKernelManager.connect_stdin)
104+
connect_hb = as_zmqstream(AsyncKernelManager.connect_hb)

jupyter_client/ioloop/restarter.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,20 @@
1010
from __future__ import absolute_import
1111
import warnings
1212

13+
from tornado import gen
1314
from zmq.eventloop import ioloop
1415

1516
from jupyter_client.restarter import KernelRestarter
1617
from traitlets import (
1718
Instance,
1819
)
1920

21+
2022
class IOLoopKernelRestarter(KernelRestarter):
2123
"""Monitor and autorestart a kernel."""
2224

2325
loop = Instance('tornado.ioloop.IOLoop')
26+
2427
def _loop_default(self):
2528
warnings.warn("IOLoopKernelRestarter.loop is deprecated in jupyter-client 5.2",
2629
DeprecationWarning, stacklevel=4,
@@ -42,3 +45,48 @@ def stop(self):
4245
if self._pcallback is not None:
4346
self._pcallback.stop()
4447
self._pcallback = None
48+
49+
50+
class AsyncIOLoopKernelRestarter(IOLoopKernelRestarter):
51+
52+
def start(self):
53+
"""Start the polling of the kernel."""
54+
if self._pcallback is None:
55+
self._pcallback = ioloop.PeriodicCallback(
56+
self.poll, 1000*self.time_to_dead,
57+
)
58+
self._pcallback.start()
59+
60+
@gen.coroutine
61+
def poll(self):
62+
if self.debug:
63+
self.log.debug('Polling kernel...')
64+
is_alive = yield gen.maybe_future(self.kernel_manager.is_alive())
65+
if not is_alive:
66+
if self._restarting:
67+
self._restart_count += 1
68+
else:
69+
self._restart_count = 1
70+
71+
if self._restart_count >= self.restart_limit:
72+
self.log.warning("AsyncIOLoopKernelRestarter: restart failed")
73+
self._fire_callbacks('dead')
74+
self._restarting = False
75+
self._restart_count = 0
76+
self.stop()
77+
else:
78+
newports = self.random_ports_until_alive and self._initial_startup
79+
self.log.info('AsyncIOLoopKernelRestarter: restarting kernel (%i/%i), %s random ports',
80+
self._restart_count,
81+
self.restart_limit,
82+
'new' if newports else 'keep'
83+
)
84+
self._fire_callbacks('restart')
85+
yield self.kernel_manager.restart_kernel(now=True, newports=newports)
86+
self._restarting = True
87+
else:
88+
if self._initial_startup:
89+
self._initial_startup = False
90+
if self._restarting:
91+
self.log.debug("AsyncIOLoopKernelRestarter: restart apparently succeeded")
92+
self._restarting = False

0 commit comments

Comments
 (0)