我想调试在代码执行过程中出现的警告。
一个简单的断点是行不通的,因为导致警告的行在第一次警告发生之前执行了数百万次而没有警告。
此外,发生这种情况的行在库代码中(更准确地说,在 中pandas/core/common.py
),所以我的偏好是根本不修改代码。
我只想在程序发出警告时立即停止程序的执行,并在此时检查堆栈,无论是 withpdb
还是 with ipdb
。
有没有办法将任一调试器配置为在发出警告时自动进入单步模式?
你可以写一个脚本dbg.py
:
import pdb, warnings, sys
import __builtin__
if __name__ == '__main__':
args, n = [], len(sys.argv)
if n < 2:
sys.exit(1)
elif n > 2:
args.append(__builtin__.__dict__[sys.argv[2]])
if n > 3:
args.append(int(sys.argv[3]))
warnings.simplefilter('error', *args) # treat warnings as exceptions
try:
execfile(sys.argv[1])
except:
pdb.post_mortem(sys.exc_info()[-1])
然后,您可以使用它来调试您的脚本。如果您想在任何警告上运行 pdb,请将您的脚本名称作为第一个参数传递:
$ python dbg.py yourscript.py
如果您只希望 pdb 在引发某些特定类型的警告时运行,则将警告类型作为第二个参数传递:
$ python dbg.py yourscript.py DeprecationWarning
行号作为第三个参数:
$ python dbg.py yourscript.py DeprecationWarning 342
您还可以使用warnings.filterwarnings
而不是重写代码,warnings.simplefilter
以使警告过滤更加灵活。
我发现@user2683246 提供的答案优雅而有用。这是为与 Python3 兼容而修改的解决方案的变体(使用 Python 3.7 测试):
#!/usr/bin/env python
import pdb, warnings, sys
import builtins
if __name__ == '__main__':
args, n = [], len(sys.argv)
if n < 2:
sys.exit(1)
elif n > 2:
args.append(builtins.__dict__[sys.argv[2]])
if n > 3:
args.append(int(sys.argv[3]))
warnings.simplefilter('error', *args) # treat warnings as exceptions
try:
with open(sys.argv[1]) as f:
code = compile(f.read(), sys.argv[1], 'exec')
exec(code)
except:
pdb.post_mortem(sys.exc_info()[-1])
显着变化:
execfile()
调用替换为 Python 3 变体;和__builtin__
为builtins
。当您无法控制启动程序时,我发现rpdb
调试此类问题非常方便。你需要的是临时修改pandas/core/common.py
添加
import rpdb
debugger = rpdb.Rpdb(port=12345)
debugger.set_trace()
触发警告时,调试器将在那里等待连接。然后连接到调试器并检查堆栈。
如果可以识别引发警告的代码范围,则可以使用warnings.catch_warnings
链接访问警告列表并将执行切换到 PDB 会话,而不是将警告视为错误。
但是,我建议使用 PDB 启动您的程序,在有问题的代码片段之后检测到警告列表的警告编号更改时设置一个断点来中断执行。如果您的调试代码片段处于循环中,您将受益。
例子:
import warnings
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
warningNum = len(w)
for i in range(someNumber):
"your code probably throw warning"
if len(w) != warningNum:
warningNum = len(w) #set break point here
运行脚本python -m pdb yourscript.py
并在 line 中设置断点warningNum = len(w)
,然后在检测到警告号更改时可以暂停执行。