diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index 869a1597f70..b9230b72bb1 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -1855,58 +1855,16 @@ def check_issue10777(): # -------------------------------------------------------------------- -class CleanContext(object): - """Provide default namespace mapping and path cache.""" - checkwarnings = None +class ElementTreeTest(unittest.TestCase): - def __init__(self, quiet=False): - if sys.flags.optimize >= 2: - # under -OO, doctests cannot be run and therefore not all warnings - # will be emitted - quiet = True - deprecations = ( - # Search behaviour is broken if search path starts with "/". - ("This search is broken in 1.3 and earlier, and will be fixed " - "in a future version. If you rely on the current behaviour, " - "change it to '.+'", FutureWarning), - # Element.getchildren() and Element.getiterator() are deprecated. - ("This method will be removed in future versions. " - "Use .+ instead.", DeprecationWarning), - ("This method will be removed in future versions. " - "Use .+ instead.", PendingDeprecationWarning), - # XMLParser.doctype() is deprecated. - ("This method of XMLParser is deprecated. Define doctype.. " - "method on the TreeBuilder target.", DeprecationWarning)) - self.checkwarnings = support.check_warnings(*deprecations, quiet=quiet) - - def __enter__(self): - from xml.etree import ElementPath - self._nsmap = ET.register_namespace._namespace_map - # Copy the default namespace mapping - self._nsmap_copy = self._nsmap.copy() - # Copy the path cache (should be empty) - self._path_cache = ElementPath._cache - ElementPath._cache = self._path_cache.copy() - self.checkwarnings.__enter__() - - def __exit__(self, *args): - from xml.etree import ElementPath - # Restore mapping and path cache - self._nsmap.clear() - self._nsmap.update(self._nsmap_copy) - ElementPath._cache = self._path_cache - self.checkwarnings.__exit__(*args) - - -class TestAcceleratorNotImported(unittest.TestCase): - # Test that the C accelerator was not imported for pyET - def test_correct_import_pyET(self): - self.assertEqual(pyET.SubElement.__module__, 'xml.etree.ElementTree') - - -class TestElementClass(unittest.TestCase): - def test_Element_is_a_type(self): + def test_istype(self): + self.assertIsInstance(ET.ParseError, type) + self.assertIsInstance(ET.QName, type) + self.assertIsInstance(ET.ElementTree, type) self.assertIsInstance(ET.Element, type) + # XXX issue 14128 with C ElementTree + # self.assertIsInstance(ET.TreeBuilder, type) + # self.assertIsInstance(ET.XMLParser, type) def test_Element_subclass_trivial(self): class MyElement(ET.Element): @@ -1936,16 +1894,115 @@ class TestElementClass(unittest.TestCase): self.assertEqual(mye.newmethod(), 'joe') +class TreeBuilderTest(unittest.TestCase): + + sample1 = ('' + 'text') + + def test_dummy_builder(self): + class BaseDummyBuilder: + def close(self): + return 42 + + class DummyBuilder(BaseDummyBuilder): + data = start = end = lambda *a: None + + parser = ET.XMLParser(target=DummyBuilder()) + parser.feed(self.sample1) + self.assertEqual(parser.close(), 42) + + parser = ET.XMLParser(target=BaseDummyBuilder()) + parser.feed(self.sample1) + self.assertEqual(parser.close(), 42) + + parser = ET.XMLParser(target=object()) + parser.feed(self.sample1) + self.assertIsNone(parser.close()) + + + @unittest.expectedFailure # XXX issue 14007 with C ElementTree + def test_doctype(self): + class DoctypeParser: + _doctype = None + + def doctype(self, name, pubid, system): + self._doctype = (name, pubid, system) + + def close(self): + return self._doctype + + parser = ET.XMLParser(target=DoctypeParser()) + parser.feed(self.sample1) + + self.assertEqual(parser.close(), + ('html', '-//W3C//DTD XHTML 1.0 Transitional//EN', + 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd')) + + +class NoAcceleratorTest(unittest.TestCase): + + # Test that the C accelerator was not imported for pyET + def test_correct_import_pyET(self): + self.assertEqual(pyET.Element.__module__, 'xml.etree.ElementTree') + self.assertEqual(pyET.SubElement.__module__, 'xml.etree.ElementTree') + +# -------------------------------------------------------------------- + + +class CleanContext(object): + """Provide default namespace mapping and path cache.""" + checkwarnings = None + + def __init__(self, quiet=False): + if sys.flags.optimize >= 2: + # under -OO, doctests cannot be run and therefore not all warnings + # will be emitted + quiet = True + deprecations = ( + # Search behaviour is broken if search path starts with "/". + ("This search is broken in 1.3 and earlier, and will be fixed " + "in a future version. If you rely on the current behaviour, " + "change it to '.+'", FutureWarning), + # Element.getchildren() and Element.getiterator() are deprecated. + ("This method will be removed in future versions. " + "Use .+ instead.", DeprecationWarning), + ("This method will be removed in future versions. " + "Use .+ instead.", PendingDeprecationWarning)) + self.checkwarnings = support.check_warnings(*deprecations, quiet=quiet) + + def __enter__(self): + from xml.etree import ElementPath + self._nsmap = ET.register_namespace._namespace_map + # Copy the default namespace mapping + self._nsmap_copy = self._nsmap.copy() + # Copy the path cache (should be empty) + self._path_cache = ElementPath._cache + ElementPath._cache = self._path_cache.copy() + self.checkwarnings.__enter__() + + def __exit__(self, *args): + from xml.etree import ElementPath + # Restore mapping and path cache + self._nsmap.clear() + self._nsmap.update(self._nsmap_copy) + ElementPath._cache = self._path_cache + self.checkwarnings.__exit__(*args) + + def test_main(module=pyET): from test import test_xml_etree - # Run the tests specific to the Python implementation - support.run_unittest(TestAcceleratorNotImported) - # The same doctests are used for both the Python and the C implementations test_xml_etree.ET = module - support.run_unittest(TestElementClass) + test_classes = [ElementTreeTest, TreeBuilderTest] + if module is pyET: + # Run the tests specific to the Python implementation + test_classes += [NoAcceleratorTest] + + support.run_unittest(*test_classes) # XXX the C module should give the same warnings as the Python module with CleanContext(quiet=(module is not pyET)): diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py index a864fa5263d..1a25f9a6302 100644 --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -1511,24 +1511,30 @@ class XMLParser: self.target = self._target = target self._error = expat.error self._names = {} # name memo cache - # callbacks + # main callbacks parser.DefaultHandlerExpand = self._default - parser.StartElementHandler = self._start - parser.EndElementHandler = self._end - parser.CharacterDataHandler = self._data - # optional callbacks - parser.CommentHandler = self._comment - parser.ProcessingInstructionHandler = self._pi + if hasattr(target, 'start'): + parser.StartElementHandler = self._start + if hasattr(target, 'end'): + parser.EndElementHandler = self._end + if hasattr(target, 'data'): + parser.CharacterDataHandler = target.data + # miscellaneous callbacks + if hasattr(target, 'comment'): + parser.CommentHandler = target.comment + if hasattr(target, 'pi'): + parser.ProcessingInstructionHandler = target.pi # let expat do the buffering, if supported try: - self._parser.buffer_text = 1 + parser.buffer_text = 1 except AttributeError: pass # use new-style attribute handling, if supported try: - self._parser.ordered_attributes = 1 - self._parser.specified_attributes = 1 - parser.StartElementHandler = self._start_list + parser.ordered_attributes = 1 + parser.specified_attributes = 1 + if hasattr(target, 'start'): + parser.StartElementHandler = self._start_list except AttributeError: pass self._doctype = None @@ -1572,44 +1578,29 @@ class XMLParser: attrib[fixname(attrib_in[i])] = attrib_in[i+1] return self.target.start(tag, attrib) - def _data(self, text): - return self.target.data(text) - def _end(self, tag): return self.target.end(self._fixname(tag)) - def _comment(self, data): - try: - comment = self.target.comment - except AttributeError: - pass - else: - return comment(data) - - def _pi(self, target, data): - try: - pi = self.target.pi - except AttributeError: - pass - else: - return pi(target, data) - def _default(self, text): prefix = text[:1] if prefix == "&": # deal with undefined entities try: - self.target.data(self.entity[text[1:-1]]) + data_handler = self.target.data + except AttributeError: + return + try: + data_handler(self.entity[text[1:-1]]) except KeyError: from xml.parsers import expat err = expat.error( "undefined entity %s: line %d, column %d" % - (text, self._parser.ErrorLineNumber, - self._parser.ErrorColumnNumber) + (text, self.parser.ErrorLineNumber, + self.parser.ErrorColumnNumber) ) err.code = 11 # XML_ERROR_UNDEFINED_ENTITY - err.lineno = self._parser.ErrorLineNumber - err.offset = self._parser.ErrorColumnNumber + err.lineno = self.parser.ErrorLineNumber + err.offset = self.parser.ErrorColumnNumber raise err elif prefix == "<" and text[:9] == "