根据自定义警告类别进行过滤

ger*_*rit 7 python warnings

除了预先存在的警告类别,用户还可以定义自己的警告类,例如在下面的代码中:

$ cat mwe.py 
#!/usr/bin/env python3.5

import warnings
import pprint

class ObnoxiousWarning(UserWarning):
    pass

for i in range(3):
    print(i)
    warnings.warn("I don't like this.", ObnoxiousWarning)
Run Code Online (Sandbox Code Playgroud)

调用Python时,该-W标志控制如何过滤警告.但是当我试图让它忽略我的新鲜警告类别时,我被告知过滤器被忽略了:

$ python3.5 -W ignore::ObnoxiousWarning ./mwe.py
Invalid -W option ignored: unknown warning category: 'ObnoxiousWarning'
0
./mwe.py:11: ObnoxiousWarning: I don't like this.
  warnings.warn("I don't like this.", ObnoxiousWarning)
1
2
Run Code Online (Sandbox Code Playgroud)

如何使用命令行为自定义警告类别插入过滤器(与所有UserWarnings相反或基于警告消息进行过滤,我可以这样做)?

编辑2018-11-29:请参见问题22543:-W选项不能使用非标准类别

r_b*_*ack 7

一些答案就在 python 源代码中。查看_getcategory函数: https://github.com/python/cpython/blob/3.5/Lib/warnings.py#L147

def _getcategory(category):
    import re
    if not category:
        return Warning
    if re.match("^[a-zA-Z0-9_]+$", category):
        try:
            cat = eval(category)
        except NameError:
            raise _OptionError("unknown warning category: %r" % (category,))
    else:
        i = category.rfind(".")
        module = category[:i]
        klass = category[i+1:]
        try:
            m = __import__(module, None, None, [klass])
        except ImportError:
            raise _OptionError("invalid module name: %r" % (module,))
        try:
            cat = getattr(m, klass)
        except AttributeError:
            raise _OptionError("unknown warning category: %r" % (category,))
    if not issubclass(cat, Warning):
        raise _OptionError("invalid warning category: %r" % (category,))
    return cat
Run Code Online (Sandbox Code Playgroud)

Python 尝试评估您的类别或从您在过滤器中指定的模块导入它。除非你的模块位于 PYTHONPATH 中,否则这将会失败。

PYTHONPATH='<path_to_dir_where_mwe_located>' python -W ignore::mwe.ObnoxiousWarning
Run Code Online (Sandbox Code Playgroud)

这样,如果您在 python shell 中导入模块,警告将按照您的意愿进行过滤。要在命令行中使用过滤器,您必须在单独的模块中定义警告,而不是您执行的模块。

mwe.py

class ObnoxiousWarning(UserWarning):
    pass
Run Code Online (Sandbox Code Playgroud)

入口点.py

#!/usr/bin/env python3.5

import warnings
import pprint

from mwe import ObnoxiousWarning

for i in range(3):
    print(i)
    warnings.warn("I don't like this.", ObnoxiousWarning)
Run Code Online (Sandbox Code Playgroud)

最后:

PYTHONPATH='<path_to_dir_where_mwe_located>' python -W ignore::mwe.ObnoxiousWarning ./entrypoint.py
0
1
2
Run Code Online (Sandbox Code Playgroud)

我不明白为什么定义带有警告的单独模块有效,但确实如此。也许有人会解释一下。

  • 这是警告模块中令人惊讶的脆弱代码,使用“eval”...... (2认同)
  • 奇怪的是,当相同的模块由于它们所在的目录是“sys.path”的一部分而通常是可导入的时,我需要显式地将目录添加到我的 PYTHONPATH 中。也许警告规则是在“sys.path”正确初始化之前处理的?看来现阶段网站包不可用。 (2认同)
  • 我刚刚注意到[这早在 2014 年就被报告为 Python 错误](https://bugs.python.org/issue22543)。另请参阅 /sf/ask/299004611/ (2认同)