From 8fc74d66d9b93ab77093788b9f29c56fcfff1478 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 14:36:07 +0000 Subject: [PATCH 1/2] feat(api): enable inventories endpoint (#54) --- .stats.yml | 2 +- api.md | 12 ++ pyproject.toml | 8 +- requirements-dev.lock | 2 +- src/studio_sdk/_base_client.py | 11 +- src/studio_sdk/resources/accounts/__init__.py | 14 ++ src/studio_sdk/resources/accounts/accounts.py | 32 ++++ .../resources/accounts/inventories.py | 173 ++++++++++++++++++ src/studio_sdk/types/account.py | 1 - src/studio_sdk/types/accounts/__init__.py | 1 + .../accounts/inventory_retrieve_response.py | 23 +++ .../types/accounts/order_create_response.py | 1 - .../types/accounts/order_retrieve_response.py | 1 - .../regt_margin_simulation_create_response.py | 1 - src/studio_sdk/types/pnl_summary.py | 1 - .../types/shared/regt_margin_simulation.py | 1 - .../accounts/test_inventories.py | 118 ++++++++++++ tests/test_client.py | 23 ++- tests/test_models.py | 2 +- 19 files changed, 408 insertions(+), 19 deletions(-) create mode 100644 src/studio_sdk/resources/accounts/inventories.py create mode 100644 src/studio_sdk/types/accounts/inventory_retrieve_response.py create mode 100644 tests/api_resources/accounts/test_inventories.py diff --git a/.stats.yml b/.stats.yml index 7c7707f..dc937dc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ -configured_endpoints: 29 +configured_endpoints: 30 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/clear-street%2Fstudio-sdk-468b5da24bbf73b3a3861d44c5a8051fe6c55a6ec64c5c6f2d45f22c76bf35b2.yml diff --git a/api.md b/api.md index 4da6f26..c99849f 100644 --- a/api.md +++ b/api.md @@ -197,6 +197,18 @@ Methods: - client.accounts.holdings.list(account_id, \*\*params) -> HoldingListResponse +## Inventories + +Types: + +```python +from studio_sdk.types.accounts import InventoryRetrieveResponse +``` + +Methods: + +- client.accounts.inventories.retrieve(symbol, \*, account_id) -> InventoryRetrieveResponse + # Instruments Types: diff --git a/pyproject.toml b/pyproject.toml index c830484..52662ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,11 +63,11 @@ format = { chain = [ "format:ruff", "format:docs", "fix:ruff", + # run formatting again to fix any inconsistencies when imports are stripped + "format:ruff", ]} -"format:black" = "black ." "format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md" "format:ruff" = "ruff format" -"format:isort" = "isort ." "lint" = { chain = [ "check:ruff", @@ -125,10 +125,6 @@ path = "README.md" pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)' replacement = '[\1](https://github.com/clear-street/studio-sdk-python/tree/main/\g<2>)' -[tool.black] -line-length = 120 -target-version = ["py37"] - [tool.pytest.ini_options] testpaths = ["tests"] addopts = "--tb=short" diff --git a/requirements-dev.lock b/requirements-dev.lock index c976df1..52085ff 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -80,7 +80,7 @@ pytz==2023.3.post1 # via dirty-equals respx==0.20.2 rich==13.7.1 -ruff==0.6.5 +ruff==0.6.9 setuptools==68.2.2 # via nodeenv six==1.16.0 diff --git a/src/studio_sdk/_base_client.py b/src/studio_sdk/_base_client.py index c659df4..b7a98a0 100644 --- a/src/studio_sdk/_base_client.py +++ b/src/studio_sdk/_base_client.py @@ -143,6 +143,12 @@ def __init__( self.url = url self.params = params + @override + def __repr__(self) -> str: + if self.url: + return f"{self.__class__.__name__}(url={self.url})" + return f"{self.__class__.__name__}(params={self.params})" + class BasePage(GenericModel, Generic[_T]): """ @@ -689,7 +695,8 @@ def _calculate_retry_timeout( if retry_after is not None and 0 < retry_after <= 60: return retry_after - nb_retries = max_retries - remaining_retries + # Also cap retry count to 1000 to avoid any potential overflows with `pow` + nb_retries = min(max_retries - remaining_retries, 1000) # Apply exponential backoff, but not more than the max. sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY) @@ -1568,7 +1575,7 @@ async def _request( except Exception as err: log.debug("Encountered Exception", exc_info=True) - if retries_taken > 0: + if remaining_retries > 0: return await self._retry_request( input_options, cast_to, diff --git a/src/studio_sdk/resources/accounts/__init__.py b/src/studio_sdk/resources/accounts/__init__.py index 99fbacb..03df518 100644 --- a/src/studio_sdk/resources/accounts/__init__.py +++ b/src/studio_sdk/resources/accounts/__init__.py @@ -56,6 +56,14 @@ BulkOrdersResourceWithStreamingResponse, AsyncBulkOrdersResourceWithStreamingResponse, ) +from .inventories import ( + InventoriesResource, + AsyncInventoriesResource, + InventoriesResourceWithRawResponse, + AsyncInventoriesResourceWithRawResponse, + InventoriesResourceWithStreamingResponse, + AsyncInventoriesResourceWithStreamingResponse, +) from .pnl_details import ( PnlDetailsResource, AsyncPnlDetailsResource, @@ -150,6 +158,12 @@ "AsyncHoldingsResourceWithRawResponse", "HoldingsResourceWithStreamingResponse", "AsyncHoldingsResourceWithStreamingResponse", + "InventoriesResource", + "AsyncInventoriesResource", + "InventoriesResourceWithRawResponse", + "AsyncInventoriesResourceWithRawResponse", + "InventoriesResourceWithStreamingResponse", + "AsyncInventoriesResourceWithStreamingResponse", "AccountsResource", "AsyncAccountsResource", "AccountsResourceWithRawResponse", diff --git a/src/studio_sdk/resources/accounts/accounts.py b/src/studio_sdk/resources/accounts/accounts.py index a87dd7b..76c4752 100644 --- a/src/studio_sdk/resources/accounts/accounts.py +++ b/src/studio_sdk/resources/accounts/accounts.py @@ -61,6 +61,14 @@ BulkOrdersResourceWithStreamingResponse, AsyncBulkOrdersResourceWithStreamingResponse, ) +from .inventories import ( + InventoriesResource, + AsyncInventoriesResource, + InventoriesResourceWithRawResponse, + AsyncInventoriesResourceWithRawResponse, + InventoriesResourceWithStreamingResponse, + AsyncInventoriesResourceWithStreamingResponse, +) from .pnl_details import ( PnlDetailsResource, AsyncPnlDetailsResource, @@ -141,6 +149,10 @@ def pnl_sums(self) -> PnlSumsResource: def holdings(self) -> HoldingsResource: return HoldingsResource(self._client) + @cached_property + def inventories(self) -> InventoriesResource: + return InventoriesResource(self._client) + @cached_property def with_raw_response(self) -> AccountsResourceWithRawResponse: """ @@ -256,6 +268,10 @@ def pnl_sums(self) -> AsyncPnlSumsResource: def holdings(self) -> AsyncHoldingsResource: return AsyncHoldingsResource(self._client) + @cached_property + def inventories(self) -> AsyncInventoriesResource: + return AsyncInventoriesResource(self._client) + @cached_property def with_raw_response(self) -> AsyncAccountsResourceWithRawResponse: """ @@ -381,6 +397,10 @@ def pnl_sums(self) -> PnlSumsResourceWithRawResponse: def holdings(self) -> HoldingsResourceWithRawResponse: return HoldingsResourceWithRawResponse(self._accounts.holdings) + @cached_property + def inventories(self) -> InventoriesResourceWithRawResponse: + return InventoriesResourceWithRawResponse(self._accounts.inventories) + class AsyncAccountsResourceWithRawResponse: def __init__(self, accounts: AsyncAccountsResource) -> None: @@ -433,6 +453,10 @@ def pnl_sums(self) -> AsyncPnlSumsResourceWithRawResponse: def holdings(self) -> AsyncHoldingsResourceWithRawResponse: return AsyncHoldingsResourceWithRawResponse(self._accounts.holdings) + @cached_property + def inventories(self) -> AsyncInventoriesResourceWithRawResponse: + return AsyncInventoriesResourceWithRawResponse(self._accounts.inventories) + class AccountsResourceWithStreamingResponse: def __init__(self, accounts: AccountsResource) -> None: @@ -485,6 +509,10 @@ def pnl_sums(self) -> PnlSumsResourceWithStreamingResponse: def holdings(self) -> HoldingsResourceWithStreamingResponse: return HoldingsResourceWithStreamingResponse(self._accounts.holdings) + @cached_property + def inventories(self) -> InventoriesResourceWithStreamingResponse: + return InventoriesResourceWithStreamingResponse(self._accounts.inventories) + class AsyncAccountsResourceWithStreamingResponse: def __init__(self, accounts: AsyncAccountsResource) -> None: @@ -536,3 +564,7 @@ def pnl_sums(self) -> AsyncPnlSumsResourceWithStreamingResponse: @cached_property def holdings(self) -> AsyncHoldingsResourceWithStreamingResponse: return AsyncHoldingsResourceWithStreamingResponse(self._accounts.holdings) + + @cached_property + def inventories(self) -> AsyncInventoriesResourceWithStreamingResponse: + return AsyncInventoriesResourceWithStreamingResponse(self._accounts.inventories) diff --git a/src/studio_sdk/resources/accounts/inventories.py b/src/studio_sdk/resources/accounts/inventories.py new file mode 100644 index 0000000..304c841 --- /dev/null +++ b/src/studio_sdk/resources/accounts/inventories.py @@ -0,0 +1,173 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.accounts.inventory_retrieve_response import InventoryRetrieveResponse + +__all__ = ["InventoriesResource", "AsyncInventoriesResource"] + + +class InventoriesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InventoriesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/clear-street/studio-sdk-python#accessing-raw-response-data-eg-headers + """ + return InventoriesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InventoriesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/clear-street/studio-sdk-python#with_streaming_response + """ + return InventoriesResourceWithStreamingResponse(self) + + def retrieve( + self, + symbol: str, + *, + account_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> InventoryRetrieveResponse: + """ + Get located inventory for a symbol. + + Args: + account_id: Account ID for the account. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + if not symbol: + raise ValueError(f"Expected a non-empty value for `symbol` but received {symbol!r}") + return self._get( + f"/accounts/{account_id}/inventories/{symbol}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InventoryRetrieveResponse, + ) + + +class AsyncInventoriesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInventoriesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/clear-street/studio-sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncInventoriesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInventoriesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/clear-street/studio-sdk-python#with_streaming_response + """ + return AsyncInventoriesResourceWithStreamingResponse(self) + + async def retrieve( + self, + symbol: str, + *, + account_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> InventoryRetrieveResponse: + """ + Get located inventory for a symbol. + + Args: + account_id: Account ID for the account. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + if not symbol: + raise ValueError(f"Expected a non-empty value for `symbol` but received {symbol!r}") + return await self._get( + f"/accounts/{account_id}/inventories/{symbol}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InventoryRetrieveResponse, + ) + + +class InventoriesResourceWithRawResponse: + def __init__(self, inventories: InventoriesResource) -> None: + self._inventories = inventories + + self.retrieve = to_raw_response_wrapper( + inventories.retrieve, + ) + + +class AsyncInventoriesResourceWithRawResponse: + def __init__(self, inventories: AsyncInventoriesResource) -> None: + self._inventories = inventories + + self.retrieve = async_to_raw_response_wrapper( + inventories.retrieve, + ) + + +class InventoriesResourceWithStreamingResponse: + def __init__(self, inventories: InventoriesResource) -> None: + self._inventories = inventories + + self.retrieve = to_streamed_response_wrapper( + inventories.retrieve, + ) + + +class AsyncInventoriesResourceWithStreamingResponse: + def __init__(self, inventories: AsyncInventoriesResource) -> None: + self._inventories = inventories + + self.retrieve = async_to_streamed_response_wrapper( + inventories.retrieve, + ) diff --git a/src/studio_sdk/types/account.py b/src/studio_sdk/types/account.py index ecce903..a08883e 100644 --- a/src/studio_sdk/types/account.py +++ b/src/studio_sdk/types/account.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .._models import BaseModel __all__ = ["Account"] diff --git a/src/studio_sdk/types/accounts/__init__.py b/src/studio_sdk/types/accounts/__init__.py index 707f897..20cbd16 100644 --- a/src/studio_sdk/types/accounts/__init__.py +++ b/src/studio_sdk/types/accounts/__init__.py @@ -24,3 +24,4 @@ from .locate_order_create_params import LocateOrderCreateParams as LocateOrderCreateParams from .locate_order_list_response import LocateOrderListResponse as LocateOrderListResponse from .locate_order_update_params import LocateOrderUpdateParams as LocateOrderUpdateParams +from .inventory_retrieve_response import InventoryRetrieveResponse as InventoryRetrieveResponse diff --git a/src/studio_sdk/types/accounts/inventory_retrieve_response.py b/src/studio_sdk/types/accounts/inventory_retrieve_response.py new file mode 100644 index 0000000..7b9d796 --- /dev/null +++ b/src/studio_sdk/types/accounts/inventory_retrieve_response.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["InventoryRetrieveResponse"] + + +class InventoryRetrieveResponse(BaseModel): + account_id: Optional[str] = None + """Account ID for the account.""" + + available: Optional[str] = None + """String representation of quantity.""" + + reserved: Optional[str] = None + """String representation of quantity.""" + + symbol: Optional[str] = None + + used: Optional[str] = None + """String representation of quantity.""" diff --git a/src/studio_sdk/types/accounts/order_create_response.py b/src/studio_sdk/types/accounts/order_create_response.py index 8c8c5c2..2b69c87 100644 --- a/src/studio_sdk/types/accounts/order_create_response.py +++ b/src/studio_sdk/types/accounts/order_create_response.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["OrderCreateResponse"] diff --git a/src/studio_sdk/types/accounts/order_retrieve_response.py b/src/studio_sdk/types/accounts/order_retrieve_response.py index c071338..9a763ad 100644 --- a/src/studio_sdk/types/accounts/order_retrieve_response.py +++ b/src/studio_sdk/types/accounts/order_retrieve_response.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel from ..shared.order import Order diff --git a/src/studio_sdk/types/entities/regt_margin_simulation_create_response.py b/src/studio_sdk/types/entities/regt_margin_simulation_create_response.py index 464ee59..a765ba0 100644 --- a/src/studio_sdk/types/entities/regt_margin_simulation_create_response.py +++ b/src/studio_sdk/types/entities/regt_margin_simulation_create_response.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel from .simulation_id import SimulationID diff --git a/src/studio_sdk/types/pnl_summary.py b/src/studio_sdk/types/pnl_summary.py index 92268c0..c23c973 100644 --- a/src/studio_sdk/types/pnl_summary.py +++ b/src/studio_sdk/types/pnl_summary.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .._models import BaseModel __all__ = ["PnlSummary"] diff --git a/src/studio_sdk/types/shared/regt_margin_simulation.py b/src/studio_sdk/types/shared/regt_margin_simulation.py index fd872bb..c84803f 100644 --- a/src/studio_sdk/types/shared/regt_margin_simulation.py +++ b/src/studio_sdk/types/shared/regt_margin_simulation.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel from ..regt_margin import RegtMargin from ..entities.simulation_id import SimulationID diff --git a/tests/api_resources/accounts/test_inventories.py b/tests/api_resources/accounts/test_inventories.py new file mode 100644 index 0000000..2997240 --- /dev/null +++ b/tests/api_resources/accounts/test_inventories.py @@ -0,0 +1,118 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from studio_sdk import StudioSDK, AsyncStudioSDK +from tests.utils import assert_matches_type +from studio_sdk.types.accounts import InventoryRetrieveResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestInventories: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: StudioSDK) -> None: + inventory = client.accounts.inventories.retrieve( + symbol="AAPL", + account_id="x", + ) + assert_matches_type(InventoryRetrieveResponse, inventory, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: StudioSDK) -> None: + response = client.accounts.inventories.with_raw_response.retrieve( + symbol="AAPL", + account_id="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + inventory = response.parse() + assert_matches_type(InventoryRetrieveResponse, inventory, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: StudioSDK) -> None: + with client.accounts.inventories.with_streaming_response.retrieve( + symbol="AAPL", + account_id="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + inventory = response.parse() + assert_matches_type(InventoryRetrieveResponse, inventory, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: StudioSDK) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.accounts.inventories.with_raw_response.retrieve( + symbol="AAPL", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `symbol` but received ''"): + client.accounts.inventories.with_raw_response.retrieve( + symbol="", + account_id="x", + ) + + +class TestAsyncInventories: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncStudioSDK) -> None: + inventory = await async_client.accounts.inventories.retrieve( + symbol="AAPL", + account_id="x", + ) + assert_matches_type(InventoryRetrieveResponse, inventory, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncStudioSDK) -> None: + response = await async_client.accounts.inventories.with_raw_response.retrieve( + symbol="AAPL", + account_id="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + inventory = await response.parse() + assert_matches_type(InventoryRetrieveResponse, inventory, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncStudioSDK) -> None: + async with async_client.accounts.inventories.with_streaming_response.retrieve( + symbol="AAPL", + account_id="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + inventory = await response.parse() + assert_matches_type(InventoryRetrieveResponse, inventory, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncStudioSDK) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.accounts.inventories.with_raw_response.retrieve( + symbol="AAPL", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `symbol` but received ''"): + await async_client.accounts.inventories.with_raw_response.retrieve( + symbol="", + account_id="x", + ) diff --git a/tests/test_client.py b/tests/test_client.py index 49ec2f6..ac074b5 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -10,6 +10,7 @@ import tracemalloc from typing import Any, Union, cast from unittest import mock +from typing_extensions import Literal import httpx import pytest @@ -730,6 +731,7 @@ class Model(BaseModel): [3, "", 0.5], [2, "", 0.5 * 2.0], [1, "", 0.5 * 4.0], + [-1100, "", 7.8], # test large number potentially overflowing ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) @@ -764,7 +766,14 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> Non @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("studio_sdk._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - def test_retries_taken(self, client: StudioSDK, failures_before_success: int, respx_mock: MockRouter) -> None: + @pytest.mark.parametrize("failure_mode", ["status", "exception"]) + def test_retries_taken( + self, + client: StudioSDK, + failures_before_success: int, + failure_mode: Literal["status", "exception"], + respx_mock: MockRouter, + ) -> None: client = client.with_options(max_retries=4) nb_retries = 0 @@ -773,6 +782,8 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: nonlocal nb_retries if nb_retries < failures_before_success: nb_retries += 1 + if failure_mode == "exception": + raise RuntimeError("oops") return httpx.Response(500) return httpx.Response(200) @@ -1513,6 +1524,7 @@ class Model(BaseModel): [3, "", 0.5], [2, "", 0.5 * 2.0], [1, "", 0.5 * 4.0], + [-1100, "", 7.8], # test large number potentially overflowing ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) @@ -1553,8 +1565,13 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) @mock.patch("studio_sdk._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @pytest.mark.asyncio + @pytest.mark.parametrize("failure_mode", ["status", "exception"]) async def test_retries_taken( - self, async_client: AsyncStudioSDK, failures_before_success: int, respx_mock: MockRouter + self, + async_client: AsyncStudioSDK, + failures_before_success: int, + failure_mode: Literal["status", "exception"], + respx_mock: MockRouter, ) -> None: client = async_client.with_options(max_retries=4) @@ -1564,6 +1581,8 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: nonlocal nb_retries if nb_retries < failures_before_success: nb_retries += 1 + if failure_mode == "exception": + raise RuntimeError("oops") return httpx.Response(500) return httpx.Response(200) diff --git a/tests/test_models.py b/tests/test_models.py index c85ecf6..29cda90 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -245,7 +245,7 @@ class Model(BaseModel): assert m.foo is True m = Model.construct(foo="CARD_HOLDER") - assert m.foo is "CARD_HOLDER" + assert m.foo == "CARD_HOLDER" m = Model.construct(foo={"bar": False}) assert isinstance(m.foo, Submodel1) From b43ca2b8c671173d6e0cdd87174e36aaed6a58ad Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 14:36:24 +0000 Subject: [PATCH 2/2] release: 0.1.0-alpha.12 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/studio_sdk/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ee49ac2..fd0ccba 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0-alpha.11" + ".": "0.1.0-alpha.12" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f54c2b4..5979d8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.1.0-alpha.12 (2024-10-25) + +Full Changelog: [v0.1.0-alpha.11...v0.1.0-alpha.12](https://github.com/clear-street/studio-sdk-python/compare/v0.1.0-alpha.11...v0.1.0-alpha.12) + +### Features + +* **api:** enable inventories endpoint ([#54](https://github.com/clear-street/studio-sdk-python/issues/54)) ([8fc74d6](https://github.com/clear-street/studio-sdk-python/commit/8fc74d66d9b93ab77093788b9f29c56fcfff1478)) + ## 0.1.0-alpha.11 (2024-10-11) Full Changelog: [v0.1.0-alpha.10...v0.1.0-alpha.11](https://github.com/clear-street/studio-sdk-python/compare/v0.1.0-alpha.10...v0.1.0-alpha.11) diff --git a/pyproject.toml b/pyproject.toml index 52662ac..4c31df4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "clear-street-studio-sdk" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" description = "The official Python library for the studio-sdk API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/studio_sdk/_version.py b/src/studio_sdk/_version.py index 275d8ae..f2c5199 100644 --- a/src/studio_sdk/_version.py +++ b/src/studio_sdk/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "studio_sdk" -__version__ = "0.1.0-alpha.11" # x-release-please-version +__version__ = "0.1.0-alpha.12" # x-release-please-version