大家在debug Python的时候有没有遇到如下的情况:我知道某个变量出了问题,但是我不知道它是在哪里被修改的,于是只好在每一个可能修改它的地方都打个print。
在gdb里,有一个很好用的feature叫watchpoints,和监控pc的breakpoint对应的,它可以监控一块内存,当这块内存被修改的时候中断程序。
然而Python自带的pdb里并没有这个功能,于是我花了一些时间做了一个功能类似的库,watchpoints!
我的核心思路是,打造一个超轻量级,极其符合人类直觉的debug工具。为此用了不少的黑科技。
从最简单的开始吧,假设我们有一个变量a
a = 0
我们希望每次a产生变化的时候,都打印出来a之前的值,和变化之后的值,以及它变化的位置。
from watchpoints import watch
a = 0
watch(a)
a = 1
是的,没有比这更符合人类直觉的用法了,我想观察a,我就watch(a)
运行之后得到
> (my_script.py:4):
> a = 1
a:
0
->
1
在程序的第四行,我们把变量a从0变成了1。
不光是单纯的variable,watchpoints还支持attribute和index。比如我就想观察某一个dict里的某一个key
from watchpoints import watch
d = {"a": 0, "b": 0}
watch(d["a"])
d["a"] = 1
d["b"] = 1
运行后得到
> (my_script.py:4):
> d["a"] = 1
d["a"]:
0
->
1
d["a"]的变化被记录下来了,而d["b"]没有。
基本上你能想到的符合人类直觉的用法,都可以直接watch(),在绝大部分情况下,都会给你想要的结果。
而和一些其他的local variable monitor工具(比如pysnooper)不同的是,watchpoints并不会被scope局限。
from watchpoints import watch
def generate():
a = []
watch(a)
return a
def change(l):
l.append(1)
lst = generate()
change(lst)
在任何一个地方我们都可以watch()一个变量(object),在scope之外的修改也会被捕捉到。
> change (my_script.py:8):
> l.append(1)
a:
[]
->
[1]
有的时候,我们不光是希望捕捉变化,我们希望捕捉变化到某个状态的时刻。比如有一个值理论上是非负整数,但是我发现它变成负数了。我们可以通过conditional callback来完成这件事。
from watchpoints import watch, unwatch
class MyObj:
def __init__(self):
self.val = 0
obj = MyObj()
watch(obj.val, when=lambda x: x < 0)
for i in range(10):
obj.val += 1
obj.val = -1 # oops
这种情况下,不满足when条件的变化就不会被记录,只有最后一行会触发
> (my_script.py:11):
> obj.val = -1 # oops
obj.val:
10
->
-1
当然,一定会有更高端的用户觉得,打印到屏幕是个并不理想的解决方案,放心,watchpoints提供了很简单的自定义callback,可以让你随心所欲地利用watch的强大功能。对输出的format不满意?希望log到文件?希望每次变化的时候改变其他的变量?都可以做到。
watch(a, callback=callback) # customized callback per variable
watch.config(callback=callback) # global customized callback
喜欢pdb的用户也不要难过,watch()完全可以做到类似breakpoint()的效果,在变量修改的时候调出pdb。只需要
watch.config(pdb=True)
watchpoints还有一些其他的小功能,在这里就不一一阐述了,感兴趣的小伙伴可以去github看一下,基本功能全部列出来了。https://github.com/gaogaotiantian/watchpointsgithub.com
这是在VizTracer之后我的另一个开源项目,依然是debugging领域的。和VizTracer不同的是,watchpoints甚至更加轻了,使用方式友善到爆炸,学习曲线几乎看不见。纯Python的实现,全平台支持。
在未来的时间里,我会给watchpoints增加更多的有价值的功能,同时也不会扔下VizTracer的开发和维护,希望给大家带来更给力的debug工具!