如何确定地抑制 Python 中的 DeprecationWarning?
How to assuredly suppress a DeprecationWarning in Python?
我相信这个问题已经被提出了很多次,但我有一个特定的用例,我无法使用网络上描述的许多方法解决问题。
在我的一个项目中,我正在使用 joblib
库,它显示 DeprecationWarning
因为它在内部某处使用了 imp
库:
from sklearn.externals.joblib import Parallel, delayed
def main():
xs = Parallel()(delayed(lambda x: x**2)(i) for i in range(1, 6))
print(sum(xs))
if __name__ == '__main__':
main()
我正在尝试使用解释器选项过滤掉警告 -W
但它没有帮助:
$ python -W ignore example.py
[...]/lib/python3.7/site-packages/sklearn/externals/joblib/externals/cloudpickle/cloudpickle.py:47:
DeprecationWarning: the imp module is deprecated in favour of importlib;
see the module's documentation for alternative uses import imp
55
此外,我正在尝试使用 warnings
模块进行显式过滤,但它也无济于事:
import warnings
warnings.simplefilter('ignore', category=DeprecationWarning)
from sklearn.externals.joblib import Parallel, delayed
def main():
xs = Parallel()(delayed(lambda x: x**2)(i) for i in range(1, 6))
print(sum(xs))
if __name__ == '__main__':
main()
我在 matplotlib
模块和其他一些第三方库中遇到了类似的问题。可能还有其他一些方法(即环境变量),但我不明白为什么这些解决方案不起作用。
谁能解释一下警告系统在 Python 中的实际工作原理?第三方库有可能故意覆盖客户端的警告设置吗?我会说这个问题对我来说是最晦涩的话题之一。
有趣的是,即使按照@Alex 的建议,我仍然有警告输出,如下所示:
import warnings
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=DeprecationWarning)
from sklearn.externals.joblib import Parallel, delayed
def main():
xs = Parallel()(delayed(lambda x: x**2)(i) for i in range(1, 6))
print(sum(xs))
if __name__ == '__main__':
main()
# $ python -W ignore example.py
# [...]
# DeprecationWarning: the imp module is deprecated in favour of importlib;
# see the module's documentation for alternative uses
# import imp
# 55
所以最终,我决定以一种非常 hacky 的方式来做到这一点并禁用所有警告,因为我有点厌倦了寻找正确的方法来处理它们。 (不仅对于这个库,而且对于许多其他似乎非常渴望用 non-suppressible 警告轰炸您的库)。
import warnings
def noop(*args, **kargs): pass
warnings.warn = noop
from sklearn.externals.joblib import Parallel, delayed
def main():
xs = Parallel()(delayed(lambda x: x**2)(i) for i in range(1, 6))
print(sum(xs))
if __name__ == '__main__':
main()
如果我错误地使用了@Alex 的建议或者你们中的一些人有更好的解决方案,我很乐意接受它作为答案。
更新 1
好的,似乎很难影响包内部某处发出的警告。因此,最简单的方法可能是将 warnings.warn
替换为 noop
,或者以某种方式提前导入内部依赖项并使用上下文管理器抑制它们。
更新 2
前段时间我发现了另一种可能的处理警告的方法。您可以将它们重定向到日志记录中。如果没有显式配置记录器,这些警告基本上会被抑制。它适用于 Jupyter 和我测试过的一些库。
import logging
logging.captureWarnings(True)
根据要求,这里是一个单独的答案post:
诀窍是在导入 sklearn
时使用 "with" 警告(或使用 sklearn 的依赖项,在我的例子中是 hdbscan
包):
with warnings.catch_warnings():
# filter sklearn\externals\joblib\parallel.py:268:
# DeprecationWarning: check_pickle is deprecated
warnings.simplefilter("ignore", category=DeprecationWarning)
import hdbscan
这将仅针对此模块禁用 DeprecationWarning(因为 warnings
-修改附加到 with-block)。
请务必将此语句放在导入模块的代码中的第一个位置,否则将无法运行。例如。如果我在 __init__.py
中加载 hdbscan
,并且上面的代码块出现在一些 sub-class 中,它也加载 hdbscan
,我仍然会收到 DeprecationWarning,因为 Python如果 module/package 已经加载,则忽略任何后续的 import
语句。
因此,重要的是检查哪些 modules/packages 使用 joblib\parallel.py
以及那些来自线性 code-perspective 的最早加载到 python 对象堆的位置。
[编辑]
正如@devforfu 在评论中指出的那样,上述解决方案(不再)有效。自从 Python 3.7 DeprecationWarning is once again shown by default when triggered directly by code in __main__.
以来,我再次对此进行了调查。此外,ignore
当依赖项显式加载其他包的折旧模块时,警告似乎不起作用。
这就是我的 hdbscan
示例中发生的情况,该示例加载了折旧模块 sklearn.external.six
和 sklearn.externals.joblib
。
以下是最终解决这个恼人问题的方法:
- 确保您已明确安装已弃用的独立软件包,例如
conda install -c conda-forge joblib six
- 创建一个将覆盖依赖项导入的假导入,例如:
try:
sys.modules['sklearn.externals.six'] = __import__('six')
sys.modules['sklearn.externals.joblib'] = __import__('joblib')
import hdbscan
except ImportError:
import hdbscan
如果没有导入错误,将使用独立的 6 和 joblib。否则,例如如果用户没有安装 six 或 joblib,该程序仍然可以运行(因为它从 sklearn.externals 加载了两个模块),但它会显示折旧警告。
我相信这个问题已经被提出了很多次,但我有一个特定的用例,我无法使用网络上描述的许多方法解决问题。
在我的一个项目中,我正在使用 joblib
库,它显示 DeprecationWarning
因为它在内部某处使用了 imp
库:
from sklearn.externals.joblib import Parallel, delayed
def main():
xs = Parallel()(delayed(lambda x: x**2)(i) for i in range(1, 6))
print(sum(xs))
if __name__ == '__main__':
main()
我正在尝试使用解释器选项过滤掉警告 -W
但它没有帮助:
$ python -W ignore example.py
[...]/lib/python3.7/site-packages/sklearn/externals/joblib/externals/cloudpickle/cloudpickle.py:47:
DeprecationWarning: the imp module is deprecated in favour of importlib;
see the module's documentation for alternative uses import imp
55
此外,我正在尝试使用 warnings
模块进行显式过滤,但它也无济于事:
import warnings
warnings.simplefilter('ignore', category=DeprecationWarning)
from sklearn.externals.joblib import Parallel, delayed
def main():
xs = Parallel()(delayed(lambda x: x**2)(i) for i in range(1, 6))
print(sum(xs))
if __name__ == '__main__':
main()
我在 matplotlib
模块和其他一些第三方库中遇到了类似的问题。可能还有其他一些方法(即环境变量),但我不明白为什么这些解决方案不起作用。
谁能解释一下警告系统在 Python 中的实际工作原理?第三方库有可能故意覆盖客户端的警告设置吗?我会说这个问题对我来说是最晦涩的话题之一。
有趣的是,即使按照@Alex 的建议,我仍然有警告输出,如下所示:
import warnings
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=DeprecationWarning)
from sklearn.externals.joblib import Parallel, delayed
def main():
xs = Parallel()(delayed(lambda x: x**2)(i) for i in range(1, 6))
print(sum(xs))
if __name__ == '__main__':
main()
# $ python -W ignore example.py
# [...]
# DeprecationWarning: the imp module is deprecated in favour of importlib;
# see the module's documentation for alternative uses
# import imp
# 55
所以最终,我决定以一种非常 hacky 的方式来做到这一点并禁用所有警告,因为我有点厌倦了寻找正确的方法来处理它们。 (不仅对于这个库,而且对于许多其他似乎非常渴望用 non-suppressible 警告轰炸您的库)。
import warnings
def noop(*args, **kargs): pass
warnings.warn = noop
from sklearn.externals.joblib import Parallel, delayed
def main():
xs = Parallel()(delayed(lambda x: x**2)(i) for i in range(1, 6))
print(sum(xs))
if __name__ == '__main__':
main()
如果我错误地使用了@Alex 的建议或者你们中的一些人有更好的解决方案,我很乐意接受它作为答案。
更新 1
好的,似乎很难影响包内部某处发出的警告。因此,最简单的方法可能是将 warnings.warn
替换为 noop
,或者以某种方式提前导入内部依赖项并使用上下文管理器抑制它们。
更新 2
前段时间我发现了另一种可能的处理警告的方法。您可以将它们重定向到日志记录中。如果没有显式配置记录器,这些警告基本上会被抑制。它适用于 Jupyter 和我测试过的一些库。
import logging
logging.captureWarnings(True)
根据要求,这里是一个单独的答案post:
诀窍是在导入 sklearn
时使用 "with" 警告(或使用 sklearn 的依赖项,在我的例子中是 hdbscan
包):
with warnings.catch_warnings():
# filter sklearn\externals\joblib\parallel.py:268:
# DeprecationWarning: check_pickle is deprecated
warnings.simplefilter("ignore", category=DeprecationWarning)
import hdbscan
这将仅针对此模块禁用 DeprecationWarning(因为 warnings
-修改附加到 with-block)。
请务必将此语句放在导入模块的代码中的第一个位置,否则将无法运行。例如。如果我在 __init__.py
中加载 hdbscan
,并且上面的代码块出现在一些 sub-class 中,它也加载 hdbscan
,我仍然会收到 DeprecationWarning,因为 Python如果 module/package 已经加载,则忽略任何后续的 import
语句。
因此,重要的是检查哪些 modules/packages 使用 joblib\parallel.py
以及那些来自线性 code-perspective 的最早加载到 python 对象堆的位置。
[编辑]
正如@devforfu 在评论中指出的那样,上述解决方案(不再)有效。自从 Python 3.7 DeprecationWarning is once again shown by default when triggered directly by code in __main__.
以来,我再次对此进行了调查。此外,ignore
当依赖项显式加载其他包的折旧模块时,警告似乎不起作用。
这就是我的 hdbscan
示例中发生的情况,该示例加载了折旧模块 sklearn.external.six
和 sklearn.externals.joblib
。
以下是最终解决这个恼人问题的方法:
- 确保您已明确安装已弃用的独立软件包,例如
conda install -c conda-forge joblib six
- 创建一个将覆盖依赖项导入的假导入,例如:
try:
sys.modules['sklearn.externals.six'] = __import__('six')
sys.modules['sklearn.externals.joblib'] = __import__('joblib')
import hdbscan
except ImportError:
import hdbscan
如果没有导入错误,将使用独立的 6 和 joblib。否则,例如如果用户没有安装 six 或 joblib,该程序仍然可以运行(因为它从 sklearn.externals 加载了两个模块),但它会显示折旧警告。