List of commits:
Subject Hash Author Date (UTC)
bpo-30264: ExpatParser closes the source on error (#1451) ef9c0e732fc50aefbdd7c5a80e04e14b31684e66 Victor Stinner 2017-05-05 07:46:47
bpo-30277: Replace _sre.getlower() with _sre.ascii_tolower() and _sre.unicode_tolower(). (#1468) 7186cc29be352bed6f1110873283d073fd0643e4 Serhiy Storchaka 2017-05-05 07:42:46
bpo-30243: Fixed the possibility of a crash in _json. (#1420) 76a3e51a403bc84ed536921866c86dd7d07aaa7e Serhiy Storchaka 2017-05-05 07:08:49
bpo-30215: Make re.compile() locale agnostic. (#1361) 898ff03e1e7925ecde3da66327d3cdc7e07625ba Serhiy Storchaka 2017-05-05 05:53:40
Make code coverage less strict (GH-1438) 647c3d381e67490e82cdbbe6c96e46d5e1628ce2 Brett Cannon 2017-05-04 21:58:54
bpo-30273: Update sysconfig (#1464) b109a1d3360fc4bb87b9887264e3634632d392ca Victor Stinner 2017-05-04 21:29:09
bpo-30185: avoid KeyboardInterrupt tracebacks in forkserver (#1319) 6dd4d734ed207ba16b017e38f8909de7ef187e29 Antoine Pitrou 2017-05-04 14:44:53
bpo-30263: regrtest: log system load (#1452) 3d0056842c5e06b4102f990b59ab3b607f932dd8 Victor Stinner 2017-05-04 13:21:12
bpo-29956: Improve the math.exp() related documentation. (#1073) dbaf746b6de0ee431c809d3175ab40ccc18898a8 Serhiy Storchaka 2017-05-04 09:25:09
bpo-30166: Import command-line parsing modules only when needed. (#1293) 7e4db2f253c555568d56177c2fd083bcf8f88d34 Serhiy Storchaka 2017-05-04 05:17:47
bpo-30225: Fix is_valid_fd() on macOS Tiger (#1443) 1c4670ea0cc3d208121af11b9b973e6bb268e570 Victor Stinner 2017-05-03 22:45:56
bpo-30184: Add tests for invalid use of PyArg_ParseTupleAndKeywords. (#1316) 5f161fd86dd5bb936a1a2a13391b13b7e59ec201 Serhiy Storchaka 2017-05-03 21:03:23
Add myself to mention-bot's alwaysNotifyForPaths for ssl/hashlib (#1425) feec3dc9c308052754f9e4848c1c1ddb007e9f66 Christian Heimes 2017-05-03 18:17:54
Explicitly mention what should (not) be discussed on GitHub (GH-1437) 7fdd30975e3fa9ec4be62464a9fcab7798d0ed6e Brett Cannon 2017-05-03 17:51:55
bpo-28315: Improve code examples in docs (GH-1372) 8856940cf2e82cb17db2b684cd5732fe658605ca UltimateCoder 2017-05-03 16:46:45
bpo-23404: make touch becomes make regen-all (#1405) a5c62a8e9f0de6c4133825a5710984a3cd5e102b Victor Stinner 2017-05-03 16:21:48
bpo-30103: Allow Uuencode in Python using backtick as zero instead of space (#1326) 13f1f423fac39f8f14a3ce919dd236975517d5c6 Xiang Zhang 2017-05-03 03:16:21
Fix tests: getsockname() can return None on OS X on unbound sockets (#1400) 0360a9d015ddbc4e3d58e3ab4b433da27bf1db3a Antoine Pitrou 2017-05-02 21:48:26
bpo-30232: Regenerate configure (#1396) 9ed34a89532763cf89f5e11fffb91ef7dee29fed Victor Stinner 2017-05-02 20:35:58
bpo-30232: Support Git worktree in configure.ac (#1391) 5facdbb29169c2799c42f887cef4cd9d087b0167 Victor Stinner 2017-05-02 19:42:48
Commit ef9c0e732fc50aefbdd7c5a80e04e14b31684e66 - bpo-30264: ExpatParser closes the source on error (#1451)
ExpatParser.parse() of xml.sax.xmlreader now always closes the
source: close the file object or the urllib object if source is a
string (not an open file-like object). The change fixes a
ResourceWarning on parsing error.

Add test_parse_close_source() unit test.
Author: Victor Stinner
Author date (UTC): 2017-05-05 07:46
Committer name: GitHub
Committer date (UTC): 2017-05-05 07:46
Parent(s): 7186cc29be352bed6f1110873283d073fd0643e4
Signer:
Signing key:
Signing status: N
Tree: a59653d14c060ab0037012529ccc678ee8049b96
File Lines added Lines deleted
Lib/test/test_sax.py 18 6
Lib/xml/sax/expatreader.py 22 11
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 = \
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/benf_wspdigital/cpython

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/benf_wspdigital/cpython

Clone this repository using git:
git clone git://git.rocketgit.com/user/benf_wspdigital/cpython

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main