如何使Python触发ResourceWarning时以非0退出码终止程序?
我理解你的困扰——明明加了-We参数想让警告直接转成错误,结果那些ResourceWarning只是打印出来,程序居然还返回0的成功退出码,这确实挺闹心的。
先给你捋清楚原因:你看到的这些警告都是在对象的__del__方法里触发的,而Python在解释器进入收尾退出阶段时,默认不会把这个阶段触发的警告转换成错误,哪怕你设置了-We。这是Python的设计逻辑,它不想在程序收尾时因为这类警告打断退出流程。
接下来给你两种解决思路,一种是从根源避免警告(更推荐的最佳实践),另一种是强制让警告触发非0退出:
方法一:规范管理资源,从根源消除警告
aiohttp的ClientSession本身就设计了配合async with上下文管理器的用法,这样会自动帮你关闭会话,根本不会产生资源未关闭的警告:
import asyncio import aiohttp async def main(): async with aiohttp.ClientSession() as client: await client.get("https://google.com") asyncio.run(main())
要是你不习惯用上下文管理器,也可以手动调用close()并等待关闭完成:
import asyncio import aiohttp async def main(): client = aiohttp.ClientSession() try: await client.get("https://google.com") finally: await client.close() asyncio.run(main())
这样运行后就不会再出现那些ResourceWarning,程序也能正常按预期退出。
方法二:强制让ResourceWarning触发非0退出
如果你就是需要在出现这类警告时让程序直接失败,那可以自定义警告处理逻辑,先记录所有ResourceWarning,再在程序结束前检查标记,有警告就以非0码退出。
在你的脚本开头加上这段代码:
import warnings import sys import atexit # 标记是否出现过ResourceWarning has_resource_warning = False def custom_warning_handler(message, category, filename, lineno, file=None, line=None): global has_resource_warning if category == ResourceWarning: has_resource_warning = True # 保留默认的警告打印行为 warnings._showwarnmsg_impl(message, category, filename, lineno, file, line) # 替换默认的警告处理器 warnings.showwarning = custom_warning_handler # 程序退出前检查警告标记 def exit_with_warning_check(): if has_resource_warning: sys.exit(1) atexit.register(exit_with_warning_check)
之后再用你原来的命令行参数运行脚本:python -bb -We -X dev test.py,一旦出现ResourceWarning,程序最后就会以退出码1终止。
不过要注意,这种方法依赖atexit钩子在解释器退出前执行,如果__del__方法的调用时机晚于atexit钩子,可能会漏掉警告,但绝大多数场景下这个方法是有效的。
备注:内容来源于stack exchange,提问作者Sam Bull

