13

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.

DSM
  • 342,061
  • 65
  • 592
  • 494
ArtOfWarfare
  • 20,617
  • 19
  • 137
  • 193

2 Answers2

34

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
DSM
  • 342,061
  • 65
  • 592
  • 494
  • +1: Very well done! Thank you very much! I had gotten as far as dir(e) and e.args, but I had stopped at that point mistaking the elements of e.args to be strings that I'd have to use regex on to extract the information I wanted. – ArtOfWarfare Oct 14 '13 at 22:58
  • The use of a fake URL was also a nice touch for generating errors. I'd been using real URLs but with my internet shut off... it was inconvenient having to go back and forth between generating the errors and researching them. – ArtOfWarfare Oct 14 '13 at 23:01
  • oh my lord - thank you thank you - this error has been driving me batty! – Aurielle Perlmann Feb 08 '17 at 09:32
  • 2
    In Python 3.8.5 I get errno in `e.errno` and `e.args[0]`; the reason text message in `e.strerror` and `e.args[1]`. – pabouk - Ukraine stay strong May 17 '21 at 15:54
1

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.

Taek
  • 1,004
  • 7
  • 20