Obtenir les arguments d'appel de la dernière fonction à partir de traceback?
-
22-07-2019 - |
Question
Puis-je obtenir les paramètres de la dernière fonction appelée dans traceback? Comment?
Je souhaite créer un récupérateur des erreurs standard afin de rendre le code lisible, tout en fournissant des informations détaillées à l'utilisateur.
Dans l'exemple suivant, je souhaite que GET_PARAMS me renvoie un tuple de paramètres fournis à os.chown. En examinant le module inspect
conseillé par Alex Martelli, je n’ai pas trouvé cela.
def catch_errors(fn):
def decorator(*args, **kwargs):
try:
return fn(*args, **kwargs)
except (IOError, OSError):
msg = sys.exc_info()[2].tb_frame.f_locals['error_message']
quit(msg.format(SEQUENCE_OF_PARAMETERS_OF_THE_LAST_FUNCTION_CALLED)\
+ '\nError #{0[0]}: {0[1]}'.format(sys.exc_info()[1].args), 1)
return decorator
@catch_errors
def do_your_job():
error_message = 'Can\'t change folder ownership \'{0}\' (uid:{1}, gid:{2})'
os.chown('/root', 1000, 1000) # note that params aren't named vars.
if __name == '__main__' and os.getenv('USERNAME') != 'root':
do_your_job()
(Merci à Jim Robert pour le décorateur)
La solution
Le problème avec l'utilisation d'un décorateur pour ce que vous essayez d'obtenir est que le cadre obtenu par le gestionnaire d'exceptions est do_your_job ()
, et non os.listdir ()
s, os.makedirs ()
ou s os.chown ()
. Les informations que vous allez imprimer sont donc les arguments de do_your_job ()
. Pour obtenir le comportement que je pense que vous souhaitez, vous devez décorer toutes les fonctions de la bibliothèque que vous appelez.
Autres conseils
Pour ces tâches d’inspection, pensez toujours au module inspecter
dans la bibliothèque standard. inspect.getargvalues ?? vous donne les valeurs d'argument données. un cadre et inspect.getinnerframes vous donne les cadres d'intérêt d'un objet de traçage.
Voici un exemple d'une telle fonction et de problèmes que vous ne pouvez pas contourner:
import sys
def get_params(tb):
while tb.tb_next:
tb = tb.tb_next
frame = tb.tb_frame
code = frame.f_code
argcount = code.co_argcount
if code.co_flags & 4: # *args
argcount += 1
if code.co_flags & 8: # **kwargs
argcount += 1
names = code.co_varnames[:argcount]
params = {}
for name in names:
params[name] = frame.f_locals.get(name, '<deleted>')
return params
def f(a, b=2, c=3, *d, **e):
del c
c = 4
e['g'] = 6
assert False
try:
f(1, f=5)
except:
print get_params(sys.exc_info()[2])
Le résultat est:
{'a': 1, 'c': 4, 'b': 2, 'e': {'g': 6, 'f': 5}, 'd': ()}
Je n'ai pas utilisé inspect.getinnerframes ()
pour indiquer un autre moyen d'obtenir l'image requise. Bien que cela simplifie un peu, il effectue également un travail supplémentaire qui n’est pas nécessaire mais qui est relativement lent ( inspect.getinnerframes ()
lit le fichier source pour chaque module de traceback; ce n’est pas important pour un appel de débogage, mais pourrait poser problème dans d’autres cas).