将Python 3资源警告转换为异常
有没有办法让Python 3的单元测试在出现任何资源警告时强制失败,而不是仅仅在错误输出中打印一个警告呢?
我试过以下方法:
import warnings
warnings.simplefilter(action='error', category=ResourceWarning)
这导致单元测试输出了以下内容:
my_test (__main__.MyTest) ... Exception ignored in: <socket.socket fd=9, family=AddressFamily.AF_INET, type=SocketType.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 54065), raddr=('127.0.0.1', 27017)>
ResourceWarning: unclosed <socket.socket fd=9, family=AddressFamily.AF_INET, type=SocketType.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 54065), raddr=('127.0.0.1', 27017)>
ok
----------------------------------------------------------------------
Ran 1 test in 0.110s
注意到“异常被忽略”的信息了吗?我希望测试能直接失败,而不是让我去看输出寻找资源警告。
2 个回答
6
很遗憾,这似乎是不可能的。那个“在这里忽略的异常”消息是由CPython中的一个函数PyErr_WriteUnraisable
生成的,具体在Python/errors.c
文件里。这个函数前面的注释提到:
/* Call when an exception has occurred but there is no way for Python
to handle it. Examples: exception in __del__ or during GC. */
实际上,ResourceWarning
是在垃圾回收时产生的,Python会打印出这个消息,因为在那个时候它无法抛出异常。这与CPython的核心实现有关,unittest无法对此进行覆盖。
更新:虽然上面的分析在技术上是正确的,但还有另一种方法可以真正解决提问者的问题。更多细节请查看J.F. Sebastian的回答。
10
这里有一个单元测试,如果在with catch_warning()
语句中的代码产生了ResourceWarning
警告,这个测试就会失败:
#!/usr/bin/env python3
import gc
import socket
import unittest
import warnings
class Test(unittest.TestCase):
def test_resource_warning(self):
s = socket.socket()
####s.close() #XXX uncomment to pass the test
# generate resource warning when s is deleted
with warnings.catch_warnings(record=True) as w:
warnings.resetwarnings() # clear all filters
warnings.simplefilter('ignore') # ignore all
warnings.simplefilter('always', ResourceWarning) # add filter
del s # remove reference
gc.collect() # run garbage collection (for pypy3)
self.assertFalse(w and str(w[-1])) # test fails if there
# are warnings
if __name__=="__main__":
unittest.main()