Skip to content

Commit a992dcb

Browse files
committed
draft
1 parent 295009e commit a992dcb

File tree

1 file changed

+34
-6
lines changed

1 file changed

+34
-6
lines changed

src/apify/_actor.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,7 @@ async def start(
693693
content_type: str | None = None,
694694
build: str | None = None,
695695
memory_mbytes: int | None = None,
696-
timeout: timedelta | None = None,
696+
timeout: timedelta | None | Literal['RemainingTime'] = None,
697697
wait_for_finish: int | None = None,
698698
webhooks: list[Webhook] | None = None,
699699
) -> ActorRun:
@@ -711,7 +711,8 @@ async def start(
711711
memory_mbytes: Memory limit for the run, in megabytes. By default, the run uses a memory limit specified
712712
in the default run configuration for the Actor.
713713
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.
715716
wait_for_finish: The maximum number of seconds the server waits for the run to finish. By default,
716717
it is 0, the maximum value is 300.
717718
webhooks: Optional ad-hoc webhooks (https://docs.apify.com/webhooks/ad-hoc-webhooks) associated with
@@ -732,18 +733,37 @@ async def start(
732733
else:
733734
serialized_webhooks = None
734735

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+
735743
api_result = await client.actor(actor_id).start(
736744
run_input=run_input,
737745
content_type=content_type,
738746
build=build,
739747
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,
741749
wait_for_finish=wait_for_finish,
742750
webhooks=serialized_webhooks,
743751
)
744752

745753
return ActorRun.model_validate(api_result)
746754

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+
747767
async def abort(
748768
self,
749769
run_id: str,
@@ -787,7 +807,7 @@ async def call(
787807
content_type: str | None = None,
788808
build: str | None = None,
789809
memory_mbytes: int | None = None,
790-
timeout: timedelta | None = None,
810+
timeout: timedelta | None | Literal['RemainingTime'] = None,
791811
webhooks: list[Webhook] | None = None,
792812
wait: timedelta | None = None,
793813
) -> ActorRun | None:
@@ -805,7 +825,8 @@ async def call(
805825
memory_mbytes: Memory limit for the run, in megabytes. By default, the run uses a memory limit specified
806826
in the default run configuration for the Actor.
807827
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.
809830
webhooks: Optional webhooks (https://docs.apify.com/webhooks) associated with the Actor run, which can
810831
be used to receive a notification, e.g. when the Actor finished or failed. If you already have
811832
a webhook set up for the Actor, you do not have to add it again here.
@@ -826,12 +847,19 @@ async def call(
826847
else:
827848
serialized_webhooks = None
828849

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+
829857
api_result = await client.actor(actor_id).call(
830858
run_input=run_input,
831859
content_type=content_type,
832860
build=build,
833861
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,
835863
webhooks=serialized_webhooks,
836864
wait_secs=int(wait.total_seconds()) if wait is not None else None,
837865
)

0 commit comments

Comments
 (0)