Skip to content

Commit e7e5137

Browse files
authored
chore: Use inherit instead of RemainingTime (#701)
- Use `inherit` literal instead of `RemainingTime` - Mark usage of `RemainingTime` as deprecated - Add `inherit` timeout option to `Actor.call_task` To be aligned with JS implementation: apify/apify-sdk-js#518 (comment)
1 parent 3ad002e commit e7e5137

File tree

2 files changed

+49
-21
lines changed

2 files changed

+49
-21
lines changed

src/apify/_actor.py

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import asyncio
44
import sys
5+
import warnings
56
from contextlib import suppress
67
from datetime import datetime, timedelta, timezone
78
from functools import cached_property
@@ -827,7 +828,7 @@ async def start(
827828
content_type: str | None = None,
828829
build: str | None = None,
829830
memory_mbytes: int | None = None,
830-
timeout: timedelta | None | Literal['RemainingTime'] = None,
831+
timeout: timedelta | None | Literal['inherit', 'RemainingTime'] = None,
831832
wait_for_finish: int | None = None,
832833
webhooks: list[Webhook] | None = None,
833834
) -> ActorRun:
@@ -845,8 +846,8 @@ async def start(
845846
memory_mbytes: Memory limit for the run, in megabytes. By default, the run uses a memory limit specified
846847
in the default run configuration for the Actor.
847848
timeout: Optional timeout for the run, in seconds. By default, the run uses timeout specified in
848-
the default run configuration for the Actor. Using `RemainingTime` will set timeout of the other Actor
849-
to the time remaining from this Actor timeout.
849+
the default run configuration for the Actor. Using `inherit` or `RemainingTime` will set timeout of the
850+
other Actor to the time remaining from this Actor timeout.
850851
wait_for_finish: The maximum number of seconds the server waits for the run to finish. By default,
851852
it is 0, the maximum value is 300.
852853
webhooks: Optional ad-hoc webhooks (https://docs.apify.com/webhooks/ad-hoc-webhooks) associated with
@@ -867,14 +868,22 @@ async def start(
867868
else:
868869
serialized_webhooks = None
869870

870-
if timeout == 'RemainingTime':
871+
if timeout in {'inherit', 'RemainingTime'}:
872+
if timeout == 'RemainingTime':
873+
warnings.warn(
874+
'`RemainingTime` is deprecated and will be removed in version 4.0.0. Use `inherit` instead.',
875+
DeprecationWarning,
876+
stacklevel=2,
877+
)
871878
actor_start_timeout = self._get_remaining_time()
872879
elif timeout is None:
873880
actor_start_timeout = None
874881
elif isinstance(timeout, timedelta):
875882
actor_start_timeout = timeout
876883
else:
877-
raise ValueError(f'Invalid timeout {timeout!r}: expected `None`, `"RemainingTime"`, or a `timedelta`.')
884+
raise ValueError(
885+
f'Invalid timeout {timeout!r}: expected `None`, `"inherit"`, `"RemainingTime"`, or a `timedelta`.'
886+
)
878887

879888
api_result = await client.actor(actor_id).start(
880889
run_input=run_input,
@@ -931,7 +940,7 @@ async def call(
931940
content_type: str | None = None,
932941
build: str | None = None,
933942
memory_mbytes: int | None = None,
934-
timeout: timedelta | None | Literal['RemainingTime'] = None,
943+
timeout: timedelta | None | Literal['inherit', 'RemainingTime'] = None,
935944
webhooks: list[Webhook] | None = None,
936945
wait: timedelta | None = None,
937946
logger: logging.Logger | None | Literal['default'] = 'default',
@@ -950,8 +959,8 @@ async def call(
950959
memory_mbytes: Memory limit for the run, in megabytes. By default, the run uses a memory limit specified
951960
in the default run configuration for the Actor.
952961
timeout: Optional timeout for the run, in seconds. By default, the run uses timeout specified in
953-
the default run configuration for the Actor. Using `RemainingTime` will set timeout of the other Actor
954-
to the time remaining from this Actor timeout.
962+
the default run configuration for the Actor. Using `inherit` or `RemainingTime` will set timeout of the
963+
other Actor to the time remaining from this Actor timeout.
955964
webhooks: Optional webhooks (https://docs.apify.com/webhooks) associated with the Actor run, which can
956965
be used to receive a notification, e.g. when the Actor finished or failed. If you already have
957966
a webhook set up for the Actor, you do not have to add it again here.
@@ -975,14 +984,23 @@ async def call(
975984
else:
976985
serialized_webhooks = None
977986

978-
if timeout == 'RemainingTime':
987+
if timeout in {'inherit', 'RemainingTime'}:
988+
if timeout == 'RemainingTime':
989+
warnings.warn(
990+
'`RemainingTime` is deprecated and will be removed in version 4.0.0. Use `inherit` instead.',
991+
DeprecationWarning,
992+
stacklevel=2,
993+
)
994+
979995
actor_call_timeout = self._get_remaining_time()
980996
elif timeout is None:
981997
actor_call_timeout = None
982998
elif isinstance(timeout, timedelta):
983999
actor_call_timeout = timeout
9841000
else:
985-
raise ValueError(f'Invalid timeout {timeout!r}: expected `None`, `"RemainingTime"`, or a `timedelta`.')
1001+
raise ValueError(
1002+
f'Invalid timeout {timeout!r}: expected `None`, `"inherit"`, `"RemainingTime"`, or a `timedelta`.'
1003+
)
9861004

9871005
api_result = await client.actor(actor_id).call(
9881006
run_input=run_input,
@@ -1004,7 +1022,7 @@ async def call_task(
10041022
*,
10051023
build: str | None = None,
10061024
memory_mbytes: int | None = None,
1007-
timeout: timedelta | None = None,
1025+
timeout: timedelta | None | Literal['inherit'] = None,
10081026
webhooks: list[Webhook] | None = None,
10091027
wait: timedelta | None = None,
10101028
token: str | None = None,
@@ -1026,7 +1044,8 @@ async def call_task(
10261044
memory_mbytes: Memory limit for the run, in megabytes. By default, the run uses a memory limit specified
10271045
in the default run configuration for the Actor.
10281046
timeout: Optional timeout for the run, in seconds. By default, the run uses timeout specified in
1029-
the default run configuration for the Actor.
1047+
the default run configuration for the Actor. Using `inherit` will set timeout of the other Actor to the
1048+
time remaining from this Actor timeout.
10301049
webhooks: Optional webhooks (https://docs.apify.com/webhooks) associated with the Actor run, which can
10311050
be used to receive a notification, e.g. when the Actor finished or failed. If you already have
10321051
a webhook set up for the Actor, you do not have to add it again here.
@@ -1047,11 +1066,20 @@ async def call_task(
10471066
else:
10481067
serialized_webhooks = None
10491068

1069+
if timeout == 'inherit':
1070+
task_call_timeout = self._get_remaining_time()
1071+
elif timeout is None:
1072+
task_call_timeout = None
1073+
elif isinstance(timeout, timedelta):
1074+
task_call_timeout = timeout
1075+
else:
1076+
raise ValueError(f'Invalid timeout {timeout!r}: expected `None`, `"inherit"`, or a `timedelta`.')
1077+
10501078
api_result = await client.task(task_id).call(
10511079
task_input=task_input,
10521080
build=build,
10531081
memory_mbytes=memory_mbytes,
1054-
timeout_secs=int(timeout.total_seconds()) if timeout is not None else None,
1082+
timeout_secs=int(task_call_timeout.total_seconds()) if task_call_timeout is not None else None,
10551083
webhooks=serialized_webhooks,
10561084
wait_secs=int(wait.total_seconds()) if wait is not None else None,
10571085
)
@@ -1321,7 +1349,7 @@ def _get_remaining_time(self) -> timedelta | None:
13211349
return self.configuration.timeout_at - datetime.now(tz=timezone.utc)
13221350

13231351
self.log.warning(
1324-
'Returning `None` instead of remaining time. Using `RemainingTime` argument is only possible when the Actor'
1352+
'Using `inherit` or `RemainingTime` argument is only possible when the Actor'
13251353
' is running on the Apify platform and when the timeout for the Actor run is set. '
13261354
f'{self.is_at_home()=}, {self.configuration.timeout_at=}'
13271355
)

tests/integration/actor/test_actor_call_timeouts.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
from .conftest import MakeActorFunction, RunActorFunction
1010

1111

12-
async def test_actor_start_remaining_timeout(
12+
async def test_actor_start_inherit_timeout(
1313
make_actor: MakeActorFunction,
1414
run_actor: RunActorFunction,
1515
) -> None:
16-
"""Test that correct timeout is set when using `RemainingTime` value for the `timeout` argument.
16+
"""Test that correct timeout is set when using `inherit` value for the `timeout` argument.
1717
1818
In this test, one Actor starts itself again and checks that the timeout is correctly set on the second Actor run.
1919
Timeout should be the remaining time of the first Actor run calculated at the moment of the other Actor start."""
@@ -32,7 +32,7 @@ async def main() -> None:
3232
other_run_data = await Actor.call(
3333
actor_id=Actor.configuration.actor_id or '',
3434
run_input={'called_from_another_actor': True},
35-
timeout='RemainingTime',
35+
timeout='inherit',
3636
)
3737
assert other_run_data is not None
3838
try:
@@ -50,17 +50,17 @@ async def main() -> None:
5050
# Make sure the other actor run is aborted
5151
await Actor.apify_client.run(other_run_data.id).abort()
5252

53-
actor = await make_actor(label='remaining-timeout', main_func=main)
53+
actor = await make_actor(label='inherit-timeout', main_func=main)
5454
run_result = await run_actor(actor)
5555

5656
assert run_result.status == 'SUCCEEDED'
5757

5858

59-
async def test_actor_call_remaining_timeout(
59+
async def test_actor_call_inherit_timeout(
6060
make_actor: MakeActorFunction,
6161
run_actor: RunActorFunction,
6262
) -> None:
63-
"""Test that correct timeout is set when using `RemainingTime` value for the `timeout` argument.
63+
"""Test that correct timeout is set when using `inherit` value for the `timeout` argument.
6464
6565
In this test, one Actor starts itself again and checks that the timeout is correctly set on the second Actor run.
6666
Timeout should be the remaining time of the first Actor run calculated at the moment of the other Actor call."""
@@ -79,7 +79,7 @@ async def main() -> None:
7979
other_run_data = await Actor.call(
8080
actor_id=Actor.configuration.actor_id or '',
8181
run_input={'called_from_another_actor': True},
82-
timeout='RemainingTime',
82+
timeout='inherit',
8383
)
8484

8585
assert other_run_data is not None

0 commit comments

Comments
 (0)