@@ -69,6 +69,7 @@ def __init__(self, config: Config) -> None:
6969 self ._started = False
7070 self ._infrastructure_adapters : list [Any ] = []
7171 self ._task_scheduler : Any | None = None
72+ self ._background_tasks : list [asyncio .Task [Any ]] = []
7273 self ._wiring_counts : dict [str , int ] = {}
7374
7475 # Register config and container as singleton beans (injectable like Spring's ApplicationContext)
@@ -236,6 +237,14 @@ async def stop(self) -> None:
236237 """
237238 shutdown_timeout = float (self ._config .get ("pyfly.context.shutdown-timeout" , 30 ))
238239
240+ # Cancel tracked background tasks
241+ for task in self ._background_tasks :
242+ if not task .done ():
243+ task .cancel ()
244+ if self ._background_tasks :
245+ await asyncio .gather (* self ._background_tasks , return_exceptions = True )
246+ self ._background_tasks .clear ()
247+
239248 # Stop task scheduler
240249 if self ._task_scheduler is not None :
241250 try :
@@ -594,7 +603,8 @@ def _wire_message_listeners(self) -> None:
594603 topic = getattr (method , "__pyfly_listener_topic__" , "" )
595604 group = getattr (method , "__pyfly_listener_group__" , None )
596605 # MessageBrokerPort.subscribe is async; defer via create_task
597- asyncio .get_event_loop ().create_task (broker .subscribe (topic , method , group = group ))
606+ task = asyncio .get_event_loop ().create_task (broker .subscribe (topic , method , group = group ))
607+ self ._background_tasks .append (task )
598608 count += 1
599609 self ._wiring_counts ["message_listeners" ] = count
600610 if count :
@@ -652,7 +662,8 @@ def _wire_scheduled(self) -> None:
652662 self ._wiring_counts ["scheduled" ] = count
653663 if count :
654664 self ._task_scheduler = scheduler
655- asyncio .get_event_loop ().create_task (scheduler .start ())
665+ task = asyncio .get_event_loop ().create_task (scheduler .start ())
666+ self ._background_tasks .append (task )
656667 logger .debug ("Discovered %d @scheduled method(s)" , count )
657668
658669 def _wire_async_methods (self ) -> None :
@@ -716,6 +727,20 @@ def _wire_shell_commands(self) -> None:
716727 if not getattr (method , "__pyfly_shell_method__" , False ):
717728 continue
718729
730+ # Check @shell_method_availability
731+ availability_checker_name = getattr (method , "__pyfly_shell_availability__" , None )
732+ if availability_checker_name :
733+ checker = getattr (reg .instance , availability_checker_name , None )
734+ if checker is not None :
735+ reason = checker ()
736+ if reason :
737+ logger .debug (
738+ "Shell command '%s' unavailable: %s" ,
739+ getattr (method , "__pyfly_shell_key__" , attr_name ),
740+ reason ,
741+ )
742+ continue
743+
719744 from pyfly .shell .param_inference import infer_params
720745
721746 key = getattr (method , "__pyfly_shell_key__" , attr_name )
0 commit comments