2009-10-30 2 views
1

Могу ли я получить параметры последней функции, вызванной в 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() 

(Благодаря Jim Robert для декоратора)

+2

Ваш исключение должен быть «за исключением (IOError, OSError):». Как и в настоящее время, вы только ловите IOError и затем назначаете экземпляр исключения OSError. – jamessan

+0

Упс. Спасибо, jamessan. –

ответ

0

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

5

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

+0

+1 Мартелли учит меня другой вещи, которую я не знал. – Yarin

3

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

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

Я не понял: что медленно - осмотрите или по-вашему? –

+0

Кажется, что ваш код работает только в том случае, если внутри функции возникает исключение. –