55"""
66# Copyright (c) Jupyter Development Team.
77# Distributed under the terms of the Modified BSD License.
8+ import asyncio
89import os
910from collections import defaultdict
1011from datetime import datetime
@@ -209,16 +210,14 @@ async def start_kernel(self, kernel_id=None, path=None, **kwargs):
209210 kwargs ["kernel_id" ] = kernel_id
210211 kernel_id = await ensure_async (self .pinned_superclass .start_kernel (self , ** kwargs ))
211212 self ._kernel_connections [kernel_id ] = 0
212- self ._kernel_ports [kernel_id ] = self ._kernels [kernel_id ].ports
213- self .start_watching_activity (kernel_id )
213+ asyncio .ensure_future (self ._finish_kernel_start (kernel_id ))
214+ # add busy/activity markers:
215+ kernel = self .get_kernel (kernel_id )
216+ kernel .execution_state = "starting"
217+ kernel .reason = ""
218+ kernel .last_activity = utcnow ()
214219 self .log .info ("Kernel started: %s" % kernel_id )
215220 self .log .debug ("Kernel args: %r" % kwargs )
216- # register callback for failed auto-restart
217- self .add_restart_callback (
218- kernel_id ,
219- lambda : self ._handle_kernel_died (kernel_id ),
220- "dead" ,
221- )
222221
223222 # Increase the metric of number of kernels running
224223 # for the relevant kernel type by 1
@@ -233,6 +232,24 @@ async def start_kernel(self, kernel_id=None, path=None, **kwargs):
233232
234233 return kernel_id
235234
235+ async def _finish_kernel_start (self , kernel_id ):
236+ km = self .get_kernel (kernel_id )
237+ if hasattr (km , "ready" ):
238+ try :
239+ await km .ready
240+ except Exception :
241+ self .log .exception (km .ready .exception ())
242+ return
243+
244+ self ._kernel_ports [kernel_id ] = km .ports
245+ self .start_watching_activity (kernel_id )
246+ # register callback for failed auto-restart
247+ self .add_restart_callback (
248+ kernel_id ,
249+ lambda : self ._handle_kernel_died (kernel_id ),
250+ "dead" ,
251+ )
252+
236253 def ports_changed (self , kernel_id ):
237254 """Used by ZMQChannelsHandler to determine how to coordinate nudge and replays.
238255
@@ -448,6 +465,8 @@ def kernel_model(self, kernel_id):
448465 "execution_state" : kernel .execution_state ,
449466 "connections" : self ._kernel_connections .get (kernel_id , 0 ),
450467 }
468+ if getattr (kernel , "reason" , None ):
469+ model ["reason" ] = kernel .reason
451470 return model
452471
453472 def list_kernels (self ):
@@ -479,6 +498,7 @@ def start_watching_activity(self, kernel_id):
479498 kernel = self ._kernels [kernel_id ]
480499 # add busy/activity markers:
481500 kernel .execution_state = "starting"
501+ kernel .reason = ""
482502 kernel .last_activity = utcnow ()
483503 kernel ._activity_stream = kernel .connect_iopub ()
484504 session = Session (
@@ -507,7 +527,7 @@ def record_activity(msg_list):
507527 def stop_watching_activity (self , kernel_id ):
508528 """Stop watching IOPub messages on a kernel for activity."""
509529 kernel = self ._kernels [kernel_id ]
510- if kernel . _activity_stream :
530+ if getattr ( kernel , " _activity_stream" , None ) :
511531 kernel ._activity_stream .close ()
512532 kernel ._activity_stream = None
513533
@@ -561,6 +581,17 @@ async def cull_kernels(self):
561581
562582 async def cull_kernel_if_idle (self , kernel_id ):
563583 kernel = self ._kernels [kernel_id ]
584+
585+ if getattr (kernel , "execution_state" ) == "dead" :
586+ self .log .warning (
587+ "Culling '%s' dead kernel '%s' (%s)." ,
588+ kernel .execution_state ,
589+ kernel .kernel_name ,
590+ kernel_id ,
591+ )
592+ await ensure_async (self .shutdown_kernel (kernel_id ))
593+ return
594+
564595 if hasattr (
565596 kernel , "last_activity"
566597 ): # last_activity is monkey-patched, so ensure that has occurred
0 commit comments