22import os
33import ssl
44import typing as T
5+ from json import dumps
56
67import requests
78from requests .adapters import HTTPAdapter
@@ -46,6 +47,106 @@ def cert_verify(self, *args, **kwargs):
4647 conn .ca_certs = None
4748
4849
50+ @T .overload
51+ def _truncate (s : bytes , limit : int = 512 ) -> bytes : ...
52+
53+
54+ @T .overload
55+ def _truncate (s : str , limit : int = 512 ) -> str : ...
56+
57+
58+ def _truncate (s , limit = 512 ):
59+ if limit < len (s ):
60+ remaining = len (s ) - limit
61+ if isinstance (s , bytes ):
62+ return (
63+ s [:limit ]
64+ + b"..."
65+ + f"({ remaining } more bytes truncated)" .encode ("utf-8" )
66+ )
67+ else :
68+ return str (s [:limit ]) + f"...({ remaining } more chars truncated)"
69+ else :
70+ return s
71+
72+
73+ def _sanitize (headers : T .Dict ):
74+ new_headers = {}
75+
76+ for k , v in headers .items ():
77+ if k .lower () in [
78+ "authorization" ,
79+ "cookie" ,
80+ "x-fb-access-token" ,
81+ "access-token" ,
82+ "access_token" ,
83+ "password" ,
84+ ]:
85+ new_headers [k ] = "[REDACTED]"
86+ else :
87+ new_headers [k ] = _truncate (v )
88+
89+ return new_headers
90+
91+
92+ def _log_debug_request (
93+ method : str ,
94+ url : str ,
95+ json : T .Optional [T .Dict ] = None ,
96+ params : T .Optional [T .Dict ] = None ,
97+ headers : T .Optional [T .Dict ] = None ,
98+ timeout : T .Any = None ,
99+ ):
100+ if logging .getLogger ().getEffectiveLevel () <= logging .DEBUG :
101+ return
102+
103+ msg = f"HTTP { method } { url } "
104+
105+ if USE_SYSTEM_CERTS :
106+ msg += " (w/sys_certs)"
107+
108+ if json :
109+ t = _truncate (dumps (_sanitize (json )))
110+ msg += f" JSON={ t } "
111+
112+ if params :
113+ msg += f" PARAMS={ _sanitize (params )} "
114+
115+ if headers :
116+ msg += f" HEADERS={ _sanitize (headers )} "
117+
118+ if timeout is not None :
119+ msg += f" TIMEOUT={ timeout } "
120+
121+ LOG .debug (msg )
122+
123+
124+ def _log_debug_response (resp : requests .Response ):
125+ if logging .getLogger ().getEffectiveLevel () <= logging .DEBUG :
126+ return
127+
128+ data : T .Union [str , bytes ]
129+ try :
130+ data = _truncate (dumps (_sanitize (resp .json ())))
131+ except Exception :
132+ data = _truncate (resp .content )
133+
134+ LOG .debug (f"HTTP { resp .status_code } ({ resp .reason } ): %s" , data )
135+
136+
137+ def readable_http_error (ex : requests .HTTPError ) -> str :
138+ req = ex .request
139+ resp = ex .response
140+
141+ data : T .Union [str , bytes ]
142+ try :
143+ data = _truncate (dumps (_sanitize (resp .json ())))
144+ except Exception :
145+ data = _truncate (resp .content )
146+
147+ return f"{ req .method } { resp .url } => { resp .status_code } ({ resp .reason } ): { str (data )} "
148+
149+
49150def request_post (
50151 url : str ,
51152 data : T .Optional [T .Any ] = None ,
@@ -54,14 +155,23 @@ def request_post(
54155) -> requests .Response :
55156 global USE_SYSTEM_CERTS
56157
158+ _log_debug_request (
159+ "POST" ,
160+ url ,
161+ json = json ,
162+ params = kwargs .get ("params" ),
163+ headers = kwargs .get ("headers" ),
164+ timeout = kwargs .get ("timeout" ),
165+ )
166+
57167 if USE_SYSTEM_CERTS :
58168 with requests .Session () as session :
59169 session .mount ("https://" , HTTPSystemCertsAdapter ())
60- return session .post (url , data = data , json = json , ** kwargs )
170+ resp = session .post (url , data = data , json = json , ** kwargs )
61171
62172 else :
63173 try :
64- return requests .post (url , data = data , json = json , ** kwargs )
174+ resp = requests .post (url , data = data , json = json , ** kwargs )
65175 except requests .exceptions .SSLError as ex :
66176 if "SSLCertVerificationError" not in str (ex ):
67177 raise ex
@@ -70,9 +180,11 @@ def request_post(
70180 LOG .warning (
71181 "SSL error occurred, falling back to system SSL certificates: %s" , ex
72182 )
73- with requests .Session () as session :
74- session .mount ("https://" , HTTPSystemCertsAdapter ())
75- return session .post (url , data = data , json = json , ** kwargs )
183+ return request_post (url , data = data , json = json , ** kwargs )
184+
185+ _log_debug_response (resp )
186+
187+ return resp
76188
77189
78190def request_get (
@@ -82,13 +194,21 @@ def request_get(
82194) -> requests .Response :
83195 global USE_SYSTEM_CERTS
84196
197+ _log_debug_request (
198+ "GET" ,
199+ url ,
200+ params = kwargs .get ("params" ),
201+ headers = kwargs .get ("headers" ),
202+ timeout = kwargs .get ("timeout" ),
203+ )
204+
85205 if USE_SYSTEM_CERTS :
86206 with requests .Session () as session :
87207 session .mount ("https://" , HTTPSystemCertsAdapter ())
88- return session .get (url , params = params , ** kwargs )
208+ resp = session .get (url , params = params , ** kwargs )
89209 else :
90210 try :
91- return requests .get (url , params = params , ** kwargs )
211+ resp = requests .get (url , params = params , ** kwargs )
92212 except requests .exceptions .SSLError as ex :
93213 if "SSLCertVerificationError" not in str (ex ):
94214 raise ex
@@ -97,15 +217,17 @@ def request_get(
97217 LOG .warning (
98218 "SSL error occurred, falling back to system SSL certificates: %s" , ex
99219 )
100- with requests .Session () as session :
101- session .mount ("https://" , HTTPSystemCertsAdapter ())
102- return session .get (url , params = params , ** kwargs )
220+ resp = request_get (url , params = params , ** kwargs )
221+
222+ _log_debug_response (resp )
223+
224+ return resp
103225
104226
105227def get_upload_token (email : str , password : str ) -> requests .Response :
106228 resp = request_post (
107229 f"{ MAPILLARY_GRAPH_API_ENDPOINT } /login" ,
108- params = {"access_token " : MAPILLARY_CLIENT_TOKEN },
230+ headers = {"Authorization " : f"OAuth { MAPILLARY_CLIENT_TOKEN } " },
109231 json = {"email" : email , "password" : password , "locale" : "en_US" },
110232 timeout = REQUESTS_TIMEOUT ,
111233 )
0 commit comments