summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_xml_etree.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_xml_etree.py')
-rw-r--r--Lib/test/test_xml_etree.py586
1 files changed, 533 insertions, 53 deletions
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
index 7bd8a2c..5c2a2af 100644
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -5,12 +5,15 @@
# For this purpose, the module-level "ET" symbol is temporarily
# monkey-patched when running the "test_xml_etree_c" test suite.
+import copy
import html
import io
import operator
import pickle
import sys
+import types
import unittest
+import warnings
import weakref
from itertools import product
@@ -120,11 +123,11 @@ class ElementTestCase:
def setUpClass(cls):
cls.modules = {pyET, ET}
- def pickleRoundTrip(self, obj, name, dumper, loader):
+ def pickleRoundTrip(self, obj, name, dumper, loader, proto):
save_m = sys.modules[name]
try:
sys.modules[name] = dumper
- temp = pickle.dumps(obj)
+ temp = pickle.dumps(obj, proto)
sys.modules[name] = loader
result = pickle.loads(temp)
except pickle.PicklingError as pe:
@@ -240,7 +243,33 @@ class ElementTreeTest(unittest.TestCase):
self.assertEqual(ET.XML, ET.fromstring)
self.assertEqual(ET.PI, ET.ProcessingInstruction)
- self.assertEqual(ET.XMLParser, ET.XMLTreeBuilder)
+
+ def test_set_attribute(self):
+ element = ET.Element('tag')
+
+ self.assertEqual(element.tag, 'tag')
+ element.tag = 'Tag'
+ self.assertEqual(element.tag, 'Tag')
+ element.tag = 'TAG'
+ self.assertEqual(element.tag, 'TAG')
+
+ self.assertIsNone(element.text)
+ element.text = 'Text'
+ self.assertEqual(element.text, 'Text')
+ element.text = 'TEXT'
+ self.assertEqual(element.text, 'TEXT')
+
+ self.assertIsNone(element.tail)
+ element.tail = 'Tail'
+ self.assertEqual(element.tail, 'Tail')
+ element.tail = 'TAIL'
+ self.assertEqual(element.tail, 'TAIL')
+
+ self.assertEqual(element.attrib, {})
+ element.attrib = {'a': 'b', 'c': 'd'}
+ self.assertEqual(element.attrib, {'a': 'b', 'c': 'd'})
+ element.attrib = {'A': 'B', 'C': 'D'}
+ self.assertEqual(element.attrib, {'A': 'B', 'C': 'D'})
def test_simpleops(self):
# Basic method sanity checks.
@@ -433,15 +462,6 @@ class ElementTreeTest(unittest.TestCase):
' <empty-element />\n'
'</root>')
- parser = ET.XMLTreeBuilder() # 1.2 compatibility
- parser.feed(data)
- self.serialize_check(parser.close(),
- '<root>\n'
- ' <element key="value">text</element>\n'
- ' <element>text</element>tail\n'
- ' <empty-element />\n'
- '</root>')
-
target = ET.TreeBuilder()
parser = ET.XMLParser(target=target)
parser.feed(data)
@@ -541,11 +561,21 @@ class ElementTreeTest(unittest.TestCase):
self.assertEqual(res, ['start-ns', 'end-ns'])
events = ("start", "end", "bogus")
- with self.assertRaises(ValueError) as cm:
- with open(SIMPLE_XMLFILE, "rb") as f:
+ with open(SIMPLE_XMLFILE, "rb") as f:
+ with self.assertRaises(ValueError) as cm:
iterparse(f, events)
+ self.assertFalse(f.closed)
self.assertEqual(str(cm.exception), "unknown event 'bogus'")
+ with warnings.catch_warnings(record=True) as w:
+ warnings.filterwarnings("always", category=ResourceWarning)
+ with self.assertRaises(ValueError) as cm:
+ iterparse(SIMPLE_XMLFILE, events)
+ self.assertEqual(str(cm.exception), "unknown event 'bogus'")
+ del cm
+ support.gc_collect()
+ self.assertEqual(w, [])
+
source = io.BytesIO(
b"<?xml version='1.0' encoding='iso-8859-1'?>\n"
b"<body xmlns='http://&#233;ffbot.org/ns'\n"
@@ -566,6 +596,21 @@ class ElementTreeTest(unittest.TestCase):
self.assertEqual(str(cm.exception),
'junk after document element: line 1, column 12')
+ with open(TESTFN, "wb") as f:
+ f.write(b"<document />junk")
+ it = iterparse(TESTFN)
+ action, elem = next(it)
+ self.assertEqual((action, elem.tag), ('end', 'document'))
+ with warnings.catch_warnings(record=True) as w:
+ warnings.filterwarnings("always", category=ResourceWarning)
+ with self.assertRaises(ET.ParseError) as cm:
+ next(it)
+ self.assertEqual(str(cm.exception),
+ 'junk after document element: line 1, column 12')
+ del cm, it
+ support.gc_collect()
+ self.assertEqual(w, [])
+
def test_writefile(self):
elem = ET.Element("tag")
elem.text = "text"
@@ -706,9 +751,9 @@ class ElementTreeTest(unittest.TestCase):
'iso8859-13', 'iso8859-14', 'iso8859-15', 'iso8859-16',
'cp437', 'cp720', 'cp737', 'cp775', 'cp850', 'cp852',
'cp855', 'cp856', 'cp857', 'cp858', 'cp860', 'cp861', 'cp862',
- 'cp863', 'cp865', 'cp866', 'cp869', 'cp874', 'cp1006', 'cp1250',
- 'cp1251', 'cp1252', 'cp1253', 'cp1254', 'cp1255', 'cp1256',
- 'cp1257', 'cp1258',
+ 'cp863', 'cp865', 'cp866', 'cp869', 'cp874', 'cp1006', 'cp1125',
+ 'cp1250', 'cp1251', 'cp1252', 'cp1253', 'cp1254', 'cp1255',
+ 'cp1256', 'cp1257', 'cp1258',
'mac-cyrillic', 'mac-greek', 'mac-iceland', 'mac-latin2',
'mac-roman', 'mac-turkish',
'iso2022-jp', 'iso2022-jp-1', 'iso2022-jp-2', 'iso2022-jp-2004',
@@ -964,6 +1009,160 @@ class ElementTreeTest(unittest.TestCase):
self.assertEqual(serialized, expected)
+class XMLPullParserTest(unittest.TestCase):
+
+ def _feed(self, parser, data, chunk_size=None):
+ if chunk_size is None:
+ parser.feed(data)
+ else:
+ for i in range(0, len(data), chunk_size):
+ parser.feed(data[i:i+chunk_size])
+
+ def assert_event_tags(self, parser, expected):
+ events = parser.read_events()
+ self.assertEqual([(action, elem.tag) for action, elem in events],
+ expected)
+
+ def test_simple_xml(self):
+ for chunk_size in (None, 1, 5):
+ with self.subTest(chunk_size=chunk_size):
+ parser = ET.XMLPullParser()
+ self.assert_event_tags(parser, [])
+ self._feed(parser, "<!-- comment -->\n", chunk_size)
+ self.assert_event_tags(parser, [])
+ self._feed(parser,
+ "<root>\n <element key='value'>text</element",
+ chunk_size)
+ self.assert_event_tags(parser, [])
+ self._feed(parser, ">\n", chunk_size)
+ self.assert_event_tags(parser, [('end', 'element')])
+ self._feed(parser, "<element>text</element>tail\n", chunk_size)
+ self._feed(parser, "<empty-element/>\n", chunk_size)
+ self.assert_event_tags(parser, [
+ ('end', 'element'),
+ ('end', 'empty-element'),
+ ])
+ self._feed(parser, "</root>\n", chunk_size)
+ self.assert_event_tags(parser, [('end', 'root')])
+ self.assertIsNone(parser.close())
+
+ def test_feed_while_iterating(self):
+ parser = ET.XMLPullParser()
+ it = parser.read_events()
+ self._feed(parser, "<root>\n <element key='value'>text</element>\n")
+ action, elem = next(it)
+ self.assertEqual((action, elem.tag), ('end', 'element'))
+ self._feed(parser, "</root>\n")
+ action, elem = next(it)
+ self.assertEqual((action, elem.tag), ('end', 'root'))
+ with self.assertRaises(StopIteration):
+ next(it)
+
+ def test_simple_xml_with_ns(self):
+ parser = ET.XMLPullParser()
+ self.assert_event_tags(parser, [])
+ self._feed(parser, "<!-- comment -->\n")
+ self.assert_event_tags(parser, [])
+ self._feed(parser, "<root xmlns='namespace'>\n")
+ self.assert_event_tags(parser, [])
+ self._feed(parser, "<element key='value'>text</element")
+ self.assert_event_tags(parser, [])
+ self._feed(parser, ">\n")
+ self.assert_event_tags(parser, [('end', '{namespace}element')])
+ self._feed(parser, "<element>text</element>tail\n")
+ self._feed(parser, "<empty-element/>\n")
+ self.assert_event_tags(parser, [
+ ('end', '{namespace}element'),
+ ('end', '{namespace}empty-element'),
+ ])
+ self._feed(parser, "</root>\n")
+ self.assert_event_tags(parser, [('end', '{namespace}root')])
+ self.assertIsNone(parser.close())
+
+ def test_ns_events(self):
+ parser = ET.XMLPullParser(events=('start-ns', 'end-ns'))
+ self._feed(parser, "<!-- comment -->\n")
+ self._feed(parser, "<root xmlns='namespace'>\n")
+ self.assertEqual(
+ list(parser.read_events()),
+ [('start-ns', ('', 'namespace'))])
+ self._feed(parser, "<element key='value'>text</element")
+ self._feed(parser, ">\n")
+ self._feed(parser, "<element>text</element>tail\n")
+ self._feed(parser, "<empty-element/>\n")
+ self._feed(parser, "</root>\n")
+ self.assertEqual(list(parser.read_events()), [('end-ns', None)])
+ self.assertIsNone(parser.close())
+
+ def test_events(self):
+ parser = ET.XMLPullParser(events=())
+ self._feed(parser, "<root/>\n")
+ self.assert_event_tags(parser, [])
+
+ parser = ET.XMLPullParser(events=('start', 'end'))
+ self._feed(parser, "<!-- comment -->\n")
+ self.assert_event_tags(parser, [])
+ self._feed(parser, "<root>\n")
+ self.assert_event_tags(parser, [('start', 'root')])
+ self._feed(parser, "<element key='value'>text</element")
+ self.assert_event_tags(parser, [('start', 'element')])
+ self._feed(parser, ">\n")
+ self.assert_event_tags(parser, [('end', 'element')])
+ self._feed(parser,
+ "<element xmlns='foo'>text<empty-element/></element>tail\n")
+ self.assert_event_tags(parser, [
+ ('start', '{foo}element'),
+ ('start', '{foo}empty-element'),
+ ('end', '{foo}empty-element'),
+ ('end', '{foo}element'),
+ ])
+ self._feed(parser, "</root>")
+ self.assertIsNone(parser.close())
+ self.assert_event_tags(parser, [('end', 'root')])
+
+ parser = ET.XMLPullParser(events=('start',))
+ self._feed(parser, "<!-- comment -->\n")
+ self.assert_event_tags(parser, [])
+ self._feed(parser, "<root>\n")
+ self.assert_event_tags(parser, [('start', 'root')])
+ self._feed(parser, "<element key='value'>text</element")
+ self.assert_event_tags(parser, [('start', 'element')])
+ self._feed(parser, ">\n")
+ self.assert_event_tags(parser, [])
+ self._feed(parser,
+ "<element xmlns='foo'>text<empty-element/></element>tail\n")
+ self.assert_event_tags(parser, [
+ ('start', '{foo}element'),
+ ('start', '{foo}empty-element'),
+ ])
+ self._feed(parser, "</root>")
+ self.assertIsNone(parser.close())
+
+ def test_events_sequence(self):
+ # Test that events can be some sequence that's not just a tuple or list
+ eventset = {'end', 'start'}
+ parser = ET.XMLPullParser(events=eventset)
+ self._feed(parser, "<foo>bar</foo>")
+ self.assert_event_tags(parser, [('start', 'foo'), ('end', 'foo')])
+
+ class DummyIter:
+ def __init__(self):
+ self.events = iter(['start', 'end', 'start-ns'])
+ def __iter__(self):
+ return self
+ def __next__(self):
+ return next(self.events)
+
+ parser = ET.XMLPullParser(events=DummyIter())
+ self._feed(parser, "<foo>bar</foo>")
+ self.assert_event_tags(parser, [('start', 'foo'), ('end', 'foo')])
+
+
+ def test_unknown_event(self):
+ with self.assertRaises(ValueError):
+ ET.XMLPullParser(events=('start', 'end', 'bogus'))
+
+
#
# xinclude tests (samples from appendix C of the xinclude specification)
@@ -1305,7 +1504,7 @@ class BugsTest(unittest.TestCase):
# Don't crash when using custom entities.
ENTITIES = {'rsquo': '\u2019', 'lsquo': '\u2018'}
- parser = ET.XMLTreeBuilder()
+ parser = ET.XMLParser()
parser.entity.update(ENTITIES)
parser.feed("""<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE patent-application-publication SYSTEM "pap-v15-2001-01-31.dtd" []>
@@ -1532,33 +1731,156 @@ class BasicElementTest(ElementTestCase, unittest.TestCase):
def test_pickle(self):
# issue #16076: the C implementation wasn't pickleable.
- for dumper, loader in product(self.modules, repeat=2):
- e = dumper.Element('foo', bar=42)
- e.text = "text goes here"
- e.tail = "opposite of head"
- dumper.SubElement(e, 'child').append(dumper.Element('grandchild'))
- e.append(dumper.Element('child'))
- e.findall('.//grandchild')[0].set('attr', 'other value')
-
- e2 = self.pickleRoundTrip(e, 'xml.etree.ElementTree',
- dumper, loader)
-
- self.assertEqual(e2.tag, 'foo')
- self.assertEqual(e2.attrib['bar'], 42)
- self.assertEqual(len(e2), 2)
- self.assertEqualElements(e, e2)
+ for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
+ for dumper, loader in product(self.modules, repeat=2):
+ e = dumper.Element('foo', bar=42)
+ e.text = "text goes here"
+ e.tail = "opposite of head"
+ dumper.SubElement(e, 'child').append(dumper.Element('grandchild'))
+ e.append(dumper.Element('child'))
+ e.findall('.//grandchild')[0].set('attr', 'other value')
+
+ e2 = self.pickleRoundTrip(e, 'xml.etree.ElementTree',
+ dumper, loader, proto)
+
+ self.assertEqual(e2.tag, 'foo')
+ self.assertEqual(e2.attrib['bar'], 42)
+ self.assertEqual(len(e2), 2)
+ self.assertEqualElements(e, e2)
def test_pickle_issue18997(self):
- for dumper, loader in product(self.modules, repeat=2):
- XMLTEXT = """<?xml version="1.0"?>
- <group><dogs>4</dogs>
- </group>"""
- e1 = dumper.fromstring(XMLTEXT)
- if hasattr(e1, '__getstate__'):
- self.assertEqual(e1.__getstate__()['tag'], 'group')
- e2 = self.pickleRoundTrip(e1, 'xml.etree.ElementTree', dumper, loader)
- self.assertEqual(e2.tag, 'group')
- self.assertEqual(e2[0].tag, 'dogs')
+ for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
+ for dumper, loader in product(self.modules, repeat=2):
+ XMLTEXT = """<?xml version="1.0"?>
+ <group><dogs>4</dogs>
+ </group>"""
+ e1 = dumper.fromstring(XMLTEXT)
+ if hasattr(e1, '__getstate__'):
+ self.assertEqual(e1.__getstate__()['tag'], 'group')
+ e2 = self.pickleRoundTrip(e1, 'xml.etree.ElementTree',
+ dumper, loader, proto)
+ self.assertEqual(e2.tag, 'group')
+ self.assertEqual(e2[0].tag, 'dogs')
+
+
+class BadElementTest(ElementTestCase, unittest.TestCase):
+ def test_extend_mutable_list(self):
+ class X:
+ @property
+ def __class__(self):
+ L[:] = [ET.Element('baz')]
+ return ET.Element
+ L = [X()]
+ e = ET.Element('foo')
+ try:
+ e.extend(L)
+ except TypeError:
+ pass
+
+ class Y(X, ET.Element):
+ pass
+ L = [Y('x')]
+ e = ET.Element('foo')
+ e.extend(L)
+
+ def test_extend_mutable_list2(self):
+ class X:
+ @property
+ def __class__(self):
+ del L[:]
+ return ET.Element
+ L = [X(), ET.Element('baz')]
+ e = ET.Element('foo')
+ try:
+ e.extend(L)
+ except TypeError:
+ pass
+
+ class Y(X, ET.Element):
+ pass
+ L = [Y('bar'), ET.Element('baz')]
+ e = ET.Element('foo')
+ e.extend(L)
+
+ def test_remove_with_mutating(self):
+ class X(ET.Element):
+ def __eq__(self, o):
+ del e[:]
+ return False
+ e = ET.Element('foo')
+ e.extend([X('bar')])
+ self.assertRaises(ValueError, e.remove, ET.Element('baz'))
+
+ e = ET.Element('foo')
+ e.extend([ET.Element('bar')])
+ self.assertRaises(ValueError, e.remove, X('baz'))
+
+
+class MutatingElementPath(str):
+ def __new__(cls, elem, *args):
+ self = str.__new__(cls, *args)
+ self.elem = elem
+ return self
+ def __eq__(self, o):
+ del self.elem[:]
+ return True
+MutatingElementPath.__hash__ = str.__hash__
+
+class BadElementPath(str):
+ def __eq__(self, o):
+ raise 1/0
+BadElementPath.__hash__ = str.__hash__
+
+class BadElementPathTest(ElementTestCase, unittest.TestCase):
+ def setUp(self):
+ super().setUp()
+ from xml.etree import ElementPath
+ self.path_cache = ElementPath._cache
+ ElementPath._cache = {}
+
+ def tearDown(self):
+ from xml.etree import ElementPath
+ ElementPath._cache = self.path_cache
+ super().tearDown()
+
+ def test_find_with_mutating(self):
+ e = ET.Element('foo')
+ e.extend([ET.Element('bar')])
+ e.find(MutatingElementPath(e, 'x'))
+
+ def test_find_with_error(self):
+ e = ET.Element('foo')
+ e.extend([ET.Element('bar')])
+ try:
+ e.find(BadElementPath('x'))
+ except ZeroDivisionError:
+ pass
+
+ def test_findtext_with_mutating(self):
+ e = ET.Element('foo')
+ e.extend([ET.Element('bar')])
+ e.findtext(MutatingElementPath(e, 'x'))
+
+ def test_findtext_with_error(self):
+ e = ET.Element('foo')
+ e.extend([ET.Element('bar')])
+ try:
+ e.findtext(BadElementPath('x'))
+ except ZeroDivisionError:
+ pass
+
+ def test_findall_with_mutating(self):
+ e = ET.Element('foo')
+ e.extend([ET.Element('bar')])
+ e.findall(MutatingElementPath(e, 'x'))
+
+ def test_findall_with_error(self):
+ e = ET.Element('foo')
+ e.extend([ET.Element('bar')])
+ try:
+ e.findall(BadElementPath('x'))
+ except ZeroDivisionError:
+ pass
class ElementTreeTypeTest(unittest.TestCase):
@@ -1643,6 +1965,11 @@ class ElementFindTest(unittest.TestCase):
self.assertEqual(e.find('./tag[last()-1]').attrib['class'], 'c')
self.assertEqual(e.find('./tag[last()-2]').attrib['class'], 'b')
+ self.assertRaisesRegex(SyntaxError, 'XPath', e.find, './tag[0]')
+ self.assertRaisesRegex(SyntaxError, 'XPath', e.find, './tag[-1]')
+ self.assertRaisesRegex(SyntaxError, 'XPath', e.find, './tag[last()-0]')
+ self.assertRaisesRegex(SyntaxError, 'XPath', e.find, './tag[last()+1]')
+
def test_findall(self):
e = ET.XML(SAMPLE_XML)
e[2] = ET.XML(SAMPLE_SECTION)
@@ -1808,6 +2135,19 @@ class ElementIterTest(unittest.TestCase):
self.assertEqual(self._ilist(doc), all_tags)
self.assertEqual(self._ilist(doc, '*'), all_tags)
+ def test_copy(self):
+ a = ET.Element('a')
+ it = a.iter()
+ with self.assertRaises(TypeError):
+ copy.copy(it)
+
+ def test_pickle(self):
+ a = ET.Element('a')
+ it = a.iter()
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ with self.assertRaises((TypeError, pickle.PicklingError)):
+ pickle.dumps(it, proto)
+
class TreeBuilderTest(unittest.TestCase):
sample1 = ('<!DOCTYPE html PUBLIC'
@@ -1902,7 +2242,7 @@ class TreeBuilderTest(unittest.TestCase):
# Mimick SimpleTAL's behaviour (issue #16089): both versions of
# TreeBuilder should be able to cope with a subclass of the
# pure Python Element class.
- base = ET._Element
+ base = ET._Element_Py
# Not from a C extension
self.assertEqual(base.__module__, 'xml.etree.ElementTree')
# Force some multiple inheritance with a C class to make things
@@ -1964,6 +2304,20 @@ class XMLParserTest(unittest.TestCase):
parser.feed(self.sample1)
self._check_sample_element(parser.close())
+ def test_doctype_warning(self):
+ parser = ET.XMLParser()
+ with self.assertWarns(DeprecationWarning):
+ parser.doctype('html', '-//W3C//DTD XHTML 1.0 Transitional//EN',
+ 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd')
+ parser.feed('<html/>')
+ parser.close()
+
+ with warnings.catch_warnings():
+ warnings.simplefilter('error', DeprecationWarning)
+ parser = ET.XMLParser()
+ parser.feed(self.sample2)
+ parser.close()
+
def test_subclass_doctype(self):
_doctype = None
class MyParserWithDoctype(ET.XMLParser):
@@ -1979,6 +2333,32 @@ class XMLParserTest(unittest.TestCase):
('html', '-//W3C//DTD XHTML 1.0 Transitional//EN',
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'))
+ _doctype = _doctype2 = None
+ with warnings.catch_warnings():
+ warnings.simplefilter('error', DeprecationWarning)
+ class DoctypeParser:
+ def doctype(self, name, pubid, system):
+ nonlocal _doctype2
+ _doctype2 = (name, pubid, system)
+
+ parser = MyParserWithDoctype(target=DoctypeParser())
+ parser.feed(self.sample2)
+ parser.close()
+ self.assertIsNone(_doctype)
+ self.assertEqual(_doctype2,
+ ('html', '-//W3C//DTD XHTML 1.0 Transitional//EN',
+ 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'))
+
+ def test_inherited_doctype(self):
+ '''Ensure that ordinary usage is not deprecated (Issue 19176)'''
+ with warnings.catch_warnings():
+ warnings.simplefilter('error', DeprecationWarning)
+ class MyParserWithoutDoctype(ET.XMLParser):
+ pass
+ parser = MyParserWithoutDoctype()
+ parser.feed(self.sample2)
+ parser.close()
+
def test_parse_string(self):
parser = ET.XMLParser(target=ET.TreeBuilder())
parser.feed(self.sample3)
@@ -2022,6 +2402,7 @@ class ElementSlicingTest(unittest.TestCase):
self.assertEqual(e[-2].tag, 'a8')
self.assertRaises(IndexError, lambda: e[12])
+ self.assertRaises(IndexError, lambda: e[-12])
def test_getslice_range(self):
e = self._make_elem_with_children(6)
@@ -2040,12 +2421,17 @@ class ElementSlicingTest(unittest.TestCase):
self.assertEqual(self._elem_tags(e[::3]), ['a0', 'a3', 'a6', 'a9'])
self.assertEqual(self._elem_tags(e[::8]), ['a0', 'a8'])
self.assertEqual(self._elem_tags(e[1::8]), ['a1', 'a9'])
+ self.assertEqual(self._elem_tags(e[3::sys.maxsize]), ['a3'])
+ self.assertEqual(self._elem_tags(e[3::sys.maxsize<<64]), ['a3'])
def test_getslice_negative_steps(self):
e = self._make_elem_with_children(4)
self.assertEqual(self._elem_tags(e[::-1]), ['a3', 'a2', 'a1', 'a0'])
self.assertEqual(self._elem_tags(e[::-2]), ['a3', 'a1'])
+ self.assertEqual(self._elem_tags(e[3::-sys.maxsize]), ['a3'])
+ self.assertEqual(self._elem_tags(e[3::-sys.maxsize-1]), ['a3'])
+ self.assertEqual(self._elem_tags(e[3::-sys.maxsize<<64]), ['a3'])
def test_delslice(self):
e = self._make_elem_with_children(4)
@@ -2072,6 +2458,75 @@ class ElementSlicingTest(unittest.TestCase):
del e[::2]
self.assertEqual(self._subelem_tags(e), ['a1'])
+ def test_setslice_single_index(self):
+ e = self._make_elem_with_children(4)
+ e[1] = ET.Element('b')
+ self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'a2', 'a3'])
+
+ e[-2] = ET.Element('c')
+ self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'c', 'a3'])
+
+ with self.assertRaises(IndexError):
+ e[5] = ET.Element('d')
+ with self.assertRaises(IndexError):
+ e[-5] = ET.Element('d')
+ self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'c', 'a3'])
+
+ def test_setslice_range(self):
+ e = self._make_elem_with_children(4)
+ e[1:3] = [ET.Element('b%s' % i) for i in range(2)]
+ self.assertEqual(self._subelem_tags(e), ['a0', 'b0', 'b1', 'a3'])
+
+ e = self._make_elem_with_children(4)
+ e[1:3] = [ET.Element('b')]
+ self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'a3'])
+
+ e = self._make_elem_with_children(4)
+ e[1:3] = [ET.Element('b%s' % i) for i in range(3)]
+ self.assertEqual(self._subelem_tags(e), ['a0', 'b0', 'b1', 'b2', 'a3'])
+
+ def test_setslice_steps(self):
+ e = self._make_elem_with_children(6)
+ e[1:5:2] = [ET.Element('b%s' % i) for i in range(2)]
+ self.assertEqual(self._subelem_tags(e), ['a0', 'b0', 'a2', 'b1', 'a4', 'a5'])
+
+ e = self._make_elem_with_children(6)
+ with self.assertRaises(ValueError):
+ e[1:5:2] = [ET.Element('b')]
+ with self.assertRaises(ValueError):
+ e[1:5:2] = [ET.Element('b%s' % i) for i in range(3)]
+ with self.assertRaises(ValueError):
+ e[1:5:2] = []
+ self.assertEqual(self._subelem_tags(e), ['a0', 'a1', 'a2', 'a3', 'a4', 'a5'])
+
+ e = self._make_elem_with_children(4)
+ e[1::sys.maxsize] = [ET.Element('b')]
+ self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'a2', 'a3'])
+ e[1::sys.maxsize<<64] = [ET.Element('c')]
+ self.assertEqual(self._subelem_tags(e), ['a0', 'c', 'a2', 'a3'])
+
+ def test_setslice_negative_steps(self):
+ e = self._make_elem_with_children(4)
+ e[2:0:-1] = [ET.Element('b%s' % i) for i in range(2)]
+ self.assertEqual(self._subelem_tags(e), ['a0', 'b1', 'b0', 'a3'])
+
+ e = self._make_elem_with_children(4)
+ with self.assertRaises(ValueError):
+ e[2:0:-1] = [ET.Element('b')]
+ with self.assertRaises(ValueError):
+ e[2:0:-1] = [ET.Element('b%s' % i) for i in range(3)]
+ with self.assertRaises(ValueError):
+ e[2:0:-1] = []
+ self.assertEqual(self._subelem_tags(e), ['a0', 'a1', 'a2', 'a3'])
+
+ e = self._make_elem_with_children(4)
+ e[1::-sys.maxsize] = [ET.Element('b')]
+ self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'a2', 'a3'])
+ e[1::-sys.maxsize-1] = [ET.Element('c')]
+ self.assertEqual(self._subelem_tags(e), ['a0', 'c', 'a2', 'a3'])
+ e[1::-sys.maxsize<<64] = [ET.Element('d')]
+ self.assertEqual(self._subelem_tags(e), ['a0', 'd', 'a2', 'a3'])
+
class IOTest(unittest.TestCase):
def tearDown(self):
@@ -2082,14 +2537,21 @@ class IOTest(unittest.TestCase):
elem = ET.Element("tag")
elem.text = "abc"
self.assertEqual(serialize(elem), '<tag>abc</tag>')
- self.assertEqual(serialize(elem, encoding="utf-8"),
- b'<tag>abc</tag>')
- self.assertEqual(serialize(elem, encoding="us-ascii"),
- b'<tag>abc</tag>')
+ for enc in ("utf-8", "us-ascii"):
+ with self.subTest(enc):
+ self.assertEqual(serialize(elem, encoding=enc),
+ b'<tag>abc</tag>')
+ self.assertEqual(serialize(elem, encoding=enc.upper()),
+ b'<tag>abc</tag>')
for enc in ("iso-8859-1", "utf-16", "utf-32"):
- self.assertEqual(serialize(elem, encoding=enc),
- ("<?xml version='1.0' encoding='%s'?>\n"
- "<tag>abc</tag>" % enc).encode(enc))
+ with self.subTest(enc):
+ self.assertEqual(serialize(elem, encoding=enc),
+ ("<?xml version='1.0' encoding='%s'?>\n"
+ "<tag>abc</tag>" % enc).encode(enc))
+ upper = enc.upper()
+ self.assertEqual(serialize(elem, encoding=upper),
+ ("<?xml version='1.0' encoding='%s'?>\n"
+ "<tag>abc</tag>" % upper).encode(enc))
elem = ET.Element("tag")
elem.text = "<&\"\'>"
@@ -2261,6 +2723,18 @@ class IOTest(unittest.TestCase):
ET.tostring(root, 'utf-16'),
b''.join(ET.tostringlist(root, 'utf-16')))
+ def test_short_empty_elements(self):
+ root = ET.fromstring('<tag>a<x />b<y></y>c</tag>')
+ self.assertEqual(
+ ET.tostring(root, 'unicode'),
+ '<tag>a<x />b<y />c</tag>')
+ self.assertEqual(
+ ET.tostring(root, 'unicode', short_empty_elements=True),
+ '<tag>a<x />b<y />c</tag>')
+ self.assertEqual(
+ ET.tostring(root, 'unicode', short_empty_elements=False),
+ '<tag>a<x></x>b<y></y>c</tag>')
+
class ParseErrorTest(unittest.TestCase):
def test_subclass(self):
@@ -2326,8 +2800,11 @@ 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')
+ # The type of methods defined in Python code is types.FunctionType,
+ # while the type of methods defined inside _elementtree is
+ # <class 'wrapper_descriptor'>
+ self.assertIsInstance(pyET.Element.__init__, types.FunctionType)
+ self.assertIsInstance(pyET.XMLParser.__init__, types.FunctionType)
# --------------------------------------------------------------------
@@ -2388,6 +2865,8 @@ def test_main(module=None):
ModuleTest,
ElementSlicingTest,
BasicElementTest,
+ BadElementTest,
+ BadElementPathTest,
ElementTreeTest,
IOTest,
ParseErrorTest,
@@ -2397,6 +2876,7 @@ def test_main(module=None):
ElementIterTest,
TreeBuilderTest,
XMLParserTest,
+ XMLPullParserTest,
BugsTest,
]