Получить аргументы последнего вызова функции из traceback?
-
22-07-2019 - |
Вопрос
Могу ли я получить параметры последней функции, вызванной в 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()
(Благодаря Джим Роберт для декоратора)
Решение
Проблема с использованием декоратора для того, чего вы пытаетесь достичь, состоит в том, что кадр, который получает обработчик исключений, это do_your_job ()
s, а не os.listdir ()
s, os.makedirs ()
s или os.chown ()
s. Таким образом, информация, которую вы будете печатать, является аргументами для do_your_job ()
. Чтобы получить поведение, которое, я думаю, вы намереваетесь, вам нужно украсить все библиотечные функции, которые вы вызываете.
Другие советы
При выполнении таких задач проверки всегда в первую очередь думайте о модуле inspect
в стандартной библиотеке.Здесь, инспектировать.getargvalues выдает вам значения аргументов, заданные для фрейма, и inspect.getinnerframes проверять.getinnerframes выдает вам интересующие вас кадры из объекта обратной трассировки.
Вот пример такой функции и некоторые проблемы, которые вы не можете обойти:
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 ()
читает исходный файл для каждого модуля в трассировке; это не важно для одного вызов отладки, но может быть проблемой в других случаях). Р>