跟踪文件路径和行号 [英] Tracing fIle path and line number
问题描述
我正在使用python的trace
模块来跟踪一些代码.当我以这种方式跟踪代码时,可以获得以下两个结果之一:
I'm using python's trace
module to trace some code. When I trace code this way, I can get one of the following two results:
致电:
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>
致电 [引用]:
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>
我真正需要的是能给我带来帮助的痕迹:
What I really need is a trace that gives me this:
<filepath> <line number>
似乎我可以使用上面的信息并对其进行交织以获得所需的信息,但是在以下用例中,这样的尝试将失败:
It would seem that I could use the above information and interleave them to get what I need, but such an attempt would fail in the following use case:
-
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
之间有一些细微差别
sys.path
contains directoryA
and directoryB
.- There are two files
A/foo.py
andB/foo.py
- Both
A/foo.py
andB/foo.py
contain the functionbar
, defined on lines 100 - 120 - There are some minor differences between
A/foo.py
andB/foo.py
在这种情况下,如果不静态分析每个bar
中的代码,就不可能使用这种交织来正确识别哪个bar
被调用(如果我错了,请纠正我),这对于非bar
本身是非常困难的.琐碎的功能.
In this scenario, using such interleaving to correctly identifying which bar
is called is impossible (please correct me if I'm wrong) without statically analyzing the code within each bar
, which itself is very difficult for non-trivial functions.
那么,如何获得所需的正确跟踪输出?
So, how can I get the correct trace output that I need?
推荐答案
使用一些猴子补丁,实际上很容易.深入研究trace
模块的源代码,似乎在每个执行步骤中都使用了回调报告. Trace.run
的基本功能(已大大简化)是:
With a little monkey-patching, this is actually quite easy. Digging around in the source code of the trace
module it seems that callbacks are used to report on each execution step. The basic functionality of Trace.run
, greatly simplified, is:
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
的一种情况,因此要获得所需的结果:
globaltrace
is defined in Trace.__init__
depending on the arguments passed. Specifically, with the arguments in your first example, Trace.globaltrace_lt
is used as the global callback, which calls Trace.localtrace_trace
for each line of execution. Changing it is simply a case of modifying Trace.localtrace
, so to get the result you want:
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
方法来实现.
There is a difference between the two examples you give; if the first the output is printed during the Trace.run
call, in the second it is printed during write_results
. The code I've given above follows the pattern of the former, so tracer.results().write_results()
is not necessary. However, if you want to manipulate this output instead it can be achieved by patching the trace.CoverageResults.write_results
method in a similar manner.
这篇关于跟踪文件路径和行号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!