Hace algún tiempo, necesitaba una función de retry en R para manejar la respuesta lenta de los servidores. La función tendría el siguiente comportamiento: (prueba una acción (función o método), y si falla, espera un poco y luego vuelve a intentarlo)x10
Se me ocurrió lo siguiente:
retry <- function(fun, max_trys = 10, init = 0){ suppressWarnings(tryCatch({ Sys.sleep(0.3); if(init<max_trys) {fun} }, error=function(e){retry(fun, max_trys, init = init+1)}))}Funcionó bien. Ahora necesito lo mismo en Python3, así que traté de hacer el mismo código:
import time def retry_fun(fun, max_trys = 10, init=0): try: time.sleep(0.3) if(init<max_trys): fun except: retry_fun(fun, max_trys, init = init+1)Pero cuando lo ejecuto, está bloqueando mi kernel. Como soy un poco principiante en Python, no estoy seguro de qué está causando el bloqueo, y si/cómo se puede pasar una función como argumento a otra función.
Podrías ayudarme ?
Además de poder pasar funciones y usarlas agregando () después del nombre (sintaxis de Python para invocar llamadas), no necesita usar la recursividad; solo ponlo en un bucle:
import time def retry(fun, max_tries=10): for i in range(max_tries): try: time.sleep(0.3) fun() break except Exception: continue except Exception debe cambiarse para capturar una excepción significativa que la función podría generar. El uso de Exception (como lo hice en el ejemplo) generalmente es una mala práctica, ya que detecta una gran clase de excepciones que es posible que no desee capturar.
Aparte de eso, es mejor usar un for-loop lugar de un tercer contador explícito y recursividad (que conduce a una pila de llamadas larga para valores grandes).
Sé que esta es una vieja pregunta. Sin embargo, me gustaría agregar la solución que he preparado. La mejor manera es escribir un decorador de retry que lo vuelva a intentar cuando ocurra una excepción. Además, también puede establecer un retraso exponencial personalizado. El docstring explica cómo puede usar el decorador. Aquí tienes:
import logging import time from functools import partial, wraps def retry(func=None, exception=Exception, n_tries=5, delay=5, backoff=1, logger=False): """Retry decorator with exponential backoff. Parameters ---------- func : typing.Callable, optional Callable on which the decorator is applied, by default None exception : Exception or tuple of Exceptions, optional Exception(s) that invoke retry, by default Exception n_tries : int, optional Number of tries before giving up, by default 5 delay : int, optional Initial delay between retries in seconds, by default 5 backoff : int, optional Backoff multiplier eg value of 2 will double the delay, by default 1 logger : bool, optional Option to log or print, by default False Returns ------- typing.Callable Decorated callable that calls itself when exception(s) occur. Examples -------- >>> import random >>> @retry(exception=Exception, n_tries=4) ... def test_random(text): ... x = random.random() ... if x < 0.5: ... raise Exception("Fail") ... else: ... print("Success: ", text) >>> test_random("It works!") """ if func is None: return partial( retry, exception=exception, n_tries=n_tries, delay=delay, backoff=backoff, logger=logger, ) @wraps(func) def wrapper(*args, **kwargs): ntries, ndelay = n_tries, delay while ntries > 1: try: return func(*args, **kwargs) except exception as e: msg = f"{str(e)}, Retrying in {ndelay} seconds..." if logger: logging.warning(msg) else: print(msg) time.sleep(ndelay) ntries -= 1 ndelay *= backoff return func(*args, **kwargs) return wrapperHay un par de paquetes de Python:
Ejemplo de retroceso
import backoff @backoff.on_exception(backoff.expo, (MyPossibleException1, MyPossibleException2)) def your_function(param1, param2): # Do somethingEjemplo de tenacidad
from tenacity import wait_exponential, retry, stop_after_attempt @retry(wait=wait_exponential(multiplier=2, min=2, max=30), stop=stop_after_attempt(5)) def your_function(param1, param2): # Do something