Skip to content

Commit 66d38c5

Browse files
committed
Add custom error properties
1 parent f2a418a commit 66d38c5

File tree

1 file changed

+112
-4
lines changed

1 file changed

+112
-4
lines changed

src/square/core/api_error.py

Lines changed: 112 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,123 @@
11
# This file was auto-generated by Fern from our API Definition.
22

3-
import typing
3+
import json
4+
from typing import Any, Dict, List, Optional
5+
6+
from square.types.error import Error
7+
8+
FALLBACK_ERROR = Error(
9+
category="API_ERROR",
10+
code="Unknown",
11+
)
412

513

614
class ApiError(Exception):
7-
status_code: typing.Optional[int]
8-
body: typing.Any
15+
"""Exception thrown for any non-2XX API responses."""
16+
17+
status_code: Optional[int]
18+
body: Any
19+
errors: List[Error]
20+
21+
def __init__(self, *, status_code: Optional[int] = None, body: Any = None):
22+
"""
23+
Initialize an ApiError.
924
10-
def __init__(self, *, status_code: typing.Optional[int] = None, body: typing.Any = None):
25+
Args:
26+
status_code: HTTP status code
27+
body: Response body
28+
"""
29+
message = "API Error"
1130
self.status_code = status_code
1231
self.body = body
32+
self.errors = self._parse_errors(body)
33+
34+
super().__init__(self._build_message(message, status_code, body))
35+
36+
def _build_message(
37+
self, message: str, status_code: Optional[int], body: Any
38+
) -> str:
39+
"""
40+
Build a detailed error message.
41+
42+
Args:
43+
message: Base error message
44+
status_code: HTTP status code
45+
body: Response body
46+
47+
Returns:
48+
Formatted error message
49+
"""
50+
result = f"{message}\nStatus code: {status_code}"
51+
52+
if body is None or body == "":
53+
return result
54+
55+
if isinstance(body, str):
56+
return f"{result}\nBody: {body}"
57+
58+
try:
59+
return f"{result}\nBody: {json.dumps(body, indent=2)}"
60+
except (TypeError, ValueError):
61+
return f"{result}\nBody: {str(body)}"
62+
63+
def _parse_errors(self, body: Any) -> List[Error]:
64+
"""
65+
Parse errors from the response body.
66+
67+
Args:
68+
body: Response body
69+
70+
Returns:
71+
List of Error objects
72+
"""
73+
74+
if body is None:
75+
return [FALLBACK_ERROR]
76+
77+
if isinstance(body, str):
78+
try:
79+
body = json.loads(body)
80+
except json.JSONDecodeError:
81+
return [FALLBACK_ERROR]
82+
83+
if hasattr(body, "__dict__"):
84+
try:
85+
json_str = json.dumps(body, default=lambda o: o.__dict__)
86+
body = json.loads(json_str)
87+
except (TypeError, ValueError):
88+
return [FALLBACK_ERROR]
89+
90+
if not isinstance(body, dict):
91+
return [FALLBACK_ERROR]
92+
93+
if "errors" in body:
94+
errors = body["errors"]
95+
if isinstance(errors, list):
96+
return [self._parse_error(error) for error in errors]
97+
98+
return [self._parse_error(errors)]
99+
100+
return [self._parse_error(body)]
101+
102+
def _parse_error(self, data: Optional[Dict[str, Any]] = None) -> Error:
103+
"""
104+
Create an Error object from error data.
105+
106+
Args:
107+
error_data: Dictionary containing error information
108+
109+
Returns:
110+
Error object
111+
"""
112+
if not data:
113+
return FALLBACK_ERROR
114+
115+
return Error(
116+
category=data.get("category", FALLBACK_ERROR.category),
117+
code=data.get("code", data.get("type", FALLBACK_ERROR.code)),
118+
detail=data.get("detail", data.get("message")),
119+
field=data.get("field"),
120+
)
13121

14122
def __str__(self) -> str:
15123
return f"status_code: {self.status_code}, body: {self.body}"

0 commit comments

Comments
 (0)