Skip to content

Commit 5071549

Browse files
committed
Update
1 parent ce0dcfb commit 5071549

33 files changed

+287
-549
lines changed

docs/02_concepts/code/05_retries_async.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ async def main() -> None:
1010
token=TOKEN,
1111
max_retries=8,
1212
min_delay_between_retries=timedelta(milliseconds=500),
13-
timeout_short=timedelta(seconds=360),
13+
timeout=timedelta(seconds=360),
1414
)

docs/02_concepts/code/05_retries_sync.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ def main() -> None:
1010
token=TOKEN,
1111
max_retries=8,
1212
min_delay_between_retries=timedelta(milliseconds=500),
13-
timeout_short=timedelta(seconds=360),
13+
timeout=timedelta(seconds=360),
1414
)

src/apify_client/_http_clients/_base.py

Lines changed: 35 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
)
2222
from apify_client._docs import docs_group
2323
from apify_client._statistics import ClientStatistics
24+
from apify_client._utils import to_seconds
2425

2526
if TYPE_CHECKING:
2627
from collections.abc import AsyncIterator, Iterator, Mapping
@@ -141,45 +142,6 @@ def __init__(
141142

142143
self._headers = {**default_headers, **(headers or {})}
143144

144-
@property
145-
def timeout_short(self) -> timedelta:
146-
"""Timeout for fast CRUD operations (e.g., get, update, delete)."""
147-
return self._timeout_short
148-
149-
@property
150-
def timeout(self) -> timedelta:
151-
"""Timeout for batch, list, and data transfer operations."""
152-
return self._timeout
153-
154-
@property
155-
def timeout_long(self) -> timedelta:
156-
"""Timeout for long-polling, streaming, and other heavy operations."""
157-
return self._timeout_long
158-
159-
@property
160-
def timeout_max(self) -> timedelta:
161-
"""Maximum timeout cap for exponential timeout growth across retries."""
162-
return self._timeout_max
163-
164-
def _resolve_timeout(self, timeout: Timeout) -> timedelta | None:
165-
"""Resolve a timeout tier literal or value to a concrete timedelta.
166-
167-
Args:
168-
timeout: The timeout specification to resolve.
169-
170-
Returns:
171-
A `timedelta` for tier literals and explicit values, `None` for `'no_timeout'`.
172-
"""
173-
if timeout == 'no_timeout':
174-
return None
175-
if timeout == 'short':
176-
return self._timeout_short
177-
if timeout == 'default':
178-
return self._timeout
179-
if timeout == 'long':
180-
return self._timeout_long
181-
return timeout
182-
183145
@staticmethod
184146
def _parse_params(params: dict[str, Any] | None) -> dict[str, Any] | None:
185147
"""Convert request parameters to Apify API-compatible formats.
@@ -204,6 +166,34 @@ def _parse_params(params: dict[str, Any] | None) -> dict[str, Any] | None:
204166

205167
return parsed_params
206168

169+
def _compute_timeout(self, timeout: Timeout, attempt: int) -> int | float | None:
170+
"""Resolve a timeout tier and compute the timeout for a request attempt with exponential increase.
171+
172+
For `'no_timeout'`, returns `None`. For tier literals and explicit `timedelta` values, doubles the timeout
173+
with each attempt but caps at `timeout_max`.
174+
175+
Args:
176+
timeout: The timeout specification to resolve (tier literal or explicit `timedelta`).
177+
attempt: Current attempt number (1-indexed).
178+
179+
Returns:
180+
Timeout in seconds, or `None` for `'no_timeout'`.
181+
"""
182+
if timeout == 'no_timeout':
183+
return None
184+
185+
if timeout == 'short':
186+
resolved = self._timeout_short
187+
elif timeout == 'default':
188+
resolved = self._timeout
189+
elif timeout == 'long':
190+
resolved = self._timeout_long
191+
else:
192+
resolved = timeout
193+
194+
new_timeout = min(resolved * (2 ** (attempt - 1)), self._timeout_max)
195+
return to_seconds(new_timeout)
196+
207197
def _prepare_request_call(
208198
self,
209199
headers: dict[str, str] | None = None,
@@ -280,9 +270,9 @@ def call(
280270
data: Raw request body data. Cannot be used together with json.
281271
json: JSON-serializable data for the request body. Cannot be used together with data.
282272
stream: Whether to stream the response body.
283-
timeout: Timeout for the API HTTP request.
284-
Use `'short'`, `'default'`, or `'long'` tier literals for preconfigured timeouts.
285-
A `timedelta` overrides it for this call, and `'no_timeout'` disables the timeout entirely.
273+
timeout: Timeout for the API HTTP request. Use `'short'`, `'default'`, or `'long'` tier literals for
274+
preconfigured timeouts. A `timedelta` overrides it for this call, and `'no_timeout'` disables
275+
the timeout entirely.
286276
287277
Returns:
288278
The HTTP response object.
@@ -324,9 +314,9 @@ async def call(
324314
data: Raw request body data. Cannot be used together with json.
325315
json: JSON-serializable data for the request body. Cannot be used together with data.
326316
stream: Whether to stream the response body.
327-
timeout: Timeout for the API HTTP request.
328-
Use `'short'`, `'default'`, or `'long'` tier literals for preconfigured timeouts.
329-
A `timedelta` overrides it for this call, and `'no_timeout'` disables the timeout entirely.
317+
timeout: Timeout for the API HTTP request. Use `'short'`, `'default'`, or `'long'` tier literals for
318+
preconfigured timeouts. A `timedelta` overrides it for this call, and `'no_timeout'` disables
319+
the timeout entirely.
330320
331321
Returns:
332322
The HTTP response object.

src/apify_client/_http_clients/_impit.py

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,6 @@ def _is_retryable_error(exc: Exception) -> bool:
5252
)
5353

5454

55-
def _compute_timeout(
56-
base_timeout: timedelta | None,
57-
attempt: int,
58-
timeout_max: timedelta,
59-
) -> float | None:
60-
"""Compute timeout for a request attempt with exponential increase, bounded by timeout_max.
61-
62-
For `None` (resolved from `'no_timeout'`), returns `None` — impit uses client-level default.
63-
For `timedelta` values, doubles the timeout with each attempt but caps at the maximum timeout.
64-
"""
65-
if base_timeout is None:
66-
return None
67-
68-
timeout_secs = to_seconds(base_timeout)
69-
timeout_max_secs = to_seconds(timeout_max)
70-
return min(timeout_max_secs, timeout_secs * 2 ** (attempt - 1))
71-
72-
7355
@docs_group('HTTP clients')
7456
class ImpitHttpClient(HttpClient):
7557
"""Synchronous HTTP client for the Apify API built on top of [Impit](https://github.com/apify/impit).
@@ -145,9 +127,9 @@ def call(
145127
data: Raw request body data. Cannot be used together with json.
146128
json: JSON-serializable data for the request body. Cannot be used together with data.
147129
stream: Whether to stream the response body.
148-
timeout: Timeout for the API HTTP request.
149-
Use `'short'`, `'default'`, or `'long'` tier literals for preconfigured timeouts.
150-
A `timedelta` overrides it for this call, and `'no_timeout'` disables the timeout entirely.
130+
timeout: Timeout for the API HTTP request. Use `'short'`, `'default'`, or `'long'` tier literals for
131+
preconfigured timeouts. A `timedelta` overrides it for this call, and `'no_timeout'` disables
132+
the timeout entirely.
151133
152134
Returns:
153135
The HTTP response object.
@@ -219,13 +201,12 @@ def _make_request(
219201
try:
220202
url_with_params = self._build_url_with_params(url, params)
221203

222-
resolved_timeout = self._resolve_timeout(timeout)
223204
response = self._impit_client.request(
224205
method=method,
225206
url=url_with_params,
226207
headers=headers,
227208
content=content,
228-
timeout=_compute_timeout(resolved_timeout, attempt, self._timeout_max),
209+
timeout=self._compute_timeout(timeout, attempt),
229210
stream=stream or False,
230211
)
231212

@@ -383,9 +364,9 @@ async def call(
383364
data: Raw request body data. Cannot be used together with json.
384365
json: JSON-serializable data for the request body. Cannot be used together with data.
385366
stream: Whether to stream the response body.
386-
timeout: Timeout for the API HTTP request.
387-
Use `'short'`, `'default'`, or `'long'` tier literals for preconfigured timeouts.
388-
A `timedelta` overrides it for this call, and `'no_timeout'` disables the timeout entirely.
367+
timeout: Timeout for the API HTTP request. Use `'short'`, `'default'`, or `'long'` tier literals for
368+
preconfigured timeouts. A `timedelta` overrides it for this call, and `'no_timeout'` disables
369+
the timeout entirely.
389370
390371
Returns:
391372
The HTTP response object.
@@ -457,13 +438,12 @@ async def _make_request(
457438
try:
458439
url_with_params = self._build_url_with_params(url, params)
459440

460-
resolved_timeout = self._resolve_timeout(timeout)
461441
response = await self._impit_async_client.request(
462442
method=method,
463443
url=url_with_params,
464444
headers=headers,
465445
content=content,
466-
timeout=_compute_timeout(resolved_timeout, attempt, self._timeout_max),
446+
timeout=self._compute_timeout(timeout, attempt),
467447
stream=stream or False,
468448
)
469449

src/apify_client/_resource_clients/_resource_client.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,7 @@ def _wait_for_finish(
284284
url: Full URL to the job endpoint.
285285
params: Base query parameters to include in each request.
286286
wait_duration: Maximum time to wait (None = indefinite).
287-
timeout: Timeout for each HTTP request. Since polling duration is controlled
288-
by `wait_duration`, the per-request timeout is typically left unlimited.
287+
timeout: Timeout for each individual HTTP request.
289288
290289
Returns:
291290
Job data dict when finished, or None if job doesn't exist after
@@ -464,8 +463,7 @@ async def _wait_for_finish(
464463
url: Full URL to the job endpoint.
465464
params: Base query parameters to include in each request.
466465
wait_duration: Maximum time to wait (None = indefinite).
467-
timeout: Timeout for each HTTP request. Since polling duration is controlled
468-
by `wait_duration`, the per-request timeout is typically left unlimited.
466+
timeout: Timeout for each individual HTTP request.
469467
470468
Returns:
471469
Job data dict when finished, or None if job doesn't exist after

src/apify_client/_resource_clients/actor.py

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,7 @@ def get(self, *, timeout: Timeout = 'short') -> Actor | None:
9191
https://docs.apify.com/api/v2#/reference/actors/actor-object/get-actor
9292
9393
Args:
94-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
95-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
94+
timeout: Timeout for the API HTTP request.
9695
9796
Returns:
9897
The retrieved Actor.
@@ -169,8 +168,7 @@ def update(
169168
tagged_builds: A dictionary mapping build tag names to their settings. Use it to create, update,
170169
or remove build tags. To assign a tag, provide a dict with 'buildId' key. To remove a tag,
171170
set its value to None. Example: {'latest': {'buildId': 'abc'}, 'beta': None}.
172-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
173-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
171+
timeout: Timeout for the API HTTP request.
174172
175173
Returns:
176174
The updated Actor.
@@ -217,8 +215,7 @@ def delete(self, *, timeout: Timeout = 'short') -> None:
217215
https://docs.apify.com/api/v2#/reference/actors/actor-object/delete-actor
218216
219217
Args:
220-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
221-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
218+
timeout: Timeout for the API HTTP request.
222219
"""
223220
self._delete(timeout=timeout)
224221

@@ -267,8 +264,7 @@ def start(
267264
* `event_types`: List of `WebhookEventType` values which trigger the webhook.
268265
* `request_url`: URL to which to send the webhook HTTP request.
269266
* `payload_template`: Optional template for the request payload.
270-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
271-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
267+
timeout: Timeout for the API HTTP request.
272268
273269
Returns:
274270
The run object.
@@ -347,8 +343,7 @@ def call(
347343
default logger will be used. Setting `None` will disable any log propagation. Passing custom logger
348344
will redirect logs to the provided logger. The logger is also used to capture status and status message
349345
of the other Actor run.
350-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
351-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
346+
timeout: Timeout for the API HTTP request.
352347
353348
Returns:
354349
The run object.
@@ -408,8 +403,7 @@ def build(
408403
This is to enable quick rebuild during development. By default, the cache is not used.
409404
wait_for_finish: The maximum number of seconds the server waits for the build to finish before returning.
410405
By default it is 0, the maximum value is 60.
411-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
412-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
406+
timeout: Timeout for the API HTTP request.
413407
414408
Returns:
415409
The build object.
@@ -459,8 +453,7 @@ def default_build(
459453
Args:
460454
wait_for_finish: The maximum number of seconds the server waits for the build to finish before returning.
461455
By default it is 0, the maximum value is 60.
462-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
463-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
456+
timeout: Timeout for the API HTTP request.
464457
465458
Returns:
466459
The resource client for the default build of this Actor.
@@ -548,8 +541,7 @@ def validate_input(
548541
run_input: The input to validate.
549542
build_tag: The Actor's build tag.
550543
content_type: The content type of the input.
551-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
552-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
544+
timeout: Timeout for the API HTTP request.
553545
554546
Returns:
555547
True if the input is valid, else raise an exception with validation error details.
@@ -595,8 +587,7 @@ async def get(self, *, timeout: Timeout = 'short') -> Actor | None:
595587
https://docs.apify.com/api/v2#/reference/actors/actor-object/get-actor
596588
597589
Args:
598-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
599-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
590+
timeout: Timeout for the API HTTP request.
600591
601592
Returns:
602593
The retrieved Actor.
@@ -673,8 +664,7 @@ async def update(
673664
tagged_builds: A dictionary mapping build tag names to their settings. Use it to create, update,
674665
or remove build tags. To assign a tag, provide a dict with 'buildId' key. To remove a tag,
675666
set its value to None. Example: {'latest': {'buildId': 'abc'}, 'beta': None}.
676-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
677-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
667+
timeout: Timeout for the API HTTP request.
678668
679669
Returns:
680670
The updated Actor.
@@ -721,8 +711,7 @@ async def delete(self, *, timeout: Timeout = 'short') -> None:
721711
https://docs.apify.com/api/v2#/reference/actors/actor-object/delete-actor
722712
723713
Args:
724-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
725-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
714+
timeout: Timeout for the API HTTP request.
726715
"""
727716
await self._delete(timeout=timeout)
728717

@@ -771,8 +760,7 @@ async def start(
771760
* `event_types`: List of `WebhookEventType` values which trigger the webhook.
772761
* `request_url`: URL to which to send the webhook HTTP request.
773762
* `payload_template`: Optional template for the request payload.
774-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
775-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
763+
timeout: Timeout for the API HTTP request.
776764
777765
Returns:
778766
The run object.
@@ -851,8 +839,7 @@ async def call(
851839
default logger will be used. Setting `None` will disable any log propagation. Passing custom logger
852840
will redirect logs to the provided logger. The logger is also used to capture status and status message
853841
of the other Actor run.
854-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
855-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
842+
timeout: Timeout for the API HTTP request.
856843
857844
Returns:
858845
The run object.
@@ -916,8 +903,7 @@ async def build(
916903
This is to enable quick rebuild during development. By default, the cache is not used.
917904
wait_for_finish: The maximum number of seconds the server waits for the build to finish before returning.
918905
By default it is 0, the maximum value is 60.
919-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
920-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
906+
timeout: Timeout for the API HTTP request.
921907
922908
Returns:
923909
The build object.
@@ -967,8 +953,7 @@ async def default_build(
967953
Args:
968954
wait_for_finish: The maximum number of seconds the server waits for the build to finish before returning.
969955
By default it is 0, the maximum value is 60.
970-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
971-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
956+
timeout: Timeout for the API HTTP request.
972957
973958
Returns:
974959
The resource client for the default build of this Actor.
@@ -1056,8 +1041,7 @@ async def validate_input(
10561041
run_input: The input to validate.
10571042
build_tag: The Actor's build tag.
10581043
content_type: The content type of the input.
1059-
timeout: Timeout for the API HTTP request. Uses the method's default tier.
1060-
Use `'short'`, `'default'`, or `'long'` tier literals, a `timedelta`, or `'no_timeout'`.
1044+
timeout: Timeout for the API HTTP request.
10611045
10621046
Returns:
10631047
True if the input is valid, else raise an exception with validation error details.

0 commit comments

Comments
 (0)