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)

¿Fue útil?

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 inspeccionar en la biblioteca estándar. Aquí, inspect.getargvalues ?? le proporciona los valores de argumento dados un marco, y inspect.getinnerframes le proporciona los marcos de interés de un objeto de rastreo.

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).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top