我正在测试程序转换工具。它导致其中一项CPython测试失败,但我不能为我的生活找出原因。
这是CPython测试套件https://github.com/python/cpython/blob/master/Lib/test/test_sax.py#L143中test_sax.py中的一个测试的缩小版本。此代码段会写入一个XML文件,然后以错误的编码方式将其读回。这会导致xml.sax.parse
失败并引发异常。当它这样做时,它打开的文件应该被泄露。垃圾收集器检测到此泄漏。以下测试检查此行为:
def test_parse_bytes(self):
make_xml_file(self.data, 'iso-8859-1', None)
with support.check_warnings(('unclosed file', ResourceWarning)) :
with self.assertRaises(SAXException):
self.check_parse(TESTFN)
gc.collect()
我的程序转换工具将此代码段更改为以下内容:
def test_parse_bytes(self):
make_xml_file(self.data, 'iso-8859-1', None)
with support.check_warnings(('unclosed file', ResourceWarning)) :
t = self.assertRaises(SAXException)
with t:
self.check_parse(TESTFN)
gc.collect()
根据我对Python的了解,这些片段应该完全相同。当我在调试器中单步执行时,我无法说出他们在做什么不同的事情。然而,不知何故,后一个片段会导致文件泄露,从而导致测试失败。为什么呢?
答案 0 :(得分:2)
在第一个代码段中,调用self.assertRaises(SAXException)
时gc.collect()
的结果超出了范围,因此它是垃圾并且保证会被收集;在第二个代码段中,引用t
仍然在范围内,因此它不会。有一些从t
到文件句柄的引用路径 - 可能它包含对引发的异常的引用,并且该异常包含对该文件的引用。因此,在gc.collect()
被调用时,该文件不是垃圾,因此它不会自动关闭。外部with
块然后检查未关闭的文件,并找到一个文件,虽然它是垃圾,如果垃圾收集器再次运行t
超出范围,它将被关闭,但仍然是打开的。 / p>