Как t = foo(); с t отличаться от с foo()?

Я тестирую инструмент преобразования программы. Это приводит к сбою одного из тестов CPython, но я не могу понять, почему.

Вот уменьшенная версия одного из тестов в test_sax.py из набора тестов CPython, https://github.com/python/cpython/blob/master/Lib/test/test_sax.py#L143 . Этот фрагмент записывает 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, эти фрагменты тоже должны быть абсолютно идентичными. Когда я прохожу через отладчик, я не могу сказать, что они делают по-другому. Тем не менее, последний фрагмент каким-то образом предотвращает утечку файла и, следовательно, приводит к сбою теста. Почему?


person James Koppel    schedule 13.04.2018    source источник


Ответы (1)


В первом фрагменте результат self.assertRaises(SAXException) выходит за рамки при вызове gc.collect(), поэтому он является мусором и гарантированно будет собран; во втором фрагменте ссылка t все еще находится в области действия, поэтому ее нет. Есть некоторый путь ссылок от t к дескриптору файла — возможно, он содержит ссылку на возникшее исключение, а исключение содержит ссылку на файл. В результате файл не является мусором при вызове gc.collect(), поэтому он не закрывается автоматически. Затем внешний блок with проверяет наличие незакрытых файлов и находит файл, который, хотя и является мусором и был бы закрыт, если бы сборщик мусора снова запустился с t вне области действия, тем не менее все еще открыт.

person jimrandomh    schedule 13.04.2018
comment
Добавлен оператор del t, и тест пройден. Подтвержденный. Спасибо Джим! - person James Koppel; 13.04.2018