From 42de23d5c7a3b9253396b94af7884e7a9300b841 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 29 Nov 2023 05:56:19 -0500 Subject: [PATCH 01/26] chore(internal): update lock file (#201) --- requirements-dev.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index fd275d2e..e7a2e23a 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -41,7 +41,6 @@ pyright==1.1.332 pytest==7.1.1 pytest-asyncio==0.21.1 python-dateutil==2.8.2 -pytz==2023.3.post1 respx==0.19.2 rfc3986==1.5.0 ruff==0.0.282 From 2049c50e723ec6ec4b4d46a18fa87800a58d581a Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 29 Nov 2023 06:38:32 -0500 Subject: [PATCH 02/26] chore(internal): updates to proxy helper (#202) --- src/finch/_utils/_proxy.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/finch/_utils/_proxy.py b/src/finch/_utils/_proxy.py index aa934a3f..3c9e790a 100644 --- a/src/finch/_utils/_proxy.py +++ b/src/finch/_utils/_proxy.py @@ -18,25 +18,43 @@ class LazyProxy(Generic[T], ABC): def __init__(self) -> None: self.__proxied: T | None = None + # Note: we have to special case proxies that themselves return proxies + # to support using a proxy as a catch-all for any random access, e.g. `proxy.foo.bar.baz` + def __getattr__(self, attr: str) -> object: - return getattr(self.__get_proxied__(), attr) + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied # pyright: ignore + return getattr(proxied, attr) @override def __repr__(self) -> str: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied.__class__.__name__ return repr(self.__get_proxied__()) @override def __str__(self) -> str: - return str(self.__get_proxied__()) + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied.__class__.__name__ + return str(proxied) @override def __dir__(self) -> Iterable[str]: - return self.__get_proxied__().__dir__() + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return [] + return proxied.__dir__() @property # type: ignore @override def __class__(self) -> type: - return self.__get_proxied__().__class__ + proxied = self.__get_proxied__() + if issubclass(type(proxied), LazyProxy): + return type(proxied) + return proxied.__class__ def __get_proxied__(self) -> T: if not self.should_cache: From b5b9f79c19e846e2e8fc3fd1faf905a65a6aa7c4 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 29 Nov 2023 07:24:27 -0500 Subject: [PATCH 03/26] chore(internal): add tests for proxy change (#203) --- tests/test_utils/test_proxy.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/test_utils/test_proxy.py diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py new file mode 100644 index 00000000..4a7336a8 --- /dev/null +++ b/tests/test_utils/test_proxy.py @@ -0,0 +1,23 @@ +import operator +from typing import Any +from typing_extensions import override + +from finch._utils import LazyProxy + + +class RecursiveLazyProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + return self + + def __call__(self, *_args: Any, **_kwds: Any) -> Any: + raise RuntimeError("This should never be called!") + + +def test_recursive_proxy() -> None: + proxy = RecursiveLazyProxy() + assert repr(proxy) == "RecursiveLazyProxy" + assert str(proxy) == "RecursiveLazyProxy" + assert dir(proxy) == [] + assert getattr(type(proxy), "__name__") == "RecursiveLazyProxy" + assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy" From 0659932aaaa69850982e6acbe076a4af7980efcd Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 29 Nov 2023 19:02:23 -0500 Subject: [PATCH 04/26] fix(client): ensure retried requests are closed (#204) --- src/finch/_base_client.py | 100 ++++++++++++++++++++++++++++++-------- src/finch/_constants.py | 1 + tests/test_client.py | 53 +++++++++++++++++++- 3 files changed, 133 insertions(+), 21 deletions(-) diff --git a/src/finch/_base_client.py b/src/finch/_base_client.py index a168301f..89d9ce48 100644 --- a/src/finch/_base_client.py +++ b/src/finch/_base_client.py @@ -72,6 +72,7 @@ DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, RAW_RESPONSE_HEADER, + STREAMED_RAW_RESPONSE_HEADER, ) from ._streaming import Stream, AsyncStream from ._exceptions import ( @@ -363,14 +364,21 @@ def _make_status_error_from_response( self, response: httpx.Response, ) -> APIStatusError: - err_text = response.text.strip() - body = err_text + if response.is_closed and not response.is_stream_consumed: + # We can't read the response body as it has been closed + # before it was read. This can happen if an event hook + # raises a status error. + body = None + err_msg = f"Error code: {response.status_code}" + else: + err_text = response.text.strip() + body = err_text - try: - body = json.loads(err_text) - err_msg = f"Error code: {response.status_code} - {body}" - except Exception: - err_msg = err_text or f"Error code: {response.status_code}" + try: + body = json.loads(err_text) + err_msg = f"Error code: {response.status_code} - {body}" + except Exception: + err_msg = err_text or f"Error code: {response.status_code}" return self._make_status_error(err_msg, body=body, response=response) @@ -534,6 +542,12 @@ def _process_response_data( except pydantic.ValidationError as err: raise APIResponseValidationError(response=response, body=data) from err + def _should_stream_response_body(self, *, request: httpx.Request) -> bool: + if request.headers.get(STREAMED_RAW_RESPONSE_HEADER) == "true": + return True + + return False + @property def qs(self) -> Querystring: return Querystring() @@ -606,7 +620,7 @@ def _calculate_retry_timeout( if response_headers is not None: retry_header = response_headers.get("retry-after") try: - retry_after = int(retry_header) + retry_after = float(retry_header) except Exception: retry_date_tuple = email.utils.parsedate_tz(retry_header) if retry_date_tuple is None: @@ -862,14 +876,21 @@ def _request( request = self._build_request(options) self._prepare_request(request) + response = None + try: - response = self._client.send(request, auth=self.custom_auth, stream=stream) + response = self._client.send( + request, + auth=self.custom_auth, + stream=stream or self._should_stream_response_body(request=request), + ) log.debug( 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase ) response.raise_for_status() except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code if retries > 0 and self._should_retry(err.response): + err.response.close() return self._retry_request( options, cast_to, @@ -881,9 +902,14 @@ def _request( # If the response is streamed then we need to explicitly read the response # to completion before attempting to access the response text. - err.response.read() + if not err.response.is_closed: + err.response.read() + raise self._make_status_error_from_response(err.response) from None except httpx.TimeoutException as err: + if response is not None: + response.close() + if retries > 0: return self._retry_request( options, @@ -891,9 +917,14 @@ def _request( retries, stream=stream, stream_cls=stream_cls, + response_headers=response.headers if response is not None else None, ) + raise APITimeoutError(request=request) from err except Exception as err: + if response is not None: + response.close() + if retries > 0: return self._retry_request( options, @@ -901,7 +932,9 @@ def _request( retries, stream=stream, stream_cls=stream_cls, + response_headers=response.headers if response is not None else None, ) + raise APIConnectionError(request=request) from err return self._process_response( @@ -917,7 +950,7 @@ def _retry_request( options: FinalRequestOptions, cast_to: Type[ResponseT], remaining_retries: int, - response_headers: Optional[httpx.Headers] = None, + response_headers: httpx.Headers | None, *, stream: bool, stream_cls: type[_StreamT] | None, @@ -1303,14 +1336,21 @@ async def _request( request = self._build_request(options) await self._prepare_request(request) + response = None + try: - response = await self._client.send(request, auth=self.custom_auth, stream=stream) + response = await self._client.send( + request, + auth=self.custom_auth, + stream=stream or self._should_stream_response_body(request=request), + ) log.debug( 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase ) response.raise_for_status() except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code if retries > 0 and self._should_retry(err.response): + await err.response.aclose() return await self._retry_request( options, cast_to, @@ -1322,19 +1362,39 @@ async def _request( # If the response is streamed then we need to explicitly read the response # to completion before attempting to access the response text. - await err.response.aread() + if not err.response.is_closed: + await err.response.aread() + raise self._make_status_error_from_response(err.response) from None - except httpx.ConnectTimeout as err: - if retries > 0: - return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls) - raise APITimeoutError(request=request) from err except httpx.TimeoutException as err: + if response is not None: + await response.aclose() + if retries > 0: - return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls) + return await self._retry_request( + options, + cast_to, + retries, + stream=stream, + stream_cls=stream_cls, + response_headers=response.headers if response is not None else None, + ) + raise APITimeoutError(request=request) from err except Exception as err: + if response is not None: + await response.aclose() + if retries > 0: - return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls) + return await self._retry_request( + options, + cast_to, + retries, + stream=stream, + stream_cls=stream_cls, + response_headers=response.headers if response is not None else None, + ) + raise APIConnectionError(request=request) from err return self._process_response( @@ -1350,7 +1410,7 @@ async def _retry_request( options: FinalRequestOptions, cast_to: Type[ResponseT], remaining_retries: int, - response_headers: Optional[httpx.Headers] = None, + response_headers: httpx.Headers | None, *, stream: bool, stream_cls: type[_AsyncStreamT] | None, diff --git a/src/finch/_constants.py b/src/finch/_constants.py index 0c3f31df..39b46eb0 100644 --- a/src/finch/_constants.py +++ b/src/finch/_constants.py @@ -3,6 +3,7 @@ import httpx RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response" +STREAMED_RAW_RESPONSE_HEADER = "X-Stainless-Streamed-Raw-Response" # default timeout is 1 minute DEFAULT_TIMEOUT = httpx.Timeout(timeout=60.0, connect=5.0) diff --git a/tests/test_client.py b/tests/test_client.py index 5c5b16b1..a95af2f6 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -18,7 +18,7 @@ from finch._types import Omit from finch._client import Finch, AsyncFinch from finch._models import BaseModel, FinalRequestOptions -from finch._exceptions import APIResponseValidationError +from finch._exceptions import APIStatusError, APIResponseValidationError from finch._base_client import ( DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, @@ -704,6 +704,31 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str calculated = client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] + @pytest.mark.respx(base_url=base_url) + def test_status_error_within_httpx(self, respx_mock: MockRouter) -> None: + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + def on_response(response: httpx.Response) -> None: + raise httpx.HTTPStatusError( + "Simulating an error inside httpx", + response=response, + request=response.request, + ) + + client = Finch( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + http_client=httpx.Client( + event_hooks={ + "response": [on_response], + } + ), + max_retries=0, + ) + with pytest.raises(APIStatusError): + client.post("/foo", cast_to=httpx.Response) + class TestAsyncFinch: client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True) @@ -1377,3 +1402,29 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte options = FinalRequestOptions(method="get", url="/foo", max_retries=3) calculated = client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] + + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_status_error_within_httpx(self, respx_mock: MockRouter) -> None: + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + def on_response(response: httpx.Response) -> None: + raise httpx.HTTPStatusError( + "Simulating an error inside httpx", + response=response, + request=response.request, + ) + + client = AsyncFinch( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + http_client=httpx.AsyncClient( + event_hooks={ + "response": [on_response], + } + ), + max_retries=0, + ) + with pytest.raises(APIStatusError): + await client.post("/foo", cast_to=httpx.Response) From 4ff1a6b04ba8e3301ac6ff4a612bb8aab801abd0 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 30 Nov 2023 08:20:28 -0500 Subject: [PATCH 05/26] docs(readme): update example snippets (#205) --- src/finch/types/hris/benfit_contribution.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/finch/types/hris/benfit_contribution.py b/src/finch/types/hris/benfit_contribution.py index 522d5602..676c3b8e 100644 --- a/src/finch/types/hris/benfit_contribution.py +++ b/src/finch/types/hris/benfit_contribution.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. - from .benefit_contribution import BenefitContribution BenfitContribution = BenefitContribution From 7c920a33c103fadee08cd784f5d028f2ab19411f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 30 Nov 2023 12:52:08 -0500 Subject: [PATCH 06/26] chore(internal): replace string concatenation with f-strings (#206) --- src/finch/_utils/_utils.py | 2 +- tests/test_required_args.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/finch/_utils/_utils.py b/src/finch/_utils/_utils.py index d2bfc91a..83f88cc3 100644 --- a/src/finch/_utils/_utils.py +++ b/src/finch/_utils/_utils.py @@ -230,7 +230,7 @@ def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> s def quote(string: str) -> str: """Add single quotation marks around the given string. Does *not* do any escaping.""" - return "'" + string + "'" + return f"'{string}'" def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: diff --git a/tests/test_required_args.py b/tests/test_required_args.py index e839289d..38007450 100644 --- a/tests/test_required_args.py +++ b/tests/test_required_args.py @@ -43,7 +43,7 @@ def foo(*, a: str | None = None) -> str | None: def test_multiple_params() -> None: @required_args(["a", "b", "c"]) def foo(a: str = "", *, b: str = "", c: str = "") -> str | None: - return a + " " + b + " " + c + return f"{a} {b} {c}" assert foo(a="a", b="b", c="c") == "a b c" From 5e04fa773d4603d0be73aa67268b17fdc4f6fed2 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:53:34 -0500 Subject: [PATCH 07/26] fix(client): correct base_url setter implementation (#207) --- src/finch/_base_client.py | 2 +- tests/test_client.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/finch/_base_client.py b/src/finch/_base_client.py index 89d9ce48..2e5678e8 100644 --- a/src/finch/_base_client.py +++ b/src/finch/_base_client.py @@ -592,7 +592,7 @@ def base_url(self) -> URL: @base_url.setter def base_url(self, url: URL | str) -> None: - self._client.base_url = url if isinstance(url, URL) else URL(url) + self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url)) @lru_cache(maxsize=None) def platform_headers(self) -> Dict[str, str]: diff --git a/tests/test_client.py b/tests/test_client.py index a95af2f6..55502d8e 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -441,6 +441,16 @@ class Model(BaseModel): assert isinstance(response, Model) assert response.foo == 2 + def test_base_url_setter(self) -> None: + client = Finch( + base_url="https://example.com/from_init", access_token=access_token, _strict_response_validation=True + ) + assert client.base_url == "https://example.com/from_init/" + + client.base_url = "https://example.com/from_setter" # type: ignore[assignment] + + assert client.base_url == "https://example.com/from_setter/" + 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) @@ -1135,6 +1145,16 @@ class Model(BaseModel): assert isinstance(response, Model) assert response.foo == 2 + def test_base_url_setter(self) -> None: + client = AsyncFinch( + base_url="https://example.com/from_init", access_token=access_token, _strict_response_validation=True + ) + assert client.base_url == "https://example.com/from_init/" + + client.base_url = "https://example.com/from_setter" # type: ignore[assignment] + + assert client.base_url == "https://example.com/from_setter/" + 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) From e1ed4a53591362f5e2579e301b43529b64f2fc8b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 4 Dec 2023 05:27:09 -0500 Subject: [PATCH 08/26] chore(package): lift anyio v4 restriction (#208) --- pyproject.toml | 4 ++-- requirements-dev.lock | 12 ++++++------ requirements.lock | 9 ++++----- tests/test_client.py | 30 +++++++++++++++++++----------- 4 files changed, 31 insertions(+), 24 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7b3b5304..b831f420 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ dependencies = [ "httpx>=0.23.0, <1", "pydantic>=1.9.0, <3", "typing-extensions>=4.5, <5", - "anyio>=3.5.0, <4", + "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", @@ -49,7 +49,7 @@ dev-dependencies = [ "pyright==1.1.332", "mypy==1.7.1", "black==23.3.0", - "respx==0.19.2", + "respx==0.20.2", "pytest==7.1.1", "pytest-asyncio==0.21.1", "ruff==0.0.282", diff --git a/requirements-dev.lock b/requirements-dev.lock index e7a2e23a..ce154682 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -8,7 +8,7 @@ -e file:. annotated-types==0.6.0 -anyio==3.7.1 +anyio==4.1.0 argcomplete==3.1.2 attrs==23.1.0 black==23.3.0 @@ -20,9 +20,9 @@ distlib==0.3.7 distro==1.8.0 exceptiongroup==1.1.3 filelock==3.12.4 -h11==0.12.0 -httpcore==0.15.0 -httpx==0.23.0 +h11==0.14.0 +httpcore==1.0.2 +httpx==0.25.2 idna==3.4 iniconfig==2.0.0 isort==5.10.1 @@ -41,8 +41,8 @@ pyright==1.1.332 pytest==7.1.1 pytest-asyncio==0.21.1 python-dateutil==2.8.2 -respx==0.19.2 -rfc3986==1.5.0 +pytz==2023.3.post1 +respx==0.20.2 ruff==0.0.282 six==1.16.0 sniffio==1.3.0 diff --git a/requirements.lock b/requirements.lock index 0c8c2c2e..2022a5c5 100644 --- a/requirements.lock +++ b/requirements.lock @@ -8,16 +8,15 @@ -e file:. annotated-types==0.6.0 -anyio==3.7.1 +anyio==4.1.0 certifi==2023.7.22 distro==1.8.0 exceptiongroup==1.1.3 -h11==0.12.0 -httpcore==0.15.0 -httpx==0.23.0 +h11==0.14.0 +httpcore==1.0.2 +httpx==0.25.2 idna==3.4 pydantic==2.4.2 pydantic-core==2.10.1 -rfc3986==1.5.0 sniffio==1.3.0 typing-extensions==4.8.0 diff --git a/tests/test_client.py b/tests/test_client.py index 55502d8e..a37278e9 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -6,7 +6,7 @@ import json import asyncio import inspect -from typing import Any, Dict, Union, cast +from typing import Any, Union, cast from unittest import mock import httpx @@ -353,7 +353,7 @@ def test_request_extra_query(self) -> None: ), ), ) - params = cast(Dict[str, str], dict(request.url.params)) + params = dict(request.url.params) assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged @@ -367,7 +367,7 @@ def test_request_extra_query(self) -> None: ), ), ) - params = cast(Dict[str, str], dict(request.url.params)) + params = dict(request.url.params) assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash @@ -381,7 +381,7 @@ def test_request_extra_query(self) -> None: ), ), ) - params = cast(Dict[str, str], dict(request.url.params)) + params = dict(request.url.params) assert params == {"foo": "2"} @pytest.mark.respx(base_url=base_url) @@ -542,7 +542,9 @@ def test_transport_option_is_deprecated(self) -> None: DeprecationWarning, match="The `transport` argument is deprecated. The `http_client` argument should be passed instead", ): - transport = httpx.MockTransport(lambda: None) + transport = httpx.MockTransport( + lambda: None, # type: ignore + ) client = Finch( base_url=base_url, access_token=access_token, _strict_response_validation=True, transport=transport @@ -558,7 +560,9 @@ def test_transport_option_mutually_exclusive_with_http_client(self) -> None: base_url=base_url, access_token=access_token, _strict_response_validation=True, - transport=httpx.MockTransport(lambda: None), + transport=httpx.MockTransport( + lambda: None, # type: ignore + ), http_client=http_client, ) @@ -1057,7 +1061,7 @@ def test_request_extra_query(self) -> None: ), ), ) - params = cast(Dict[str, str], dict(request.url.params)) + params = dict(request.url.params) assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged @@ -1071,7 +1075,7 @@ def test_request_extra_query(self) -> None: ), ), ) - params = cast(Dict[str, str], dict(request.url.params)) + params = dict(request.url.params) assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash @@ -1085,7 +1089,7 @@ def test_request_extra_query(self) -> None: ), ), ) - params = cast(Dict[str, str], dict(request.url.params)) + params = dict(request.url.params) assert params == {"foo": "2"} @pytest.mark.respx(base_url=base_url) @@ -1246,7 +1250,9 @@ def test_transport_option_is_deprecated(self) -> None: DeprecationWarning, match="The `transport` argument is deprecated. The `http_client` argument should be passed instead", ): - transport = httpx.MockTransport(lambda: None) + transport = httpx.MockTransport( + lambda: None, # type: ignore + ) client = AsyncFinch( base_url=base_url, access_token=access_token, _strict_response_validation=True, transport=transport @@ -1262,7 +1268,9 @@ async def test_transport_option_mutually_exclusive_with_http_client(self) -> Non base_url=base_url, access_token=access_token, _strict_response_validation=True, - transport=httpx.MockTransport(lambda: None), + transport=httpx.MockTransport( + lambda: None, # type: ignore + ), http_client=http_client, ) From 6b8c8a39e528e5fef8b4ad2c33ab85548803fb40 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 4 Dec 2023 05:45:09 -0500 Subject: [PATCH 09/26] ci: ensure PR titles use conventional commits (#209) --- .github/workflows/lint-pr.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/lint-pr.yml diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml new file mode 100644 index 00000000..82ee9e18 --- /dev/null +++ b/.github/workflows/lint-pr.yml @@ -0,0 +1,21 @@ +name: "Lint PR" + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +permissions: + pull-requests: read + +jobs: + pr_title: + name: Validate PR title + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' && github.repository == 'Finch-API/finch-api-python' + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From cd3cc6febbb46537d6694303c52fbfa266e762cd Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 4 Dec 2023 13:27:06 -0500 Subject: [PATCH 10/26] feat(api): add `/jobs` endpoints (#210) --- .stats.yml | 2 +- api.md | 32 +- src/finch/_client.py | 6 + src/finch/pagination.py | 62 +++- src/finch/resources/__init__.py | 5 + src/finch/resources/jobs/__init__.py | 30 ++ src/finch/resources/jobs/automated.py | 318 ++++++++++++++++++ src/finch/resources/jobs/jobs.py | 60 ++++ src/finch/resources/jobs/manual.py | 114 +++++++ src/finch/types/__init__.py | 2 +- .../types/hris/pay_statement_response_body.py | 2 +- src/finch/types/jobs/__init__.py | 11 + src/finch/types/jobs/automated_async_job.py | 42 +++ .../types/jobs/automated_create_params.py | 12 + .../types/jobs/automated_create_response.py | 19 ++ src/finch/types/jobs/automated_list_params.py | 15 + src/finch/types/jobs/manual_async_job.py | 17 + src/finch/types/shared/__init__.py | 1 + src/finch/types/{ => shared}/paging.py | 2 +- src/finch/types/shared_params/__init__.py | 1 + src/finch/types/shared_params/paging.py | 15 + tests/api_resources/jobs/__init__.py | 1 + tests/api_resources/jobs/test_automated.py | 132 ++++++++ tests/api_resources/jobs/test_manual.py | 59 ++++ 24 files changed, 953 insertions(+), 7 deletions(-) create mode 100644 src/finch/resources/jobs/__init__.py create mode 100644 src/finch/resources/jobs/automated.py create mode 100644 src/finch/resources/jobs/jobs.py create mode 100644 src/finch/resources/jobs/manual.py create mode 100644 src/finch/types/jobs/__init__.py create mode 100644 src/finch/types/jobs/automated_async_job.py create mode 100644 src/finch/types/jobs/automated_create_params.py create mode 100644 src/finch/types/jobs/automated_create_response.py create mode 100644 src/finch/types/jobs/automated_list_params.py create mode 100644 src/finch/types/jobs/manual_async_job.py rename src/finch/types/{ => shared}/paging.py (91%) create mode 100644 src/finch/types/shared_params/paging.py create mode 100644 tests/api_resources/jobs/__init__.py create mode 100644 tests/api_resources/jobs/test_automated.py create mode 100644 tests/api_resources/jobs/test_manual.py diff --git a/.stats.yml b/.stats.yml index a78b7ab0..c125dfb2 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 19 +configured_endpoints: 23 diff --git a/api.md b/api.md index 0fb9d472..05326915 100644 --- a/api.md +++ b/api.md @@ -1,7 +1,7 @@ # Shared Types ```python -from finch.types import OperationSupport, OperationSupportMatrix +from finch.types import OperationSupport, OperationSupportMatrix, Paging ``` # Finch @@ -16,7 +16,7 @@ Methods: Types: ```python -from finch.types import Income, Location, Money, Paging +from finch.types import Income, Location, Money ``` ## CompanyResource @@ -186,3 +186,31 @@ from finch.types import RequestForwardingForwardResponse Methods: - client.request_forwarding.forward(\*\*params) -> RequestForwardingForwardResponse + +# Jobs + +## Automated + +Types: + +```python +from finch.types.jobs import AutomatedAsyncJob, AutomatedCreateResponse +``` + +Methods: + +- client.jobs.automated.create(\*\*params) -> AutomatedCreateResponse +- client.jobs.automated.retrieve(job_id) -> AutomatedAsyncJob +- client.jobs.automated.list(\*\*params) -> SyncPage[AutomatedAsyncJob] + +## Manual + +Types: + +```python +from finch.types.jobs import ManualAsyncJob +``` + +Methods: + +- client.jobs.manual.retrieve(job_id) -> ManualAsyncJob diff --git a/src/finch/_client.py b/src/finch/_client.py index d5579f9f..149406a0 100644 --- a/src/finch/_client.py +++ b/src/finch/_client.py @@ -53,6 +53,7 @@ class Finch(SyncAPIClient): account: resources.Account webhooks: resources.Webhooks request_forwarding: resources.RequestForwarding + jobs: resources.Jobs with_raw_response: FinchWithRawResponse # client options @@ -136,6 +137,7 @@ def __init__( self.account = resources.Account(self) self.webhooks = resources.Webhooks(self) self.request_forwarding = resources.RequestForwarding(self) + self.jobs = resources.Jobs(self) self.with_raw_response = FinchWithRawResponse(self) @property @@ -357,6 +359,7 @@ class AsyncFinch(AsyncAPIClient): account: resources.AsyncAccount webhooks: resources.AsyncWebhooks request_forwarding: resources.AsyncRequestForwarding + jobs: resources.AsyncJobs with_raw_response: AsyncFinchWithRawResponse # client options @@ -440,6 +443,7 @@ def __init__( self.account = resources.AsyncAccount(self) self.webhooks = resources.AsyncWebhooks(self) self.request_forwarding = resources.AsyncRequestForwarding(self) + self.jobs = resources.AsyncJobs(self) self.with_raw_response = AsyncFinchWithRawResponse(self) @property @@ -664,6 +668,7 @@ def __init__(self, client: Finch) -> None: self.providers = resources.ProvidersWithRawResponse(client.providers) self.account = resources.AccountWithRawResponse(client.account) self.request_forwarding = resources.RequestForwardingWithRawResponse(client.request_forwarding) + self.jobs = resources.JobsWithRawResponse(client.jobs) class AsyncFinchWithRawResponse: @@ -672,6 +677,7 @@ def __init__(self, client: AsyncFinch) -> None: self.providers = resources.AsyncProvidersWithRawResponse(client.providers) self.account = resources.AsyncAccountWithRawResponse(client.account) self.request_forwarding = resources.AsyncRequestForwardingWithRawResponse(client.request_forwarding) + self.jobs = resources.AsyncJobsWithRawResponse(client.jobs) Client = Finch diff --git a/src/finch/pagination.py b/src/finch/pagination.py index 3cb9ed96..38caea48 100644 --- a/src/finch/pagination.py +++ b/src/finch/pagination.py @@ -5,11 +5,11 @@ from httpx import Response -from .types import Paging from ._types import ModelT from ._utils import is_mapping from ._models import BaseModel from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage +from .types.shared import Paging __all__ = [ "SyncSinglePage", @@ -18,6 +18,8 @@ "AsyncResponsesPage", "SyncIndividualsPage", "AsyncIndividualsPage", + "SyncPage", + "AsyncPage", ] _BaseModelT = TypeVar("_BaseModelT", bound=BaseModel) @@ -161,3 +163,61 @@ def next_page_info(self) -> Optional[PageInfo]: return PageInfo(params={"offset": current_count}) return None + + +PagePaging = Paging +"""This is deprecated, Paging should be used instead""" + + +class SyncPage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): + paging: Paging + data: List[ModelT] + + @override + def _get_page_items(self) -> List[ModelT]: + return self.data + + @override + def next_page_info(self) -> Optional[PageInfo]: + offset = self.paging.offset + if offset is None: + return None + + length = len(self.data) + current_count = offset + length + + total_count = self.paging.count + if total_count is None: + return None + + if current_count < total_count: + return PageInfo(params={"offset": current_count}) + + return None + + +class AsyncPage(BaseAsyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): + paging: Paging + data: List[ModelT] + + @override + def _get_page_items(self) -> List[ModelT]: + return self.data + + @override + def next_page_info(self) -> Optional[PageInfo]: + offset = self.paging.offset + if offset is None: + return None + + length = len(self.data) + current_count = offset + length + + total_count = self.paging.count + if total_count is None: + return None + + if current_count < total_count: + return PageInfo(params={"offset": current_count}) + + return None diff --git a/src/finch/resources/__init__.py b/src/finch/resources/__init__.py index b87ddc17..dcab5333 100644 --- a/src/finch/resources/__init__.py +++ b/src/finch/resources/__init__.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from .hris import HRIS, AsyncHRIS, HRISWithRawResponse, AsyncHRISWithRawResponse +from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse from .account import ( Account, AsyncAccount, @@ -40,4 +41,8 @@ "AsyncRequestForwarding", "RequestForwardingWithRawResponse", "AsyncRequestForwardingWithRawResponse", + "Jobs", + "AsyncJobs", + "JobsWithRawResponse", + "AsyncJobsWithRawResponse", ] diff --git a/src/finch/resources/jobs/__init__.py b/src/finch/resources/jobs/__init__.py new file mode 100644 index 00000000..f1f7692d --- /dev/null +++ b/src/finch/resources/jobs/__init__.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. + +from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse +from .manual import ( + Manual, + AsyncManual, + ManualWithRawResponse, + AsyncManualWithRawResponse, +) +from .automated import ( + Automated, + AsyncAutomated, + AutomatedWithRawResponse, + AsyncAutomatedWithRawResponse, +) + +__all__ = [ + "Automated", + "AsyncAutomated", + "AutomatedWithRawResponse", + "AsyncAutomatedWithRawResponse", + "Manual", + "AsyncManual", + "ManualWithRawResponse", + "AsyncManualWithRawResponse", + "Jobs", + "AsyncJobs", + "JobsWithRawResponse", + "AsyncJobsWithRawResponse", +] diff --git a/src/finch/resources/jobs/automated.py b/src/finch/resources/jobs/automated.py new file mode 100644 index 00000000..9f43c60a --- /dev/null +++ b/src/finch/resources/jobs/automated.py @@ -0,0 +1,318 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING +from typing_extensions import Literal + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import maybe_transform +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ...pagination import SyncPage, AsyncPage +from ...types.jobs import ( + AutomatedAsyncJob, + AutomatedCreateResponse, + automated_list_params, + automated_create_params, +) +from ..._base_client import AsyncPaginator, make_request_options + +if TYPE_CHECKING: + from ..._client import Finch, AsyncFinch + +__all__ = ["Automated", "AsyncAutomated"] + + +class Automated(SyncAPIResource): + with_raw_response: AutomatedWithRawResponse + + def __init__(self, client: Finch) -> None: + super().__init__(client) + self.with_raw_response = AutomatedWithRawResponse(self) + + def create( + self, + *, + type: Literal["data_sync_all"], + # 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, + ) -> AutomatedCreateResponse: + """Enqueue an automated job. + + Currently, only the `data_sync_all` job type is + supported, which will enqueue a job to re-sync all data for a connection. + `data_sync_all` has a concurrency limit of 1 job at a time per connection. This + means that if this endpoint is called while a job is already in progress for + this connection, Finch will return the `job_id` of the job that is currently in + progress. Finch allows a fixed window rate limit of 1 forced refresh per hour + per connection. + + This endpoint is available for _Scale_ tier customers as an add-on. To request + access to this endpoint, please contact your Finch account manager. + + Args: + type: The type of job to start. Currently the only supported type is `data_sync_all` + + 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 + """ + return self._post( + "/jobs/automated", + body=maybe_transform({"type": type}, automated_create_params.AutomatedCreateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AutomatedCreateResponse, + ) + + def retrieve( + self, + job_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, + ) -> AutomatedAsyncJob: + """ + Get an automated job by `job_id`. + + Args: + 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 + """ + return self._get( + f"/jobs/automated/{job_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AutomatedAsyncJob, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + offset: int | 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, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncPage[AutomatedAsyncJob]: + """Get all automated jobs. + + Automated jobs are completed by a machine. By default, + jobs are sorted in descending order by submission time. For scheduled jobs such + as data syncs, only the next scheduled job is shown. + + Args: + limit: Number of items to return + + offset: Index to start from (defaults to 0) + + 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 + """ + return self._get_api_list( + "/jobs/automated", + page=SyncPage[AutomatedAsyncJob], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "offset": offset, + }, + automated_list_params.AutomatedListParams, + ), + ), + model=AutomatedAsyncJob, + ) + + +class AsyncAutomated(AsyncAPIResource): + with_raw_response: AsyncAutomatedWithRawResponse + + def __init__(self, client: AsyncFinch) -> None: + super().__init__(client) + self.with_raw_response = AsyncAutomatedWithRawResponse(self) + + async def create( + self, + *, + type: Literal["data_sync_all"], + # 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, + ) -> AutomatedCreateResponse: + """Enqueue an automated job. + + Currently, only the `data_sync_all` job type is + supported, which will enqueue a job to re-sync all data for a connection. + `data_sync_all` has a concurrency limit of 1 job at a time per connection. This + means that if this endpoint is called while a job is already in progress for + this connection, Finch will return the `job_id` of the job that is currently in + progress. Finch allows a fixed window rate limit of 1 forced refresh per hour + per connection. + + This endpoint is available for _Scale_ tier customers as an add-on. To request + access to this endpoint, please contact your Finch account manager. + + Args: + type: The type of job to start. Currently the only supported type is `data_sync_all` + + 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 + """ + return await self._post( + "/jobs/automated", + body=maybe_transform({"type": type}, automated_create_params.AutomatedCreateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AutomatedCreateResponse, + ) + + async def retrieve( + self, + job_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, + ) -> AutomatedAsyncJob: + """ + Get an automated job by `job_id`. + + Args: + 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 + """ + return await self._get( + f"/jobs/automated/{job_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AutomatedAsyncJob, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + offset: int | 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, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[AutomatedAsyncJob, AsyncPage[AutomatedAsyncJob]]: + """Get all automated jobs. + + Automated jobs are completed by a machine. By default, + jobs are sorted in descending order by submission time. For scheduled jobs such + as data syncs, only the next scheduled job is shown. + + Args: + limit: Number of items to return + + offset: Index to start from (defaults to 0) + + 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 + """ + return self._get_api_list( + "/jobs/automated", + page=AsyncPage[AutomatedAsyncJob], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "offset": offset, + }, + automated_list_params.AutomatedListParams, + ), + ), + model=AutomatedAsyncJob, + ) + + +class AutomatedWithRawResponse: + def __init__(self, automated: Automated) -> None: + self.create = to_raw_response_wrapper( + automated.create, + ) + self.retrieve = to_raw_response_wrapper( + automated.retrieve, + ) + self.list = to_raw_response_wrapper( + automated.list, + ) + + +class AsyncAutomatedWithRawResponse: + def __init__(self, automated: AsyncAutomated) -> None: + self.create = async_to_raw_response_wrapper( + automated.create, + ) + self.retrieve = async_to_raw_response_wrapper( + automated.retrieve, + ) + self.list = async_to_raw_response_wrapper( + automated.list, + ) diff --git a/src/finch/resources/jobs/jobs.py b/src/finch/resources/jobs/jobs.py new file mode 100644 index 00000000..f522c9c5 --- /dev/null +++ b/src/finch/resources/jobs/jobs.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from .manual import ( + Manual, + AsyncManual, + ManualWithRawResponse, + AsyncManualWithRawResponse, +) +from .automated import ( + Automated, + AsyncAutomated, + AutomatedWithRawResponse, + AsyncAutomatedWithRawResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource + +if TYPE_CHECKING: + from ..._client import Finch, AsyncFinch + +__all__ = ["Jobs", "AsyncJobs"] + + +class Jobs(SyncAPIResource): + automated: Automated + manual: Manual + with_raw_response: JobsWithRawResponse + + def __init__(self, client: Finch) -> None: + super().__init__(client) + self.automated = Automated(client) + self.manual = Manual(client) + self.with_raw_response = JobsWithRawResponse(self) + + +class AsyncJobs(AsyncAPIResource): + automated: AsyncAutomated + manual: AsyncManual + with_raw_response: AsyncJobsWithRawResponse + + def __init__(self, client: AsyncFinch) -> None: + super().__init__(client) + self.automated = AsyncAutomated(client) + self.manual = AsyncManual(client) + self.with_raw_response = AsyncJobsWithRawResponse(self) + + +class JobsWithRawResponse: + def __init__(self, jobs: Jobs) -> None: + self.automated = AutomatedWithRawResponse(jobs.automated) + self.manual = ManualWithRawResponse(jobs.manual) + + +class AsyncJobsWithRawResponse: + def __init__(self, jobs: AsyncJobs) -> None: + self.automated = AsyncAutomatedWithRawResponse(jobs.automated) + self.manual = AsyncManualWithRawResponse(jobs.manual) diff --git a/src/finch/resources/jobs/manual.py b/src/finch/resources/jobs/manual.py new file mode 100644 index 00000000..9334f2da --- /dev/null +++ b/src/finch/resources/jobs/manual.py @@ -0,0 +1,114 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ...types.jobs import ManualAsyncJob +from ..._base_client import make_request_options + +if TYPE_CHECKING: + from ..._client import Finch, AsyncFinch + +__all__ = ["Manual", "AsyncManual"] + + +class Manual(SyncAPIResource): + with_raw_response: ManualWithRawResponse + + def __init__(self, client: Finch) -> None: + super().__init__(client) + self.with_raw_response = ManualWithRawResponse(self) + + def retrieve( + self, + job_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, + ) -> ManualAsyncJob: + """Get a manual job by `job_id`. + + Manual jobs are completed by a human and include + Assisted Benefits jobs. + + Args: + 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 + """ + return self._get( + f"/jobs/manual/{job_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ManualAsyncJob, + ) + + +class AsyncManual(AsyncAPIResource): + with_raw_response: AsyncManualWithRawResponse + + def __init__(self, client: AsyncFinch) -> None: + super().__init__(client) + self.with_raw_response = AsyncManualWithRawResponse(self) + + async def retrieve( + self, + job_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, + ) -> ManualAsyncJob: + """Get a manual job by `job_id`. + + Manual jobs are completed by a human and include + Assisted Benefits jobs. + + Args: + 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 + """ + return await self._get( + f"/jobs/manual/{job_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ManualAsyncJob, + ) + + +class ManualWithRawResponse: + def __init__(self, manual: Manual) -> None: + self.retrieve = to_raw_response_wrapper( + manual.retrieve, + ) + + +class AsyncManualWithRawResponse: + def __init__(self, manual: AsyncManual) -> None: + self.retrieve = async_to_raw_response_wrapper( + manual.retrieve, + ) diff --git a/src/finch/types/__init__.py b/src/finch/types/__init__.py index eee945a5..f6a15f80 100644 --- a/src/finch/types/__init__.py +++ b/src/finch/types/__init__.py @@ -4,7 +4,7 @@ from .money import Money as Money from .income import Income as Income -from .paging import Paging as Paging +from .shared import Paging as Paging from .shared import OperationSupport as OperationSupport from .shared import OperationSupportMatrix as OperationSupportMatrix from .location import Location as Location diff --git a/src/finch/types/hris/pay_statement_response_body.py b/src/finch/types/hris/pay_statement_response_body.py index 712e442d..c7563aac 100644 --- a/src/finch/types/hris/pay_statement_response_body.py +++ b/src/finch/types/hris/pay_statement_response_body.py @@ -2,7 +2,7 @@ from typing import List, Optional -from ..paging import Paging +from ..shared import Paging from ..._models import BaseModel from .pay_statement import PayStatement diff --git a/src/finch/types/jobs/__init__.py b/src/finch/types/jobs/__init__.py new file mode 100644 index 00000000..25ad24cf --- /dev/null +++ b/src/finch/types/jobs/__init__.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from .manual_async_job import ManualAsyncJob as ManualAsyncJob +from .automated_async_job import AutomatedAsyncJob as AutomatedAsyncJob +from .automated_list_params import AutomatedListParams as AutomatedListParams +from .automated_create_params import AutomatedCreateParams as AutomatedCreateParams +from .automated_create_response import ( + AutomatedCreateResponse as AutomatedCreateResponse, +) diff --git a/src/finch/types/jobs/automated_async_job.py b/src/finch/types/jobs/automated_async_job.py new file mode 100644 index 00000000..0b3e727d --- /dev/null +++ b/src/finch/types/jobs/automated_async_job.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AutomatedAsyncJob"] + + +class AutomatedAsyncJob(BaseModel): + completed_at: Optional[datetime] + """The datetime the job completed.""" + + created_at: datetime + """The datetime when the job was created. + + for scheduled jobs, this will be the initial connection time. For ad-hoc jobs, + this will be the time the creation request was received. + """ + + job_id: str + """The id of the job that has been created.""" + + job_url: str + """The url that can be used to retrieve the job status""" + + scheduled_at: Optional[datetime] + """The datetime a job is scheduled to be run. + + For scheduled jobs, this datetime can be in the future if the job has not yet + been enqueued. For ad-hoc jobs, this field will be null. + """ + + started_at: Optional[datetime] + """The datetime a job entered into the job queue.""" + + status: Literal["pending", "in_progress", "complete", "error", "reauth_error", "permissions_error"] + + type: Literal["data_sync_all"] + """Only `data_sync_all` currently supported""" diff --git a/src/finch/types/jobs/automated_create_params.py b/src/finch/types/jobs/automated_create_params.py new file mode 100644 index 00000000..ea39170f --- /dev/null +++ b/src/finch/types/jobs/automated_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["AutomatedCreateParams"] + + +class AutomatedCreateParams(TypedDict, total=False): + type: Required[Literal["data_sync_all"]] + """The type of job to start. Currently the only supported type is `data_sync_all`""" diff --git a/src/finch/types/jobs/automated_create_response.py b/src/finch/types/jobs/automated_create_response.py new file mode 100644 index 00000000..1341d52b --- /dev/null +++ b/src/finch/types/jobs/automated_create_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. + +from ..._models import BaseModel + +__all__ = ["AutomatedCreateResponse"] + + +class AutomatedCreateResponse(BaseModel): + allowed_refreshes: int + """The number of allowed refreshes per hour (per hour, fixed window)""" + + job_id: str + """The id of the job that has been created.""" + + job_url: str + """The url that can be used to retrieve the job status""" + + remaining_refreshes: int + """The number of remaining refreshes available (per hour, fixed window)""" diff --git a/src/finch/types/jobs/automated_list_params.py b/src/finch/types/jobs/automated_list_params.py new file mode 100644 index 00000000..4c24874f --- /dev/null +++ b/src/finch/types/jobs/automated_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AutomatedListParams"] + + +class AutomatedListParams(TypedDict, total=False): + limit: int + """Number of items to return""" + + offset: int + """Index to start from (defaults to 0)""" diff --git a/src/finch/types/jobs/manual_async_job.py b/src/finch/types/jobs/manual_async_job.py new file mode 100644 index 00000000..0f831fb0 --- /dev/null +++ b/src/finch/types/jobs/manual_async_job.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ManualAsyncJob"] + + +class ManualAsyncJob(BaseModel): + body: Optional[List[object]] + """Specific information about the job, such as individual statuses for batch jobs.""" + + job_id: str + + status: Literal["pending", "in_progress", "error", "complete"] diff --git a/src/finch/types/shared/__init__.py b/src/finch/types/shared/__init__.py index 3be971aa..6c699bd7 100644 --- a/src/finch/types/shared/__init__.py +++ b/src/finch/types/shared/__init__.py @@ -1,4 +1,5 @@ # File generated from our OpenAPI spec by Stainless. +from .paging import Paging as Paging from .operation_support import OperationSupport as OperationSupport from .operation_support_matrix import OperationSupportMatrix as OperationSupportMatrix diff --git a/src/finch/types/paging.py b/src/finch/types/shared/paging.py similarity index 91% rename from src/finch/types/paging.py rename to src/finch/types/shared/paging.py index 296a46f6..726478fa 100644 --- a/src/finch/types/paging.py +++ b/src/finch/types/shared/paging.py @@ -2,7 +2,7 @@ from typing import Optional -from .._models import BaseModel +from ..._models import BaseModel __all__ = ["Paging"] diff --git a/src/finch/types/shared_params/__init__.py b/src/finch/types/shared_params/__init__.py index 3be971aa..6c699bd7 100644 --- a/src/finch/types/shared_params/__init__.py +++ b/src/finch/types/shared_params/__init__.py @@ -1,4 +1,5 @@ # File generated from our OpenAPI spec by Stainless. +from .paging import Paging as Paging from .operation_support import OperationSupport as OperationSupport from .operation_support_matrix import OperationSupportMatrix as OperationSupportMatrix diff --git a/src/finch/types/shared_params/paging.py b/src/finch/types/shared_params/paging.py new file mode 100644 index 00000000..e3ea2ca9 --- /dev/null +++ b/src/finch/types/shared_params/paging.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["Paging"] + + +class Paging(TypedDict, total=False): + count: int + """The total number of elements for the entire query (not just the given page)""" + + offset: int + """The current start index of the returned list of elements""" diff --git a/tests/api_resources/jobs/__init__.py b/tests/api_resources/jobs/__init__.py new file mode 100644 index 00000000..1016754e --- /dev/null +++ b/tests/api_resources/jobs/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/jobs/test_automated.py b/tests/api_resources/jobs/test_automated.py new file mode 100644 index 00000000..ae4ff4fa --- /dev/null +++ b/tests/api_resources/jobs/test_automated.py @@ -0,0 +1,132 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from finch import Finch, AsyncFinch +from tests.utils import assert_matches_type +from finch._client import Finch, AsyncFinch +from finch.pagination import SyncPage, AsyncPage +from finch.types.jobs import AutomatedAsyncJob, AutomatedCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") +access_token = "My Access Token" + + +class TestAutomated: + strict_client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + loose_client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: Finch) -> None: + automated = client.jobs.automated.create( + type="data_sync_all", + ) + assert_matches_type(AutomatedCreateResponse, automated, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Finch) -> None: + response = client.jobs.automated.with_raw_response.create( + type="data_sync_all", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + automated = response.parse() + assert_matches_type(AutomatedCreateResponse, automated, path=["response"]) + + @parametrize + def test_method_retrieve(self, client: Finch) -> None: + automated = client.jobs.automated.retrieve( + "string", + ) + assert_matches_type(AutomatedAsyncJob, automated, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Finch) -> None: + response = client.jobs.automated.with_raw_response.retrieve( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + automated = response.parse() + assert_matches_type(AutomatedAsyncJob, automated, path=["response"]) + + @parametrize + def test_method_list(self, client: Finch) -> None: + automated = client.jobs.automated.list() + assert_matches_type(SyncPage[AutomatedAsyncJob], automated, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Finch) -> None: + automated = client.jobs.automated.list( + limit=0, + offset=0, + ) + assert_matches_type(SyncPage[AutomatedAsyncJob], automated, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Finch) -> None: + response = client.jobs.automated.with_raw_response.list() + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + automated = response.parse() + assert_matches_type(SyncPage[AutomatedAsyncJob], automated, path=["response"]) + + +class TestAsyncAutomated: + strict_client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + loose_client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncFinch) -> None: + automated = await client.jobs.automated.create( + type="data_sync_all", + ) + assert_matches_type(AutomatedCreateResponse, automated, path=["response"]) + + @parametrize + async def test_raw_response_create(self, client: AsyncFinch) -> None: + response = await client.jobs.automated.with_raw_response.create( + type="data_sync_all", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + automated = response.parse() + assert_matches_type(AutomatedCreateResponse, automated, path=["response"]) + + @parametrize + async def test_method_retrieve(self, client: AsyncFinch) -> None: + automated = await client.jobs.automated.retrieve( + "string", + ) + assert_matches_type(AutomatedAsyncJob, automated, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, client: AsyncFinch) -> None: + response = await client.jobs.automated.with_raw_response.retrieve( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + automated = response.parse() + assert_matches_type(AutomatedAsyncJob, automated, path=["response"]) + + @parametrize + async def test_method_list(self, client: AsyncFinch) -> None: + automated = await client.jobs.automated.list() + assert_matches_type(AsyncPage[AutomatedAsyncJob], automated, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, client: AsyncFinch) -> None: + automated = await client.jobs.automated.list( + limit=0, + offset=0, + ) + assert_matches_type(AsyncPage[AutomatedAsyncJob], automated, path=["response"]) + + @parametrize + async def test_raw_response_list(self, client: AsyncFinch) -> None: + response = await client.jobs.automated.with_raw_response.list() + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + automated = response.parse() + assert_matches_type(AsyncPage[AutomatedAsyncJob], automated, path=["response"]) diff --git a/tests/api_resources/jobs/test_manual.py b/tests/api_resources/jobs/test_manual.py new file mode 100644 index 00000000..9df08a02 --- /dev/null +++ b/tests/api_resources/jobs/test_manual.py @@ -0,0 +1,59 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from finch import Finch, AsyncFinch +from tests.utils import assert_matches_type +from finch._client import Finch, AsyncFinch +from finch.types.jobs import ManualAsyncJob + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") +access_token = "My Access Token" + + +class TestManual: + strict_client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + loose_client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_retrieve(self, client: Finch) -> None: + manual = client.jobs.manual.retrieve( + "string", + ) + assert_matches_type(ManualAsyncJob, manual, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Finch) -> None: + response = client.jobs.manual.with_raw_response.retrieve( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + manual = response.parse() + assert_matches_type(ManualAsyncJob, manual, path=["response"]) + + +class TestAsyncManual: + strict_client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + loose_client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_retrieve(self, client: AsyncFinch) -> None: + manual = await client.jobs.manual.retrieve( + "string", + ) + assert_matches_type(ManualAsyncJob, manual, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, client: AsyncFinch) -> None: + response = await client.jobs.manual.with_raw_response.retrieve( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + manual = response.parse() + assert_matches_type(ManualAsyncJob, manual, path=["response"]) From d354f0a501deeea8090c0e2a6200302fe40a7638 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 5 Dec 2023 16:03:19 +0000 Subject: [PATCH 11/26] feat(api): add `client_type` and `connection_type` to introspection (#211) --- src/finch/types/introspection.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/finch/types/introspection.py b/src/finch/types/introspection.py index b9d15202..c4e6178d 100644 --- a/src/finch/types/introspection.py +++ b/src/finch/types/introspection.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from typing import List +from typing_extensions import Literal from .._models import BaseModel @@ -14,9 +15,18 @@ class Introspection(BaseModel): client_id: str """The client id of the application associated with the `access_token`.""" + client_type: Literal["production", "development", "sandbox"] + """The type of application associated with a token.""" + company_id: str """The Finch uuid of the company associated with the `access_token`.""" + connection_type: Literal["provider", "finch"] + """ + The type of the connection associated with the token.
`provider` - + connection to an external provider
`finch` - finch-generated data. + """ + manual: bool """ Whether the connection associated with the `access_token` uses the Assisted From ff189d86c46bf3ac0e6900d849eea5568c6d1d40 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 6 Dec 2023 01:03:55 +0000 Subject: [PATCH 12/26] ci: remove PR title linter (#212) --- .github/workflows/lint-pr.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .github/workflows/lint-pr.yml diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml deleted file mode 100644 index 82ee9e18..00000000 --- a/.github/workflows/lint-pr.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: "Lint PR" - -on: - pull_request_target: - types: - - opened - - edited - - synchronize - -permissions: - pull-requests: read - -jobs: - pr_title: - name: Validate PR title - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' && github.repository == 'Finch-API/finch-api-python' - steps: - - uses: amannn/action-semantic-pull-request@v5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 81a6e2f22a5c5fe862c693a0b5b2affda78d88af Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 7 Dec 2023 10:09:16 +0000 Subject: [PATCH 13/26] chore(internal): reformat imports (#213) --- pyproject.toml | 37 ++++----- requirements-dev.lock | 3 +- src/finch/_client.py | 3 +- src/finch/_compat.py | 30 ++++---- src/finch/_models.py | 14 +--- src/finch/_types.py | 9 +-- src/finch/_utils/__init__.py | 76 ++++++++++--------- src/finch/_utils/_utils.py | 4 +- src/finch/resources/__init__.py | 14 +--- src/finch/resources/account.py | 12 ++- src/finch/resources/hris/__init__.py | 35 ++------- src/finch/resources/hris/benefits/__init__.py | 14 +--- src/finch/resources/hris/benefits/benefits.py | 20 +++-- .../resources/hris/benefits/individuals.py | 13 +++- src/finch/resources/hris/company.py | 12 ++- src/finch/resources/hris/directory.py | 18 ++++- src/finch/resources/hris/employments.py | 13 +++- src/finch/resources/hris/hris.py | 35 ++------- src/finch/resources/hris/individuals.py | 13 +++- src/finch/resources/hris/pay_statements.py | 13 +++- src/finch/resources/hris/payments.py | 13 +++- src/finch/resources/jobs/__init__.py | 14 +--- src/finch/resources/jobs/automated.py | 13 +++- src/finch/resources/jobs/jobs.py | 14 +--- src/finch/resources/jobs/manual.py | 12 ++- src/finch/resources/providers.py | 13 +++- src/finch/resources/request_forwarding.py | 17 ++++- src/finch/resources/webhooks.py | 8 +- src/finch/types/__init__.py | 8 +- src/finch/types/hris/__init__.py | 32 ++------ src/finch/types/hris/benefits/__init__.py | 12 +-- src/finch/types/hris/benfit_contribution.py | 2 + src/finch/types/jobs/__init__.py | 4 +- tests/utils.py | 7 +- 34 files changed, 280 insertions(+), 277 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b831f420..59fcb8e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,16 +46,16 @@ Repository = "https://github.com/Finch-API/finch-api-python" [tool.rye] managed = true dev-dependencies = [ - "pyright==1.1.332", - "mypy==1.7.1", - "black==23.3.0", - "respx==0.20.2", - "pytest==7.1.1", - "pytest-asyncio==0.21.1", - "ruff==0.0.282", - "isort==5.10.1", - "time-machine==2.9.0", - "nox==2023.4.22", + # version pins are in requirements-dev.lock + "pyright", + "mypy", + "black", + "respx", + "pytest", + "pytest-asyncio", + "ruff", + "time-machine", + "nox", "dirty-equals>=0.6.0", ] @@ -65,12 +65,10 @@ format = { chain = [ "format:black", "format:docs", "format:ruff", - "format:isort", ]} "format:black" = "black ." "format:docs" = "python bin/blacken-docs.py README.md api.md" "format:ruff" = "ruff --fix ." -"format:isort" = "isort ." "check:ruff" = "ruff ." @@ -125,16 +123,13 @@ reportImplicitOverride = true reportImportCycles = false reportPrivateUsage = false -[tool.isort] -profile = "black" -length_sort = true -extra_standard_library = ["typing_extensions"] - [tool.ruff] line-length = 120 -format = "grouped" +output-format = "grouped" target-version = "py37" select = [ + # isort + "I", # remove unused imports "F401", # bare except statements @@ -152,6 +147,12 @@ unfixable = [ ] ignore-init-module-imports = true +[tool.ruff.lint.isort] +length-sort = true +length-sort-straight = true +combine-as-imports = true +extra-standard-library = ["typing_extensions"] +known-first-party = ["finch", "tests"] [tool.ruff.per-file-ignores] "bin/**.py" = ["T201", "T203"] diff --git a/requirements-dev.lock b/requirements-dev.lock index ce154682..36843262 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -25,7 +25,6 @@ httpcore==1.0.2 httpx==0.25.2 idna==3.4 iniconfig==2.0.0 -isort==5.10.1 mypy==1.7.1 mypy-extensions==1.0.0 nodeenv==1.8.0 @@ -43,7 +42,7 @@ pytest-asyncio==0.21.1 python-dateutil==2.8.2 pytz==2023.3.post1 respx==0.20.2 -ruff==0.0.282 +ruff==0.1.7 six==1.16.0 sniffio==1.3.0 time-machine==2.9.0 diff --git a/src/finch/_client.py b/src/finch/_client.py index 149406a0..01252f1f 100644 --- a/src/finch/_client.py +++ b/src/finch/_client.py @@ -24,8 +24,7 @@ ) from ._utils import is_given, get_async_library from ._version import __version__ -from ._streaming import Stream as Stream -from ._streaming import AsyncStream as AsyncStream +from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import APIStatusError from ._base_client import ( DEFAULT_LIMITS, diff --git a/src/finch/_compat.py b/src/finch/_compat.py index 34323c9b..d95db8ed 100644 --- a/src/finch/_compat.py +++ b/src/finch/_compat.py @@ -43,21 +43,23 @@ def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 else: if PYDANTIC_V2: - from pydantic.v1.typing import get_args as get_args - from pydantic.v1.typing import is_union as is_union - from pydantic.v1.typing import get_origin as get_origin - from pydantic.v1.typing import is_typeddict as is_typeddict - from pydantic.v1.typing import is_literal_type as is_literal_type - from pydantic.v1.datetime_parse import parse_date as parse_date - from pydantic.v1.datetime_parse import parse_datetime as parse_datetime + from pydantic.v1.typing import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, + ) + from pydantic.v1.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime else: - from pydantic.typing import get_args as get_args - from pydantic.typing import is_union as is_union - from pydantic.typing import get_origin as get_origin - from pydantic.typing import is_typeddict as is_typeddict - from pydantic.typing import is_literal_type as is_literal_type - from pydantic.datetime_parse import parse_date as parse_date - from pydantic.datetime_parse import parse_datetime as parse_datetime + from pydantic.typing import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, + ) + from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime # refactored config diff --git a/src/finch/_models.py b/src/finch/_models.py index 5b8c9601..cdd44ccb 100644 --- a/src/finch/_models.py +++ b/src/finch/_models.py @@ -30,17 +30,11 @@ AnyMapping, HttpxRequestFiles, ) -from ._utils import ( - is_list, - is_given, - is_mapping, - parse_date, - parse_datetime, - strip_not_given, -) -from ._compat import PYDANTIC_V2, ConfigDict -from ._compat import GenericModel as BaseGenericModel +from ._utils import is_list, is_given, is_mapping, parse_date, parse_datetime, strip_not_given from ._compat import ( + PYDANTIC_V2, + ConfigDict, + GenericModel as BaseGenericModel, get_args, is_union, parse_obj, diff --git a/src/finch/_types.py b/src/finch/_types.py index e12f064d..7f82cf4a 100644 --- a/src/finch/_types.py +++ b/src/finch/_types.py @@ -19,14 +19,7 @@ Sequence, AsyncIterator, ) -from typing_extensions import ( - Literal, - Protocol, - TypeAlias, - TypedDict, - override, - runtime_checkable, -) +from typing_extensions import Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable import pydantic from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport diff --git a/src/finch/_utils/__init__.py b/src/finch/_utils/__init__.py index 400ca9b8..e98636c9 100644 --- a/src/finch/_utils/__init__.py +++ b/src/finch/_utils/__init__.py @@ -1,37 +1,41 @@ from ._proxy import LazyProxy as LazyProxy -from ._utils import flatten as flatten -from ._utils import is_dict as is_dict -from ._utils import is_list as is_list -from ._utils import is_given as is_given -from ._utils import is_tuple as is_tuple -from ._utils import is_mapping as is_mapping -from ._utils import is_tuple_t as is_tuple_t -from ._utils import parse_date as parse_date -from ._utils import is_sequence as is_sequence -from ._utils import coerce_float as coerce_float -from ._utils import is_list_type as is_list_type -from ._utils import is_mapping_t as is_mapping_t -from ._utils import removeprefix as removeprefix -from ._utils import removesuffix as removesuffix -from ._utils import extract_files as extract_files -from ._utils import is_sequence_t as is_sequence_t -from ._utils import is_union_type as is_union_type -from ._utils import required_args as required_args -from ._utils import coerce_boolean as coerce_boolean -from ._utils import coerce_integer as coerce_integer -from ._utils import file_from_path as file_from_path -from ._utils import parse_datetime as parse_datetime -from ._utils import strip_not_given as strip_not_given -from ._utils import deepcopy_minimal as deepcopy_minimal -from ._utils import extract_type_arg as extract_type_arg -from ._utils import is_required_type as is_required_type -from ._utils import get_async_library as get_async_library -from ._utils import is_annotated_type as is_annotated_type -from ._utils import maybe_coerce_float as maybe_coerce_float -from ._utils import get_required_header as get_required_header -from ._utils import maybe_coerce_boolean as maybe_coerce_boolean -from ._utils import maybe_coerce_integer as maybe_coerce_integer -from ._utils import strip_annotated_type as strip_annotated_type -from ._transform import PropertyInfo as PropertyInfo -from ._transform import transform as transform -from ._transform import maybe_transform as maybe_transform +from ._utils import ( + flatten as flatten, + is_dict as is_dict, + is_list as is_list, + is_given as is_given, + is_tuple as is_tuple, + is_mapping as is_mapping, + is_tuple_t as is_tuple_t, + parse_date as parse_date, + is_sequence as is_sequence, + coerce_float as coerce_float, + is_list_type as is_list_type, + is_mapping_t as is_mapping_t, + removeprefix as removeprefix, + removesuffix as removesuffix, + extract_files as extract_files, + is_sequence_t as is_sequence_t, + is_union_type as is_union_type, + required_args as required_args, + coerce_boolean as coerce_boolean, + coerce_integer as coerce_integer, + file_from_path as file_from_path, + parse_datetime as parse_datetime, + strip_not_given as strip_not_given, + deepcopy_minimal as deepcopy_minimal, + extract_type_arg as extract_type_arg, + is_required_type as is_required_type, + get_async_library as get_async_library, + is_annotated_type as is_annotated_type, + maybe_coerce_float as maybe_coerce_float, + get_required_header as get_required_header, + maybe_coerce_boolean as maybe_coerce_boolean, + maybe_coerce_integer as maybe_coerce_integer, + strip_annotated_type as strip_annotated_type, +) +from ._transform import ( + PropertyInfo as PropertyInfo, + transform as transform, + maybe_transform as maybe_transform, +) diff --git a/src/finch/_utils/_utils.py b/src/finch/_utils/_utils.py index 83f88cc3..cce69238 100644 --- a/src/finch/_utils/_utils.py +++ b/src/finch/_utils/_utils.py @@ -21,9 +21,7 @@ import sniffio from .._types import Headers, NotGiven, FileTypes, NotGivenOr, HeadersLike -from .._compat import is_union as _is_union -from .._compat import parse_date as parse_date -from .._compat import parse_datetime as parse_datetime +from .._compat import is_union as _is_union, parse_date as parse_date, parse_datetime as parse_datetime _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) diff --git a/src/finch/resources/__init__.py b/src/finch/resources/__init__.py index dcab5333..32d0cf28 100644 --- a/src/finch/resources/__init__.py +++ b/src/finch/resources/__init__.py @@ -2,19 +2,9 @@ from .hris import HRIS, AsyncHRIS, HRISWithRawResponse, AsyncHRISWithRawResponse from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse -from .account import ( - Account, - AsyncAccount, - AccountWithRawResponse, - AsyncAccountWithRawResponse, -) +from .account import Account, AsyncAccount, AccountWithRawResponse, AsyncAccountWithRawResponse from .webhooks import Webhooks, AsyncWebhooks -from .providers import ( - Providers, - AsyncProviders, - ProvidersWithRawResponse, - AsyncProvidersWithRawResponse, -) +from .providers import Providers, AsyncProviders, ProvidersWithRawResponse, AsyncProvidersWithRawResponse from .request_forwarding import ( RequestForwarding, AsyncRequestForwarding, diff --git a/src/finch/resources/account.py b/src/finch/resources/account.py index 5d90dfdc..0cb7b3aa 100644 --- a/src/finch/resources/account.py +++ b/src/finch/resources/account.py @@ -7,10 +7,18 @@ import httpx from ..types import Introspection, DisconnectResponse -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import make_request_options +from .._base_client import ( + make_request_options, +) if TYPE_CHECKING: from .._client import Finch, AsyncFinch diff --git a/src/finch/resources/hris/__init__.py b/src/finch/resources/hris/__init__.py index 4659abb9..8f292e8f 100644 --- a/src/finch/resources/hris/__init__.py +++ b/src/finch/resources/hris/__init__.py @@ -7,36 +7,11 @@ CompanyResourceWithRawResponse, AsyncCompanyResourceWithRawResponse, ) -from .benefits import ( - Benefits, - AsyncBenefits, - BenefitsWithRawResponse, - AsyncBenefitsWithRawResponse, -) -from .payments import ( - Payments, - AsyncPayments, - PaymentsWithRawResponse, - AsyncPaymentsWithRawResponse, -) -from .directory import ( - Directory, - AsyncDirectory, - DirectoryWithRawResponse, - AsyncDirectoryWithRawResponse, -) -from .employments import ( - Employments, - AsyncEmployments, - EmploymentsWithRawResponse, - AsyncEmploymentsWithRawResponse, -) -from .individuals import ( - Individuals, - AsyncIndividuals, - IndividualsWithRawResponse, - AsyncIndividualsWithRawResponse, -) +from .benefits import Benefits, AsyncBenefits, BenefitsWithRawResponse, AsyncBenefitsWithRawResponse +from .payments import Payments, AsyncPayments, PaymentsWithRawResponse, AsyncPaymentsWithRawResponse +from .directory import Directory, AsyncDirectory, DirectoryWithRawResponse, AsyncDirectoryWithRawResponse +from .employments import Employments, AsyncEmployments, EmploymentsWithRawResponse, AsyncEmploymentsWithRawResponse +from .individuals import Individuals, AsyncIndividuals, IndividualsWithRawResponse, AsyncIndividualsWithRawResponse from .pay_statements import ( PayStatements, AsyncPayStatements, diff --git a/src/finch/resources/hris/benefits/__init__.py b/src/finch/resources/hris/benefits/__init__.py index fbbe99ea..9a5b9063 100644 --- a/src/finch/resources/hris/benefits/__init__.py +++ b/src/finch/resources/hris/benefits/__init__.py @@ -1,17 +1,7 @@ # File generated from our OpenAPI spec by Stainless. -from .benefits import ( - Benefits, - AsyncBenefits, - BenefitsWithRawResponse, - AsyncBenefitsWithRawResponse, -) -from .individuals import ( - Individuals, - AsyncIndividuals, - IndividualsWithRawResponse, - AsyncIndividualsWithRawResponse, -) +from .benefits import Benefits, AsyncBenefits, BenefitsWithRawResponse, AsyncBenefitsWithRawResponse +from .individuals import Individuals, AsyncIndividuals, IndividualsWithRawResponse, AsyncIndividualsWithRawResponse __all__ = [ "Individuals", diff --git a/src/finch/resources/hris/benefits/benefits.py b/src/finch/resources/hris/benefits/benefits.py index f90a5100..4752a838 100644 --- a/src/finch/resources/hris/benefits/benefits.py +++ b/src/finch/resources/hris/benefits/benefits.py @@ -6,14 +6,15 @@ import httpx -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import maybe_transform -from .individuals import ( - Individuals, - AsyncIndividuals, - IndividualsWithRawResponse, - AsyncIndividualsWithRawResponse, +from ...._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, ) +from ...._utils import maybe_transform +from .individuals import Individuals, AsyncIndividuals, IndividualsWithRawResponse, AsyncIndividualsWithRawResponse from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ....pagination import SyncSinglePage, AsyncSinglePage @@ -27,7 +28,10 @@ benefit_create_params, benefit_update_params, ) -from ...._base_client import AsyncPaginator, make_request_options +from ...._base_client import ( + AsyncPaginator, + make_request_options, +) if TYPE_CHECKING: from ...._client import Finch, AsyncFinch diff --git a/src/finch/resources/hris/benefits/individuals.py b/src/finch/resources/hris/benefits/individuals.py index fb4b3450..cb09ddf4 100644 --- a/src/finch/resources/hris/benefits/individuals.py +++ b/src/finch/resources/hris/benefits/individuals.py @@ -6,12 +6,21 @@ import httpx -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ...._utils import maybe_transform from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ....pagination import SyncSinglePage, AsyncSinglePage -from ...._base_client import AsyncPaginator, make_request_options +from ...._base_client import ( + AsyncPaginator, + make_request_options, +) from ....types.hris.benefits import ( IndividualBenefit, EnrolledIndividual, diff --git a/src/finch/resources/hris/company.py b/src/finch/resources/hris/company.py index af6d6441..3ee67da7 100644 --- a/src/finch/resources/hris/company.py +++ b/src/finch/resources/hris/company.py @@ -6,11 +6,19 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.hris import Company -from ..._base_client import make_request_options +from ..._base_client import ( + make_request_options, +) if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/hris/directory.py b/src/finch/resources/hris/directory.py index ff3950aa..221586f6 100644 --- a/src/finch/resources/hris/directory.py +++ b/src/finch/resources/hris/directory.py @@ -7,13 +7,25 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...pagination import SyncIndividualsPage, AsyncIndividualsPage -from ...types.hris import IndividualInDirectory, directory_list_params -from ..._base_client import AsyncPaginator, make_request_options +from ...types.hris import ( + IndividualInDirectory, + directory_list_params, +) +from ..._base_client import ( + AsyncPaginator, + make_request_options, +) if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/hris/employments.py b/src/finch/resources/hris/employments.py index 0b16202e..c0bd79ba 100644 --- a/src/finch/resources/hris/employments.py +++ b/src/finch/resources/hris/employments.py @@ -6,13 +6,22 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...pagination import SyncResponsesPage, AsyncResponsesPage from ...types.hris import EmploymentDataResponse, employment_retrieve_many_params -from ..._base_client import AsyncPaginator, make_request_options +from ..._base_client import ( + AsyncPaginator, + make_request_options, +) if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/hris/hris.py b/src/finch/resources/hris/hris.py index 8cd4f554..0ba66160 100644 --- a/src/finch/resources/hris/hris.py +++ b/src/finch/resources/hris/hris.py @@ -10,37 +10,12 @@ CompanyResourceWithRawResponse, AsyncCompanyResourceWithRawResponse, ) -from .benefits import ( - Benefits, - AsyncBenefits, - BenefitsWithRawResponse, - AsyncBenefitsWithRawResponse, -) -from .payments import ( - Payments, - AsyncPayments, - PaymentsWithRawResponse, - AsyncPaymentsWithRawResponse, -) -from .directory import ( - Directory, - AsyncDirectory, - DirectoryWithRawResponse, - AsyncDirectoryWithRawResponse, -) +from .benefits import Benefits, AsyncBenefits, BenefitsWithRawResponse, AsyncBenefitsWithRawResponse +from .payments import Payments, AsyncPayments, PaymentsWithRawResponse, AsyncPaymentsWithRawResponse +from .directory import Directory, AsyncDirectory, DirectoryWithRawResponse, AsyncDirectoryWithRawResponse from ..._resource import SyncAPIResource, AsyncAPIResource -from .employments import ( - Employments, - AsyncEmployments, - EmploymentsWithRawResponse, - AsyncEmploymentsWithRawResponse, -) -from .individuals import ( - Individuals, - AsyncIndividuals, - IndividualsWithRawResponse, - AsyncIndividualsWithRawResponse, -) +from .employments import Employments, AsyncEmployments, EmploymentsWithRawResponse, AsyncEmploymentsWithRawResponse +from .individuals import Individuals, AsyncIndividuals, IndividualsWithRawResponse, AsyncIndividualsWithRawResponse from .pay_statements import ( PayStatements, AsyncPayStatements, diff --git a/src/finch/resources/hris/individuals.py b/src/finch/resources/hris/individuals.py index 892eb820..8ca24125 100644 --- a/src/finch/resources/hris/individuals.py +++ b/src/finch/resources/hris/individuals.py @@ -6,13 +6,22 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...pagination import SyncResponsesPage, AsyncResponsesPage from ...types.hris import IndividualResponse, individual_retrieve_many_params -from ..._base_client import AsyncPaginator, make_request_options +from ..._base_client import ( + AsyncPaginator, + make_request_options, +) if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/hris/pay_statements.py b/src/finch/resources/hris/pay_statements.py index 4c444e9b..6ad4be76 100644 --- a/src/finch/resources/hris/pay_statements.py +++ b/src/finch/resources/hris/pay_statements.py @@ -6,13 +6,22 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...pagination import SyncResponsesPage, AsyncResponsesPage from ...types.hris import PayStatementResponse, pay_statement_retrieve_many_params -from ..._base_client import AsyncPaginator, make_request_options +from ..._base_client import ( + AsyncPaginator, + make_request_options, +) if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/hris/payments.py b/src/finch/resources/hris/payments.py index 17364d80..d5d0b28d 100644 --- a/src/finch/resources/hris/payments.py +++ b/src/finch/resources/hris/payments.py @@ -7,13 +7,22 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...pagination import SyncSinglePage, AsyncSinglePage from ...types.hris import Payment, payment_list_params -from ..._base_client import AsyncPaginator, make_request_options +from ..._base_client import ( + AsyncPaginator, + make_request_options, +) if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/jobs/__init__.py b/src/finch/resources/jobs/__init__.py index f1f7692d..112d3130 100644 --- a/src/finch/resources/jobs/__init__.py +++ b/src/finch/resources/jobs/__init__.py @@ -1,18 +1,8 @@ # File generated from our OpenAPI spec by Stainless. from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse -from .manual import ( - Manual, - AsyncManual, - ManualWithRawResponse, - AsyncManualWithRawResponse, -) -from .automated import ( - Automated, - AsyncAutomated, - AutomatedWithRawResponse, - AsyncAutomatedWithRawResponse, -) +from .manual import Manual, AsyncManual, ManualWithRawResponse, AsyncManualWithRawResponse +from .automated import Automated, AsyncAutomated, AutomatedWithRawResponse, AsyncAutomatedWithRawResponse __all__ = [ "Automated", diff --git a/src/finch/resources/jobs/automated.py b/src/finch/resources/jobs/automated.py index 9f43c60a..48f2199b 100644 --- a/src/finch/resources/jobs/automated.py +++ b/src/finch/resources/jobs/automated.py @@ -7,7 +7,13 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper @@ -18,7 +24,10 @@ automated_list_params, automated_create_params, ) -from ..._base_client import AsyncPaginator, make_request_options +from ..._base_client import ( + AsyncPaginator, + make_request_options, +) if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/jobs/jobs.py b/src/finch/resources/jobs/jobs.py index f522c9c5..a47c4ddd 100644 --- a/src/finch/resources/jobs/jobs.py +++ b/src/finch/resources/jobs/jobs.py @@ -4,18 +4,8 @@ from typing import TYPE_CHECKING -from .manual import ( - Manual, - AsyncManual, - ManualWithRawResponse, - AsyncManualWithRawResponse, -) -from .automated import ( - Automated, - AsyncAutomated, - AutomatedWithRawResponse, - AsyncAutomatedWithRawResponse, -) +from .manual import Manual, AsyncManual, ManualWithRawResponse, AsyncManualWithRawResponse +from .automated import Automated, AsyncAutomated, AutomatedWithRawResponse, AsyncAutomatedWithRawResponse from ..._resource import SyncAPIResource, AsyncAPIResource if TYPE_CHECKING: diff --git a/src/finch/resources/jobs/manual.py b/src/finch/resources/jobs/manual.py index 9334f2da..61f16619 100644 --- a/src/finch/resources/jobs/manual.py +++ b/src/finch/resources/jobs/manual.py @@ -6,11 +6,19 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.jobs import ManualAsyncJob -from ..._base_client import make_request_options +from ..._base_client import ( + make_request_options, +) if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/providers.py b/src/finch/resources/providers.py index aa6eedc5..5f8b17c7 100644 --- a/src/finch/resources/providers.py +++ b/src/finch/resources/providers.py @@ -7,11 +7,20 @@ import httpx from ..types import Provider -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ..pagination import SyncSinglePage, AsyncSinglePage -from .._base_client import AsyncPaginator, make_request_options +from .._base_client import ( + AsyncPaginator, + make_request_options, +) if TYPE_CHECKING: from .._client import Finch, AsyncFinch diff --git a/src/finch/resources/request_forwarding.py b/src/finch/resources/request_forwarding.py index 2464bb35..4c571b39 100644 --- a/src/finch/resources/request_forwarding.py +++ b/src/finch/resources/request_forwarding.py @@ -6,12 +6,23 @@ import httpx -from ..types import RequestForwardingForwardResponse, request_forwarding_forward_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..types import ( + RequestForwardingForwardResponse, + request_forwarding_forward_params, +) +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._utils import maybe_transform from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import make_request_options +from .._base_client import ( + make_request_options, +) if TYPE_CHECKING: from .._client import Finch, AsyncFinch diff --git a/src/finch/resources/webhooks.py b/src/finch/resources/webhooks.py index 13b88d61..2ef89a80 100644 --- a/src/finch/resources/webhooks.py +++ b/src/finch/resources/webhooks.py @@ -10,8 +10,12 @@ from typing import TYPE_CHECKING from datetime import datetime, timezone, timedelta -from .._types import HeadersLike -from .._utils import get_required_header +from .._types import ( + HeadersLike, +) +from .._utils import ( + get_required_header, +) from .._resource import SyncAPIResource, AsyncAPIResource if TYPE_CHECKING: diff --git a/src/finch/types/__init__.py b/src/finch/types/__init__.py index f6a15f80..cbf77492 100644 --- a/src/finch/types/__init__.py +++ b/src/finch/types/__init__.py @@ -4,9 +4,11 @@ from .money import Money as Money from .income import Income as Income -from .shared import Paging as Paging -from .shared import OperationSupport as OperationSupport -from .shared import OperationSupportMatrix as OperationSupportMatrix +from .shared import ( + Paging as Paging, + OperationSupport as OperationSupport, + OperationSupportMatrix as OperationSupportMatrix, +) from .location import Location as Location from .provider import Provider as Provider from .introspection import Introspection as Introspection diff --git a/src/finch/types/hris/__init__.py b/src/finch/types/hris/__init__.py index da28d4a5..0234b44b 100644 --- a/src/finch/types/hris/__init__.py +++ b/src/finch/types/hris/__init__.py @@ -23,27 +23,11 @@ from .individual_in_directory import IndividualInDirectory as IndividualInDirectory from .employment_data_response import EmploymentDataResponse as EmploymentDataResponse from .support_per_benefit_type import SupportPerBenefitType as SupportPerBenefitType -from .pay_statement_response_body import ( - PayStatementResponseBody as PayStatementResponseBody, -) -from .benefit_features_and_operations import ( - BenefitFeaturesAndOperations as BenefitFeaturesAndOperations, -) -from .employment_retrieve_many_params import ( - EmploymentRetrieveManyParams as EmploymentRetrieveManyParams, -) -from .individual_retrieve_many_params import ( - IndividualRetrieveManyParams as IndividualRetrieveManyParams, -) -from .update_company_benefit_response import ( - UpdateCompanyBenefitResponse as UpdateCompanyBenefitResponse, -) -from .create_company_benefits_response import ( - CreateCompanyBenefitsResponse as CreateCompanyBenefitsResponse, -) -from .directory_list_individuals_params import ( - DirectoryListIndividualsParams as DirectoryListIndividualsParams, -) -from .pay_statement_retrieve_many_params import ( - PayStatementRetrieveManyParams as PayStatementRetrieveManyParams, -) +from .pay_statement_response_body import PayStatementResponseBody as PayStatementResponseBody +from .benefit_features_and_operations import BenefitFeaturesAndOperations as BenefitFeaturesAndOperations +from .employment_retrieve_many_params import EmploymentRetrieveManyParams as EmploymentRetrieveManyParams +from .individual_retrieve_many_params import IndividualRetrieveManyParams as IndividualRetrieveManyParams +from .update_company_benefit_response import UpdateCompanyBenefitResponse as UpdateCompanyBenefitResponse +from .create_company_benefits_response import CreateCompanyBenefitsResponse as CreateCompanyBenefitsResponse +from .directory_list_individuals_params import DirectoryListIndividualsParams as DirectoryListIndividualsParams +from .pay_statement_retrieve_many_params import PayStatementRetrieveManyParams as PayStatementRetrieveManyParams diff --git a/src/finch/types/hris/benefits/__init__.py b/src/finch/types/hris/benefits/__init__.py index dc686ea2..0d548269 100644 --- a/src/finch/types/hris/benefits/__init__.py +++ b/src/finch/types/hris/benefits/__init__.py @@ -5,15 +5,9 @@ from .individual_benefit import IndividualBenefit as IndividualBenefit from .enrolled_individual import EnrolledIndividual as EnrolledIndividual from .unenrolled_individual import UnenrolledIndividual as UnenrolledIndividual -from .individual_enroll_many_params import ( - IndividualEnrollManyParams as IndividualEnrollManyParams, -) -from .individual_unenroll_many_params import ( - IndividualUnenrollManyParams as IndividualUnenrollManyParams, -) -from .individual_enrolled_ids_response import ( - IndividualEnrolledIDsResponse as IndividualEnrolledIDsResponse, -) +from .individual_enroll_many_params import IndividualEnrollManyParams as IndividualEnrollManyParams +from .individual_unenroll_many_params import IndividualUnenrollManyParams as IndividualUnenrollManyParams +from .individual_enrolled_ids_response import IndividualEnrolledIDsResponse as IndividualEnrolledIDsResponse from .individual_retrieve_many_benefits_params import ( IndividualRetrieveManyBenefitsParams as IndividualRetrieveManyBenefitsParams, ) diff --git a/src/finch/types/hris/benfit_contribution.py b/src/finch/types/hris/benfit_contribution.py index 676c3b8e..b3bee5cb 100644 --- a/src/finch/types/hris/benfit_contribution.py +++ b/src/finch/types/hris/benfit_contribution.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. + + from .benefit_contribution import BenefitContribution BenfitContribution = BenefitContribution diff --git a/src/finch/types/jobs/__init__.py b/src/finch/types/jobs/__init__.py index 25ad24cf..b0470846 100644 --- a/src/finch/types/jobs/__init__.py +++ b/src/finch/types/jobs/__init__.py @@ -6,6 +6,4 @@ from .automated_async_job import AutomatedAsyncJob as AutomatedAsyncJob from .automated_list_params import AutomatedListParams as AutomatedListParams from .automated_create_params import AutomatedCreateParams as AutomatedCreateParams -from .automated_create_response import ( - AutomatedCreateResponse as AutomatedCreateResponse, -) +from .automated_create_response import AutomatedCreateResponse as AutomatedCreateResponse diff --git a/tests/utils.py b/tests/utils.py index bc62203d..2dc9c0da 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -8,7 +8,12 @@ from typing_extensions import Literal, get_args, get_origin, assert_type from finch._types import NoneType -from finch._utils import is_dict, is_list, is_list_type, is_union_type +from finch._utils import ( + is_dict, + is_list, + is_list_type, + is_union_type, +) from finch._compat import PYDANTIC_V2, field_outer_type, get_model_fields from finch._models import BaseModel From cd5253c9a8417eec88dc8c30fdfa817ec1519a9e Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 7 Dec 2023 10:56:43 +0000 Subject: [PATCH 14/26] chore(internal): update formatting (#214) --- src/finch/_client.py | 5 ++++- src/finch/types/__init__.py | 8 ++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/finch/_client.py b/src/finch/_client.py index 01252f1f..3c3b9636 100644 --- a/src/finch/_client.py +++ b/src/finch/_client.py @@ -22,7 +22,10 @@ AsyncTransport, RequestOptions, ) -from ._utils import is_given, get_async_library +from ._utils import ( + is_given, + get_async_library, +) from ._version import __version__ from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import APIStatusError diff --git a/src/finch/types/__init__.py b/src/finch/types/__init__.py index cbf77492..ece2573d 100644 --- a/src/finch/types/__init__.py +++ b/src/finch/types/__init__.py @@ -13,9 +13,5 @@ from .provider import Provider as Provider from .introspection import Introspection as Introspection from .disconnect_response import DisconnectResponse as DisconnectResponse -from .request_forwarding_forward_params import ( - RequestForwardingForwardParams as RequestForwardingForwardParams, -) -from .request_forwarding_forward_response import ( - RequestForwardingForwardResponse as RequestForwardingForwardResponse, -) +from .request_forwarding_forward_params import RequestForwardingForwardParams as RequestForwardingForwardParams +from .request_forwarding_forward_response import RequestForwardingForwardResponse as RequestForwardingForwardResponse From 9adf490d6b23ddb642eb630be867690a8f8a8693 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 7 Dec 2023 14:35:20 +0000 Subject: [PATCH 15/26] feat(pagination): remove unused types (#215) --- src/finch/pagination.py | 108 ++++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 38 deletions(-) diff --git a/src/finch/pagination.py b/src/finch/pagination.py index 38caea48..cd513eb1 100644 --- a/src/finch/pagination.py +++ b/src/finch/pagination.py @@ -30,7 +30,10 @@ class SyncSinglePage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): @override def _get_page_items(self) -> List[ModelT]: - return self.items + items = self.items + if not items: + return [] + return items @override def next_page_info(self) -> None: @@ -55,7 +58,10 @@ class AsyncSinglePage(BaseAsyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): @override def _get_page_items(self) -> List[ModelT]: - return self.items + items = self.items + if not items: + return [] + return items @override def next_page_info(self) -> None: @@ -80,7 +86,10 @@ class SyncResponsesPage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]) @override def _get_page_items(self) -> List[ModelT]: - return self.responses + responses = self.responses + if not responses: + return [] + return responses @override def next_page_info(self) -> None: @@ -96,7 +105,10 @@ class AsyncResponsesPage(BaseAsyncPage[ModelT], BasePage[ModelT], Generic[ModelT @override def _get_page_items(self) -> List[ModelT]: - return self.responses + responses = self.responses + if not responses: + return [] + return responses @override def next_page_info(self) -> None: @@ -107,32 +119,35 @@ def next_page_info(self) -> None: return None -IndividualsPagePaging = Paging -"""This is deprecated, Paging should be used instead""" - - class SyncIndividualsPage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): individuals: List[ModelT] paging: Paging @override def _get_page_items(self) -> List[ModelT]: - return self.individuals + individuals = self.individuals + if not individuals: + return [] + return individuals @override def next_page_info(self) -> Optional[PageInfo]: - offset = self.paging.offset + offset = None + if self.paging is not None: # pyright: ignore[reportUnnecessaryComparison] + offset = self.paging.offset if offset is None: return None - length = len(self.individuals) + length = len(self._get_page_items()) current_count = offset + length - total_count = self.paging.count - if total_count is None: + count = None + if self.paging is not None: # pyright: ignore[reportUnnecessaryComparison] + count = self.paging.count + if count is None: return None - if current_count < total_count: + if current_count < count: return PageInfo(params={"offset": current_count}) return None @@ -144,80 +159,97 @@ class AsyncIndividualsPage(BaseAsyncPage[ModelT], BasePage[ModelT], Generic[Mode @override def _get_page_items(self) -> List[ModelT]: - return self.individuals + individuals = self.individuals + if not individuals: + return [] + return individuals @override def next_page_info(self) -> Optional[PageInfo]: - offset = self.paging.offset + offset = None + if self.paging is not None: # pyright: ignore[reportUnnecessaryComparison] + offset = self.paging.offset if offset is None: return None - length = len(self.individuals) + length = len(self._get_page_items()) current_count = offset + length - total_count = self.paging.count - if total_count is None: + count = None + if self.paging is not None: # pyright: ignore[reportUnnecessaryComparison] + count = self.paging.count + if count is None: return None - if current_count < total_count: + if current_count < count: return PageInfo(params={"offset": current_count}) return None -PagePaging = Paging -"""This is deprecated, Paging should be used instead""" - - class SyncPage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): - paging: Paging data: List[ModelT] + paging: Paging @override def _get_page_items(self) -> List[ModelT]: - return self.data + data = self.data + if not data: + return [] + return data @override def next_page_info(self) -> Optional[PageInfo]: - offset = self.paging.offset + offset = None + if self.paging is not None: # pyright: ignore[reportUnnecessaryComparison] + offset = self.paging.offset if offset is None: return None - length = len(self.data) + length = len(self._get_page_items()) current_count = offset + length - total_count = self.paging.count - if total_count is None: + count = None + if self.paging is not None: # pyright: ignore[reportUnnecessaryComparison] + count = self.paging.count + if count is None: return None - if current_count < total_count: + if current_count < count: return PageInfo(params={"offset": current_count}) return None class AsyncPage(BaseAsyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): - paging: Paging data: List[ModelT] + paging: Paging @override def _get_page_items(self) -> List[ModelT]: - return self.data + data = self.data + if not data: + return [] + return data @override def next_page_info(self) -> Optional[PageInfo]: - offset = self.paging.offset + offset = None + if self.paging is not None: # pyright: ignore[reportUnnecessaryComparison] + offset = self.paging.offset if offset is None: return None - length = len(self.data) + length = len(self._get_page_items()) current_count = offset + length - total_count = self.paging.count - if total_count is None: + count = None + if self.paging is not None: # pyright: ignore[reportUnnecessaryComparison] + count = self.paging.count + if count is None: return None - if current_count < total_count: + if current_count < count: return PageInfo(params={"offset": current_count}) return None From 14c8df00568bba59acab45ff6cdef2cb04599b43 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 7 Dec 2023 14:55:37 +0000 Subject: [PATCH 16/26] chore(internal): reformat imports (#216) --- pyproject.toml | 37 +++++---- requirements-dev.lock | 3 +- src/finch/_client.py | 8 +- src/finch/_compat.py | 30 ++++---- src/finch/_models.py | 14 +++- src/finch/_types.py | 9 ++- src/finch/_utils/__init__.py | 76 +++++++++---------- src/finch/_utils/_utils.py | 4 +- src/finch/resources/__init__.py | 14 +++- src/finch/resources/account.py | 12 +-- src/finch/resources/hris/__init__.py | 35 +++++++-- src/finch/resources/hris/benefits/__init__.py | 14 +++- src/finch/resources/hris/benefits/benefits.py | 20 ++--- .../resources/hris/benefits/individuals.py | 13 +--- src/finch/resources/hris/company.py | 12 +-- src/finch/resources/hris/directory.py | 18 +---- src/finch/resources/hris/employments.py | 13 +--- src/finch/resources/hris/hris.py | 35 +++++++-- src/finch/resources/hris/individuals.py | 13 +--- src/finch/resources/hris/pay_statements.py | 13 +--- src/finch/resources/hris/payments.py | 13 +--- src/finch/resources/jobs/__init__.py | 14 +++- src/finch/resources/jobs/automated.py | 13 +--- src/finch/resources/jobs/jobs.py | 14 +++- src/finch/resources/jobs/manual.py | 12 +-- src/finch/resources/providers.py | 13 +--- src/finch/resources/request_forwarding.py | 17 +---- src/finch/resources/webhooks.py | 8 +- src/finch/types/__init__.py | 16 ++-- src/finch/types/hris/__init__.py | 32 ++++++-- src/finch/types/hris/benefits/__init__.py | 12 ++- src/finch/types/hris/benfit_contribution.py | 2 - src/finch/types/jobs/__init__.py | 4 +- tests/utils.py | 7 +- 34 files changed, 284 insertions(+), 286 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 59fcb8e2..b831f420 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,16 +46,16 @@ Repository = "https://github.com/Finch-API/finch-api-python" [tool.rye] managed = true dev-dependencies = [ - # version pins are in requirements-dev.lock - "pyright", - "mypy", - "black", - "respx", - "pytest", - "pytest-asyncio", - "ruff", - "time-machine", - "nox", + "pyright==1.1.332", + "mypy==1.7.1", + "black==23.3.0", + "respx==0.20.2", + "pytest==7.1.1", + "pytest-asyncio==0.21.1", + "ruff==0.0.282", + "isort==5.10.1", + "time-machine==2.9.0", + "nox==2023.4.22", "dirty-equals>=0.6.0", ] @@ -65,10 +65,12 @@ format = { chain = [ "format:black", "format:docs", "format:ruff", + "format:isort", ]} "format:black" = "black ." "format:docs" = "python bin/blacken-docs.py README.md api.md" "format:ruff" = "ruff --fix ." +"format:isort" = "isort ." "check:ruff" = "ruff ." @@ -123,13 +125,16 @@ reportImplicitOverride = true reportImportCycles = false reportPrivateUsage = false +[tool.isort] +profile = "black" +length_sort = true +extra_standard_library = ["typing_extensions"] + [tool.ruff] line-length = 120 -output-format = "grouped" +format = "grouped" target-version = "py37" select = [ - # isort - "I", # remove unused imports "F401", # bare except statements @@ -147,12 +152,6 @@ unfixable = [ ] ignore-init-module-imports = true -[tool.ruff.lint.isort] -length-sort = true -length-sort-straight = true -combine-as-imports = true -extra-standard-library = ["typing_extensions"] -known-first-party = ["finch", "tests"] [tool.ruff.per-file-ignores] "bin/**.py" = ["T201", "T203"] diff --git a/requirements-dev.lock b/requirements-dev.lock index 36843262..ce154682 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -25,6 +25,7 @@ httpcore==1.0.2 httpx==0.25.2 idna==3.4 iniconfig==2.0.0 +isort==5.10.1 mypy==1.7.1 mypy-extensions==1.0.0 nodeenv==1.8.0 @@ -42,7 +43,7 @@ pytest-asyncio==0.21.1 python-dateutil==2.8.2 pytz==2023.3.post1 respx==0.20.2 -ruff==0.1.7 +ruff==0.0.282 six==1.16.0 sniffio==1.3.0 time-machine==2.9.0 diff --git a/src/finch/_client.py b/src/finch/_client.py index 3c3b9636..149406a0 100644 --- a/src/finch/_client.py +++ b/src/finch/_client.py @@ -22,12 +22,10 @@ AsyncTransport, RequestOptions, ) -from ._utils import ( - is_given, - get_async_library, -) +from ._utils import is_given, get_async_library from ._version import __version__ -from ._streaming import Stream as Stream, AsyncStream as AsyncStream +from ._streaming import Stream as Stream +from ._streaming import AsyncStream as AsyncStream from ._exceptions import APIStatusError from ._base_client import ( DEFAULT_LIMITS, diff --git a/src/finch/_compat.py b/src/finch/_compat.py index d95db8ed..34323c9b 100644 --- a/src/finch/_compat.py +++ b/src/finch/_compat.py @@ -43,23 +43,21 @@ def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 else: if PYDANTIC_V2: - from pydantic.v1.typing import ( - get_args as get_args, - is_union as is_union, - get_origin as get_origin, - is_typeddict as is_typeddict, - is_literal_type as is_literal_type, - ) - from pydantic.v1.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime + from pydantic.v1.typing import get_args as get_args + from pydantic.v1.typing import is_union as is_union + from pydantic.v1.typing import get_origin as get_origin + from pydantic.v1.typing import is_typeddict as is_typeddict + from pydantic.v1.typing import is_literal_type as is_literal_type + from pydantic.v1.datetime_parse import parse_date as parse_date + from pydantic.v1.datetime_parse import parse_datetime as parse_datetime else: - from pydantic.typing import ( - get_args as get_args, - is_union as is_union, - get_origin as get_origin, - is_typeddict as is_typeddict, - is_literal_type as is_literal_type, - ) - from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime + from pydantic.typing import get_args as get_args + from pydantic.typing import is_union as is_union + from pydantic.typing import get_origin as get_origin + from pydantic.typing import is_typeddict as is_typeddict + from pydantic.typing import is_literal_type as is_literal_type + from pydantic.datetime_parse import parse_date as parse_date + from pydantic.datetime_parse import parse_datetime as parse_datetime # refactored config diff --git a/src/finch/_models.py b/src/finch/_models.py index cdd44ccb..5b8c9601 100644 --- a/src/finch/_models.py +++ b/src/finch/_models.py @@ -30,11 +30,17 @@ AnyMapping, HttpxRequestFiles, ) -from ._utils import is_list, is_given, is_mapping, parse_date, parse_datetime, strip_not_given +from ._utils import ( + is_list, + is_given, + is_mapping, + parse_date, + parse_datetime, + strip_not_given, +) +from ._compat import PYDANTIC_V2, ConfigDict +from ._compat import GenericModel as BaseGenericModel from ._compat import ( - PYDANTIC_V2, - ConfigDict, - GenericModel as BaseGenericModel, get_args, is_union, parse_obj, diff --git a/src/finch/_types.py b/src/finch/_types.py index 7f82cf4a..e12f064d 100644 --- a/src/finch/_types.py +++ b/src/finch/_types.py @@ -19,7 +19,14 @@ Sequence, AsyncIterator, ) -from typing_extensions import Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable +from typing_extensions import ( + Literal, + Protocol, + TypeAlias, + TypedDict, + override, + runtime_checkable, +) import pydantic from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport diff --git a/src/finch/_utils/__init__.py b/src/finch/_utils/__init__.py index e98636c9..400ca9b8 100644 --- a/src/finch/_utils/__init__.py +++ b/src/finch/_utils/__init__.py @@ -1,41 +1,37 @@ from ._proxy import LazyProxy as LazyProxy -from ._utils import ( - flatten as flatten, - is_dict as is_dict, - is_list as is_list, - is_given as is_given, - is_tuple as is_tuple, - is_mapping as is_mapping, - is_tuple_t as is_tuple_t, - parse_date as parse_date, - is_sequence as is_sequence, - coerce_float as coerce_float, - is_list_type as is_list_type, - is_mapping_t as is_mapping_t, - removeprefix as removeprefix, - removesuffix as removesuffix, - extract_files as extract_files, - is_sequence_t as is_sequence_t, - is_union_type as is_union_type, - required_args as required_args, - coerce_boolean as coerce_boolean, - coerce_integer as coerce_integer, - file_from_path as file_from_path, - parse_datetime as parse_datetime, - strip_not_given as strip_not_given, - deepcopy_minimal as deepcopy_minimal, - extract_type_arg as extract_type_arg, - is_required_type as is_required_type, - get_async_library as get_async_library, - is_annotated_type as is_annotated_type, - maybe_coerce_float as maybe_coerce_float, - get_required_header as get_required_header, - maybe_coerce_boolean as maybe_coerce_boolean, - maybe_coerce_integer as maybe_coerce_integer, - strip_annotated_type as strip_annotated_type, -) -from ._transform import ( - PropertyInfo as PropertyInfo, - transform as transform, - maybe_transform as maybe_transform, -) +from ._utils import flatten as flatten +from ._utils import is_dict as is_dict +from ._utils import is_list as is_list +from ._utils import is_given as is_given +from ._utils import is_tuple as is_tuple +from ._utils import is_mapping as is_mapping +from ._utils import is_tuple_t as is_tuple_t +from ._utils import parse_date as parse_date +from ._utils import is_sequence as is_sequence +from ._utils import coerce_float as coerce_float +from ._utils import is_list_type as is_list_type +from ._utils import is_mapping_t as is_mapping_t +from ._utils import removeprefix as removeprefix +from ._utils import removesuffix as removesuffix +from ._utils import extract_files as extract_files +from ._utils import is_sequence_t as is_sequence_t +from ._utils import is_union_type as is_union_type +from ._utils import required_args as required_args +from ._utils import coerce_boolean as coerce_boolean +from ._utils import coerce_integer as coerce_integer +from ._utils import file_from_path as file_from_path +from ._utils import parse_datetime as parse_datetime +from ._utils import strip_not_given as strip_not_given +from ._utils import deepcopy_minimal as deepcopy_minimal +from ._utils import extract_type_arg as extract_type_arg +from ._utils import is_required_type as is_required_type +from ._utils import get_async_library as get_async_library +from ._utils import is_annotated_type as is_annotated_type +from ._utils import maybe_coerce_float as maybe_coerce_float +from ._utils import get_required_header as get_required_header +from ._utils import maybe_coerce_boolean as maybe_coerce_boolean +from ._utils import maybe_coerce_integer as maybe_coerce_integer +from ._utils import strip_annotated_type as strip_annotated_type +from ._transform import PropertyInfo as PropertyInfo +from ._transform import transform as transform +from ._transform import maybe_transform as maybe_transform diff --git a/src/finch/_utils/_utils.py b/src/finch/_utils/_utils.py index cce69238..83f88cc3 100644 --- a/src/finch/_utils/_utils.py +++ b/src/finch/_utils/_utils.py @@ -21,7 +21,9 @@ import sniffio from .._types import Headers, NotGiven, FileTypes, NotGivenOr, HeadersLike -from .._compat import is_union as _is_union, parse_date as parse_date, parse_datetime as parse_datetime +from .._compat import is_union as _is_union +from .._compat import parse_date as parse_date +from .._compat import parse_datetime as parse_datetime _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) diff --git a/src/finch/resources/__init__.py b/src/finch/resources/__init__.py index 32d0cf28..dcab5333 100644 --- a/src/finch/resources/__init__.py +++ b/src/finch/resources/__init__.py @@ -2,9 +2,19 @@ from .hris import HRIS, AsyncHRIS, HRISWithRawResponse, AsyncHRISWithRawResponse from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse -from .account import Account, AsyncAccount, AccountWithRawResponse, AsyncAccountWithRawResponse +from .account import ( + Account, + AsyncAccount, + AccountWithRawResponse, + AsyncAccountWithRawResponse, +) from .webhooks import Webhooks, AsyncWebhooks -from .providers import Providers, AsyncProviders, ProvidersWithRawResponse, AsyncProvidersWithRawResponse +from .providers import ( + Providers, + AsyncProviders, + ProvidersWithRawResponse, + AsyncProvidersWithRawResponse, +) from .request_forwarding import ( RequestForwarding, AsyncRequestForwarding, diff --git a/src/finch/resources/account.py b/src/finch/resources/account.py index 0cb7b3aa..5d90dfdc 100644 --- a/src/finch/resources/account.py +++ b/src/finch/resources/account.py @@ -7,18 +7,10 @@ import httpx from ..types import Introspection, DisconnectResponse -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import ( - make_request_options, -) +from .._base_client import make_request_options if TYPE_CHECKING: from .._client import Finch, AsyncFinch diff --git a/src/finch/resources/hris/__init__.py b/src/finch/resources/hris/__init__.py index 8f292e8f..4659abb9 100644 --- a/src/finch/resources/hris/__init__.py +++ b/src/finch/resources/hris/__init__.py @@ -7,11 +7,36 @@ CompanyResourceWithRawResponse, AsyncCompanyResourceWithRawResponse, ) -from .benefits import Benefits, AsyncBenefits, BenefitsWithRawResponse, AsyncBenefitsWithRawResponse -from .payments import Payments, AsyncPayments, PaymentsWithRawResponse, AsyncPaymentsWithRawResponse -from .directory import Directory, AsyncDirectory, DirectoryWithRawResponse, AsyncDirectoryWithRawResponse -from .employments import Employments, AsyncEmployments, EmploymentsWithRawResponse, AsyncEmploymentsWithRawResponse -from .individuals import Individuals, AsyncIndividuals, IndividualsWithRawResponse, AsyncIndividualsWithRawResponse +from .benefits import ( + Benefits, + AsyncBenefits, + BenefitsWithRawResponse, + AsyncBenefitsWithRawResponse, +) +from .payments import ( + Payments, + AsyncPayments, + PaymentsWithRawResponse, + AsyncPaymentsWithRawResponse, +) +from .directory import ( + Directory, + AsyncDirectory, + DirectoryWithRawResponse, + AsyncDirectoryWithRawResponse, +) +from .employments import ( + Employments, + AsyncEmployments, + EmploymentsWithRawResponse, + AsyncEmploymentsWithRawResponse, +) +from .individuals import ( + Individuals, + AsyncIndividuals, + IndividualsWithRawResponse, + AsyncIndividualsWithRawResponse, +) from .pay_statements import ( PayStatements, AsyncPayStatements, diff --git a/src/finch/resources/hris/benefits/__init__.py b/src/finch/resources/hris/benefits/__init__.py index 9a5b9063..fbbe99ea 100644 --- a/src/finch/resources/hris/benefits/__init__.py +++ b/src/finch/resources/hris/benefits/__init__.py @@ -1,7 +1,17 @@ # File generated from our OpenAPI spec by Stainless. -from .benefits import Benefits, AsyncBenefits, BenefitsWithRawResponse, AsyncBenefitsWithRawResponse -from .individuals import Individuals, AsyncIndividuals, IndividualsWithRawResponse, AsyncIndividualsWithRawResponse +from .benefits import ( + Benefits, + AsyncBenefits, + BenefitsWithRawResponse, + AsyncBenefitsWithRawResponse, +) +from .individuals import ( + Individuals, + AsyncIndividuals, + IndividualsWithRawResponse, + AsyncIndividualsWithRawResponse, +) __all__ = [ "Individuals", diff --git a/src/finch/resources/hris/benefits/benefits.py b/src/finch/resources/hris/benefits/benefits.py index 4752a838..f90a5100 100644 --- a/src/finch/resources/hris/benefits/benefits.py +++ b/src/finch/resources/hris/benefits/benefits.py @@ -6,15 +6,14 @@ import httpx -from ...._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform -from .individuals import Individuals, AsyncIndividuals, IndividualsWithRawResponse, AsyncIndividualsWithRawResponse +from .individuals import ( + Individuals, + AsyncIndividuals, + IndividualsWithRawResponse, + AsyncIndividualsWithRawResponse, +) from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ....pagination import SyncSinglePage, AsyncSinglePage @@ -28,10 +27,7 @@ benefit_create_params, benefit_update_params, ) -from ...._base_client import ( - AsyncPaginator, - make_request_options, -) +from ...._base_client import AsyncPaginator, make_request_options if TYPE_CHECKING: from ...._client import Finch, AsyncFinch diff --git a/src/finch/resources/hris/benefits/individuals.py b/src/finch/resources/hris/benefits/individuals.py index cb09ddf4..fb4b3450 100644 --- a/src/finch/resources/hris/benefits/individuals.py +++ b/src/finch/resources/hris/benefits/individuals.py @@ -6,21 +6,12 @@ import httpx -from ...._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ....pagination import SyncSinglePage, AsyncSinglePage -from ...._base_client import ( - AsyncPaginator, - make_request_options, -) +from ...._base_client import AsyncPaginator, make_request_options from ....types.hris.benefits import ( IndividualBenefit, EnrolledIndividual, diff --git a/src/finch/resources/hris/company.py b/src/finch/resources/hris/company.py index 3ee67da7..af6d6441 100644 --- a/src/finch/resources/hris/company.py +++ b/src/finch/resources/hris/company.py @@ -6,19 +6,11 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.hris import Company -from ..._base_client import ( - make_request_options, -) +from ..._base_client import make_request_options if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/hris/directory.py b/src/finch/resources/hris/directory.py index 221586f6..ff3950aa 100644 --- a/src/finch/resources/hris/directory.py +++ b/src/finch/resources/hris/directory.py @@ -7,25 +7,13 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...pagination import SyncIndividualsPage, AsyncIndividualsPage -from ...types.hris import ( - IndividualInDirectory, - directory_list_params, -) -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ...types.hris import IndividualInDirectory, directory_list_params +from ..._base_client import AsyncPaginator, make_request_options if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/hris/employments.py b/src/finch/resources/hris/employments.py index c0bd79ba..0b16202e 100644 --- a/src/finch/resources/hris/employments.py +++ b/src/finch/resources/hris/employments.py @@ -6,22 +6,13 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...pagination import SyncResponsesPage, AsyncResponsesPage from ...types.hris import EmploymentDataResponse, employment_retrieve_many_params -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ..._base_client import AsyncPaginator, make_request_options if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/hris/hris.py b/src/finch/resources/hris/hris.py index 0ba66160..8cd4f554 100644 --- a/src/finch/resources/hris/hris.py +++ b/src/finch/resources/hris/hris.py @@ -10,12 +10,37 @@ CompanyResourceWithRawResponse, AsyncCompanyResourceWithRawResponse, ) -from .benefits import Benefits, AsyncBenefits, BenefitsWithRawResponse, AsyncBenefitsWithRawResponse -from .payments import Payments, AsyncPayments, PaymentsWithRawResponse, AsyncPaymentsWithRawResponse -from .directory import Directory, AsyncDirectory, DirectoryWithRawResponse, AsyncDirectoryWithRawResponse +from .benefits import ( + Benefits, + AsyncBenefits, + BenefitsWithRawResponse, + AsyncBenefitsWithRawResponse, +) +from .payments import ( + Payments, + AsyncPayments, + PaymentsWithRawResponse, + AsyncPaymentsWithRawResponse, +) +from .directory import ( + Directory, + AsyncDirectory, + DirectoryWithRawResponse, + AsyncDirectoryWithRawResponse, +) from ..._resource import SyncAPIResource, AsyncAPIResource -from .employments import Employments, AsyncEmployments, EmploymentsWithRawResponse, AsyncEmploymentsWithRawResponse -from .individuals import Individuals, AsyncIndividuals, IndividualsWithRawResponse, AsyncIndividualsWithRawResponse +from .employments import ( + Employments, + AsyncEmployments, + EmploymentsWithRawResponse, + AsyncEmploymentsWithRawResponse, +) +from .individuals import ( + Individuals, + AsyncIndividuals, + IndividualsWithRawResponse, + AsyncIndividualsWithRawResponse, +) from .pay_statements import ( PayStatements, AsyncPayStatements, diff --git a/src/finch/resources/hris/individuals.py b/src/finch/resources/hris/individuals.py index 8ca24125..892eb820 100644 --- a/src/finch/resources/hris/individuals.py +++ b/src/finch/resources/hris/individuals.py @@ -6,22 +6,13 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...pagination import SyncResponsesPage, AsyncResponsesPage from ...types.hris import IndividualResponse, individual_retrieve_many_params -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ..._base_client import AsyncPaginator, make_request_options if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/hris/pay_statements.py b/src/finch/resources/hris/pay_statements.py index 6ad4be76..4c444e9b 100644 --- a/src/finch/resources/hris/pay_statements.py +++ b/src/finch/resources/hris/pay_statements.py @@ -6,22 +6,13 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...pagination import SyncResponsesPage, AsyncResponsesPage from ...types.hris import PayStatementResponse, pay_statement_retrieve_many_params -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ..._base_client import AsyncPaginator, make_request_options if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/hris/payments.py b/src/finch/resources/hris/payments.py index d5d0b28d..17364d80 100644 --- a/src/finch/resources/hris/payments.py +++ b/src/finch/resources/hris/payments.py @@ -7,22 +7,13 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...pagination import SyncSinglePage, AsyncSinglePage from ...types.hris import Payment, payment_list_params -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ..._base_client import AsyncPaginator, make_request_options if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/jobs/__init__.py b/src/finch/resources/jobs/__init__.py index 112d3130..f1f7692d 100644 --- a/src/finch/resources/jobs/__init__.py +++ b/src/finch/resources/jobs/__init__.py @@ -1,8 +1,18 @@ # File generated from our OpenAPI spec by Stainless. from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse -from .manual import Manual, AsyncManual, ManualWithRawResponse, AsyncManualWithRawResponse -from .automated import Automated, AsyncAutomated, AutomatedWithRawResponse, AsyncAutomatedWithRawResponse +from .manual import ( + Manual, + AsyncManual, + ManualWithRawResponse, + AsyncManualWithRawResponse, +) +from .automated import ( + Automated, + AsyncAutomated, + AutomatedWithRawResponse, + AsyncAutomatedWithRawResponse, +) __all__ = [ "Automated", diff --git a/src/finch/resources/jobs/automated.py b/src/finch/resources/jobs/automated.py index 48f2199b..9f43c60a 100644 --- a/src/finch/resources/jobs/automated.py +++ b/src/finch/resources/jobs/automated.py @@ -7,13 +7,7 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper @@ -24,10 +18,7 @@ automated_list_params, automated_create_params, ) -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ..._base_client import AsyncPaginator, make_request_options if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/jobs/jobs.py b/src/finch/resources/jobs/jobs.py index a47c4ddd..f522c9c5 100644 --- a/src/finch/resources/jobs/jobs.py +++ b/src/finch/resources/jobs/jobs.py @@ -4,8 +4,18 @@ from typing import TYPE_CHECKING -from .manual import Manual, AsyncManual, ManualWithRawResponse, AsyncManualWithRawResponse -from .automated import Automated, AsyncAutomated, AutomatedWithRawResponse, AsyncAutomatedWithRawResponse +from .manual import ( + Manual, + AsyncManual, + ManualWithRawResponse, + AsyncManualWithRawResponse, +) +from .automated import ( + Automated, + AsyncAutomated, + AutomatedWithRawResponse, + AsyncAutomatedWithRawResponse, +) from ..._resource import SyncAPIResource, AsyncAPIResource if TYPE_CHECKING: diff --git a/src/finch/resources/jobs/manual.py b/src/finch/resources/jobs/manual.py index 61f16619..9334f2da 100644 --- a/src/finch/resources/jobs/manual.py +++ b/src/finch/resources/jobs/manual.py @@ -6,19 +6,11 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.jobs import ManualAsyncJob -from ..._base_client import ( - make_request_options, -) +from ..._base_client import make_request_options if TYPE_CHECKING: from ..._client import Finch, AsyncFinch diff --git a/src/finch/resources/providers.py b/src/finch/resources/providers.py index 5f8b17c7..aa6eedc5 100644 --- a/src/finch/resources/providers.py +++ b/src/finch/resources/providers.py @@ -7,20 +7,11 @@ import httpx from ..types import Provider -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ..pagination import SyncSinglePage, AsyncSinglePage -from .._base_client import ( - AsyncPaginator, - make_request_options, -) +from .._base_client import AsyncPaginator, make_request_options if TYPE_CHECKING: from .._client import Finch, AsyncFinch diff --git a/src/finch/resources/request_forwarding.py b/src/finch/resources/request_forwarding.py index 4c571b39..2464bb35 100644 --- a/src/finch/resources/request_forwarding.py +++ b/src/finch/resources/request_forwarding.py @@ -6,23 +6,12 @@ import httpx -from ..types import ( - RequestForwardingForwardResponse, - request_forwarding_forward_params, -) -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..types import RequestForwardingForwardResponse, request_forwarding_forward_params +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import ( - make_request_options, -) +from .._base_client import make_request_options if TYPE_CHECKING: from .._client import Finch, AsyncFinch diff --git a/src/finch/resources/webhooks.py b/src/finch/resources/webhooks.py index 2ef89a80..13b88d61 100644 --- a/src/finch/resources/webhooks.py +++ b/src/finch/resources/webhooks.py @@ -10,12 +10,8 @@ from typing import TYPE_CHECKING from datetime import datetime, timezone, timedelta -from .._types import ( - HeadersLike, -) -from .._utils import ( - get_required_header, -) +from .._types import HeadersLike +from .._utils import get_required_header from .._resource import SyncAPIResource, AsyncAPIResource if TYPE_CHECKING: diff --git a/src/finch/types/__init__.py b/src/finch/types/__init__.py index ece2573d..f6a15f80 100644 --- a/src/finch/types/__init__.py +++ b/src/finch/types/__init__.py @@ -4,14 +4,16 @@ from .money import Money as Money from .income import Income as Income -from .shared import ( - Paging as Paging, - OperationSupport as OperationSupport, - OperationSupportMatrix as OperationSupportMatrix, -) +from .shared import Paging as Paging +from .shared import OperationSupport as OperationSupport +from .shared import OperationSupportMatrix as OperationSupportMatrix from .location import Location as Location from .provider import Provider as Provider from .introspection import Introspection as Introspection from .disconnect_response import DisconnectResponse as DisconnectResponse -from .request_forwarding_forward_params import RequestForwardingForwardParams as RequestForwardingForwardParams -from .request_forwarding_forward_response import RequestForwardingForwardResponse as RequestForwardingForwardResponse +from .request_forwarding_forward_params import ( + RequestForwardingForwardParams as RequestForwardingForwardParams, +) +from .request_forwarding_forward_response import ( + RequestForwardingForwardResponse as RequestForwardingForwardResponse, +) diff --git a/src/finch/types/hris/__init__.py b/src/finch/types/hris/__init__.py index 0234b44b..da28d4a5 100644 --- a/src/finch/types/hris/__init__.py +++ b/src/finch/types/hris/__init__.py @@ -23,11 +23,27 @@ from .individual_in_directory import IndividualInDirectory as IndividualInDirectory from .employment_data_response import EmploymentDataResponse as EmploymentDataResponse from .support_per_benefit_type import SupportPerBenefitType as SupportPerBenefitType -from .pay_statement_response_body import PayStatementResponseBody as PayStatementResponseBody -from .benefit_features_and_operations import BenefitFeaturesAndOperations as BenefitFeaturesAndOperations -from .employment_retrieve_many_params import EmploymentRetrieveManyParams as EmploymentRetrieveManyParams -from .individual_retrieve_many_params import IndividualRetrieveManyParams as IndividualRetrieveManyParams -from .update_company_benefit_response import UpdateCompanyBenefitResponse as UpdateCompanyBenefitResponse -from .create_company_benefits_response import CreateCompanyBenefitsResponse as CreateCompanyBenefitsResponse -from .directory_list_individuals_params import DirectoryListIndividualsParams as DirectoryListIndividualsParams -from .pay_statement_retrieve_many_params import PayStatementRetrieveManyParams as PayStatementRetrieveManyParams +from .pay_statement_response_body import ( + PayStatementResponseBody as PayStatementResponseBody, +) +from .benefit_features_and_operations import ( + BenefitFeaturesAndOperations as BenefitFeaturesAndOperations, +) +from .employment_retrieve_many_params import ( + EmploymentRetrieveManyParams as EmploymentRetrieveManyParams, +) +from .individual_retrieve_many_params import ( + IndividualRetrieveManyParams as IndividualRetrieveManyParams, +) +from .update_company_benefit_response import ( + UpdateCompanyBenefitResponse as UpdateCompanyBenefitResponse, +) +from .create_company_benefits_response import ( + CreateCompanyBenefitsResponse as CreateCompanyBenefitsResponse, +) +from .directory_list_individuals_params import ( + DirectoryListIndividualsParams as DirectoryListIndividualsParams, +) +from .pay_statement_retrieve_many_params import ( + PayStatementRetrieveManyParams as PayStatementRetrieveManyParams, +) diff --git a/src/finch/types/hris/benefits/__init__.py b/src/finch/types/hris/benefits/__init__.py index 0d548269..dc686ea2 100644 --- a/src/finch/types/hris/benefits/__init__.py +++ b/src/finch/types/hris/benefits/__init__.py @@ -5,9 +5,15 @@ from .individual_benefit import IndividualBenefit as IndividualBenefit from .enrolled_individual import EnrolledIndividual as EnrolledIndividual from .unenrolled_individual import UnenrolledIndividual as UnenrolledIndividual -from .individual_enroll_many_params import IndividualEnrollManyParams as IndividualEnrollManyParams -from .individual_unenroll_many_params import IndividualUnenrollManyParams as IndividualUnenrollManyParams -from .individual_enrolled_ids_response import IndividualEnrolledIDsResponse as IndividualEnrolledIDsResponse +from .individual_enroll_many_params import ( + IndividualEnrollManyParams as IndividualEnrollManyParams, +) +from .individual_unenroll_many_params import ( + IndividualUnenrollManyParams as IndividualUnenrollManyParams, +) +from .individual_enrolled_ids_response import ( + IndividualEnrolledIDsResponse as IndividualEnrolledIDsResponse, +) from .individual_retrieve_many_benefits_params import ( IndividualRetrieveManyBenefitsParams as IndividualRetrieveManyBenefitsParams, ) diff --git a/src/finch/types/hris/benfit_contribution.py b/src/finch/types/hris/benfit_contribution.py index b3bee5cb..676c3b8e 100644 --- a/src/finch/types/hris/benfit_contribution.py +++ b/src/finch/types/hris/benfit_contribution.py @@ -1,7 +1,5 @@ # File generated from our OpenAPI spec by Stainless. - - from .benefit_contribution import BenefitContribution BenfitContribution = BenefitContribution diff --git a/src/finch/types/jobs/__init__.py b/src/finch/types/jobs/__init__.py index b0470846..25ad24cf 100644 --- a/src/finch/types/jobs/__init__.py +++ b/src/finch/types/jobs/__init__.py @@ -6,4 +6,6 @@ from .automated_async_job import AutomatedAsyncJob as AutomatedAsyncJob from .automated_list_params import AutomatedListParams as AutomatedListParams from .automated_create_params import AutomatedCreateParams as AutomatedCreateParams -from .automated_create_response import AutomatedCreateResponse as AutomatedCreateResponse +from .automated_create_response import ( + AutomatedCreateResponse as AutomatedCreateResponse, +) diff --git a/tests/utils.py b/tests/utils.py index 2dc9c0da..bc62203d 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -8,12 +8,7 @@ from typing_extensions import Literal, get_args, get_origin, assert_type from finch._types import NoneType -from finch._utils import ( - is_dict, - is_list, - is_list_type, - is_union_type, -) +from finch._utils import is_dict, is_list, is_list_type, is_union_type from finch._compat import PYDANTIC_V2, field_outer_type, get_model_fields from finch._models import BaseModel From 61018d4794ec858953dc0a3746054dbc04807ff9 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 7 Dec 2023 18:24:09 +0000 Subject: [PATCH 17/26] chore(internal): enable more lint rules (#217) --- pyproject.toml | 31 ++++++++++++++++++++----------- requirements-dev.lock | 2 +- src/finch/__init__.py | 2 +- src/finch/_streaming.py | 4 ++-- src/finch/_types.py | 1 + src/finch/_utils/_utils.py | 8 +++++--- src/finch/resources/webhooks.py | 16 ++++++++-------- tests/test_utils/test_proxy.py | 2 +- tests/utils.py | 2 +- 9 files changed, 40 insertions(+), 28 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b831f420..31c3cd8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,17 +45,18 @@ Repository = "https://github.com/Finch-API/finch-api-python" [tool.rye] managed = true +# version pins are in requirements-dev.lock dev-dependencies = [ - "pyright==1.1.332", - "mypy==1.7.1", - "black==23.3.0", - "respx==0.20.2", - "pytest==7.1.1", - "pytest-asyncio==0.21.1", - "ruff==0.0.282", - "isort==5.10.1", - "time-machine==2.9.0", - "nox==2023.4.22", + "pyright", + "mypy", + "black", + "respx", + "pytest", + "pytest-asyncio", + "ruff", + "isort", + "time-machine", + "nox", "dirty-equals>=0.6.0", ] @@ -132,9 +133,11 @@ extra_standard_library = ["typing_extensions"] [tool.ruff] line-length = 120 -format = "grouped" +output-format = "grouped" target-version = "py37" select = [ + # bugbear rules + "B", # remove unused imports "F401", # bare except statements @@ -145,6 +148,12 @@ select = [ "T201", "T203", ] +ignore = [ + # lru_cache in methods, will be fixed separately + "B019", + # mutable defaults + "B006", +] unfixable = [ # disable auto fix for print statements "T201", diff --git a/requirements-dev.lock b/requirements-dev.lock index ce154682..2ad33ff6 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -43,7 +43,7 @@ pytest-asyncio==0.21.1 python-dateutil==2.8.2 pytz==2023.3.post1 respx==0.20.2 -ruff==0.0.282 +ruff==0.1.7 six==1.16.0 sniffio==1.3.0 time-machine==2.9.0 diff --git a/src/finch/__init__.py b/src/finch/__init__.py index c720161f..37904fae 100644 --- a/src/finch/__init__.py +++ b/src/finch/__init__.py @@ -75,7 +75,7 @@ for __name in __all__: if not __name.startswith("__"): try: - setattr(__locals[__name], "__module__", "finch") + __locals[__name].__module__ = "finch" except (TypeError, AttributeError): # Some of our exported symbols are builtins which we can't set attributes for. pass diff --git a/src/finch/_streaming.py b/src/finch/_streaming.py index 913159fd..e816ca74 100644 --- a/src/finch/_streaming.py +++ b/src/finch/_streaming.py @@ -51,7 +51,7 @@ def __stream__(self) -> Iterator[ResponseT]: yield process_data(data=sse.json(), cast_to=cast_to, response=response) # Ensure the entire stream is consumed - for sse in iterator: + for _sse in iterator: ... @@ -94,7 +94,7 @@ async def __stream__(self) -> AsyncIterator[ResponseT]: yield process_data(data=sse.json(), cast_to=cast_to, response=response) # Ensure the entire stream is consumed - async for sse in iterator: + async for _sse in iterator: ... diff --git a/src/finch/_types.py b/src/finch/_types.py index e12f064d..c4f652b4 100644 --- a/src/finch/_types.py +++ b/src/finch/_types.py @@ -44,6 +44,7 @@ class BinaryResponseContent(ABC): + @abstractmethod def __init__( self, response: Any, diff --git a/src/finch/_utils/_utils.py b/src/finch/_utils/_utils.py index 83f88cc3..c874d368 100644 --- a/src/finch/_utils/_utils.py +++ b/src/finch/_utils/_utils.py @@ -194,8 +194,8 @@ def extract_type_arg(typ: type, index: int) -> type: args = get_args(typ) try: return cast(type, args[index]) - except IndexError: - raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") + except IndexError as err: + raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") from err def deepcopy_minimal(item: _T) -> _T: @@ -275,7 +275,9 @@ def wrapper(*args: object, **kwargs: object) -> object: try: given_params.add(positional[i]) except IndexError: - raise TypeError(f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given") + raise TypeError( + f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given" + ) from None for key in kwargs.keys(): given_params.add(key) diff --git a/src/finch/resources/webhooks.py b/src/finch/resources/webhooks.py index 13b88d61..40c7678b 100644 --- a/src/finch/resources/webhooks.py +++ b/src/finch/resources/webhooks.py @@ -56,8 +56,8 @@ def verify_signature( try: parsedSecret = base64.b64decode(secret) - except Exception: - raise ValueError("Bad secret") + except Exception as err: + raise ValueError("Bad secret") from err msg_id = get_required_header(headers, "finch-event-id") msg_timestamp = get_required_header(headers, "finch-timestamp") @@ -68,8 +68,8 @@ def verify_signature( try: timestamp = datetime.fromtimestamp(float(msg_timestamp), tz=timezone.utc) - except Exception: - raise ValueError("Invalid timestamp header: " + msg_timestamp + ". Could not convert to timestamp") + except Exception as err: + raise ValueError("Invalid timestamp header: " + msg_timestamp + ". Could not convert to timestamp") from err # too old if timestamp < (now - webhook_tolerance): @@ -152,8 +152,8 @@ def verify_signature( try: parsedSecret = base64.b64decode(secret) - except Exception: - raise ValueError("Bad secret") + except Exception as err: + raise ValueError("Bad secret") from err msg_id = get_required_header(headers, "finch-event-id") msg_timestamp = get_required_header(headers, "finch-timestamp") @@ -164,8 +164,8 @@ def verify_signature( try: timestamp = datetime.fromtimestamp(float(msg_timestamp), tz=timezone.utc) - except Exception: - raise ValueError("Invalid timestamp header: " + msg_timestamp + ". Could not convert to timestamp") + except Exception as err: + raise ValueError("Invalid timestamp header: " + msg_timestamp + ". Could not convert to timestamp") from err # too old if timestamp < (now - webhook_tolerance): diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py index 4a7336a8..f5a7028d 100644 --- a/tests/test_utils/test_proxy.py +++ b/tests/test_utils/test_proxy.py @@ -19,5 +19,5 @@ def test_recursive_proxy() -> None: assert repr(proxy) == "RecursiveLazyProxy" assert str(proxy) == "RecursiveLazyProxy" assert dir(proxy) == [] - assert getattr(type(proxy), "__name__") == "RecursiveLazyProxy" + assert type(proxy).__name__ == "RecursiveLazyProxy" assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy" diff --git a/tests/utils.py b/tests/utils.py index bc62203d..3cd9cf08 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -91,7 +91,7 @@ def assert_matches_type( traceback.print_exc() continue - assert False, "Did not match any variants" + raise AssertionError("Did not match any variants") elif issubclass(origin, BaseModel): assert isinstance(value, type_) assert assert_matches_model(type_, cast(Any, value), path=path) From d0f1493c4bbbabe9fa9b7c5c3ef55588820ba87b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 7 Dec 2023 19:20:16 +0000 Subject: [PATCH 18/26] feat(api): add `lp` tax payer type enum value (#218) --- src/finch/types/hris/company.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/finch/types/hris/company.py b/src/finch/types/hris/company.py index fb0ea41f..6823a150 100644 --- a/src/finch/types/hris/company.py +++ b/src/finch/types/hris/company.py @@ -46,7 +46,9 @@ class Entity(BaseModel): subtype: Optional[Literal["s_corporation", "c_corporation", "b_corporation"]] = None """The tax payer subtype of the company.""" - type: Optional[Literal["llc", "corporation", "sole_proprietor", "non_profit", "partnership", "cooperative"]] = None + type: Optional[ + Literal["llc", "lp", "corporation", "sole_proprietor", "non_profit", "partnership", "cooperative"] + ] = None """The tax payer type of the company.""" From ee8fb3977610c5a7578353042553665a07943b42 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 8 Dec 2023 10:07:19 +0000 Subject: [PATCH 19/26] fix(errors): properly assign APIError.body (#219) --- src/finch/_exceptions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/finch/_exceptions.py b/src/finch/_exceptions.py index 8d69569f..ba7b00fa 100644 --- a/src/finch/_exceptions.py +++ b/src/finch/_exceptions.py @@ -41,6 +41,7 @@ def __init__(self, message: str, request: httpx.Request, *, body: object | None) super().__init__(message) self.request = request self.message = message + self.body = body class APIResponseValidationError(APIError): From c6347717feebc43eb69d191aeee3369aa010e0a0 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 8 Dec 2023 19:02:10 +0000 Subject: [PATCH 20/26] fix: avoid leaking memory when Client.with_options is used (#220) Fixes https://github.com/openai/openai-python/issues/865. --- pyproject.toml | 2 - src/finch/_base_client.py | 28 +++++---- src/finch/_client.py | 4 +- tests/test_client.py | 124 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 17 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 31c3cd8c..6d403b5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -149,8 +149,6 @@ select = [ "T203", ] ignore = [ - # lru_cache in methods, will be fixed separately - "B019", # mutable defaults "B006", ] diff --git a/src/finch/_base_client.py b/src/finch/_base_client.py index 2e5678e8..bbbb8a54 100644 --- a/src/finch/_base_client.py +++ b/src/finch/_base_client.py @@ -403,14 +403,12 @@ def _build_headers(self, options: FinalRequestOptions) -> httpx.Headers: headers_dict = _merge_mappings(self.default_headers, custom_headers) self._validate_headers(headers_dict, custom_headers) + # headers are case-insensitive while dictionaries are not. headers = httpx.Headers(headers_dict) idempotency_header = self._idempotency_header if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers: - if not options.idempotency_key: - options.idempotency_key = self._idempotency_key() - - headers[idempotency_header] = options.idempotency_key + headers[idempotency_header] = options.idempotency_key or self._idempotency_key() return headers @@ -594,16 +592,8 @@ def base_url(self) -> URL: def base_url(self, url: URL | str) -> None: self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url)) - @lru_cache(maxsize=None) def platform_headers(self) -> Dict[str, str]: - return { - "X-Stainless-Lang": "python", - "X-Stainless-Package-Version": self._version, - "X-Stainless-OS": str(get_platform()), - "X-Stainless-Arch": str(get_architecture()), - "X-Stainless-Runtime": platform.python_implementation(), - "X-Stainless-Runtime-Version": platform.python_version(), - } + return platform_headers(self._version) def _calculate_retry_timeout( self, @@ -1691,6 +1681,18 @@ def get_platform() -> Platform: return "Unknown" +@lru_cache(maxsize=None) +def platform_headers(version: str) -> Dict[str, str]: + return { + "X-Stainless-Lang": "python", + "X-Stainless-Package-Version": version, + "X-Stainless-OS": str(get_platform()), + "X-Stainless-Arch": str(get_architecture()), + "X-Stainless-Runtime": platform.python_implementation(), + "X-Stainless-Runtime-Version": platform.python_version(), + } + + class OtherArch: def __init__(self, name: str) -> None: self.name = name diff --git a/src/finch/_client.py b/src/finch/_client.py index 149406a0..76629545 100644 --- a/src/finch/_client.py +++ b/src/finch/_client.py @@ -236,7 +236,7 @@ def copy( client_id=client_id or self.client_id, client_secret=client_secret or self.client_secret, webhook_secret=webhook_secret or self.webhook_secret, - base_url=base_url or str(self.base_url), + base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, connection_pool_limits=connection_pool_limits, @@ -542,7 +542,7 @@ def copy( client_id=client_id or self.client_id, client_secret=client_secret or self.client_secret, webhook_secret=webhook_secret or self.webhook_secret, - base_url=base_url or str(self.base_url), + base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, connection_pool_limits=connection_pool_limits, diff --git a/tests/test_client.py b/tests/test_client.py index a37278e9..d5010a92 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -2,10 +2,12 @@ from __future__ import annotations +import gc import os import json import asyncio import inspect +import tracemalloc from typing import Any, Union, cast from unittest import mock @@ -174,6 +176,67 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" + def test_copy_build_request(self) -> None: + options = FinalRequestOptions(method="get", url="/foo") + + def build_request(options: FinalRequestOptions) -> None: + client = self.client.copy() + client._build_request(options) + + # ensure that the machinery is warmed up before tracing starts. + build_request(options) + gc.collect() + + tracemalloc.start(1000) + + snapshot_before = tracemalloc.take_snapshot() + + ITERATIONS = 10 + for _ in range(ITERATIONS): + build_request(options) + gc.collect() + + snapshot_after = tracemalloc.take_snapshot() + + tracemalloc.stop() + + def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.StatisticDiff) -> None: + if diff.count == 0: + # Avoid false positives by considering only leaks (i.e. allocations that persist). + return + + if diff.count % ITERATIONS != 0: + # Avoid false positives by considering only leaks that appear per iteration. + return + + for frame in diff.traceback: + if any( + frame.filename.endswith(fragment) + for fragment in [ + # to_raw_response_wrapper leaks through the @functools.wraps() decorator. + # + # removing the decorator fixes the leak for reasons we don't understand. + "finch/_response.py", + # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. + "finch/_compat.py", + # Standard library leaks we don't care about. + "/logging/__init__.py", + ] + ): + return + + leaks.append(diff) + + leaks: list[tracemalloc.StatisticDiff] = [] + for diff in snapshot_after.compare_to(snapshot_before, "traceback"): + add_leak(leaks, diff) + if leaks: + for leak in leaks: + print("MEMORY LEAK:", leak) + for frame in leak.traceback: + print(frame) + raise AssertionError() + def test_request_timeout(self) -> None: request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore @@ -882,6 +945,67 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" + def test_copy_build_request(self) -> None: + options = FinalRequestOptions(method="get", url="/foo") + + def build_request(options: FinalRequestOptions) -> None: + client = self.client.copy() + client._build_request(options) + + # ensure that the machinery is warmed up before tracing starts. + build_request(options) + gc.collect() + + tracemalloc.start(1000) + + snapshot_before = tracemalloc.take_snapshot() + + ITERATIONS = 10 + for _ in range(ITERATIONS): + build_request(options) + gc.collect() + + snapshot_after = tracemalloc.take_snapshot() + + tracemalloc.stop() + + def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.StatisticDiff) -> None: + if diff.count == 0: + # Avoid false positives by considering only leaks (i.e. allocations that persist). + return + + if diff.count % ITERATIONS != 0: + # Avoid false positives by considering only leaks that appear per iteration. + return + + for frame in diff.traceback: + if any( + frame.filename.endswith(fragment) + for fragment in [ + # to_raw_response_wrapper leaks through the @functools.wraps() decorator. + # + # removing the decorator fixes the leak for reasons we don't understand. + "finch/_response.py", + # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. + "finch/_compat.py", + # Standard library leaks we don't care about. + "/logging/__init__.py", + ] + ): + return + + leaks.append(diff) + + leaks: list[tracemalloc.StatisticDiff] = [] + for diff in snapshot_after.compare_to(snapshot_before, "traceback"): + add_leak(leaks, diff) + if leaks: + for leak in leaks: + print("MEMORY LEAK:", leak) + for frame in leak.traceback: + print(frame) + raise AssertionError() + async def test_request_timeout(self) -> None: request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore From 1c946dd97fc43bfdda3346311282086af8cac6cb Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 11 Dec 2023 23:54:53 +0000 Subject: [PATCH 21/26] docs: improve README timeout comment (#221) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e2b76e88..89067751 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,7 @@ from finch import Finch # Configure the default for all requests: client = Finch( - # default is 60s + # 20 seconds (default is 1 minute) timeout=20.0, ) From 7f116d2665c22e08988fb01cbbb692c8c0b79fea Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:56:08 +0000 Subject: [PATCH 22/26] refactor(client): simplify cleanup (#222) This removes Client.__del__, but users are not expected to call this directly. --- pyproject.toml | 2 +- src/finch/_base_client.py | 26 ++++++++++++++++++++------ src/finch/_client.py | 30 ++++-------------------------- tests/test_client.py | 23 ++--------------------- 4 files changed, 27 insertions(+), 54 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6d403b5a..2edff854 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,7 +81,7 @@ typecheck = { chain = [ ]} "typecheck:pyright" = "pyright" "typecheck:verify-types" = "pyright --verifytypes finch --ignoreexternal" -"typecheck:mypy" = "mypy --enable-incomplete-feature=Unpack ." +"typecheck:mypy" = "mypy ." [build-system] requires = ["hatchling"] diff --git a/src/finch/_base_client.py b/src/finch/_base_client.py index bbbb8a54..04a20bfd 100644 --- a/src/finch/_base_client.py +++ b/src/finch/_base_client.py @@ -5,6 +5,7 @@ import time import uuid import email +import asyncio import inspect import logging import platform @@ -672,9 +673,16 @@ def _idempotency_key(self) -> str: return f"stainless-python-retry-{uuid.uuid4()}" +class SyncHttpxClientWrapper(httpx.Client): + def __del__(self) -> None: + try: + self.close() + except Exception: + pass + + class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]): _client: httpx.Client - _has_custom_http_client: bool _default_stream_cls: type[Stream[Any]] | None = None def __init__( @@ -747,7 +755,7 @@ def __init__( custom_headers=custom_headers, _strict_response_validation=_strict_response_validation, ) - self._client = http_client or httpx.Client( + self._client = http_client or SyncHttpxClientWrapper( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), @@ -755,7 +763,6 @@ def __init__( transport=transport, limits=limits, ) - self._has_custom_http_client = bool(http_client) def is_closed(self) -> bool: return self._client.is_closed @@ -1135,9 +1142,17 @@ def get_api_list( return self._request_api_list(model, page, opts) +class AsyncHttpxClientWrapper(httpx.AsyncClient): + def __del__(self) -> None: + try: + # TODO(someday): support non asyncio runtimes here + asyncio.get_running_loop().create_task(self.aclose()) + except Exception: + pass + + class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]): _client: httpx.AsyncClient - _has_custom_http_client: bool _default_stream_cls: type[AsyncStream[Any]] | None = None def __init__( @@ -1210,7 +1225,7 @@ def __init__( custom_headers=custom_headers, _strict_response_validation=_strict_response_validation, ) - self._client = http_client or httpx.AsyncClient( + self._client = http_client or AsyncHttpxClientWrapper( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), @@ -1218,7 +1233,6 @@ def __init__( transport=transport, limits=limits, ) - self._has_custom_http_client = bool(http_client) def is_closed(self) -> bool: return self._client.is_closed diff --git a/src/finch/_client.py b/src/finch/_client.py index 76629545..404f7fa4 100644 --- a/src/finch/_client.py +++ b/src/finch/_client.py @@ -3,7 +3,6 @@ from __future__ import annotations import os -import asyncio from typing import Any, Union, Mapping from typing_extensions import Self, override @@ -32,6 +31,8 @@ DEFAULT_MAX_RETRIES, SyncAPIClient, AsyncAPIClient, + SyncHttpxClientWrapper, + AsyncHttpxClientWrapper, ) __all__ = [ @@ -217,7 +218,7 @@ def copy( if http_client is not None: raise ValueError("The 'http_client' argument is mutually exclusive with 'connection_pool_limits'") - if self._has_custom_http_client: + if not isinstance(self._client, SyncHttpxClientWrapper): raise ValueError( "A custom HTTP client has been set and is mutually exclusive with the 'connection_pool_limits' argument" ) @@ -250,16 +251,6 @@ def copy( # client.with_options(timeout=10).foo.create(...) with_options = copy - def __del__(self) -> None: - if not hasattr(self, "_has_custom_http_client") or not hasattr(self, "close"): - # this can happen if the '__init__' method raised an error - return - - if self._has_custom_http_client: - return - - self.close() - def get_access_token( self, code: str, @@ -523,7 +514,7 @@ def copy( if http_client is not None: raise ValueError("The 'http_client' argument is mutually exclusive with 'connection_pool_limits'") - if self._has_custom_http_client: + if not isinstance(self._client, AsyncHttpxClientWrapper): raise ValueError( "A custom HTTP client has been set and is mutually exclusive with the 'connection_pool_limits' argument" ) @@ -556,19 +547,6 @@ def copy( # client.with_options(timeout=10).foo.create(...) with_options = copy - def __del__(self) -> None: - if not hasattr(self, "_has_custom_http_client") or not hasattr(self, "close"): - # this can happen if the '__init__' method raised an error - return - - if self._has_custom_http_client: - return - - try: - asyncio.get_running_loop().create_task(self.close()) - except Exception: - pass - async def get_access_token( self, code: str, diff --git a/tests/test_client.py b/tests/test_client.py index d5010a92..d369741d 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -695,14 +695,6 @@ def test_proxies_option_mutually_exclusive_with_http_client(self) -> None: http_client=http_client, ) - def test_client_del(self) -> None: - client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=True) - assert not client.is_closed() - - client.__del__() - - assert client.is_closed() - def test_copied_client_does_not_close_http(self) -> None: client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=True) assert not client.is_closed() @@ -710,9 +702,8 @@ def test_copied_client_does_not_close_http(self) -> None: copied = client.copy() assert copied is not client - copied.__del__() + del copied - assert not copied.is_closed() assert not client.is_closed() def test_client_context_manager(self) -> None: @@ -1464,15 +1455,6 @@ async def test_proxies_option_mutually_exclusive_with_http_client(self) -> None: http_client=http_client, ) - async def test_client_del(self) -> None: - client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True) - assert not client.is_closed() - - client.__del__() - - await asyncio.sleep(0.2) - assert client.is_closed() - 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) assert not client.is_closed() @@ -1480,10 +1462,9 @@ async def test_copied_client_does_not_close_http(self) -> None: copied = client.copy() assert copied is not client - copied.__del__() + del copied await asyncio.sleep(0.2) - assert not copied.is_closed() assert not client.is_closed() async def test_client_context_manager(self) -> None: From 481dc7de11733b2493e820ff241b7999d873fd16 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 12 Dec 2023 18:16:27 -0500 Subject: [PATCH 23/26] refactor: simplify internal error handling (#223) --- src/finch/_base_client.py | 102 +++++++++++++++++--------------------- tests/test_client.py | 53 +------------------- 2 files changed, 47 insertions(+), 108 deletions(-) diff --git a/src/finch/_base_client.py b/src/finch/_base_client.py index 04a20bfd..92189617 100644 --- a/src/finch/_base_client.py +++ b/src/finch/_base_client.py @@ -873,40 +873,25 @@ def _request( request = self._build_request(options) self._prepare_request(request) - response = None - try: response = self._client.send( request, auth=self.custom_auth, stream=stream or self._should_stream_response_body(request=request), ) - log.debug( - 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase - ) - response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - if retries > 0 and self._should_retry(err.response): - err.response.close() + except httpx.TimeoutException as err: + if retries > 0: return self._retry_request( options, cast_to, retries, - err.response.headers, stream=stream, stream_cls=stream_cls, + response_headers=None, ) - # If the response is streamed then we need to explicitly read the response - # to completion before attempting to access the response text. - if not err.response.is_closed: - err.response.read() - - raise self._make_status_error_from_response(err.response) from None - except httpx.TimeoutException as err: - if response is not None: - response.close() - + raise APITimeoutError(request=request) from err + except Exception as err: if retries > 0: return self._retry_request( options, @@ -914,25 +899,35 @@ def _request( retries, stream=stream, stream_cls=stream_cls, - response_headers=response.headers if response is not None else None, + response_headers=None, ) - raise APITimeoutError(request=request) from err - except Exception as err: - if response is not None: - response.close() + raise APIConnectionError(request=request) from err - if retries > 0: + log.debug( + 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase + ) + + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + if retries > 0 and self._should_retry(err.response): + err.response.close() return self._retry_request( options, cast_to, retries, + err.response.headers, stream=stream, stream_cls=stream_cls, - response_headers=response.headers if response is not None else None, ) - raise APIConnectionError(request=request) from err + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + err.response.read() + + raise self._make_status_error_from_response(err.response) from None return self._process_response( cast_to=cast_to, @@ -1340,40 +1335,25 @@ async def _request( request = self._build_request(options) await self._prepare_request(request) - response = None - try: response = await self._client.send( request, auth=self.custom_auth, stream=stream or self._should_stream_response_body(request=request), ) - log.debug( - 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase - ) - response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - if retries > 0 and self._should_retry(err.response): - await err.response.aclose() + except httpx.TimeoutException as err: + if retries > 0: return await self._retry_request( options, cast_to, retries, - err.response.headers, stream=stream, stream_cls=stream_cls, + response_headers=None, ) - # If the response is streamed then we need to explicitly read the response - # to completion before attempting to access the response text. - if not err.response.is_closed: - await err.response.aread() - - raise self._make_status_error_from_response(err.response) from None - except httpx.TimeoutException as err: - if response is not None: - await response.aclose() - + raise APITimeoutError(request=request) from err + except Exception as err: if retries > 0: return await self._retry_request( options, @@ -1381,25 +1361,35 @@ async def _request( retries, stream=stream, stream_cls=stream_cls, - response_headers=response.headers if response is not None else None, + response_headers=None, ) - raise APITimeoutError(request=request) from err - except Exception as err: - if response is not None: - await response.aclose() + raise APIConnectionError(request=request) from err - if retries > 0: + log.debug( + 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase + ) + + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + if retries > 0 and self._should_retry(err.response): + await err.response.aclose() return await self._retry_request( options, cast_to, retries, + err.response.headers, stream=stream, stream_cls=stream_cls, - response_headers=response.headers if response is not None else None, ) - raise APIConnectionError(request=request) from err + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + await err.response.aread() + + raise self._make_status_error_from_response(err.response) from None return self._process_response( cast_to=cast_to, diff --git a/tests/test_client.py b/tests/test_client.py index d369741d..b2d64aee 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -20,7 +20,7 @@ from finch._types import Omit from finch._client import Finch, AsyncFinch from finch._models import BaseModel, FinalRequestOptions -from finch._exceptions import APIStatusError, APIResponseValidationError +from finch._exceptions import APIResponseValidationError from finch._base_client import ( DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, @@ -772,31 +772,6 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str calculated = client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] - @pytest.mark.respx(base_url=base_url) - def test_status_error_within_httpx(self, respx_mock: MockRouter) -> None: - respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - - def on_response(response: httpx.Response) -> None: - raise httpx.HTTPStatusError( - "Simulating an error inside httpx", - response=response, - request=response.request, - ) - - client = Finch( - base_url=base_url, - access_token=access_token, - _strict_response_validation=True, - http_client=httpx.Client( - event_hooks={ - "response": [on_response], - } - ), - max_retries=0, - ) - with pytest.raises(APIStatusError): - client.post("/foo", cast_to=httpx.Response) - class TestAsyncFinch: client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True) @@ -1535,29 +1510,3 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte options = FinalRequestOptions(method="get", url="/foo", max_retries=3) calculated = client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] - - @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_status_error_within_httpx(self, respx_mock: MockRouter) -> None: - respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - - def on_response(response: httpx.Response) -> None: - raise httpx.HTTPStatusError( - "Simulating an error inside httpx", - response=response, - request=response.request, - ) - - client = AsyncFinch( - base_url=base_url, - access_token=access_token, - _strict_response_validation=True, - http_client=httpx.AsyncClient( - event_hooks={ - "response": [on_response], - } - ), - max_retries=0, - ) - with pytest.raises(APIStatusError): - await client.post("/foo", cast_to=httpx.Response) From f0b3fb7fceb4cdfe42abc5e49e04de30633cc55c Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 12 Dec 2023 20:27:22 -0500 Subject: [PATCH 24/26] refactor: remove unused model types used in params (#224) --- src/finch/types/shared_params/__init__.py | 5 -- .../types/shared_params/operation_support.py | 9 --- .../shared_params/operation_support_matrix.py | 56 ------------------- src/finch/types/shared_params/paging.py | 15 ----- 4 files changed, 85 deletions(-) delete mode 100644 src/finch/types/shared_params/__init__.py delete mode 100644 src/finch/types/shared_params/operation_support.py delete mode 100644 src/finch/types/shared_params/operation_support_matrix.py delete mode 100644 src/finch/types/shared_params/paging.py diff --git a/src/finch/types/shared_params/__init__.py b/src/finch/types/shared_params/__init__.py deleted file mode 100644 index 6c699bd7..00000000 --- a/src/finch/types/shared_params/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from .paging import Paging as Paging -from .operation_support import OperationSupport as OperationSupport -from .operation_support_matrix import OperationSupportMatrix as OperationSupportMatrix diff --git a/src/finch/types/shared_params/operation_support.py b/src/finch/types/shared_params/operation_support.py deleted file mode 100644 index 290a5214..00000000 --- a/src/finch/types/shared_params/operation_support.py +++ /dev/null @@ -1,9 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing_extensions import Literal - -__all__ = ["OperationSupport"] - -OperationSupport = Literal["supported", "not_supported_by_finch", "not_supported_by_provider", "client_access_only"] diff --git a/src/finch/types/shared_params/operation_support_matrix.py b/src/finch/types/shared_params/operation_support_matrix.py deleted file mode 100644 index 4fa6df6b..00000000 --- a/src/finch/types/shared_params/operation_support_matrix.py +++ /dev/null @@ -1,56 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing_extensions import TypedDict - -from ..shared import OperationSupport -from .operation_support import OperationSupport - -__all__ = ["OperationSupportMatrix"] - - -class OperationSupportMatrix(TypedDict, total=False): - create: OperationSupport - """ - - `supported`: This operation is supported by both the provider and Finch
- - `not_supported_by_finch`: This operation is not supported by Finch but - supported by the provider
- - `not_supported_by_provider`: This operation is not supported by the provider, - so Finch cannot support
- - `client_access_only`: This behavior is supported by the provider, but only - available to the client and not to Finch - """ - - delete: OperationSupport - """ - - `supported`: This operation is supported by both the provider and Finch
- - `not_supported_by_finch`: This operation is not supported by Finch but - supported by the provider
- - `not_supported_by_provider`: This operation is not supported by the provider, - so Finch cannot support
- - `client_access_only`: This behavior is supported by the provider, but only - available to the client and not to Finch - """ - - read: OperationSupport - """ - - `supported`: This operation is supported by both the provider and Finch
- - `not_supported_by_finch`: This operation is not supported by Finch but - supported by the provider
- - `not_supported_by_provider`: This operation is not supported by the provider, - so Finch cannot support
- - `client_access_only`: This behavior is supported by the provider, but only - available to the client and not to Finch - """ - - update: OperationSupport - """ - - `supported`: This operation is supported by both the provider and Finch
- - `not_supported_by_finch`: This operation is not supported by Finch but - supported by the provider
- - `not_supported_by_provider`: This operation is not supported by the provider, - so Finch cannot support
- - `client_access_only`: This behavior is supported by the provider, but only - available to the client and not to Finch - """ diff --git a/src/finch/types/shared_params/paging.py b/src/finch/types/shared_params/paging.py deleted file mode 100644 index e3ea2ca9..00000000 --- a/src/finch/types/shared_params/paging.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["Paging"] - - -class Paging(TypedDict, total=False): - count: int - """The total number of elements for the entire query (not just the given page)""" - - offset: int - """The current start index of the returned list of elements""" From 7147466ca5ce520e88fb392bd2d84be99a03e2b7 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sat, 16 Dec 2023 19:38:10 -0500 Subject: [PATCH 25/26] chore(ci): run release workflow once per day (#226) --- .github/workflows/create-releases.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index 4d08483e..1f1c959f 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -1,5 +1,7 @@ name: Create releases on: + schedule: + - cron: '0 5 * * *' # every day at 5am UTC push: branches: - main From 2fe598125e84903f6e4094bddead231a25241e8d Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sat, 16 Dec 2023 19:38:31 -0500 Subject: [PATCH 26/26] release: 0.9.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 46 +++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/finch/_version.py | 2 +- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 34dc535b..6d78745c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.8.2" + ".": "0.9.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c9038084..e5825e3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,51 @@ # Changelog +## 0.9.0 (2023-12-17) + +Full Changelog: [v0.8.2...v0.9.0](https://github.com/Finch-API/finch-api-python/compare/v0.8.2...v0.9.0) + +### Features + +* **api:** add `/jobs` endpoints ([#210](https://github.com/Finch-API/finch-api-python/issues/210)) ([cd3cc6f](https://github.com/Finch-API/finch-api-python/commit/cd3cc6febbb46537d6694303c52fbfa266e762cd)) +* **api:** add `client_type` and `connection_type` to introspection ([#211](https://github.com/Finch-API/finch-api-python/issues/211)) ([d354f0a](https://github.com/Finch-API/finch-api-python/commit/d354f0a501deeea8090c0e2a6200302fe40a7638)) +* **api:** add `lp` tax payer type enum value ([#218](https://github.com/Finch-API/finch-api-python/issues/218)) ([d0f1493](https://github.com/Finch-API/finch-api-python/commit/d0f1493c4bbbabe9fa9b7c5c3ef55588820ba87b)) +* **pagination:** remove unused types ([#215](https://github.com/Finch-API/finch-api-python/issues/215)) ([9adf490](https://github.com/Finch-API/finch-api-python/commit/9adf490d6b23ddb642eb630be867690a8f8a8693)) + + +### Bug Fixes + +* avoid leaking memory when Client.with_options is used ([#220](https://github.com/Finch-API/finch-api-python/issues/220)) ([c634771](https://github.com/Finch-API/finch-api-python/commit/c6347717feebc43eb69d191aeee3369aa010e0a0)) +* **client:** correct base_url setter implementation ([#207](https://github.com/Finch-API/finch-api-python/issues/207)) ([5e04fa7](https://github.com/Finch-API/finch-api-python/commit/5e04fa773d4603d0be73aa67268b17fdc4f6fed2)) +* **client:** ensure retried requests are closed ([#204](https://github.com/Finch-API/finch-api-python/issues/204)) ([0659932](https://github.com/Finch-API/finch-api-python/commit/0659932aaaa69850982e6acbe076a4af7980efcd)) +* **errors:** properly assign APIError.body ([#219](https://github.com/Finch-API/finch-api-python/issues/219)) ([ee8fb39](https://github.com/Finch-API/finch-api-python/commit/ee8fb3977610c5a7578353042553665a07943b42)) + + +### Chores + +* **ci:** run release workflow once per day ([#226](https://github.com/Finch-API/finch-api-python/issues/226)) ([7147466](https://github.com/Finch-API/finch-api-python/commit/7147466ca5ce520e88fb392bd2d84be99a03e2b7)) +* **internal:** add tests for proxy change ([#203](https://github.com/Finch-API/finch-api-python/issues/203)) ([b5b9f79](https://github.com/Finch-API/finch-api-python/commit/b5b9f79c19e846e2e8fc3fd1faf905a65a6aa7c4)) +* **internal:** enable more lint rules ([#217](https://github.com/Finch-API/finch-api-python/issues/217)) ([61018d4](https://github.com/Finch-API/finch-api-python/commit/61018d4794ec858953dc0a3746054dbc04807ff9)) +* **internal:** reformat imports ([#213](https://github.com/Finch-API/finch-api-python/issues/213)) ([81a6e2f](https://github.com/Finch-API/finch-api-python/commit/81a6e2f22a5c5fe862c693a0b5b2affda78d88af)) +* **internal:** reformat imports ([#216](https://github.com/Finch-API/finch-api-python/issues/216)) ([14c8df0](https://github.com/Finch-API/finch-api-python/commit/14c8df00568bba59acab45ff6cdef2cb04599b43)) +* **internal:** replace string concatenation with f-strings ([#206](https://github.com/Finch-API/finch-api-python/issues/206)) ([7c920a3](https://github.com/Finch-API/finch-api-python/commit/7c920a33c103fadee08cd784f5d028f2ab19411f)) +* **internal:** update formatting ([#214](https://github.com/Finch-API/finch-api-python/issues/214)) ([cd5253c](https://github.com/Finch-API/finch-api-python/commit/cd5253c9a8417eec88dc8c30fdfa817ec1519a9e)) +* **internal:** update lock file ([#201](https://github.com/Finch-API/finch-api-python/issues/201)) ([42de23d](https://github.com/Finch-API/finch-api-python/commit/42de23d5c7a3b9253396b94af7884e7a9300b841)) +* **internal:** updates to proxy helper ([#202](https://github.com/Finch-API/finch-api-python/issues/202)) ([2049c50](https://github.com/Finch-API/finch-api-python/commit/2049c50e723ec6ec4b4d46a18fa87800a58d581a)) +* **package:** lift anyio v4 restriction ([#208](https://github.com/Finch-API/finch-api-python/issues/208)) ([e1ed4a5](https://github.com/Finch-API/finch-api-python/commit/e1ed4a53591362f5e2579e301b43529b64f2fc8b)) + + +### Documentation + +* improve README timeout comment ([#221](https://github.com/Finch-API/finch-api-python/issues/221)) ([1c946dd](https://github.com/Finch-API/finch-api-python/commit/1c946dd97fc43bfdda3346311282086af8cac6cb)) +* **readme:** update example snippets ([#205](https://github.com/Finch-API/finch-api-python/issues/205)) ([4ff1a6b](https://github.com/Finch-API/finch-api-python/commit/4ff1a6b04ba8e3301ac6ff4a612bb8aab801abd0)) + + +### Refactors + +* **client:** simplify cleanup ([#222](https://github.com/Finch-API/finch-api-python/issues/222)) ([7f116d2](https://github.com/Finch-API/finch-api-python/commit/7f116d2665c22e08988fb01cbbb692c8c0b79fea)) +* remove unused model types used in params ([#224](https://github.com/Finch-API/finch-api-python/issues/224)) ([f0b3fb7](https://github.com/Finch-API/finch-api-python/commit/f0b3fb7fceb4cdfe42abc5e49e04de30633cc55c)) +* simplify internal error handling ([#223](https://github.com/Finch-API/finch-api-python/issues/223)) ([481dc7d](https://github.com/Finch-API/finch-api-python/commit/481dc7de11733b2493e820ff241b7999d873fd16)) + ## 0.8.2 (2023-11-28) Full Changelog: [v0.8.1...v0.8.2](https://github.com/Finch-API/finch-api-python/compare/v0.8.1...v0.8.2) diff --git a/pyproject.toml b/pyproject.toml index 2edff854..b477e34b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "finch-api" -version = "0.8.2" +version = "0.9.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 d085a27d..cfd63a12 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.8.2" # x-release-please-version +__version__ = "0.9.0" # x-release-please-version