Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions docs/02_concepts/code/05_retries_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
async def main() -> None:
apify_client = ApifyClientAsync(
token=TOKEN,
max_retries=8,
max_retries=4,
min_delay_between_retries=timedelta(milliseconds=500),
timeout=timedelta(seconds=360),
timeout_short=timedelta(seconds=5),
timeout_medium=timedelta(seconds=30),
timeout_long=timedelta(seconds=360),
timeout_max=timedelta(seconds=360),
)
7 changes: 5 additions & 2 deletions docs/02_concepts/code/05_retries_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
def main() -> None:
apify_client = ApifyClient(
token=TOKEN,
max_retries=8,
max_retries=4,
min_delay_between_retries=timedelta(milliseconds=500),
timeout=timedelta(seconds=360),
timeout_short=timedelta(seconds=5),
timeout_medium=timedelta(seconds=30),
timeout_long=timedelta(seconds=360),
timeout_max=timedelta(seconds=360),
)
2 changes: 1 addition & 1 deletion docs/02_concepts/code/10_default_http_client_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ async def main() -> None:
token=TOKEN,
max_retries=4,
min_delay_between_retries=timedelta(milliseconds=500),
timeout=timedelta(seconds=360),
timeout_medium=timedelta(seconds=360),
)
2 changes: 1 addition & 1 deletion docs/02_concepts/code/10_default_http_client_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ def main() -> None:
token=TOKEN,
max_retries=4,
min_delay_between_retries=timedelta(milliseconds=500),
timeout=timedelta(seconds=360),
timeout_medium=timedelta(seconds=360),
)
5 changes: 2 additions & 3 deletions docs/02_concepts/code/10_plugging_in_async.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from datetime import timedelta
from typing import Any

from apify_client import ApifyClientAsync, HttpClientAsync, HttpResponse
from apify_client import ApifyClientAsync, HttpClientAsync, HttpResponse, Timeout

TOKEN = 'MY-APIFY-TOKEN'

Expand All @@ -19,7 +18,7 @@ async def call(
data: str | bytes | bytearray | None = None,
json: Any = None,
stream: bool | None = None,
timeout: timedelta | None = None,
timeout: Timeout = 'medium',
) -> HttpResponse: ...


Expand Down
5 changes: 2 additions & 3 deletions docs/02_concepts/code/10_plugging_in_sync.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from datetime import timedelta
from typing import Any

from apify_client import ApifyClient, HttpClient, HttpResponse
from apify_client import ApifyClient, HttpClient, HttpResponse, Timeout

TOKEN = 'MY-APIFY-TOKEN'

Expand All @@ -19,7 +18,7 @@ def call(
data: str | bytes | bytearray | None = None,
json: Any = None,
stream: bool | None = None,
timeout: timedelta | None = None,
timeout: Timeout = 'medium',
) -> HttpResponse: ...


Expand Down
11 changes: 4 additions & 7 deletions docs/03_guides/code/05_custom_http_client_async.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
from __future__ import annotations

import asyncio
from typing import TYPE_CHECKING, Any
from typing import Any

import httpx

from apify_client import ApifyClientAsync, HttpClientAsync, HttpResponse

if TYPE_CHECKING:
from datetime import timedelta
from apify_client import ApifyClientAsync, HttpClientAsync, HttpResponse, Timeout

TOKEN = 'MY-APIFY-TOKEN'

Expand All @@ -30,9 +27,9 @@ async def call(
data: str | bytes | bytearray | None = None,
json: Any = None,
stream: bool | None = None,
timeout: timedelta | None = None,
timeout: Timeout = 'medium',
) -> HttpResponse:
timeout_secs = timeout.total_seconds() if timeout else 0
timeout_secs = self._compute_timeout(timeout, attempt=1) or 0

# httpx.Response satisfies the HttpResponse protocol,
# so it can be returned directly.
Expand Down
11 changes: 4 additions & 7 deletions docs/03_guides/code/05_custom_http_client_sync.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any
from typing import Any

import httpx

from apify_client import ApifyClient, HttpClient, HttpResponse

if TYPE_CHECKING:
from datetime import timedelta
from apify_client import ApifyClient, HttpClient, HttpResponse, Timeout

TOKEN = 'MY-APIFY-TOKEN'

Expand All @@ -29,9 +26,9 @@ def call(
data: str | bytes | bytearray | None = None,
json: Any = None,
stream: bool | None = None,
timeout: timedelta | None = None,
timeout: Timeout = 'medium',
) -> HttpResponse:
timeout_secs = timeout.total_seconds() if timeout else 0
timeout_secs = self._compute_timeout(timeout, attempt=1) or 0

# httpx.Response satisfies the HttpResponse protocol,
# so it can be returned directly.
Expand Down
2 changes: 2 additions & 0 deletions src/apify_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
ImpitHttpClient,
ImpitHttpClientAsync,
)
from ._types import Timeout

__version__ = metadata.version('apify-client')

Expand All @@ -19,5 +20,6 @@
'HttpResponse',
'ImpitHttpClient',
'ImpitHttpClientAsync',
'Timeout',
'__version__',
]
45 changes: 36 additions & 9 deletions src/apify_client/_apify_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
DEFAULT_API_URL,
DEFAULT_MAX_RETRIES,
DEFAULT_MIN_DELAY_BETWEEN_RETRIES,
DEFAULT_TIMEOUT,
DEFAULT_TIMEOUT_LONG,
DEFAULT_TIMEOUT_MAX,
DEFAULT_TIMEOUT_MEDIUM,
DEFAULT_TIMEOUT_SHORT,
)
from apify_client._docs import docs_group
from apify_client._http_clients import HttpClient, HttpClientAsync, ImpitHttpClient, ImpitHttpClientAsync
Expand Down Expand Up @@ -114,7 +117,10 @@ def __init__(
api_public_url: str | None = DEFAULT_API_URL,
max_retries: int = DEFAULT_MAX_RETRIES,
min_delay_between_retries: timedelta = DEFAULT_MIN_DELAY_BETWEEN_RETRIES,
timeout: timedelta = DEFAULT_TIMEOUT,
timeout_short: timedelta = DEFAULT_TIMEOUT_SHORT,
timeout_medium: timedelta = DEFAULT_TIMEOUT_MEDIUM,
timeout_long: timedelta = DEFAULT_TIMEOUT_LONG,
timeout_max: timedelta = DEFAULT_TIMEOUT_MAX,
headers: dict[str, str] | None = None,
) -> None:
"""Initialize the Apify API client.
Expand All @@ -132,7 +138,10 @@ def __init__(
max_retries: How many times to retry a failed request at most.
min_delay_between_retries: How long will the client wait between retrying requests
(increases exponentially from this value).
timeout: The socket timeout of the HTTP requests sent to the Apify API.
timeout_short: Default timeout for short-duration API operations (simple CRUD operations, ...).
timeout_medium: Default timeout for medium-duration API operations (batch operations, listing, ...).
timeout_long: Default timeout for long-duration API operations (long-polling, streaming, ...).
timeout_max: Maximum timeout cap for exponential timeout growth across retries.
headers: Additional HTTP headers to include in all API requests.
"""
# We need to do this because of mocking in tests and default mutable arguments.
Expand Down Expand Up @@ -191,7 +200,10 @@ def __init__(
# Configuration for the default HTTP client (used if a custom client is not provided).
self._max_retries = max_retries
self._min_delay_between_retries = min_delay_between_retries
self._timeout = timeout
self._timeout_short = timeout_short
self._timeout_medium = timeout_medium
self._timeout_long = timeout_long
self._timeout_max = timeout_max
self._headers = headers

@classmethod
Expand Down Expand Up @@ -249,7 +261,10 @@ def http_client(self) -> HttpClient:
if self._http_client is None:
self._http_client = ImpitHttpClient(
token=self._token,
timeout=self._timeout,
timeout_short=self._timeout_short,
timeout_medium=self._timeout_medium,
timeout_long=self._timeout_long,
timeout_max=self._timeout_max,
max_retries=self._max_retries,
min_delay_between_retries=self._min_delay_between_retries,
statistics=self._statistics,
Expand Down Expand Up @@ -455,7 +470,10 @@ def __init__(
api_public_url: str | None = DEFAULT_API_URL,
max_retries: int = DEFAULT_MAX_RETRIES,
min_delay_between_retries: timedelta = DEFAULT_MIN_DELAY_BETWEEN_RETRIES,
timeout: timedelta = DEFAULT_TIMEOUT,
timeout_short: timedelta = DEFAULT_TIMEOUT_SHORT,
timeout_medium: timedelta = DEFAULT_TIMEOUT_MEDIUM,
timeout_long: timedelta = DEFAULT_TIMEOUT_LONG,
timeout_max: timedelta = DEFAULT_TIMEOUT_MAX,
headers: dict[str, str] | None = None,
) -> None:
"""Initialize the Apify API client.
Expand All @@ -473,7 +491,10 @@ def __init__(
max_retries: How many times to retry a failed request at most.
min_delay_between_retries: How long will the client wait between retrying requests
(increases exponentially from this value).
timeout: The socket timeout of the HTTP requests sent to the Apify API.
timeout_short: Default timeout for short-duration API operations (simple CRUD operations, ...).
timeout_medium: Default timeout for medium-duration API operations (batch operations, listing, ...).
timeout_long: Default timeout for long-duration API operations (long-polling, streaming, ...).
timeout_max: Maximum timeout cap for exponential timeout growth across retries.
headers: Additional HTTP headers to include in all API requests.
"""
# We need to do this because of mocking in tests and default mutable arguments.
Expand Down Expand Up @@ -532,7 +553,10 @@ def __init__(
# Configuration for the default HTTP client (used if a custom client is not provided).
self._max_retries = max_retries
self._min_delay_between_retries = min_delay_between_retries
self._timeout = timeout
self._timeout_short = timeout_short
self._timeout_medium = timeout_medium
self._timeout_long = timeout_long
self._timeout_max = timeout_max
self._headers = headers

@classmethod
Expand Down Expand Up @@ -590,7 +614,10 @@ def http_client(self) -> HttpClientAsync:
if self._http_client is None:
self._http_client = ImpitHttpClientAsync(
token=self._token,
timeout=self._timeout,
timeout_short=self._timeout_short,
timeout_medium=self._timeout_medium,
timeout_long=self._timeout_long,
timeout_max=self._timeout_max,
max_retries=self._max_retries,
min_delay_between_retries=self._min_delay_between_retries,
statistics=self._statistics,
Expand Down
27 changes: 12 additions & 15 deletions src/apify_client/_consts.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
from __future__ import annotations

from datetime import timedelta
from typing import Any

from apify_client._models import ActorJobStatus

JsonSerializable = str | int | float | bool | None | dict[str, Any] | list[Any]
"""Type for representing json-serializable values. It's close enough to the real thing supported by json.parse.
It was suggested in a discussion with (and approved by) Guido van Rossum, so I'd consider it correct enough.
"""

DEFAULT_API_URL = 'https://api.apify.com'
"""Default base URL for the Apify API."""

API_VERSION = 'v2'
"""Current Apify API version."""

DEFAULT_TIMEOUT = timedelta(seconds=360)
"""Default request timeout."""
DEFAULT_TIMEOUT_SHORT = timedelta(seconds=5)
"""Default timeout for fast CRUD operations (e.g., get, update, delete)."""

DEFAULT_TIMEOUT_MEDIUM = timedelta(seconds=30)
"""Default timeout for batch, list, and data transfer operations."""

DEFAULT_TIMEOUT_LONG = timedelta(seconds=360)
"""Default timeout for long-polling, streaming, and other heavy operations."""

DEFAULT_MAX_RETRIES = 8
DEFAULT_TIMEOUT_MAX = timedelta(seconds=360)
"""Default maximum timeout cap for individual API requests (limits exponential growth)."""

DEFAULT_MAX_RETRIES = 4
"""Default maximum number of retries for failed requests."""

DEFAULT_MIN_DELAY_BETWEEN_RETRIES = timedelta(milliseconds=500)
Expand All @@ -31,12 +34,6 @@
DEFAULT_WAIT_WHEN_JOB_NOT_EXIST = timedelta(seconds=3)
"""How long to wait for a job to exist before giving up."""

FAST_OPERATION_TIMEOUT = timedelta(seconds=5)
"""Timeout for fast, idempotent operations (e.g., GET, DELETE)."""

STANDARD_OPERATION_TIMEOUT = timedelta(seconds=30)
"""Timeout for operations that may take longer (e.g., list operations, batch operations)."""

TERMINAL_STATUSES = frozenset(
{
ActorJobStatus.SUCCEEDED,
Expand Down
Loading