@@ -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,37 @@ async def start(
732
733
else :
733
734
serialized_webhooks = None
734
735
736
+ if timeout == 'RemainingTime' :
737
+ actor_start_timeout = await self ._get_remaining_time (client )
738
+ elif isinstance (timeout , str ):
739
+ raise ValueError (f'`timeout` can be `None`, `RemainingTime` or `timedelta` instance, but is { timeout = } ' )
740
+ else :
741
+ actor_start_timeout = timeout
742
+
735
743
api_result = await client .actor (actor_id ).start (
736
744
run_input = run_input ,
737
745
content_type = content_type ,
738
746
build = build ,
739
747
memory_mbytes = memory_mbytes ,
740
- timeout_secs = int (timeout .total_seconds ()) if timeout is not None else None ,
748
+ timeout_secs = int (actor_start_timeout .total_seconds ()) if actor_start_timeout is not None else None ,
741
749
wait_for_finish = wait_for_finish ,
742
750
webhooks = serialized_webhooks ,
743
751
)
744
752
745
753
return ActorRun .model_validate (api_result )
746
754
755
+ async def _get_remaining_time (self , client : ApifyClientAsync ) -> timedelta | None :
756
+ """Get time remaining from the actor timeout. Returns `None` if not on Apify platform."""
757
+ if self .is_at_home () and self .configuration .actor_run_id :
758
+ run_data = await client .run (self .configuration .actor_run_id ).get ()
759
+ if run_data is not None and (timeout := run_data .get ('options' , {}).get ('timeoutSecs' , None )):
760
+ runtime = timedelta (seconds = run_data .get ('runTimeSecs' , None ))
761
+ remaining_time = timeout - runtime
762
+ return timedelta (seconds = remaining_time )
763
+
764
+ self .log .warning ('Using `RemainingTime` argument for timeout outside of the Apify platform. Returning `None`' )
765
+ return None
766
+
747
767
async def abort (
748
768
self ,
749
769
run_id : str ,
@@ -787,7 +807,7 @@ async def call(
787
807
content_type : str | None = None ,
788
808
build : str | None = None ,
789
809
memory_mbytes : int | None = None ,
790
- timeout : timedelta | None = None ,
810
+ timeout : timedelta | None | Literal [ 'RemainingTime' ] = None ,
791
811
webhooks : list [Webhook ] | None = None ,
792
812
wait : timedelta | None = None ,
793
813
) -> ActorRun | None :
@@ -805,7 +825,8 @@ async def call(
805
825
memory_mbytes: Memory limit for the run, in megabytes. By default, the run uses a memory limit specified
806
826
in the default run configuration for the Actor.
807
827
timeout: Optional timeout for the run, in seconds. By default, the run uses timeout specified in
808
- the default run configuration for the Actor.
828
+ the default run configuration for the Actor. Using `RemainingTime` will set timeout of the other actor
829
+ to the time remaining from this actor timeout.
809
830
webhooks: Optional webhooks (https://docs.apify.com/webhooks) associated with the Actor run, which can
810
831
be used to receive a notification, e.g. when the Actor finished or failed. If you already have
811
832
a webhook set up for the Actor, you do not have to add it again here.
@@ -826,12 +847,19 @@ async def call(
826
847
else :
827
848
serialized_webhooks = None
828
849
850
+ if timeout == 'RemainingTime' :
851
+ actor_call_timeout = await self ._get_remaining_time (client )
852
+ elif isinstance (timeout , str ):
853
+ raise ValueError (f'`timeout` can be `None`, `RemainingTime` or `timedelta` instance, but is { timeout = } ' )
854
+ else :
855
+ actor_call_timeout = timeout
856
+
829
857
api_result = await client .actor (actor_id ).call (
830
858
run_input = run_input ,
831
859
content_type = content_type ,
832
860
build = build ,
833
861
memory_mbytes = memory_mbytes ,
834
- timeout_secs = int (timeout .total_seconds ()) if timeout is not None else None ,
862
+ timeout_secs = int (actor_call_timeout .total_seconds ()) if actor_call_timeout is not None else None ,
835
863
webhooks = serialized_webhooks ,
836
864
wait_secs = int (wait .total_seconds ()) if wait is not None else None ,
837
865
)
0 commit comments