From 970044799cf5e05f7ea51e2b4cc98041eca8e129 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:09:36 +0100 Subject: [PATCH 1/4] chore(internal): support pre-release versioning (#279) --- release-please-config.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/release-please-config.json b/release-please-config.json index 894d43e8..95bc4492 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -5,6 +5,8 @@ "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", "include-v-in-tag": true, "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, "bump-minor-pre-major": true, "bump-patch-for-minor-pre-major": false, "pull-request-header": "Automated Release PR", From ee9b8901448dc5bc6cb3d45312e3a69747766e4d Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 1 Feb 2024 20:18:38 +0000 Subject: [PATCH 2/4] feat(api): create access token reads client opts if not provided (#281) --- src/finch/resources/access_tokens.py | 38 +- src/finch/types/access_token_create_params.py | 8 +- tests/api_resources/test_access_tokens.py | 32 +- tests/conftest.py | 18 +- tests/test_client.py | 326 ++++++++++++++++-- 5 files changed, 365 insertions(+), 57 deletions(-) diff --git a/src/finch/resources/access_tokens.py b/src/finch/resources/access_tokens.py index 68668295..6c1f7f82 100644 --- a/src/finch/resources/access_tokens.py +++ b/src/finch/resources/access_tokens.py @@ -7,7 +7,7 @@ from .. import _legacy_response from ..types import CreateAccessTokenResponse, access_token_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import maybe_transform +from .._utils import is_given, maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -30,10 +30,10 @@ def with_streaming_response(self) -> AccessTokensWithStreamingResponse: def create( self, *, - client_id: str, - client_secret: str, code: str, redirect_uri: str, + client_id: str | NotGiven = NOT_GIVEN, + client_secret: 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. extra_headers: Headers | None = None, @@ -53,6 +53,20 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ + if not is_given(client_id): + if self._client.client_id is None: + raise ValueError( + "client_id must be provided as an argument or with the FINCH_CLIENT_ID environment variable" + ) + client_id = self._client.client_id + + if not is_given(client_secret): + if self._client.client_secret is None: + raise ValueError( + "client_secret must be provided as an argument or with the FINCH_CLIENT_SECRET environment variable" + ) + client_secret = self._client.client_secret + return self._post( "/auth/token", body=maybe_transform( @@ -83,10 +97,10 @@ def with_streaming_response(self) -> AsyncAccessTokensWithStreamingResponse: async def create( self, *, - client_id: str, - client_secret: str, code: str, redirect_uri: str, + client_id: str | NotGiven = NOT_GIVEN, + client_secret: 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. extra_headers: Headers | None = None, @@ -106,6 +120,20 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ + if not is_given(client_id): + if self._client.client_id is None: + raise ValueError( + "client_id must be provided as an argument or with the FINCH_CLIENT_ID environment variable" + ) + client_id = self._client.client_id + + if not is_given(client_secret): + if self._client.client_secret is None: + raise ValueError( + "client_secret must be provided as an argument or with the FINCH_CLIENT_SECRET environment variable" + ) + client_secret = self._client.client_secret + return await self._post( "/auth/token", body=maybe_transform( diff --git a/src/finch/types/access_token_create_params.py b/src/finch/types/access_token_create_params.py index 2fc25749..57412ee2 100644 --- a/src/finch/types/access_token_create_params.py +++ b/src/finch/types/access_token_create_params.py @@ -8,10 +8,10 @@ class AccessTokenCreateParams(TypedDict, total=False): - client_id: Required[str] - - client_secret: Required[str] - code: Required[str] redirect_uri: Required[str] + + client_id: str + + client_secret: str diff --git a/tests/api_resources/test_access_tokens.py b/tests/api_resources/test_access_tokens.py index b03994d3..3abe3e65 100644 --- a/tests/api_resources/test_access_tokens.py +++ b/tests/api_resources/test_access_tokens.py @@ -20,18 +20,24 @@ class TestAccessTokens: @parametrize def test_method_create(self, client: Finch) -> None: access_token = client.access_tokens.create( - client_id="", - client_secret="", code="", redirect_uri="https://example.com", ) assert_matches_type(CreateAccessTokenResponse, access_token, path=["response"]) @parametrize - def test_raw_response_create(self, client: Finch) -> None: - response = client.access_tokens.with_raw_response.create( + def test_method_create_with_all_params(self, client: Finch) -> None: + access_token = client.access_tokens.create( + code="", + redirect_uri="https://example.com", client_id="", client_secret="", + ) + assert_matches_type(CreateAccessTokenResponse, access_token, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Finch) -> None: + response = client.access_tokens.with_raw_response.create( code="", redirect_uri="https://example.com", ) @@ -44,8 +50,6 @@ def test_raw_response_create(self, client: Finch) -> None: @parametrize def test_streaming_response_create(self, client: Finch) -> None: with client.access_tokens.with_streaming_response.create( - client_id="", - client_secret="", code="", redirect_uri="https://example.com", ) as response: @@ -64,18 +68,24 @@ class TestAsyncAccessTokens: @parametrize async def test_method_create(self, async_client: AsyncFinch) -> None: access_token = await async_client.access_tokens.create( - client_id="", - client_secret="", code="", redirect_uri="https://example.com", ) assert_matches_type(CreateAccessTokenResponse, access_token, path=["response"]) @parametrize - async def test_raw_response_create(self, async_client: AsyncFinch) -> None: - response = await async_client.access_tokens.with_raw_response.create( + async def test_method_create_with_all_params(self, async_client: AsyncFinch) -> None: + access_token = await async_client.access_tokens.create( + code="", + redirect_uri="https://example.com", client_id="", client_secret="", + ) + assert_matches_type(CreateAccessTokenResponse, access_token, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncFinch) -> None: + response = await async_client.access_tokens.with_raw_response.create( code="", redirect_uri="https://example.com", ) @@ -88,8 +98,6 @@ async def test_raw_response_create(self, async_client: AsyncFinch) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncFinch) -> None: async with async_client.access_tokens.with_streaming_response.create( - client_id="", - client_secret="", code="", redirect_uri="https://example.com", ) as response: diff --git a/tests/conftest.py b/tests/conftest.py index 2c791d06..0a070000 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,6 +27,8 @@ def event_loop() -> Iterator[asyncio.AbstractEventLoop]: base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") access_token = "My Access Token" +client_id = "My Client ID" +client_secret = "My Client Secret" @pytest.fixture(scope="session") @@ -35,7 +37,13 @@ def client(request: FixtureRequest) -> Iterator[Finch]: if not isinstance(strict, bool): raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") - with Finch(base_url=base_url, access_token=access_token, _strict_response_validation=strict) as client: + with Finch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=strict, + ) as client: yield client @@ -45,5 +53,11 @@ async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncFinch]: if not isinstance(strict, bool): raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") - async with AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=strict) as client: + async with AsyncFinch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=strict, + ) as client: yield client diff --git a/tests/test_client.py b/tests/test_client.py index d3c64e1b..bc8fdd59 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -27,6 +27,8 @@ base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") access_token = "My Access Token" +client_id = "My Client ID" +client_secret = "My Client Secret" def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]: @@ -36,7 +38,13 @@ def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]: class TestFinch: - client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + client = Finch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) @pytest.mark.respx(base_url=base_url) def test_raw_response(self, respx_mock: MockRouter) -> None: @@ -66,6 +74,14 @@ def test_copy(self) -> None: assert copied.access_token == "another My Access Token" assert self.client.access_token == "My Access Token" + copied = self.client.copy(client_id="another My Client ID") + assert copied.client_id == "another My Client ID" + assert self.client.client_id == "My Client ID" + + copied = self.client.copy(client_secret="another My Client Secret") + assert copied.client_secret == "another My Client Secret" + assert self.client.client_secret == "My Client Secret" + def test_copy_default_options(self) -> None: # options that have a default are overridden correctly copied = self.client.copy(max_retries=7) @@ -86,6 +102,8 @@ def test_copy_default_headers(self) -> None: client = Finch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, default_headers={"X-Foo": "bar"}, ) @@ -121,7 +139,12 @@ def test_copy_default_headers(self) -> None: def test_copy_default_query(self) -> None: client = Finch( - base_url=base_url, access_token=access_token, _strict_response_validation=True, default_query={"foo": "bar"} + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + default_query={"foo": "bar"}, ) assert _get_params(client)["foo"] == "bar" @@ -246,7 +269,12 @@ def test_request_timeout(self) -> None: def test_client_timeout_option(self) -> None: client = Finch( - base_url=base_url, access_token=access_token, _strict_response_validation=True, timeout=httpx.Timeout(0) + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + timeout=httpx.Timeout(0), ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -257,7 +285,12 @@ def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used with httpx.Client(timeout=None) as http_client: client = Finch( - base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + http_client=http_client, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -267,7 +300,12 @@ def test_http_client_timeout_option(self) -> None: # no timeout given to the httpx client should not use the httpx default with httpx.Client() as http_client: client = Finch( - base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + http_client=http_client, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -277,7 +315,12 @@ def test_http_client_timeout_option(self) -> None: # explicitly passing the default timeout currently results in it being ignored with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = Finch( - base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + http_client=http_client, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -288,6 +331,8 @@ def test_default_headers_option(self) -> None: client = Finch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, default_headers={"X-Foo": "bar"}, ) @@ -298,6 +343,8 @@ def test_default_headers_option(self) -> None: client2 = Finch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, default_headers={ "X-Foo": "stainless", @@ -309,11 +356,23 @@ def test_default_headers_option(self) -> None: assert request.headers.get("x-stainless-lang") == "my-overriding-header" def test_validate_headers(self) -> None: - client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + client = Finch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("Authorization") == f"Bearer {access_token}" - client2 = Finch(base_url=base_url, access_token=None, _strict_response_validation=True) + client2 = Finch( + base_url=base_url, + access_token=None, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) with pytest.raises( TypeError, match="Could not resolve authentication method. Expected either access_token, sandbox_client_id or sandbox_client_secret to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted", @@ -329,6 +388,8 @@ def test_default_query_option(self) -> None: client = Finch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, default_query={"query_param": "bar"}, ) @@ -531,7 +592,11 @@ class Model(BaseModel): def test_base_url_setter(self) -> None: client = Finch( - base_url="https://example.com/from_init", access_token=access_token, _strict_response_validation=True + base_url="https://example.com/from_init", + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, ) assert client.base_url == "https://example.com/from_init/" @@ -541,7 +606,12 @@ def test_base_url_setter(self) -> None: def test_base_url_env(self) -> None: with update_env(FINCH_BASE_URL="http://localhost:5000/from/env"): - client = Finch(access_token=access_token, _strict_response_validation=True) + client = Finch( + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) assert client.base_url == "http://localhost:5000/from/env/" @pytest.mark.parametrize( @@ -550,11 +620,15 @@ def test_base_url_env(self) -> None: Finch( base_url="http://localhost:5000/custom/path/", access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, ), Finch( base_url="http://localhost:5000/custom/path/", access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, http_client=httpx.Client(), ), @@ -577,11 +651,15 @@ def test_base_url_trailing_slash(self, client: Finch) -> None: Finch( base_url="http://localhost:5000/custom/path/", access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, ), Finch( base_url="http://localhost:5000/custom/path/", access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, http_client=httpx.Client(), ), @@ -604,11 +682,15 @@ def test_base_url_no_trailing_slash(self, client: Finch) -> None: Finch( base_url="http://localhost:5000/custom/path/", access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, ), Finch( base_url="http://localhost:5000/custom/path/", access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, http_client=httpx.Client(), ), @@ -635,7 +717,12 @@ def test_transport_option_is_deprecated(self) -> None: ) client = Finch( - base_url=base_url, access_token=access_token, _strict_response_validation=True, transport=transport + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + transport=transport, ) assert client._client._transport is transport @@ -647,6 +734,8 @@ def test_transport_option_mutually_exclusive_with_http_client(self) -> None: Finch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, transport=httpx.MockTransport( lambda: None, # type: ignore @@ -666,6 +755,8 @@ def test_connection_pool_limits_option_is_deprecated(self) -> None: client = Finch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, connection_pool_limits=connection_pool_limits, ) @@ -684,6 +775,8 @@ def test_connection_pool_limits_option_mutually_exclusive_with_http_client(self) Finch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, connection_pool_limits=httpx.Limits( max_connections=101, max_keepalive_connections=76, keepalive_expiry=23 @@ -699,7 +792,12 @@ def test_proxies_option_is_deprecated(self) -> None: proxies = "https://www.example.com/proxy" client = Finch( - base_url=base_url, access_token=access_token, _strict_response_validation=True, proxies=proxies + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + proxies=proxies, ) mounts = list(client._client._mounts.keys()) @@ -715,13 +813,21 @@ def test_proxies_option_mutually_exclusive_with_http_client(self) -> None: Finch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, proxies="https://www.example.com/proxy", http_client=http_client, ) def test_copied_client_does_not_close_http(self) -> None: - client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + client = Finch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) assert not client.is_closed() copied = client.copy() @@ -732,7 +838,13 @@ def test_copied_client_does_not_close_http(self) -> None: assert not client.is_closed() def test_client_context_manager(self) -> None: - client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + client = Finch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) with client as c2: assert c2 is client assert not c2.is_closed() @@ -758,12 +870,24 @@ class Model(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) - strict_client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + strict_client = Finch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) with pytest.raises(APIResponseValidationError): strict_client.get("/foo", cast_to=Model) - client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=False) + client = Finch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=False, + ) response = client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] @@ -790,7 +914,13 @@ class Model(BaseModel): ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: - client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + client = Finch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) @@ -799,7 +929,13 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str class TestAsyncFinch: - client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + client = AsyncFinch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) @pytest.mark.respx(base_url=base_url) @pytest.mark.asyncio @@ -831,6 +967,14 @@ def test_copy(self) -> None: assert copied.access_token == "another My Access Token" assert self.client.access_token == "My Access Token" + copied = self.client.copy(client_id="another My Client ID") + assert copied.client_id == "another My Client ID" + assert self.client.client_id == "My Client ID" + + copied = self.client.copy(client_secret="another My Client Secret") + assert copied.client_secret == "another My Client Secret" + assert self.client.client_secret == "My Client Secret" + def test_copy_default_options(self) -> None: # options that have a default are overridden correctly copied = self.client.copy(max_retries=7) @@ -851,6 +995,8 @@ def test_copy_default_headers(self) -> None: client = AsyncFinch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, default_headers={"X-Foo": "bar"}, ) @@ -886,7 +1032,12 @@ def test_copy_default_headers(self) -> None: def test_copy_default_query(self) -> None: client = AsyncFinch( - base_url=base_url, access_token=access_token, _strict_response_validation=True, default_query={"foo": "bar"} + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + default_query={"foo": "bar"}, ) assert _get_params(client)["foo"] == "bar" @@ -1011,7 +1162,12 @@ async def test_request_timeout(self) -> None: async def test_client_timeout_option(self) -> None: client = AsyncFinch( - base_url=base_url, access_token=access_token, _strict_response_validation=True, timeout=httpx.Timeout(0) + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + timeout=httpx.Timeout(0), ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1022,7 +1178,12 @@ async def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used async with httpx.AsyncClient(timeout=None) as http_client: client = AsyncFinch( - base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + http_client=http_client, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1032,7 +1193,12 @@ async def test_http_client_timeout_option(self) -> None: # no timeout given to the httpx client should not use the httpx default async with httpx.AsyncClient() as http_client: client = AsyncFinch( - base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + http_client=http_client, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1042,7 +1208,12 @@ async def test_http_client_timeout_option(self) -> None: # explicitly passing the default timeout currently results in it being ignored async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = AsyncFinch( - base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + http_client=http_client, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1053,6 +1224,8 @@ def test_default_headers_option(self) -> None: client = AsyncFinch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, default_headers={"X-Foo": "bar"}, ) @@ -1063,6 +1236,8 @@ def test_default_headers_option(self) -> None: client2 = AsyncFinch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, default_headers={ "X-Foo": "stainless", @@ -1074,11 +1249,23 @@ def test_default_headers_option(self) -> None: assert request.headers.get("x-stainless-lang") == "my-overriding-header" def test_validate_headers(self) -> None: - client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + client = AsyncFinch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("Authorization") == f"Bearer {access_token}" - client2 = AsyncFinch(base_url=base_url, access_token=None, _strict_response_validation=True) + client2 = AsyncFinch( + base_url=base_url, + access_token=None, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) with pytest.raises( TypeError, match="Could not resolve authentication method. Expected either access_token, sandbox_client_id or sandbox_client_secret to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted", @@ -1094,6 +1281,8 @@ def test_default_query_option(self) -> None: client = AsyncFinch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, default_query={"query_param": "bar"}, ) @@ -1296,7 +1485,11 @@ class Model(BaseModel): def test_base_url_setter(self) -> None: client = AsyncFinch( - base_url="https://example.com/from_init", access_token=access_token, _strict_response_validation=True + base_url="https://example.com/from_init", + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, ) assert client.base_url == "https://example.com/from_init/" @@ -1306,7 +1499,12 @@ def test_base_url_setter(self) -> None: def test_base_url_env(self) -> None: with update_env(FINCH_BASE_URL="http://localhost:5000/from/env"): - client = AsyncFinch(access_token=access_token, _strict_response_validation=True) + client = AsyncFinch( + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) assert client.base_url == "http://localhost:5000/from/env/" @pytest.mark.parametrize( @@ -1315,11 +1513,15 @@ def test_base_url_env(self) -> None: AsyncFinch( base_url="http://localhost:5000/custom/path/", access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, ), AsyncFinch( base_url="http://localhost:5000/custom/path/", access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), @@ -1342,11 +1544,15 @@ def test_base_url_trailing_slash(self, client: AsyncFinch) -> None: AsyncFinch( base_url="http://localhost:5000/custom/path/", access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, ), AsyncFinch( base_url="http://localhost:5000/custom/path/", access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), @@ -1369,11 +1575,15 @@ def test_base_url_no_trailing_slash(self, client: AsyncFinch) -> None: AsyncFinch( base_url="http://localhost:5000/custom/path/", access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, ), AsyncFinch( base_url="http://localhost:5000/custom/path/", access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), @@ -1400,7 +1610,12 @@ def test_transport_option_is_deprecated(self) -> None: ) client = AsyncFinch( - base_url=base_url, access_token=access_token, _strict_response_validation=True, transport=transport + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + transport=transport, ) assert client._client._transport is transport @@ -1412,6 +1627,8 @@ async def test_transport_option_mutually_exclusive_with_http_client(self) -> Non AsyncFinch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, transport=httpx.MockTransport( lambda: None, # type: ignore @@ -1431,6 +1648,8 @@ def test_connection_pool_limits_option_is_deprecated(self) -> None: client = AsyncFinch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, connection_pool_limits=connection_pool_limits, ) @@ -1449,6 +1668,8 @@ async def test_connection_pool_limits_option_mutually_exclusive_with_http_client AsyncFinch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, connection_pool_limits=httpx.Limits( max_connections=101, max_keepalive_connections=76, keepalive_expiry=23 @@ -1464,7 +1685,12 @@ def test_proxies_option_is_deprecated(self) -> None: proxies = "https://www.example.com/proxy" client = AsyncFinch( - base_url=base_url, access_token=access_token, _strict_response_validation=True, proxies=proxies + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + proxies=proxies, ) mounts = list(client._client._mounts.keys()) @@ -1480,13 +1706,21 @@ async def test_proxies_option_mutually_exclusive_with_http_client(self) -> None: AsyncFinch( base_url=base_url, access_token=access_token, + client_id=client_id, + client_secret=client_secret, _strict_response_validation=True, proxies="https://www.example.com/proxy", http_client=http_client, ) async def test_copied_client_does_not_close_http(self) -> None: - client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + client = AsyncFinch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) assert not client.is_closed() copied = client.copy() @@ -1498,7 +1732,13 @@ async def test_copied_client_does_not_close_http(self) -> None: assert not client.is_closed() async def test_client_context_manager(self) -> None: - client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + client = AsyncFinch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) async with client as c2: assert c2 is client assert not c2.is_closed() @@ -1526,12 +1766,24 @@ class Model(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) - strict_client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + strict_client = AsyncFinch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) with pytest.raises(APIResponseValidationError): await strict_client.get("/foo", cast_to=Model) - client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=False) + client = AsyncFinch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=False, + ) response = await client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] @@ -1559,7 +1811,13 @@ class Model(BaseModel): @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) @pytest.mark.asyncio async def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: - client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + client = AsyncFinch( + base_url=base_url, + access_token=access_token, + client_id=client_id, + client_secret=client_secret, + _strict_response_validation=True, + ) headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) From 73dc78bea0ed2847d5bb19f229ffcfcaa9368af2 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 2 Feb 2024 04:44:03 +0000 Subject: [PATCH 3/4] chore(interal): make link to api.md relative (#282) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a4e29fb6..ef8230aa 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ and offers both synchronous and asynchronous clients powered by [httpx](https:// ## Documentation -The REST API documentation can be found [in the Finch Documentation Center](https://developer.tryfinch.com/). The full API of this library can be found in [api.md](https://www.github.com/Finch-API/finch-api-python/blob/main/api.md). +The REST API documentation can be found [in the Finch Documentation Center](https://developer.tryfinch.com/). The full API of this library can be found in [api.md](api.md). ## Installation @@ -18,7 +18,7 @@ pip install finch-api ## Usage -The full API of this library can be found in [api.md](https://www.github.com/Finch-API/finch-api-python/blob/main/api.md). +The full API of this library can be found in [api.md](api.md). ```python from finch import Finch From ea16d4f9c76e87ed797b0b7d8990b61110c76e53 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 04:44:27 +0000 Subject: [PATCH 4/4] release: 0.14.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/finch/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d52d2b97..a26ebfc1 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.13.0" + ".": "0.14.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 410d0781..b191418e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.14.0 (2024-02-02) + +Full Changelog: [v0.13.0...v0.14.0](https://github.com/Finch-API/finch-api-python/compare/v0.13.0...v0.14.0) + +### Features + +* **api:** create access token reads client opts if not provided ([#281](https://github.com/Finch-API/finch-api-python/issues/281)) ([ee9b890](https://github.com/Finch-API/finch-api-python/commit/ee9b8901448dc5bc6cb3d45312e3a69747766e4d)) + + +### Chores + +* **interal:** make link to api.md relative ([#282](https://github.com/Finch-API/finch-api-python/issues/282)) ([73dc78b](https://github.com/Finch-API/finch-api-python/commit/73dc78bea0ed2847d5bb19f229ffcfcaa9368af2)) +* **internal:** support pre-release versioning ([#279](https://github.com/Finch-API/finch-api-python/issues/279)) ([9700447](https://github.com/Finch-API/finch-api-python/commit/970044799cf5e05f7ea51e2b4cc98041eca8e129)) + ## 0.13.0 (2024-01-30) Full Changelog: [v0.12.0...v0.13.0](https://github.com/Finch-API/finch-api-python/compare/v0.12.0...v0.13.0) diff --git a/pyproject.toml b/pyproject.toml index 786b58db..a958edc8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "finch-api" -version = "0.13.0" +version = "0.14.0" description = "The official Python library for the Finch API" readme = "README.md" license = "Apache-2.0" diff --git a/src/finch/_version.py b/src/finch/_version.py index 59b2bc6f..02b61c9d 100644 --- a/src/finch/_version.py +++ b/src/finch/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "finch" -__version__ = "0.13.0" # x-release-please-version +__version__ = "0.14.0" # x-release-please-version