Skip to content

Commit baf6610

Browse files
Add HTTP method and URL context to transport failures
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent c9ad34f commit baf6610

3 files changed

Lines changed: 124 additions & 10 deletions

File tree

hyperbrowser/transport/async_transport.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ async def _handle_response(self, response: httpx.Response) -> APIResponse:
6767
original_error=e,
6868
)
6969
except httpx.RequestError as e:
70-
raise HyperbrowserError("Request failed", original_error=e)
70+
request_url = str(e.request.url) if e.request else "unknown URL"
71+
raise HyperbrowserError(
72+
f"Request failed for {request_url}", original_error=e
73+
)
7174

7275
async def post(
7376
self, url: str, data: Optional[dict] = None, files: Optional[dict] = None
@@ -78,10 +81,16 @@ async def post(
7881
else:
7982
response = await self.client.post(url, json=data)
8083
return await self._handle_response(response)
84+
except httpx.RequestError as e:
85+
raise HyperbrowserError(
86+
f"POST request to {url} failed", original_error=e
87+
) from e
8188
except HyperbrowserError:
8289
raise
8390
except Exception as e:
84-
raise HyperbrowserError("Post request failed", original_error=e)
91+
raise HyperbrowserError(
92+
f"POST request to {url} failed", original_error=e
93+
) from e
8594

8695
async def get(
8796
self, url: str, params: Optional[dict] = None, follow_redirects: bool = False
@@ -93,25 +102,43 @@ async def get(
93102
url, params=params, follow_redirects=follow_redirects
94103
)
95104
return await self._handle_response(response)
105+
except httpx.RequestError as e:
106+
raise HyperbrowserError(
107+
f"GET request to {url} failed", original_error=e
108+
) from e
96109
except HyperbrowserError:
97110
raise
98111
except Exception as e:
99-
raise HyperbrowserError("Get request failed", original_error=e)
112+
raise HyperbrowserError(
113+
f"GET request to {url} failed", original_error=e
114+
) from e
100115

101116
async def put(self, url: str, data: Optional[dict] = None) -> APIResponse:
102117
try:
103118
response = await self.client.put(url, json=data)
104119
return await self._handle_response(response)
120+
except httpx.RequestError as e:
121+
raise HyperbrowserError(
122+
f"PUT request to {url} failed", original_error=e
123+
) from e
105124
except HyperbrowserError:
106125
raise
107126
except Exception as e:
108-
raise HyperbrowserError("Put request failed", original_error=e)
127+
raise HyperbrowserError(
128+
f"PUT request to {url} failed", original_error=e
129+
) from e
109130

110131
async def delete(self, url: str) -> APIResponse:
111132
try:
112133
response = await self.client.delete(url)
113134
return await self._handle_response(response)
135+
except httpx.RequestError as e:
136+
raise HyperbrowserError(
137+
f"DELETE request to {url} failed", original_error=e
138+
) from e
114139
except HyperbrowserError:
115140
raise
116141
except Exception as e:
117-
raise HyperbrowserError("Delete request failed", original_error=e)
142+
raise HyperbrowserError(
143+
f"DELETE request to {url} failed", original_error=e
144+
) from e

hyperbrowser/transport/sync.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ def _handle_response(self, response: httpx.Response) -> APIResponse:
5555
original_error=e,
5656
)
5757
except httpx.RequestError as e:
58-
raise HyperbrowserError("Request failed", original_error=e)
58+
request_url = str(e.request.url) if e.request else "unknown URL"
59+
raise HyperbrowserError(
60+
f"Request failed for {request_url}", original_error=e
61+
)
5962

6063
def close(self) -> None:
6164
self.client.close()
@@ -69,10 +72,16 @@ def post(
6972
else:
7073
response = self.client.post(url, json=data)
7174
return self._handle_response(response)
75+
except httpx.RequestError as e:
76+
raise HyperbrowserError(
77+
f"POST request to {url} failed", original_error=e
78+
) from e
7279
except HyperbrowserError:
7380
raise
7481
except Exception as e:
75-
raise HyperbrowserError("Post request failed", original_error=e)
82+
raise HyperbrowserError(
83+
f"POST request to {url} failed", original_error=e
84+
) from e
7685

7786
def get(
7887
self, url: str, params: Optional[dict] = None, follow_redirects: bool = False
@@ -84,25 +93,43 @@ def get(
8493
url, params=params, follow_redirects=follow_redirects
8594
)
8695
return self._handle_response(response)
96+
except httpx.RequestError as e:
97+
raise HyperbrowserError(
98+
f"GET request to {url} failed", original_error=e
99+
) from e
87100
except HyperbrowserError:
88101
raise
89102
except Exception as e:
90-
raise HyperbrowserError("Get request failed", original_error=e)
103+
raise HyperbrowserError(
104+
f"GET request to {url} failed", original_error=e
105+
) from e
91106

92107
def put(self, url: str, data: Optional[dict] = None) -> APIResponse:
93108
try:
94109
response = self.client.put(url, json=data)
95110
return self._handle_response(response)
111+
except httpx.RequestError as e:
112+
raise HyperbrowserError(
113+
f"PUT request to {url} failed", original_error=e
114+
) from e
96115
except HyperbrowserError:
97116
raise
98117
except Exception as e:
99-
raise HyperbrowserError("Put request failed", original_error=e)
118+
raise HyperbrowserError(
119+
f"PUT request to {url} failed", original_error=e
120+
) from e
100121

101122
def delete(self, url: str) -> APIResponse:
102123
try:
103124
response = self.client.delete(url)
104125
return self._handle_response(response)
126+
except httpx.RequestError as e:
127+
raise HyperbrowserError(
128+
f"DELETE request to {url} failed", original_error=e
129+
) from e
105130
except HyperbrowserError:
106131
raise
107132
except Exception as e:
108-
raise HyperbrowserError("Delete request failed", original_error=e)
133+
raise HyperbrowserError(
134+
f"DELETE request to {url} failed", original_error=e
135+
) from e

tests/test_transport_response_handling.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,63 @@ async def run() -> None:
140140
await transport.close()
141141

142142
asyncio.run(run())
143+
144+
145+
def test_sync_transport_post_wraps_request_errors_with_url_context():
146+
transport = SyncTransport(api_key="test-key")
147+
original_post = transport.client.post
148+
149+
def failing_post(*args, **kwargs):
150+
request = httpx.Request("POST", "https://example.com/post")
151+
raise httpx.RequestError("network down", request=request)
152+
153+
transport.client.post = failing_post # type: ignore[assignment]
154+
try:
155+
with pytest.raises(
156+
HyperbrowserError, match="POST request to https://example.com/post failed"
157+
):
158+
transport.post("https://example.com/post", data={"ok": True})
159+
finally:
160+
transport.client.post = original_post # type: ignore[assignment]
161+
transport.close()
162+
163+
164+
def test_sync_transport_post_wraps_unexpected_errors_with_url_context():
165+
transport = SyncTransport(api_key="test-key")
166+
original_post = transport.client.post
167+
168+
def failing_post(*args, **kwargs):
169+
raise RuntimeError("boom")
170+
171+
transport.client.post = failing_post # type: ignore[assignment]
172+
try:
173+
with pytest.raises(
174+
HyperbrowserError, match="POST request to https://example.com/post failed"
175+
):
176+
transport.post("https://example.com/post", data={"ok": True})
177+
finally:
178+
transport.client.post = original_post # type: ignore[assignment]
179+
transport.close()
180+
181+
182+
def test_async_transport_get_wraps_request_errors_with_url_context():
183+
async def run() -> None:
184+
transport = AsyncTransport(api_key="test-key")
185+
original_get = transport.client.get
186+
187+
async def failing_get(*args, **kwargs):
188+
request = httpx.Request("GET", "https://example.com/get")
189+
raise httpx.RequestError("network down", request=request)
190+
191+
transport.client.get = failing_get # type: ignore[assignment]
192+
try:
193+
with pytest.raises(
194+
HyperbrowserError,
195+
match="GET request to https://example.com/get failed",
196+
):
197+
await transport.get("https://example.com/get")
198+
finally:
199+
transport.client.get = original_get # type: ignore[assignment]
200+
await transport.close()
201+
202+
asyncio.run(run())

0 commit comments

Comments
 (0)