Utilizando decorador e a fórmula de Bhaskara para modificar uma função e criar um laço de repetição com tempo de espera incremental no Python.
Motivação
Atualmente um dos problemas ao desenvolver é ter que esperar para que o código consiga executar normalmente, um exemplo clássico é a na utilização de Web Scraping, que nada mais é do que uma raspagem de dados na Web.
Às vezes precisamos esperar terminar um download para manipular aquele arquivo e não sabemos quanto tempo irá demorar, às vezes as páginas carregam um Loading para adicionar conteúdo, e se nesse meio tempo o nosso script tentar fazer alguma manipulação irá gerar erro e terminar a execução.
Pensando nisso eu resolvi criar uma função que trabalharia como decorador, no Python.
O decorador no Python é um elemento extremamente significante e também é conhecido como meta-programação, sua ideia principal é modificar uma outra função criando uma nova funcionalidade para a mesma. Com isso ela permite que o código seja menor, mais limpo e acaba se tornando Pythônico.
Ideia
A ideia é criar uma função que modifica outras funções, fazendo com que as funções sejam executadas repetidas vezes até que ela retorne sem erro ou estoure o tempo de sua execução, abaixo descrevo os requisitos da função:
Deve haver um laço de repetição para a função que será modificada
O tempo de espera será incremental de 1 em 1 segundo para cada laço de repetição, isto é a cada vez que repetir a tentativa o script irá esperar 1 segundo a mais que na tentativa anterior, assim caracterizando um soma de Gauss no tempo de espera.
O tempo total de espera padrão será de 60 segundos ou poderá ser informado pelo usuário no momento da chamada da função e o mesmo deve ser informado em segundos.
Retornar erro caso o tempo de espera total for maior que retorno sucesso.
Desenvolvimento
Para desenvolver o tempo incremental de 1 em 1 segundo e somente solicitar ao usuário o tempo total em segundos da execução preciso calcular o inverso da soma de Gauss (com ajuda do meu amigo João Francisco Vieira Rodrigues Filho conseguimos chegar ao cálculo), e o que é a soma de Gauss:
A soma de Gauss é um método para somar uma sequência de números entre eles. por exemplo, a soma de Gauss de 10 será a soma de 1+2+3+4+5+6+7+8+9+10, totalizando 55. Esse cálculo pode ser feito de cabeça graças a teoria da soma de Gauss.
Você deve somar o último número com o primeiro, no caso da soma de Gauss o primeiro sempre será 1, depois você irá multiplicar pela meta do número, com isso conseguimos a seguinte equação Soma de Gauss = (x+1)(x/2), simplificando e chamando a soma de Gauss de y teremos x²+x-2y=0
Com isso teremos que fazer um cálculo de equação do 2º grau, por exemplo y=55:
x² + x - 110= 0, Com isso temos a, b, e c onde a = 1, b = 1 e c = -110
A fórmula de ∆ = b² - 4 * a * c, evoluindo o cálculo ∆ = 1² - 4 * 1 * - 110 resultando ∆ = 441
A fórmula de Bhaskara = (-b+√∆)/2a, evoluindo o cálculo (-1+√441)/21, temos o resultado positivo 10 (Omiti o resultado negativo pois não iremos utilizar por se tratar de tempo e não existir tempo negativo).
Caso queiram dar uma força no artigo que publiquei no Linkedin será muito bem vindo.
Código
def retry(time:int=60):
'''Decorador utilizado para fazer uma função retentar após
segundos incrementais a cada nova tentativa.
---
### `time` [Opcional]: é o tempo total das tentativas.
Construção obrigatória
Obrigatoriamente posicionado antes da função que será chamado
>>> @Retry()
ou
>>> @Retry(time=15)
'''
def decorator(function):
# Calculate Delta
delta = 1-4*-1*time*2
# Rounds is the unknown that will be used in the sum of gauss
rounds = (-1+math.sqrt(delta))/2
def wrapper(*args,**kwargs):
count=1
while count<=rounds:
try:
print(f'Tentativa {count}')
func_response = function(*args,**kwargs)
break
except Exception as error:
sleep(count)
count+=1
func_response=None
print(str(error))
return func_response
return wrapper
return decorator
Exemplo de uso
@retry(time=10)
def teste():
#Está função apenas retorna um erro simples
raise Exception("Erro genérico")
teste()