在Python中改进堆栈追踪钩子
大家好,
我有一个问题,跟这个问题2617120有点像,可以在这里找到:
提问者想知道如何让Python在通过跟踪钩子执行时打印出函数的参数。
我想要的东西跟这个很相似,但有点不同。我希望在代码运行时对其进行eval(执行),并打印出任何被执行的变量。例如,使用以下代码:
for modname in modnames:
if not modname or '.' in modname:
continue
...
这个跟踪钩子会导致以下内容被打印出来:
for modname in modnames: | for init in init,., encoding
|
if not modname or '.' in modname: | if not init or '.' in init
continue | continue
if not modname or '.' in modname: | if not . or '.' in .
... |
这里的代码行会根据当前的运行环境进行插值。我在Perl中做过这个,在某些情况下真的是个救星。
有没有人知道在Python中实现这个的最佳方法?我有一些想法,但我想听听大家的看法(如果有人有现成的解决方案也可以分享)
顺便说一下,这里是参考代码:
import sys
import linecache
import random
def traceit(frame, event, arg):
if event == "line":
lineno = frame.f_lineno
filename = frame.f_globals["__file__"]
if filename == "<stdin>":
filename = "traceit.py"
if (filename.endswith(".pyc") or
filename.endswith(".pyo")):
filename = filename[:-1]
name = frame.f_globals["__name__"]
line = linecache.getline(filename, lineno)
print "%s:%s:%s: %s" % (name, lineno,frame.f_code.co_name,line.rstrip())
return traceit
def main():
print "In main"
for i in range(5):
print i, random.randrange(0, 10)
print "Done."
sys.settrace(traceit)
main()
1 个回答
0
这里有个简单的小技巧,可以帮助你开始处理一行文本,假设这行文本存储在 line
里,而当前的堆栈框架在 frame
中(顺便说一下,这个是 traceit
函数的内部功能)。
import re
from types import *
def interpolatevar(matchobj):
excludetypes = set((NoneType, TypeType, FunctionType, LambdaType, ClassType,
CodeType, InstanceType, MethodType, BuiltinFunctionType,
BuiltinMethodType))
var = matchobj.group(0)
basevar = var.split(".")[0]
if basevar in frame.f_code.co_names or basevar in frame.f_code.co_varnames:
if basevar in frame.f_globals or basevar in frame.f_locals:
val = eval(var, frame.f_globals, frame.f_locals)
if type(val) not in excludetypes:
return repr(val)
return var
line = re.sub(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*",
interpolatevar, line)
这个方法使用正则表达式来查找标识符,所以即使它们在字符串中,也会被愚蠢地找到。不过,标识符必须在函数中实际使用,并且在局部或全局范围内定义。
它可以处理对象属性的访问(比如 foo.bar
会被替换成对应的值),而我最初发布的版本是做不到这一点的。同时,它会过滤掉各种类型的值,因为把 foo
替换成 <function foo at 0x02793470>
并不会告诉你什么新信息。(当然,如果你对某些类型的值感兴趣,排除列表是可以轻松自定义的,或者你也可以从 types
模块中添加其他类型。)
从长远来看,我觉得查看这行代码的字节码可能会更有帮助,因为那样更容易识别哪些标记实际上是标识符。ast
模块也可能有用,它可以生成语句的解析树,这样你就能搞清楚标识符是什么,但在只看到一行代码的情况下,这对条件语句和循环来说会有些麻烦。