From 160f11e767ab3d5f7f1fdd8e423a6277a651fc82 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2025 15:29:44 +0000 Subject: [PATCH 01/19] chore(internal): change default timeout to an int (#56) --- src/prelude_python_sdk/_constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prelude_python_sdk/_constants.py b/src/prelude_python_sdk/_constants.py index a2ac3b6..6ddf2c7 100644 --- a/src/prelude_python_sdk/_constants.py +++ b/src/prelude_python_sdk/_constants.py @@ -6,7 +6,7 @@ OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to" # default timeout is 1 minute -DEFAULT_TIMEOUT = httpx.Timeout(timeout=60.0, connect=5.0) +DEFAULT_TIMEOUT = httpx.Timeout(timeout=60, connect=5.0) DEFAULT_MAX_RETRIES = 2 DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20) From 2381d4a22cfd032f97470e0d012b6fc8a133305c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2025 15:31:31 +0000 Subject: [PATCH 02/19] chore(internal): bummp ruff dependency (#58) --- pyproject.toml | 2 +- requirements-dev.lock | 2 +- scripts/utils/ruffen-docs.py | 4 ++-- src/prelude_python_sdk/_models.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8169a86..8dd822b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -177,7 +177,7 @@ select = [ "T201", "T203", # misuse of typing.TYPE_CHECKING - "TCH004", + "TC004", # import rules "TID251", ] diff --git a/requirements-dev.lock b/requirements-dev.lock index 5981b85..baa8124 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -78,7 +78,7 @@ pytz==2023.3.post1 # via dirty-equals respx==0.22.0 rich==13.7.1 -ruff==0.6.9 +ruff==0.9.4 setuptools==68.2.2 # via nodeenv six==1.16.0 diff --git a/scripts/utils/ruffen-docs.py b/scripts/utils/ruffen-docs.py index 37b3d94..0cf2bd2 100644 --- a/scripts/utils/ruffen-docs.py +++ b/scripts/utils/ruffen-docs.py @@ -47,7 +47,7 @@ def _md_match(match: Match[str]) -> str: with _collect_error(match): code = format_code_block(code) code = textwrap.indent(code, match["indent"]) - return f'{match["before"]}{code}{match["after"]}' + return f"{match['before']}{code}{match['after']}" def _pycon_match(match: Match[str]) -> str: code = "" @@ -97,7 +97,7 @@ def finish_fragment() -> None: def _md_pycon_match(match: Match[str]) -> str: code = _pycon_match(match) code = textwrap.indent(code, match["indent"]) - return f'{match["before"]}{code}{match["after"]}' + return f"{match['before']}{code}{match['after']}" src = MD_RE.sub(_md_match, src) src = MD_PYCON_RE.sub(_md_pycon_match, src) diff --git a/src/prelude_python_sdk/_models.py b/src/prelude_python_sdk/_models.py index 9a918aa..12c34b7 100644 --- a/src/prelude_python_sdk/_models.py +++ b/src/prelude_python_sdk/_models.py @@ -172,7 +172,7 @@ def to_json( @override def __str__(self) -> str: # mypy complains about an invalid self arg - return f'{self.__repr_name__()}({self.__repr_str__(", ")})' # type: ignore[misc] + return f"{self.__repr_name__()}({self.__repr_str__(', ')})" # type: ignore[misc] # Override the 'construct' method in a way that supports recursive parsing without validation. # Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836. From 6dcc82a592bdad9316eae8ab7b93095d2176caf3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2025 17:12:34 +0000 Subject: [PATCH 03/19] feat(client): send `X-Stainless-Read-Timeout` header (#59) --- src/prelude_python_sdk/_base_client.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/prelude_python_sdk/_base_client.py b/src/prelude_python_sdk/_base_client.py index e2d89d2..9c7b807 100644 --- a/src/prelude_python_sdk/_base_client.py +++ b/src/prelude_python_sdk/_base_client.py @@ -418,10 +418,17 @@ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0 if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers: headers[idempotency_header] = options.idempotency_key or self._idempotency_key() - # Don't set the retry count header if it was already set or removed by the caller. We check + # Don't set these headers if they were already set or removed by the caller. We check # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. - if "x-stainless-retry-count" not in (header.lower() for header in custom_headers): + lower_custom_headers = [header.lower() for header in custom_headers] + if "x-stainless-retry-count" not in lower_custom_headers: headers["x-stainless-retry-count"] = str(retries_taken) + if "x-stainless-read-timeout" not in lower_custom_headers: + timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout + if isinstance(timeout, Timeout): + timeout = timeout.read + if timeout is not None: + headers["x-stainless-read-timeout"] = str(timeout) return headers From 9bf6b958c8b1ac01d191fb3ffdad7beb9ad0f06a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 12:55:12 +0000 Subject: [PATCH 04/19] chore(internal): fix type traversing dictionary params (#60) --- src/prelude_python_sdk/_utils/_transform.py | 12 +++++++++++- tests/test_transform.py | 11 ++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/prelude_python_sdk/_utils/_transform.py b/src/prelude_python_sdk/_utils/_transform.py index a6b62ca..18afd9d 100644 --- a/src/prelude_python_sdk/_utils/_transform.py +++ b/src/prelude_python_sdk/_utils/_transform.py @@ -25,7 +25,7 @@ is_annotated_type, strip_annotated_type, ) -from .._compat import model_dump, is_typeddict +from .._compat import get_origin, model_dump, is_typeddict _T = TypeVar("_T") @@ -164,9 +164,14 @@ def _transform_recursive( inner_type = annotation stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type if is_typeddict(stripped_type) and is_mapping(data): return _transform_typeddict(data, stripped_type) + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + if ( # List[T] (is_list_type(stripped_type) and is_list(data)) @@ -307,9 +312,14 @@ async def _async_transform_recursive( inner_type = annotation stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type if is_typeddict(stripped_type) and is_mapping(data): return await _async_transform_typeddict(data, stripped_type) + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + if ( # List[T] (is_list_type(stripped_type) and is_list(data)) diff --git a/tests/test_transform.py b/tests/test_transform.py index ed8a80c..b48736d 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -2,7 +2,7 @@ import io import pathlib -from typing import Any, List, Union, TypeVar, Iterable, Optional, cast +from typing import Any, Dict, List, Union, TypeVar, Iterable, Optional, cast from datetime import date, datetime from typing_extensions import Required, Annotated, TypedDict @@ -388,6 +388,15 @@ def my_iter() -> Iterable[Baz8]: } +@parametrize +@pytest.mark.asyncio +async def test_dictionary_items(use_async: bool) -> None: + class DictItems(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + assert await transform({"foo": {"foo_baz": "bar"}}, Dict[str, DictItems], use_async) == {"foo": {"fooBaz": "bar"}} + + class TypedDictIterableUnionStr(TypedDict): foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")] From 0639a28c925209b6d2adb2d3022f350044bf5995 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:19:51 +0000 Subject: [PATCH 05/19] chore(internal): minor type handling changes (#61) --- src/prelude_python_sdk/_models.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/prelude_python_sdk/_models.py b/src/prelude_python_sdk/_models.py index 12c34b7..c4401ff 100644 --- a/src/prelude_python_sdk/_models.py +++ b/src/prelude_python_sdk/_models.py @@ -426,10 +426,16 @@ def construct_type(*, value: object, type_: object) -> object: If the given value does not match the expected type then it is returned as-is. """ + + # store a reference to the original type we were given before we extract any inner + # types so that we can properly resolve forward references in `TypeAliasType` annotations + original_type = None + # we allow `object` as the input type because otherwise, passing things like # `Literal['value']` will be reported as a type error by type checkers type_ = cast("type[object]", type_) if is_type_alias_type(type_): + original_type = type_ # type: ignore[unreachable] type_ = type_.__value__ # type: ignore[unreachable] # unwrap `Annotated[T, ...]` -> `T` @@ -446,7 +452,7 @@ def construct_type(*, value: object, type_: object) -> object: if is_union(origin): try: - return validate_type(type_=cast("type[object]", type_), value=value) + return validate_type(type_=cast("type[object]", original_type or type_), value=value) except Exception: pass From 6096c2aff213dca771b4e8f8675569e1bc1d1edf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 18:27:35 +0000 Subject: [PATCH 06/19] chore(internal): update client tests (#62) --- tests/test_client.py | 54 ++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 71af557..a9d9e60 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -23,6 +23,7 @@ from prelude_python_sdk import Prelude, AsyncPrelude, APIResponseValidationError from prelude_python_sdk._types import Omit +from prelude_python_sdk._utils import maybe_transform from prelude_python_sdk._models import BaseModel, FinalRequestOptions from prelude_python_sdk._constants import RAW_RESPONSE_HEADER from prelude_python_sdk._exceptions import PreludeError, APIStatusError, APITimeoutError, APIResponseValidationError @@ -32,6 +33,7 @@ BaseClient, make_request_options, ) +from prelude_python_sdk.types.verification_create_params import VerificationCreateParams from .utils import update_env @@ -732,11 +734,14 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> No "/v2/verification", body=cast( object, - dict( - target={ - "type": "phone_number", - "value": "+30123456789", - } + maybe_transform( + dict( + target={ + "type": "phone_number", + "value": "+30123456789", + } + ), + VerificationCreateParams, ), ), cast_to=httpx.Response, @@ -755,11 +760,14 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> Non "/v2/verification", body=cast( object, - dict( - target={ - "type": "phone_number", - "value": "+30123456789", - } + maybe_transform( + dict( + target={ + "type": "phone_number", + "value": "+30123456789", + } + ), + VerificationCreateParams, ), ), cast_to=httpx.Response, @@ -1544,11 +1552,14 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) "/v2/verification", body=cast( object, - dict( - target={ - "type": "phone_number", - "value": "+30123456789", - } + maybe_transform( + dict( + target={ + "type": "phone_number", + "value": "+30123456789", + } + ), + VerificationCreateParams, ), ), cast_to=httpx.Response, @@ -1567,11 +1578,14 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) "/v2/verification", body=cast( object, - dict( - target={ - "type": "phone_number", - "value": "+30123456789", - } + maybe_transform( + dict( + target={ + "type": "phone_number", + "value": "+30123456789", + } + ), + VerificationCreateParams, ), ), cast_to=httpx.Response, From 05164849027af87dba0911340086b7904a7171f2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 20 Feb 2025 15:20:58 +0000 Subject: [PATCH 07/19] chore(internal): codegen related update (#63) --- src/prelude_python_sdk/_utils/_sync.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/prelude_python_sdk/_utils/_sync.py b/src/prelude_python_sdk/_utils/_sync.py index 8b3aaf2..ad7ec71 100644 --- a/src/prelude_python_sdk/_utils/_sync.py +++ b/src/prelude_python_sdk/_utils/_sync.py @@ -7,16 +7,20 @@ from typing import Any, TypeVar, Callable, Awaitable from typing_extensions import ParamSpec +import anyio +import sniffio +import anyio.to_thread + T_Retval = TypeVar("T_Retval") T_ParamSpec = ParamSpec("T_ParamSpec") if sys.version_info >= (3, 9): - to_thread = asyncio.to_thread + _asyncio_to_thread = asyncio.to_thread else: # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread # for Python 3.8 support - async def to_thread( + async def _asyncio_to_thread( func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs ) -> Any: """Asynchronously run function *func* in a separate thread. @@ -34,6 +38,17 @@ async def to_thread( return await loop.run_in_executor(None, func_call) +async def to_thread( + func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs +) -> T_Retval: + if sniffio.current_async_library() == "asyncio": + return await _asyncio_to_thread(func, *args, **kwargs) + + return await anyio.to_thread.run_sync( + functools.partial(func, *args, **kwargs), + ) + + # inspired by `asyncer`, https://github.com/tiangolo/asyncer def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: """ From b32f98934973c8c2cfacd3ad9a6c0817405ec3c9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 20 Feb 2025 22:34:23 +0000 Subject: [PATCH 08/19] feat(client): allow passing `NotGiven` for body (#64) fix(client): mark some request bodies as optional --- src/prelude_python_sdk/_base_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prelude_python_sdk/_base_client.py b/src/prelude_python_sdk/_base_client.py index 9c7b807..2d6c8a3 100644 --- a/src/prelude_python_sdk/_base_client.py +++ b/src/prelude_python_sdk/_base_client.py @@ -518,7 +518,7 @@ def _build_request( # so that passing a `TypedDict` doesn't cause an error. # https://github.com/microsoft/pyright/issues/3526#event-6715453066 params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, - json=json_data, + json=json_data if is_given(json_data) else None, files=files, **kwargs, ) From da3f6c6f48241dfe0909aabeb3eec2ba83c0e8ef Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:00:02 +0000 Subject: [PATCH 09/19] chore(internal): fix devcontainers setup (#65) --- .devcontainer/Dockerfile | 2 +- .devcontainer/devcontainer.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ac9a2e7..55d2025 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,4 +6,4 @@ USER vscode RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.35.0" RYE_INSTALL_OPTION="--yes" bash ENV PATH=/home/vscode/.rye/shims:$PATH -RUN echo "[[ -d .venv ]] && source .venv/bin/activate" >> /home/vscode/.bashrc +RUN echo "[[ -d .venv ]] && source .venv/bin/activate || export PATH=\$PATH" >> /home/vscode/.bashrc diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bbeb30b..c17fdc1 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -24,6 +24,9 @@ } } } + }, + "features": { + "ghcr.io/devcontainers/features/node:1": {} } // Features to add to the dev container. More info: https://containers.dev/features. From affe056afdc01fc46d7dc23a003b69bb8528c16d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 25 Feb 2025 11:00:40 +0000 Subject: [PATCH 10/19] chore(internal): properly set __pydantic_private__ (#66) --- src/prelude_python_sdk/_base_client.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/prelude_python_sdk/_base_client.py b/src/prelude_python_sdk/_base_client.py index 2d6c8a3..c6d73a4 100644 --- a/src/prelude_python_sdk/_base_client.py +++ b/src/prelude_python_sdk/_base_client.py @@ -63,7 +63,7 @@ ModelBuilderProtocol, ) from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping -from ._compat import model_copy, model_dump +from ._compat import PYDANTIC_V2, model_copy, model_dump from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type from ._response import ( APIResponse, @@ -207,6 +207,9 @@ def _set_private_attributes( model: Type[_T], options: FinalRequestOptions, ) -> None: + if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + self._model = model self._client = client self._options = options @@ -292,6 +295,9 @@ def _set_private_attributes( client: AsyncAPIClient, options: FinalRequestOptions, ) -> None: + if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + self._model = model self._client = client self._options = options From 32798a95e57769f4fc29abac8ba2dcd58d55a6ef Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 26 Feb 2025 16:55:24 +0000 Subject: [PATCH 11/19] chore(internal): codegen related update (#67) --- .stats.yml | 2 +- .../resources/transactional.py | 14 ------- .../resources/verification.py | 8 ---- .../types/transactional_send_params.py | 8 ---- .../types/verification_check_response.py | 6 +-- .../types/verification_create_params.py | 42 ++++--------------- .../types/verification_create_response.py | 14 +++---- .../types/watch_feed_back_response.py | 3 +- .../types/watch_predict_response.py | 6 +-- tests/api_resources/test_transactional.py | 38 ++++++++--------- tests/api_resources/test_verification.py | 40 +++++++----------- 11 files changed, 55 insertions(+), 126 deletions(-) diff --git a/.stats.yml b/.stats.yml index f7be301..6028d62 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 5 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/prelude%2Fprelude-ba7ca51ae8c674c16740d0254504bc208d0c3ea5f9970e347e49e70a22dd9072.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/prelude%2Fprelude-ca3f4103971d8bfdb9ea7c345b6112409a62e183460acd29da40a155192d2213.yml diff --git a/src/prelude_python_sdk/resources/transactional.py b/src/prelude_python_sdk/resources/transactional.py index 5fac16f..a6c7015 100644 --- a/src/prelude_python_sdk/resources/transactional.py +++ b/src/prelude_python_sdk/resources/transactional.py @@ -55,7 +55,6 @@ def send( correlation_id: str | NotGiven = NOT_GIVEN, expires_at: str | NotGiven = NOT_GIVEN, from_: str | NotGiven = NOT_GIVEN, - locale: str | NotGiven = NOT_GIVEN, variables: Dict[str, str] | NotGiven = NOT_GIVEN, # 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. @@ -80,11 +79,6 @@ def send( from_: The Sender ID. - locale: A BCP-47 formatted locale string with the language the text message will be sent - to. If there's no locale set, the language will be determined by the country - code of the phone number. If the language specified doesn't exist, the default - set on the template will be used. - variables: The variables to be replaced in the template. extra_headers: Send extra headers @@ -105,7 +99,6 @@ def send( "correlation_id": correlation_id, "expires_at": expires_at, "from_": from_, - "locale": locale, "variables": variables, }, transactional_send_params.TransactionalSendParams, @@ -146,7 +139,6 @@ async def send( correlation_id: str | NotGiven = NOT_GIVEN, expires_at: str | NotGiven = NOT_GIVEN, from_: str | NotGiven = NOT_GIVEN, - locale: str | NotGiven = NOT_GIVEN, variables: Dict[str, str] | NotGiven = NOT_GIVEN, # 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. @@ -171,11 +163,6 @@ async def send( from_: The Sender ID. - locale: A BCP-47 formatted locale string with the language the text message will be sent - to. If there's no locale set, the language will be determined by the country - code of the phone number. If the language specified doesn't exist, the default - set on the template will be used. - variables: The variables to be replaced in the template. extra_headers: Send extra headers @@ -196,7 +183,6 @@ async def send( "correlation_id": correlation_id, "expires_at": expires_at, "from_": from_, - "locale": locale, "variables": variables, }, transactional_send_params.TransactionalSendParams, diff --git a/src/prelude_python_sdk/resources/verification.py b/src/prelude_python_sdk/resources/verification.py index 735a7cc..e6201e1 100644 --- a/src/prelude_python_sdk/resources/verification.py +++ b/src/prelude_python_sdk/resources/verification.py @@ -49,7 +49,6 @@ def create( self, *, target: verification_create_params.Target, - dispatch_id: str | NotGiven = NOT_GIVEN, metadata: verification_create_params.Metadata | NotGiven = NOT_GIVEN, options: verification_create_params.Options | NotGiven = NOT_GIVEN, signals: verification_create_params.Signals | NotGiven = NOT_GIVEN, @@ -69,8 +68,6 @@ def create( Args: target: The target. Currently this can only be an E.164 formatted phone number. - dispatch_id: The identifier of the dispatch that came from the front-end SDK. - metadata: The metadata for this verification. This object will be returned with every response or webhook sent that refers to this verification. @@ -91,7 +88,6 @@ def create( body=maybe_transform( { "target": target, - "dispatch_id": dispatch_id, "metadata": metadata, "options": options, "signals": signals, @@ -172,7 +168,6 @@ async def create( self, *, target: verification_create_params.Target, - dispatch_id: str | NotGiven = NOT_GIVEN, metadata: verification_create_params.Metadata | NotGiven = NOT_GIVEN, options: verification_create_params.Options | NotGiven = NOT_GIVEN, signals: verification_create_params.Signals | NotGiven = NOT_GIVEN, @@ -192,8 +187,6 @@ async def create( Args: target: The target. Currently this can only be an E.164 formatted phone number. - dispatch_id: The identifier of the dispatch that came from the front-end SDK. - metadata: The metadata for this verification. This object will be returned with every response or webhook sent that refers to this verification. @@ -214,7 +207,6 @@ async def create( body=await async_maybe_transform( { "target": target, - "dispatch_id": dispatch_id, "metadata": metadata, "options": options, "signals": signals, diff --git a/src/prelude_python_sdk/types/transactional_send_params.py b/src/prelude_python_sdk/types/transactional_send_params.py index 02be0af..6aba37f 100644 --- a/src/prelude_python_sdk/types/transactional_send_params.py +++ b/src/prelude_python_sdk/types/transactional_send_params.py @@ -29,13 +29,5 @@ class TransactionalSendParams(TypedDict, total=False): from_: Annotated[str, PropertyInfo(alias="from")] """The Sender ID.""" - locale: str - """ - A BCP-47 formatted locale string with the language the text message will be sent - to. If there's no locale set, the language will be determined by the country - code of the phone number. If the language specified doesn't exist, the default - set on the template will be used. - """ - variables: Dict[str, str] """The variables to be replaced in the template.""" diff --git a/src/prelude_python_sdk/types/verification_check_response.py b/src/prelude_python_sdk/types/verification_check_response.py index 7bca9cf..4bcb3b4 100644 --- a/src/prelude_python_sdk/types/verification_check_response.py +++ b/src/prelude_python_sdk/types/verification_check_response.py @@ -13,9 +13,6 @@ class Metadata(BaseModel): class VerificationCheckResponse(BaseModel): - status: Literal["success", "failure", "expired_or_not_found"] - """The status of the check.""" - id: Optional[str] = None """The verification identifier.""" @@ -23,3 +20,6 @@ class VerificationCheckResponse(BaseModel): """The metadata for this verification.""" request_id: Optional[str] = None + + status: Optional[Literal["success", "failure", "expired"]] = None + """The status of the check.""" diff --git a/src/prelude_python_sdk/types/verification_create_params.py b/src/prelude_python_sdk/types/verification_create_params.py index 9ea69a7..1b59abf 100644 --- a/src/prelude_python_sdk/types/verification_create_params.py +++ b/src/prelude_python_sdk/types/verification_create_params.py @@ -4,16 +4,13 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["VerificationCreateParams", "Target", "Metadata", "Options", "OptionsAppRealm", "Signals"] +__all__ = ["VerificationCreateParams", "Target", "Metadata", "Options", "Signals"] class VerificationCreateParams(TypedDict, total=False): target: Required[Target] """The target. Currently this can only be an E.164 formatted phone number.""" - dispatch_id: str - """The identifier of the dispatch that came from the front-end SDK.""" - metadata: Metadata """The metadata for this verification. @@ -41,37 +38,12 @@ class Metadata(TypedDict, total=False): """A user-defined identifier to correlate this verification with.""" -class OptionsAppRealm(TypedDict, total=False): - platform: Required[Literal["android"]] - """The platform the SMS will be sent to. - - We are currently only supporting "android". - """ - - value: Required[str] - """The Android SMS Retriever API hash code that identifies your app.""" - - class Options(TypedDict, total=False): - app_realm: OptionsAppRealm - """This allows you to automatically retrieve and fill the OTP code on mobile apps. - - Currently only Android devices are supported. - """ - - code_size: int - """The size of the code generated. - - It should be between 4 and 8. Defaults to the code size specified from the - Dashboard. - """ - - custom_code: str - """The custom code to use for OTP verification. + app_realm: str + """The Android SMS Retriever API hash code that identifies your app. - This feature is only available for compatibility purposes and subject to - Prelude’s approval. Contact us to discuss your use case. For more details, refer - to [Multi Routing](/concepts/multi-routing). + This allows you to automatically retrieve and fill the OTP code on Android + devices. """ locale: str @@ -110,13 +82,13 @@ class Signals(TypedDict, total=False): device_model: str """The model of the user's device.""" - device_platform: Literal["android", "ios", "ipados", "tvos", "web"] + device_platform: Literal["android", "ios", "web"] """The type of the user's device.""" ip: str """The IP address of the user's device.""" - is_trusted_user: bool + is_trusted_user: str """ This signal should provide a higher level of trust, indicating that the user is genuine. For more details, refer to [Signals](/guides/prevent-fraud#signals). diff --git a/src/prelude_python_sdk/types/verification_create_response.py b/src/prelude_python_sdk/types/verification_create_response.py index fdc66ff..f1121cb 100644 --- a/src/prelude_python_sdk/types/verification_create_response.py +++ b/src/prelude_python_sdk/types/verification_create_response.py @@ -13,16 +13,16 @@ class Metadata(BaseModel): class VerificationCreateResponse(BaseModel): - id: str + id: Optional[str] = None """The verification identifier.""" - method: Literal["message"] - """The method used for verifying this phone number.""" - - status: Literal["success", "retry", "blocked"] - """The status of the verification.""" - metadata: Optional[Metadata] = None """The metadata for this verification.""" + method: Optional[Literal["message"]] = None + """The method used for verifying this phone number.""" + request_id: Optional[str] = None + + status: Optional[Literal["success", "retry", "blocked"]] = None + """The status of the verification.""" diff --git a/src/prelude_python_sdk/types/watch_feed_back_response.py b/src/prelude_python_sdk/types/watch_feed_back_response.py index c879860..aa10b12 100644 --- a/src/prelude_python_sdk/types/watch_feed_back_response.py +++ b/src/prelude_python_sdk/types/watch_feed_back_response.py @@ -1,5 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from .._models import BaseModel @@ -7,5 +8,5 @@ class WatchFeedBackResponse(BaseModel): - id: str + id: Optional[str] = None """A unique identifier for your feedback request.""" diff --git a/src/prelude_python_sdk/types/watch_predict_response.py b/src/prelude_python_sdk/types/watch_predict_response.py index cb5a871..d85e251 100644 --- a/src/prelude_python_sdk/types/watch_predict_response.py +++ b/src/prelude_python_sdk/types/watch_predict_response.py @@ -20,10 +20,10 @@ class Reasoning(BaseModel): class WatchPredictResponse(BaseModel): - id: str + id: Optional[str] = None """A unique identifier for your prediction request.""" - prediction: Literal["allow", "block"] + prediction: Optional[Literal["allow", "block"]] = None """A label indicating the trustworthiness of the phone number.""" - reasoning: Reasoning + reasoning: Optional[Reasoning] = None diff --git a/tests/api_resources/test_transactional.py b/tests/api_resources/test_transactional.py index b4f77bd..7a105f6 100644 --- a/tests/api_resources/test_transactional.py +++ b/tests/api_resources/test_transactional.py @@ -23,8 +23,8 @@ class TestTransactional: @parametrize def test_method_send(self, client: Prelude) -> None: transactional = client.transactional.send( - template_id="template_01jd1xq0cffycayqtdkdbv4d61", - to="+30123456789", + template_id="template_id", + to="to", ) assert_matches_type(TransactionalSendResponse, transactional, path=["response"]) @@ -34,14 +34,13 @@ def test_method_send(self, client: Prelude) -> None: @parametrize def test_method_send_with_all_params(self, client: Prelude) -> None: transactional = client.transactional.send( - template_id="template_01jd1xq0cffycayqtdkdbv4d61", - to="+30123456789", + template_id="template_id", + to="to", callback_url="callback_url", correlation_id="correlation_id", expires_at="expires_at", from_="from", - locale="el-GR", - variables={"foo": "bar"}, + variables={"foo": "string"}, ) assert_matches_type(TransactionalSendResponse, transactional, path=["response"]) @@ -51,8 +50,8 @@ def test_method_send_with_all_params(self, client: Prelude) -> None: @parametrize def test_raw_response_send(self, client: Prelude) -> None: response = client.transactional.with_raw_response.send( - template_id="template_01jd1xq0cffycayqtdkdbv4d61", - to="+30123456789", + template_id="template_id", + to="to", ) assert response.is_closed is True @@ -66,8 +65,8 @@ def test_raw_response_send(self, client: Prelude) -> None: @parametrize def test_streaming_response_send(self, client: Prelude) -> None: with client.transactional.with_streaming_response.send( - template_id="template_01jd1xq0cffycayqtdkdbv4d61", - to="+30123456789", + template_id="template_id", + to="to", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -87,8 +86,8 @@ class TestAsyncTransactional: @parametrize async def test_method_send(self, async_client: AsyncPrelude) -> None: transactional = await async_client.transactional.send( - template_id="template_01jd1xq0cffycayqtdkdbv4d61", - to="+30123456789", + template_id="template_id", + to="to", ) assert_matches_type(TransactionalSendResponse, transactional, path=["response"]) @@ -98,14 +97,13 @@ async def test_method_send(self, async_client: AsyncPrelude) -> None: @parametrize async def test_method_send_with_all_params(self, async_client: AsyncPrelude) -> None: transactional = await async_client.transactional.send( - template_id="template_01jd1xq0cffycayqtdkdbv4d61", - to="+30123456789", + template_id="template_id", + to="to", callback_url="callback_url", correlation_id="correlation_id", expires_at="expires_at", from_="from", - locale="el-GR", - variables={"foo": "bar"}, + variables={"foo": "string"}, ) assert_matches_type(TransactionalSendResponse, transactional, path=["response"]) @@ -115,8 +113,8 @@ async def test_method_send_with_all_params(self, async_client: AsyncPrelude) -> @parametrize async def test_raw_response_send(self, async_client: AsyncPrelude) -> None: response = await async_client.transactional.with_raw_response.send( - template_id="template_01jd1xq0cffycayqtdkdbv4d61", - to="+30123456789", + template_id="template_id", + to="to", ) assert response.is_closed is True @@ -130,8 +128,8 @@ async def test_raw_response_send(self, async_client: AsyncPrelude) -> None: @parametrize async def test_streaming_response_send(self, async_client: AsyncPrelude) -> None: async with async_client.transactional.with_streaming_response.send( - template_id="template_01jd1xq0cffycayqtdkdbv4d61", - to="+30123456789", + template_id="template_id", + to="to", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/test_verification.py b/tests/api_resources/test_verification.py index 6170e67..df37638 100644 --- a/tests/api_resources/test_verification.py +++ b/tests/api_resources/test_verification.py @@ -37,27 +37,21 @@ def test_method_create_with_all_params(self, client: Prelude) -> None: "type": "phone_number", "value": "+30123456789", }, - dispatch_id="dispatch_id", metadata={"correlation_id": "correlation_id"}, options={ - "app_realm": { - "platform": "android", - "value": "value", - }, - "code_size": 5, - "custom_code": "custom_code", + "app_realm": "app_realm", "locale": "el-GR", "sender_id": "sender_id", "template_id": "template_id", }, signals={ - "app_version": "1.2.34", - "device_id": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2", - "device_model": "iPhone17,2", + "app_version": "app_version", + "device_id": "device_id", + "device_model": "device_model", "device_platform": "android", - "ip": "192.0.2.1", - "is_trusted_user": False, - "os_version": "18.0.1", + "ip": "8.8.8.8", + "is_trusted_user": "is_trusted_user", + "os_version": "os_version", }, ) assert_matches_type(VerificationCreateResponse, verification, path=["response"]) @@ -156,27 +150,21 @@ async def test_method_create_with_all_params(self, async_client: AsyncPrelude) - "type": "phone_number", "value": "+30123456789", }, - dispatch_id="dispatch_id", metadata={"correlation_id": "correlation_id"}, options={ - "app_realm": { - "platform": "android", - "value": "value", - }, - "code_size": 5, - "custom_code": "custom_code", + "app_realm": "app_realm", "locale": "el-GR", "sender_id": "sender_id", "template_id": "template_id", }, signals={ - "app_version": "1.2.34", - "device_id": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2", - "device_model": "iPhone17,2", + "app_version": "app_version", + "device_id": "device_id", + "device_model": "device_model", "device_platform": "android", - "ip": "192.0.2.1", - "is_trusted_user": False, - "os_version": "18.0.1", + "ip": "8.8.8.8", + "is_trusted_user": "is_trusted_user", + "os_version": "os_version", }, ) assert_matches_type(VerificationCreateResponse, verification, path=["response"]) From f921517c1c0b0c8197886f6948cecf56a1fdea87 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 26 Feb 2025 21:19:19 +0000 Subject: [PATCH 12/19] chore(internal): codegen related update (#68) --- .../resources/transactional.py | 14 +++++++ .../resources/verification.py | 8 ++++ .../types/transactional_send_params.py | 8 ++++ .../types/verification_check_response.py | 6 +-- .../types/verification_create_params.py | 42 +++++++++++++++---- .../types/verification_create_response.py | 14 +++---- .../types/watch_feed_back_response.py | 3 +- .../types/watch_predict_response.py | 6 +-- tests/api_resources/test_transactional.py | 38 +++++++++-------- tests/api_resources/test_verification.py | 40 +++++++++++------- 10 files changed, 125 insertions(+), 54 deletions(-) diff --git a/src/prelude_python_sdk/resources/transactional.py b/src/prelude_python_sdk/resources/transactional.py index a6c7015..5fac16f 100644 --- a/src/prelude_python_sdk/resources/transactional.py +++ b/src/prelude_python_sdk/resources/transactional.py @@ -55,6 +55,7 @@ def send( correlation_id: str | NotGiven = NOT_GIVEN, expires_at: str | NotGiven = NOT_GIVEN, from_: str | NotGiven = NOT_GIVEN, + locale: str | NotGiven = NOT_GIVEN, variables: Dict[str, str] | NotGiven = NOT_GIVEN, # 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. @@ -79,6 +80,11 @@ def send( from_: The Sender ID. + locale: A BCP-47 formatted locale string with the language the text message will be sent + to. If there's no locale set, the language will be determined by the country + code of the phone number. If the language specified doesn't exist, the default + set on the template will be used. + variables: The variables to be replaced in the template. extra_headers: Send extra headers @@ -99,6 +105,7 @@ def send( "correlation_id": correlation_id, "expires_at": expires_at, "from_": from_, + "locale": locale, "variables": variables, }, transactional_send_params.TransactionalSendParams, @@ -139,6 +146,7 @@ async def send( correlation_id: str | NotGiven = NOT_GIVEN, expires_at: str | NotGiven = NOT_GIVEN, from_: str | NotGiven = NOT_GIVEN, + locale: str | NotGiven = NOT_GIVEN, variables: Dict[str, str] | NotGiven = NOT_GIVEN, # 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. @@ -163,6 +171,11 @@ async def send( from_: The Sender ID. + locale: A BCP-47 formatted locale string with the language the text message will be sent + to. If there's no locale set, the language will be determined by the country + code of the phone number. If the language specified doesn't exist, the default + set on the template will be used. + variables: The variables to be replaced in the template. extra_headers: Send extra headers @@ -183,6 +196,7 @@ async def send( "correlation_id": correlation_id, "expires_at": expires_at, "from_": from_, + "locale": locale, "variables": variables, }, transactional_send_params.TransactionalSendParams, diff --git a/src/prelude_python_sdk/resources/verification.py b/src/prelude_python_sdk/resources/verification.py index e6201e1..735a7cc 100644 --- a/src/prelude_python_sdk/resources/verification.py +++ b/src/prelude_python_sdk/resources/verification.py @@ -49,6 +49,7 @@ def create( self, *, target: verification_create_params.Target, + dispatch_id: str | NotGiven = NOT_GIVEN, metadata: verification_create_params.Metadata | NotGiven = NOT_GIVEN, options: verification_create_params.Options | NotGiven = NOT_GIVEN, signals: verification_create_params.Signals | NotGiven = NOT_GIVEN, @@ -68,6 +69,8 @@ def create( Args: target: The target. Currently this can only be an E.164 formatted phone number. + dispatch_id: The identifier of the dispatch that came from the front-end SDK. + metadata: The metadata for this verification. This object will be returned with every response or webhook sent that refers to this verification. @@ -88,6 +91,7 @@ def create( body=maybe_transform( { "target": target, + "dispatch_id": dispatch_id, "metadata": metadata, "options": options, "signals": signals, @@ -168,6 +172,7 @@ async def create( self, *, target: verification_create_params.Target, + dispatch_id: str | NotGiven = NOT_GIVEN, metadata: verification_create_params.Metadata | NotGiven = NOT_GIVEN, options: verification_create_params.Options | NotGiven = NOT_GIVEN, signals: verification_create_params.Signals | NotGiven = NOT_GIVEN, @@ -187,6 +192,8 @@ async def create( Args: target: The target. Currently this can only be an E.164 formatted phone number. + dispatch_id: The identifier of the dispatch that came from the front-end SDK. + metadata: The metadata for this verification. This object will be returned with every response or webhook sent that refers to this verification. @@ -207,6 +214,7 @@ async def create( body=await async_maybe_transform( { "target": target, + "dispatch_id": dispatch_id, "metadata": metadata, "options": options, "signals": signals, diff --git a/src/prelude_python_sdk/types/transactional_send_params.py b/src/prelude_python_sdk/types/transactional_send_params.py index 6aba37f..02be0af 100644 --- a/src/prelude_python_sdk/types/transactional_send_params.py +++ b/src/prelude_python_sdk/types/transactional_send_params.py @@ -29,5 +29,13 @@ class TransactionalSendParams(TypedDict, total=False): from_: Annotated[str, PropertyInfo(alias="from")] """The Sender ID.""" + locale: str + """ + A BCP-47 formatted locale string with the language the text message will be sent + to. If there's no locale set, the language will be determined by the country + code of the phone number. If the language specified doesn't exist, the default + set on the template will be used. + """ + variables: Dict[str, str] """The variables to be replaced in the template.""" diff --git a/src/prelude_python_sdk/types/verification_check_response.py b/src/prelude_python_sdk/types/verification_check_response.py index 4bcb3b4..7bca9cf 100644 --- a/src/prelude_python_sdk/types/verification_check_response.py +++ b/src/prelude_python_sdk/types/verification_check_response.py @@ -13,6 +13,9 @@ class Metadata(BaseModel): class VerificationCheckResponse(BaseModel): + status: Literal["success", "failure", "expired_or_not_found"] + """The status of the check.""" + id: Optional[str] = None """The verification identifier.""" @@ -20,6 +23,3 @@ class VerificationCheckResponse(BaseModel): """The metadata for this verification.""" request_id: Optional[str] = None - - status: Optional[Literal["success", "failure", "expired"]] = None - """The status of the check.""" diff --git a/src/prelude_python_sdk/types/verification_create_params.py b/src/prelude_python_sdk/types/verification_create_params.py index 1b59abf..9ea69a7 100644 --- a/src/prelude_python_sdk/types/verification_create_params.py +++ b/src/prelude_python_sdk/types/verification_create_params.py @@ -4,13 +4,16 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["VerificationCreateParams", "Target", "Metadata", "Options", "Signals"] +__all__ = ["VerificationCreateParams", "Target", "Metadata", "Options", "OptionsAppRealm", "Signals"] class VerificationCreateParams(TypedDict, total=False): target: Required[Target] """The target. Currently this can only be an E.164 formatted phone number.""" + dispatch_id: str + """The identifier of the dispatch that came from the front-end SDK.""" + metadata: Metadata """The metadata for this verification. @@ -38,12 +41,37 @@ class Metadata(TypedDict, total=False): """A user-defined identifier to correlate this verification with.""" +class OptionsAppRealm(TypedDict, total=False): + platform: Required[Literal["android"]] + """The platform the SMS will be sent to. + + We are currently only supporting "android". + """ + + value: Required[str] + """The Android SMS Retriever API hash code that identifies your app.""" + + class Options(TypedDict, total=False): - app_realm: str - """The Android SMS Retriever API hash code that identifies your app. + app_realm: OptionsAppRealm + """This allows you to automatically retrieve and fill the OTP code on mobile apps. + + Currently only Android devices are supported. + """ + + code_size: int + """The size of the code generated. + + It should be between 4 and 8. Defaults to the code size specified from the + Dashboard. + """ + + custom_code: str + """The custom code to use for OTP verification. - This allows you to automatically retrieve and fill the OTP code on Android - devices. + This feature is only available for compatibility purposes and subject to + Prelude’s approval. Contact us to discuss your use case. For more details, refer + to [Multi Routing](/concepts/multi-routing). """ locale: str @@ -82,13 +110,13 @@ class Signals(TypedDict, total=False): device_model: str """The model of the user's device.""" - device_platform: Literal["android", "ios", "web"] + device_platform: Literal["android", "ios", "ipados", "tvos", "web"] """The type of the user's device.""" ip: str """The IP address of the user's device.""" - is_trusted_user: str + is_trusted_user: bool """ This signal should provide a higher level of trust, indicating that the user is genuine. For more details, refer to [Signals](/guides/prevent-fraud#signals). diff --git a/src/prelude_python_sdk/types/verification_create_response.py b/src/prelude_python_sdk/types/verification_create_response.py index f1121cb..fdc66ff 100644 --- a/src/prelude_python_sdk/types/verification_create_response.py +++ b/src/prelude_python_sdk/types/verification_create_response.py @@ -13,16 +13,16 @@ class Metadata(BaseModel): class VerificationCreateResponse(BaseModel): - id: Optional[str] = None + id: str """The verification identifier.""" + method: Literal["message"] + """The method used for verifying this phone number.""" + + status: Literal["success", "retry", "blocked"] + """The status of the verification.""" + metadata: Optional[Metadata] = None """The metadata for this verification.""" - method: Optional[Literal["message"]] = None - """The method used for verifying this phone number.""" - request_id: Optional[str] = None - - status: Optional[Literal["success", "retry", "blocked"]] = None - """The status of the verification.""" diff --git a/src/prelude_python_sdk/types/watch_feed_back_response.py b/src/prelude_python_sdk/types/watch_feed_back_response.py index aa10b12..c879860 100644 --- a/src/prelude_python_sdk/types/watch_feed_back_response.py +++ b/src/prelude_python_sdk/types/watch_feed_back_response.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional from .._models import BaseModel @@ -8,5 +7,5 @@ class WatchFeedBackResponse(BaseModel): - id: Optional[str] = None + id: str """A unique identifier for your feedback request.""" diff --git a/src/prelude_python_sdk/types/watch_predict_response.py b/src/prelude_python_sdk/types/watch_predict_response.py index d85e251..cb5a871 100644 --- a/src/prelude_python_sdk/types/watch_predict_response.py +++ b/src/prelude_python_sdk/types/watch_predict_response.py @@ -20,10 +20,10 @@ class Reasoning(BaseModel): class WatchPredictResponse(BaseModel): - id: Optional[str] = None + id: str """A unique identifier for your prediction request.""" - prediction: Optional[Literal["allow", "block"]] = None + prediction: Literal["allow", "block"] """A label indicating the trustworthiness of the phone number.""" - reasoning: Optional[Reasoning] = None + reasoning: Reasoning diff --git a/tests/api_resources/test_transactional.py b/tests/api_resources/test_transactional.py index 7a105f6..b4f77bd 100644 --- a/tests/api_resources/test_transactional.py +++ b/tests/api_resources/test_transactional.py @@ -23,8 +23,8 @@ class TestTransactional: @parametrize def test_method_send(self, client: Prelude) -> None: transactional = client.transactional.send( - template_id="template_id", - to="to", + template_id="template_01jd1xq0cffycayqtdkdbv4d61", + to="+30123456789", ) assert_matches_type(TransactionalSendResponse, transactional, path=["response"]) @@ -34,13 +34,14 @@ def test_method_send(self, client: Prelude) -> None: @parametrize def test_method_send_with_all_params(self, client: Prelude) -> None: transactional = client.transactional.send( - template_id="template_id", - to="to", + template_id="template_01jd1xq0cffycayqtdkdbv4d61", + to="+30123456789", callback_url="callback_url", correlation_id="correlation_id", expires_at="expires_at", from_="from", - variables={"foo": "string"}, + locale="el-GR", + variables={"foo": "bar"}, ) assert_matches_type(TransactionalSendResponse, transactional, path=["response"]) @@ -50,8 +51,8 @@ def test_method_send_with_all_params(self, client: Prelude) -> None: @parametrize def test_raw_response_send(self, client: Prelude) -> None: response = client.transactional.with_raw_response.send( - template_id="template_id", - to="to", + template_id="template_01jd1xq0cffycayqtdkdbv4d61", + to="+30123456789", ) assert response.is_closed is True @@ -65,8 +66,8 @@ def test_raw_response_send(self, client: Prelude) -> None: @parametrize def test_streaming_response_send(self, client: Prelude) -> None: with client.transactional.with_streaming_response.send( - template_id="template_id", - to="to", + template_id="template_01jd1xq0cffycayqtdkdbv4d61", + to="+30123456789", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -86,8 +87,8 @@ class TestAsyncTransactional: @parametrize async def test_method_send(self, async_client: AsyncPrelude) -> None: transactional = await async_client.transactional.send( - template_id="template_id", - to="to", + template_id="template_01jd1xq0cffycayqtdkdbv4d61", + to="+30123456789", ) assert_matches_type(TransactionalSendResponse, transactional, path=["response"]) @@ -97,13 +98,14 @@ async def test_method_send(self, async_client: AsyncPrelude) -> None: @parametrize async def test_method_send_with_all_params(self, async_client: AsyncPrelude) -> None: transactional = await async_client.transactional.send( - template_id="template_id", - to="to", + template_id="template_01jd1xq0cffycayqtdkdbv4d61", + to="+30123456789", callback_url="callback_url", correlation_id="correlation_id", expires_at="expires_at", from_="from", - variables={"foo": "string"}, + locale="el-GR", + variables={"foo": "bar"}, ) assert_matches_type(TransactionalSendResponse, transactional, path=["response"]) @@ -113,8 +115,8 @@ async def test_method_send_with_all_params(self, async_client: AsyncPrelude) -> @parametrize async def test_raw_response_send(self, async_client: AsyncPrelude) -> None: response = await async_client.transactional.with_raw_response.send( - template_id="template_id", - to="to", + template_id="template_01jd1xq0cffycayqtdkdbv4d61", + to="+30123456789", ) assert response.is_closed is True @@ -128,8 +130,8 @@ async def test_raw_response_send(self, async_client: AsyncPrelude) -> None: @parametrize async def test_streaming_response_send(self, async_client: AsyncPrelude) -> None: async with async_client.transactional.with_streaming_response.send( - template_id="template_id", - to="to", + template_id="template_01jd1xq0cffycayqtdkdbv4d61", + to="+30123456789", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/test_verification.py b/tests/api_resources/test_verification.py index df37638..6170e67 100644 --- a/tests/api_resources/test_verification.py +++ b/tests/api_resources/test_verification.py @@ -37,21 +37,27 @@ def test_method_create_with_all_params(self, client: Prelude) -> None: "type": "phone_number", "value": "+30123456789", }, + dispatch_id="dispatch_id", metadata={"correlation_id": "correlation_id"}, options={ - "app_realm": "app_realm", + "app_realm": { + "platform": "android", + "value": "value", + }, + "code_size": 5, + "custom_code": "custom_code", "locale": "el-GR", "sender_id": "sender_id", "template_id": "template_id", }, signals={ - "app_version": "app_version", - "device_id": "device_id", - "device_model": "device_model", + "app_version": "1.2.34", + "device_id": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2", + "device_model": "iPhone17,2", "device_platform": "android", - "ip": "8.8.8.8", - "is_trusted_user": "is_trusted_user", - "os_version": "os_version", + "ip": "192.0.2.1", + "is_trusted_user": False, + "os_version": "18.0.1", }, ) assert_matches_type(VerificationCreateResponse, verification, path=["response"]) @@ -150,21 +156,27 @@ async def test_method_create_with_all_params(self, async_client: AsyncPrelude) - "type": "phone_number", "value": "+30123456789", }, + dispatch_id="dispatch_id", metadata={"correlation_id": "correlation_id"}, options={ - "app_realm": "app_realm", + "app_realm": { + "platform": "android", + "value": "value", + }, + "code_size": 5, + "custom_code": "custom_code", "locale": "el-GR", "sender_id": "sender_id", "template_id": "template_id", }, signals={ - "app_version": "app_version", - "device_id": "device_id", - "device_model": "device_model", + "app_version": "1.2.34", + "device_id": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2", + "device_model": "iPhone17,2", "device_platform": "android", - "ip": "8.8.8.8", - "is_trusted_user": "is_trusted_user", - "os_version": "os_version", + "ip": "192.0.2.1", + "is_trusted_user": False, + "os_version": "18.0.1", }, ) assert_matches_type(VerificationCreateResponse, verification, path=["response"]) From f3c2dc7a219c04490aa22cdab677410136dc09d3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 27 Feb 2025 21:56:09 +0000 Subject: [PATCH 13/19] docs: update URLs from stainlessapi.com to stainless.com (#69) More details at https://www.stainless.com/changelog/stainless-com --- README.md | 2 +- SECURITY.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3fba965..23c1bfc 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The Prelude Python library provides convenient access to the Prelude REST API fr application. The library includes type definitions for all request params and response fields, and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). -It is generated with [Stainless](https://www.stainlessapi.com/). +It is generated with [Stainless](https://www.stainless.com/). ## Documentation diff --git a/SECURITY.md b/SECURITY.md index b80688e..deb37e3 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,9 +2,9 @@ ## Reporting Security Issues -This SDK is generated by [Stainless Software Inc](http://stainlessapi.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. +This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. -To report a security issue, please contact the Stainless team at security@stainlessapi.com. +To report a security issue, please contact the Stainless team at security@stainless.com. ## Responsible Disclosure From 61cec666606b1999db0d6c7bc08e77aa2aed869e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 27 Feb 2025 22:48:17 +0000 Subject: [PATCH 14/19] chore(docs): update client docstring (#70) --- src/prelude_python_sdk/_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prelude_python_sdk/_client.py b/src/prelude_python_sdk/_client.py index e7706c8..e2ad734 100644 --- a/src/prelude_python_sdk/_client.py +++ b/src/prelude_python_sdk/_client.py @@ -241,7 +241,7 @@ def __init__( # part of our public interface in the future. _strict_response_validation: bool = False, ) -> None: - """Construct a new async Prelude client instance. + """Construct a new async AsyncPrelude client instance. This automatically infers the `api_token` argument from the `API_TOKEN` environment variable if it is not provided. """ From ec7fd9feb6f45caf98d9667072910b6a0ebfc25d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 20:35:08 +0000 Subject: [PATCH 15/19] chore(internal): codegen related update (#71) --- src/prelude_python_sdk/_base_client.py | 97 +------------------------- 1 file changed, 1 insertion(+), 96 deletions(-) diff --git a/src/prelude_python_sdk/_base_client.py b/src/prelude_python_sdk/_base_client.py index c6d73a4..b034c0c 100644 --- a/src/prelude_python_sdk/_base_client.py +++ b/src/prelude_python_sdk/_base_client.py @@ -9,7 +9,6 @@ import inspect import logging import platform -import warnings import email.utils from types import TracebackType from random import random @@ -36,7 +35,7 @@ import httpx import distro import pydantic -from httpx import URL, Limits +from httpx import URL from pydantic import PrivateAttr from . import _exceptions @@ -51,13 +50,10 @@ Timeout, NotGiven, ResponseT, - Transport, AnyMapping, PostParser, - ProxiesTypes, RequestFiles, HttpxSendArgs, - AsyncTransport, RequestOptions, HttpxRequestFiles, ModelBuilderProtocol, @@ -337,9 +333,6 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]): _base_url: URL max_retries: int timeout: Union[float, Timeout, None] - _limits: httpx.Limits - _proxies: ProxiesTypes | None - _transport: Transport | AsyncTransport | None _strict_response_validation: bool _idempotency_header: str | None _default_stream_cls: type[_DefaultStreamT] | None = None @@ -352,9 +345,6 @@ def __init__( _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None = DEFAULT_TIMEOUT, - limits: httpx.Limits, - transport: Transport | AsyncTransport | None, - proxies: ProxiesTypes | None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, ) -> None: @@ -362,9 +352,6 @@ def __init__( self._base_url = self._enforce_trailing_slash(URL(base_url)) self.max_retries = max_retries self.timeout = timeout - self._limits = limits - self._proxies = proxies - self._transport = transport self._custom_headers = custom_headers or {} self._custom_query = custom_query or {} self._strict_response_validation = _strict_response_validation @@ -800,46 +787,11 @@ def __init__( base_url: str | URL, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - transport: Transport | None = None, - proxies: ProxiesTypes | None = None, - limits: Limits | None = None, http_client: httpx.Client | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, _strict_response_validation: bool, ) -> None: - kwargs: dict[str, Any] = {} - if limits is not None: - warnings.warn( - "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") - else: - limits = DEFAULT_CONNECTION_LIMITS - - if transport is not None: - kwargs["transport"] = transport - warnings.warn( - "The `transport` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `transport`") - - if proxies is not None: - kwargs["proxies"] = proxies - warnings.warn( - "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `proxies`") - if not is_given(timeout): # if the user passed in a custom http client with a non-default # timeout set then we use that timeout. @@ -860,12 +812,9 @@ def __init__( super().__init__( version=version, - limits=limits, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - proxies=proxies, base_url=base_url, - transport=transport, max_retries=max_retries, custom_query=custom_query, custom_headers=custom_headers, @@ -875,9 +824,6 @@ def __init__( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - limits=limits, - follow_redirects=True, - **kwargs, # type: ignore ) def is_closed(self) -> bool: @@ -1372,45 +1318,10 @@ def __init__( _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - transport: AsyncTransport | None = None, - proxies: ProxiesTypes | None = None, - limits: Limits | None = None, http_client: httpx.AsyncClient | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, ) -> None: - kwargs: dict[str, Any] = {} - if limits is not None: - warnings.warn( - "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") - else: - limits = DEFAULT_CONNECTION_LIMITS - - if transport is not None: - kwargs["transport"] = transport - warnings.warn( - "The `transport` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `transport`") - - if proxies is not None: - kwargs["proxies"] = proxies - warnings.warn( - "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `proxies`") - if not is_given(timeout): # if the user passed in a custom http client with a non-default # timeout set then we use that timeout. @@ -1432,11 +1343,8 @@ def __init__( super().__init__( version=version, base_url=base_url, - limits=limits, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - proxies=proxies, - transport=transport, max_retries=max_retries, custom_query=custom_query, custom_headers=custom_headers, @@ -1446,9 +1354,6 @@ def __init__( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - limits=limits, - follow_redirects=True, - **kwargs, # type: ignore ) def is_closed(self) -> bool: From bff24a785fbd56e126249cbfbc8f2af5c179b8a6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 9 Mar 2025 21:38:41 +0000 Subject: [PATCH 16/19] docs: revise readme docs about nested params (#72) --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 23c1bfc..36d42ad 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,24 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. +## Nested params + +Nested parameters are dictionaries, typed using `TypedDict`, for example: + +```python +from prelude_python_sdk import Prelude + +client = Prelude() + +verification = client.verification.create( + target={ + "type": "phone_number", + "value": "+30123456789", + }, +) +print(verification.target) +``` + ## Handling errors When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `prelude_python_sdk.APIConnectionError` is raised. From 77f4c81e78e14fb716506ef972ecf51014e43875 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 01:06:56 +0000 Subject: [PATCH 17/19] test: add DEFER_PYDANTIC_BUILD=false flag to tests (#73) --- scripts/test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/test b/scripts/test index 4fa5698..2b87845 100755 --- a/scripts/test +++ b/scripts/test @@ -52,6 +52,8 @@ else echo fi +export DEFER_PYDANTIC_BUILD=false + echo "==> Running tests" rye run pytest "$@" From f9658f1ebacf25f72ae9a8e9076958055c2de570 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 13:40:24 +0000 Subject: [PATCH 18/19] feat(api): update via SDK Studio (#74) --- .../resources/verification.py | 18 +++++--- src/prelude_python_sdk/resources/watch.py | 12 ++++-- .../types/verification_check_params.py | 12 ++++-- .../types/verification_create_params.py | 42 +++++++++++++++---- .../types/watch_feed_back_params.py | 12 ++++-- .../types/watch_predict_params.py | 12 ++++-- tests/api_resources/test_verification.py | 10 ++++- 7 files changed, 86 insertions(+), 32 deletions(-) diff --git a/src/prelude_python_sdk/resources/verification.py b/src/prelude_python_sdk/resources/verification.py index 735a7cc..7367e88 100644 --- a/src/prelude_python_sdk/resources/verification.py +++ b/src/prelude_python_sdk/resources/verification.py @@ -67,7 +67,8 @@ def create( this endpoint will perform a retry instead. Args: - target: The target. Currently this can only be an E.164 formatted phone number. + target: The verification target. Either a phone number or an email address. To use the + email verification feature contact us to discuss your use case. dispatch_id: The identifier of the dispatch that came from the front-end SDK. @@ -76,7 +77,8 @@ def create( options: Verification options - signals: The signals used for anti-fraud. + signals: The signals used for anti-fraud. For more details, refer to + [Signals](/guides/prevent-fraud#signals). extra_headers: Send extra headers @@ -122,7 +124,8 @@ def check( Args: code: The OTP code to validate. - target: The target. Currently this can only be an E.164 formatted phone number. + target: The verification target. Either a phone number or an email address. To use the + email verification feature contact us to discuss your use case. extra_headers: Send extra headers @@ -190,7 +193,8 @@ async def create( this endpoint will perform a retry instead. Args: - target: The target. Currently this can only be an E.164 formatted phone number. + target: The verification target. Either a phone number or an email address. To use the + email verification feature contact us to discuss your use case. dispatch_id: The identifier of the dispatch that came from the front-end SDK. @@ -199,7 +203,8 @@ async def create( options: Verification options - signals: The signals used for anti-fraud. + signals: The signals used for anti-fraud. For more details, refer to + [Signals](/guides/prevent-fraud#signals). extra_headers: Send extra headers @@ -245,7 +250,8 @@ async def check( Args: code: The OTP code to validate. - target: The target. Currently this can only be an E.164 formatted phone number. + target: The verification target. Either a phone number or an email address. To use the + email verification feature contact us to discuss your use case. extra_headers: Send extra headers diff --git a/src/prelude_python_sdk/resources/watch.py b/src/prelude_python_sdk/resources/watch.py index 0233667..0907e8b 100644 --- a/src/prelude_python_sdk/resources/watch.py +++ b/src/prelude_python_sdk/resources/watch.py @@ -65,7 +65,8 @@ def feed_back( feedback: You should send a feedback event back to Watch API when your user demonstrates authentic behavior. - target: The target. Currently this can only be an E.164 formatted phone number. + target: The verification target. Either a phone number or an email address. To use the + email verification feature contact us to discuss your use case. extra_headers: Send extra headers @@ -108,7 +109,8 @@ def predict( must be implemented in conjunction with the `watch/feedback` endpoint. Args: - target: The target. Currently this can only be an E.164 formatted phone number. + target: The verification target. Either a phone number or an email address. To use the + email verification feature contact us to discuss your use case. signals: It is highly recommended that you provide the signals to increase prediction performance. @@ -177,7 +179,8 @@ async def feed_back( feedback: You should send a feedback event back to Watch API when your user demonstrates authentic behavior. - target: The target. Currently this can only be an E.164 formatted phone number. + target: The verification target. Either a phone number or an email address. To use the + email verification feature contact us to discuss your use case. extra_headers: Send extra headers @@ -220,7 +223,8 @@ async def predict( must be implemented in conjunction with the `watch/feedback` endpoint. Args: - target: The target. Currently this can only be an E.164 formatted phone number. + target: The verification target. Either a phone number or an email address. To use the + email verification feature contact us to discuss your use case. signals: It is highly recommended that you provide the signals to increase prediction performance. diff --git a/src/prelude_python_sdk/types/verification_check_params.py b/src/prelude_python_sdk/types/verification_check_params.py index ad7b8eb..dea9702 100644 --- a/src/prelude_python_sdk/types/verification_check_params.py +++ b/src/prelude_python_sdk/types/verification_check_params.py @@ -12,12 +12,16 @@ class VerificationCheckParams(TypedDict, total=False): """The OTP code to validate.""" target: Required[Target] - """The target. Currently this can only be an E.164 formatted phone number.""" + """The verification target. + + Either a phone number or an email address. To use the email verification feature + contact us to discuss your use case. + """ class Target(TypedDict, total=False): - type: Required[Literal["phone_number"]] - """The type of the target. Currently this can only be "phone_number".""" + type: Required[Literal["phone_number", "email_address"]] + """The type of the target. Either "phone_number" or "email_address".""" value: Required[str] - """An E.164 formatted phone number to verify.""" + """An E.164 formatted phone number or an email address.""" diff --git a/src/prelude_python_sdk/types/verification_create_params.py b/src/prelude_python_sdk/types/verification_create_params.py index 9ea69a7..15a83b6 100644 --- a/src/prelude_python_sdk/types/verification_create_params.py +++ b/src/prelude_python_sdk/types/verification_create_params.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Dict from typing_extensions import Literal, Required, TypedDict __all__ = ["VerificationCreateParams", "Target", "Metadata", "Options", "OptionsAppRealm", "Signals"] @@ -9,7 +10,11 @@ class VerificationCreateParams(TypedDict, total=False): target: Required[Target] - """The target. Currently this can only be an E.164 formatted phone number.""" + """The verification target. + + Either a phone number or an email address. To use the email verification feature + contact us to discuss your use case. + """ dispatch_id: str """The identifier of the dispatch that came from the front-end SDK.""" @@ -25,15 +30,18 @@ class VerificationCreateParams(TypedDict, total=False): """Verification options""" signals: Signals - """The signals used for anti-fraud.""" + """The signals used for anti-fraud. + + For more details, refer to [Signals](/guides/prevent-fraud#signals). + """ class Target(TypedDict, total=False): - type: Required[Literal["phone_number"]] - """The type of the target. Currently this can only be "phone_number".""" + type: Required[Literal["phone_number", "email_address"]] + """The type of the target. Either "phone_number" or "email_address".""" value: Required[str] - """An E.164 formatted phone number to verify.""" + """An E.164 formatted phone number or an email address.""" class Metadata(TypedDict, total=False): @@ -59,6 +67,13 @@ class Options(TypedDict, total=False): Currently only Android devices are supported. """ + callback_url: str + """ + The URL where webhooks will be sent when verification events occur, including + verification creation, attempt creation, and delivery status changes. For more + details, refer to [Webhook](/api-reference/v2/verify/webhook). + """ + code_size: int """The size of the code generated. @@ -89,12 +104,15 @@ class Options(TypedDict, total=False): """ template_id: str - """The identifier of a verification settings template. + """The identifier of a verification template. - It is used to be able to switch behavior for specific use cases. Contact us if - you need to use this functionality. + It applies use case-specific settings, such as the message content or certain + verification parameters. """ + variables: Dict[str, str] + """The variables to be replaced in the template.""" + class Signals(TypedDict, total=False): app_version: str @@ -124,3 +142,11 @@ class Signals(TypedDict, total=False): os_version: str """The version of the user's device operating system.""" + + user_agent: str + """The user agent of the user's device. + + If the individual fields (os_version, device_platform, device_model) are + provided, we will prioritize those values instead of parsing them from the user + agent string. + """ diff --git a/src/prelude_python_sdk/types/watch_feed_back_params.py b/src/prelude_python_sdk/types/watch_feed_back_params.py index a7853fa..c4cc330 100644 --- a/src/prelude_python_sdk/types/watch_feed_back_params.py +++ b/src/prelude_python_sdk/types/watch_feed_back_params.py @@ -15,7 +15,11 @@ class WatchFeedBackParams(TypedDict, total=False): """ target: Required[Target] - """The target. Currently this can only be an E.164 formatted phone number.""" + """The verification target. + + Either a phone number or an email address. To use the email verification feature + contact us to discuss your use case. + """ class Feedback(TypedDict, total=False): @@ -27,8 +31,8 @@ class Feedback(TypedDict, total=False): class Target(TypedDict, total=False): - type: Required[Literal["phone_number"]] - """The type of the target. Currently this can only be "phone_number".""" + type: Required[Literal["phone_number", "email_address"]] + """The type of the target. Either "phone_number" or "email_address".""" value: Required[str] - """An E.164 formatted phone number to verify.""" + """An E.164 formatted phone number or an email address.""" diff --git a/src/prelude_python_sdk/types/watch_predict_params.py b/src/prelude_python_sdk/types/watch_predict_params.py index 7d8a645..6ef2093 100644 --- a/src/prelude_python_sdk/types/watch_predict_params.py +++ b/src/prelude_python_sdk/types/watch_predict_params.py @@ -9,7 +9,11 @@ class WatchPredictParams(TypedDict, total=False): target: Required[Target] - """The target. Currently this can only be an E.164 formatted phone number.""" + """The verification target. + + Either a phone number or an email address. To use the email verification feature + contact us to discuss your use case. + """ signals: Signals """ @@ -19,11 +23,11 @@ class WatchPredictParams(TypedDict, total=False): class Target(TypedDict, total=False): - type: Required[Literal["phone_number"]] - """The type of the target. Currently this can only be "phone_number".""" + type: Required[Literal["phone_number", "email_address"]] + """The type of the target. Either "phone_number" or "email_address".""" value: Required[str] - """An E.164 formatted phone number to verify.""" + """An E.164 formatted phone number or an email address.""" class Signals(TypedDict, total=False): diff --git a/tests/api_resources/test_verification.py b/tests/api_resources/test_verification.py index 6170e67..d88eca4 100644 --- a/tests/api_resources/test_verification.py +++ b/tests/api_resources/test_verification.py @@ -44,11 +44,13 @@ def test_method_create_with_all_params(self, client: Prelude) -> None: "platform": "android", "value": "value", }, + "callback_url": "callback_url", "code_size": 5, "custom_code": "custom_code", "locale": "el-GR", "sender_id": "sender_id", - "template_id": "template_id", + "template_id": "prelude:psd2", + "variables": {"foo": "bar"}, }, signals={ "app_version": "1.2.34", @@ -58,6 +60,7 @@ def test_method_create_with_all_params(self, client: Prelude) -> None: "ip": "192.0.2.1", "is_trusted_user": False, "os_version": "18.0.1", + "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1", }, ) assert_matches_type(VerificationCreateResponse, verification, path=["response"]) @@ -163,11 +166,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncPrelude) - "platform": "android", "value": "value", }, + "callback_url": "callback_url", "code_size": 5, "custom_code": "custom_code", "locale": "el-GR", "sender_id": "sender_id", - "template_id": "template_id", + "template_id": "prelude:psd2", + "variables": {"foo": "bar"}, }, signals={ "app_version": "1.2.34", @@ -177,6 +182,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncPrelude) - "ip": "192.0.2.1", "is_trusted_user": False, "os_version": "18.0.1", + "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1", }, ) assert_matches_type(VerificationCreateResponse, verification, path=["response"]) From eed7324a4784d0529573012bcbf4f55f2a052b8d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 13:40:44 +0000 Subject: [PATCH 19/19] release: 0.2.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 37 ++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/prelude_python_sdk/_version.py | 2 +- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3d2ac0b..10f3091 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0" + ".": "0.2.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 38e11fe..1961b2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,42 @@ # Changelog +## 0.2.0 (2025-03-11) + +Full Changelog: [v0.1.0...v0.2.0](https://github.com/prelude-so/python-sdk/compare/v0.1.0...v0.2.0) + +### Features + +* **api:** update via SDK Studio ([#74](https://github.com/prelude-so/python-sdk/issues/74)) ([f9658f1](https://github.com/prelude-so/python-sdk/commit/f9658f1ebacf25f72ae9a8e9076958055c2de570)) +* **client:** allow passing `NotGiven` for body ([#64](https://github.com/prelude-so/python-sdk/issues/64)) ([b32f989](https://github.com/prelude-so/python-sdk/commit/b32f98934973c8c2cfacd3ad9a6c0817405ec3c9)) +* **client:** send `X-Stainless-Read-Timeout` header ([#59](https://github.com/prelude-so/python-sdk/issues/59)) ([6dcc82a](https://github.com/prelude-so/python-sdk/commit/6dcc82a592bdad9316eae8ab7b93095d2176caf3)) + + +### Bug Fixes + +* **client:** mark some request bodies as optional ([b32f989](https://github.com/prelude-so/python-sdk/commit/b32f98934973c8c2cfacd3ad9a6c0817405ec3c9)) + + +### Chores + +* **docs:** update client docstring ([#70](https://github.com/prelude-so/python-sdk/issues/70)) ([61cec66](https://github.com/prelude-so/python-sdk/commit/61cec666606b1999db0d6c7bc08e77aa2aed869e)) +* **internal:** bummp ruff dependency ([#58](https://github.com/prelude-so/python-sdk/issues/58)) ([2381d4a](https://github.com/prelude-so/python-sdk/commit/2381d4a22cfd032f97470e0d012b6fc8a133305c)) +* **internal:** change default timeout to an int ([#56](https://github.com/prelude-so/python-sdk/issues/56)) ([160f11e](https://github.com/prelude-so/python-sdk/commit/160f11e767ab3d5f7f1fdd8e423a6277a651fc82)) +* **internal:** codegen related update ([#63](https://github.com/prelude-so/python-sdk/issues/63)) ([0516484](https://github.com/prelude-so/python-sdk/commit/05164849027af87dba0911340086b7904a7171f2)) +* **internal:** codegen related update ([#67](https://github.com/prelude-so/python-sdk/issues/67)) ([32798a9](https://github.com/prelude-so/python-sdk/commit/32798a95e57769f4fc29abac8ba2dcd58d55a6ef)) +* **internal:** codegen related update ([#68](https://github.com/prelude-so/python-sdk/issues/68)) ([f921517](https://github.com/prelude-so/python-sdk/commit/f921517c1c0b0c8197886f6948cecf56a1fdea87)) +* **internal:** codegen related update ([#71](https://github.com/prelude-so/python-sdk/issues/71)) ([ec7fd9f](https://github.com/prelude-so/python-sdk/commit/ec7fd9feb6f45caf98d9667072910b6a0ebfc25d)) +* **internal:** fix devcontainers setup ([#65](https://github.com/prelude-so/python-sdk/issues/65)) ([da3f6c6](https://github.com/prelude-so/python-sdk/commit/da3f6c6f48241dfe0909aabeb3eec2ba83c0e8ef)) +* **internal:** fix type traversing dictionary params ([#60](https://github.com/prelude-so/python-sdk/issues/60)) ([9bf6b95](https://github.com/prelude-so/python-sdk/commit/9bf6b958c8b1ac01d191fb3ffdad7beb9ad0f06a)) +* **internal:** minor type handling changes ([#61](https://github.com/prelude-so/python-sdk/issues/61)) ([0639a28](https://github.com/prelude-so/python-sdk/commit/0639a28c925209b6d2adb2d3022f350044bf5995)) +* **internal:** properly set __pydantic_private__ ([#66](https://github.com/prelude-so/python-sdk/issues/66)) ([affe056](https://github.com/prelude-so/python-sdk/commit/affe056afdc01fc46d7dc23a003b69bb8528c16d)) +* **internal:** update client tests ([#62](https://github.com/prelude-so/python-sdk/issues/62)) ([6096c2a](https://github.com/prelude-so/python-sdk/commit/6096c2aff213dca771b4e8f8675569e1bc1d1edf)) + + +### Documentation + +* revise readme docs about nested params ([#72](https://github.com/prelude-so/python-sdk/issues/72)) ([bff24a7](https://github.com/prelude-so/python-sdk/commit/bff24a785fbd56e126249cbfbc8f2af5c179b8a6)) +* update URLs from stainlessapi.com to stainless.com ([#69](https://github.com/prelude-so/python-sdk/issues/69)) ([f3c2dc7](https://github.com/prelude-so/python-sdk/commit/f3c2dc7a219c04490aa22cdab677410136dc09d3)) + ## 0.1.0 (2025-02-05) Full Changelog: [v0.1.0-beta.1...v0.1.0](https://github.com/prelude-so/python-sdk/compare/v0.1.0-beta.1...v0.1.0) diff --git a/pyproject.toml b/pyproject.toml index 8dd822b..619f4b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "prelude-python-sdk" -version = "0.1.0" +version = "0.2.0" description = "The official Python library for the Prelude API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/prelude_python_sdk/_version.py b/src/prelude_python_sdk/_version.py index 6e6c7ca..9122928 100644 --- a/src/prelude_python_sdk/_version.py +++ b/src/prelude_python_sdk/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "prelude_python_sdk" -__version__ = "0.1.0" # x-release-please-version +__version__ = "0.2.0" # x-release-please-version