Skip to content

Commit 481dc7d

Browse files
refactor: simplify internal error handling (#223)
1 parent 7f116d2 commit 481dc7d

File tree

2 files changed

+47
-108
lines changed

2 files changed

+47
-108
lines changed

src/finch/_base_client.py

+46-56
Original file line numberDiff line numberDiff line change
@@ -873,66 +873,61 @@ def _request(
873873
request = self._build_request(options)
874874
self._prepare_request(request)
875875

876-
response = None
877-
878876
try:
879877
response = self._client.send(
880878
request,
881879
auth=self.custom_auth,
882880
stream=stream or self._should_stream_response_body(request=request),
883881
)
884-
log.debug(
885-
'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase
886-
)
887-
response.raise_for_status()
888-
except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
889-
if retries > 0 and self._should_retry(err.response):
890-
err.response.close()
882+
except httpx.TimeoutException as err:
883+
if retries > 0:
891884
return self._retry_request(
892885
options,
893886
cast_to,
894887
retries,
895-
err.response.headers,
896888
stream=stream,
897889
stream_cls=stream_cls,
890+
response_headers=None,
898891
)
899892

900-
# If the response is streamed then we need to explicitly read the response
901-
# to completion before attempting to access the response text.
902-
if not err.response.is_closed:
903-
err.response.read()
904-
905-
raise self._make_status_error_from_response(err.response) from None
906-
except httpx.TimeoutException as err:
907-
if response is not None:
908-
response.close()
909-
893+
raise APITimeoutError(request=request) from err
894+
except Exception as err:
910895
if retries > 0:
911896
return self._retry_request(
912897
options,
913898
cast_to,
914899
retries,
915900
stream=stream,
916901
stream_cls=stream_cls,
917-
response_headers=response.headers if response is not None else None,
902+
response_headers=None,
918903
)
919904

920-
raise APITimeoutError(request=request) from err
921-
except Exception as err:
922-
if response is not None:
923-
response.close()
905+
raise APIConnectionError(request=request) from err
924906

925-
if retries > 0:
907+
log.debug(
908+
'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase
909+
)
910+
911+
try:
912+
response.raise_for_status()
913+
except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
914+
if retries > 0 and self._should_retry(err.response):
915+
err.response.close()
926916
return self._retry_request(
927917
options,
928918
cast_to,
929919
retries,
920+
err.response.headers,
930921
stream=stream,
931922
stream_cls=stream_cls,
932-
response_headers=response.headers if response is not None else None,
933923
)
934924

935-
raise APIConnectionError(request=request) from err
925+
# If the response is streamed then we need to explicitly read the response
926+
# to completion before attempting to access the response text.
927+
if not err.response.is_closed:
928+
err.response.read()
929+
930+
raise self._make_status_error_from_response(err.response) from None
936931

937932
return self._process_response(
938933
cast_to=cast_to,
@@ -1340,66 +1335,61 @@ async def _request(
13401335
request = self._build_request(options)
13411336
await self._prepare_request(request)
13421337

1343-
response = None
1344-
13451338
try:
13461339
response = await self._client.send(
13471340
request,
13481341
auth=self.custom_auth,
13491342
stream=stream or self._should_stream_response_body(request=request),
13501343
)
1351-
log.debug(
1352-
'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase
1353-
)
1354-
response.raise_for_status()
1355-
except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
1356-
if retries > 0 and self._should_retry(err.response):
1357-
await err.response.aclose()
1344+
except httpx.TimeoutException as err:
1345+
if retries > 0:
13581346
return await self._retry_request(
13591347
options,
13601348
cast_to,
13611349
retries,
1362-
err.response.headers,
13631350
stream=stream,
13641351
stream_cls=stream_cls,
1352+
response_headers=None,
13651353
)
13661354

1367-
# If the response is streamed then we need to explicitly read the response
1368-
# to completion before attempting to access the response text.
1369-
if not err.response.is_closed:
1370-
await err.response.aread()
1371-
1372-
raise self._make_status_error_from_response(err.response) from None
1373-
except httpx.TimeoutException as err:
1374-
if response is not None:
1375-
await response.aclose()
1376-
1355+
raise APITimeoutError(request=request) from err
1356+
except Exception as err:
13771357
if retries > 0:
13781358
return await self._retry_request(
13791359
options,
13801360
cast_to,
13811361
retries,
13821362
stream=stream,
13831363
stream_cls=stream_cls,
1384-
response_headers=response.headers if response is not None else None,
1364+
response_headers=None,
13851365
)
13861366

1387-
raise APITimeoutError(request=request) from err
1388-
except Exception as err:
1389-
if response is not None:
1390-
await response.aclose()
1367+
raise APIConnectionError(request=request) from err
13911368

1392-
if retries > 0:
1369+
log.debug(
1370+
'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase
1371+
)
1372+
1373+
try:
1374+
response.raise_for_status()
1375+
except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
1376+
if retries > 0 and self._should_retry(err.response):
1377+
await err.response.aclose()
13931378
return await self._retry_request(
13941379
options,
13951380
cast_to,
13961381
retries,
1382+
err.response.headers,
13971383
stream=stream,
13981384
stream_cls=stream_cls,
1399-
response_headers=response.headers if response is not None else None,
14001385
)
14011386

1402-
raise APIConnectionError(request=request) from err
1387+
# If the response is streamed then we need to explicitly read the response
1388+
# to completion before attempting to access the response text.
1389+
if not err.response.is_closed:
1390+
await err.response.aread()
1391+
1392+
raise self._make_status_error_from_response(err.response) from None
14031393

14041394
return self._process_response(
14051395
cast_to=cast_to,

tests/test_client.py

+1-52
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from finch._types import Omit
2121
from finch._client import Finch, AsyncFinch
2222
from finch._models import BaseModel, FinalRequestOptions
23-
from finch._exceptions import APIStatusError, APIResponseValidationError
23+
from finch._exceptions import APIResponseValidationError
2424
from finch._base_client import (
2525
DEFAULT_TIMEOUT,
2626
HTTPX_DEFAULT_TIMEOUT,
@@ -772,31 +772,6 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str
772772
calculated = client._calculate_retry_timeout(remaining_retries, options, headers)
773773
assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType]
774774

775-
@pytest.mark.respx(base_url=base_url)
776-
def test_status_error_within_httpx(self, respx_mock: MockRouter) -> None:
777-
respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
778-
779-
def on_response(response: httpx.Response) -> None:
780-
raise httpx.HTTPStatusError(
781-
"Simulating an error inside httpx",
782-
response=response,
783-
request=response.request,
784-
)
785-
786-
client = Finch(
787-
base_url=base_url,
788-
access_token=access_token,
789-
_strict_response_validation=True,
790-
http_client=httpx.Client(
791-
event_hooks={
792-
"response": [on_response],
793-
}
794-
),
795-
max_retries=0,
796-
)
797-
with pytest.raises(APIStatusError):
798-
client.post("/foo", cast_to=httpx.Response)
799-
800775

801776
class TestAsyncFinch:
802777
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
15351510
options = FinalRequestOptions(method="get", url="/foo", max_retries=3)
15361511
calculated = client._calculate_retry_timeout(remaining_retries, options, headers)
15371512
assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType]
1538-
1539-
@pytest.mark.respx(base_url=base_url)
1540-
@pytest.mark.asyncio
1541-
async def test_status_error_within_httpx(self, respx_mock: MockRouter) -> None:
1542-
respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
1543-
1544-
def on_response(response: httpx.Response) -> None:
1545-
raise httpx.HTTPStatusError(
1546-
"Simulating an error inside httpx",
1547-
response=response,
1548-
request=response.request,
1549-
)
1550-
1551-
client = AsyncFinch(
1552-
base_url=base_url,
1553-
access_token=access_token,
1554-
_strict_response_validation=True,
1555-
http_client=httpx.AsyncClient(
1556-
event_hooks={
1557-
"response": [on_response],
1558-
}
1559-
),
1560-
max_retries=0,
1561-
)
1562-
with pytest.raises(APIStatusError):
1563-
await client.post("/foo", cast_to=httpx.Response)

0 commit comments

Comments
 (0)