Obtener argumentos de llamada de la última función de rastreo?
-
22-07-2019 - |
Pregunta
¿Puedo obtener los parámetros de la última función llamada en rastreo? ¿Cómo?
Quiero hacer un receptor de errores estándar para hacer un código legible, pero proporcionar información detallada al usuario.
En el siguiente ejemplo, quiero que GET_PARAMS me devuelva una tupla de parámetros suministrados a os.chown. Al examinar el módulo inspeccionar
aconsejado por Alex Martelli, no pude encontrar eso.
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()
(Gracias a Jim Robert por el decorador)
Solución
El problema con el uso de un decorador para lo que está tratando de lograr es que el marco que obtiene el controlador de excepciones es do_your_job ()
s, no os.listdir ()
s, os.makedirs ()
s u os.chown ()
s. Entonces, la información que imprimirá son los argumentos de do_your_job ()
. Para obtener el comportamiento que creo que pretendes, deberías decorar todas las funciones de la biblioteca que estás llamando.
Otros consejos
Para tales tareas de inspección, siempre piense primero en el módulo
Aquí hay un ejemplo de dicha función y algunos problemas que no puede solucionar:
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])
El resultado es:
{'a': 1, 'c': 4, 'b': 2, 'e': {'g': 6, 'f': 5}, 'd': ()}
No utilicé inspect.getinnerframes ()
para mostrar otra forma de obtener el marco necesario. Aunque se simplifica un poco, también hace un trabajo adicional que no es necesario para usted, mientras que es relativamente lento ( inspect.getinnerframes ()
lee el archivo fuente para cada módulo en rastreo; esto no es importante para uno llamada de depuración, pero podría ser un problema en otros casos).