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)

È stato utile?

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 inspect nella libreria standard. Qui, inspect.getargvalues ?? fornisce i valori degli argomenti forniti un frame e inspect.getinnerframes ti dà i frame di interesse da un oggetto traceback.

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top