5
5
import inspect
6
6
import os
7
7
import sys
8
- from datetime import datetime , timezone
8
+ from datetime import datetime , timedelta , timezone
9
9
from typing import TYPE_CHECKING , Any , Awaitable , Callable , TypeVar , cast
10
10
11
11
from apify_client import ApifyClientAsync
@@ -301,6 +301,7 @@ async def exit(
301
301
exit_code : int = 0 ,
302
302
event_listeners_timeout_secs : float | None = EVENT_LISTENERS_TIMEOUT_SECS ,
303
303
status_message : str | None = None ,
304
+ cleanup_timeout : timedelta = timedelta (seconds = 30 ),
304
305
) -> None :
305
306
"""Exit the actor instance.
306
307
@@ -314,11 +315,13 @@ async def exit(
314
315
exit_code (int, optional): The exit code with which the actor should fail (defaults to `0`).
315
316
event_listeners_timeout_secs (float, optional): How long should the actor wait for actor event listeners to finish before exiting.
316
317
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.
317
319
"""
318
320
return await cls ._get_default_instance ().exit (
319
321
exit_code = exit_code ,
320
322
event_listeners_timeout_secs = event_listeners_timeout_secs ,
321
323
status_message = status_message ,
324
+ cleanup_timeout = cleanup_timeout ,
322
325
)
323
326
324
327
async def _exit_internal (
@@ -327,6 +330,7 @@ async def _exit_internal(
327
330
exit_code : int = 0 ,
328
331
event_listeners_timeout_secs : float | None = EVENT_LISTENERS_TIMEOUT_SECS ,
329
332
status_message : str | None = None ,
333
+ cleanup_timeout : timedelta = timedelta (seconds = 30 ),
330
334
) -> None :
331
335
self ._raise_if_not_initialized ()
332
336
@@ -336,21 +340,23 @@ async def _exit_internal(
336
340
337
341
self .log .info ('Exiting actor' , extra = {'exit_code' : exit_code })
338
342
339
- await self ._cancel_event_emitting_intervals ()
343
+ async def finalize () -> None :
344
+ await self ._cancel_event_emitting_intervals ()
340
345
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
345
350
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 )
348
353
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 )
351
356
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 )
353
358
359
+ await asyncio .wait_for (finalize (), cleanup_timeout .total_seconds ())
354
360
self ._is_initialized = False
355
361
356
362
if is_running_in_ipython ():
0 commit comments