Skip to content

chore(internal): move error classes from _base_exceptions to _exceptions (⚠️ breaking) #107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/finch/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,18 @@
"Transport",
"ProxiesTypes",
"APIError",
"APIConnectionError",
"APIResponseValidationError",
"APIStatusError",
"APITimeoutError",
"AuthenticationError",
"APIConnectionError",
"APIResponseValidationError",
"BadRequestError",
"ConflictError",
"InternalServerError",
"NotFoundError",
"AuthenticationError",
"PermissionDeniedError",
"RateLimitError",
"NotFoundError",
"ConflictError",
"UnprocessableEntityError",
"RateLimitError",
"InternalServerError",
"Timeout",
"RequestOptions",
"Client",
Expand All @@ -65,7 +65,7 @@
# Update the __module__ attribute for exported symbols so that
# error messages point to this module instead of the module
# it was originally defined in, e.g.
# finch._base_exceptions.NotFoundError -> finch.NotFoundError
# finch._exceptions.NotFoundError -> finch.NotFoundError
__locals = locals()
for __name in __all__:
if not __name.startswith("__"):
Expand Down
56 changes: 16 additions & 40 deletions src/finch/_base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from httpx import URL, Limits
from pydantic import PrivateAttr

from . import _base_exceptions as exceptions
from . import _exceptions
from ._qs import Querystring
from ._types import (
NOT_GIVEN,
Expand Down Expand Up @@ -64,7 +64,7 @@
construct_type,
)
from ._streaming import Stream, AsyncStream
from ._base_exceptions import (
from ._exceptions import (
APIStatusError,
APITimeoutError,
APIConnectionError,
Expand Down Expand Up @@ -335,7 +335,6 @@ def __init__(

def _make_status_error_from_response(
self,
request: httpx.Request,
response: httpx.Response,
) -> APIStatusError:
err_text = response.text.strip()
Expand All @@ -347,33 +346,16 @@ def _make_status_error_from_response(
except Exception:
err_msg = err_text or f"Error code: {response.status_code}"

return self._make_status_error(err_msg, body=body, request=request, response=response)
return self._make_status_error(err_msg, body=body, response=response)

def _make_status_error(
self,
err_msg: str,
*,
body: object,
request: httpx.Request,
response: httpx.Response,
) -> APIStatusError:
if response.status_code == 400:
return exceptions.BadRequestError(err_msg, request=request, response=response, body=body)
if response.status_code == 401:
return exceptions.AuthenticationError(err_msg, request=request, response=response, body=body)
if response.status_code == 403:
return exceptions.PermissionDeniedError(err_msg, request=request, response=response, body=body)
if response.status_code == 404:
return exceptions.NotFoundError(err_msg, request=request, response=response, body=body)
if response.status_code == 409:
return exceptions.ConflictError(err_msg, request=request, response=response, body=body)
if response.status_code == 422:
return exceptions.UnprocessableEntityError(err_msg, request=request, response=response, body=body)
if response.status_code == 429:
return exceptions.RateLimitError(err_msg, request=request, response=response, body=body)
if response.status_code >= 500:
return exceptions.InternalServerError(err_msg, request=request, response=response, body=body)
return APIStatusError(err_msg, request=request, response=response, body=body)
) -> _exceptions.APIStatusError:
raise NotImplementedError()

def _remaining_retries(
self,
Expand Down Expand Up @@ -532,10 +514,10 @@ def _process_response(
content_type, *_ = response.headers.get("content-type").split(";")
if content_type != "application/json":
if self._strict_response_validation:
raise exceptions.APIResponseValidationError(
raise APIResponseValidationError(
response=response,
request=response.request,
message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.",
body=response.text,
)

# If the API responds with content that isn't JSON then we just return
Expand All @@ -544,7 +526,11 @@ def _process_response(
return response.text # type: ignore

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

try:
return self._process_response_data(data=data, cast_to=cast_to, response=response)
except pydantic.ValidationError as err:
raise APIResponseValidationError(response=response, body=data) from err

def _process_response_data(
self,
Expand Down Expand Up @@ -826,7 +812,7 @@ def _request(
# If the response is streamed then we need to explicitly read the response
# to completion before attempting to access the response text.
err.response.read()
raise self._make_status_error_from_response(request, err.response) from None
raise self._make_status_error_from_response(err.response) from None
except httpx.TimeoutException as err:
if retries > 0:
return self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls)
Expand All @@ -845,12 +831,7 @@ def _request(
raise MissingStreamClassError()
return stream_cls(cast_to=cast_to, response=response, client=self)

try:
rsp = self._process_response(cast_to=cast_to, options=options, response=response)
except pydantic.ValidationError as err:
raise APIResponseValidationError(request=request, response=response) from err

return rsp
return self._process_response(cast_to=cast_to, options=options, response=response)

def _retry_request(
self,
Expand Down Expand Up @@ -1184,7 +1165,7 @@ async def _request(
# If the response is streamed then we need to explicitly read the response
# to completion before attempting to access the response text.
await err.response.aread()
raise self._make_status_error_from_response(request, err.response) from None
raise self._make_status_error_from_response(err.response) from None
except httpx.ConnectTimeout as err:
if retries > 0:
return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls)
Expand Down Expand Up @@ -1213,12 +1194,7 @@ async def _request(
raise MissingStreamClassError()
return stream_cls(cast_to=cast_to, response=response, client=self)

try:
rsp = self._process_response(cast_to=cast_to, options=options, response=response)
except pydantic.ValidationError as err:
raise APIResponseValidationError(request=request, response=response) from err

return rsp
return self._process_response(cast_to=cast_to, options=options, response=response)

async def _retry_request(
self,
Expand Down
119 changes: 0 additions & 119 deletions src/finch/_base_exceptions.py

This file was deleted.

67 changes: 66 additions & 1 deletion src/finch/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import httpx

from . import resources
from . import resources, _exceptions
from ._qs import Querystring
from ._types import (
NOT_GIVEN,
Expand All @@ -23,6 +23,7 @@
from ._version import __version__
from ._streaming import Stream as Stream
from ._streaming import AsyncStream as AsyncStream
from ._exceptions import APIStatusError
from ._base_client import (
DEFAULT_LIMITS,
DEFAULT_TIMEOUT,
Expand Down Expand Up @@ -274,6 +275,38 @@ def get_auth_url(
)
)

def _make_status_error(
self,
err_msg: str,
*,
body: object,
response: httpx.Response,
) -> APIStatusError:
if response.status_code == 400:
return _exceptions.BadRequestError(err_msg, response=response, body=body)

if response.status_code == 401:
return _exceptions.AuthenticationError(err_msg, response=response, body=body)

if response.status_code == 403:
return _exceptions.PermissionDeniedError(err_msg, response=response, body=body)

if response.status_code == 404:
return _exceptions.NotFoundError(err_msg, response=response, body=body)

if response.status_code == 409:
return _exceptions.ConflictError(err_msg, response=response, body=body)

if response.status_code == 422:
return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body)

if response.status_code == 429:
return _exceptions.RateLimitError(err_msg, response=response, body=body)

if response.status_code >= 500:
return _exceptions.InternalServerError(err_msg, response=response, body=body)
return APIStatusError(err_msg, response=response, body=body)


class AsyncFinch(AsyncAPIClient):
hris: resources.AsyncHRIS
Expand Down Expand Up @@ -508,6 +541,38 @@ def get_auth_url(
)
)

def _make_status_error(
self,
err_msg: str,
*,
body: object,
response: httpx.Response,
) -> APIStatusError:
if response.status_code == 400:
return _exceptions.BadRequestError(err_msg, response=response, body=body)

if response.status_code == 401:
return _exceptions.AuthenticationError(err_msg, response=response, body=body)

if response.status_code == 403:
return _exceptions.PermissionDeniedError(err_msg, response=response, body=body)

if response.status_code == 404:
return _exceptions.NotFoundError(err_msg, response=response, body=body)

if response.status_code == 409:
return _exceptions.ConflictError(err_msg, response=response, body=body)

if response.status_code == 422:
return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body)

if response.status_code == 429:
return _exceptions.RateLimitError(err_msg, response=response, body=body)

if response.status_code >= 500:
return _exceptions.InternalServerError(err_msg, response=response, body=body)
return APIStatusError(err_msg, response=response, body=body)


Client = Finch

Expand Down
Loading