4
4
import os
5
5
import sys
6
6
from contextlib import suppress
7
- from datetime import timedelta
7
+ from datetime import datetime , timedelta , timezone
8
8
from typing import TYPE_CHECKING , Any , Callable , Literal , TypeVar , cast , overload
9
9
10
10
from lazy_object_proxy import Proxy
@@ -693,7 +693,7 @@ async def start(
693
693
content_type : str | None = None ,
694
694
build : str | None = None ,
695
695
memory_mbytes : int | None = None ,
696
- timeout : timedelta | None = None ,
696
+ timeout : timedelta | None | Literal [ 'RemainingTime' ] = None ,
697
697
wait_for_finish : int | None = None ,
698
698
webhooks : list [Webhook ] | None = None ,
699
699
) -> ActorRun :
@@ -711,7 +711,8 @@ async def start(
711
711
memory_mbytes: Memory limit for the run, in megabytes. By default, the run uses a memory limit specified
712
712
in the default run configuration for the Actor.
713
713
timeout: Optional timeout for the run, in seconds. By default, the run uses timeout specified in
714
- the default run configuration for the Actor.
714
+ the default run configuration for the Actor. Using `RemainingTime` will set timeout of the other Actor
715
+ to the time remaining from this Actor timeout.
715
716
wait_for_finish: The maximum number of seconds the server waits for the run to finish. By default,
716
717
it is 0, the maximum value is 300.
717
718
webhooks: Optional ad-hoc webhooks (https://docs.apify.com/webhooks/ad-hoc-webhooks) associated with
@@ -732,18 +733,39 @@ async def start(
732
733
else :
733
734
serialized_webhooks = None
734
735
736
+ if timeout == 'RemainingTime' :
737
+ actor_start_timeout = self ._get_remaining_time ()
738
+ elif timeout is None :
739
+ actor_start_timeout = None
740
+ elif isinstance (timeout , timedelta ):
741
+ actor_start_timeout = timeout
742
+ else :
743
+ raise ValueError (f'Invalid timeout { timeout !r} : expected `None`, `"RemainingTime"`, or a `timedelta`.' )
744
+
735
745
api_result = await client .actor (actor_id ).start (
736
746
run_input = run_input ,
737
747
content_type = content_type ,
738
748
build = build ,
739
749
memory_mbytes = memory_mbytes ,
740
- timeout_secs = int (timeout .total_seconds ()) if timeout is not None else None ,
750
+ timeout_secs = int (actor_start_timeout .total_seconds ()) if actor_start_timeout is not None else None ,
741
751
wait_for_finish = wait_for_finish ,
742
752
webhooks = serialized_webhooks ,
743
753
)
744
754
745
755
return ActorRun .model_validate (api_result )
746
756
757
+ def _get_remaining_time (self ) -> timedelta | None :
758
+ """Get time remaining from the actor timeout. Returns `None` if not on an Apify platform."""
759
+ if self .is_at_home () and self .configuration .timeout_at :
760
+ return self .configuration .timeout_at - datetime .now (tz = timezone .utc )
761
+
762
+ self .log .warning (
763
+ 'Returning `None` instead of remaining time. Using `RemainingTime` argument is only possible when the Actor'
764
+ ' is running on the Apify platform and when the timeout for the Actor run is set. '
765
+ f'{ self .is_at_home ()= } , { self .configuration .timeout_at = } '
766
+ )
767
+ return None
768
+
747
769
async def abort (
748
770
self ,
749
771
run_id : str ,
@@ -787,7 +809,7 @@ async def call(
787
809
content_type : str | None = None ,
788
810
build : str | None = None ,
789
811
memory_mbytes : int | None = None ,
790
- timeout : timedelta | None = None ,
812
+ timeout : timedelta | None | Literal [ 'RemainingTime' ] = None ,
791
813
webhooks : list [Webhook ] | None = None ,
792
814
wait : timedelta | None = None ,
793
815
) -> ActorRun | None :
@@ -805,7 +827,8 @@ async def call(
805
827
memory_mbytes: Memory limit for the run, in megabytes. By default, the run uses a memory limit specified
806
828
in the default run configuration for the Actor.
807
829
timeout: Optional timeout for the run, in seconds. By default, the run uses timeout specified in
808
- the default run configuration for the Actor.
830
+ the default run configuration for the Actor. Using `RemainingTime` will set timeout of the other Actor
831
+ to the time remaining from this Actor timeout.
809
832
webhooks: Optional webhooks (https://docs.apify.com/webhooks) associated with the Actor run, which can
810
833
be used to receive a notification, e.g. when the Actor finished or failed. If you already have
811
834
a webhook set up for the Actor, you do not have to add it again here.
@@ -826,12 +849,21 @@ async def call(
826
849
else :
827
850
serialized_webhooks = None
828
851
852
+ if timeout == 'RemainingTime' :
853
+ actor_call_timeout = self ._get_remaining_time ()
854
+ elif timeout is None :
855
+ actor_call_timeout = None
856
+ elif isinstance (timeout , timedelta ):
857
+ actor_call_timeout = timeout
858
+ else :
859
+ raise ValueError (f'Invalid timeout { timeout !r} : expected `None`, `"RemainingTime"`, or a `timedelta`.' )
860
+
829
861
api_result = await client .actor (actor_id ).call (
830
862
run_input = run_input ,
831
863
content_type = content_type ,
832
864
build = build ,
833
865
memory_mbytes = memory_mbytes ,
834
- timeout_secs = int (timeout .total_seconds ()) if timeout is not None else None ,
866
+ timeout_secs = int (actor_call_timeout .total_seconds ()) if actor_call_timeout is not None else None ,
835
867
webhooks = serialized_webhooks ,
836
868
wait_secs = int (wait .total_seconds ()) if wait is not None else None ,
837
869
)
0 commit comments