Ricevi gli argomenti di chiamata dell'ultima funzione da traceback?
-
22-07-2019 - |
Domanda
Posso ottenere i parametri dell'ultima funzione chiamata in traceback? Come?
Voglio fare un catcher per errori standard per rendere il codice leggibile, ma fornire informazioni dettagliate all'utente.
Nel seguente esempio voglio che GET_PARAMS mi restituisca una tupla di parametri forniti a os.chown. Esaminando il modulo inspect
consigliato da Alex Martelli, non sono riuscito a trovarlo.
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()
(Grazie a Jim Robert per il decoratore)
Soluzione
Il problema con l'utilizzo di un decoratore per quello che stai cercando di ottenere è che il frame che ottiene il gestore delle eccezioni è do_your_job ()
s, non os.listdir ()
s, os.makedirs ()
s o os.chown ()
s. Quindi le informazioni che verranno stampate sono gli argomenti per do_your_job ()
. Per ottenere il comportamento che penso tu abbia intenzione, dovresti decorare tutte le funzioni della libreria che stai chiamando.
Altri suggerimenti
Per tali compiti di ispezione, pensa sempre prima al modulo
Ecco un esempio di tale funzione e alcuni problemi che non è possibile aggirare:
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])
L'output è:
{'a': 1, 'c': 4, 'b': 2, 'e': {'g': 6, 'f': 5}, 'd': ()}
Non ho usato inspect.getinnerframes ()
per mostrare un altro modo per ottenere il frame necessario. Sebbene semplifichi un po ', fa anche qualche lavoro extra che non è necessario per te mentre sei relativamente lento ( inspect.getinnerframes ()
legge il file sorgente per ogni modulo in traceback; questo non è importante per uno chiamata di debug, ma potrebbe essere un problema in altri casi).