Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access server response when Python requests library encounters the retry limit

I am using the Python requests library to implement retry logic. Here is a simple script I made to reproduce the problem that I am having. In the case where we run out of retries, I would like to be able to log at least one of the responses from the server to help debugging. However, it is not clear to me how to access that information. Of course, I could implement retries in some other way to accomplish my goal, but it seemed like this wasn't that much of an edge case and I would be surprised to find out that requests did not support my use case.

I have looked at the requests.exceptions.RetryError, the requests.packages.urllib3.exceptions.MaxRetryError that it wraps, and the requests.packages.urllib3.exceptions.ResponseError that that wraps all to no avail.

Am I missing something?

#!/usr/bin/env python

import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
from requests.exceptions import RetryError


def main():
    retry_policy = Retry(
        total=3,
        status_forcelist=[418])

    session = requests.Session()
    session.mount('http://', HTTPAdapter(max_retries=retry_policy))

    try:
        session.get('http://httpbin.org/status/418')
    except RetryError as retry_error:
        print retry_error
        print retry_error.response is None


if __name__ == '__main__':
    main()

$ python test.py

HTTPConnectionPool(host='httpbin.org', port=80): Max retries exceeded with url: /status/418 (Caused by ResponseError('too many 418 error responses',))

True
like image 660
Brett Widmeier Avatar asked Apr 18 '17 20:04

Brett Widmeier


1 Answers

Checking out the urllib3 source, I discovered that the Retry object takes a named parameter called raise_on_status. True is the default. When set to False, running into the retry limit causes a response to be returned instead of an exception being thrown. The code below shows me causing an HTTPError (containing the Response) to be thrown by using the raise_for_status method on the Response, but one could just as easily use the Response directly.

#!/usr/bin/env python

import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
from requests.exceptions import HTTPError


def main():
    retry_policy = Retry(
        total=3,
        status_forcelist=[418],
        raise_on_status=False)

    session = requests.Session()
    session.mount('http://', HTTPAdapter(max_retries=retry_policy))

    try:
        response = session.get('http://httpbin.org/status/418')
        response.raise_for_status()
    except HTTPError as e:
        print e.response.status_code
        print e.response.content


if __name__ == '__main__':
    main()

$ python test.py

418

-=[ teapot ]=-

   _...._
 .'  _ _ `.
| ."` ^ `". _,
\_;`"---"`|//
  |       ;/
  \_     _/
    `"""`
like image 59
Brett Widmeier Avatar answered Sep 21 '22 23:09

Brett Widmeier