Skip to content

Commit 3f8d2fa

Browse files
committed
add body to error message
1 parent be8dcd6 commit 3f8d2fa

File tree

2 files changed

+63
-24
lines changed

2 files changed

+63
-24
lines changed

fastcore/net.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
from .utils import *
1919
from .parallel import *
2020
from functools import wraps
21-
21+
from pprint import pformat
2222
import json,urllib,contextlib
2323
import socket,urllib.request,http,urllib
2424
from contextlib import contextmanager,ExitStack
25-
from urllib.request import Request,urlretrieve,install_opener
25+
from urllib.request import Request,urlretrieve,install_opener,HTTPErrorProcessor
2626
from urllib.error import HTTPError,URLError
2727
from urllib.parse import urlencode,urlparse,urlunparse
2828
from http.client import InvalidURL
@@ -104,7 +104,11 @@ def urlopen(url, data=None, headers=None, timeout=None, **kwargs):
104104
if data is not None:
105105
if not isinstance(data, (str,bytes)): data = urlencode(data)
106106
if not isinstance(data, bytes): data = data.encode('ascii')
107-
return urlopener().open(urlwrap(url, data=data, headers=headers), timeout=timeout)
107+
try: return urlopener().open(urlwrap(url, data=data, headers=headers), timeout=timeout)
108+
except HTTPError as e:
109+
e.msg += f"\n====Error Body====\n{pformat(json.loads(e.read()))}"
110+
raise e
111+
except Exception as e: raise e
108112

109113
# %% ../nbs/03b_net.ipynb 18
110114
def urlread(url, data=None, headers=None, decode=True, return_json=False, return_headers=False, timeout=None, **kwargs):
@@ -119,13 +123,13 @@ def urlread(url, data=None, headers=None, decode=True, return_json=False, return
119123
if return_json: res = loads(res)
120124
return (res,dict(hdrs)) if return_headers else res
121125

122-
# %% ../nbs/03b_net.ipynb 19
126+
# %% ../nbs/03b_net.ipynb 20
123127
def urljson(url, data=None, timeout=None):
124128
"Retrieve `url` and decode json"
125129
res = urlread(url, data=data, timeout=timeout)
126130
return json.loads(res) if res else {}
127131

128-
# %% ../nbs/03b_net.ipynb 21
132+
# %% ../nbs/03b_net.ipynb 22
129133
def urlcheck(url, timeout=10):
130134
if not url: return True
131135
try:
@@ -134,12 +138,12 @@ def urlcheck(url, timeout=10):
134138
except socket.timeout: return False
135139
except InvalidURL: return False
136140

137-
# %% ../nbs/03b_net.ipynb 22
141+
# %% ../nbs/03b_net.ipynb 23
138142
def urlclean(url):
139143
"Remove fragment, params, and querystring from `url` if present"
140144
return urlunparse(urlparse(str(url))[:3]+('','',''))
141145

142-
# %% ../nbs/03b_net.ipynb 24
146+
# %% ../nbs/03b_net.ipynb 25
143147
def urlretrieve(url, filename=None, reporthook=None, data=None, timeout=None):
144148
"Same as `urllib.request.urlretrieve` but also works with `Request` objects"
145149
with contextlib.closing(urlopen(url, data, timeout=timeout)) as fp:
@@ -165,43 +169,43 @@ def urlretrieve(url, filename=None, reporthook=None, data=None, timeout=None):
165169
raise ContentTooShortError(f"retrieval incomplete: got only {read} out of {size} bytes", headers)
166170
return filename,headers
167171

168-
# %% ../nbs/03b_net.ipynb 25
172+
# %% ../nbs/03b_net.ipynb 26
169173
def urldest(url, dest=None):
170174
name = urlclean(Path(url).name)
171175
if dest is None: dest = name
172176
dest = Path(dest)
173177
return dest/name if dest.is_dir() else dest
174178

175-
# %% ../nbs/03b_net.ipynb 26
179+
# %% ../nbs/03b_net.ipynb 27
176180
def urlsave(url, dest=None, reporthook=None, timeout=None):
177181
"Retrieve `url` and save based on its name"
178182
dest = urldest(url, dest)
179183
dest.parent.mkdir(parents=True, exist_ok=True)
180184
nm,msg = urlretrieve(url, dest, reporthook, timeout=timeout)
181185
return nm
182186

183-
# %% ../nbs/03b_net.ipynb 28
187+
# %% ../nbs/03b_net.ipynb 29
184188
def urlvalid(x):
185189
"Test if `x` is a valid URL"
186190
return all (getattrs(urlparse(str(x)), 'scheme', 'netloc'))
187191

188-
# %% ../nbs/03b_net.ipynb 30
192+
# %% ../nbs/03b_net.ipynb 31
189193
def urlrequest(url, verb, headers=None, route=None, query=None, data=None, json_data=True):
190194
"`Request` for `url` with optional route params replaced by `route`, plus `query` string, and post `data`"
191195
if route: url = url.format(**route)
192196
if query: url += '?' + urlencode(query)
193197
if isinstance(data,dict): data = (json.dumps if json_data else urlencode)(data).encode('ascii')
194198
return Request(url, headers=headers or {}, data=data or None, method=verb.upper())
195199

196-
# %% ../nbs/03b_net.ipynb 33
200+
# %% ../nbs/03b_net.ipynb 34
197201
@patch
198202
def summary(self:Request, skip=None)->dict:
199203
"Summary containing full_url, headers, method, and data, removing `skip` from headers"
200204
res = L('full_url','method','data').map_dict(partial(getattr,self))
201205
res['headers'] = {k:v for k,v in self.headers.items() if k not in listify(skip)}
202206
return res
203207

204-
# %% ../nbs/03b_net.ipynb 35
208+
# %% ../nbs/03b_net.ipynb 36
205209
def urlsend(url, verb, headers=None, route=None, query=None, data=None, json_data=True,
206210
return_json=True, return_headers=False, debug=None):
207211
"Send request with `urlrequest`, converting result to json if `return_json`"
@@ -213,7 +217,7 @@ def urlsend(url, verb, headers=None, route=None, query=None, data=None, json_dat
213217

214218
return urlread(req, return_json=return_json, return_headers=return_headers)
215219

216-
# %% ../nbs/03b_net.ipynb 36
220+
# %% ../nbs/03b_net.ipynb 37
217221
def do_request(url, post=False, headers=None, **data):
218222
"Call GET or json-encoded POST on `url`, depending on `post`"
219223
if data:
@@ -223,13 +227,13 @@ def do_request(url, post=False, headers=None, **data):
223227
data = None
224228
return urljson(Request(url, headers=headers, data=data or None))
225229

226-
# %% ../nbs/03b_net.ipynb 37
230+
# %% ../nbs/03b_net.ipynb 38
227231
def _socket_det(port,host,dgram):
228232
if isinstance(port,int): family,addr = socket.AF_INET,(host or socket.gethostname(),port)
229233
else: family,addr = socket.AF_UNIX,port
230234
return family,addr,(socket.SOCK_STREAM,socket.SOCK_DGRAM)[dgram]
231235

232-
# %% ../nbs/03b_net.ipynb 38
236+
# %% ../nbs/03b_net.ipynb 39
233237
def start_server(port, host=None, dgram=False, reuse_addr=True, n_queue=None):
234238
"Create a `socket` server on `port`, with optional `host`, of type `dgram`"
235239
listen_args = [n_queue] if n_queue else []
@@ -243,7 +247,7 @@ def start_server(port, host=None, dgram=False, reuse_addr=True, n_queue=None):
243247
s.listen(*listen_args)
244248
return s
245249

246-
# %% ../nbs/03b_net.ipynb 40
250+
# %% ../nbs/03b_net.ipynb 41
247251
def start_client(port, host=None, dgram=False):
248252
"Create a `socket` client on `port`, with optional `host`, of type `dgram`"
249253
family,addr,typ = _socket_det(port,host,dgram)

nbs/03b_net.ipynb

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
"from fastcore.utils import *\n",
2020
"from fastcore.parallel import *\n",
2121
"from functools import wraps\n",
22-
"\n",
22+
"from pprint import pformat\n",
2323
"import json,urllib,contextlib\n",
2424
"import socket,urllib.request,http,urllib\n",
2525
"from contextlib import contextmanager,ExitStack\n",
26-
"from urllib.request import Request,urlretrieve,install_opener\n",
26+
"from urllib.request import Request,urlretrieve,install_opener,HTTPErrorProcessor\n",
2727
"from urllib.error import HTTPError,URLError\n",
2828
"from urllib.parse import urlencode,urlparse,urlunparse\n",
2929
"from http.client import InvalidURL"
@@ -184,14 +184,24 @@
184184
"text/markdown": [
185185
"---\n",
186186
"\n",
187-
"### HTTP4xxClientError\n",
187+
"[source](https://github.com/fastai/fastcore/blob/master/fastcore/net.py#L64){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
188+
"\n",
189+
"#### HTTP4xxClientError\n",
188190
"\n",
189191
"> HTTP4xxClientError (url, code, msg, hdrs, fp)\n",
190192
"\n",
191193
"Base class for client exceptions (code 4xx) from `url*` functions"
192194
],
193195
"text/plain": [
194-
"<nbdev.showdoc.BasicMarkdownRenderer>"
196+
"---\n",
197+
"\n",
198+
"[source](https://github.com/fastai/fastcore/blob/master/fastcore/net.py#L64){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
199+
"\n",
200+
"#### HTTP4xxClientError\n",
201+
"\n",
202+
"> HTTP4xxClientError (url, code, msg, hdrs, fp)\n",
203+
"\n",
204+
"Base class for client exceptions (code 4xx) from `url*` functions"
195205
]
196206
},
197207
"execution_count": null,
@@ -213,14 +223,24 @@
213223
"text/markdown": [
214224
"---\n",
215225
"\n",
216-
"### HTTP5xxServerError\n",
226+
"[source](https://github.com/fastai/fastcore/blob/master/fastcore/net.py#L69){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
227+
"\n",
228+
"#### HTTP5xxServerError\n",
217229
"\n",
218230
"> HTTP5xxServerError (url, code, msg, hdrs, fp)\n",
219231
"\n",
220232
"Base class for server exceptions (code 5xx) from `url*` functions"
221233
],
222234
"text/plain": [
223-
"<nbdev.showdoc.BasicMarkdownRenderer>"
235+
"---\n",
236+
"\n",
237+
"[source](https://github.com/fastai/fastcore/blob/master/fastcore/net.py#L69){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
238+
"\n",
239+
"#### HTTP5xxServerError\n",
240+
"\n",
241+
"> HTTP5xxServerError (url, code, msg, hdrs, fp)\n",
242+
"\n",
243+
"Base class for server exceptions (code 5xx) from `url*` functions"
224244
]
225245
},
226246
"execution_count": null,
@@ -293,7 +313,11 @@
293313
" if data is not None:\n",
294314
" if not isinstance(data, (str,bytes)): data = urlencode(data)\n",
295315
" if not isinstance(data, bytes): data = data.encode('ascii')\n",
296-
" return urlopener().open(urlwrap(url, data=data, headers=headers), timeout=timeout)"
316+
" try: return urlopener().open(urlwrap(url, data=data, headers=headers), timeout=timeout)\n",
317+
" except HTTPError as e: \n",
318+
" e.msg += f\"\\n====Error Body====\\n{pformat(json.loads(e.read()))}\"\n",
319+
" raise e\n",
320+
" except Exception as e: raise e"
297321
]
298322
},
299323
{
@@ -316,6 +340,17 @@
316340
" return (res,dict(hdrs)) if return_headers else res"
317341
]
318342
},
343+
{
344+
"cell_type": "code",
345+
"execution_count": null,
346+
"metadata": {},
347+
"outputs": [],
348+
"source": [
349+
"# The error should contain the body of the error, too\n",
350+
"try: urlread('https://api.github.com/v3')\n",
351+
"except HTTPError as e: assert 'documentation_url' in e.msg"
352+
]
353+
},
319354
{
320355
"cell_type": "code",
321356
"execution_count": null,

0 commit comments

Comments
 (0)