0%

pudb使用指南

最近在调试python程序,对于这种动态语言,我之前的方法大多都是打tag,真是痛苦的要死。话说回来,debug是为了什么?debug可以看成是对我们的猜测的一种验证。如果我们能通过工具将我们需要的信息(变量、堆栈)都显示出来,就可以debug的非常有效率。下面就介绍一个python调试工具:pudb。

本文部分内容参考自Professor Norm Matloff的文章,我特此发了邮件给他征得了翻译的权限。

Professor Norm Matloff, that is very nice of you, and thanks.

pudb是全屏的基于控制台的可视化调试器。如果你还记得Turbo C的话,看到这个你应该会感到亲切的。Homepage

先概要看一下pudb的特性:

  • 源码语法高亮,栈、断点、变量可见并且一直动态更新。变量展示还有很多可以定制化的功能。
  • 基于键盘,简单高效。为什么说高效呢?因为它支持VI的鼠标移动。还支持PDB的某些命令
  • 支持查找源代码,可以使用m代用module browser查看载入的模块
  • 断点设置:鼠标移到某行代码,按b,然后可以在断点窗口编辑断点
  • PuDB看重异常处理,post-mortem模式使折回到crash的最后一步更简单

安装

1
2
pip install pudb
easy_install pudb

使用

为了支持pudb,需要在代码中插入

1
2
3
from pudb import set_trace; set_trace()
or
import pudb; pu.db

然后通过下面命令启动pudb

1
pudb my-script.py

step by step

我们以下面这段二分程序binsearch.py为例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pudb; pu.db

def findinspt(x, xnew):
n = len(x)
lo = 0
hi = n-1
while True:
mid = (lo + hi) / 2
if xnew > x[mid]:
lo = mid + 1
else:
hi = mid
if xnew == x[mid]:
return mid

if __name__ == "__main__":
y = [5,12,13]
print findinspt(y,3)
print findinspt(y,8)
print findinspt(y,12)
print findinspt(y,30)

运行下面命令

1
pudb binsearch.py

不出意外会得到下面的窗口,左半边是源代码,右边一次是变量窗口、程序调用栈、断点

先介绍几个简单命令,不需要记住,因为下面会多次提到

  • n: next,也就是执行一步
  • s: step into,进入函数内部
  • c: continue
  • b: break point,断点
  • !: python command line
  • ?: help

最重要的是记住?,需要的时候按”?”查询
我们按一下n会发现y=[5,12,13]这行代码高亮显示了,表示执行到了这行代码。

再按一下n,y=[5,12,13]这行代码就执行完了,仔细看会发现右上角的变量窗口出现了y:list,表示程序内存中有了变量y。后面的List是type。

要查看y的值,按s即可。但是如果这个时候你要是不小心按到了Enter,你会发现下面的界面。

对的,这就是有关变量的所有信息。你只需要将光标移动到你要查看的信息,比如 Show repr(),按’Enter’键,再移动向右方向键到ok上,再按Enter键,就可以查看相应的信息的。但是如果你查看了变量不存在的信息,就会出现下面的画面。

当光标停在函数上的时候可以选择n执行到下一行代码,或者s进入函数内部。正常如果有终端输出的时候,pudb会回到终端。但是我在mac上并没有回去,有待进一步验证。这个时候我们执行n的时候,如果函数有问题,会出现异常:[PROCESSING EXCEPTION - hit e to examine]。按e就会显示异常的具体信息。

下面就是进入函数进行调试了。先按q选择restart重启pudb,执行到上面异常的哪一行选择s进入函数内部。这个时候findinspet()函数内部的变量就会显示在右上角变量窗口中。按n单步调试一会,发现lo,hi都变成0了。说明出现问题了。

修改程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pudb; pu.db

def findinspt(x, xnew):
n = len(x)
lo = 0
hi = n-1
while True:
mid = (lo + hi) / 2
if xnew > x[mid]:
lo = mid + 1
else:
hi = mid
if xnew == x[mid]:
return mid
if lo == hi:
return lo

if __name__ == "__main__":
y = [5,12,13]
print findinspt(y,3)
print findinspt(y,8)
print findinspt(y,12)
print findinspt(y,30)

重新启动pudb。光标到main,按c会得到如下界面。

这时候选择restart,然后按o就可以看到输出结果了。

结果对于3,8,12都是对的,30的插入位置不对。为了让debug效率更高,下面使用断点功能。使用向下方向键,光标移动到第13行,按b即在该行设置了断点,右下角的断点显示窗口可以看到。

但是我们只想debug当xnew等于30的时候的情况。先使用向右方向键,再使用向下方向键到断点显示窗口的断点上,按Enter。下图。在condition的右侧设置条件xnew==30,点击OK保存。

使用方向键切换到左侧源码部分,先按n单步执行到main主程序,然后按c,程序就执行到了断点位置处。从右侧可以看到xnew的值为30。

这个时候就可以根据我们需要来单步调试了。对于本文的程序,会发现需要对边界条件处理一下。程序改为下面即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import pudb; pu.db

def findinspt(x, xnew):
n = len(x)
lo = 0
hi = n-1
while True:
mid = (lo + hi) / 2
if xnew > x[mid]:
lo = mid + 1
else:
hi = mid
if xnew == x[mid]:
return mid
if lo == hi:
if xnew <= x[lo]:
return lo
else:
return lo

if __name__ == "__main__":
y = [5,12,13]
print findinspt(y,3)
print findinspt(y,8)
print findinspt(y,12)
print findinspt(y,30)

到此,基本上涉及了Debug的一些主要情况。如果说最后的代码是一个产品,Debug就是产品出问题时候的用来解决问题的工具。使用工具的效率一定程度上也决定了产品的生产效率。

最后,再次感谢一下 Professor Norm Matloff。