Egregious Python Type Violations
requests.exceptions.ConnectionError inherits from requests.exceptions.RequestException,
which in turn inherits from IOError which in turn inherits from EnvironmentError.
EnvironmentError is described as containing a 2-tuple value: on element 0,
errno and on element 1, strerror (presumably a string description of the
error). For example:
try:
f = open("/foo")
except IOError as e:
print("errno: %s" % e.errno)
print("strerror: %s" % e.strerror)
However, requests seems to take this concept of the 2-tuple errno/strerror pair
to the extreme. Instead of an integer and string, a ConnectionError gets as its
pair a requests.package.urllib3.exceptions.ProtocolError on element 0 and None
on element 1. ProtocolError similarly violates the errno/strerror tuple promise,
setting a string on element 0 and a socket.gaierror on element 1. Neither
ConnectionError nor ProtocolError set their errno or strerror attributes,
so to actually get a string description of a ConnectionError returned by requests,
we have to access the args attribute directly: e.args[0].args[0] (or more
cryptically: e[0][0]). Don’t forget to except the inevitable IndexError
inside your except block for when indexes change out from under you without
warning.