我正在编写一个需要几分钟才能运行的脚本,并希望向用户提供有关其进度的一些输出。不幸的是,我非常懒惰。我想要做的是编写一个没有日志记录的函数,然后应用一个装饰器,它会逐步执行该函数并在执行该行之前打印每一行。基本上我正在寻找的是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
不幸的是,这打印得太多了;它跟随函数调用并逐行打印出来(好吧,这实际上不打印源代码行,现有的答案使用inspect,结合跟踪函数中的框架对象上的东西就可以了),但是我除非有问题的功能真正被装饰,否则logging_tracer
有点难过。
答案 0 :(得分:4)
所以这就是我想出来的。 @Corley Brigman的评论让我开始朝着正确的方向前进。这有点hackey,sys.gettrace/settrace
相当procnently记录为“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
答案 1 :(得分:1)
如果只有一个缩进级别,这是一个丑陋的例子:
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()
虽然真的很难看......
答案 2 :(得分:0)
您可以这样做:
>>> 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
找到示例