Skip to content

Commit dee638f

Browse files
committed
Add the no-op client mock for httpx.AsyncClient
The hooks do not give us access to the AsyncClient, which breaks the mock no-op request in the splitting hook. For now, let's add it as a param to the sdk_init hook. This means we'll have to bring this code back whenever the SDK regenerates. At least, until we have an update from Speakeasy for this.
1 parent b8d0af9 commit dee638f

File tree

8 files changed

+62
-20
lines changed

8 files changed

+62
-20
lines changed

_test_unstructured_client/integration/test_integration_freemium.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,29 @@ def test_partition_handling_server_error(error, split_pdf, monkeypatch, doc_path
101101
response = client.general.partition(request=req)
102102

103103

104+
@pytest.mark.asyncio
105+
async def test_partition_async_returns_elements(client, doc_path):
106+
filename = "layout-parser-paper.pdf"
107+
with open(doc_path / filename, "rb") as f:
108+
files = shared.Files(
109+
content=f.read(),
110+
file_name=filename,
111+
)
112+
113+
req = operations.PartitionRequest(
114+
partition_parameters=shared.PartitionParameters(
115+
files=files,
116+
strategy="fast",
117+
languages=["eng"],
118+
split_pdf_page=True,
119+
)
120+
)
121+
122+
response = await client.general.partition_async(request=req)
123+
assert response.status_code == 200
124+
assert len(response.elements)
125+
126+
104127
def test_uvloop_partitions_without_errors(client, doc_path):
105128
async def call_api():
106129
filename = "layout-parser-paper-fast.pdf"

src/unstructured_client/_hooks/custom/clean_server_url_hook.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from urllib.parse import ParseResult, urlparse, urlunparse
33

44
from unstructured_client._hooks.types import SDKInitHook
5-
from unstructured_client.httpclient import HttpClient
5+
from unstructured_client.httpclient import HttpClient, AsyncHttpClient
66

77

88
class CleanServerUrlSDKInitHook(SDKInitHook):
@@ -25,9 +25,9 @@ def clean_server_url(self, base_url) -> str:
2525
return urlunparse(parsed_url._replace(path=""))
2626

2727
def sdk_init(
28-
self, base_url: str, client: HttpClient
29-
) -> Tuple[str, HttpClient]:
28+
self, base_url: str, client: HttpClient, async_client: AsyncHttpClient
29+
) -> Tuple[str, HttpClient, AsyncHttpClient]:
3030
"""Concrete implementation for SDKInitHook."""
3131
cleaned_url = self.clean_server_url(base_url)
3232

33-
return cleaned_url, client
33+
return cleaned_url, client, async_client

src/unstructured_client/_hooks/custom/logger_hook.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
SDKInitHook,
1212
AfterSuccessHook,
1313
)
14-
from unstructured_client.httpclient import HttpClient
14+
from unstructured_client.httpclient import HttpClient, AsyncHttpClient
1515
from collections import defaultdict
1616

1717
logger = logging.getLogger(UNSTRUCTURED_CLIENT_LOGGER_NAME)
@@ -46,10 +46,10 @@ def log_retries(self, response: Optional[httpx.Response], error: Optional[Excep
4646

4747

4848
def sdk_init(
49-
self, base_url: str, client: HttpClient
50-
) -> Tuple[str, HttpClient]:
49+
self, base_url: str, client: HttpClient, async_client: AsyncHttpClient
50+
) -> Tuple[str, HttpClient, AsyncHttpClient]:
5151
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
52-
return base_url, client
52+
return base_url, client, async_client
5353

5454
def after_success(
5555
self, hook_ctx: AfterSuccessContext, response: httpx.Response

src/unstructured_client/_hooks/custom/split_pdf_hook.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
BeforeRequestHook,
3333
SDKInitHook,
3434
)
35-
from unstructured_client.httpclient import HttpClient
35+
from unstructured_client.httpclient import HttpClient, AsyncHttpClient
3636
from unstructured_client.models import shared
3737

3838
logger = logging.getLogger(UNSTRUCTURED_CLIENT_LOGGER_NAME)
@@ -104,6 +104,7 @@ class SplitPdfHook(SDKInitHook, BeforeRequestHook, AfterSuccessHook, AfterErrorH
104104

105105
def __init__(self) -> None:
106106
self.client: Optional[HttpClient] = None
107+
self.async_client: Optional[AsyncHttpClient] = None
107108
self.coroutines_to_execute: dict[
108109
str, list[Coroutine[Any, Any, httpx.Response]]
109110
] = {}
@@ -112,8 +113,8 @@ def __init__(self) -> None:
112113
self.allow_failed: bool = DEFAULT_ALLOW_FAILED
113114

114115
def sdk_init(
115-
self, base_url: str, client: HttpClient
116-
) -> Tuple[str, HttpClient]:
116+
self, base_url: str, client: HttpClient, async_client: AsyncHttpClient
117+
) -> Tuple[str, HttpClient, AsyncHttpClient]:
117118
"""Initializes Split PDF Hook.
118119
119120
Adds a mock transport layer to the httpx client. This will return an
@@ -140,14 +141,31 @@ def handle_request(self, request: httpx.Request) -> httpx.Response:
140141
# Otherwise, pass the request to the default transport
141142
return self.base_transport.handle_request(request)
142143

144+
class AsyncDummyTransport(httpx.AsyncBaseTransport):
145+
def __init__(self, base_transport: httpx.AsyncBaseTransport):
146+
self.base_transport = base_transport
147+
148+
async def handle_async_request(self, request: httpx.Request) -> httpx.Response:
149+
# Return an empty 200 response if we send a request to this dummy host
150+
if request.method == "GET" and request.url.host == "no-op":
151+
return httpx.Response(status_code=200, content=b'')
152+
153+
# Otherwise, pass the request to the default transport
154+
return await self.base_transport.handle_async_request(request)
155+
143156
# Explicit cast to httpx.Client to avoid a typing error
144157
httpx_client = cast(httpx.Client, client)
158+
async_httpx_client = cast(httpx.AsyncClient, async_client)
145159

146160
# pylint: disable=protected-access
147161
httpx_client._transport = DummyTransport(httpx_client._transport)
148162

163+
# pylint: disable=protected-access
164+
async_httpx_client._transport = AsyncDummyTransport(async_httpx_client._transport)
165+
149166
self.client = httpx_client
150-
return base_url, self.client
167+
self.async_client = async_httpx_client
168+
return base_url, self.client, self.async_client
151169

152170
# pylint: disable=too-many-return-statements
153171
def before_request(

src/unstructured_client/_hooks/sdkhooks.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from .types import SDKInitHook, BeforeRequestContext, BeforeRequestHook, AfterSuccessContext, AfterSuccessHook, AfterErrorContext, AfterErrorHook, Hooks
55
from .registration import init_hooks
66
from typing import List, Optional, Tuple
7-
from unstructured_client.httpclient import HttpClient
7+
from unstructured_client.httpclient import HttpClient, AsyncHttpClient
88

99
class SDKHooks(Hooks):
1010
def __init__(self) -> None:
@@ -26,10 +26,10 @@ def register_after_success_hook(self, hook: AfterSuccessHook) -> None:
2626
def register_after_error_hook(self, hook: AfterErrorHook) -> None:
2727
self.after_error_hooks.append(hook)
2828

29-
def sdk_init(self, base_url: str, client: HttpClient) -> Tuple[str, HttpClient]:
29+
def sdk_init(self, base_url: str, client: HttpClient, async_client: AsyncHttpClient) -> Tuple[str, HttpClient, AsyncHttpClient]:
3030
for hook in self.sdk_init_hooks:
31-
base_url, client = hook.sdk_init(base_url, client)
32-
return base_url, client
31+
base_url, client, async_client = hook.sdk_init(base_url, client, async_client)
32+
return base_url, client, async_client
3333

3434
def before_request(self, hook_ctx: BeforeRequestContext, request: httpx.Request) -> httpx.Request:
3535
for hook in self.before_request_hooks:

src/unstructured_client/_hooks/types.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from abc import ABC, abstractmethod
55
import httpx
66
from typing import Any, Callable, List, Optional, Tuple, Union
7-
from unstructured_client.httpclient import HttpClient
7+
from unstructured_client.httpclient import HttpClient, AsyncHttpClient
88

99

1010
class HookContext:
@@ -36,7 +36,7 @@ def __init__(self, hook_ctx: HookContext):
3636

3737
class SDKInitHook(ABC):
3838
@abstractmethod
39-
def sdk_init(self, base_url: str, client: HttpClient) -> Tuple[str, HttpClient]:
39+
def sdk_init(self, base_url: str, client: HttpClient, async_client: AsyncHttpClient) -> Tuple[str, HttpClient, AsyncHttpClient]:
4040
pass
4141

4242

src/unstructured_client/general.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,9 @@ def partition(
9494

9595

9696
async def partition_async(
97-
self, *,
97+
self,
9898
request: Union[operations.PartitionRequest, operations.PartitionRequestTypedDict],
99+
*,
99100
retries: OptionalNullable[utils.RetryConfig] = UNSET,
100101
server_url: Optional[str] = None,
101102
timeout_ms: Optional[int] = None,

src/unstructured_client/sdk.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def __init__(
7474
hooks = SDKHooks()
7575

7676
current_server_url, *_ = self.sdk_configuration.get_server_details()
77-
server_url, self.sdk_configuration.client = hooks.sdk_init(current_server_url, self.sdk_configuration.client)
77+
server_url, self.sdk_configuration.client, self.sdk_configuration.async_client = hooks.sdk_init(current_server_url, self.sdk_configuration.client, self.sdk_configuration.async_client)
7878
if current_server_url != server_url:
7979
self.sdk_configuration.server_url = server_url
8080

0 commit comments

Comments
 (0)