title: How to read the response body on Python urllib when the status is an error like 400 which raises an exception?
body: <p>I'm trying to make a request to the GitHub API with Python 3 <code>urllib</code> to create a release, but I made some mistake and it fails with an exception:</p>
  
  <pre><code>Traceback (most recent call last):
    File "./a.py", line 27, in &lt;module&gt;
      'Authorization': 'token ' + token,
    File "/usr/lib/python3.6/urllib/request.py", line 223, in urlopen
      return opener.open(url, data, timeout)
    File "/usr/lib/python3.6/urllib/request.py", line 532, in open
      response = meth(req, response)
    File "/usr/lib/python3.6/urllib/request.py", line 642, in http_response
      'http', request, response, code, msg, hdrs)
    File "/usr/lib/python3.6/urllib/request.py", line 570, in error
      return self._call_chain(*args)
    File "/usr/lib/python3.6/urllib/request.py", line 504, in _call_chain
      result = func(*args)
    File "/usr/lib/python3.6/urllib/request.py", line 650, in http_error_default
      raise HTTPError(req.full_url, code, msg, hdrs, fp)
  urllib.error.HTTPError: HTTP Error 422: Unprocessable Entity
  </code></pre>
  
  <p>GitHub however is nice, and explains why it failed on the response body as shown at: <a href="https://stackoverflow.com/questions/16133923/400-vs-422-response-to-post-of-data/52363900#52363900">400 vs 422 response to POST of data</a></p>
  
  <p>So, how do I read the response body? Is there a way to prevent the exception from being raised?</p>
  
  <p>I've tried to catch the exception and explore it in <code>ipdb</code>, which gives an object of type <a href="https://docs.python.org/3/library/urllib.error.html#urllib.error.HTTPError" rel="noreferrer"><code>urllib.error.HTTPError</code></a> but I couldn't find that body data there, only headers.</p>
  
  <p>The script:</p>
  
  <pre><code>#!/usr/bin/env python3
  
  import json
  import os
  import sys
  
  from urllib.parse import urlencode
  from urllib.request import Request, urlopen
  
  repo = sys.argv[1]
  tag = sys.argv[2]
  upload_file = sys.argv[3]
  
  token = os.environ['GITHUB_TOKEN']
  url_template = 'https://{}.github.com/repos/' + repo + '/releases'
  
  # Create.
  _json = json.loads(urlopen(Request(
      url_template.format('api'),
      json.dumps({
          'tag_namezxcvxzcv': tag,
          'name': tag,
          'prerelease': True,
      }).encode(),
      headers={
          'Accept': 'application/vnd.github.v3+json',
          'Authorization': 'token ' + token,
      },
  )).read().decode())
  # This is not the tag, but rather some database integer identifier.
  release_id = _json['id']
  </code></pre>
  
  <p>usage: <a href="https://stackoverflow.com/questions/38153418/can-someone-give-a-python-requests-example-of-uploading-a-release-asset-in-githu/52354681#52354681">Can someone give a python requests example of uploading a release asset in github?</a></p>