diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c523ce1..c3c9552 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.8.0" + ".": "1.9.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 183571e..a23fed7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 29 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-e67b8e285739a9a3998fbe39b5eb97fac5bb8f571781d4f5d242b3824d4d743e.yml -openapi_spec_hash: bd661c93deb864b606bfc2d2ee110a1f -config_hash: 00b1a3fd1b197bd253cdd6b7bc360c02 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-f59a2fe653b8ffaf4db2369ed4faaf97babf6525836c332bfcf3bbaf253de229.yml +openapi_spec_hash: 2597723172f71e3be691d3dda87fcea8 +config_hash: 70cdb57c982c578d1961657c07b8b397 diff --git a/CHANGELOG.md b/CHANGELOG.md index 56599e3..b296fd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 1.9.0 (2025-11-04) + +Full Changelog: [v1.8.0...v1.9.0](https://github.com/ArcadeAI/arcade-py/compare/v1.8.0...v1.9.0) + +### Features + +* **api:** api update ([85c5f67](https://github.com/ArcadeAI/arcade-py/commit/85c5f671f829356f6b4556745785e9ecf3d86ff5)) +* **api:** api update ([73a9b83](https://github.com/ArcadeAI/arcade-py/commit/73a9b83cd333bd38db8560b91764f662fa8f4e7a)) +* **api:** api update ([cc7a611](https://github.com/ArcadeAI/arcade-py/commit/cc7a611b60084672f41979aea807f18249d1cb01)) +* **api:** api update ([eebc9ed](https://github.com/ArcadeAI/arcade-py/commit/eebc9edf476f0838f584f6d356fdaee8d8d79c76)) + + +### Bug Fixes + +* **client:** close streams without requiring full consumption ([e5ceb83](https://github.com/ArcadeAI/arcade-py/commit/e5ceb832d91dfca38c306f69526c8a909d932f16)) + + +### Chores + +* bump `httpx-aiohttp` version to 0.1.9 ([8d8e4b7](https://github.com/ArcadeAI/arcade-py/commit/8d8e4b7d707dce5a3e9869f3346cd256cd759c5c)) +* do not install brew dependencies in ./scripts/bootstrap by default ([927371b](https://github.com/ArcadeAI/arcade-py/commit/927371b47365c528be87e5dce549c0363d9b7a11)) +* **internal/tests:** avoid race condition with implicit client cleanup ([b14f88e](https://github.com/ArcadeAI/arcade-py/commit/b14f88efb33569f5307d9eabc4d7b6a6da9f477e)) +* **internal:** detect missing future annotations with ruff ([0afbe98](https://github.com/ArcadeAI/arcade-py/commit/0afbe984f92a6323e30c8eeb50685083ad83869e)) +* **internal:** grammar fix (it's -> its) ([95c0ec1](https://github.com/ArcadeAI/arcade-py/commit/95c0ec14da8731bd724acb9c64973a05288c7fda)) +* **internal:** update pydantic dependency ([450a852](https://github.com/ArcadeAI/arcade-py/commit/450a852ffa8000a2c7e1d4d294a925366301c3fd)) +* **types:** change optional parameter type from NotGiven to Omit ([484c472](https://github.com/ArcadeAI/arcade-py/commit/484c472696a53d5b7ea9c14d9a826bc6701f0704)) + ## 1.8.0 (2025-09-11) Full Changelog: [v1.7.0...v1.8.0](https://github.com/ArcadeAI/arcade-py/compare/v1.7.0...v1.8.0) diff --git a/pyproject.toml b/pyproject.toml index f0395f7..fa8b5e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "arcadepy" -version = "1.8.0" +version = "1.9.0" description = "The official Python library for the Arcade API" dynamic = ["readme"] license = "MIT" @@ -39,7 +39,7 @@ Homepage = "https://github.com/ArcadeAI/arcade-py" Repository = "https://github.com/ArcadeAI/arcade-py" [project.optional-dependencies] -aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.8"] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] [tool.rye] managed = true @@ -224,6 +224,8 @@ select = [ "B", # remove unused imports "F401", + # check for missing future annotations + "FA102", # bare except statements "E722", # unused arguments @@ -246,6 +248,8 @@ unfixable = [ "T203", ] +extend-safe-fixes = ["FA102"] + [tool.ruff.lint.flake8-tidy-imports.banned-api] "functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead" diff --git a/requirements-dev.lock b/requirements-dev.lock index cdc05b4..3c2bd65 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -56,7 +56,7 @@ httpx==0.28.1 # via arcadepy # via httpx-aiohttp # via respx -httpx-aiohttp==0.1.8 +httpx-aiohttp==0.1.9 # via arcadepy idna==3.4 # via anyio @@ -88,9 +88,9 @@ pluggy==1.5.0 propcache==0.3.1 # via aiohttp # via yarl -pydantic==2.10.3 +pydantic==2.11.9 # via arcadepy -pydantic-core==2.27.1 +pydantic-core==2.33.2 # via pydantic pygments==2.18.0 # via rich @@ -126,6 +126,9 @@ typing-extensions==4.12.2 # via pydantic # via pydantic-core # via pyright + # via typing-inspection +typing-inspection==0.4.1 + # via pydantic virtualenv==20.24.5 # via nox yarl==1.20.0 diff --git a/requirements.lock b/requirements.lock index 453acf2..b5a9719 100644 --- a/requirements.lock +++ b/requirements.lock @@ -43,7 +43,7 @@ httpcore==1.0.9 httpx==0.28.1 # via arcadepy # via httpx-aiohttp -httpx-aiohttp==0.1.8 +httpx-aiohttp==0.1.9 # via arcadepy idna==3.4 # via anyio @@ -55,9 +55,9 @@ multidict==6.4.4 propcache==0.3.1 # via aiohttp # via yarl -pydantic==2.10.3 +pydantic==2.11.9 # via arcadepy -pydantic-core==2.27.1 +pydantic-core==2.33.2 # via pydantic sniffio==1.3.0 # via anyio @@ -68,5 +68,8 @@ typing-extensions==4.12.2 # via multidict # via pydantic # via pydantic-core + # via typing-inspection +typing-inspection==0.4.1 + # via pydantic yarl==1.20.0 # via aiohttp diff --git a/scripts/bootstrap b/scripts/bootstrap index e84fe62..b430fee 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,10 +4,18 @@ set -e cd "$(dirname "$0")/.." -if ! command -v rye >/dev/null 2>&1 && [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { - echo "==> Installing Homebrew dependencies…" - brew bundle + echo -n "==> Install Homebrew dependencies? (y/N): " + read -r response + case "$response" in + [yY][eE][sS]|[yY]) + brew bundle + ;; + *) + ;; + esac + echo } fi diff --git a/src/arcadepy/__init__.py b/src/arcadepy/__init__.py index fe2533d..a66e928 100644 --- a/src/arcadepy/__init__.py +++ b/src/arcadepy/__init__.py @@ -3,7 +3,7 @@ import typing as _t from . import types -from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes +from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given from ._utils import file_from_path from ._client import Arcade, Client, Stream, Timeout, Transport, AsyncArcade, AsyncClient, AsyncStream, RequestOptions from ._models import BaseModel @@ -38,7 +38,9 @@ "ProxiesTypes", "NotGiven", "NOT_GIVEN", + "not_given", "Omit", + "omit", "ArcadeError", "APIError", "APIStatusError", diff --git a/src/arcadepy/_base_client.py b/src/arcadepy/_base_client.py index 0d9b8ac..57cf33c 100644 --- a/src/arcadepy/_base_client.py +++ b/src/arcadepy/_base_client.py @@ -42,7 +42,6 @@ from ._qs import Querystring from ._files import to_httpx_files, async_to_httpx_files from ._types import ( - NOT_GIVEN, Body, Omit, Query, @@ -57,6 +56,7 @@ RequestOptions, HttpxRequestFiles, ModelBuilderProtocol, + not_given, ) from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping from ._compat import PYDANTIC_V1, model_copy, model_dump @@ -145,9 +145,9 @@ def __init__( def __init__( self, *, - url: URL | NotGiven = NOT_GIVEN, - json: Body | NotGiven = NOT_GIVEN, - params: Query | NotGiven = NOT_GIVEN, + url: URL | NotGiven = not_given, + json: Body | NotGiven = not_given, + params: Query | NotGiven = not_given, ) -> None: self.url = url self.json = json @@ -595,7 +595,7 @@ def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalReques # we internally support defining a temporary header to override the # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response` # see _response.py for implementation details - override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, NOT_GIVEN) + override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given) if is_given(override_cast_to): options.headers = headers return cast(Type[ResponseT], override_cast_to) @@ -825,7 +825,7 @@ def __init__( version: str, base_url: str | URL, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, @@ -1356,7 +1356,7 @@ def __init__( base_url: str | URL, _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, @@ -1818,8 +1818,8 @@ def make_request_options( extra_query: Query | None = None, extra_body: Body | None = None, idempotency_key: str | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - post_parser: PostParser | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + post_parser: PostParser | NotGiven = not_given, ) -> RequestOptions: """Create a dict of type RequestOptions without keys of NotGiven values.""" options: RequestOptions = {} diff --git a/src/arcadepy/_client.py b/src/arcadepy/_client.py index 76bf3a2..7ad9056 100644 --- a/src/arcadepy/_client.py +++ b/src/arcadepy/_client.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import Any, Union, Mapping +from typing import Any, Mapping from typing_extensions import Self, override import httpx @@ -11,13 +11,13 @@ from . import _exceptions from ._qs import Querystring from ._types import ( - NOT_GIVEN, Omit, Timeout, NotGiven, Transport, ProxiesTypes, RequestOptions, + not_given, ) from ._utils import is_given, get_async_library from ._version import __version__ @@ -54,7 +54,7 @@ def __init__( *, api_key: str | None = None, base_url: str | httpx.URL | None = None, - timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -134,9 +134,9 @@ def copy( *, api_key: str | None = None, base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, - max_retries: int | NotGiven = NOT_GIVEN, + max_retries: int | NotGiven = not_given, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -232,7 +232,7 @@ def __init__( *, api_key: str | None = None, base_url: str | httpx.URL | None = None, - timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -312,9 +312,9 @@ def copy( *, api_key: str | None = None, base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, - max_retries: int | NotGiven = NOT_GIVEN, + max_retries: int | NotGiven = not_given, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, diff --git a/src/arcadepy/_models.py b/src/arcadepy/_models.py index 3a6017e..6a3cd1d 100644 --- a/src/arcadepy/_models.py +++ b/src/arcadepy/_models.py @@ -256,7 +256,7 @@ def model_dump( mode: Literal["json", "python"] | str = "python", include: IncEx | None = None, exclude: IncEx | None = None, - by_alias: bool = False, + by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, @@ -264,6 +264,7 @@ def model_dump( warnings: bool | Literal["none", "warn", "error"] = True, context: dict[str, Any] | None = None, serialize_as_any: bool = False, + fallback: Callable[[Any], Any] | None = None, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -295,10 +296,12 @@ def model_dump( raise ValueError("context is only supported in Pydantic v2") if serialize_as_any != False: raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, - by_alias=by_alias, + by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, @@ -313,13 +316,14 @@ def model_dump_json( indent: int | None = None, include: IncEx | None = None, exclude: IncEx | None = None, - by_alias: bool = False, + by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, context: dict[str, Any] | None = None, + fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> str: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json @@ -348,11 +352,13 @@ def model_dump_json( raise ValueError("context is only supported in Pydantic v2") if serialize_as_any != False: raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, exclude=exclude, - by_alias=by_alias, + by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, diff --git a/src/arcadepy/_qs.py b/src/arcadepy/_qs.py index 274320c..ada6fd3 100644 --- a/src/arcadepy/_qs.py +++ b/src/arcadepy/_qs.py @@ -4,7 +4,7 @@ from urllib.parse import parse_qs, urlencode from typing_extensions import Literal, get_args -from ._types import NOT_GIVEN, NotGiven, NotGivenOr +from ._types import NotGiven, not_given from ._utils import flatten _T = TypeVar("_T") @@ -41,8 +41,8 @@ def stringify( self, params: Params, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> str: return urlencode( self.stringify_items( @@ -56,8 +56,8 @@ def stringify_items( self, params: Params, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> list[tuple[str, str]]: opts = Options( qs=self, @@ -143,8 +143,8 @@ def __init__( self, qs: Querystring = _qs, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> None: self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format diff --git a/src/arcadepy/_streaming.py b/src/arcadepy/_streaming.py index c3f3544..6bf52e7 100644 --- a/src/arcadepy/_streaming.py +++ b/src/arcadepy/_streaming.py @@ -57,9 +57,8 @@ def __stream__(self) -> Iterator[_T]: for sse in iterator: yield process_data(data=sse.json(), cast_to=cast_to, response=response) - # Ensure the entire stream is consumed - for _sse in iterator: - ... + # As we might not fully consume the response stream, we need to close it explicitly + response.close() def __enter__(self) -> Self: return self @@ -121,9 +120,8 @@ async def __stream__(self) -> AsyncIterator[_T]: async for sse in iterator: yield process_data(data=sse.json(), cast_to=cast_to, response=response) - # Ensure the entire stream is consumed - async for _sse in iterator: - ... + # As we might not fully consume the response stream, we need to close it explicitly + await response.aclose() async def __aenter__(self) -> Self: return self diff --git a/src/arcadepy/_types.py b/src/arcadepy/_types.py index c7ceaa8..39e9386 100644 --- a/src/arcadepy/_types.py +++ b/src/arcadepy/_types.py @@ -117,18 +117,21 @@ class RequestOptions(TypedDict, total=False): # Sentinel class used until PEP 0661 is accepted class NotGiven: """ - A sentinel singleton class used to distinguish omitted keyword arguments - from those passed in with the value None (which may have different behavior). + For parameters with a meaningful None value, we need to distinguish between + the user explicitly passing None, and the user not passing the parameter at + all. + + User code shouldn't need to use not_given directly. For example: ```py - def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ... + def create(timeout: Timeout | None | NotGiven = not_given): ... - get(timeout=1) # 1s timeout - get(timeout=None) # No timeout - get() # Default timeout behavior, which may not be statically known at the method definition. + create(timeout=1) # 1s timeout + create(timeout=None) # No timeout + create() # Default timeout behavior ``` """ @@ -140,13 +143,14 @@ def __repr__(self) -> str: return "NOT_GIVEN" -NotGivenOr = Union[_T, NotGiven] +not_given = NotGiven() +# for backwards compatibility: NOT_GIVEN = NotGiven() class Omit: - """In certain situations you need to be able to represent a case where a default value has - to be explicitly removed and `None` is not an appropriate substitute, for example: + """ + To explicitly omit something from being sent in a request, use `omit`. ```py # as the default `Content-Type` header is `application/json` that will be sent @@ -156,8 +160,8 @@ class Omit: # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' client.post(..., headers={"Content-Type": "multipart/form-data"}) - # instead you can remove the default `application/json` header by passing Omit - client.post(..., headers={"Content-Type": Omit()}) + # instead you can remove the default `application/json` header by passing omit + client.post(..., headers={"Content-Type": omit}) ``` """ @@ -165,6 +169,9 @@ def __bool__(self) -> Literal[False]: return False +omit = Omit() + + @runtime_checkable class ModelBuilderProtocol(Protocol): @classmethod diff --git a/src/arcadepy/_utils/_transform.py b/src/arcadepy/_utils/_transform.py index c19124f..5207549 100644 --- a/src/arcadepy/_utils/_transform.py +++ b/src/arcadepy/_utils/_transform.py @@ -268,7 +268,7 @@ def _transform_typeddict( annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): if not is_given(value): - # we don't need to include `NotGiven` values here as they'll + # we don't need to include omitted values here as they'll # be stripped out before the request is sent anyway continue @@ -434,7 +434,7 @@ async def _async_transform_typeddict( annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): if not is_given(value): - # we don't need to include `NotGiven` values here as they'll + # we don't need to include omitted values here as they'll # be stripped out before the request is sent anyway continue diff --git a/src/arcadepy/_utils/_utils.py b/src/arcadepy/_utils/_utils.py index f081859..eec7f4a 100644 --- a/src/arcadepy/_utils/_utils.py +++ b/src/arcadepy/_utils/_utils.py @@ -21,7 +21,7 @@ import sniffio -from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike +from .._types import Omit, NotGiven, FileTypes, HeadersLike _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) @@ -63,7 +63,7 @@ def _extract_items( try: key = path[index] except IndexError: - if isinstance(obj, NotGiven): + if not is_given(obj): # no value was provided - we can safely ignore return [] @@ -126,14 +126,14 @@ def _extract_items( return [] -def is_given(obj: NotGivenOr[_T]) -> TypeGuard[_T]: - return not isinstance(obj, NotGiven) +def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]: + return not isinstance(obj, NotGiven) and not isinstance(obj, Omit) # Type safe methods for narrowing types with TypeVars. # The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], # however this cause Pyright to rightfully report errors. As we know we don't -# care about the contained types we can safely use `object` in it's place. +# care about the contained types we can safely use `object` in its place. # # There are two separate functions defined, `is_*` and `is_*_t` for different use cases. # `is_*` is for when you're dealing with an unknown input diff --git a/src/arcadepy/_version.py b/src/arcadepy/_version.py index 39976b0..285daf2 100644 --- a/src/arcadepy/_version.py +++ b/src/arcadepy/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "arcadepy" -__version__ = "1.8.0" # x-release-please-version +__version__ = "1.9.0" # x-release-please-version diff --git a/src/arcadepy/resources/admin/auth_providers.py b/src/arcadepy/resources/admin/auth_providers.py index 076fd7e..4378409 100644 --- a/src/arcadepy/resources/admin/auth_providers.py +++ b/src/arcadepy/resources/admin/auth_providers.py @@ -4,7 +4,7 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -46,18 +46,18 @@ def create( self, *, id: str, - description: str | NotGiven = NOT_GIVEN, - external_id: str | NotGiven = NOT_GIVEN, - oauth2: auth_provider_create_params.Oauth2 | NotGiven = NOT_GIVEN, - provider_id: str | NotGiven = NOT_GIVEN, - status: str | NotGiven = NOT_GIVEN, - type: str | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + external_id: str | Omit = omit, + oauth2: auth_provider_create_params.Oauth2 | Omit = omit, + provider_id: str | Omit = omit, + status: str | Omit = omit, + type: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthProviderResponse: """ Create a new auth provider @@ -101,7 +101,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthProviderListResponse: """List a page of auth providers that are available to the caller""" return self._get( @@ -121,7 +121,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthProviderResponse: """ Delete a specific auth provider @@ -154,7 +154,7 @@ def get( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthProviderResponse: """ Get the details of a specific auth provider @@ -182,18 +182,18 @@ def patch( self, path_id: str, *, - body_id: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - oauth2: auth_provider_patch_params.Oauth2 | NotGiven = NOT_GIVEN, - provider_id: str | NotGiven = NOT_GIVEN, - status: str | NotGiven = NOT_GIVEN, - type: str | NotGiven = NOT_GIVEN, + body_id: str | Omit = omit, + description: str | Omit = omit, + oauth2: auth_provider_patch_params.Oauth2 | Omit = omit, + provider_id: str | Omit = omit, + status: str | Omit = omit, + type: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthProviderResponse: """ Patch an existing auth provider @@ -253,18 +253,18 @@ async def create( self, *, id: str, - description: str | NotGiven = NOT_GIVEN, - external_id: str | NotGiven = NOT_GIVEN, - oauth2: auth_provider_create_params.Oauth2 | NotGiven = NOT_GIVEN, - provider_id: str | NotGiven = NOT_GIVEN, - status: str | NotGiven = NOT_GIVEN, - type: str | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + external_id: str | Omit = omit, + oauth2: auth_provider_create_params.Oauth2 | Omit = omit, + provider_id: str | Omit = omit, + status: str | Omit = omit, + type: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthProviderResponse: """ Create a new auth provider @@ -308,7 +308,7 @@ async def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthProviderListResponse: """List a page of auth providers that are available to the caller""" return await self._get( @@ -328,7 +328,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthProviderResponse: """ Delete a specific auth provider @@ -361,7 +361,7 @@ async def get( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthProviderResponse: """ Get the details of a specific auth provider @@ -389,18 +389,18 @@ async def patch( self, path_id: str, *, - body_id: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - oauth2: auth_provider_patch_params.Oauth2 | NotGiven = NOT_GIVEN, - provider_id: str | NotGiven = NOT_GIVEN, - status: str | NotGiven = NOT_GIVEN, - type: str | NotGiven = NOT_GIVEN, + body_id: str | Omit = omit, + description: str | Omit = omit, + oauth2: auth_provider_patch_params.Oauth2 | Omit = omit, + provider_id: str | Omit = omit, + status: str | Omit = omit, + type: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthProviderResponse: """ Patch an existing auth provider diff --git a/src/arcadepy/resources/admin/secrets.py b/src/arcadepy/resources/admin/secrets.py index 88cecaa..ac839d9 100644 --- a/src/arcadepy/resources/admin/secrets.py +++ b/src/arcadepy/resources/admin/secrets.py @@ -4,7 +4,7 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._types import Body, Query, Headers, NoneType, NotGiven, not_given from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -47,7 +47,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SecretListResponse: """List all secrets that are visible to the caller""" return self._get( @@ -67,7 +67,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: """ Delete a secret by its ID @@ -121,7 +121,7 @@ async def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SecretListResponse: """List all secrets that are visible to the caller""" return await self._get( @@ -141,7 +141,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: """ Delete a secret by its ID diff --git a/src/arcadepy/resources/admin/user_connections.py b/src/arcadepy/resources/admin/user_connections.py index 9a5e164..dc9b9b0 100644 --- a/src/arcadepy/resources/admin/user_connections.py +++ b/src/arcadepy/resources/admin/user_connections.py @@ -4,7 +4,7 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given from ..._utils import maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -45,16 +45,16 @@ def with_streaming_response(self) -> UserConnectionsResourceWithStreamingRespons def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - provider: user_connection_list_params.Provider | NotGiven = NOT_GIVEN, - user: user_connection_list_params.User | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + offset: int | Omit = omit, + provider: user_connection_list_params.Provider | Omit = omit, + user: user_connection_list_params.User | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncOffsetPage[UserConnectionResponse]: """ List all auth connections @@ -102,7 +102,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: """ Delete a user/auth provider connection @@ -151,16 +151,16 @@ def with_streaming_response(self) -> AsyncUserConnectionsResourceWithStreamingRe def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - provider: user_connection_list_params.Provider | NotGiven = NOT_GIVEN, - user: user_connection_list_params.User | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + offset: int | Omit = omit, + provider: user_connection_list_params.Provider | Omit = omit, + user: user_connection_list_params.User | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[UserConnectionResponse, AsyncOffsetPage[UserConnectionResponse]]: """ List all auth connections @@ -208,7 +208,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: """ Delete a user/auth provider connection diff --git a/src/arcadepy/resources/auth.py b/src/arcadepy/resources/auth.py index fd7afaf..b3b794e 100644 --- a/src/arcadepy/resources/auth.py +++ b/src/arcadepy/resources/auth.py @@ -5,7 +5,7 @@ import httpx from ..types import auth_status_params, auth_authorize_params, auth_confirm_user_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -49,13 +49,13 @@ def authorize( *, auth_requirement: auth_authorize_params.AuthRequirement, user_id: str, - next_uri: str | NotGiven = NOT_GIVEN, + next_uri: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthorizationResponse: """ Starts the authorization process for given authorization requirements @@ -128,7 +128,7 @@ def confirm_user( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConfirmUserResponse: """ Confirms a user's details during an authorization flow @@ -161,13 +161,13 @@ def status( self, *, id: str, - wait: int | NotGiven = NOT_GIVEN, + wait: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthorizationResponse: """Checks the status of an ongoing authorization process for a specific tool. @@ -259,13 +259,13 @@ async def authorize( *, auth_requirement: auth_authorize_params.AuthRequirement, user_id: str, - next_uri: str | NotGiven = NOT_GIVEN, + next_uri: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthorizationResponse: """ Starts the authorization process for given authorization requirements @@ -338,7 +338,7 @@ async def confirm_user( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConfirmUserResponse: """ Confirms a user's details during an authorization flow @@ -371,13 +371,13 @@ async def status( self, *, id: str, - wait: int | NotGiven = NOT_GIVEN, + wait: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthorizationResponse: """Checks the status of an ongoing authorization process for a specific tool. diff --git a/src/arcadepy/resources/chat/completions.py b/src/arcadepy/resources/chat/completions.py index 14448ad..a875aaf 100644 --- a/src/arcadepy/resources/chat/completions.py +++ b/src/arcadepy/resources/chat/completions.py @@ -6,7 +6,7 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -47,32 +47,32 @@ def with_streaming_response(self) -> CompletionsResourceWithStreamingResponse: def create( self, *, - frequency_penalty: float | NotGiven = NOT_GIVEN, - logit_bias: Dict[str, int] | NotGiven = NOT_GIVEN, - logprobs: bool | NotGiven = NOT_GIVEN, - max_tokens: int | NotGiven = NOT_GIVEN, - messages: Iterable[ChatMessageParam] | NotGiven = NOT_GIVEN, - model: str | NotGiven = NOT_GIVEN, - n: int | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - presence_penalty: float | NotGiven = NOT_GIVEN, - response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, - seed: int | NotGiven = NOT_GIVEN, - stop: SequenceNotStr[str] | NotGiven = NOT_GIVEN, - stream: bool | NotGiven = NOT_GIVEN, - stream_options: completion_create_params.StreamOptions | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, - tool_choice: object | NotGiven = NOT_GIVEN, - tools: object | NotGiven = NOT_GIVEN, - top_logprobs: int | NotGiven = NOT_GIVEN, - top_p: float | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + frequency_penalty: float | Omit = omit, + logit_bias: Dict[str, int] | Omit = omit, + logprobs: bool | Omit = omit, + max_tokens: int | Omit = omit, + messages: Iterable[ChatMessageParam] | Omit = omit, + model: str | Omit = omit, + n: int | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + presence_penalty: float | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + seed: int | Omit = omit, + stop: SequenceNotStr[str] | Omit = omit, + stream: bool | Omit = omit, + stream_options: completion_create_params.StreamOptions | Omit = omit, + temperature: float | Omit = omit, + tool_choice: object | Omit = omit, + tools: object | Omit = omit, + top_logprobs: int | Omit = omit, + top_p: float | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatResponse: """ Interact with language models via OpenAI's chat completions API @@ -163,32 +163,32 @@ def with_streaming_response(self) -> AsyncCompletionsResourceWithStreamingRespon async def create( self, *, - frequency_penalty: float | NotGiven = NOT_GIVEN, - logit_bias: Dict[str, int] | NotGiven = NOT_GIVEN, - logprobs: bool | NotGiven = NOT_GIVEN, - max_tokens: int | NotGiven = NOT_GIVEN, - messages: Iterable[ChatMessageParam] | NotGiven = NOT_GIVEN, - model: str | NotGiven = NOT_GIVEN, - n: int | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - presence_penalty: float | NotGiven = NOT_GIVEN, - response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, - seed: int | NotGiven = NOT_GIVEN, - stop: SequenceNotStr[str] | NotGiven = NOT_GIVEN, - stream: bool | NotGiven = NOT_GIVEN, - stream_options: completion_create_params.StreamOptions | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, - tool_choice: object | NotGiven = NOT_GIVEN, - tools: object | NotGiven = NOT_GIVEN, - top_logprobs: int | NotGiven = NOT_GIVEN, - top_p: float | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + frequency_penalty: float | Omit = omit, + logit_bias: Dict[str, int] | Omit = omit, + logprobs: bool | Omit = omit, + max_tokens: int | Omit = omit, + messages: Iterable[ChatMessageParam] | Omit = omit, + model: str | Omit = omit, + n: int | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + presence_penalty: float | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + seed: int | Omit = omit, + stop: SequenceNotStr[str] | Omit = omit, + stream: bool | Omit = omit, + stream_options: completion_create_params.StreamOptions | Omit = omit, + temperature: float | Omit = omit, + tool_choice: object | Omit = omit, + tools: object | Omit = omit, + top_logprobs: int | Omit = omit, + top_p: float | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatResponse: """ Interact with language models via OpenAI's chat completions API diff --git a/src/arcadepy/resources/health.py b/src/arcadepy/resources/health.py index 02f18e4..34d2cc7 100644 --- a/src/arcadepy/resources/health.py +++ b/src/arcadepy/resources/health.py @@ -4,7 +4,7 @@ import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -47,7 +47,7 @@ def check( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> HealthSchema: """Check if Arcade Engine is healthy""" return self._get( @@ -87,7 +87,7 @@ async def check( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> HealthSchema: """Check if Arcade Engine is healthy""" return await self._get( diff --git a/src/arcadepy/resources/tools/formatted.py b/src/arcadepy/resources/tools/formatted.py index c1d4db6..4b85016 100644 --- a/src/arcadepy/resources/tools/formatted.py +++ b/src/arcadepy/resources/tools/formatted.py @@ -4,7 +4,7 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -44,17 +44,17 @@ def with_streaming_response(self) -> FormattedResourceWithStreamingResponse: def list( self, *, - format: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - toolkit: str | NotGiven = NOT_GIVEN, - user_id: str | NotGiven = NOT_GIVEN, + format: str | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + toolkit: str | Omit = omit, + user_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncOffsetPage[object]: """ Returns a page of tools from the engine configuration, optionally filtered by @@ -105,14 +105,14 @@ def get( self, name: str, *, - format: str | NotGiven = NOT_GIVEN, - user_id: str | NotGiven = NOT_GIVEN, + format: str | Omit = omit, + user_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Returns the formatted tool specification for a specific tool, given a provider @@ -174,17 +174,17 @@ def with_streaming_response(self) -> AsyncFormattedResourceWithStreamingResponse def list( self, *, - format: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - toolkit: str | NotGiven = NOT_GIVEN, - user_id: str | NotGiven = NOT_GIVEN, + format: str | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + toolkit: str | Omit = omit, + user_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[object, AsyncOffsetPage[object]]: """ Returns a page of tools from the engine configuration, optionally filtered by @@ -235,14 +235,14 @@ async def get( self, name: str, *, - format: str | NotGiven = NOT_GIVEN, - user_id: str | NotGiven = NOT_GIVEN, + format: str | Omit = omit, + user_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Returns the formatted tool specification for a specific tool, given a provider diff --git a/src/arcadepy/resources/tools/scheduled.py b/src/arcadepy/resources/tools/scheduled.py index af32f59..5003b23 100644 --- a/src/arcadepy/resources/tools/scheduled.py +++ b/src/arcadepy/resources/tools/scheduled.py @@ -4,7 +4,7 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -50,14 +50,14 @@ def with_streaming_response(self) -> ScheduledResourceWithStreamingResponse: def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + offset: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncOffsetPage[ToolExecution]: """ Returns a page of scheduled tool executions @@ -103,7 +103,7 @@ def get( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ScheduledGetResponse: """ Returns the details for a specific scheduled tool execution @@ -151,14 +151,14 @@ def with_streaming_response(self) -> AsyncScheduledResourceWithStreamingResponse def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + offset: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[ToolExecution, AsyncOffsetPage[ToolExecution]]: """ Returns a page of scheduled tool executions @@ -204,7 +204,7 @@ async def get( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ScheduledGetResponse: """ Returns the details for a specific scheduled tool execution diff --git a/src/arcadepy/resources/tools/tools.py b/src/arcadepy/resources/tools/tools.py index 4157070..30aec45 100644 --- a/src/arcadepy/resources/tools/tools.py +++ b/src/arcadepy/resources/tools/tools.py @@ -8,7 +8,7 @@ import httpx from ...types import tool_get_params, tool_list_params, tool_execute_params, tool_authorize_params -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from .formatted import ( @@ -74,17 +74,17 @@ def with_streaming_response(self) -> ToolsResourceWithStreamingResponse: def list( self, *, - include_format: List[Literal["arcade", "openai", "anthropic"]] | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - toolkit: str | NotGiven = NOT_GIVEN, - user_id: str | NotGiven = NOT_GIVEN, + include_format: List[Literal["arcade", "openai", "anthropic"]] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + toolkit: str | Omit = omit, + user_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncOffsetPage[ToolDefinition]: """ Returns a page of tools from the engine configuration, optionally filtered by @@ -135,15 +135,15 @@ def authorize( self, *, tool_name: str, - next_uri: str | NotGiven = NOT_GIVEN, - tool_version: str | NotGiven = NOT_GIVEN, - user_id: str | NotGiven = NOT_GIVEN, + next_uri: str | Omit = omit, + tool_version: str | Omit = omit, + user_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthorizationResponse: """ Authorizes a user for a specific tool by name @@ -185,17 +185,17 @@ def execute( self, *, tool_name: str, - include_error_stacktrace: bool | NotGiven = NOT_GIVEN, - input: Dict[str, object] | NotGiven = NOT_GIVEN, - run_at: str | NotGiven = NOT_GIVEN, - tool_version: str | NotGiven = NOT_GIVEN, - user_id: str | NotGiven = NOT_GIVEN, + include_error_stacktrace: bool | Omit = omit, + input: Dict[str, object] | Omit = omit, + run_at: str | Omit = omit, + tool_version: str | Omit = omit, + user_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ExecuteToolResponse: """ Executes a tool by name and arguments @@ -242,14 +242,14 @@ def get( self, name: str, *, - include_format: List[Literal["arcade", "openai", "anthropic"]] | NotGiven = NOT_GIVEN, - user_id: str | NotGiven = NOT_GIVEN, + include_format: List[Literal["arcade", "openai", "anthropic"]] | Omit = omit, + user_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ToolDefinition: """ Returns the arcade tool specification for a specific tool @@ -319,17 +319,17 @@ def with_streaming_response(self) -> AsyncToolsResourceWithStreamingResponse: def list( self, *, - include_format: List[Literal["arcade", "openai", "anthropic"]] | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - toolkit: str | NotGiven = NOT_GIVEN, - user_id: str | NotGiven = NOT_GIVEN, + include_format: List[Literal["arcade", "openai", "anthropic"]] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + toolkit: str | Omit = omit, + user_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[ToolDefinition, AsyncOffsetPage[ToolDefinition]]: """ Returns a page of tools from the engine configuration, optionally filtered by @@ -380,15 +380,15 @@ async def authorize( self, *, tool_name: str, - next_uri: str | NotGiven = NOT_GIVEN, - tool_version: str | NotGiven = NOT_GIVEN, - user_id: str | NotGiven = NOT_GIVEN, + next_uri: str | Omit = omit, + tool_version: str | Omit = omit, + user_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AuthorizationResponse: """ Authorizes a user for a specific tool by name @@ -430,17 +430,17 @@ async def execute( self, *, tool_name: str, - include_error_stacktrace: bool | NotGiven = NOT_GIVEN, - input: Dict[str, object] | NotGiven = NOT_GIVEN, - run_at: str | NotGiven = NOT_GIVEN, - tool_version: str | NotGiven = NOT_GIVEN, - user_id: str | NotGiven = NOT_GIVEN, + include_error_stacktrace: bool | Omit = omit, + input: Dict[str, object] | Omit = omit, + run_at: str | Omit = omit, + tool_version: str | Omit = omit, + user_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ExecuteToolResponse: """ Executes a tool by name and arguments @@ -487,14 +487,14 @@ async def get( self, name: str, *, - include_format: List[Literal["arcade", "openai", "anthropic"]] | NotGiven = NOT_GIVEN, - user_id: str | NotGiven = NOT_GIVEN, + include_format: List[Literal["arcade", "openai", "anthropic"]] | Omit = omit, + user_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ToolDefinition: """ Returns the arcade tool specification for a specific tool diff --git a/src/arcadepy/resources/workers.py b/src/arcadepy/resources/workers.py index 3fa6e2c..1bb6a03 100644 --- a/src/arcadepy/resources/workers.py +++ b/src/arcadepy/resources/workers.py @@ -5,7 +5,7 @@ import httpx from ..types import worker_list_params, worker_tools_params, worker_create_params, worker_update_params -from .._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -48,16 +48,16 @@ def create( self, *, id: str, - enabled: bool | NotGiven = NOT_GIVEN, - http: worker_create_params.HTTP | NotGiven = NOT_GIVEN, - mcp: worker_create_params.Mcp | NotGiven = NOT_GIVEN, - type: str | NotGiven = NOT_GIVEN, + enabled: bool | Omit = omit, + http: worker_create_params.HTTP | Omit = omit, + mcp: worker_create_params.Mcp | Omit = omit, + type: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WorkerResponse: """ Create a worker @@ -93,15 +93,15 @@ def update( self, id: str, *, - enabled: bool | NotGiven = NOT_GIVEN, - http: worker_update_params.HTTP | NotGiven = NOT_GIVEN, - mcp: worker_update_params.Mcp | NotGiven = NOT_GIVEN, + enabled: bool | Omit = omit, + http: worker_update_params.HTTP | Omit = omit, + mcp: worker_update_params.Mcp | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WorkerResponse: """ Update a worker @@ -136,14 +136,14 @@ def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + offset: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncOffsetPage[WorkerResponse]: """ List all workers with their definitions @@ -189,7 +189,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: """ Delete a worker @@ -223,7 +223,7 @@ def get( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WorkerResponse: """ Get a worker by ID @@ -256,7 +256,7 @@ def health( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WorkerHealthResponse: """ Get the health of a worker @@ -284,14 +284,14 @@ def tools( self, id: str, *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + offset: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncOffsetPage[ToolDefinition]: """ Returns a page of tools @@ -355,16 +355,16 @@ async def create( self, *, id: str, - enabled: bool | NotGiven = NOT_GIVEN, - http: worker_create_params.HTTP | NotGiven = NOT_GIVEN, - mcp: worker_create_params.Mcp | NotGiven = NOT_GIVEN, - type: str | NotGiven = NOT_GIVEN, + enabled: bool | Omit = omit, + http: worker_create_params.HTTP | Omit = omit, + mcp: worker_create_params.Mcp | Omit = omit, + type: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WorkerResponse: """ Create a worker @@ -400,15 +400,15 @@ async def update( self, id: str, *, - enabled: bool | NotGiven = NOT_GIVEN, - http: worker_update_params.HTTP | NotGiven = NOT_GIVEN, - mcp: worker_update_params.Mcp | NotGiven = NOT_GIVEN, + enabled: bool | Omit = omit, + http: worker_update_params.HTTP | Omit = omit, + mcp: worker_update_params.Mcp | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WorkerResponse: """ Update a worker @@ -443,14 +443,14 @@ async def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + offset: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[WorkerResponse, AsyncOffsetPage[WorkerResponse]]: """ List all workers with their definitions @@ -496,7 +496,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: """ Delete a worker @@ -530,7 +530,7 @@ async def get( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WorkerResponse: """ Get a worker by ID @@ -563,7 +563,7 @@ async def health( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WorkerHealthResponse: """ Get the health of a worker @@ -591,14 +591,14 @@ def tools( self, id: str, *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + offset: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[ToolDefinition, AsyncOffsetPage[ToolDefinition]]: """ Returns a page of tools diff --git a/src/arcadepy/types/admin/user_connection_response.py b/src/arcadepy/types/admin/user_connection_response.py index bbf62e8..1dde3be 100644 --- a/src/arcadepy/types/admin/user_connection_response.py +++ b/src/arcadepy/types/admin/user_connection_response.py @@ -18,6 +18,8 @@ class UserConnectionResponse(BaseModel): provider_id: Optional[str] = None + provider_type: Optional[str] = None + provider_user_info: Optional[object] = None scopes: Optional[List[str]] = None diff --git a/src/arcadepy/types/worker_create_params.py b/src/arcadepy/types/worker_create_params.py index 00c6966..83cf16d 100644 --- a/src/arcadepy/types/worker_create_params.py +++ b/src/arcadepy/types/worker_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations +from typing import Dict from typing_extensions import Required, TypedDict -__all__ = ["WorkerCreateParams", "HTTP", "Mcp"] +__all__ = ["WorkerCreateParams", "HTTP", "Mcp", "McpOauth2"] class WorkerCreateParams(TypedDict, total=False): @@ -29,9 +30,25 @@ class HTTP(TypedDict, total=False): uri: Required[str] +class McpOauth2(TypedDict, total=False): + authorization_url: str + + client_id: str + + client_secret: str + + external_id: str + + class Mcp(TypedDict, total=False): retry: Required[int] timeout: Required[int] uri: Required[str] + + headers: Dict[str, str] + + oauth2: McpOauth2 + + secrets: Dict[str, str] diff --git a/src/arcadepy/types/worker_response.py b/src/arcadepy/types/worker_response.py index 47c4ea8..5e0efc2 100644 --- a/src/arcadepy/types/worker_response.py +++ b/src/arcadepy/types/worker_response.py @@ -1,11 +1,23 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Optional from typing_extensions import Literal from .._models import BaseModel -__all__ = ["WorkerResponse", "Binding", "HTTP", "HTTPSecret", "Mcp", "Oxp", "OxpSecret"] +__all__ = [ + "WorkerResponse", + "Binding", + "HTTP", + "HTTPSecret", + "Mcp", + "McpOauth2", + "McpOauth2ClientSecret", + "McpSecrets", + "Requirements", + "RequirementsAuthorization", + "RequirementsAuthorizationOauth2", +] class Binding(BaseModel): @@ -36,15 +48,29 @@ class HTTP(BaseModel): uri: Optional[str] = None -class Mcp(BaseModel): - retry: Optional[int] = None +class McpOauth2ClientSecret(BaseModel): + binding: Optional[Literal["static", "tenant", "project", "account"]] = None - timeout: Optional[int] = None + editable: Optional[bool] = None - uri: Optional[str] = None + exists: Optional[bool] = None + + hint: Optional[str] = None + + value: Optional[str] = None + + +class McpOauth2(BaseModel): + authorization_url: Optional[str] = None + + client_id: Optional[str] = None + client_secret: Optional[McpOauth2ClientSecret] = None -class OxpSecret(BaseModel): + redirect_uri: Optional[str] = None + + +class McpSecrets(BaseModel): binding: Optional[Literal["static", "tenant", "project", "account"]] = None editable: Optional[bool] = None @@ -56,16 +82,36 @@ class OxpSecret(BaseModel): value: Optional[str] = None -class Oxp(BaseModel): +class Mcp(BaseModel): + headers: Optional[Dict[str, str]] = None + + oauth2: Optional[McpOauth2] = None + retry: Optional[int] = None - secret: Optional[OxpSecret] = None + secrets: Optional[Dict[str, McpSecrets]] = None timeout: Optional[int] = None uri: Optional[str] = None +class RequirementsAuthorizationOauth2(BaseModel): + met: Optional[bool] = None + + +class RequirementsAuthorization(BaseModel): + met: Optional[bool] = None + + oauth2: Optional[RequirementsAuthorizationOauth2] = None + + +class Requirements(BaseModel): + authorization: Optional[RequirementsAuthorization] = None + + met: Optional[bool] = None + + class WorkerResponse(BaseModel): id: Optional[str] = None @@ -79,6 +125,6 @@ class WorkerResponse(BaseModel): mcp: Optional[Mcp] = None - oxp: Optional[Oxp] = None + requirements: Optional[Requirements] = None type: Optional[Literal["http", "mcp", "unknown"]] = None diff --git a/src/arcadepy/types/worker_update_params.py b/src/arcadepy/types/worker_update_params.py index a07fbb6..172f0d0 100644 --- a/src/arcadepy/types/worker_update_params.py +++ b/src/arcadepy/types/worker_update_params.py @@ -2,9 +2,10 @@ from __future__ import annotations +from typing import Dict from typing_extensions import TypedDict -__all__ = ["WorkerUpdateParams", "HTTP", "Mcp"] +__all__ = ["WorkerUpdateParams", "HTTP", "Mcp", "McpOauth2"] class WorkerUpdateParams(TypedDict, total=False): @@ -25,9 +26,23 @@ class HTTP(TypedDict, total=False): uri: str +class McpOauth2(TypedDict, total=False): + authorization_url: str + + client_id: str + + client_secret: str + + class Mcp(TypedDict, total=False): + headers: Dict[str, str] + + oauth2: McpOauth2 + retry: int + secrets: Dict[str, str] + timeout: int uri: str diff --git a/tests/api_resources/test_workers.py b/tests/api_resources/test_workers.py index 482e6e0..98ede5a 100644 --- a/tests/api_resources/test_workers.py +++ b/tests/api_resources/test_workers.py @@ -44,6 +44,14 @@ def test_method_create_with_all_params(self, client: Arcade) -> None: "retry": 0, "timeout": 1, "uri": "uri", + "headers": {"foo": "string"}, + "oauth2": { + "authorization_url": "authorization_url", + "client_id": "client_id", + "client_secret": "client_secret", + "external_id": "external_id", + }, + "secrets": {"foo": "string"}, }, type="type", ) @@ -92,7 +100,14 @@ def test_method_update_with_all_params(self, client: Arcade) -> None: "uri": "uri", }, mcp={ + "headers": {"foo": "string"}, + "oauth2": { + "authorization_url": "authorization_url", + "client_id": "client_id", + "client_secret": "client_secret", + }, "retry": 0, + "secrets": {"foo": "string"}, "timeout": 1, "uri": "uri", }, @@ -352,6 +367,14 @@ async def test_method_create_with_all_params(self, async_client: AsyncArcade) -> "retry": 0, "timeout": 1, "uri": "uri", + "headers": {"foo": "string"}, + "oauth2": { + "authorization_url": "authorization_url", + "client_id": "client_id", + "client_secret": "client_secret", + "external_id": "external_id", + }, + "secrets": {"foo": "string"}, }, type="type", ) @@ -400,7 +423,14 @@ async def test_method_update_with_all_params(self, async_client: AsyncArcade) -> "uri": "uri", }, mcp={ + "headers": {"foo": "string"}, + "oauth2": { + "authorization_url": "authorization_url", + "client_id": "client_id", + "client_secret": "client_secret", + }, "retry": 0, + "secrets": {"foo": "string"}, "timeout": 1, "uri": "uri", }, diff --git a/tests/test_client.py b/tests/test_client.py index 475ea26..49b90da 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -59,51 +59,49 @@ def _get_open_connections(client: Arcade | AsyncArcade) -> int: class TestArcade: - client = Arcade(base_url=base_url, api_key=api_key, _strict_response_validation=True) - @pytest.mark.respx(base_url=base_url) - def test_raw_response(self, respx_mock: MockRouter) -> None: + def test_raw_response(self, respx_mock: MockRouter, client: Arcade) -> None: respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.post("/foo", cast_to=httpx.Response) + response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) - def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + def test_raw_response_for_binary(self, respx_mock: MockRouter, client: Arcade) -> None: respx_mock.post("/foo").mock( return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') ) - response = self.client.post("/foo", cast_to=httpx.Response) + response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} - def test_copy(self) -> None: - copied = self.client.copy() - assert id(copied) != id(self.client) + def test_copy(self, client: Arcade) -> None: + copied = client.copy() + assert id(copied) != id(client) - copied = self.client.copy(api_key="another My API Key") + copied = client.copy(api_key="another My API Key") assert copied.api_key == "another My API Key" - assert self.client.api_key == "My API Key" + assert client.api_key == "My API Key" - def test_copy_default_options(self) -> None: + def test_copy_default_options(self, client: Arcade) -> None: # options that have a default are overridden correctly - copied = self.client.copy(max_retries=7) + copied = client.copy(max_retries=7) assert copied.max_retries == 7 - assert self.client.max_retries == 2 + assert client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout - assert isinstance(self.client.timeout, httpx.Timeout) - copied = self.client.copy(timeout=None) + assert isinstance(client.timeout, httpx.Timeout) + copied = client.copy(timeout=None) assert copied.timeout is None - assert isinstance(self.client.timeout, httpx.Timeout) + assert isinstance(client.timeout, httpx.Timeout) def test_copy_default_headers(self) -> None: client = Arcade( @@ -138,6 +136,7 @@ def test_copy_default_headers(self) -> None: match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + client.close() def test_copy_default_query(self) -> None: client = Arcade( @@ -175,13 +174,15 @@ def test_copy_default_query(self) -> None: ): client.copy(set_default_query={}, default_query={"foo": "Bar"}) - def test_copy_signature(self) -> None: + client.close() + + def test_copy_signature(self, client: Arcade) -> None: # ensure the same parameters that can be passed to the client are defined in the `.copy()` method init_signature = inspect.signature( # mypy doesn't like that we access the `__init__` property. - self.client.__init__, # type: ignore[misc] + client.__init__, # type: ignore[misc] ) - copy_signature = inspect.signature(self.client.copy) + copy_signature = inspect.signature(client.copy) exclude_params = {"transport", "proxies", "_strict_response_validation"} for name in init_signature.parameters.keys(): @@ -192,12 +193,12 @@ def test_copy_signature(self) -> None: assert copy_param is not None, f"copy() signature is missing the {name} param" @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") - def test_copy_build_request(self) -> None: + def test_copy_build_request(self, client: Arcade) -> None: options = FinalRequestOptions(method="get", url="/foo") def build_request(options: FinalRequestOptions) -> None: - client = self.client.copy() - client._build_request(options) + client_copy = client.copy() + client_copy._build_request(options) # ensure that the machinery is warmed up before tracing starts. build_request(options) @@ -254,14 +255,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic print(frame) raise AssertionError() - def test_request_timeout(self) -> None: - request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + def test_request_timeout(self, client: Arcade) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT - request = self.client._build_request( - FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) - ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0))) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(100.0) @@ -272,6 +271,8 @@ def test_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(0) + client.close() + def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used with httpx.Client(timeout=None) as http_client: @@ -283,6 +284,8 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(None) + client.close() + # no timeout given to the httpx client should not use the httpx default with httpx.Client() as http_client: client = Arcade( @@ -293,6 +296,8 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT + client.close() + # explicitly passing the default timeout currently results in it being ignored with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = Arcade( @@ -303,6 +308,8 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + client.close() + async def test_invalid_http_client(self) -> None: with pytest.raises(TypeError, match="Invalid `http_client` arg"): async with httpx.AsyncClient() as http_client: @@ -314,14 +321,14 @@ async def test_invalid_http_client(self) -> None: ) def test_default_headers_option(self) -> None: - client = Arcade( + test_client = Arcade( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) - request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" assert request.headers.get("x-stainless-lang") == "python" - client2 = Arcade( + test_client2 = Arcade( base_url=base_url, api_key=api_key, _strict_response_validation=True, @@ -330,10 +337,13 @@ def test_default_headers_option(self) -> None: "X-Stainless-Lang": "my-overriding-header", }, ) - request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "stainless" assert request.headers.get("x-stainless-lang") == "my-overriding-header" + test_client.close() + test_client2.close() + def test_validate_headers(self) -> None: client = Arcade(base_url=base_url, api_key=api_key, _strict_response_validation=True) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -362,8 +372,10 @@ def test_default_query_option(self) -> None: url = httpx.URL(request.url) assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} - def test_request_extra_json(self) -> None: - request = self.client._build_request( + client.close() + + def test_request_extra_json(self, client: Arcade) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -374,7 +386,7 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": False} - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -385,7 +397,7 @@ def test_request_extra_json(self) -> None: assert data == {"baz": False} # `extra_json` takes priority over `json_data` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -396,8 +408,8 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": None} - def test_request_extra_headers(self) -> None: - request = self.client._build_request( + def test_request_extra_headers(self, client: Arcade) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -407,7 +419,7 @@ def test_request_extra_headers(self) -> None: assert request.headers.get("X-Foo") == "Foo" # `extra_headers` takes priority over `default_headers` when keys clash - request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( FinalRequestOptions( method="post", url="/foo", @@ -418,8 +430,8 @@ def test_request_extra_headers(self) -> None: ) assert request.headers.get("X-Bar") == "false" - def test_request_extra_query(self) -> None: - request = self.client._build_request( + def test_request_extra_query(self, client: Arcade) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -432,7 +444,7 @@ def test_request_extra_query(self) -> None: assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -446,7 +458,7 @@ def test_request_extra_query(self) -> None: assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -489,7 +501,7 @@ def test_multipart_repeating_array(self, client: Arcade) -> None: ] @pytest.mark.respx(base_url=base_url) - def test_basic_union_response(self, respx_mock: MockRouter) -> None: + def test_basic_union_response(self, respx_mock: MockRouter, client: Arcade) -> None: class Model1(BaseModel): name: str @@ -498,12 +510,12 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" @pytest.mark.respx(base_url=base_url) - def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + def test_union_response_different_types(self, respx_mock: MockRouter, client: Arcade) -> None: """Union of objects with the same field name using a different type""" class Model1(BaseModel): @@ -514,18 +526,18 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model1) assert response.foo == 1 @pytest.mark.respx(base_url=base_url) - def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter, client: Arcade) -> None: """ Response that sets Content-Type to something other than application/json but returns json data """ @@ -541,7 +553,7 @@ class Model(BaseModel): ) ) - response = self.client.get("/foo", cast_to=Model) + response = client.get("/foo", cast_to=Model) assert isinstance(response, Model) assert response.foo == 2 @@ -553,6 +565,8 @@ def test_base_url_setter(self) -> None: assert client.base_url == "https://example.com/from_setter/" + client.close() + def test_base_url_env(self) -> None: with update_env(ARCADE_BASE_URL="http://localhost:5000/from/env"): client = Arcade(api_key=api_key, _strict_response_validation=True) @@ -580,6 +594,7 @@ def test_base_url_trailing_slash(self, client: Arcade) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + client.close() @pytest.mark.parametrize( "client", @@ -603,6 +618,7 @@ def test_base_url_no_trailing_slash(self, client: Arcade) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + client.close() @pytest.mark.parametrize( "client", @@ -626,35 +642,36 @@ def test_absolute_request_url(self, client: Arcade) -> None: ), ) assert request.url == "https://myapi.com/foo" + client.close() def test_copied_client_does_not_close_http(self) -> None: - client = Arcade(base_url=base_url, api_key=api_key, _strict_response_validation=True) - assert not client.is_closed() + test_client = Arcade(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert not test_client.is_closed() - copied = client.copy() - assert copied is not client + copied = test_client.copy() + assert copied is not test_client del copied - assert not client.is_closed() + assert not test_client.is_closed() def test_client_context_manager(self) -> None: - client = Arcade(base_url=base_url, api_key=api_key, _strict_response_validation=True) - with client as c2: - assert c2 is client + test_client = Arcade(base_url=base_url, api_key=api_key, _strict_response_validation=True) + with test_client as c2: + assert c2 is test_client assert not c2.is_closed() - assert not client.is_closed() - assert client.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() @pytest.mark.respx(base_url=base_url) - def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + def test_client_response_validation_error(self, respx_mock: MockRouter, client: Arcade) -> None: class Model(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) with pytest.raises(APIResponseValidationError) as exc: - self.client.get("/foo", cast_to=Model) + client.get("/foo", cast_to=Model) assert isinstance(exc.value.__cause__, ValidationError) @@ -674,11 +691,14 @@ class Model(BaseModel): with pytest.raises(APIResponseValidationError): strict_client.get("/foo", cast_to=Model) - client = Arcade(base_url=base_url, api_key=api_key, _strict_response_validation=False) + non_strict_client = Arcade(base_url=base_url, api_key=api_key, _strict_response_validation=False) - response = client.get("/foo", cast_to=Model) + response = non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] + strict_client.close() + non_strict_client.close() + @pytest.mark.parametrize( "remaining_retries,retry_after,timeout", [ @@ -701,9 +721,9 @@ class Model(BaseModel): ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) - def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: - client = Arcade(base_url=base_url, api_key=api_key, _strict_response_validation=True) - + def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, client: Arcade + ) -> None: headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) calculated = client._calculate_retry_timeout(remaining_retries, options, headers) @@ -717,7 +737,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien with pytest.raises(APITimeoutError): client.chat.completions.with_streaming_response.create().__enter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(client) == 0 @mock.patch("arcadepy._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @@ -726,7 +746,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client with pytest.raises(APIStatusError): client.chat.completions.with_streaming_response.create().__enter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("arcadepy._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -828,83 +848,77 @@ def test_default_client_creation(self) -> None: ) @pytest.mark.respx(base_url=base_url) - def test_follow_redirects(self, respx_mock: MockRouter) -> None: + def test_follow_redirects(self, respx_mock: MockRouter, client: Arcade) -> None: # Test that the default follow_redirects=True allows following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) - response = self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + response = client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) assert response.status_code == 200 assert response.json() == {"status": "ok"} @pytest.mark.respx(base_url=base_url) - def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None: + def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: Arcade) -> None: # Test that follow_redirects=False prevents following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) with pytest.raises(APIStatusError) as exc_info: - self.client.post( - "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response - ) + client.post("/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response) assert exc_info.value.response.status_code == 302 assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" class TestAsyncArcade: - client = AsyncArcade(base_url=base_url, api_key=api_key, _strict_response_validation=True) - @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_raw_response(self, respx_mock: MockRouter) -> None: + async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncArcade) -> None: respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.post("/foo", cast_to=httpx.Response) + response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_client: AsyncArcade) -> None: respx_mock.post("/foo").mock( return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') ) - response = await self.client.post("/foo", cast_to=httpx.Response) + response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} - def test_copy(self) -> None: - copied = self.client.copy() - assert id(copied) != id(self.client) + def test_copy(self, async_client: AsyncArcade) -> None: + copied = async_client.copy() + assert id(copied) != id(async_client) - copied = self.client.copy(api_key="another My API Key") + copied = async_client.copy(api_key="another My API Key") assert copied.api_key == "another My API Key" - assert self.client.api_key == "My API Key" + assert async_client.api_key == "My API Key" - def test_copy_default_options(self) -> None: + def test_copy_default_options(self, async_client: AsyncArcade) -> None: # options that have a default are overridden correctly - copied = self.client.copy(max_retries=7) + copied = async_client.copy(max_retries=7) assert copied.max_retries == 7 - assert self.client.max_retries == 2 + assert async_client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout - assert isinstance(self.client.timeout, httpx.Timeout) - copied = self.client.copy(timeout=None) + assert isinstance(async_client.timeout, httpx.Timeout) + copied = async_client.copy(timeout=None) assert copied.timeout is None - assert isinstance(self.client.timeout, httpx.Timeout) + assert isinstance(async_client.timeout, httpx.Timeout) - def test_copy_default_headers(self) -> None: + async def test_copy_default_headers(self) -> None: client = AsyncArcade( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) @@ -937,8 +951,9 @@ def test_copy_default_headers(self) -> None: match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + await client.close() - def test_copy_default_query(self) -> None: + async def test_copy_default_query(self) -> None: client = AsyncArcade( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} ) @@ -974,13 +989,15 @@ def test_copy_default_query(self) -> None: ): client.copy(set_default_query={}, default_query={"foo": "Bar"}) - def test_copy_signature(self) -> None: + await client.close() + + def test_copy_signature(self, async_client: AsyncArcade) -> None: # ensure the same parameters that can be passed to the client are defined in the `.copy()` method init_signature = inspect.signature( # mypy doesn't like that we access the `__init__` property. - self.client.__init__, # type: ignore[misc] + async_client.__init__, # type: ignore[misc] ) - copy_signature = inspect.signature(self.client.copy) + copy_signature = inspect.signature(async_client.copy) exclude_params = {"transport", "proxies", "_strict_response_validation"} for name in init_signature.parameters.keys(): @@ -991,12 +1008,12 @@ def test_copy_signature(self) -> None: assert copy_param is not None, f"copy() signature is missing the {name} param" @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") - def test_copy_build_request(self) -> None: + def test_copy_build_request(self, async_client: AsyncArcade) -> None: options = FinalRequestOptions(method="get", url="/foo") def build_request(options: FinalRequestOptions) -> None: - client = self.client.copy() - client._build_request(options) + client_copy = async_client.copy() + client_copy._build_request(options) # ensure that the machinery is warmed up before tracing starts. build_request(options) @@ -1053,12 +1070,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic print(frame) raise AssertionError() - async def test_request_timeout(self) -> None: - request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + async def test_request_timeout(self, async_client: AsyncArcade) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT - request = self.client._build_request( + request = async_client._build_request( FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) ) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore @@ -1073,6 +1090,8 @@ async def test_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(0) + await client.close() + async def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used async with httpx.AsyncClient(timeout=None) as http_client: @@ -1084,6 +1103,8 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(None) + await client.close() + # no timeout given to the httpx client should not use the httpx default async with httpx.AsyncClient() as http_client: client = AsyncArcade( @@ -1094,6 +1115,8 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT + await client.close() + # explicitly passing the default timeout currently results in it being ignored async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = AsyncArcade( @@ -1104,6 +1127,8 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + await client.close() + def test_invalid_http_client(self) -> None: with pytest.raises(TypeError, match="Invalid `http_client` arg"): with httpx.Client() as http_client: @@ -1114,15 +1139,15 @@ def test_invalid_http_client(self) -> None: http_client=cast(Any, http_client), ) - def test_default_headers_option(self) -> None: - client = AsyncArcade( + async def test_default_headers_option(self) -> None: + test_client = AsyncArcade( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) - request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" assert request.headers.get("x-stainless-lang") == "python" - client2 = AsyncArcade( + test_client2 = AsyncArcade( base_url=base_url, api_key=api_key, _strict_response_validation=True, @@ -1131,10 +1156,13 @@ def test_default_headers_option(self) -> None: "X-Stainless-Lang": "my-overriding-header", }, ) - request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "stainless" assert request.headers.get("x-stainless-lang") == "my-overriding-header" + await test_client.close() + await test_client2.close() + def test_validate_headers(self) -> None: client = AsyncArcade(base_url=base_url, api_key=api_key, _strict_response_validation=True) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1145,7 +1173,7 @@ def test_validate_headers(self) -> None: client2 = AsyncArcade(base_url=base_url, api_key=None, _strict_response_validation=True) _ = client2 - def test_default_query_option(self) -> None: + async def test_default_query_option(self) -> None: client = AsyncArcade( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} ) @@ -1163,8 +1191,10 @@ def test_default_query_option(self) -> None: url = httpx.URL(request.url) assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} - def test_request_extra_json(self) -> None: - request = self.client._build_request( + await client.close() + + def test_request_extra_json(self, client: Arcade) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1175,7 +1205,7 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": False} - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1186,7 +1216,7 @@ def test_request_extra_json(self) -> None: assert data == {"baz": False} # `extra_json` takes priority over `json_data` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1197,8 +1227,8 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": None} - def test_request_extra_headers(self) -> None: - request = self.client._build_request( + def test_request_extra_headers(self, client: Arcade) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1208,7 +1238,7 @@ def test_request_extra_headers(self) -> None: assert request.headers.get("X-Foo") == "Foo" # `extra_headers` takes priority over `default_headers` when keys clash - request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1219,8 +1249,8 @@ def test_request_extra_headers(self) -> None: ) assert request.headers.get("X-Bar") == "false" - def test_request_extra_query(self) -> None: - request = self.client._build_request( + def test_request_extra_query(self, client: Arcade) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1233,7 +1263,7 @@ def test_request_extra_query(self) -> None: assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1247,7 +1277,7 @@ def test_request_extra_query(self) -> None: assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1290,7 +1320,7 @@ def test_multipart_repeating_array(self, async_client: AsyncArcade) -> None: ] @pytest.mark.respx(base_url=base_url) - async def test_basic_union_response(self, respx_mock: MockRouter) -> None: + async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncArcade) -> None: class Model1(BaseModel): name: str @@ -1299,12 +1329,12 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" @pytest.mark.respx(base_url=base_url) - async def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + async def test_union_response_different_types(self, respx_mock: MockRouter, async_client: AsyncArcade) -> None: """Union of objects with the same field name using a different type""" class Model1(BaseModel): @@ -1315,18 +1345,20 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model1) assert response.foo == 1 @pytest.mark.respx(base_url=base_url) - async def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + async def test_non_application_json_content_type_for_json_data( + self, respx_mock: MockRouter, async_client: AsyncArcade + ) -> None: """ Response that sets Content-Type to something other than application/json but returns json data """ @@ -1342,11 +1374,11 @@ class Model(BaseModel): ) ) - response = await self.client.get("/foo", cast_to=Model) + response = await async_client.get("/foo", cast_to=Model) assert isinstance(response, Model) assert response.foo == 2 - def test_base_url_setter(self) -> None: + async def test_base_url_setter(self) -> None: client = AsyncArcade( base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True ) @@ -1356,7 +1388,9 @@ def test_base_url_setter(self) -> None: assert client.base_url == "https://example.com/from_setter/" - def test_base_url_env(self) -> None: + await client.close() + + async def test_base_url_env(self) -> None: with update_env(ARCADE_BASE_URL="http://localhost:5000/from/env"): client = AsyncArcade(api_key=api_key, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" @@ -1376,7 +1410,7 @@ def test_base_url_env(self) -> None: ], ids=["standard", "custom http client"], ) - def test_base_url_trailing_slash(self, client: AsyncArcade) -> None: + async def test_base_url_trailing_slash(self, client: AsyncArcade) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1385,6 +1419,7 @@ def test_base_url_trailing_slash(self, client: AsyncArcade) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() @pytest.mark.parametrize( "client", @@ -1401,7 +1436,7 @@ def test_base_url_trailing_slash(self, client: AsyncArcade) -> None: ], ids=["standard", "custom http client"], ) - def test_base_url_no_trailing_slash(self, client: AsyncArcade) -> None: + async def test_base_url_no_trailing_slash(self, client: AsyncArcade) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1410,6 +1445,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncArcade) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() @pytest.mark.parametrize( "client", @@ -1426,7 +1462,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncArcade) -> None: ], ids=["standard", "custom http client"], ) - def test_absolute_request_url(self, client: AsyncArcade) -> None: + async def test_absolute_request_url(self, client: AsyncArcade) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1435,37 +1471,37 @@ def test_absolute_request_url(self, client: AsyncArcade) -> None: ), ) assert request.url == "https://myapi.com/foo" + await client.close() async def test_copied_client_does_not_close_http(self) -> None: - client = AsyncArcade(base_url=base_url, api_key=api_key, _strict_response_validation=True) - assert not client.is_closed() + test_client = AsyncArcade(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert not test_client.is_closed() - copied = client.copy() - assert copied is not client + copied = test_client.copy() + assert copied is not test_client del copied await asyncio.sleep(0.2) - assert not client.is_closed() + assert not test_client.is_closed() async def test_client_context_manager(self) -> None: - client = AsyncArcade(base_url=base_url, api_key=api_key, _strict_response_validation=True) - async with client as c2: - assert c2 is client + test_client = AsyncArcade(base_url=base_url, api_key=api_key, _strict_response_validation=True) + async with test_client as c2: + assert c2 is test_client assert not c2.is_closed() - assert not client.is_closed() - assert client.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + async def test_client_response_validation_error(self, respx_mock: MockRouter, async_client: AsyncArcade) -> None: class Model(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) with pytest.raises(APIResponseValidationError) as exc: - await self.client.get("/foo", cast_to=Model) + await async_client.get("/foo", cast_to=Model) assert isinstance(exc.value.__cause__, ValidationError) @@ -1476,7 +1512,6 @@ async def test_client_max_retries_validation(self) -> None: ) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: class Model(BaseModel): name: str @@ -1488,11 +1523,14 @@ class Model(BaseModel): with pytest.raises(APIResponseValidationError): await strict_client.get("/foo", cast_to=Model) - client = AsyncArcade(base_url=base_url, api_key=api_key, _strict_response_validation=False) + non_strict_client = AsyncArcade(base_url=base_url, api_key=api_key, _strict_response_validation=False) - response = await client.get("/foo", cast_to=Model) + response = await non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] + await strict_client.close() + await non_strict_client.close() + @pytest.mark.parametrize( "remaining_retries,retry_after,timeout", [ @@ -1515,13 +1553,12 @@ class Model(BaseModel): ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) - @pytest.mark.asyncio - async def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: - client = AsyncArcade(base_url=base_url, api_key=api_key, _strict_response_validation=True) - + async def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, async_client: AsyncArcade + ) -> None: headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) - calculated = client._calculate_retry_timeout(remaining_retries, options, headers) + calculated = async_client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] @mock.patch("arcadepy._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -1532,7 +1569,7 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, with pytest.raises(APITimeoutError): await async_client.chat.completions.with_streaming_response.create().__aenter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(async_client) == 0 @mock.patch("arcadepy._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @@ -1541,12 +1578,11 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, with pytest.raises(APIStatusError): await async_client.chat.completions.with_streaming_response.create().__aenter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("arcadepy._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio @pytest.mark.parametrize("failure_mode", ["status", "exception"]) async def test_retries_taken( self, @@ -1578,7 +1614,6 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("arcadepy._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_omit_retry_count_header( self, async_client: AsyncArcade, failures_before_success: int, respx_mock: MockRouter ) -> None: @@ -1604,7 +1639,6 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("arcadepy._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_overwrite_retry_count_header( self, async_client: AsyncArcade, failures_before_success: int, respx_mock: MockRouter ) -> None: @@ -1654,26 +1688,26 @@ async def test_default_client_creation(self) -> None: ) @pytest.mark.respx(base_url=base_url) - async def test_follow_redirects(self, respx_mock: MockRouter) -> None: + async def test_follow_redirects(self, respx_mock: MockRouter, async_client: AsyncArcade) -> None: # Test that the default follow_redirects=True allows following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) - response = await self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + response = await async_client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) assert response.status_code == 200 assert response.json() == {"status": "ok"} @pytest.mark.respx(base_url=base_url) - async def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None: + async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_client: AsyncArcade) -> None: # Test that follow_redirects=False prevents following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) with pytest.raises(APIStatusError) as exc_info: - await self.client.post( + await async_client.post( "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response ) diff --git a/tests/test_transform.py b/tests/test_transform.py index b104e1c..bbb8c87 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -8,7 +8,7 @@ import pytest -from arcadepy._types import NOT_GIVEN, Base64FileInput +from arcadepy._types import Base64FileInput, omit, not_given from arcadepy._utils import ( PropertyInfo, transform as _transform, @@ -450,4 +450,11 @@ async def test_transform_skipping(use_async: bool) -> None: @pytest.mark.asyncio async def test_strips_notgiven(use_async: bool) -> None: assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} - assert await transform({"foo_bar": NOT_GIVEN}, Foo1, use_async) == {} + assert await transform({"foo_bar": not_given}, Foo1, use_async) == {} + + +@parametrize +@pytest.mark.asyncio +async def test_strips_omit(use_async: bool) -> None: + assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} + assert await transform({"foo_bar": omit}, Foo1, use_async) == {}