我正在使用python的trace
模块来跟踪一些代码。当我以这种方式跟踪代码时,我可以得到以下两个结果之一:
拨打:
tracer = trace.Trace(count=False, trace=True, ignoredirs=[sys.prefix, sys.exec_prefix])
r = tracer.run('run()')
tracer.results().write_results(show_missing=True)
结果:
<filename>(<line number>): <line of code>
致电 [citation]:
tracer = trace.Trace(count=False, trace=True, ignoredirs=[sys.prefix, sys.exec_prefix], countfuncs=True)
r = tracer.run('run()')
tracer.results().write_results(show_missing=True)
结果:
filename:<filepath>, modulename:<module name>, funcname: <function name>
我真正需要的是一条能让我这样做的痕迹:
<filepath> <line number>
似乎我可以使用上述信息并将它们交错以获得我需要的信息,但是在以下用例中这样的尝试会失败:
sys.path
包含目录A
和目录B
。A/foo.py
和B/foo.py
A/foo.py
和B/foo.py
都包含在第100 - 120行定义的函数bar
A/foo.py
和B/foo.py
在这种情况下,使用这种交错来正确识别调用哪个bar
是不可能的(如果我错了请纠正我),而不是静态分析每个bar
中的代码,这本身就是非常很难获得非平凡的功能。
那么,我怎样才能获得所需的正确跟踪输出?
答案 0 :(得分:1)
通过一点点猴子修补,这实际上非常简单。在trace
模块的源代码中挖掘,似乎回调用于报告每个执行步骤。 Trace.run
的基本功能(大大简化)是:
sys.settrace(globaltrace) # Set the trace callback function
exec function # Execute the function, invoking the callback as necessary
sys.settrace(None) # Reset the trace
globaltrace
在Trace.__init__
中定义,具体取决于传递的参数。具体来说,使用第一个示例中的参数,Trace.globaltrace_lt
用作全局回调,它为每个执行行调用Trace.localtrace_trace
。更改它只是修改Trace.localtrace
的情况,因此要获得您想要的结果:
import trace
import sys
import time
import linecache
class Trace(trace.Trace):
def localtrace_trace(self, frame, why, arg):
if why == "line":
# record the file name and line number of every trace
filename = frame.f_code.co_filename
lineno = frame.f_lineno
if self.start_time:
print '%.2f' % (time.time() - self.start_time),
print "%s (%d): %s" % (filename, lineno,
linecache.getline(filename, lineno)),
return self.localtrace
tracer = Trace(count=False, trace=True, ignoredirs=[sys.prefix, sys.exec_prefix])
r = tracer.run('run()')
您给出的两个例子之间存在差异;如果在Trace.run
调用期间打印输出的第一个输出,则在write_results
期间打印输出。我上面给出的代码遵循前者的模式,因此tracer.results().write_results()
不是必需的。但是,如果您想要操作此输出,则可以通过以类似方式修补trace.CoverageResults.write_results
方法来实现。