在 Python 中使用 joblib 并行执行时抑制警告

问题描述 投票:0回答:2

我正在使用一个生成警告的函数,我真的不需要阅读。问题是我想并行运行该函数,并且在这样做时,似乎我无法再抑制警告。考虑这个例子:

import warnings
import numpy as np
from joblib import Parallel, delayed

def test(a, b):
    if a * b > 10:
        warnings.warn("You are being warned!!")
    return(a*b)

ii = np.arange(5)
jj = ii + 1

with warnings.catch_warnings():
    warnings.filterwarnings("ignore")
    with Parallel(n_jobs=4) as parallel:
        result = parallel(delayed(test)(i, j) for i, j in zip(ii, jj))

这仍然会产生警告消息...另请注意,在我的情况下,我无法重写函数

test
,因为它是从另一个包导入的。有什么办法可以不收到警告信息吗?

python warnings joblib
2个回答
11
投票

是的,这很烦人!

默认的

joblib
后端会产生额外的进程,这些进程似乎没有继承使用
warnings.filterwarnings
应用的警告过滤器。但是,您可以使用
PYTHONWARNINGS
环境变量来设置警告过滤器;这将影响所有新生成的进程,这些进程从主进程中继承环境变量。

来自相关文档页面

警告过滤器由传递到 Python 解释器命令行的

-W
选项和
PYTHONWARNINGS
环境变量初始化。解释器将所有提供的条目的参数保存在
sys.warnoptions
中,而不进行解释;
warnings
模块在首次导入时解析这些内容(在将消息打印到
sys.stderr
后,无效选项将被忽略)。

各个警告过滤器被指定为一系列用冒号分隔的字段:

action:message:category:module:line

一个单独的页面更详细地描述了每个字段的含义,但基本上:

  • action
    描述如何处理警告;对于你来说,你想要
    ignore
    抑制消息
  • message
    可以是一个字符串,必须与警告消息的开头匹配才能过滤它
  • category
    是警告类别,例如
    FutureWarning
    DeprecationWarning
  • module
    line
    指发出警告的位置

这些字段中的任何一个都可以为空,并且您可以省略尾随分号。

所以,忽略所有

FutureWarning

在 Jupyter 笔记本中,您可以执行类似的操作

%env PYTHONWARNINGS=ignore::FutureWarning

或者在脚本中,添加一个条目到

os.environ
:

import os
os.environ['PYTHONWARNINGS']='ignore::FutureWarning'

似乎应该有一种方法只为生成的进程设置环境变量,但我不知道

joblib
是否为此公开了API。


0
投票

您可以尝试以下上下文管理器。

import os

class SuppresedOutput:
    '''Context manager for always supressing
    output to standard output and error.'''

    def __init__(self):
        '''Initializer'''
        self.null_fds = [os.open(os.devnull, os.O_RDWR) for x in range(2)]
        self.save_fds = [os.dup(1), os.dup(2)]

    def __enter__(self):
        '''Context begins.'''
        # Assign the null pointers to stdout and stderr
        os.dup2(self.null_fds[0], 1)
        os.dup2(self.null_fds[1], 2)

    def __exit__(self, *args):
        '''Context Ends.'''
        # Re-assign the real stdout/stderr back to (1) and (2)
        os.dup2(self.save_fds[0], 1)
        os.dup2(self.save_fds[1], 2)
        # Close all file descriptors
        for fd in self.null_fds + self.save_fds:
            os.close(fd)

if __name__ == "__main__":
    print("This will be printed")
    
    with SuppresedOutput():
        print("This will NOT be printed")
    
    print("This will be printed again")
© www.soinside.com 2019 - 2024. All rights reserved.