Получить аргументы вызова последней функции из traceback? - PullRequest
1 голос
/ 30 октября 2009

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

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

В следующем примере я хочу, чтобы 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()

(Спасибо Джиму Роберту за оформителя)

Ответы [ 3 ]

5 голосов
/ 30 октября 2009

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

3 голосов
/ 30 октября 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() читает исходный файл для каждого модуля при трассировке; это не важно для одного вызова отладки, но может быть проблемой в другие случаи).

0 голосов
/ 30 октября 2009

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

...