Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get Errno from Python Requests ConnectionError?

I'm catching and printing Python Requests ConnectionErrors fine with just this:

except requests.exceptions.ConnectionError as e:
    logger.warning(str(e.message))

It prints out messages such as:

HTTPSConnectionPool(host='10.100.24.16', port=443): Max retries exceeded with url: /api/datastores/06651841-bbdb-472a-bde2-689d8cb8da19 (Caused by <class 'socket.error'>: [Errno 61] Connection refused)

and

HTTPSConnectionPool(host='10.100.24.16', port=443): Max retries exceeded with url: /api/datastores/06651841-bbdb-472a-bde2-689d8cb8da19 (Caused by <class 'socket.error'>: [Errno 65] No route to host)

And many others. What I'm wondering is, what's the best, most Pythonic, way to get that errno that's displaying in the message? I'd like to have a reliable system for catching the issues and offering as helpful and relevant error message to the user as possible. As far as I can tell, ConnectionError is an indirect decedent of BaseException, with no new properties or methods being added beyond what BaseException offers. I'm hesitant to simply use regex because it seems to me I run the risk of assuming all error messages are formatted the same way in all localities.

like image 346
ArtOfWarfare Avatar asked Oct 14 '13 22:10

ArtOfWarfare


2 Answers

I think you can access it using e.args[0].reason.errno.

This is probably documented somewhere, but usually when I have to track down something like this I just try it at the console and dig around a little bit. (I use IPython so it's easy to do tab-inspection, but let's try it without).

First, let's generate an error using

import requests
try:
    requests.get("http://not.a.real.url/really_not")
except requests.exceptions.ConnectionError as e:
    pass

which should give us the error in e:

>>> e
ConnectionError(MaxRetryError("HTTPConnectionPool(host='not.a.real.url', port=80): Max retries exceeded with url: /really_not (Caused by <class 'socket.gaierror'>: [Errno -2] Name or service not known)",),)

Information is usually in args:

>>> e.args
(MaxRetryError("HTTPConnectionPool(host='not.a.real.url', port=80): Max retries exceeded with url: /really_not (Caused by <class 'socket.gaierror'>: [Errno -2] Name or service not known)",),)
>>> e.args[0]
MaxRetryError("HTTPConnectionPool(host='not.a.real.url', port=80): Max retries exceeded with url: /really_not (Caused by <class 'socket.gaierror'>: [Errno -2] Name or service not known)",)

Looking inside, we see:

>>> dir(e.args[0])
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__',
 '__getitem__', '__getslice__', '__hash__', '__init__', '__module__', '__new__',
 '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__',
 '__str__', '__subclasshook__', '__unicode__', '__weakref__', 'args', 'message', 'pool',
 'reason', 'url']

reason looks encouraging:

>>> e.args[0].reason
gaierror(-2, 'Name or service not known')
>>> dir(e.args[0].reason)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__',
 '__getitem__', '__getslice__', '__hash__', '__init__', '__module__', '__new__',
 '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__',
 '__str__', '__subclasshook__', '__unicode__', '__weakref__', 'args', 'errno', 'filename',
 'message', 'strerror']
>>> e.args[0].reason.errno
-2
like image 102
DSM Avatar answered Oct 15 '22 12:10

DSM


I had troubles getting the same results with python 3.6 and requests 2.18. I managed to get the errno using the http and socket modules :

import socket, html
try:
    http.client.HTTPConnection('invalid').connect()
except (socket.gaierror, ConnectionError) as e:
    print(e.errno)

Hopefully it helps someonelse.

like image 33
Taek Avatar answered Oct 15 '22 10:10

Taek