我想调试一个在代码执行过程中出现的警告。
一个简单的断点是不行的,因为导致警告的行在第一个警告发生之前执行了数百万次而没有警告。
此外,发生这种情况的行在库代码中(更准确地说,在pandas/core/common.py
中),所以我倾向于根本不修改代码。
我只想在程序发出警告时立即停止执行,并在此时使用pdb
或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
如果可以识别引发警告的代码范围,则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
运行脚本,并在warningNum = len(w)
行设置断点,然后在检测到警告号更改时可以暂停执行。
https://pypi.python.org/pypi/rpdb/
我发现rpdb
在无法控制程序启动的情况下非常方便地调试这样的问题。您需要临时修改pandas/core/common.py
以添加
import rpdb
debugger = rpdb.Rpdb(port=12345)
debugger.set_trace()
当触发警告时,调试器将在那里等待连接。然后连接到调试器并检查堆栈。