bpo-30264: ExpatParser closes the source on error (#1451) (#1474)

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.
(cherry picked from commit ef9c0e732fc50aefbdd7c5a80e04e14b31684e66)
diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py
index 2411895..2eb6290 100644
--- a/Lib/test/test_sax.py
+++ b/Lib/test/test_sax.py
@@ -4,6 +4,7 @@
 from xml.sax import make_parser, ContentHandler, \
                     SAXException, SAXReaderNotAvailable, SAXParseException
 import unittest
+from unittest import mock
 try:
     make_parser()
 except SAXReaderNotAvailable:
@@ -175,12 +176,8 @@
         with self.assertRaises(SAXException):
             self.check_parse(BytesIO(xml_bytes(self.data, 'iso-8859-1', None)))
         make_xml_file(self.data, 'iso-8859-1', None)
-        with support.check_warnings(('unclosed file', ResourceWarning)):
-            # XXX Failed parser leaks an opened file.
-            with self.assertRaises(SAXException):
-                self.check_parse(TESTFN)
-            # Collect leaked file.
-            gc.collect()
+        with self.assertRaises(SAXException):
+            self.check_parse(TESTFN)
         with open(TESTFN, 'rb') as f:
             with self.assertRaises(SAXException):
                 self.check_parse(f)
@@ -194,6 +191,21 @@
             input.setEncoding('iso-8859-1')
             self.check_parse(input)
 
+    def test_parse_close_source(self):
+        builtin_open = open
+        fileobj = None
+
+        def mock_open(*args):
+            nonlocal fileobj
+            fileobj = builtin_open(*args)
+            return fileobj
+
+        with mock.patch('xml.sax.saxutils.open', side_effect=mock_open):
+            make_xml_file(self.data, 'iso-8859-1', None)
+            with self.assertRaises(SAXException):
+                self.check_parse(TESTFN)
+            self.assertTrue(fileobj.closed)
+
     def check_parseString(self, s):
         from xml.sax import parseString
         result = StringIO()