¿Puedo establecer max_retries para requests.request?

El módulo de solicitudes de Python es simple y elegante, pero una cosa me molesta. Es posible obtener una requests.exception.ConnectionError con un mensaje como:

Max retries exceeded with url: ... 

Esto implica que las solicitudes pueden intentar acceder a los datos varias veces. Pero no hay una sola mención de esta posibilidad en ninguna parte de los documentos. Mirando el código fuente no encontré ningún lugar donde pudiera alterar el valor predeterminado (probablemente 0).

Entonces, ¿es posible establecer de alguna manera el número máximo de rebashs para las solicitudes?

Es la biblioteca subyacente de urllib3 que lo vuelve a intentar. Para establecer un conteo máximo diferente de rebashs, use adaptadores de transporte alternativos :

 from requests.adapters import HTTPAdapter s = requests.Session() s.mount('http://stackoverflow.com', HTTPAdapter(max_retries=5)) 

El argumento max_retries toma un entero o un objeto Retry() ; el último le brinda un control detallado sobre qué tipos de fallas se reintentan (un valor entero se convierte en una instancia Retry() que solo maneja las fallas de conexión; los errores posteriores a la conexión no se manejan de manera predeterminada, ya que podrían conducir a un lado -efectos).


Respuesta anterior, anterior a la liberación de solicitudes 1.2.1 :

La biblioteca de requests realmente no hace esto configurable, ni tiene la intención de hacerlo (ver esta solicitud de extracción ). Actualmente (solicitudes 1.1), el recuento de rebashs se establece en 0. Si realmente desea establecerlo en un valor más alto, tendrá que establecerlo globalmente:

 import requests requests.adapters.DEFAULT_RETRIES = 5 

Esta constante no está documentada; úselo bajo su propio riesgo, ya que las futuras versiones podrían cambiar la forma en que se maneja esto.

Actualización : y esto cambió; en la versión 1.2.1 , se agregó la opción de establecer el parámetro max_retries en la clase HTTPAdapter() , de modo que ahora tiene que usar adaptadores de transporte alternativos, consulte más arriba. El enfoque de mono-parche ya no funciona, a menos que también parche el HTTPAdapter.__init__() defecto (muy poco recomendado).

Esto no solo cambiará max_retries sino que también habilitará una estrategia de backoff que realiza solicitudes a todas las direcciones http: // sleep durante un período de tiempo antes de volver a intentarlo (hasta un total de 5 veces):

 import requests from urllib3.util.retry import Retry from requests.adapters import HTTPAdapter s = requests.Session() retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[ 500, 502, 503, 504 ]) s.mount('http://', HTTPAdapter(max_retries=retries)) s.get('http://httpstat.us/500') 

Según la documentación para Retry : si el backoff_factor es 0.1 , entonces sleep () dormirá durante [0.1s, 0.2s, 0.4s, …] entre los rebashs. También forzará un rebash si el código de estado devuelto es 500 , 502 , 503 o 504 .

Varias otras opciones para Retry permiten un control más granular:

  • total : número total de rebashs que se deben permitir.
  • conectar – Cuántos errores relacionados con la conexión reintentar.
  • leer : cuántas veces reintentar en los errores de lectura.
  • redirigir – Cuántos redireccionamientos realizar.
  • method_whitelist – Conjunto de verbos de método HTTP en mayúsculas que debemos reintentar.
  • status_forcelist : un conjunto de códigos de estado HTTP en los que debemos forzar un nuevo bash.
  • backoff_factor : un factor de retroceso para aplicar entre bashs.
  • raise_on_redirect : si, si se ha agotado el número de redireccionamientos, para generar un MaxRetryError , o para devolver una respuesta con un código de respuesta en el rango 3xx .
  • raise_on_status – Significado similar a raise_on_redirect : si deberíamos lanzar una excepción o devolver una respuesta, si el estado cae en el rango status_forcelist y se han agotado los rebashs.

NB : raise_on_status es relativamente nuevo, y aún no se ha convertido en una versión de urllib3 o solicitudes. El argumento de la palabra clave raise_on_status parece haber llegado a la biblioteca estándar como máximo en la versión 3.6 de python.

Para hacer que las solicitudes vuelvan a intentar con códigos de estado HTTP específicos, use status_forcelist . Por ejemplo, status_forcelist = [503] volverá a intentar con el código de estado 503 (servicio no disponible).

Por defecto, el rebash solo se dispara por estas condiciones:

  • No se pudo obtener una conexión de la piscina.
  • TimeoutError
  • HTTPException generado HTTPException (de http.client en Python 3 en otro lugar httplib ). Esto parece ser excepciones HTTP de bajo nivel, como la URL o el protocolo que no se formaron correctamente.
  • SocketError
  • ProtocolError

Observe que estas son todas las excepciones que impiden que se reciba una respuesta HTTP regular. Si se genera una respuesta regular, no se vuelve a intentar. Sin usar el status_forcelist , incluso una respuesta con el estado 500 no se volverá a intentar.

Para hacer que se comporte de una manera más intuitiva para trabajar con una API remota o un servidor web, usaría el fragmento de código anterior, que fuerza los rebashs en los estados 500 , 502 , 503 y 504 , todos los cuales no son infrecuentes en el web y (posiblemente) recuperables dado un período de espera suficientemente grande.

EDITADO : Importe la clase Retry directamente desde urllib3 .

Tenga cuidado, la respuesta de Martijn Pieters no es adecuada para la versión 1.2.1+. No puedes configurarlo globalmente sin parchear la biblioteca.

Puedes hacer esto en su lugar:

 import requests from requests.adapters import HTTPAdapter s = requests.Session() s.mount('http://www.github.com', HTTPAdapter(max_retries=5)) s.mount('https://www.github.com', HTTPAdapter(max_retries=5)) 

Después de luchar un poco con algunas de las respuestas aquí, encontré una biblioteca llamada backoff que funcionó mejor para mi situación. Un ejemplo básico:

 import backoff @backoff.on_exception( backoff.expo, requests.exceptions.RequestException, max_tries=5, giveup=lambda e: e.response is not None and e.response.status_code < 500 ) def publish(self, data): r = requests.post(url, timeout=10, json=data) r.raise_for_status() 

Aun así, recomiendo darle una oportunidad a la funcionalidad nativa de la biblioteca, pero si tiene algún problema o necesita un control más amplio, el backoff es una opción.

Una forma más limpia de obtener un mayor control podría ser empaquetar las cosas de rebash en una función y hacer que esa función sea retraible utilizando un decorador y las excepciones en la lista blanca.

He creado lo mismo aquí: http://www.praddy.in/retry-decorator-whitelisted-exceptions/

Reproduciendo el código en ese enlace:

 def retry(exceptions, delay=0, times=2): """ A decorator for retrying a function call with a specified delay in case of a set of exceptions Parameter List ------------- :param exceptions: A tuple of all exceptions that need to be caught for retry eg retry(exception_list = (Timeout, Readtimeout)) :param delay: Amount of delay (seconds) needed between successive retries. :param times: no of times the function should be retried """ def outer_wrapper(function): @functools.wraps(function) def inner_wrapper(*args, **kwargs): final_excep = None for counter in xrange(times): if counter > 0: time.sleep(delay) final_excep = None try: value = function(*args, **kwargs) return value except (exceptions) as e: final_excep = e pass #or log it if final_excep is not None: raise final_excep return inner_wrapper return outer_wrapper @retry(exceptions=(TimeoutError, ConnectTimeoutError), delay=0, times=3) def call_api(): 
  while page is None: try: page = requests.get(url, timeout=5,proxies=proxies) except Exception: page = None