Skip to content
Draft
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
18 changes: 9 additions & 9 deletions src/apify_client/_resource_clients/_resource_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
if TYPE_CHECKING:
from apify_client._client_registry import ClientRegistry, ClientRegistryAsync
from apify_client.http_clients import HttpClient, HttpClientAsync
from apify_client.types import Timeout
from apify_client.types import QueryParams, QueryParamValue, Timeout

_TerminalActorJobStatus = Literal['SUCCEEDED', 'FAILED', 'TIMED-OUT', 'ABORTED']
"""Subset of `ActorJobStatus` values that indicate the job has finished and will not change again."""
Expand All @@ -41,7 +41,7 @@ def __init__(
resource_path: str,
client_registry: Any,
resource_id: str | None = None,
params: dict | None = None,
params: QueryParams | None = None,
api_base_url: str | None = None,
) -> None:
"""Initialize the resource client.
Expand All @@ -63,7 +63,7 @@ def __init__(
self._api_base_url = api_base_url or base_url
self._public_base_url = public_base_url
self._http_client = http_client
self._default_params = params or {}
self._default_params = dict(params) if params else {}
self._resource_path = resource_path
self._resource_id = resource_id
self._client_registry = client_registry
Expand Down Expand Up @@ -120,7 +120,7 @@ def _build_url(

return url

def _build_public_url(self, path: str, params: dict[str, Any]) -> str:
def _build_public_url(self, path: str, params: QueryParams) -> str:
"""Build a public resource URL with API-normalized query params.

Normalizes `params` the same way the HTTP request path does (bool -> true/false, list -> comma-joined,
Expand All @@ -139,7 +139,7 @@ def _build_public_url(self, path: str, params: dict[str, Any]) -> str:
public_url = public_url._replace(query=urlencode(filtered_params))
return urlunparse(public_url)

def _build_params(self, **kwargs: Any) -> dict:
def _build_params(self, **kwargs: QueryParamValue) -> dict[str, QueryParamValue]:
"""Merge default params with method params, filtering out None values.

Args:
Expand Down Expand Up @@ -200,7 +200,7 @@ def __init__(
resource_path: str,
client_registry: ClientRegistry,
resource_id: str | None = None,
params: dict | None = None,
params: QueryParams | None = None,
api_base_url: str | None = None,
) -> None:
"""Initialize the resource client.
Expand Down Expand Up @@ -313,7 +313,7 @@ def _wait_for_finish(
self,
*,
url: str,
params: dict,
params: QueryParams,
timeout: Timeout,
wait_duration: timedelta | None = None,
) -> dict | None:
Expand Down Expand Up @@ -395,7 +395,7 @@ def __init__(
resource_path: str,
client_registry: ClientRegistryAsync,
resource_id: str | None = None,
params: dict | None = None,
params: QueryParams | None = None,
api_base_url: str | None = None,
) -> None:
"""Initialize the resource client.
Expand Down Expand Up @@ -508,7 +508,7 @@ async def _wait_for_finish(
self,
*,
url: str,
params: dict,
params: QueryParams,
timeout: Timeout,
wait_duration: timedelta | None = None,
) -> dict | None:
Expand Down
4 changes: 2 additions & 2 deletions src/apify_client/_resource_clients/request_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
RequestWithoutIdCamelDict,
RequestWithoutIdDict,
)
from apify_client.types import Timeout
from apify_client.types import QueryParams, Timeout

_RQ_MAX_REQUESTS_PER_BATCH = 25
"""Maximum number of requests the API accepts in a single batch call."""
Expand Down Expand Up @@ -946,7 +946,7 @@ async def _batch_add_requests_worker(
self,
*,
queue: asyncio.Queue[Iterable[bytes]],
request_params: dict,
request_params: QueryParams,
timeout: Timeout,
) -> BatchAddResponse:
"""Worker function to process a batch of requests.
Expand Down
16 changes: 9 additions & 7 deletions src/apify_client/http_clients/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
from typing import Self

from apify_client.http_compressors._base import HttpCompressor
from apify_client.types import JsonSerializable, StreamedBodySource, Timeout
from apify_client.types import JsonSerializable, QueryParams, StreamedBodySource, Timeout

logger = logging.getLogger(logger_name)
logger_once = LoggerOnce(logger)
Expand Down Expand Up @@ -227,7 +227,7 @@ def _get_header(headers: dict[str, str], name: str) -> str | None:
return next((value for key, value in headers.items() if key.lower() == name.lower()), None)

@staticmethod
def _parse_params(params: dict[str, Any] | None) -> dict[str, Any] | None:
def _parse_params(params: QueryParams | None) -> dict[str, Any] | None:
"""Convert request parameters to Apify API-compatible formats.

Converts booleans to 'false'/'true', lists to comma-separated strings, datetimes to ISO 8601 Zulu format.
Expand Down Expand Up @@ -309,7 +309,7 @@ def _prepare_request_call(
self,
*,
headers: dict[str, str] | None = None,
params: dict[str, Any] | None = None,
params: QueryParams | None = None,
data: str | bytes | bytearray | StreamedBodySource | None = None,
json: JsonSerializable | None = None,
) -> tuple[dict[str, str], dict[str, Any] | None, bytes | StreamedRequestBody | None]:
Expand Down Expand Up @@ -504,7 +504,7 @@ def call(
method: str,
url: str,
headers: dict[str, str] | None = None,
params: dict[str, Any] | None = None,
params: QueryParams | None = None,
data: str | bytes | bytearray | StreamedBodySource | None = None,
json: JsonSerializable | None = None,
stream: bool | None = None,
Expand All @@ -516,7 +516,8 @@ def call(
method: HTTP method (GET, POST, PUT, DELETE, etc.).
url: Full URL to make the request to.
headers: Additional headers to include.
params: Query parameters to append to the URL.
params: Query parameters to append to the URL. See `QueryParamValue` for how each value is converted
before it is sent.
data: Raw request body. An `io.IOBase` stream such as an open file, an iterable of byte chunks, or a
streamed `HttpResponse` is sent in chunks as it is read, and any other object with a callable `read`
is read whole, see `StreamedRequestBody`. Streaming is experimental, and its behavior may change in
Expand Down Expand Up @@ -715,7 +716,7 @@ async def call(
method: str,
url: str,
headers: dict[str, str] | None = None,
params: dict[str, Any] | None = None,
params: QueryParams | None = None,
data: str | bytes | bytearray | StreamedBodySource | None = None,
json: JsonSerializable | None = None,
stream: bool | None = None,
Expand All @@ -727,7 +728,8 @@ async def call(
method: HTTP method (GET, POST, PUT, DELETE, etc.).
url: Full URL to make the request to.
headers: Additional headers to include.
params: Query parameters to append to the URL.
params: Query parameters to append to the URL. See `QueryParamValue` for how each value is converted
before it is sent.
data: Raw request body. An `io.IOBase` stream such as an open file, an iterable of byte chunks, or a
streamed `HttpResponse` is sent in chunks as it is read, and any other object with a callable `read`
is read whole, see `StreamedRequestBody`. Streaming is experimental, and its behavior may change in
Expand Down
17 changes: 16 additions & 1 deletion src/apify_client/types.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from __future__ import annotations

from collections.abc import AsyncIterable, Iterable, Mapping, Sequence
from datetime import timedelta
from datetime import datetime, timedelta
from decimal import Decimal
from typing import IO, TYPE_CHECKING, Literal, Protocol

from apify_client._models import CreateWebhookRequest, WebhookRepresentation
Expand All @@ -19,6 +20,18 @@
HttpCompressionAlgorithm = Literal['brotli', 'gzip']
"""Accepted string literals for the `compression` parameter on `ApifyClient` and `ApifyClientAsync`."""

QueryParamValue = str | int | float | bool | Decimal | datetime | list[str] | None
"""Type of one query parameter value accepted by the client.

The HTTP client converts a value before sending it: a `bool` becomes `true` or `false`, a `list[str]` becomes one
comma-separated value, a `datetime` becomes an ISO 8601 string in UTC (a naive one is treated as UTC), and a `None`
value is left out. Any other value is sent as its `str()`. Only a `list` is joined, so pass a `list`, not a tuple
or another sequence.
"""

QueryParams = Mapping[str, QueryParamValue]
"""Type for the `params` parameter on `HttpClient.call` and `HttpClientAsync.call`: query parameters of the request."""

Timeout = timedelta | Literal['no_timeout', 'short', 'medium', 'long']
"""Type for the `timeout` parameter on resource client methods.

Expand Down Expand Up @@ -91,6 +104,8 @@ def read(self) -> bytes | str | Awaitable[bytes | str]:
__all__ = [
'HttpCompressionAlgorithm',
'JsonSerializable',
'QueryParamValue',
'QueryParams',
'StreamedBodySource',
'SupportsRead',
'Timeout',
Expand Down
10 changes: 5 additions & 5 deletions tests/unit/test_pluggable_http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

from pytest_httpserver import HTTPServer

from apify_client.types import StreamedBodySource, Timeout
from apify_client.types import QueryParams, StreamedBodySource, Timeout


@dataclass
Expand Down Expand Up @@ -94,7 +94,7 @@ def call(
method: str,
url: str,
headers: dict[str, str] | None = None,
params: dict[str, Any] | None = None,
params: QueryParams | None = None,
data: str | bytes | bytearray | StreamedBodySource | None = None,
json: Any = None,
stream: bool | None = None,
Expand Down Expand Up @@ -128,7 +128,7 @@ async def call(
method: str,
url: str,
headers: dict[str, str] | None = None,
params: dict[str, Any] | None = None,
params: QueryParams | None = None,
data: str | bytes | bytearray | StreamedBodySource | None = None,
json: Any = None,
stream: bool | None = None,
Expand Down Expand Up @@ -550,7 +550,7 @@ def call(
method: str,
url: str,
headers: dict[str, str] | None = None,
params: dict[str, Any] | None = None,
params: QueryParams | None = None,
data: str | bytes | bytearray | StreamedBodySource | None = None,
json: Any = None,
**_kwargs: Any,
Expand All @@ -575,7 +575,7 @@ async def call(
method: str,
url: str,
headers: dict[str, str] | None = None,
params: dict[str, Any] | None = None,
params: QueryParams | None = None,
data: str | bytes | bytearray | StreamedBodySource | None = None,
json: Any = None,
**_kwargs: Any,
Expand Down
Loading