我正在编写一个需要几分钟才能运行的脚本,并希望向用户提供有关其进度的一些输出.不幸的是,我非常懒惰.我想要做的是编写一个没有日志记录的函数,然后应用一个装饰器,它会逐步执行该函数并在执行该行之前打印每一行.基本上我正在寻找的是loggingdecorator这样的:
>>> @loggingdecorator
... def myfunction():
... foo()
... bar()
... baz()
>>> myfunction()
Starting myfunction
foo() ... [OK]
bar() ... [OK]
baz() ... [OK]
myfunction Done!
Run Code Online (Sandbox Code Playgroud)
这是我到目前为止所尝试的:
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
Run Code Online (Sandbox Code Playgroud)
不幸的是,这打印得太多了; 它跟随函数调用并逐行打印出来(好吧,这实际上不打印源代码行,现有的答案使用inspect,结合跟踪函数中的框架对象上的东西就可以了),但是我有点难过,logging_tracer除非有问题的功能实际装饰.
这就是我的想法。 @Corley Brigman的评论让我开始朝着正确的方向前进。这有点 hackey,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
Run Code Online (Sandbox Code Playgroud)