Logique de nouvelle tentative Python basée sur le code de retour et la sortie
J'essaie d'écrire une fonction qui exécute la commande donnée avec un nombre donné de tentatives et de délais. Si le code de retour est 0 et que la chaîne de sortie a attendu_output (partiel) doit rompre la boucle et renvoyer le code de retour et la sortie. Y a-t-il mieux pour écrire ce 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
Réponses
Sans entrer dans un code golf'esque "le moyen le plus court possible d'écrire cette fonction", je vais juste noter quelques alternatives à votre implémentation particulière, et vous pourrez vous demander si vous pensez qu'elles sont plus simples ou non.
if ret == 0équivaut àif not ret. Bien que ce ne soit probablement pas plus lisible dans l'ifinstruction, il se peut que vous ayez nommé cette variable, par exempleprogram_failed = not ret.expected_out in outputest juste une chose que quelqu'un pourrait vouloir faire pour évaluer si la sortie était ce qu'il voulait (par exemple, peut-être à la place qu'il veut faire une vérification regex). Vous voudrez peut-être remplacer cela par une fonction totalement libre, puis appelez simplement cette fonction et utilisez son résultat; par exemple,if ret == 0 and output_ok(output):En s'appuyant sur ces deux éléments, il serait peut-être plus joli de nommer les conditions:
status_ok = ret == 0 output_ok = check_output(output) if status_ok and output_ok: # ...En raison de la nature de la
ifdéclaration, laelsecondition est superflue. Ce serait équivalent à écrire:if condition: break time.sleep(delay)Vous savez
return ret, outputsi le programme réussit ou non, ce qui peut prêter à confusion. Puisque le but de votre fonction est de vérifier la sortie du programme et de réessayer en cas d'échec, il semble étrange de retourner "avec succès" si le programme échoue à ces vérifications simplement parce qu'il n'a plus de tentatives. Envisagez plutôt de lever une exception.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 et potentiellement d' autres stylers Python pourraient se fâcher à vous pour avoir une variable nommée vous ne l' utilisez pas:
i. Pensez à le remplacer par_:for _ in range(retry): # ...Je vais simplement supposer que ce
cmd check_output...n'est qu'une faute de frappe, puisque Python utilisedef.Écrivez un docstring!
Encore une fois, vous ne pensez peut-être pas que tout ce qui précède est des améliorations, juste des alternatives. Si vous les avez tous suivis, vous pourriez vous retrouver avec ce qui suit:
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