Python retry logic based on return code and output
I am trying to write a function which execute the given command with given number of retry and delay. If the return code is 0 and output string have expected_output(partial) have to break the loop and return the return code and output. Is there any better to write this code?
cmd check_output_and_retry(cmd, expected_output, delay=1, retry=3):
for i in range(retry):
# Run the given command.
# Return value will be return code with cmd output.
ret, output = execute_cmd(cmd)
if ret == 0 and expected_out in output:
break
else:
time.sleep(delay)
return ret, output
Respostas
Without getting into a code golf'esque "shortest possible way to write this function", I'll just note a few alternatives to your particular implementation, and you can consider if you think they're simpler or not.
if ret == 0is equivalent toif not ret. While that's probably not more legible in theifstatement, it might be if you named this variable, e.g.program_failed = not ret.expected_out in outputé apenas uma coisa que alguém pode querer fazer para avaliar se a saída foi o que eles queriam (por exemplo, talvez em vez disso, eles queiram fazer uma verificação regex). Você pode querer substituir isso por uma função de forma totalmente livre, então apenas chame essa função e use seu resultado; por exemplo,if ret == 0 and output_ok(output):Com base em ambos, pode ser mais bonito nomear as condições:
status_ok = ret == 0 output_ok = check_output(output) if status_ok and output_ok: # ...Devido à natureza da
ifdeclaração, aelsecondição é supérflua. Seria equivalente a escrever:if condition: break time.sleep(delay)Você
return ret, outputquer o programa seja bem-sucedido ou não, o que pode ser confuso. Como o objetivo da sua função é verificar a saída do programa e tentar novamente se ele falhar, parece estranho retornar "com êxito" se o programa falhar nessas verificações apenas porque está sem tentativas. Em vez disso, considere levantar uma exceção.for _ in range(retry): # ... if condition: break # ... else: raise ValueError("Failed to run program successfully, <some useful information about ret, output, etc.>") return output # Only output, since ``ret`` is guaranteed to be 0PyCharm e potencialmente outros stylers Python pode ficar chateado com você por ter uma variável chamada você não usar:
i. Considere substituir por_:for _ in range(retry): # ...Vou apenas supor que
cmd check_output...é apenas um erro de digitação, já que o Python usadef.Escreva uma docstring!
Novamente, você pode não pensar que todos os itens acima são melhorias, apenas alternativas. Se você seguiu todos eles, pode acabar com o seguinte:
def check_output_and_retry(cmd, check_output: lambda out: True, delay=1, retry=3):
"""Runs command until it succeeds. Raises a ValueError if it can't get the program to succeed.
Args:
cmd (str): The command to run
check_output (callable): A function that takes the output string and returns whether or not it's ok
delay (int|float): The number of seconds to wait between attempts
retry (int): The number of times to try running the command before failing
Returns:
str: The program output. The program return code is guaranteed to be 0.
"""
for _ in range(retry):
# Run the given command.
ret, output = execute_cmd(cmd)
status_ok = ret == 0
output_ok = check_output(output)
if status_ok and output_ok:
break
time.sleep(delay)
else:
raise ValueError(f"Program failed to execute within {retry} attempts (status: {ret}: {output}")
# Return value will be cmd output, since the return code is guaranteed to always be 0
return output