如何在警告时自动切换到调试模式?

时间:2015-12-21 18:39:11

标签: python ipython pdb ipdb

我想调试代码执行中出现的警告。

一个简单的断点不会发生,因为在第一次警告发生之前,导致警告的行会在没有警告的情况下执行数百万次。

此外,发生这种情况的行是在库代码中(更确切地说,在pandas/core/common.py中),所以我的首选是根本不修改代码。

我只想在发出警告时立即停止程序的执行,并在此时检查堆栈,使用pdbipdb

  

有没有办法将调试器配置为在发出警告时自动进入单步模式?

4 个答案:

答案 0 :(得分:7)

您可以编写脚本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重写代码,以使警告过滤更加灵活。

答案 1 :(得分:1)

warnings.catch_warnings link除了可以标识引发警告的代码范围之外,还可以用来访问警告列表并将执行切换到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)中设置一个断点,然后在检测到警告编号更改时可以暂停执行。

答案 2 :(得分:0)

  

https://pypi.python.org/pypi/rpdb/

当你无法控制启动程序时,我发现rpdb非常方便调试这个问题。你需要的是暂时修改pandas/core/common.py添加

import rpdb
debugger = rpdb.Rpdb(port=12345)
debugger.set_trace()

当触发警告时,调试器将在那里等待连接。然后连接到调试器并检查堆栈。

答案 3 :(得分:0)

我发现@ 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])

重要更改:

  • 用Python 3变体替换execfile()调用;和
  • __builtin__替换为builtins