在错误时自动启动python调试器

这个问题我想了很久,但一直没有找到合适的解决方案。如果我运行一个脚本,遇到一个IndexError, python会打印出错误的行、位置和快速描述,然后退出。是否有可能在遇到错误时自动启动pdb ?我并不反对在文件顶部添加额外的import语句,也不反对添加几行额外的代码。

108006 次浏览

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

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

我刚刚检查了python -?,如果您使用-i命令,您可以在脚本停止的地方进行交互。

给定这个脚本:

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

您可以得到这个输出!

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
>>>

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

你可以使用traceback.print_exc来打印异常回溯。然后使用sys.exc_info提取跟踪结果,最后使用该跟踪结果调用pdb.post_mortem

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启动一个交互式命令行,使用异常产生的帧的局部变量,你可以这样做

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)

使用以下模块:

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

python -m pdb -c continue myscript.py

如果你没有提供-c continue标志,那么你需要在执行开始时输入'c'(表示继续)。然后它会运行到错误点并在那里给你控制权。与eqzx提到一样,此标志是python 3.2中的新添加,因此在较早的python版本中需要输入'c'(参见https://docs.python.org/3/library/pdb.html)。

你可以在代码中加入这一行:

import pdb ; pdb.set_trace()

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

Ipython有一个用于切换此行为的命令:它所做的与您所描述的完全相同,甚至可能更多(通过语法高亮显示和代码补全为您提供更多信息回溯)。这绝对值得一试!

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

设置断点意味着你想要它意味着什么:你可以使用IDE,或pdb.set_trace,或任何东西

要让它运行而不需要在开始时输入c,请使用:

python -m pdb -c c <script name>

Pdb有自己的命令行参数:-c c将在执行开始时执行c(continue)命令,程序将不间断地运行,直到出现错误。

IPython在命令行上简化了这一点:

python myscript.py arg1 arg2

可以改写为

ipython --pdb myscript.py -- arg1 arg2

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

python -m mymodule arg1 arg2

可以改写为

ipython --pdb -m mymodule -- arg1 arg2

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

这还具有调用增强的IPython调试器(ipdb)而不是pdb的优点。

如果你正在运行一个模块:

python -m mymodule

现在你想在异常发生时输入pdb,这样做:

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

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

如果您正在使用IPython环境,您可以只使用%debug, shell将带您回到ipdb环境中的违规行进行检查等。上面提到的另一个选项是使用iPython magic %pdb,它可以有效地完成同样的工作。

Python -m PDB script.py在python2.7中按continue start,它将运行到错误处并在那里中断以进行调试。

如果你正在使用ipython,启动后类型%pdb

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

ipdb有一个很好的上下文管理器来实现这种行为,这使得意图在语义上更清晰:

from ipdb import launch_ipdb_on_exception


with launch_ipdb_on_exception():
...

从3.7开始,你可以在你的代码中直接使用关键字断点(不需要任何导入),就像这样:

try:
...  # The line that raises an error
except:
breakpoint()