@@ -378,7 +378,7 @@ def __repr__(self):
378378 fields ["profile_dir" ] = repr (profile_dir )
379379
380380 if self .controller :
381- fields ["controller" ] = "<running >"
381+ fields ["controller" ] = f"< { self . controller . state } >"
382382 if self .engines :
383383 fields ["engine_sets" ] = list (self .engines )
384384
@@ -398,7 +398,7 @@ def _cls_str(cls):
398398
399399 cluster_info ["class" ] = _cls_str (self .__class__ )
400400
401- if self .controller :
401+ if self .controller and self . controller . state != 'after' :
402402 d ["controller" ] = {
403403 "class" : _cls_str (self .controller_launcher_class ),
404404 "state" : None ,
@@ -411,7 +411,8 @@ def _cls_str(cls):
411411 }
412412 sets = d ["engines" ]["sets" ]
413413 for engine_set_id , engine_launcher in self .engines .items ():
414- sets [engine_set_id ] = engine_launcher .to_dict ()
414+ if engine_launcher .state != 'after' :
415+ sets [engine_set_id ] = engine_launcher .to_dict ()
415416 return d
416417
417418 @classmethod
@@ -447,13 +448,15 @@ def from_dict(cls, d, **kwargs):
447448 )
448449 except launcher .NotRunning as e :
449450 self .log .error (f"Controller for { cluster_key } not running: { e } " )
451+ else :
452+ self .controller .on_stop (self ._controller_stopped )
450453
451454 engine_info = d .get ("engines" )
452455 if engine_info :
453456 cls = self .engine_launcher_class = import_item (engine_info ["class" ])
454457 for engine_set_id , engine_state in engine_info .get ("sets" , {}).items ():
455458 try :
456- self .engines [engine_set_id ] = cls .from_dict (
459+ self .engines [engine_set_id ] = engine_set = cls .from_dict (
457460 engine_state ,
458461 engine_set_id = engine_set_id ,
459462 parent = self ,
@@ -462,6 +465,9 @@ def from_dict(cls, d, **kwargs):
462465 self .log .error (
463466 f"Engine set { cluster_key } { engine_set_id } not running: { e } "
464467 )
468+ else :
469+ engine_set .on_stop (partial (self ._engines_stopped , engine_set_id ))
470+
465471 # check if state changed
466472 if self .to_dict () != d :
467473 # if so, update our cluster file
@@ -527,7 +533,9 @@ def update_cluster_file(self):
527533 # setting cluster_file='' disables saving to disk
528534 return
529535
530- if not self .controller and not self .engines :
536+ if (not self .controller or self .controller .state == 'after' ) and not any (
537+ es .state == 'after' for es in self .engines .values ()
538+ ):
531539 self .remove_cluster_file ()
532540 else :
533541 self .write_cluster_file ()
@@ -594,6 +602,7 @@ def add_args(args):
594602 def _controller_stopped (self , stop_data = None ):
595603 """Callback when a controller stops"""
596604 self .log .info (f"Controller stopped: { stop_data } " )
605+ self .update_cluster_file ()
597606
598607 async def start_engines (self , n = None , engine_set_id = None , ** kwargs ):
599608 """Start an engine set
@@ -839,6 +848,17 @@ def load_clusters(
839848 - single profile by name
840849 - all IPython profiles, if nothing else specified
841850 """
851+
852+ # first, check our current clusters
853+ for key , cluster in list (self .clusters .items ()):
854+ # remove stopped clusters
855+ # but not *new* clusters that haven't started yet
856+ if (cluster .controller and cluster .controller .state == 'after' ) and all (
857+ es .state == 'after' for es in cluster .engines .values ()
858+ ):
859+ self .log .info ("Removing stopped cluster {key}" )
860+ self .clusters .pop (key )
861+
842862 if profile_dirs is None :
843863 if profile_dir is not None :
844864 profile_dirs = [profile_dir ]
0 commit comments