Получить аргументы вызова последней функции из трассировки?

Могу ли я получить параметры последней функции, вызванной в трассировке? Как?

Я хочу сделать ловушку для стандартных ошибок, чтобы сделать читаемый код, но предоставить пользователю подробную информацию.

В следующем примере я хочу, чтобы GET_PARAMS вернул мне кортеж параметров, переданных в os.chown. Изучив модуль inspect, рекомендованный Алексом Мартелли, я не смог его найти.

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

(Спасибо Джиму Роберту за декоратора)


person culebrón    schedule 30.10.2009    source источник
comment
Ваше за исключением должно быть за исключением (IOError, OSError):. В настоящее время вы только перехватываете IOError, а затем назначаете экземпляр исключения OSError.   -  person jamessan    schedule 30.10.2009


Ответы (3)


Проблема с использованием декоратора для того, чего вы пытаетесь достичь, заключается в том, что кадр, который получает обработчик исключений, равен do_your_job()s, а не os.listdir()s, os.makedirs()s или os.chown()s. Таким образом, информация, которую вы будете распечатывать, — это аргументы do_your_job(). Чтобы получить поведение, которое, как я думаю, вы хотите, вам придется декорировать все библиотечные функции, которые вы вызываете.

person jamessan    schedule 30.10.2009

Для таких задач проверки всегда сначала думайте о модуле inspect. в стандартной библиотеке. Здесь inspect.getargvalues дает вам значения аргументов, указанные кадр, и inspect.getinnerframes дает вам кадры интерес от объекта трассировки.

person Alex Martelli    schedule 30.10.2009

Вот пример такой функции и некоторые проблемы, которые вы не можете обойти:

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': 1, 'c': 4, 'b': 2, 'e': {'g': 6, 'f': 5}, 'd': ()}

Я не использовал inspect.getinnerframes(), чтобы показать другой способ получения нужного кадра. Хотя это немного упрощает процесс, он также выполняет некоторую дополнительную работу, которая вам не нужна, будучи относительно медленным (inspect.getinnerframes() читает исходный файл для каждого модуля в трассировке; это не важно для одного вызова отладки, но может быть проблемой в других случаях). ).

person Denis Otkidach    schedule 30.10.2009
comment
Я не понял: что тормозит - инспектировать или по-вашему? - person culebrón; 31.10.2009
comment
Похоже, ваш код работает, только если внутри функции возникает исключение. - person culebrón; 31.10.2009