python-debugging-skills

整理一些python调试相关的方法, 列举出一些各个场景下使用的例子。

pudb

PuDB is a full-screen, console-based visual debugger for Python. 相对于pdb更加容易使用. 下面整理一下pudb的使用,在需要用的时候当做一个参考.

安装

pip install pudb

代码

下面例子使用的代码

文件名 描述 路径
main.py 测试调试 main
multiprocess_main.py 测试pudb多进程调试 multiprocess_main
     

基本使用

不设置断点方式

python -m pudb main.py

如图,光标指向了第一行. 因为没有设置断点,直接从第一行开始调试运行.

从界面上可以看到几个信息:

例子中代码如下:

def generate_list(number, number_list=[]):
    number_list.append(number)
    return number_list

def main():
    num = 1
    list1 = generate_list(num)
    print('list1:', list1)
    assert list1[0] == 1, 'bad list 1'
    num = 2
    list2 = generate_list(num)
    print('list2:', list2)
    assert list2[0] == 2, 'bad list 2'

if __name__ == "__main__":
    main()

函数generate_list会根据传入的参数返回一个包含着参数的列表. 下面运行一下看看效果:

(pudb_demo-cp5XedMM) root@env-monitor-5-2-5:/opt/pudb_demo# python  main.py 
list1: [1]
list2: [1, 2]
Traceback (most recent call last):
  File "main.py", line 21, in <module>
    main()
  File "main.py", line 17, in main
    assert list2[0] == 2, 'bad list 2'
AssertionError: bad list 2

竟然出错了。。。因为缺少上下文,所以直接看不出错误. 这下改我们的调试帮手pudb上手了.

运行python -m pudb main.py ,会出现上面图中那样. 我们输入ns等命令一步步看在这里停下来:

list2 = generate_list(num)

输入s进入函数,如图:

奇怪,number_list.append(number)这一行还未执行, number_list竟然已经有一个值了. 明明参数上通过number_list=[]设置空了. 到目前为止,根据调试找到了错误的位置. 现在一共有两个问题要解决:

  1. 右边Variables只显示了number_list参数个数,要是能显示内容就好了.
  2. number_list=[]为啥不生效.

下面来解答这两个问题:

  1. 通过键盘->键可以将光标移到Variables,再上下选择number_list变量. 如图:

    输入回车,可以看到对变量展示的一个配置Expanded打钩,然后保存.如图:

    现在已经看到number_list具体值了.

  2. 在python里,如果将字典或列表作为函数的参数, 函数第一次调用的时候,python会创建一个持久对象(persistent object ),后面再次调用的时候使用的是同一个对象. 打印对象id可以看出list1list2是一样的.

设置断点方式

可以直接在错误的地方加断点.

from pudb import set_trace; set_trace()
or
import pudb; pu.db

这里在list2 = generate_list(num)上一行加断点. 有断点就不需要python -m pudb方式了, 直接python main.py就可以了,如图:

然后就可以像上面那样调试了.

分离调试窗口

当我们在一个窗口里调试时, 是看不到程序的输出的. pudb可以通过如下方式实现在新的窗口显示调试时程序的输出. 假设现在两个窗口:窗口A和窗口B. 我们要进行调试的窗口是窗口 B.

远程调试

首先需要在代码里加上

from pudb.remote import set_trace
set_trace(term_size=(80, 24))

比如现在有窗口A和窗口 B.

如果需要在其他机器访问,需要这样启动:

PUDB_RDB_HOST=0.0.0.0 python  main.py

多进程调试

有时我们的程序会启动进程去做一些事情. 在多进程模式下,如果普通方式设置断点,例如:

from pudb import set_trace; set_trace()

启动时会出现如下错误:

    return self._getch(0)
  File "/root/.local/share/virtualenvs/pudb_demo-cp5XedMM/lib/python3.6/site-packages/urwid/raw_display.py", line 554, in _getch
    return ord(os.read(self._term_input_file.fileno(), 1))
TypeError: ord() expected a character, but string of length 0 found

正确的方式是利用远程调试. 执行如下命令:

PUDB_RDB_HOST=0.0.0.0 python multiprocess_main.py

在远程机器运行:

telnet x.x.x.x 6899

如图:

就好像在一个进程里调试一样,剩余的进程该做什么做什么,互不影响.

参考