Skip to content

Commit 61dd6ba

Browse files
fix(client): don't error by default for unexpected content types (#104)
1 parent df685b7 commit 61dd6ba

File tree

3 files changed

+51
-6
lines changed

3 files changed

+51
-6
lines changed

src/finch/_base_client.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -531,9 +531,17 @@ def _process_response(
531531
# in the response, e.g. application/json; charset=utf-8
532532
content_type, *_ = response.headers.get("content-type").split(";")
533533
if content_type != "application/json":
534-
raise ValueError(
535-
f"Expected Content-Type response header to be `application/json` but received {content_type} instead."
536-
)
534+
if self._strict_response_validation:
535+
raise exceptions.APIResponseValidationError(
536+
response=response,
537+
request=response.request,
538+
message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.",
539+
)
540+
541+
# If the API responds with content that isn't JSON then we just return
542+
# the (decoded) text without performing any parsing so that you can still
543+
# handle the response however you need to.
544+
return response.text # type: ignore
537545

538546
data = response.json()
539547
return self._process_response_data(data=data, cast_to=cast_to, response=response)

src/finch/_base_exceptions.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
from typing_extensions import Literal
24

35
from httpx import Request, Response
@@ -17,8 +19,8 @@ class APIResponseValidationError(APIError):
1719
response: Response
1820
status_code: int
1921

20-
def __init__(self, request: Request, response: Response) -> None:
21-
super().__init__("Data returned by API invalid for expected schema.", request)
22+
def __init__(self, request: Request, response: Response, *, message: str | None = None) -> None:
23+
super().__init__(message or "Data returned by API invalid for expected schema.", request)
2224
self.response = response
2325
self.status_code = response.status_code
2426

tests/test_client.py

+36-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import pytest
1313
from respx import MockRouter
1414

15-
from finch import Finch, AsyncFinch
15+
from finch import Finch, AsyncFinch, APIResponseValidationError
1616
from finch._types import Omit
1717
from finch._models import BaseModel, FinalRequestOptions
1818
from finch._base_client import BaseClient, make_request_options
@@ -385,6 +385,23 @@ def test_client_context_manager(self) -> None:
385385
assert not client.is_closed()
386386
assert client.is_closed()
387387

388+
@pytest.mark.respx(base_url=base_url)
389+
def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None:
390+
class Model(BaseModel):
391+
name: str
392+
393+
respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format"))
394+
395+
strict_client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=True)
396+
397+
with pytest.raises(APIResponseValidationError):
398+
strict_client.get("/foo", cast_to=Model)
399+
400+
client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=False)
401+
402+
response = client.get("/foo", cast_to=Model)
403+
assert isinstance(response, str) # type: ignore[unreachable]
404+
388405

389406
class TestAsyncFinch:
390407
client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True)
@@ -744,3 +761,21 @@ async def test_client_context_manager(self) -> None:
744761
assert not c2.is_closed()
745762
assert not client.is_closed()
746763
assert client.is_closed()
764+
765+
@pytest.mark.respx(base_url=base_url)
766+
@pytest.mark.asyncio
767+
async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None:
768+
class Model(BaseModel):
769+
name: str
770+
771+
respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format"))
772+
773+
strict_client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True)
774+
775+
with pytest.raises(APIResponseValidationError):
776+
await strict_client.get("/foo", cast_to=Model)
777+
778+
client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=False)
779+
780+
response = await client.get("/foo", cast_to=Model)
781+
assert isinstance(response, str) # type: ignore[unreachable]

0 commit comments

Comments
 (0)