1+ # Necessary to maintain compatibility with Python < 3.11
2+ from __future__ import annotations
3+
14from builtins import super
5+ from json import JSONDecodeError
6+ from typing import Any , Dict , Optional
7+
8+ from requests import Response
29
310
411class ApiError (RuntimeError ):
@@ -8,14 +15,90 @@ class ApiError(RuntimeError):
815 often, this will be caused by invalid input to the API.
916 """
1017
11- def __init__ (self , message , status = 400 , json = None ):
18+ def __init__ (
19+ self ,
20+ message : str ,
21+ status : int = 400 ,
22+ json : Optional [Dict [str , Any ]] = None ,
23+ response : Optional [Response ] = None ,
24+ ):
1225 super ().__init__ (message )
26+
1327 self .status = status
1428 self .json = json
29+ self .response = response
30+
1531 self .errors = []
32+
1633 if json and "errors" in json and isinstance (json ["errors" ], list ):
1734 self .errors = [e ["reason" ] for e in json ["errors" ]]
1835
36+ @classmethod
37+ def from_response (
38+ cls ,
39+ response : Response ,
40+ message : Optional [str ] = None ,
41+ disable_formatting : bool = False ,
42+ ) -> Optional [ApiError ]:
43+ """
44+ Creates an ApiError object from the given response,
45+ or None if the response does not contain an error.
46+
47+ :arg response: The response to create an ApiError from.
48+ :arg message: An optional message to prepend to the error's message.
49+ :arg disable_formatting: If true, the error's message will not automatically be formatted
50+ with details from the API response.
51+
52+ :returns: The new API error.
53+ """
54+
55+ if response .status_code < 400 or response .status_code > 599 :
56+ # No error was found
57+ return None
58+
59+ request = response .request
60+
61+ try :
62+ response_json = response .json ()
63+ except JSONDecodeError :
64+ response_json = None
65+
66+ # Use the user-defined message is formatting is disabled
67+ if disable_formatting :
68+ return cls (
69+ message ,
70+ status = response .status_code ,
71+ json = response_json ,
72+ response = response ,
73+ )
74+
75+ # Build the error string
76+ error_fmt = "N/A"
77+
78+ if response_json is not None and "errors" in response_json :
79+ errors = []
80+
81+ for error in response_json ["errors" ]:
82+ field = error .get ("field" )
83+ reason = error .get ("reason" )
84+ errors .append (f"{ field + ': ' if field else '' } { reason } " )
85+
86+ error_fmt = "; " .join (errors )
87+
88+ elif len (response .text or "" ) > 0 :
89+ error_fmt = response .text
90+
91+ return cls (
92+ (
93+ f"{ message + ': ' if message is not None else '' } "
94+ f"{ f'{ request .method } { request .path_url } : ' if request else '' } "
95+ f"[{ response .status_code } ] { error_fmt } "
96+ ),
97+ status = response .status_code ,
98+ json = response_json ,
99+ response = response ,
100+ )
101+
19102
20103class UnexpectedResponseError (RuntimeError ):
21104 """
@@ -26,7 +109,41 @@ class UnexpectedResponseError(RuntimeError):
26109 library, and should be fixed with changes to this codebase.
27110 """
28111
29- def __init__ (self , message , status = 200 , json = None ):
112+ def __init__ (
113+ self ,
114+ message : str ,
115+ status : int = 200 ,
116+ json : Optional [Dict [str , Any ]] = None ,
117+ response : Optional [Response ] = None ,
118+ ):
30119 super ().__init__ (message )
120+
31121 self .status = status
32122 self .json = json
123+ self .response = response
124+
125+ @classmethod
126+ def from_response (
127+ cls ,
128+ message : str ,
129+ response : Response ,
130+ ) -> Optional [UnexpectedResponseError ]:
131+ """
132+ Creates an UnexpectedResponseError object from the given response and message.
133+
134+ :arg message: The message to create this error with.
135+ :arg response: The response to create an UnexpectedResponseError from.
136+ :returns: The new UnexpectedResponseError.
137+ """
138+
139+ try :
140+ response_json = response .json ()
141+ except JSONDecodeError :
142+ response_json = None
143+
144+ return cls (
145+ message ,
146+ status = response .status_code ,
147+ json = response_json ,
148+ response = response ,
149+ )
0 commit comments