restarter.py
111 lines
| 3.5 KiB
| text/x-python
|
PythonLexer
/ jupyter_client / restarter.py
MinRK
|
r10295 | """A basic kernel monitor with autorestarting. | ||
This watches a kernel's state using KernelManager.is_alive and auto | ||||
restarts the kernel if it dies. | ||||
It is an incomplete base class, and must be subclassed. | ||||
""" | ||||
MinRK
|
r17713 | # Copyright (c) IPython Development Team. | ||
# Distributed under the terms of the Modified BSD License. | ||||
MinRK
|
r10295 | |||
from IPython.config.configurable import LoggingConfigurable | ||||
from IPython.utils.traitlets import ( | ||||
MinRK
|
r10334 | Instance, Float, Dict, Bool, Integer, | ||
MinRK
|
r10295 | ) | ||
class KernelRestarter(LoggingConfigurable): | ||||
"""Monitor and autorestart a kernel.""" | ||||
Min RK
|
r20951 | kernel_manager = Instance('jupyter_client.KernelManager') | ||
Min RK
|
r20949 | |||
MinRK
|
r17713 | debug = Bool(False, config=True, | ||
help="""Whether to include every poll event in debugging output. | ||||
Min RK
|
r20949 | |||
MinRK
|
r17713 | Has to be set explicitly, because there will be *a lot* of output. | ||
""" | ||||
) | ||||
Min RK
|
r20949 | |||
MinRK
|
r10295 | time_to_dead = Float(3.0, config=True, | ||
help="""Kernel heartbeat interval in seconds.""" | ||||
) | ||||
MinRK
|
r10314 | |||
MinRK
|
r10334 | restart_limit = Integer(5, config=True, | ||
help="""The number of consecutive autorestarts before the kernel is presumed dead.""" | ||||
) | ||||
MinRK
|
r10318 | _restarting = Bool(False) | ||
MinRK
|
r10334 | _restart_count = Integer(0) | ||
MinRK
|
r10314 | |||
MinRK
|
r10313 | callbacks = Dict() | ||
def _callbacks_default(self): | ||||
return dict(restart=[], dead=[]) | ||||
MinRK
|
r10295 | |||
def start(self): | ||||
"""Start the polling of the kernel.""" | ||||
raise NotImplementedError("Must be implemented in a subclass") | ||||
def stop(self): | ||||
"""Stop the kernel polling.""" | ||||
raise NotImplementedError("Must be implemented in a subclass") | ||||
MinRK
|
r10313 | def add_callback(self, f, event='restart'): | ||
"""register a callback to fire on a particular event | ||||
Possible values for event: | ||||
'restart' (default): kernel has died, and will be restarted. | ||||
'dead': restart has failed, kernel will be left dead. | ||||
""" | ||||
self.callbacks[event].append(f) | ||||
def remove_callback(self, f, event='restart'): | ||||
"""unregister a callback to fire on a particular event | ||||
Possible values for event: | ||||
'restart' (default): kernel has died, and will be restarted. | ||||
'dead': restart has failed, kernel will be left dead. | ||||
MinRK
|
r10295 | |||
MinRK
|
r10313 | """ | ||
MinRK
|
r10295 | try: | ||
MinRK
|
r10313 | self.callbacks[event].remove(f) | ||
MinRK
|
r10295 | except ValueError: | ||
pass | ||||
MinRK
|
r10314 | def _fire_callbacks(self, event): | ||
"""fire our callbacks for a particular event""" | ||||
for callback in self.callbacks[event]: | ||||
try: | ||||
callback() | ||||
except Exception as e: | ||||
self.log.error("KernelRestarter: %s callback %r failed", event, callback, exc_info=True) | ||||
MinRK
|
r10295 | def poll(self): | ||
MinRK
|
r17713 | if self.debug: | ||
self.log.debug('Polling kernel...') | ||||
MinRK
|
r10295 | if not self.kernel_manager.is_alive(): | ||
MinRK
|
r10314 | if self._restarting: | ||
MinRK
|
r10334 | self._restart_count += 1 | ||
else: | ||||
self._restart_count = 1 | ||||
if self._restart_count >= self.restart_limit: | ||||
MinRK
|
r10314 | self.log.warn("KernelRestarter: restart failed") | ||
self._fire_callbacks('dead') | ||||
self._restarting = False | ||||
MinRK
|
r10334 | self._restart_count = 0 | ||
MinRK
|
r10320 | self.stop() | ||
MinRK
|
r10314 | else: | ||
MinRK
|
r10334 | self.log.info('KernelRestarter: restarting kernel (%i/%i)', | ||
self._restart_count, | ||||
self.restart_limit | ||||
) | ||||
MinRK
|
r10314 | self._fire_callbacks('restart') | ||
self.kernel_manager.restart_kernel(now=True) | ||||
self._restarting = True | ||||
MinRK
|
r10320 | else: | ||
if self._restarting: | ||||
self.log.debug("KernelRestarter: restart apparently succeeded") | ||||
self._restarting = False | ||||