diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 623b397aa..5ac298d4d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.187.0" + ".": "0.188.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index d415aa66b..4803a266d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 201 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-ce741f23d99b551b38099da78d730af0cf9019e3b51e61bc988a5e5979c26143.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-f4db2590b8381f1ce20ef003e571392a1b33023e29febd4a3caca983705db688.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 37c38845a..932b67ec6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 0.188.0 (2025-02-07) + +Full Changelog: [v0.187.0...v0.188.0](https://github.com/Increase/increase-python/compare/v0.187.0...v0.188.0) + +### Features + +* **api:** api update ([#956](https://github.com/Increase/increase-python/issues/956)) ([256d107](https://github.com/Increase/increase-python/commit/256d1075464dace95b926dc88f93864ffab19efe)) +* **api:** api update ([#957](https://github.com/Increase/increase-python/issues/957)) ([a8dfd3d](https://github.com/Increase/increase-python/commit/a8dfd3d326b715980f58ff983dd322082262bce5)) +* **client:** send `X-Stainless-Read-Timeout` header ([#952](https://github.com/Increase/increase-python/issues/952)) ([56c1cba](https://github.com/Increase/increase-python/commit/56c1cbac74b219e2a82d4c4088f6caae31fee502)) + + +### Chores + +* **internal:** fix type traversing dictionary params ([#954](https://github.com/Increase/increase-python/issues/954)) ([e0cc599](https://github.com/Increase/increase-python/commit/e0cc599129b55a5826bb3b61baab5c9973aee63f)) +* **internal:** minor type handling changes ([#955](https://github.com/Increase/increase-python/issues/955)) ([11f8788](https://github.com/Increase/increase-python/commit/11f87883da72091548389e386fa26203f6928199)) + ## 0.187.0 (2025-02-04) Full Changelog: [v0.186.0...v0.187.0](https://github.com/Increase/increase-python/compare/v0.186.0...v0.187.0) diff --git a/api.md b/api.md index 846461cfc..40206ddf6 100644 --- a/api.md +++ b/api.md @@ -717,7 +717,7 @@ from increase.types import IntrafiBalance Methods: -- client.intrafi_balances.retrieve(account_id) -> IntrafiBalance +- client.intrafi_balances.intrafi_balance(account_id) -> IntrafiBalance # IntrafiExclusions diff --git a/pyproject.toml b/pyproject.toml index 4dcdf5865..d5cdc4520 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "increase" -version = "0.187.0" +version = "0.188.0" description = "The official Python library for the increase API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/increase/_base_client.py b/src/increase/_base_client.py index 40466184d..e8ba26a43 100644 --- a/src/increase/_base_client.py +++ b/src/increase/_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 diff --git a/src/increase/_models.py b/src/increase/_models.py index 12c34b7d1..c4401ff86 100644 --- a/src/increase/_models.py +++ b/src/increase/_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 diff --git a/src/increase/_utils/_transform.py b/src/increase/_utils/_transform.py index a6b62cad0..18afd9d8b 100644 --- a/src/increase/_utils/_transform.py +++ b/src/increase/_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/src/increase/_version.py b/src/increase/_version.py index 9e36aab94..00b84fe50 100644 --- a/src/increase/_version.py +++ b/src/increase/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "increase" -__version__ = "0.187.0" # x-release-please-version +__version__ = "0.188.0" # x-release-please-version diff --git a/src/increase/resources/intrafi_balances.py b/src/increase/resources/intrafi_balances.py index cc9c517d5..a5b56f2b0 100644 --- a/src/increase/resources/intrafi_balances.py +++ b/src/increase/resources/intrafi_balances.py @@ -39,7 +39,7 @@ def with_streaming_response(self) -> IntrafiBalancesResourceWithStreamingRespons """ return IntrafiBalancesResourceWithStreamingResponse(self) - def retrieve( + def intrafi_balance( self, account_id: str, *, @@ -67,7 +67,7 @@ def retrieve( if not account_id: raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") return self._get( - f"/intrafi_balances/{account_id}", + f"/accounts/{account_id}/intrafi_balance", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -95,7 +95,7 @@ def with_streaming_response(self) -> AsyncIntrafiBalancesResourceWithStreamingRe """ return AsyncIntrafiBalancesResourceWithStreamingResponse(self) - async def retrieve( + async def intrafi_balance( self, account_id: str, *, @@ -123,7 +123,7 @@ async def retrieve( if not account_id: raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") return await self._get( - f"/intrafi_balances/{account_id}", + f"/accounts/{account_id}/intrafi_balance", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -135,8 +135,8 @@ class IntrafiBalancesResourceWithRawResponse: def __init__(self, intrafi_balances: IntrafiBalancesResource) -> None: self._intrafi_balances = intrafi_balances - self.retrieve = to_raw_response_wrapper( - intrafi_balances.retrieve, + self.intrafi_balance = to_raw_response_wrapper( + intrafi_balances.intrafi_balance, ) @@ -144,8 +144,8 @@ class AsyncIntrafiBalancesResourceWithRawResponse: def __init__(self, intrafi_balances: AsyncIntrafiBalancesResource) -> None: self._intrafi_balances = intrafi_balances - self.retrieve = async_to_raw_response_wrapper( - intrafi_balances.retrieve, + self.intrafi_balance = async_to_raw_response_wrapper( + intrafi_balances.intrafi_balance, ) @@ -153,8 +153,8 @@ class IntrafiBalancesResourceWithStreamingResponse: def __init__(self, intrafi_balances: IntrafiBalancesResource) -> None: self._intrafi_balances = intrafi_balances - self.retrieve = to_streamed_response_wrapper( - intrafi_balances.retrieve, + self.intrafi_balance = to_streamed_response_wrapper( + intrafi_balances.intrafi_balance, ) @@ -162,6 +162,6 @@ class AsyncIntrafiBalancesResourceWithStreamingResponse: def __init__(self, intrafi_balances: AsyncIntrafiBalancesResource) -> None: self._intrafi_balances = intrafi_balances - self.retrieve = async_to_streamed_response_wrapper( - intrafi_balances.retrieve, + self.intrafi_balance = async_to_streamed_response_wrapper( + intrafi_balances.intrafi_balance, ) diff --git a/src/increase/types/inbound_wire_transfer.py b/src/increase/types/inbound_wire_transfer.py index c2d381d36..341f29679 100644 --- a/src/increase/types/inbound_wire_transfer.py +++ b/src/increase/types/inbound_wire_transfer.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from datetime import datetime from typing_extensions import Literal from .._models import BaseModel @@ -36,6 +37,12 @@ class InboundWireTransfer(BaseModel): beneficiary_reference: Optional[str] = None """A free-form reference string set by the sender, to help identify the transfer.""" + created_at: datetime + """ + The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time at which + the inbound wire transfer was created. + """ + description: str """An Increase-constructed description of the transfer.""" diff --git a/src/increase/types/intrafi_account_enrollment.py b/src/increase/types/intrafi_account_enrollment.py index 4f7eb73d5..b04e39136 100644 --- a/src/increase/types/intrafi_account_enrollment.py +++ b/src/increase/types/intrafi_account_enrollment.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from datetime import datetime from typing_extensions import Literal from .._models import BaseModel @@ -15,6 +16,12 @@ class IntrafiAccountEnrollment(BaseModel): account_id: str """The identifier of the Increase Account being swept into the network.""" + created_at: datetime + """ + The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time at which + the enrollment was created. + """ + idempotency_key: Optional[str] = None """The idempotency key you chose for this object. diff --git a/src/increase/types/intrafi_exclusion.py b/src/increase/types/intrafi_exclusion.py index 6b1c35abd..c0a215fa7 100644 --- a/src/increase/types/intrafi_exclusion.py +++ b/src/increase/types/intrafi_exclusion.py @@ -16,6 +16,12 @@ class IntrafiExclusion(BaseModel): bank_name: str """The name of the excluded institution.""" + created_at: datetime + """ + The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time at which + the exclusion was created. + """ + entity_id: str """The entity for which this institution is excluded.""" diff --git a/src/increase/types/wire_drawdown_request.py b/src/increase/types/wire_drawdown_request.py index ac56aaa7f..b9c877167 100644 --- a/src/increase/types/wire_drawdown_request.py +++ b/src/increase/types/wire_drawdown_request.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from datetime import datetime from typing_extensions import Literal from .._models import BaseModel @@ -29,6 +30,12 @@ class WireDrawdownRequest(BaseModel): amount: int """The amount being requested in cents.""" + created_at: datetime + """ + The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time at which + the wire drawdown request was created. + """ + currency: str """ The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) code for the amount being diff --git a/tests/api_resources/test_intrafi_balances.py b/tests/api_resources/test_intrafi_balances.py index 4e3f99c2b..6dd282615 100644 --- a/tests/api_resources/test_intrafi_balances.py +++ b/tests/api_resources/test_intrafi_balances.py @@ -18,15 +18,15 @@ class TestIntrafiBalances: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - def test_method_retrieve(self, client: Increase) -> None: - intrafi_balance = client.intrafi_balances.retrieve( + def test_method_intrafi_balance(self, client: Increase) -> None: + intrafi_balance = client.intrafi_balances.intrafi_balance( "account_id", ) assert_matches_type(IntrafiBalance, intrafi_balance, path=["response"]) @parametrize - def test_raw_response_retrieve(self, client: Increase) -> None: - response = client.intrafi_balances.with_raw_response.retrieve( + def test_raw_response_intrafi_balance(self, client: Increase) -> None: + response = client.intrafi_balances.with_raw_response.intrafi_balance( "account_id", ) @@ -36,8 +36,8 @@ def test_raw_response_retrieve(self, client: Increase) -> None: assert_matches_type(IntrafiBalance, intrafi_balance, path=["response"]) @parametrize - def test_streaming_response_retrieve(self, client: Increase) -> None: - with client.intrafi_balances.with_streaming_response.retrieve( + def test_streaming_response_intrafi_balance(self, client: Increase) -> None: + with client.intrafi_balances.with_streaming_response.intrafi_balance( "account_id", ) as response: assert not response.is_closed @@ -49,9 +49,9 @@ def test_streaming_response_retrieve(self, client: Increase) -> None: assert cast(Any, response.is_closed) is True @parametrize - def test_path_params_retrieve(self, client: Increase) -> None: + def test_path_params_intrafi_balance(self, client: Increase) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): - client.intrafi_balances.with_raw_response.retrieve( + client.intrafi_balances.with_raw_response.intrafi_balance( "", ) @@ -60,15 +60,15 @@ class TestAsyncIntrafiBalances: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_retrieve(self, async_client: AsyncIncrease) -> None: - intrafi_balance = await async_client.intrafi_balances.retrieve( + async def test_method_intrafi_balance(self, async_client: AsyncIncrease) -> None: + intrafi_balance = await async_client.intrafi_balances.intrafi_balance( "account_id", ) assert_matches_type(IntrafiBalance, intrafi_balance, path=["response"]) @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncIncrease) -> None: - response = await async_client.intrafi_balances.with_raw_response.retrieve( + async def test_raw_response_intrafi_balance(self, async_client: AsyncIncrease) -> None: + response = await async_client.intrafi_balances.with_raw_response.intrafi_balance( "account_id", ) @@ -78,8 +78,8 @@ async def test_raw_response_retrieve(self, async_client: AsyncIncrease) -> None: assert_matches_type(IntrafiBalance, intrafi_balance, path=["response"]) @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncIncrease) -> None: - async with async_client.intrafi_balances.with_streaming_response.retrieve( + async def test_streaming_response_intrafi_balance(self, async_client: AsyncIncrease) -> None: + async with async_client.intrafi_balances.with_streaming_response.intrafi_balance( "account_id", ) as response: assert not response.is_closed @@ -91,8 +91,8 @@ async def test_streaming_response_retrieve(self, async_client: AsyncIncrease) -> assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve(self, async_client: AsyncIncrease) -> None: + async def test_path_params_intrafi_balance(self, async_client: AsyncIncrease) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): - await async_client.intrafi_balances.with_raw_response.retrieve( + await async_client.intrafi_balances.with_raw_response.intrafi_balance( "", ) diff --git a/tests/api_resources/test_oauth_connections.py b/tests/api_resources/test_oauth_connections.py index d8e08b2d9..8c218df2f 100644 --- a/tests/api_resources/test_oauth_connections.py +++ b/tests/api_resources/test_oauth_connections.py @@ -21,14 +21,14 @@ class TestOAuthConnections: @parametrize def test_method_retrieve(self, client: Increase) -> None: oauth_connection = client.oauth_connections.retrieve( - "oauth_connection_id", + "x", ) assert_matches_type(OAuthConnection, oauth_connection, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Increase) -> None: response = client.oauth_connections.with_raw_response.retrieve( - "oauth_connection_id", + "x", ) assert response.is_closed is True @@ -39,7 +39,7 @@ def test_raw_response_retrieve(self, client: Increase) -> None: @parametrize def test_streaming_response_retrieve(self, client: Increase) -> None: with client.oauth_connections.with_streaming_response.retrieve( - "oauth_connection_id", + "x", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -98,14 +98,14 @@ class TestAsyncOAuthConnections: @parametrize async def test_method_retrieve(self, async_client: AsyncIncrease) -> None: oauth_connection = await async_client.oauth_connections.retrieve( - "oauth_connection_id", + "x", ) assert_matches_type(OAuthConnection, oauth_connection, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncIncrease) -> None: response = await async_client.oauth_connections.with_raw_response.retrieve( - "oauth_connection_id", + "x", ) assert response.is_closed is True @@ -116,7 +116,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncIncrease) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncIncrease) -> None: async with async_client.oauth_connections.with_streaming_response.retrieve( - "oauth_connection_id", + "x", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/test_transform.py b/tests/test_transform.py index 1f0743ce0..80c914329 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")]