我使用 pytest 和模拟遇到了一个奇怪的问题:我试图通过使用
__del__
删除对象来创建对 del ...
的调用。根据文档,del
只会减少正在“删除”的对象的引用计数器,并且只有在没有其他人仍然持有对该对象的引用时才真正删除该对象。看起来如果 Mock 抛出异常,就会以某种方式导致某人获取并保留对该对象的额外引用。
我整理了一个快速演示测试来展示问题:
test_del_passes
(完成得很好)和test_del_fails
(最后一个断言失败,即del del_test
不会导致调用del_test.__del__()
)之间的唯一区别
)是,在第一个中,test_fn
返回一个值,而在第二个中,test_fn
抛出一个TimeoutError
。我尝试删除 test_fn
对象,或者将 TimeoutError
分配给变量并删除它,但我根本找不到让第二个测试通过的方法。因此,在测试的某个地方,有人保留了对 del_test
的额外引用,但我不知道谁、为什么或如何摆脱它。
import unittest.mock as mock
class DelTest:
def __init__(self, flags, test_fn):
self.flags = flags
self.test_fn = test_fn
def __del__(self):
self.flags[0] = 1
def run(self):
try:
self.test_fn()
except TimeoutError:
pass
def test_del_passes():
flags = [0]
test_fn = mock.Mock(side_effect=[True])
del_test = DelTest(flags, test_fn)
del_test.run()
assert flags[0] == 0
del del_test
assert flags[0] == 1
def test_del_fails():
flags = [0]
test_fn = mock.Mock(side_effect=[TimeoutError()])
del_test = DelTest(flags, test_fn)
del_test.run()
assert flags[0] == 0
del del_test
assert flags[0] == 1
框架保留对对象的引用,您可以看到它调用了
gc.get_referrers(del_test)
:
[<frame at 0x101b15080, file 'xxx.py', line 17, code run>]