File Lib/test/test_sax.py changed (mode: 100644) (index 2411895d9d..2eb62905ff) |
4 |
4 |
from xml.sax import make_parser, ContentHandler, \ |
from xml.sax import make_parser, ContentHandler, \ |
5 |
5 |
SAXException, SAXReaderNotAvailable, SAXParseException |
SAXException, SAXReaderNotAvailable, SAXParseException |
6 |
6 |
import unittest |
import unittest |
|
7 |
|
from unittest import mock |
7 |
8 |
try: |
try: |
8 |
9 |
make_parser() |
make_parser() |
9 |
10 |
except SAXReaderNotAvailable: |
except SAXReaderNotAvailable: |
|
... |
... |
class ParseTest(unittest.TestCase): |
175 |
176 |
with self.assertRaises(SAXException): |
with self.assertRaises(SAXException): |
176 |
177 |
self.check_parse(BytesIO(xml_bytes(self.data, 'iso-8859-1', None))) |
self.check_parse(BytesIO(xml_bytes(self.data, 'iso-8859-1', None))) |
177 |
178 |
make_xml_file(self.data, 'iso-8859-1', None) |
make_xml_file(self.data, 'iso-8859-1', None) |
178 |
|
with support.check_warnings(('unclosed file', ResourceWarning)): |
|
179 |
|
# XXX Failed parser leaks an opened file. |
|
180 |
|
with self.assertRaises(SAXException): |
|
181 |
|
self.check_parse(TESTFN) |
|
182 |
|
# Collect leaked file. |
|
183 |
|
gc.collect() |
|
|
179 |
|
with self.assertRaises(SAXException): |
|
180 |
|
self.check_parse(TESTFN) |
184 |
181 |
with open(TESTFN, 'rb') as f: |
with open(TESTFN, 'rb') as f: |
185 |
182 |
with self.assertRaises(SAXException): |
with self.assertRaises(SAXException): |
186 |
183 |
self.check_parse(f) |
self.check_parse(f) |
|
... |
... |
class ParseTest(unittest.TestCase): |
194 |
191 |
input.setEncoding('iso-8859-1') |
input.setEncoding('iso-8859-1') |
195 |
192 |
self.check_parse(input) |
self.check_parse(input) |
196 |
193 |
|
|
|
194 |
|
def test_parse_close_source(self): |
|
195 |
|
builtin_open = open |
|
196 |
|
fileobj = None |
|
197 |
|
|
|
198 |
|
def mock_open(*args): |
|
199 |
|
nonlocal fileobj |
|
200 |
|
fileobj = builtin_open(*args) |
|
201 |
|
return fileobj |
|
202 |
|
|
|
203 |
|
with mock.patch('xml.sax.saxutils.open', side_effect=mock_open): |
|
204 |
|
make_xml_file(self.data, 'iso-8859-1', None) |
|
205 |
|
with self.assertRaises(SAXException): |
|
206 |
|
self.check_parse(TESTFN) |
|
207 |
|
self.assertTrue(fileobj.closed) |
|
208 |
|
|
197 |
209 |
def check_parseString(self, s): |
def check_parseString(self, s): |
198 |
210 |
from xml.sax import parseString |
from xml.sax import parseString |
199 |
211 |
result = StringIO() |
result = StringIO() |
File Lib/xml/sax/expatreader.py changed (mode: 100644) (index 98b5ca9539..421358fa5b) |
... |
... |
class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator): |
105 |
105 |
source = saxutils.prepare_input_source(source) |
source = saxutils.prepare_input_source(source) |
106 |
106 |
|
|
107 |
107 |
self._source = source |
self._source = source |
108 |
|
self.reset() |
|
109 |
|
self._cont_handler.setDocumentLocator(ExpatLocator(self)) |
|
110 |
|
xmlreader.IncrementalParser.parse(self, source) |
|
|
108 |
|
try: |
|
109 |
|
self.reset() |
|
110 |
|
self._cont_handler.setDocumentLocator(ExpatLocator(self)) |
|
111 |
|
xmlreader.IncrementalParser.parse(self, source) |
|
112 |
|
except: |
|
113 |
|
# bpo-30264: Close the source on error to not leak resources: |
|
114 |
|
# xml.sax.parse() doesn't give access to the underlying parser |
|
115 |
|
# to the caller |
|
116 |
|
self._close_source() |
|
117 |
|
raise |
111 |
118 |
|
|
112 |
119 |
def prepareParser(self, source): |
def prepareParser(self, source): |
113 |
120 |
if source.getSystemId() is not None: |
if source.getSystemId() is not None: |
|
... |
... |
class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator): |
213 |
220 |
# FIXME: when to invoke error()? |
# FIXME: when to invoke error()? |
214 |
221 |
self._err_handler.fatalError(exc) |
self._err_handler.fatalError(exc) |
215 |
222 |
|
|
|
223 |
|
def _close_source(self): |
|
224 |
|
source = self._source |
|
225 |
|
try: |
|
226 |
|
file = source.getCharacterStream() |
|
227 |
|
if file is not None: |
|
228 |
|
file.close() |
|
229 |
|
finally: |
|
230 |
|
file = source.getByteStream() |
|
231 |
|
if file is not None: |
|
232 |
|
file.close() |
|
233 |
|
|
216 |
234 |
def close(self): |
def close(self): |
217 |
235 |
if (self._entity_stack or self._parser is None or |
if (self._entity_stack or self._parser is None or |
218 |
236 |
isinstance(self._parser, _ClosedParser)): |
isinstance(self._parser, _ClosedParser)): |
|
... |
... |
class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator): |
232 |
250 |
parser.ErrorColumnNumber = self._parser.ErrorColumnNumber |
parser.ErrorColumnNumber = self._parser.ErrorColumnNumber |
233 |
251 |
parser.ErrorLineNumber = self._parser.ErrorLineNumber |
parser.ErrorLineNumber = self._parser.ErrorLineNumber |
234 |
252 |
self._parser = parser |
self._parser = parser |
235 |
|
try: |
|
236 |
|
file = self._source.getCharacterStream() |
|
237 |
|
if file is not None: |
|
238 |
|
file.close() |
|
239 |
|
finally: |
|
240 |
|
file = self._source.getByteStream() |
|
241 |
|
if file is not None: |
|
242 |
|
file.close() |
|
|
253 |
|
self._close_source() |
243 |
254 |
|
|
244 |
255 |
def _reset_cont_handler(self): |
def _reset_cont_handler(self): |
245 |
256 |
self._parser.ProcessingInstructionHandler = \ |
self._parser.ProcessingInstructionHandler = \ |