关于调试:出错时自动启动python调试器

Starting python debugger automatically on error

这是一个我思考了很长时间的问题,但我从未找到合适的解决方案。如果我运行一个脚本,遇到一个索引错误,那么Python将打印该错误的行、位置和快速描述,然后退出。遇到错误时是否可以自动启动PDB?我不反对在文件的顶部有一个额外的导入语句,也不反对有一些额外的代码行。


1
python -m pdb -c continue myscript.py

如果不提供-c continue标志,则在执行开始时需要输入'c'(用于继续)。然后它将运行到错误点,并在那里给您控制权。正如eqzx所提到的,这个标志是python 3.2中的一个新添加项,因此对于早期的python版本,需要输入"c"(请参见https://docs.python.org/3/library/pdb.html)。


您可以使用traceback.print_exc来打印异常的traceback。然后使用sys.exc_info提取回溯,最后使用该回溯调用pdb.post_mortem

1
2
3
4
5
6
7
8
9
10
11
12
13
import pdb, traceback, sys

def bombs():
    a = []
    print a[0]

if __name__ == '__main__':
    try:
        bombs()
    except:
        extype, value, tb = sys.exc_info()
        traceback.print_exc()
        pdb.post_mortem(tb)

如果您想用code.interact启动一个交互式命令行,可以使用产生异常的帧的局部变量进行交互。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import traceback, sys, code

def bombs():
    a = []
    print a[0]

if __name__ == '__main__':
    try:
        bombs()
    except:
        type, value, tb = sys.exc_info()
        traceback.print_exc()
        last_frame = lambda tb=tb: last_frame(tb.tb_next) if tb.tb_next else tb
        frame = last_frame().tb_frame
        ns = dict(frame.f_globals)
        ns.update(frame.f_locals)
        code.interact(local=ns)


使用以下模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import sys

def info(type, value, tb):
    if hasattr(sys, 'ps1') or not sys.stderr.isatty():
    # we are in interactive mode or we don't have a tty-like
    # device, so we call the default hook
        sys.__excepthook__(type, value, tb)
    else:
        import traceback, pdb
        # we are NOT in interactive mode, print the exception...
        traceback.print_exception(type, value, tb)
        print
        # ...then start the debugger in post-mortem mode.
        # pdb.pm() # deprecated
        pdb.post_mortem(tb) # more"modern"

sys.excepthook = info

把它命名为debug(或者你喜欢的任何名称),并把它放在你的python路径的某个地方。

现在,在脚本的开头,只需添加一个import debug


IPython有一个用于切换此行为的命令:%pdb。它完全符合您所描述的,甚至更多(通过语法突出显示和代码完成为您提供更丰富的回溯信息)。这绝对值得一试!


这不是调试器,但可能同样有用(?)

我知道我在某个地方的演讲中听到吉多提到这个。

我刚检查过python-?如果使用-i命令,则可以在脚本停止的位置进行交互。

因此,根据这个脚本:

1
2
3
4
5
6
7
8
testlist = [1,2,3,4,5, 0]

prev_i = None
for i in testlist:
    if not prev_i:
        prev_i = i
    else:
        result = prev_i/i

你可以得到这个输出!

1
2
3
4
5
6
7
8
9
10
11
12
PS D:\> python -i debugtest.py
Traceback (most recent call last):
  File"debugtest.py", line 10, in <module>
    result = prev_i/i
ZeroDivisionError: integer division or modulo by zero
>>>
>>>
>>> prev_i
1
>>> i
0
>>>

老实说,我没有用过这个,但我应该用,看起来很有用。


ipython在命令行中简化了这一点:

1
python myscript.py arg1 arg2

可重写为

1
ipython --pdb myscript.py -- arg1 arg2

或者,类似地,如果调用模块:

1
python -m mymodule arg1 arg2

可重写为

1
ipython --pdb -m mymodule -- arg1 arg2

注意--以阻止ipython将脚本的参数作为自己的参数来读取。

这还具有调用增强的IPython调试器(IPDB)而不是PDB的优势。


如果您使用的是IPython环境,那么您只需使用%debug,shell将带您返回到与IPDB环境冲突的行进行检查等。上面提到的另一个选项是使用IPython magic%pdb,它有效地做到了这一点。


如果您正在使用ipython,则在启动类型%pdb之后

1
2
In [1]: %pdb
Automatic pdb calling has been turned ON


您可以将此行放入代码中:

1
import pdb ; pdb.set_trace()

更多信息:在任意行启动python调试器


要使其运行而不必在开始时键入c,请执行以下操作:

7

pdb有自己的命令行参数:-c c将在执行开始时执行c(c ontinue)命令,并且程序将不间断运行,直到出现错误为止。


python2.7中的python-m pdb script.py按"继续"以启动,它将运行到错误处并在那里中断以进行调试。


如果正在运行模块:

1
python -m mymodule

现在,您要在发生异常时输入pdb,请执行以下操作:

1
PYTHONPATH="." python -m pdb -c c mymodule/__main__.py

(或扩展您的PYTHONPATH)。需要PYTHONPATH以便在路径中找到模块,因为您现在正在运行pdb模块。


在层次结构中最顶层异常类的构造函数中放置一个断点,大多数情况下,您将看到错误出现在哪里。

放置一个断点意味着你想要它意味着什么:你可以使用一个IDE,或者pdb.set_trace,或者其他什么。