【问题标题】:How can "t = foo(); with t" be different from "with foo()"?"t = foo(); with t" 与 "with foo()" 有何不同?
【发布时间】:2018-04-14 03:42:57
【问题描述】:

我正在测试一个程序转换工具。它导致其中一个 CPython 测试失败,但我终生无法弄清楚原因。

这是来自 CPython 测试套件的 test_sax.py 中的一项测试的缩小版本,https://github.com/python/cpython/blob/master/Lib/test/test_sax.py#L143。这个 sn-p 写入一个 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()

我的程序转换工具将这个 sn-p 更改为如下内容:

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 的了解,这些 sn-ps 也应该是完全相同的。当我进入调试器时,我不知道他们在做什么不同。然而,不知何故,后者 sn-p 导致文件不被泄露,从而导致测试失败。为什么?

【问题讨论】:

    标签: python-3.x cpython


    【解决方案1】:

    在第一个sn-p中,self.assertRaises(SAXException)的结果在gc.collect()被调用时超出范围,所以是垃圾,保证被回收;在第二个 sn-p 中,引用 t 仍在范围内,所以它不在。有一些从t 到文件句柄的引用路径——也许它包含对引发的异常的引用,并且异常包含对文件的引用。因此,当gc.collect() 被调用时,该文件不是垃圾文件,因此它不会自动关闭。外部的with 块然后检查未关闭的文件,并找到一个文件,虽然它是垃圾并且如果垃圾收集器再次运行t 超出范围时将被关闭,但仍然是打开的。

    【讨论】:

    • 添加了“del t”语句,测试通过。确认的。谢谢吉姆!
    猜你喜欢
    • 1970-01-01
    • 2011-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-19
    • 2020-10-03
    • 1970-01-01
    • 2021-02-18
    相关资源
    最近更新 更多