55import  inspect 
66import  os 
77import  sys 
8- from  datetime  import  datetime , timezone 
8+ from  datetime  import  datetime , timedelta ,  timezone 
99from  typing  import  TYPE_CHECKING , Any , Awaitable , Callable , TypeVar , cast 
1010
1111from  apify_client  import  ApifyClientAsync 
@@ -301,6 +301,7 @@ async def exit(
301301        exit_code : int  =  0 ,
302302        event_listeners_timeout_secs : float  |  None  =  EVENT_LISTENERS_TIMEOUT_SECS ,
303303        status_message : str  |  None  =  None ,
304+         cleanup_timeout : timedelta  =  timedelta (seconds = 30 ),
304305    ) ->  None :
305306        """Exit the actor instance. 
306307
@@ -314,11 +315,13 @@ async def exit(
314315            exit_code (int, optional): The exit code with which the actor should fail (defaults to `0`). 
315316            event_listeners_timeout_secs (float, optional): How long should the actor wait for actor event listeners to finish before exiting. 
316317            status_message (str, optional): The final status message that the actor should display. 
318+             cleanup_timeout (timedelta, optional): How long we should wait for event listeners. 
317319        """ 
318320        return  await  cls ._get_default_instance ().exit (
319321            exit_code = exit_code ,
320322            event_listeners_timeout_secs = event_listeners_timeout_secs ,
321323            status_message = status_message ,
324+             cleanup_timeout = cleanup_timeout ,
322325        )
323326
324327    async  def  _exit_internal (
@@ -327,6 +330,7 @@ async def _exit_internal(
327330        exit_code : int  =  0 ,
328331        event_listeners_timeout_secs : float  |  None  =  EVENT_LISTENERS_TIMEOUT_SECS ,
329332        status_message : str  |  None  =  None ,
333+         cleanup_timeout : timedelta  =  timedelta (seconds = 30 ),
330334    ) ->  None :
331335        self ._raise_if_not_initialized ()
332336
@@ -336,21 +340,23 @@ async def _exit_internal(
336340
337341        self .log .info ('Exiting actor' , extra = {'exit_code' : exit_code })
338342
339-         await  self ._cancel_event_emitting_intervals ()
343+         async  def  finalize () ->  None :
344+             await  self ._cancel_event_emitting_intervals ()
340345
341-         # Send final persist state event 
342-         if  not  self ._was_final_persist_state_emitted :
343-             self ._event_manager .emit (ActorEventTypes .PERSIST_STATE , {'isMigrating' : False })
344-             self ._was_final_persist_state_emitted  =  True 
346+              # Send final persist state event 
347+              if  not  self ._was_final_persist_state_emitted :
348+                  self ._event_manager .emit (ActorEventTypes .PERSIST_STATE , {'isMigrating' : False })
349+                  self ._was_final_persist_state_emitted  =  True 
345350
346-         if  status_message  is  not None :
347-             await  self .set_status_message (status_message , is_terminal = True )
351+              if  status_message  is  not None :
352+                  await  self .set_status_message (status_message , is_terminal = True )
348353
349-         # Sleep for a bit so that the listeners have a chance to trigger 
350-         await  asyncio .sleep (0.1 )
354+              # Sleep for a bit so that the listeners have a chance to trigger 
355+              await  asyncio .sleep (0.1 )
351356
352-         await  self ._event_manager .close (event_listeners_timeout_secs = event_listeners_timeout_secs )
357+              await  self ._event_manager .close (event_listeners_timeout_secs = event_listeners_timeout_secs )
353358
359+         await  asyncio .wait_for (finalize (), cleanup_timeout .total_seconds ())
354360        self ._is_initialized  =  False 
355361
356362        if  is_running_in_ipython ():
0 commit comments