Skip to content

Commit

Permalink
catch network exceptions when transferring data
Browse files Browse the repository at this point in the history
This fixes #78 by moving the call to f.read() inside the exception-handling
structure of _safe_open (now _safe_read). The function now returns a string,
which util.bytes_to_elementtree has been modified to accept.

Signed-off-by: Adrian Sampson <adrian@radbox.org>
  • Loading branch information
sampsyo committed Mar 6, 2013
1 parent 747c6de commit 5bd7f98
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 15 deletions.
12 changes: 5 additions & 7 deletions musicbrainzngs/musicbrainz.py
Expand Up @@ -384,11 +384,11 @@ def get_method(self):

# Core (internal) functions for calling the MB API.

def _safe_open(opener, req, body=None, max_retries=8, retry_delay_delta=2.0):
def _safe_read(opener, req, body=None, max_retries=8, retry_delay_delta=2.0):
"""Open an HTTP request with a given URL opener and (optionally) a
request body. Transient errors lead to retries. Permanent errors
and repeated errors are translated into a small set of handleable
exceptions. Returns a file-like object.
exceptions. Return a bytestring.
"""
last_exc = None
for retry_num in range(max_retries):
Expand All @@ -401,6 +401,7 @@ def _safe_open(opener, req, body=None, max_retries=8, retry_delay_delta=2.0):
f = opener.open(req, body)
else:
f = opener.open(req)
return f.read()

except compat.HTTPError as exc:
if exc.code in (400, 404, 411):
Expand Down Expand Up @@ -437,9 +438,6 @@ def _safe_open(opener, req, body=None, max_retries=8, retry_delay_delta=2.0):
raise NetworkError(cause=exc)
except IOError as exc:
raise NetworkError(cause=exc)
else:
# No exception! Yay!
return f

# Out of retries!
raise NetworkError("retried %i times" % max_retries, last_exc)
Expand Down Expand Up @@ -516,11 +514,11 @@ def _mb_request(path, method='GET', auth_required=False, client_required=False,
# Explicitly indicate zero content length if no request data
# will be sent (avoids HTTP 411 error).
req.add_header('Content-Length', '0')
f = _safe_open(opener, req, body)
resp = _safe_read(opener, req, body)

# Parse the response.
try:
return mbxml.parse_message(f)
return mbxml.parse_message(resp)
except UnicodeError as exc:
raise ResponseError(cause=exc)
except Exception as exc:
Expand Down
23 changes: 15 additions & 8 deletions musicbrainzngs/util.py
Expand Up @@ -27,11 +27,18 @@ def _unicode(string, encoding=None):
unicode_string = compat.unicode(string)
return unicode_string.replace('\x00', '').strip()

def bytes_to_elementtree(_bytes):
if compat.is_py3:
s = _unicode(_bytes.read(), "utf-8")
else:
s = _bytes.read()
f = compat.StringIO(s)
tree = ET.ElementTree(file=f)
return tree
def bytes_to_elementtree(bytes_or_file):
"""Given a bytestring or a file-like object that will produce them,
parse and return an ElementTree.
"""
if isinstance(bytes_or_file, compat.basestring):
s = bytes_or_file
else:
s = bytes_or_file.read()

if compat.is_py3:
s = _unicode(s, "utf-8")

f = compat.StringIO(s)
tree = ET.ElementTree(file=f)
return tree

0 comments on commit 5bd7f98

Please sign in to comment.