Skip to content

Commit b53790f

Browse files
fix: asyncify on non-asyncio runtimes (#696)
1 parent 276e85f commit b53790f

File tree

3 files changed

+31
-14
lines changed

3 files changed

+31
-14
lines changed

src/lithic/_utils/_sync.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,20 @@
77
from typing import Any, TypeVar, Callable, Awaitable
88
from typing_extensions import ParamSpec
99

10+
import anyio
11+
import sniffio
12+
import anyio.to_thread
13+
1014
T_Retval = TypeVar("T_Retval")
1115
T_ParamSpec = ParamSpec("T_ParamSpec")
1216

1317

1418
if sys.version_info >= (3, 9):
15-
to_thread = asyncio.to_thread
19+
_asyncio_to_thread = asyncio.to_thread
1620
else:
1721
# backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread
1822
# for Python 3.8 support
19-
async def to_thread(
23+
async def _asyncio_to_thread(
2024
func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
2125
) -> Any:
2226
"""Asynchronously run function *func* in a separate thread.
@@ -34,6 +38,17 @@ async def to_thread(
3438
return await loop.run_in_executor(None, func_call)
3539

3640

41+
async def to_thread(
42+
func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
43+
) -> T_Retval:
44+
if sniffio.current_async_library() == "asyncio":
45+
return await _asyncio_to_thread(func, *args, **kwargs)
46+
47+
return await anyio.to_thread.run_sync(
48+
functools.partial(func, *args, **kwargs),
49+
)
50+
51+
3752
# inspired by `asyncer`, https://github.com/tiangolo/asyncer
3853
def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]:
3954
"""

tests/api_resources/test_account_holders.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -720,15 +720,15 @@ def test_path_params_retrieve_document(self, client: Lithic) -> None:
720720
@parametrize
721721
def test_method_simulate_enrollment_document_review(self, client: Lithic) -> None:
722722
account_holder = client.account_holders.simulate_enrollment_document_review(
723-
document_upload_token="b11cd67b-0a52-4180-8365-314f3def5426",
723+
document_upload_token="document_upload_token",
724724
status="UPLOADED",
725725
)
726726
assert_matches_type(Document, account_holder, path=["response"])
727727

728728
@parametrize
729729
def test_method_simulate_enrollment_document_review_with_all_params(self, client: Lithic) -> None:
730730
account_holder = client.account_holders.simulate_enrollment_document_review(
731-
document_upload_token="b11cd67b-0a52-4180-8365-314f3def5426",
731+
document_upload_token="document_upload_token",
732732
status="UPLOADED",
733733
accepted_entity_status_reasons=["string"],
734734
status_reason="DOCUMENT_MISSING_REQUIRED_DATA",
@@ -738,7 +738,7 @@ def test_method_simulate_enrollment_document_review_with_all_params(self, client
738738
@parametrize
739739
def test_raw_response_simulate_enrollment_document_review(self, client: Lithic) -> None:
740740
response = client.account_holders.with_raw_response.simulate_enrollment_document_review(
741-
document_upload_token="b11cd67b-0a52-4180-8365-314f3def5426",
741+
document_upload_token="document_upload_token",
742742
status="UPLOADED",
743743
)
744744

@@ -750,7 +750,7 @@ def test_raw_response_simulate_enrollment_document_review(self, client: Lithic)
750750
@parametrize
751751
def test_streaming_response_simulate_enrollment_document_review(self, client: Lithic) -> None:
752752
with client.account_holders.with_streaming_response.simulate_enrollment_document_review(
753-
document_upload_token="b11cd67b-0a52-4180-8365-314f3def5426",
753+
document_upload_token="document_upload_token",
754754
status="UPLOADED",
755755
) as response:
756756
assert not response.is_closed
@@ -1539,15 +1539,15 @@ async def test_path_params_retrieve_document(self, async_client: AsyncLithic) ->
15391539
@parametrize
15401540
async def test_method_simulate_enrollment_document_review(self, async_client: AsyncLithic) -> None:
15411541
account_holder = await async_client.account_holders.simulate_enrollment_document_review(
1542-
document_upload_token="b11cd67b-0a52-4180-8365-314f3def5426",
1542+
document_upload_token="document_upload_token",
15431543
status="UPLOADED",
15441544
)
15451545
assert_matches_type(Document, account_holder, path=["response"])
15461546

15471547
@parametrize
15481548
async def test_method_simulate_enrollment_document_review_with_all_params(self, async_client: AsyncLithic) -> None:
15491549
account_holder = await async_client.account_holders.simulate_enrollment_document_review(
1550-
document_upload_token="b11cd67b-0a52-4180-8365-314f3def5426",
1550+
document_upload_token="document_upload_token",
15511551
status="UPLOADED",
15521552
accepted_entity_status_reasons=["string"],
15531553
status_reason="DOCUMENT_MISSING_REQUIRED_DATA",
@@ -1557,7 +1557,7 @@ async def test_method_simulate_enrollment_document_review_with_all_params(self,
15571557
@parametrize
15581558
async def test_raw_response_simulate_enrollment_document_review(self, async_client: AsyncLithic) -> None:
15591559
response = await async_client.account_holders.with_raw_response.simulate_enrollment_document_review(
1560-
document_upload_token="b11cd67b-0a52-4180-8365-314f3def5426",
1560+
document_upload_token="document_upload_token",
15611561
status="UPLOADED",
15621562
)
15631563

@@ -1569,7 +1569,7 @@ async def test_raw_response_simulate_enrollment_document_review(self, async_clie
15691569
@parametrize
15701570
async def test_streaming_response_simulate_enrollment_document_review(self, async_client: AsyncLithic) -> None:
15711571
async with async_client.account_holders.with_streaming_response.simulate_enrollment_document_review(
1572-
document_upload_token="b11cd67b-0a52-4180-8365-314f3def5426",
1572+
document_upload_token="document_upload_token",
15731573
status="UPLOADED",
15741574
) as response:
15751575
assert not response.is_closed

tests/test_client.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@
2424

2525
from lithic import Lithic, AsyncLithic, APIResponseValidationError
2626
from lithic._types import Omit
27+
from lithic._utils import maybe_transform
2728
from lithic._models import BaseModel, FinalRequestOptions
2829
from lithic._constants import RAW_RESPONSE_HEADER
2930
from lithic._exceptions import LithicError, APIStatusError, APITimeoutError, APIResponseValidationError
3031
from lithic._base_client import DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, make_request_options
32+
from lithic.types.card_create_params import CardCreateParams
3133

3234
from .utils import update_env
3335

@@ -822,7 +824,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> No
822824
with pytest.raises(APITimeoutError):
823825
self.client.post(
824826
"/v1/cards",
825-
body=cast(object, dict(type="SINGLE_USE")),
827+
body=cast(object, maybe_transform(dict(type="SINGLE_USE"), CardCreateParams)),
826828
cast_to=httpx.Response,
827829
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
828830
)
@@ -837,7 +839,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> Non
837839
with pytest.raises(APIStatusError):
838840
self.client.post(
839841
"/v1/cards",
840-
body=cast(object, dict(type="SINGLE_USE")),
842+
body=cast(object, maybe_transform(dict(type="SINGLE_USE"), CardCreateParams)),
841843
cast_to=httpx.Response,
842844
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
843845
)
@@ -1740,7 +1742,7 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter)
17401742
with pytest.raises(APITimeoutError):
17411743
await self.client.post(
17421744
"/v1/cards",
1743-
body=cast(object, dict(type="SINGLE_USE")),
1745+
body=cast(object, maybe_transform(dict(type="SINGLE_USE"), CardCreateParams)),
17441746
cast_to=httpx.Response,
17451747
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
17461748
)
@@ -1755,7 +1757,7 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter)
17551757
with pytest.raises(APIStatusError):
17561758
await self.client.post(
17571759
"/v1/cards",
1758-
body=cast(object, dict(type="SINGLE_USE")),
1760+
body=cast(object, maybe_transform(dict(type="SINGLE_USE"), CardCreateParams)),
17591761
cast_to=httpx.Response,
17601762
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
17611763
)

0 commit comments

Comments
 (0)