Pergunta

Posso obter os parâmetros da última função chamada em Traceback? Como?

Quero fazer um apanhador de erros padrão para criar código legível, mas fornecer informações detalhadas ao usuário.

No exemplo seguinte, quero que get_params me devolva uma tupla de parâmetros fornecidos ao OS.Chown. Examinando o inspect Módulo aconselhado por Alex Martelli, eu não consegui encontrar isso.

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

(Graças a Jim Robert para o decorador)

Foi útil?

Solução

O problema de usar um decorador para o que você está tentando alcançar é que o quadro que o manipulador de exceção recebe é do_your_job()meleca os.listdir()s, os.makedirs()s ou os.chown()s. Portanto, a informação que você estará imprimindo são os argumentos para do_your_job(). Para obter o comportamento, acho que você pretende, você teria que decorar todas as funções da biblioteca que está ligando.

Outras dicas

Para tais tarefas de inspeção, sempre pense primeiro no módulo inspect na biblioteca padrão. Aqui, inspect.getargvalues fornece os valores de argumento que dam um quadro e Inspect.getInnerFrames Dá a você os quadros de interesse de um objeto Traceback.

Aqui está um exemplo dessa função e alguns problemas que você não consegue se locomover:

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

A saída é:

{'a': 1, 'c': 4, 'b': 2, 'e': {'g': 6, 'f': 5}, 'd': ()}

Eu não usei inspect.getinnerframes() para mostrar outra maneira de obter o quadro necessário. Embora simplifique um pouco, também faz um trabalho extra que não é necessário para você, sendo relativamente lento (inspect.getinnerframes() lê o arquivo de origem para cada módulo em Traceback; Isso não é importante para uma chamada de depuração, mas pode ser um problema em outros casos).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top