Декоратор для выполнения функции журнала по строкам
Я работаю над script, который занимает несколько минут для запуска, и хотел бы предоставить пользователю некоторый результат о его прогрессе. К сожалению, я очень ленив. то, что я хотел бы сделать, это написать функцию без ведения журнала, а затем применить к ней декоратор, который выполняет функцию и печатает каждую строку перед выполнением этой строки. В основном я ищу loggingdecorator
, чтобы:
>>> @loggingdecorator
... def myfunction():
... foo()
... bar()
... baz()
>>> myfunction()
Starting myfunction
foo() ... [OK]
bar() ... [OK]
baz() ... [OK]
myfunction Done!
Вот что я пробовал до сих пор:
import sys
def logging_tracer(frame, event, arg):
def local_tracer(local_frame, event, arg):
if frame is local_frame:
print frame.f_code.co_name, event, arg
print frame.f_code.co_name, event, arg
return local_tracer
def loggingdecorator(func):
def _wrapper():
old_trace_function = sys.gettrace()
sys.settrace(logging_tracer)
try:
result = func()
except:
raise
else:
return result
finally:
sys.settrace(old_trace_function)
return _wrapper
К сожалению, это слишком много печатает; он следует за вызовами функций и распечатывает их также по очереди (ну, на самом деле это не печатает исходную строку, существующие ответы, использующие проверку, в сочетании с вещами на объекте фрейма в функции трассировки, сделают это), но я "немного тупик относительно того, как logging_tracer
, если только соответствующая функция не оформлена.
Ответы
Ответ 1
Итак, вот что я придумал. @Corley Brigman прокомментировал, что я начал в правильном направлении. Это немного хакей, sys.gettrace/settrace
скорее документируются как "детали реализации CPython", и поэтому это решение не должно работать в других реализациях. Тем не менее, похоже, все получилось очень хорошо. Функция трассировки в cpython не предоставляет никакого уведомления о "завершении строки", поэтому [ok]
из моего вопроса на самом деле не имеет никакого смысла.
Исправление проблемы рекурсивного трассировки - это просто вопрос сохранения кеша функций, которые были украшены, а затем выдавать только выходные данные, если отслеживаемый кадр - из функции, которая была украшена.
import inspect
import sys
def logging_tracer(frame, event, arg):
lines, firstline = inspect.getsourcelines(frame)
def local_tracer(local_frame, event, arg):
if event == 'line' and frame is local_frame:
print event, frame.f_lineno,'\t', lines[frame.f_lineno - firstline]
#print event, lines[frame.f_lineno - firstline]
#print frame.f_code.co_name, frame.f_lineno, event, arg
if frame.f_code in LOG_THESE_FUNCTIONS:
print event, frame.f_lineno,'\t', lines[frame.f_lineno - firstline + (event == 'call')]
#print frame.f_code.co_name, event, arg
return local_tracer
else:
return None
LOG_THESE_FUNCTIONS = set()
def loggingdecorator(func):
LOG_THESE_FUNCTIONS.add(func.func_code)
def _wrapper():
old_trace_function = sys.gettrace()
sys.settrace(logging_tracer)
try:
result = func()
except:
raise
else:
return result
finally:
sys.settrace(old_trace_function)
return _wrapper
Ответ 2
Вот уродливый пример, который работает, если есть только один уровень отступов:
import inspect
def myDec(func):
temp = list(inspect.getsourcelines(func)[0])
temp.append(' print("{} Done!")\n'.format(func.__name__))
for index in range(len(temp)-2, 0, -1):
temp.insert(index+1, " print('''{}...[OK]''')\n".format(temp[index].strip().rstrip("\n")))
temp.insert(1, ' print("Starting {}")\n'.format(func.__name__))
temp = "".join(temp)
exec(temp)
return locals()[func.__name__]
def foo():
a = 4+5
list_int = range(100)
foo = myDec(foo)
foo()
Действительно уродливо, хотя...
Ответ 3
Вы можете сделать что-то вроде этого:
>>> class loggingdecorator:
... def __init__(self, func):
... self.func = func
... def __call__(self):
... print "Entering", self.func.__name__
... self.func()
... print "Exited", self.func.__name__
...
Затем с каждой из ваших функций:
>>> @loggingdecorator
... def func1():
... print "Hello from func1(), how are you doing?"
...
>>> @loggingdecorator
... def func2():
... print "Hello from func2(), Whaddup Dawg...?"
...
>>> func1()
Entering func1
Hello from func1(), how are you doing?
Exited func1
>>> func2()
Entering func2
Hello from func2(), Whaddup Dawg...?
Exited func2
Найден пример из этой страницы