Skip to content

Commit e4cf664

Browse files
authored
Merge branch 'main' into speakeasy-sdk-regen-1740071825
2 parents 84c4075 + e0b8220 commit e4cf664

File tree

10 files changed

+326
-32
lines changed

10 files changed

+326
-32
lines changed

.genignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ _test_unstructured_client
88

99
# ignore Makefile
1010
Makefile
11+
12+
# Ignore the general.partition code until we can fix the base_url issue
13+
src/unstructured_client/general.py

.speakeasy/workflow.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ speakeasyVersion: latest
33
sources:
44
my-source:
55
inputs:
6-
- location: https://api.unstructured.io/general/openapi.json
76
- location: https://platform.unstructuredapp.io/openapi.json
7+
- location: https://api.unstructured.io/general/openapi.json
88
overlays:
99
- location: ./overlay_client.yaml
1010
registry:

_test_contract/platform_api/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def platform_api_url():
1717
def client(platform_api_url) -> UnstructuredClient:
1818
_client = UnstructuredClient(
1919
api_key_auth=FAKE_API_KEY,
20-
server_url="platform-api",
20+
server_url=platform_api_url,
2121
retry_config=RetryConfig(
2222
strategy="backoff",
2323
retry_connection_errors=False,

_test_unstructured_client/unit/test_custom_hooks.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
FAKE_KEY = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
1818

1919

20+
@pytest.mark.xfail(run=False, reason="This test is causing a hang")
2021
def test_unit_retry_with_backoff_does_retry(caplog):
2122
caplog.set_level(logging.INFO)
2223
filename = "README.md"
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
import httpx
2+
import pytest
3+
4+
from unstructured_client.models import operations
5+
from unstructured_client import UnstructuredClient, basesdk, utils
6+
7+
8+
# Raise one of these from our mock to return to the test code
9+
class BaseUrlCorrect(Exception):
10+
pass
11+
12+
13+
class BaseUrlIncorrect(Exception):
14+
pass
15+
16+
17+
def get_client_method_with_mock(
18+
sdk_endpoint_name,
19+
client_instance,
20+
mocked_server_url,
21+
monkeypatch
22+
):
23+
"""
24+
Given an endpoint name, e.g. "general.partition", return a reference
25+
to that method off of the given client instance.
26+
27+
The client's _build_request will have the following mock:
28+
Assert that the provided server_url is passed into _build_request.
29+
Raise a custom exception to get back to the test.
30+
"""
31+
# Mock this to get past param validation
32+
def mock_unmarshal(*args, **kwargs):
33+
return {}
34+
35+
monkeypatch.setattr(utils, "unmarshal", mock_unmarshal)
36+
37+
# Assert that the correct base_url makes it to here
38+
def mock_build_request(*args, base_url, **kwargs):
39+
if base_url == mocked_server_url:
40+
raise BaseUrlCorrect
41+
else:
42+
raise BaseUrlIncorrect(base_url)
43+
44+
# Find the method from the given string
45+
class_name, method_name = sdk_endpoint_name.split(".")
46+
endpoint_class = getattr(client_instance, class_name)
47+
endpoint_method = getattr(endpoint_class, method_name)
48+
49+
if "async" in method_name:
50+
monkeypatch.setattr(endpoint_class, "_build_request_async", mock_build_request)
51+
else:
52+
monkeypatch.setattr(endpoint_class, "_build_request", mock_build_request)
53+
54+
return endpoint_method
55+
56+
57+
@pytest.mark.parametrize(
58+
"sdk_endpoint_name",
59+
[
60+
("general.partition"),
61+
],
62+
)
63+
def test_endpoint_uses_correct_url(monkeypatch, sdk_endpoint_name):
64+
# Test 1
65+
# Pass server_url to the client, no path
66+
s = UnstructuredClient(server_url="http://localhost:8000")
67+
client_method = get_client_method_with_mock(
68+
sdk_endpoint_name,
69+
s,
70+
"http://localhost:8000",
71+
monkeypatch
72+
)
73+
74+
try:
75+
client_method(request={})
76+
except BaseUrlCorrect:
77+
pass
78+
except BaseUrlIncorrect as e:
79+
pytest.fail(f"server_url was passed to client and ignored, got {e}")
80+
81+
# Test 2
82+
# Pass server_url to the client, with path
83+
s = UnstructuredClient(server_url="http://localhost:8000/my/endpoint")
84+
client_method = get_client_method_with_mock(
85+
sdk_endpoint_name,
86+
s,
87+
"http://localhost:8000",
88+
monkeypatch
89+
)
90+
91+
try:
92+
client_method(request={})
93+
except BaseUrlCorrect:
94+
pass
95+
except BaseUrlIncorrect as e:
96+
pytest.fail(f"server_url was passed to client and was not stripped, got {e}")
97+
98+
# Test 3
99+
# Pass server_url to the endpoint, no path
100+
s = UnstructuredClient()
101+
client_method = get_client_method_with_mock(
102+
sdk_endpoint_name,
103+
s,
104+
"http://localhost:8000",
105+
monkeypatch
106+
)
107+
108+
try:
109+
client_method(request={}, server_url="http://localhost:8000")
110+
except BaseUrlCorrect:
111+
pass
112+
except BaseUrlIncorrect as e:
113+
pytest.fail(f"server_url was passed to endpoint and ignored, got {e}")
114+
115+
# Test 4
116+
# Pass server_url to the endpoint, with path
117+
s = UnstructuredClient()
118+
client_method = get_client_method_with_mock(
119+
sdk_endpoint_name,
120+
s,
121+
"http://localhost:8000",
122+
monkeypatch
123+
)
124+
125+
try:
126+
client_method(request={}, server_url="http://localhost:8000/my/endpoint")
127+
except BaseUrlCorrect:
128+
pass
129+
except BaseUrlIncorrect as e:
130+
pytest.fail(f"server_url was passed to endpoint and ignored, got {e}")
131+
132+
133+
# Test 5
134+
# No server_url, should take the default
135+
server_url = "https://api.unstructuredapp.io" if "partition" in sdk_endpoint_name else "https://platform.unstructuredapp.io"
136+
137+
s = UnstructuredClient()
138+
client_method = get_client_method_with_mock(
139+
sdk_endpoint_name,
140+
s,
141+
server_url,
142+
monkeypatch
143+
)
144+
145+
try:
146+
client_method(request={})
147+
except BaseUrlCorrect:
148+
pass
149+
except BaseUrlIncorrect as e:
150+
pytest.fail(f"Default url not used, got {e}")
151+
152+
153+
@pytest.mark.asyncio
154+
@pytest.mark.parametrize(
155+
"sdk_endpoint_name",
156+
[
157+
("general.partition_async"),
158+
],
159+
)
160+
async def test_async_endpoint_uses_correct_url(monkeypatch, sdk_endpoint_name):
161+
# Test 1
162+
# Pass server_url to the client, no path
163+
s = UnstructuredClient(server_url="http://localhost:8000")
164+
client_method = get_client_method_with_mock(
165+
sdk_endpoint_name,
166+
s,
167+
"http://localhost:8000",
168+
monkeypatch
169+
)
170+
171+
try:
172+
await client_method(request={})
173+
except BaseUrlCorrect:
174+
pass
175+
except BaseUrlIncorrect as e:
176+
pytest.fail(f"server_url was passed to client and ignored, got {e}")
177+
178+
# Test 2
179+
# Pass server_url to the client, with path
180+
s = UnstructuredClient(server_url="http://localhost:8000/my/endpoint")
181+
client_method = get_client_method_with_mock(
182+
sdk_endpoint_name,
183+
s,
184+
"http://localhost:8000",
185+
monkeypatch
186+
)
187+
188+
try:
189+
await client_method(request={})
190+
except BaseUrlCorrect:
191+
pass
192+
except BaseUrlIncorrect as e:
193+
pytest.fail(f"server_url was passed to client and was not stripped, got {e}")
194+
195+
# Test 3
196+
# Pass server_url to the endpoint, no path
197+
s = UnstructuredClient()
198+
client_method = get_client_method_with_mock(
199+
sdk_endpoint_name,
200+
s,
201+
"http://localhost:8000",
202+
monkeypatch
203+
)
204+
205+
try:
206+
await client_method(request={}, server_url="http://localhost:8000")
207+
except BaseUrlCorrect:
208+
pass
209+
except BaseUrlIncorrect as e:
210+
pytest.fail(f"server_url was passed to endpoint and ignored, got {e}")
211+
212+
# Test 4
213+
# Pass server_url to the endpoint, with path
214+
s = UnstructuredClient()
215+
client_method = get_client_method_with_mock(
216+
sdk_endpoint_name,
217+
s,
218+
"http://localhost:8000",
219+
monkeypatch
220+
)
221+
222+
try:
223+
await client_method(request={}, server_url="http://localhost:8000/my/endpoint")
224+
except BaseUrlCorrect:
225+
pass
226+
except BaseUrlIncorrect as e:
227+
pytest.fail(f"server_url was passed to endpoint and ignored, got {e}")
228+
229+
230+
# Test 5
231+
# No server_url, should take the default
232+
server_url = "https://api.unstructuredapp.io" if "partition" in sdk_endpoint_name else "https://platform.unstructuredapp.io"
233+
234+
s = UnstructuredClient()
235+
client_method = get_client_method_with_mock(
236+
sdk_endpoint_name,
237+
s,
238+
server_url,
239+
monkeypatch
240+
)
241+
242+
try:
243+
await client_method(request={})
244+
except BaseUrlCorrect:
245+
pass
246+
except BaseUrlIncorrect as e:
247+
pytest.fail(f"Default url not used, got {e}")

docs/models/errors/httpvalidationerror.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33

44
## Fields
55

6-
| Field | Type | Required | Description |
7-
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------- |
8-
| `detail` | List[[shared.ValidationError](../../models/shared/validationerror.md)] | :heavy_minus_sign: | N/A |
6+
| Field | Type | Required | Description |
7+
| -------------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------- |
8+
| `detail` | [Optional[errors.Detail]](../../models/errors/detail.md) | :heavy_minus_sign: | N/A |

src/unstructured_client/_hooks/custom/clean_server_url_hook.py

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,54 @@
77
from unstructured_client.httpclient import HttpClient
88

99

10-
class CleanServerUrlSDKInitHook(SDKInitHook):
11-
"""Hook fixing common mistakes by users in defining `server_url` in the unstructured-client"""
10+
def clean_server_url(base_url: str) -> str:
11+
"""Fix url scheme and remove the '/general/v0/general' path."""
12+
13+
if not base_url:
14+
return ""
15+
16+
# add a url scheme if not present (urllib.parse does not work reliably without it)
17+
if "http" not in base_url:
18+
base_url = "http://" + base_url
19+
20+
parsed_url: ParseResult = urlparse(base_url)
21+
22+
if "api.unstructuredapp.io" in parsed_url.netloc:
23+
if parsed_url.scheme != "https":
24+
parsed_url = parsed_url._replace(scheme="https")
25+
26+
# We only want the base url
27+
return urlunparse(parsed_url._replace(path="", params="", query="", fragment=""))
1228

13-
def clean_server_url(self, base_url: str) -> str:
14-
"""Fix url scheme and remove the '/general/v0/general' path."""
1529

16-
if not base_url:
17-
return ""
18-
# -- add a url scheme if not present (urllib.parse does not work reliably without it)
19-
if "http" not in base_url:
20-
base_url = "http://" + base_url
30+
def choose_server_url(endpoint_url: str | None, client_url: str, default_endpoint_url: str) -> str:
31+
"""
32+
Helper function to fix a breaking change in the SDK past 0.30.0.
33+
When we merged the partition and platform specs, the client server_url stopped working,
34+
and users need to pass it in the endpoint function.
35+
For now, call this helper in the generated code to set the correct url.
2136
22-
parsed_url: ParseResult = urlparse(base_url)
37+
Order of choices:
38+
Endpoint server_url -> s.general.partition(server_url=...)
39+
(Passed in as None if not set)
2340
24-
if "api.unstructuredapp.io" in base_url:
25-
if parsed_url.scheme != "https":
26-
parsed_url = parsed_url._replace(scheme="https")
41+
Base client server_url -> s = UnstructuredClient(server_url=...)
42+
(Passed as empty string if not set)
2743
28-
# -- path should always be empty
29-
return urlunparse(parsed_url._replace(path=""))
44+
Default endpoint URL as defined in the spec
45+
"""
46+
47+
url = endpoint_url if endpoint_url is not None else (client_url or default_endpoint_url)
48+
return clean_server_url(url)
49+
50+
51+
class CleanServerUrlSDKInitHook(SDKInitHook):
52+
"""Hook fixing common mistakes by users in defining `server_url` in the unstructured-client"""
3053

3154
def sdk_init(
3255
self, base_url: str, client: HttpClient
3356
) -> Tuple[str, HttpClient]:
3457
"""Concrete implementation for SDKInitHook."""
35-
cleaned_url = self.clean_server_url(base_url)
58+
cleaned_url = clean_server_url(base_url)
3659

3760
return cleaned_url, client

0 commit comments

Comments
 (0)