1 # -*- coding: utf-8 -*-
4 Tests for the ElementTree API
6 Only test cases that apply equally well to etree and ElementTree
7 belong here. Note that there is a second test module called test_io.py
8 for IO related test cases.
11 from __future__ import absolute_import
21 from contextlib import contextmanager
22 from functools import wraps, partial
23 from itertools import islice
25 from .common_imports import (
26 BytesIO, etree, HelperTestCase,
27 ElementTree, cElementTree, ET_VERSION, CET_VERSION,
28 filter_by_version, fileInTestDir, canonicalize, tmpfile,
29 _str, _bytes, unicode, next, IS_PYTHON2
32 if cElementTree is not None and (CET_VERSION <= (1,0,7) or sys.version_info[0] >= 3):
35 if ElementTree is not None:
36 print("Comparing with ElementTree %s" % getattr(ElementTree, "VERSION", "?"))
38 if cElementTree is not None:
39 print("Comparing with cElementTree %s" % getattr(cElementTree, "VERSION", "?"))
42 def et_needs_pyversion(*version):
45 def testfunc(self, *args):
46 if self.etree is not etree and sys.version_info < version:
47 raise unittest.SkipTest("requires ET in Python %s" % '.'.join(map(str, version)))
48 return method(self, *args)
53 class _ETreeTestCaseBase(HelperTestCase):
55 required_versions_ET = {}
56 required_versions_cET = {}
58 def XMLParser(self, **kwargs):
60 XMLParser = self.etree.XMLParser
61 except AttributeError:
62 assert 'ElementTree' in self.etree.__name__
63 XMLParser = self.etree.TreeBuilder
64 return XMLParser(**kwargs)
67 HelperTestCase.assertRegex
68 except AttributeError:
69 def assertRegex(self, *args, **kwargs):
70 return self.assertRegexpMatches(*args, **kwargs)
72 @et_needs_pyversion(3, 6)
73 def test_interface(self):
74 # Test element tree interface.
76 def check_string(string):
79 self.assertEqual(len(char), 1,
80 msg="expected one-character string, got %r" % char)
81 new_string = string + ""
82 new_string = string + " "
85 def check_mapping(mapping):
88 items = mapping.items()
91 mapping["key"] = "value"
92 self.assertEqual(mapping["key"], "value",
93 msg="expected value string, got %r" % mapping["key"])
95 def check_element(element):
96 self.assertTrue(self.etree.iselement(element), msg="not an element")
97 direlem = dir(element)
98 for attr in 'tag', 'attrib', 'text', 'tail':
99 self.assertTrue(hasattr(element, attr),
100 msg='no %s member' % attr)
101 self.assertIn(attr, direlem,
102 msg='no %s visible by dir' % attr)
104 check_string(element.tag)
105 check_mapping(element.attrib)
106 if element.text is not None:
107 check_string(element.text)
108 if element.tail is not None:
109 check_string(element.tail)
113 element = self.etree.Element("tag")
114 check_element(element)
115 tree = self.etree.ElementTree(element)
116 check_element(tree.getroot())
117 element = self.etree.Element(u"t\xe4g", key="value")
118 tree = self.etree.ElementTree(element)
119 # lxml and ET Py2: slightly different repr()
120 #self.assertRegex(repr(element), r"^<Element 't\xe4g' at 0x.*>$")
121 element = self.etree.Element("tag", key="value")
123 # Make sure all standard element methods exist.
125 def check_method(method):
126 self.assertTrue(hasattr(method, '__call__'),
127 msg="%s not callable" % method)
129 check_method(element.append)
130 check_method(element.extend)
131 check_method(element.insert)
132 check_method(element.remove)
134 #check_method(element.getchildren)
135 check_method(element.find)
136 check_method(element.iterfind)
137 check_method(element.findall)
138 check_method(element.findtext)
139 check_method(element.clear)
140 check_method(element.get)
141 check_method(element.set)
142 check_method(element.keys)
143 check_method(element.items)
144 check_method(element.iter)
145 check_method(element.itertext)
147 #check_method(element.getiterator)
149 # These methods return an iterable. See bug 6472.
152 check_method(it.next if IS_PYTHON2 else it.__next__)
154 check_iter(element.iterfind("tag"))
155 check_iter(element.iterfind("*"))
156 check_iter(tree.iterfind("tag"))
157 check_iter(tree.iterfind("*"))
159 # These aliases are provided:
161 # not an alias in lxml
162 #self.assertEqual(self.etree.XML, self.etree.fromstring)
163 self.assertEqual(self.etree.PI, self.etree.ProcessingInstruction)
165 def test_element(self):
167 e = self.etree.Element('foo')
168 self.assertEqual(e.tag, 'foo')
169 self.assertEqual(e.text, None)
170 self.assertEqual(e.tail, None)
172 def test_simple(self):
173 Element = self.etree.Element
175 root = Element('root')
176 root.append(Element('one'))
177 root.append(Element('two'))
178 root.append(Element('three'))
179 self.assertEqual(3, len(root))
180 self.assertEqual('one', root[0].tag)
181 self.assertEqual('two', root[1].tag)
182 self.assertEqual('three', root[2].tag)
183 self.assertRaises(IndexError, operator.getitem, root, 3)
185 # test weird dictionary interaction leading to segfault previously
186 def test_weird_dict_interaction(self):
187 root = self.etree.Element('root')
188 self.assertEqual(root.tag, "root")
189 add = self.etree.ElementTree(file=BytesIO('<foo>Foo</foo>'))
190 self.assertEqual(add.getroot().tag, "foo")
191 self.assertEqual(add.getroot().text, "Foo")
192 root.append(self.etree.Element('baz'))
193 self.assertEqual(root.tag, "root")
194 self.assertEqual(root[0].tag, "baz")
196 def test_subelement(self):
197 Element = self.etree.Element
198 SubElement = self.etree.SubElement
200 root = Element('root')
201 SubElement(root, 'one')
202 SubElement(root, 'two')
203 SubElement(root, 'three')
204 self.assertEqual(3, len(root))
205 self.assertEqual('one', root[0].tag)
206 self.assertEqual('two', root[1].tag)
207 self.assertEqual('three', root[2].tag)
209 def test_element_contains(self):
210 Element = self.etree.Element
211 SubElement = self.etree.SubElement
213 root1 = Element('root')
214 SubElement(root1, 'one')
215 self.assertTrue(root1[0] in root1)
217 root2 = Element('root')
218 SubElement(root2, 'two')
219 SubElement(root2, 'three')
220 self.assertTrue(root2[0] in root2)
221 self.assertTrue(root2[1] in root2)
223 self.assertFalse(root1[0] in root2)
224 self.assertFalse(root2[0] in root1)
225 self.assertFalse(None in root2)
227 def test_element_indexing_with_text(self):
228 ElementTree = self.etree.ElementTree
230 f = BytesIO('<doc>Test<one>One</one></doc>')
231 doc = ElementTree(file=f)
233 self.assertEqual(1, len(root))
234 self.assertEqual('one', root[0].tag)
235 self.assertRaises(IndexError, operator.getitem, root, 1)
237 def test_element_indexing_with_text2(self):
238 ElementTree = self.etree.ElementTree
240 f = BytesIO('<doc><one>One</one><two>Two</two>hm<three>Three</three></doc>')
241 doc = ElementTree(file=f)
243 self.assertEqual(3, len(root))
244 self.assertEqual('one', root[0].tag)
245 self.assertEqual('two', root[1].tag)
246 self.assertEqual('three', root[2].tag)
248 def test_element_indexing_only_text(self):
249 ElementTree = self.etree.ElementTree
251 f = BytesIO('<doc>Test</doc>')
252 doc = ElementTree(file=f)
254 self.assertEqual(0, len(root))
256 def test_element_indexing_negative(self):
257 Element = self.etree.Element
258 SubElement = self.etree.SubElement
260 b = SubElement(a, 'b')
261 c = SubElement(a, 'c')
262 d = SubElement(a, 'd')
263 self.assertEqual(d, a[-1])
264 self.assertEqual(c, a[-2])
265 self.assertEqual(b, a[-3])
266 self.assertRaises(IndexError, operator.getitem, a, -4)
267 a[-1] = e = Element('e')
268 self.assertEqual(e, a[-1])
270 self.assertEqual(2, len(a))
272 def test_elementtree(self):
273 ElementTree = self.etree.ElementTree
275 f = BytesIO('<doc><one>One</one><two>Two</two></doc>')
276 doc = ElementTree(file=f)
278 self.assertEqual(2, len(root))
279 self.assertEqual('one', root[0].tag)
280 self.assertEqual('two', root[1].tag)
283 ElementTree = self.etree.ElementTree
285 f = BytesIO('<doc>This is a text</doc>')
286 doc = ElementTree(file=f)
288 self.assertEqual('This is a text', root.text)
290 def test_text_empty(self):
291 ElementTree = self.etree.ElementTree
293 f = BytesIO('<doc></doc>')
294 doc = ElementTree(file=f)
296 self.assertEqual(None, root.text)
298 def test_text_other(self):
299 ElementTree = self.etree.ElementTree
301 f = BytesIO('<doc><one>One</one></doc>')
302 doc = ElementTree(file=f)
304 self.assertEqual(None, root.text)
305 self.assertEqual('One', root[0].text)
307 def test_text_escape_in(self):
308 ElementTree = self.etree.ElementTree
310 f = BytesIO('<doc>This is > than a text</doc>')
311 doc = ElementTree(file=f)
313 self.assertEqual('This is > than a text', root.text)
315 def test_text_escape_out(self):
316 Element = self.etree.Element
320 self.assertXML(_bytes('<a><>&</a>'),
323 def test_text_escape_tostring(self):
324 tostring = self.etree.tostring
325 Element = self.etree.Element
329 self.assertEqual(_bytes('<a><>&</a>'),
332 def test_text_str_subclass(self):
333 Element = self.etree.Element
339 a.text = strTest("text")
340 self.assertXML(_bytes('<a>text</a>'),
344 ElementTree = self.etree.ElementTree
346 f = BytesIO('<doc>This is <i>mixed</i> content.</doc>')
347 doc = ElementTree(file=f)
349 self.assertEqual(1, len(root))
350 self.assertEqual('This is ', root.text)
351 self.assertEqual(None, root.tail)
352 self.assertEqual('mixed', root[0].text)
353 self.assertEqual(' content.', root[0].tail)
355 def test_tail_str_subclass(self):
356 Element = self.etree.Element
357 SubElement = self.etree.SubElement
363 SubElement(a, "t").tail = strTest("tail")
364 self.assertXML(_bytes('<a><t></t>tail</a>'),
367 def _test_del_tail(self):
368 # this is discouraged for ET compat, should not be tested...
371 root = XML(_bytes('<doc>This is <i>mixed</i> content.</doc>'))
372 self.assertEqual(1, len(root))
373 self.assertEqual('This is ', root.text)
374 self.assertEqual(None, root.tail)
375 self.assertEqual('mixed', root[0].text)
376 self.assertEqual(' content.', root[0].tail)
380 self.assertEqual(1, len(root))
381 self.assertEqual('This is ', root.text)
382 self.assertEqual(None, root.tail)
383 self.assertEqual('mixed', root[0].text)
384 self.assertEqual(None, root[0].tail)
386 root[0].tail = "TAIL"
388 self.assertEqual(1, len(root))
389 self.assertEqual('This is ', root.text)
390 self.assertEqual(None, root.tail)
391 self.assertEqual('mixed', root[0].text)
392 self.assertEqual('TAIL', root[0].tail)
394 def test_ElementTree(self):
395 Element = self.etree.Element
396 ElementTree = self.etree.ElementTree
399 doc = ElementTree(el)
401 self.assertEqual(None, root.text)
402 self.assertEqual('hoi', root.tag)
404 def test_attrib(self):
405 ElementTree = self.etree.ElementTree
407 f = BytesIO('<doc one="One" two="Two"/>')
408 doc = ElementTree(file=f)
410 self.assertEqual('One', root.attrib['one'])
411 self.assertEqual('Two', root.attrib['two'])
412 self.assertRaises(KeyError, operator.getitem, root.attrib, 'three')
414 def test_attrib_get(self):
415 ElementTree = self.etree.ElementTree
417 f = BytesIO('<doc one="One" two="Two"/>')
418 doc = ElementTree(file=f)
420 self.assertEqual('One', root.attrib.get('one'))
421 self.assertEqual('Two', root.attrib.get('two'))
422 self.assertEqual(None, root.attrib.get('three'))
423 self.assertEqual('foo', root.attrib.get('three', 'foo'))
425 def test_attrib_dict(self):
426 ElementTree = self.etree.ElementTree
428 f = BytesIO('<doc one="One" two="Two"/>')
429 doc = ElementTree(file=f)
431 attrib = dict(root.attrib)
432 self.assertEqual('One', attrib['one'])
433 self.assertEqual('Two', attrib['two'])
434 self.assertRaises(KeyError, operator.getitem, attrib, 'three')
436 def test_attrib_copy(self):
437 ElementTree = self.etree.ElementTree
439 f = BytesIO('<doc one="One" two="Two"/>')
440 doc = ElementTree(file=f)
442 attrib = copy.copy(root.attrib)
443 self.assertEqual('One', attrib['one'])
444 self.assertEqual('Two', attrib['two'])
445 self.assertRaises(KeyError, operator.getitem, attrib, 'three')
447 def test_attrib_deepcopy(self):
448 ElementTree = self.etree.ElementTree
450 f = BytesIO('<doc one="One" two="Two"/>')
451 doc = ElementTree(file=f)
453 attrib = copy.deepcopy(root.attrib)
454 self.assertEqual('One', attrib['one'])
455 self.assertEqual('Two', attrib['two'])
456 self.assertRaises(KeyError, operator.getitem, attrib, 'three')
458 def test_attributes_get(self):
459 ElementTree = self.etree.ElementTree
461 f = BytesIO('<doc one="One" two="Two"/>')
462 doc = ElementTree(file=f)
464 self.assertEqual('One', root.get('one'))
465 self.assertEqual('Two', root.get('two'))
466 self.assertEqual(None, root.get('three'))
467 self.assertEqual('foo', root.get('three', 'foo'))
469 def test_attrib_clear(self):
472 root = XML(_bytes('<doc one="One" two="Two"/>'))
473 self.assertEqual('One', root.get('one'))
474 self.assertEqual('Two', root.get('two'))
476 self.assertEqual(None, root.get('one'))
477 self.assertEqual(None, root.get('two'))
479 def test_attrib_set_clear(self):
480 Element = self.etree.Element
482 root = Element("root", one="One")
483 root.set("two", "Two")
484 self.assertEqual('One', root.get('one'))
485 self.assertEqual('Two', root.get('two'))
487 self.assertEqual(None, root.get('one'))
488 self.assertEqual(None, root.get('two'))
490 def test_attrib_ns_clear(self):
491 Element = self.etree.Element
492 SubElement = self.etree.SubElement
494 attribNS = '{http://foo/bar}x'
496 parent = Element('parent')
497 parent.set(attribNS, 'a')
498 child = SubElement(parent, 'child')
499 child.set(attribNS, 'b')
501 self.assertEqual('a', parent.get(attribNS))
502 self.assertEqual('b', child.get(attribNS))
505 self.assertEqual(None, parent.get(attribNS))
506 self.assertEqual('b', child.get(attribNS))
508 def test_attrib_pop(self):
509 ElementTree = self.etree.ElementTree
511 f = BytesIO('<doc one="One" two="Two"/>')
512 doc = ElementTree(file=f)
514 self.assertEqual('One', root.attrib['one'])
515 self.assertEqual('Two', root.attrib['two'])
517 self.assertEqual('One', root.attrib.pop('one'))
519 self.assertEqual(None, root.attrib.get('one'))
520 self.assertEqual('Two', root.attrib['two'])
522 def test_attrib_pop_unknown(self):
523 root = self.etree.XML(_bytes('<doc one="One" two="Two"/>'))
524 self.assertRaises(KeyError, root.attrib.pop, 'NONE')
526 self.assertEqual('One', root.attrib['one'])
527 self.assertEqual('Two', root.attrib['two'])
529 def test_attrib_pop_default(self):
530 root = self.etree.XML(_bytes('<doc one="One" two="Two"/>'))
531 self.assertEqual('Three', root.attrib.pop('three', 'Three'))
533 def test_attrib_pop_empty_default(self):
534 root = self.etree.XML(_bytes('<doc/>'))
535 self.assertEqual('Three', root.attrib.pop('three', 'Three'))
537 def test_attrib_pop_invalid_args(self):
538 root = self.etree.XML(_bytes('<doc one="One" two="Two"/>'))
539 self.assertRaises(TypeError, root.attrib.pop, 'One', None, None)
541 def test_attribute_update_dict(self):
544 root = XML(_bytes('<doc alpha="Alpha" beta="Beta"/>'))
545 items = list(root.attrib.items())
548 [('alpha', 'Alpha'), ('beta', 'Beta')],
551 root.attrib.update({'alpha' : 'test', 'gamma' : 'Gamma'})
553 items = list(root.attrib.items())
556 [('alpha', 'test'), ('beta', 'Beta'), ('gamma', 'Gamma')],
559 def test_attribute_update_sequence(self):
562 root = XML(_bytes('<doc alpha="Alpha" beta="Beta"/>'))
563 items = list(root.attrib.items())
566 [('alpha', 'Alpha'), ('beta', 'Beta')],
569 root.attrib.update({'alpha' : 'test', 'gamma' : 'Gamma'}.items())
571 items = list(root.attrib.items())
574 [('alpha', 'test'), ('beta', 'Beta'), ('gamma', 'Gamma')],
577 def test_attribute_update_iter(self):
580 root = XML(_bytes('<doc alpha="Alpha" beta="Beta"/>'))
581 items = list(root.attrib.items())
584 [('alpha', 'Alpha'), ('beta', 'Beta')],
587 root.attrib.update(iter({'alpha' : 'test', 'gamma' : 'Gamma'}.items()))
589 items = list(root.attrib.items())
592 [('alpha', 'test'), ('beta', 'Beta'), ('gamma', 'Gamma')],
595 def test_attribute_update_attrib(self):
598 root = XML(_bytes('<doc alpha="Alpha" beta="Beta"/>'))
599 items = list(root.attrib.items())
602 [('alpha', 'Alpha'), ('beta', 'Beta')],
605 other = XML(_bytes('<doc alpha="test" gamma="Gamma"/>'))
606 root.attrib.update(other.attrib)
608 items = list(root.attrib.items())
611 [('alpha', 'test'), ('beta', 'Beta'), ('gamma', 'Gamma')],
614 def test_attribute_keys(self):
617 root = XML(_bytes('<doc alpha="Alpha" beta="Beta" gamma="Gamma"/>'))
618 keys = list(root.attrib.keys())
620 self.assertEqual(['alpha', 'beta', 'gamma'], keys)
622 def test_attribute_keys2(self):
625 root = XML(_bytes('<doc alpha="Alpha" beta="Beta" gamma="Gamma"/>'))
626 keys = list(root.keys())
628 self.assertEqual(['alpha', 'beta', 'gamma'], keys)
630 def test_attribute_items2(self):
633 root = XML(_bytes('<doc alpha="Alpha" beta="Beta" gamma="Gamma"/>'))
634 items = list(root.items())
637 [('alpha','Alpha'), ('beta','Beta'), ('gamma','Gamma')],
640 def test_attribute_keys_ns(self):
643 root = XML(_bytes('<foo bar="Bar" xmlns:ns="http://ns.codespeak.net/test" ns:baz="Baz" />'))
644 keys = list(root.keys())
646 self.assertEqual(['bar', '{http://ns.codespeak.net/test}baz'],
649 def test_attribute_values(self):
652 root = XML(_bytes('<doc alpha="Alpha" beta="Beta" gamma="Gamma"/>'))
653 values = list(root.attrib.values())
655 self.assertEqual(['Alpha', 'Beta', 'Gamma'], values)
657 def test_attribute_values_ns(self):
660 root = XML(_bytes('<foo bar="Bar" xmlns:ns="http://ns.codespeak.net/test" ns:baz="Baz" />'))
661 values = list(root.attrib.values())
664 ['Bar', 'Baz'], values)
666 def test_attribute_items(self):
669 root = XML(_bytes('<doc alpha="Alpha" beta="Beta" gamma="Gamma"/>'))
670 items = list(root.attrib.items())
679 def test_attribute_items_ns(self):
682 root = XML(_bytes('<foo bar="Bar" xmlns:ns="http://ns.codespeak.net/test" ns:baz="Baz" />'))
683 items = list(root.attrib.items())
686 [('bar', 'Bar'), ('{http://ns.codespeak.net/test}baz', 'Baz')],
689 def test_attribute_str(self):
692 expected = "{'{http://ns.codespeak.net/test}baz': 'Baz', 'bar': 'Bar'}"
693 alternative = "{'bar': 'Bar', '{http://ns.codespeak.net/test}baz': 'Baz'}"
695 root = XML(_bytes('<foo bar="Bar" xmlns:ns="http://ns.codespeak.net/test" ns:baz="Baz" />'))
697 self.assertEqual(expected, str(root.attrib))
698 except AssertionError:
699 self.assertEqual(alternative, str(root.attrib))
701 def test_attribute_contains(self):
704 root = XML(_bytes('<foo bar="Bar" xmlns:ns="http://ns.codespeak.net/test" ns:baz="Baz" />'))
706 True, 'bar' in root.attrib)
708 False, 'baz' in root.attrib)
710 False, 'hah' in root.attrib)
713 '{http://ns.codespeak.net/test}baz' in root.attrib)
715 def test_attribute_set(self):
716 Element = self.etree.Element
718 root = Element("root")
719 root.set("attr", "TEST")
720 self.assertEqual("TEST", root.get("attr"))
722 def test_attrib_as_attrib(self):
723 Element = self.etree.Element
725 root = Element("root")
726 root.set("attr", "TEST")
727 self.assertEqual("TEST", root.attrib["attr"])
729 root2 = Element("root2", root.attrib)
730 self.assertEqual("TEST", root2.attrib["attr"])
732 def test_attribute_iterator(self):
735 root = XML(_bytes('<doc alpha="Alpha" beta="Beta" gamma="Gamma" />'))
737 for key in root.attrib:
740 self.assertEqual(['alpha', 'beta', 'gamma'], result)
742 def test_attribute_manipulation(self):
743 Element = self.etree.Element
746 a.attrib['foo'] = 'Foo'
747 a.attrib['bar'] = 'Bar'
748 self.assertEqual('Foo', a.attrib['foo'])
750 self.assertRaises(KeyError, operator.getitem, a.attrib, 'foo')
752 def test_del_attribute_ns(self):
753 Element = self.etree.Element
756 a.attrib['{http://a/}foo'] = 'Foo'
757 a.attrib['{http://a/}bar'] = 'Bar'
758 self.assertEqual(None, a.get('foo'))
759 self.assertEqual('Foo', a.get('{http://a/}foo'))
760 self.assertEqual('Foo', a.attrib['{http://a/}foo'])
762 self.assertRaises(KeyError, operator.delitem, a.attrib, 'foo')
763 self.assertEqual('Foo', a.attrib['{http://a/}foo'])
765 del a.attrib['{http://a/}foo']
766 self.assertRaises(KeyError, operator.getitem, a.attrib, 'foo')
768 def test_del_attribute_ns_parsed(self):
771 a = XML(_bytes('<a xmlns:nsa="http://a/" nsa:foo="FooNS" foo="Foo" />'))
773 self.assertEqual('Foo', a.attrib['foo'])
774 self.assertEqual('FooNS', a.attrib['{http://a/}foo'])
777 self.assertEqual('FooNS', a.attrib['{http://a/}foo'])
778 self.assertRaises(KeyError, operator.getitem, a.attrib, 'foo')
779 self.assertRaises(KeyError, operator.delitem, a.attrib, 'foo')
781 del a.attrib['{http://a/}foo']
782 self.assertRaises(KeyError, operator.getitem, a.attrib, '{http://a/}foo')
783 self.assertRaises(KeyError, operator.getitem, a.attrib, 'foo')
785 a = XML(_bytes('<a xmlns:nsa="http://a/" foo="Foo" nsa:foo="FooNS" />'))
787 self.assertEqual('Foo', a.attrib['foo'])
788 self.assertEqual('FooNS', a.attrib['{http://a/}foo'])
791 self.assertEqual('FooNS', a.attrib['{http://a/}foo'])
792 self.assertRaises(KeyError, operator.getitem, a.attrib, 'foo')
794 del a.attrib['{http://a/}foo']
795 self.assertRaises(KeyError, operator.getitem, a.attrib, '{http://a/}foo')
796 self.assertRaises(KeyError, operator.getitem, a.attrib, 'foo')
801 root = XML(_bytes('<doc>This is a text.</doc>'))
802 self.assertEqual(0, len(root))
803 self.assertEqual('This is a text.', root.text)
805 def test_XMLID(self):
806 XMLID = self.etree.XMLID
808 xml_text = _bytes('''
810 <h1 id="chapter1">...</h1>
811 <p id="note1" class="note">...</p>
812 <p>Regular paragraph.</p>
813 <p xml:id="xmlid">XML:ID paragraph.</p>
814 <p id="warn1" class="warning">...</p>
818 root, dic = XMLID(xml_text)
819 root2 = XML(xml_text)
820 self.assertEqual(self._writeElement(root),
821 self._writeElement(root2))
823 "chapter1" : root[0],
827 self.assertEqual(dic, expected)
829 def test_fromstring(self):
830 fromstring = self.etree.fromstring
832 root = fromstring('<doc>This is a text.</doc>')
833 self.assertEqual(0, len(root))
834 self.assertEqual('This is a text.', root.text)
836 required_versions_ET['test_fromstringlist'] = (1,3)
837 def test_fromstringlist(self):
838 fromstringlist = self.etree.fromstringlist
840 root = fromstringlist(["<do", "c>T", "hi", "s is",
841 " a text.<", "/doc", ">"])
842 self.assertEqual(0, len(root))
843 self.assertEqual('This is a text.', root.text)
845 required_versions_ET['test_fromstringlist_characters'] = (1,3)
846 def test_fromstringlist_characters(self):
847 fromstringlist = self.etree.fromstringlist
849 root = fromstringlist(list('<doc>This is a text.</doc>'))
850 self.assertEqual(0, len(root))
851 self.assertEqual('This is a text.', root.text)
853 required_versions_ET['test_fromstringlist_single'] = (1,3)
854 def test_fromstringlist_single(self):
855 fromstringlist = self.etree.fromstringlist
857 root = fromstringlist(['<doc>This is a text.</doc>'])
858 self.assertEqual(0, len(root))
859 self.assertEqual('This is a text.', root.text)
861 def test_iselement(self):
862 iselement = self.etree.iselement
863 Element = self.etree.Element
864 ElementTree = self.etree.ElementTree
866 Comment = self.etree.Comment
867 ProcessingInstruction = self.etree.ProcessingInstruction
870 self.assertTrue(iselement(el))
872 el2 = XML(_bytes('<foo/>'))
873 self.assertTrue(iselement(el2))
875 tree = ElementTree(element=Element('dag'))
876 self.assertTrue(not iselement(tree))
877 self.assertTrue(iselement(tree.getroot()))
880 self.assertTrue(iselement(c))
882 p = ProcessingInstruction("test", "some text")
883 self.assertTrue(iselement(p))
885 def test_iteration(self):
888 root = XML(_bytes('<doc><one/><two>Two</two>Hm<three/></doc>'))
891 result.append(el.tag)
892 self.assertEqual(['one', 'two', 'three'], result)
894 def test_iteration_empty(self):
897 root = XML(_bytes('<doc></doc>'))
900 result.append(el.tag)
901 self.assertEqual([], result)
903 def test_iteration_text_only(self):
906 root = XML(_bytes('<doc>Text</doc>'))
909 result.append(el.tag)
910 self.assertEqual([], result)
912 def test_iteration_set_tail_empty(self):
913 # this would cause a crash in the past
914 fromstring = self.etree.fromstring
915 root = fromstring('<html><p></p>x</html>')
919 def test_iteration_clear_tail(self):
920 # this would cause a crash in the past
921 fromstring = self.etree.fromstring
922 root = fromstring('<html><p></p>x</html>')
926 def test_iteration_reversed(self):
928 root = XML(_bytes('<doc><one/><two>Two</two>Hm<three/></doc>'))
930 for el in reversed(root):
931 result.append(el.tag)
932 self.assertEqual(['three', 'two', 'one'], result)
934 def test_iteration_subelement(self):
937 root = XML(_bytes('<doc><one/><two>Two</two>Hm<three/></doc>'))
941 result.append(el.tag)
943 self.etree.SubElement(root, 'four')
945 self.assertEqual(['one', 'two', 'three', 'four'], result)
947 def test_iteration_del_child(self):
950 root = XML(_bytes('<doc><one/><two>Two</two>Hm<three/></doc>'))
953 result.append(el.tag)
955 self.assertEqual(['one', 'two'], result)
957 def test_iteration_double(self):
960 root = XML(_bytes('<doc><one/><two/></doc>'))
963 result.append(el0.tag)
965 result.append(el1.tag)
966 self.assertEqual(['one','one', 'two', 'two', 'one', 'two'], result)
968 required_versions_ET['test_itertext'] = (1,3)
969 def test_itertext(self):
972 root = XML(_bytes("<root>RTEXT<a></a>ATAIL<b/><c>CTEXT</c>CTAIL</root>"))
974 text = list(root.itertext())
975 self.assertEqual(["RTEXT", "ATAIL", "CTEXT", "CTAIL"],
978 required_versions_ET['test_itertext_child'] = (1,3)
979 def test_itertext_child(self):
982 root = XML(_bytes("<root>RTEXT<a></a>ATAIL<b/><c>CTEXT</c>CTAIL</root>"))
984 text = list(root[2].itertext())
985 self.assertEqual(["CTEXT"],
988 def test_findall(self):
990 root = XML(_bytes('<a><b><c/></b><b/><c><b/></c></a>'))
991 self.assertEqual(len(list(root.findall("c"))), 1)
992 self.assertEqual(len(list(root.findall(".//c"))), 2)
993 self.assertEqual(len(list(root.findall(".//b"))), 3)
994 self.assertEqual(len(list(root.findall(".//b"))[0]), 1)
995 self.assertEqual(len(list(root.findall(".//b"))[1]), 0)
996 self.assertEqual(len(list(root.findall(".//b"))[2]), 0)
998 def test_findall_ns(self):
1000 root = XML(_bytes('<a xmlns:x="X" xmlns:y="Y"><x:b><c/></x:b><b/><c><x:b/><b/></c><b/></a>'))
1001 self.assertEqual(len(list(root.findall(".//{X}b"))), 2)
1002 self.assertEqual(len(list(root.findall(".//b"))), 3)
1003 self.assertEqual(len(list(root.findall("b"))), 2)
1005 @et_needs_pyversion(3, 8, 0, 'alpha', 4)
1006 def test_findall_wildcard(self):
1007 def summarize_list(l):
1008 return [el.tag for el in l]
1010 root = self.etree.XML('''
1011 <a xmlns:x="X" xmlns:y="Y">
1014 <c><x:b/><b/></c><y:b/>
1016 root.append(self.etree.Comment('test'))
1018 self.assertEqual(summarize_list(root.findall("{*}b")),
1019 ['{X}b', 'b', '{Y}b'])
1020 self.assertEqual(summarize_list(root.findall("{*}c")),
1022 self.assertEqual(summarize_list(root.findall("{X}*")),
1024 self.assertEqual(summarize_list(root.findall("{Y}*")),
1026 self.assertEqual(summarize_list(root.findall("{}*")),
1028 self.assertEqual(summarize_list(root.findall("{}b")), # only for consistency
1030 self.assertEqual(summarize_list(root.findall("{}b")),
1031 summarize_list(root.findall("b")))
1032 self.assertEqual(summarize_list(root.findall("{*}*")),
1033 ['{X}b', 'b', 'c', '{Y}b'])
1034 self.assertEqual(summarize_list(root.findall("{*}*")
1035 + ([] if self.etree is etree else [root[-1]])),
1036 summarize_list(root.findall("*")))
1038 self.assertEqual(summarize_list(root.findall(".//{*}b")),
1039 ['{X}b', 'b', '{X}b', 'b', '{Y}b'])
1040 self.assertEqual(summarize_list(root.findall(".//{*}c")),
1042 self.assertEqual(summarize_list(root.findall(".//{X}*")),
1044 self.assertEqual(summarize_list(root.findall(".//{Y}*")),
1046 self.assertEqual(summarize_list(root.findall(".//{}*")),
1047 ['c', 'b', 'c', 'b'])
1048 self.assertEqual(summarize_list(root.findall(".//{}b")),
1051 def test_element_with_attributes_keywords(self):
1052 Element = self.etree.Element
1054 el = Element('tag', foo='Foo', bar='Bar')
1055 self.assertEqual('Foo', el.attrib['foo'])
1056 self.assertEqual('Bar', el.attrib['bar'])
1058 def test_element_with_attributes(self):
1059 Element = self.etree.Element
1061 el = Element('tag', {'foo': 'Foo', 'bar': 'Bar'})
1062 self.assertEqual('Foo', el.attrib['foo'])
1063 self.assertEqual('Bar', el.attrib['bar'])
1065 def test_element_with_attributes_extra(self):
1066 Element = self.etree.Element
1068 el = Element('tag', {'foo': 'Foo', 'bar': 'Bar'}, baz='Baz')
1069 self.assertEqual('Foo', el.attrib['foo'])
1070 self.assertEqual('Bar', el.attrib['bar'])
1071 self.assertEqual('Baz', el.attrib['baz'])
1073 def test_element_with_attributes_extra_duplicate(self):
1074 Element = self.etree.Element
1076 el = Element('tag', {'foo': 'Foo', 'bar': 'Bar'}, bar='Baz')
1077 self.assertEqual('Foo', el.attrib['foo'])
1078 self.assertEqual('Baz', el.attrib['bar'])
1080 def test_element_with_attributes_ns(self):
1081 Element = self.etree.Element
1083 el = Element('tag', {'{ns1}foo':'Foo', '{ns2}bar':'Bar'})
1084 self.assertEqual('Foo', el.attrib['{ns1}foo'])
1085 self.assertEqual('Bar', el.attrib['{ns2}bar'])
1087 def test_subelement_with_attributes(self):
1088 Element = self.etree.Element
1089 SubElement = self.etree.SubElement
1092 SubElement(el, 'foo', {'foo':'Foo'}, baz="Baz")
1093 self.assertEqual("Baz", el[0].attrib['baz'])
1094 self.assertEqual('Foo', el[0].attrib['foo'])
1096 def test_subelement_with_attributes_ns(self):
1097 Element = self.etree.Element
1098 SubElement = self.etree.SubElement
1101 SubElement(el, 'foo', {'{ns1}foo':'Foo', '{ns2}bar':'Bar'})
1102 self.assertEqual('Foo', el[0].attrib['{ns1}foo'])
1103 self.assertEqual('Bar', el[0].attrib['{ns2}bar'])
1105 def test_write(self):
1106 ElementTree = self.etree.ElementTree
1107 XML = self.etree.XML
1111 root = XML(_bytes('<doc%s>This is a test.</doc%s>' % (i, i)))
1112 tree = ElementTree(element=root)
1116 _bytes('<doc%s>This is a test.</doc%s>' % (i, i)),
1119 required_versions_ET['test_write_method_html'] = (1,3)
1120 def test_write_method_html(self):
1121 ElementTree = self.etree.ElementTree
1122 Element = self.etree.Element
1123 SubElement = self.etree.SubElement
1125 html = Element('html')
1126 body = SubElement(html, 'body')
1127 p = SubElement(body, 'p')
1129 SubElement(p, 'br').tail = "test"
1131 tree = ElementTree(element=html)
1133 tree.write(f, method="html")
1134 data = f.getvalue().replace(_bytes('\n'),_bytes(''))
1136 self.assertEqual(_bytes('<html><body><p>html<br>test</p></body></html>'),
1139 required_versions_ET['test_write_method_text'] = (1,3)
1140 def test_write_method_text(self):
1141 ElementTree = self.etree.ElementTree
1142 Element = self.etree.Element
1143 SubElement = self.etree.SubElement
1148 b = SubElement(a, 'b')
1151 c = SubElement(a, 'c')
1154 tree = ElementTree(element=a)
1156 tree.write(f, method="text")
1159 self.assertEqual(_bytes('ABTAILCtail'),
1162 def test_write_fail(self):
1163 ElementTree = self.etree.ElementTree
1164 XML = self.etree.XML
1166 tree = ElementTree( XML(_bytes('<doc>This is a test.</doc>')) )
1167 self.assertRaises(IOError, tree.write,
1168 "definitely////\\-\\nonexisting\\-\\////FILE")
1170 # this could trigger a crash, apparently because the document
1171 # reference was prematurely garbage collected
1172 def test_crash(self):
1173 Element = self.etree.Element
1175 element = Element('tag')
1177 element.attrib['key'] = 'value'
1178 value = element.attrib['key']
1179 self.assertEqual(value, 'value')
1181 # from doctest; for some reason this caused crashes too
1182 def test_write_ElementTreeDoctest(self):
1183 Element = self.etree.Element
1184 ElementTree = self.etree.ElementTree
1188 element = Element('tag%s' % i)
1189 self._check_element(element)
1190 tree = ElementTree(element)
1192 self._check_element_tree(tree)
1194 def test_subelement_reference(self):
1195 Element = self.etree.Element
1196 SubElement = self.etree.SubElement
1199 el2 = SubElement(el, 'bar')
1200 el3 = SubElement(el2, 'baz')
1202 al = Element('foo2')
1203 al2 = SubElement(al, 'bar2')
1204 al3 = SubElement(al2, 'baz2')
1206 # now move al2 into el
1209 # now change al3 directly
1210 al3.text = 'baz2-modified'
1212 # it should have changed through this route too
1217 def test_set_text(self):
1218 Element = self.etree.Element
1219 SubElement = self.etree.SubElement
1222 b = SubElement(a, 'b')
1231 def test_set_text2(self):
1232 Element = self.etree.Element
1233 SubElement = self.etree.SubElement
1237 b = SubElement(a ,'b')
1245 def test_set_text_none(self):
1246 Element = self.etree.Element
1256 self.assertXML(_bytes('<a></a>'), a)
1258 def test_set_text_empty(self):
1259 Element = self.etree.Element
1262 self.assertEqual(None, a.text)
1265 self.assertEqual('', a.text)
1266 self.assertXML(_bytes('<a></a>'), a)
1268 def test_tail1(self):
1269 Element = self.etree.Element
1270 SubElement = self.etree.SubElement
1274 self.assertEqual('dag',
1276 b = SubElement(a, 'b')
1278 self.assertEqual('hoi',
1280 self.assertEqual('dag',
1283 def test_tail_append(self):
1284 Element = self.etree.Element
1290 self.assertEqual('b_tail',
1293 def test_tail_set_twice(self):
1294 Element = self.etree.Element
1295 SubElement = self.etree.SubElement
1298 b = SubElement(a, 'b')
1301 self.assertEqual('bar',
1303 self.assertXML(_bytes('<a><b></b>bar</a>'), a)
1305 def test_tail_set_none(self):
1306 Element = self.etree.Element
1313 self.assertXML(_bytes('<a></a>'), a)
1315 required_versions_ET['test_extend'] = (1,3)
1316 def test_extend(self):
1317 root = self.etree.Element('foo')
1319 element = self.etree.SubElement(root, 'a%s' % i)
1320 element.text = "text%d" % i
1321 element.tail = "tail%d" % i
1325 new_element = self.etree.Element("test%s" % i)
1326 new_element.text = "TEXT%s" % i
1327 new_element.tail = "TAIL%s" % i
1328 elements.append(new_element)
1330 root.extend(elements)
1333 ["a0", "a1", "a2", "test0", "test1", "test2"],
1334 [ el.tag for el in root ])
1336 ["text0", "text1", "text2", "TEXT0", "TEXT1", "TEXT2"],
1337 [ el.text for el in root ])
1339 ["tail0", "tail1", "tail2", "TAIL0", "TAIL1", "TAIL2"],
1340 [ el.tail for el in root ])
1342 def test_comment(self):
1343 Element = self.etree.Element
1344 SubElement = self.etree.SubElement
1345 Comment = self.etree.Comment
1348 a.append(Comment('foo'))
1349 self.assertEqual(a[0].tag, Comment)
1350 self.assertEqual(a[0].text, 'foo')
1352 # ElementTree < 1.3 adds whitespace around comments
1353 required_versions_ET['test_comment_text'] = (1,3)
1354 def test_comment_text(self):
1355 Element = self.etree.Element
1356 SubElement = self.etree.SubElement
1357 Comment = self.etree.Comment
1358 tostring = self.etree.tostring
1361 a.append(Comment('foo'))
1362 self.assertEqual(a[0].text, 'foo')
1365 _bytes('<a><!--foo--></a>'),
1369 self.assertEqual(a[0].text, 'TEST')
1372 _bytes('<a><!--TEST--></a>'),
1375 # ElementTree < 1.3 adds whitespace around comments
1376 required_versions_ET['test_comment_whitespace'] = (1,3)
1377 def test_comment_whitespace(self):
1378 Element = self.etree.Element
1379 SubElement = self.etree.SubElement
1380 Comment = self.etree.Comment
1381 tostring = self.etree.tostring
1384 a.append(Comment(' foo '))
1385 self.assertEqual(a[0].text, ' foo ')
1387 _bytes('<a><!-- foo --></a>'),
1390 def test_comment_nonsense(self):
1391 Comment = self.etree.Comment
1393 self.assertEqual({}, c.attrib)
1394 self.assertEqual([], list(c.keys()))
1395 self.assertEqual([], list(c.items()))
1396 self.assertEqual(None, c.get('hoi'))
1397 self.assertEqual(0, len(c))
1398 # should not iterate
1403 # lxml.etree separates target and text
1404 Element = self.etree.Element
1405 SubElement = self.etree.SubElement
1406 ProcessingInstruction = self.etree.ProcessingInstruction
1409 a.append(ProcessingInstruction('foo', 'some more text'))
1410 self.assertEqual(a[0].tag, ProcessingInstruction)
1411 self.assertXML(_bytes("<a><?foo some more text?></a>"),
1414 def test_processinginstruction(self):
1415 # lxml.etree separates target and text
1416 Element = self.etree.Element
1417 SubElement = self.etree.SubElement
1418 ProcessingInstruction = self.etree.PI
1421 a.append(ProcessingInstruction('foo', 'some more text'))
1422 self.assertEqual(a[0].tag, ProcessingInstruction)
1423 self.assertXML(_bytes("<a><?foo some more text?></a>"),
1426 def test_pi_nonsense(self):
1427 ProcessingInstruction = self.etree.ProcessingInstruction
1428 pi = ProcessingInstruction('foo')
1429 self.assertEqual({}, pi.attrib)
1430 self.assertEqual([], list(pi.keys()))
1431 self.assertEqual([], list(pi.items()))
1432 self.assertEqual(None, pi.get('hoi'))
1433 self.assertEqual(0, len(pi))
1434 # should not iterate
1438 def test_setitem(self):
1439 Element = self.etree.Element
1440 SubElement = self.etree.SubElement
1443 b = SubElement(a, 'b')
1449 self.assertXML(_bytes('<a><c></c></a>'),
1451 self.assertXML(_bytes('<b></b>'),
1454 def test_setitem2(self):
1455 Element = self.etree.Element
1456 SubElement = self.etree.SubElement
1460 b = SubElement(a, 'b%s' % i)
1461 c = SubElement(b, 'c')
1464 e = SubElement(d, 'e')
1467 _bytes('<a><d><e></e></d><d><e></e></d><d><e></e></d><d><e></e></d><d><e></e></d></a>'),
1469 self.assertXML(_bytes('<c></c>'),
1472 def test_setitem_replace(self):
1473 Element = self.etree.Element
1474 SubElement = self.etree.SubElement
1480 self.assertXML(_bytes('<a><d></d></a>'), a)
1482 def test_setitem_indexerror(self):
1483 Element = self.etree.Element
1484 SubElement = self.etree.SubElement
1487 b = SubElement(a, 'b')
1489 self.assertRaises(IndexError, operator.setitem, a, 1, Element('c'))
1491 def test_setitem_tail(self):
1492 Element = self.etree.Element
1493 SubElement = self.etree.SubElement
1496 b = SubElement(a, 'b')
1503 _bytes('<a><c></c>C2</a>'),
1506 def test_tag_write(self):
1507 Element = self.etree.Element
1508 SubElement = self.etree.SubElement
1511 b = SubElement(a, 'b')
1520 _bytes('<c><b></b></c>'),
1523 def test_tag_reset_ns(self):
1524 Element = self.etree.Element
1525 SubElement = self.etree.SubElement
1526 tostring = self.etree.tostring
1529 b1 = SubElement(a, '{a}b')
1530 b2 = SubElement(a, '{b}b')
1532 self.assertEqual('{a}b', b1.tag)
1536 # can't use C14N here!
1537 self.assertEqual('c', b1.tag)
1538 self.assertEqual(_bytes('<c'), tostring(b1)[:2])
1539 self.assertTrue(_bytes('<c') in tostring(a))
1541 def test_tag_reset_root_ns(self):
1542 Element = self.etree.Element
1543 SubElement = self.etree.SubElement
1544 tostring = self.etree.tostring
1547 b1 = SubElement(a, '{a}b')
1548 b2 = SubElement(a, '{b}b')
1556 # can't use C14N here!
1557 self.assertEqual('c', a.tag)
1558 self.assertEqual(_bytes('<c'), tostring(a)[:2])
1560 def test_tag_str_subclass(self):
1561 Element = self.etree.Element
1567 a.tag = strTest("TAG")
1568 self.assertXML(_bytes('<TAG></TAG>'),
1571 def test_delitem(self):
1572 Element = self.etree.Element
1573 SubElement = self.etree.SubElement
1576 b = SubElement(a, 'b')
1577 c = SubElement(a, 'c')
1578 d = SubElement(a, 'd')
1582 _bytes('<a><b></b><d></d></a>'),
1587 _bytes('<a><d></d></a>'),
1594 # move deleted element into other tree afterwards
1595 other = Element('other')
1598 _bytes('<other><c></c></other>'),
1601 def test_del_insert(self):
1602 Element = self.etree.Element
1603 SubElement = self.etree.SubElement
1606 b = SubElement(a, 'b')
1607 bs = SubElement(b, 'bs')
1608 c = SubElement(a, 'c')
1609 cs = SubElement(c, 'cs')
1613 _bytes('<a><b><bs></bs></b><c><cs></cs></c></a>'),
1615 self.assertXML(_bytes('<b><bs></bs></b>'), b)
1616 self.assertXML(_bytes('<c><cs></cs></c>'), c)
1620 _bytes('<a><c><cs></cs></c></a>'),
1622 self.assertXML(_bytes('<b><bs></bs></b>'), b)
1623 self.assertXML(_bytes('<c><cs></cs></c>'), c)
1627 _bytes('<a><b><bs></bs></b><c><cs></cs></c></a>'),
1629 self.assertXML(_bytes('<b><bs></bs></b>'), b)
1630 self.assertXML(_bytes('<c><cs></cs></c>'), c)
1632 def test_del_setitem(self):
1633 Element = self.etree.Element
1634 SubElement = self.etree.SubElement
1637 b = SubElement(a, 'b')
1638 bs = SubElement(b, 'bs')
1639 c = SubElement(a, 'c')
1640 cs = SubElement(c, 'cs')
1646 _bytes('<a><b><bs></bs></b></a>'),
1648 self.assertXML(_bytes('<b><bs></bs></b>'), b)
1649 self.assertXML(_bytes('<c><cs></cs></c>'), c)
1651 def test_del_setslice(self):
1652 Element = self.etree.Element
1653 SubElement = self.etree.SubElement
1656 b = SubElement(a, 'b')
1657 bs = SubElement(b, 'bs')
1658 c = SubElement(a, 'c')
1659 cs = SubElement(c, 'cs')
1665 _bytes('<a><b><bs></bs></b><c><cs></cs></c></a>'),
1667 self.assertXML(_bytes('<b><bs></bs></b>'), b)
1668 self.assertXML(_bytes('<c><cs></cs></c>'), c)
1670 def test_replace_slice_tail(self):
1671 XML = self.etree.XML
1672 a = XML(_bytes('<a><b></b>B2<c></c>C2</a>'))
1677 self.assertEqual("B2", b.tail)
1678 self.assertEqual("C2", c.tail)
1680 def test_merge_namespaced_subtree_as_slice(self):
1681 XML = self.etree.XML
1683 '<foo><bar xmlns:baz="http://huhu"><puh><baz:bump1 /><baz:bump2 /></puh></bar></foo>'))
1684 root[:] = root.findall('.//puh') # delete bar from hierarchy
1686 # previously, this lost a namespace declaration on bump2
1687 result = self.etree.tostring(root)
1688 foo = self.etree.fromstring(result)
1690 self.assertEqual('puh', foo[0].tag)
1691 self.assertEqual('{http://huhu}bump1', foo[0][0].tag)
1692 self.assertEqual('{http://huhu}bump2', foo[0][1].tag)
1694 def test_delitem_tail_dealloc(self):
1695 ElementTree = self.etree.ElementTree
1696 f = BytesIO('<a><b></b>B2<c></c>C2</a>')
1697 doc = ElementTree(file=f)
1701 _bytes('<a><c></c>C2</a>'),
1704 def test_delitem_tail(self):
1705 ElementTree = self.etree.ElementTree
1706 f = BytesIO('<a><b></b>B2<c></c>C2</a>')
1707 doc = ElementTree(file=f)
1712 _bytes('<a><c></c>C2</a>'),
1714 self.assertEqual("B2", b.tail)
1715 self.assertEqual("C2", c.tail)
1717 def test_clear(self):
1718 Element = self.etree.Element
1725 self.assertEqual(None, a.text)
1726 self.assertEqual(None, a.tail)
1727 self.assertEqual(None, a.get('hoi'))
1728 self.assertEqual('a', a.tag)
1730 def test_clear_sub(self):
1731 Element = self.etree.Element
1732 SubElement = self.etree.SubElement
1738 b = SubElement(a, 'b')
1739 c = SubElement(b, 'c')
1741 self.assertEqual(None, a.text)
1742 self.assertEqual(None, a.tail)
1743 self.assertEqual(None, a.get('hoi'))
1744 self.assertEqual('a', a.tag)
1745 self.assertEqual(0, len(a))
1746 self.assertXML(_bytes('<a></a>'),
1748 self.assertXML(_bytes('<b><c></c></b>'),
1751 def test_clear_tail(self):
1752 ElementTree = self.etree.ElementTree
1753 f = BytesIO('<a><b></b>B2<c></c>C2</a>')
1754 doc = ElementTree(file=f)
1761 def test_insert(self):
1762 Element = self.etree.Element
1763 SubElement = self.etree.SubElement
1766 b = SubElement(a, 'b')
1767 c = SubElement(a, 'c')
1776 _bytes('<a><d></d><b></b><c></c></a>'),
1785 _bytes('<a><d></d><b></b><e></e><c></c></a>'),
1788 def test_insert_name_interning(self):
1789 # See GH#268 / LP#1773749.
1790 Element = self.etree.Element
1791 SubElement = self.etree.SubElement
1793 # Use unique names to make sure they are new in the tag name dict.
1795 names = dict((k, 'tag-' + str(uuid.uuid4())) for k in 'abcde')
1797 a = Element(names['a'])
1798 b = SubElement(a, names['b'])
1799 c = SubElement(a, names['c'])
1800 d = Element(names['d'])
1808 _bytes('<%(a)s><%(d)s></%(d)s><%(b)s></%(b)s><%(c)s></%(c)s></%(a)s>' % names),
1811 e = Element(names['e'])
1817 _bytes('<%(a)s><%(d)s></%(d)s><%(b)s></%(b)s><%(e)s></%(e)s><%(c)s></%(c)s></%(a)s>' % names),
1820 def test_insert_beyond_index(self):
1821 Element = self.etree.Element
1822 SubElement = self.etree.SubElement
1825 b = SubElement(a, 'b')
1833 _bytes('<a><b></b><c></c></a>'),
1836 def test_insert_negative(self):
1837 Element = self.etree.Element
1838 SubElement = self.etree.SubElement
1841 b = SubElement(a, 'b')
1842 c = SubElement(a, 'c')
1850 _bytes('<a><b></b><d></d><c></c></a>'),
1853 def test_insert_tail(self):
1854 Element = self.etree.Element
1855 SubElement = self.etree.SubElement
1858 b = SubElement(a, 'b')
1865 _bytes('<a><c></c>C2<b></b></a>'),
1868 def test_remove(self):
1869 Element = self.etree.Element
1870 SubElement = self.etree.SubElement
1873 b = SubElement(a, 'b')
1874 c = SubElement(a, 'c')
1881 _bytes('<a><c></c></a>'),
1884 def test_remove_ns(self):
1885 Element = self.etree.Element
1886 SubElement = self.etree.SubElement
1888 a = Element('{http://test}a')
1889 b = SubElement(a, '{http://test}b')
1890 c = SubElement(a, '{http://test}c')
1894 _bytes('<ns0:a xmlns:ns0="http://test"><ns0:c></ns0:c></ns0:a>'),
1897 _bytes('<ns0:b xmlns:ns0="http://test"></ns0:b>'),
1900 def test_remove_nonexisting(self):
1901 Element = self.etree.Element
1902 SubElement = self.etree.SubElement
1905 b = SubElement(a, 'b')
1906 c = SubElement(a, 'c')
1909 ValueError, a.remove, d)
1911 def test_remove_tail(self):
1912 Element = self.etree.Element
1913 SubElement = self.etree.SubElement
1916 b = SubElement(a, 'b')
1922 self.assertEqual('b2', b.tail)
1924 def test_remove_while_iterating(self):
1925 # There is no guarantee that this "works", but it should
1926 # remove at least one child and not crash.
1927 Element = self.etree.Element
1928 SubElement = self.etree.SubElement
1936 self.assertLess(len(a), 3)
1938 def test_makeelement(self):
1939 Element = self.etree.Element
1942 b = a.makeelement('c', {'hoi':'dag'})
1944 _bytes('<c hoi="dag"></c>'),
1947 required_versions_ET['test_iter'] = (1,3)
1948 def test_iter(self):
1949 Element = self.etree.Element
1950 SubElement = self.etree.SubElement
1953 b = SubElement(a, 'b')
1954 c = SubElement(a, 'c')
1955 d = SubElement(b, 'd')
1956 e = SubElement(c, 'e')
1965 def test_iter_remove_tail(self):
1966 Element = self.etree.Element
1967 SubElement = self.etree.SubElement
1972 b = SubElement(a, 'b')
1975 c = SubElement(a, 'c')
1978 d = SubElement(b, 'd')
1981 e = SubElement(c, 'e')
1991 [el.tail for el in a.iter()])
1993 def test_getslice(self):
1994 Element = self.etree.Element
1995 SubElement = self.etree.SubElement
1998 b = SubElement(a, 'b')
1999 c = SubElement(a, 'c')
2000 d = SubElement(a, 'd')
2018 def test_getslice_negative(self):
2019 Element = self.etree.Element
2020 SubElement = self.etree.SubElement
2023 b = SubElement(a, 'b')
2024 c = SubElement(a, 'c')
2025 d = SubElement(a, 'd')
2043 def test_getslice_step(self):
2044 Element = self.etree.Element
2045 SubElement = self.etree.SubElement
2048 b = SubElement(a, 'b')
2049 c = SubElement(a, 'c')
2050 d = SubElement(a, 'd')
2051 e = SubElement(a, 'e')
2069 def test_getslice_text(self):
2070 ElementTree = self.etree.ElementTree
2072 f = BytesIO('<a><b>B</b>B1<c>C</c>C1</a>')
2073 doc = ElementTree(file=f)
2087 def test_comment_getitem_getslice(self):
2088 Element = self.etree.Element
2089 Comment = self.etree.Comment
2090 SubElement = self.etree.SubElement
2093 b = SubElement(a, 'b')
2094 foo = Comment('foo')
2096 c = SubElement(a, 'c')
2103 a[1] = new = Element('new')
2108 _bytes('<a><b></b><new></new><c></c></a>'),
2111 def test_delslice(self):
2112 Element = self.etree.Element
2113 SubElement = self.etree.SubElement
2116 b = SubElement(a, 'b')
2117 c = SubElement(a, 'c')
2118 d = SubElement(a, 'd')
2119 e = SubElement(a, 'e')
2126 def test_delslice_negative1(self):
2127 Element = self.etree.Element
2128 SubElement = self.etree.SubElement
2131 b = SubElement(a, 'b')
2132 c = SubElement(a, 'c')
2133 d = SubElement(a, 'd')
2134 e = SubElement(a, 'e')
2141 def test_delslice_negative2(self):
2142 Element = self.etree.Element
2143 SubElement = self.etree.SubElement
2146 b = SubElement(a, 'b')
2147 c = SubElement(a, 'c')
2148 d = SubElement(a, 'd')
2149 e = SubElement(a, 'e')
2156 def test_delslice_step(self):
2157 Element = self.etree.Element
2158 SubElement = self.etree.SubElement
2161 b = SubElement(a, 'b')
2162 c = SubElement(a, 'c')
2163 d = SubElement(a, 'd')
2164 e = SubElement(a, 'e')
2171 def test_delslice_step_negative(self):
2172 Element = self.etree.Element
2173 SubElement = self.etree.SubElement
2176 b = SubElement(a, 'b')
2177 c = SubElement(a, 'c')
2178 d = SubElement(a, 'd')
2179 e = SubElement(a, 'e')
2186 def test_delslice_step_negative2(self):
2187 Element = self.etree.Element
2188 SubElement = self.etree.SubElement
2191 b = SubElement(a, 'b')
2192 c = SubElement(a, 'c')
2193 d = SubElement(a, 'd')
2194 e = SubElement(a, 'e')
2201 def test_delslice_child_tail_dealloc(self):
2202 ElementTree = self.etree.ElementTree
2203 f = BytesIO('<a><b></b>B2<c></c>C2<d></d>D2<e></e>E2</a>')
2204 doc = ElementTree(file=f)
2208 _bytes('<a><b></b>B2<e></e>E2</a>'),
2211 def test_delslice_child_tail(self):
2212 ElementTree = self.etree.ElementTree
2213 f = BytesIO('<a><b></b>B2<c></c>C2<d></d>D2<e></e>E2</a>')
2214 doc = ElementTree(file=f)
2219 _bytes('<a><b></b>B2<e></e>E2</a>'),
2221 self.assertEqual("B2", b.tail)
2222 self.assertEqual("C2", c.tail)
2223 self.assertEqual("D2", d.tail)
2224 self.assertEqual("E2", e.tail)
2226 def test_delslice_tail(self):
2227 XML = self.etree.XML
2228 a = XML(_bytes('<a><b></b>B2<c></c>C2</a>'))
2233 self.assertEqual("B2", b.tail)
2234 self.assertEqual("C2", c.tail)
2236 def test_delslice_memory(self):
2237 # this could trigger a crash
2238 Element = self.etree.Element
2239 SubElement = self.etree.SubElement
2241 b = SubElement(a, 'b')
2242 c = SubElement(b, 'c')
2243 del b # no more reference to b
2245 self.assertEqual('c', c.tag)
2247 def test_setslice(self):
2248 Element = self.etree.Element
2249 SubElement = self.etree.SubElement
2252 b = SubElement(a, 'b')
2253 c = SubElement(a, 'c')
2254 d = SubElement(a, 'd')
2266 def test_setslice_all(self):
2267 Element = self.etree.Element
2268 SubElement = self.etree.SubElement
2271 b = SubElement(a, 'b')
2272 c = SubElement(a, 'c')
2284 def test_setslice_all_empty(self):
2285 Element = self.etree.Element
2286 SubElement = self.etree.SubElement
2300 def test_setslice_all_replace(self):
2301 Element = self.etree.Element
2302 SubElement = self.etree.SubElement
2305 b = SubElement(a, 'b')
2306 c = SubElement(a, 'c')
2307 d = SubElement(a, 'd')
2315 def test_setslice_all_replace_reversed(self):
2316 Element = self.etree.Element
2317 SubElement = self.etree.SubElement
2320 b = SubElement(a, 'b')
2321 c = SubElement(a, 'c')
2322 d = SubElement(a, 'd')
2330 def test_setslice_all_replace_reversed_ns1(self):
2331 Element = self.etree.Element
2332 SubElement = self.etree.SubElement
2334 a = Element('{ns}a')
2335 b = SubElement(a, '{ns}b', {'{ns1}a1': 'test'})
2336 c = SubElement(a, '{ns}c', {'{ns2}a2': 'test'})
2337 d = SubElement(a, '{ns}d', {'{ns3}a3': 'test'})
2345 ['{ns}d', '{ns}c', '{ns}b'],
2346 [ child.tag for child in a ])
2349 [['{ns3}a3'], ['{ns2}a2'], ['{ns1}a1']],
2350 [ list(child.attrib.keys()) for child in a ])
2352 def test_setslice_all_replace_reversed_ns2(self):
2353 Element = self.etree.Element
2354 SubElement = self.etree.SubElement
2356 a = Element('{ns}a')
2357 b = SubElement(a, '{ns1}b', {'{ns}a1': 'test'})
2358 c = SubElement(a, '{ns2}c', {'{ns}a2': 'test'})
2359 d = SubElement(a, '{ns3}d', {'{ns}a3': 'test'})
2367 ['{ns3}d', '{ns2}c', '{ns1}b'],
2368 [ child.tag for child in a ])
2371 [['{ns}a3'], ['{ns}a2'], ['{ns}a1']],
2372 [ list(child.attrib.keys()) for child in a ])
2374 def test_setslice_end(self):
2375 Element = self.etree.Element
2376 SubElement = self.etree.SubElement
2379 b = SubElement(a, 'b')
2380 c = SubElement(a, 'c')
2399 def test_setslice_end_exact(self):
2400 Element = self.etree.Element
2401 SubElement = self.etree.SubElement
2404 b = SubElement(a, 'b')
2405 c = SubElement(a, 'c')
2406 d = SubElement(a, 'd')
2418 def test_setslice_single(self):
2419 Element = self.etree.Element
2420 SubElement = self.etree.SubElement
2423 b = SubElement(a, 'b')
2424 c = SubElement(a, 'c')
2441 def test_setslice_tail(self):
2442 ElementTree = self.etree.ElementTree
2443 Element = self.etree.Element
2444 f = BytesIO('<a><b></b>B2<c></c>C2<d></d>D2<e></e>E2</a>')
2445 doc = ElementTree(file=f)
2455 _bytes('<a><b></b>B2<x></x>X2<y></y>Y2<z></z>Z2<e></e>E2</a>'),
2458 def test_setslice_negative(self):
2459 Element = self.etree.Element
2460 SubElement = self.etree.SubElement
2463 b = SubElement(a, 'b')
2464 c = SubElement(a, 'c')
2465 d = SubElement(a, 'd')
2475 def test_setslice_negative2(self):
2476 Element = self.etree.Element
2477 SubElement = self.etree.SubElement
2480 b = SubElement(a, 'b')
2481 c = SubElement(a, 'c')
2482 d = SubElement(a, 'd')
2492 def test_setslice_empty(self):
2493 Element = self.etree.Element
2505 def test_tail_elementtree_root(self):
2506 Element = self.etree.Element
2507 ElementTree = self.etree.ElementTree
2511 t = ElementTree(element=a)
2512 self.assertEqual('A2',
2515 def test_ns_access(self):
2516 ElementTree = self.etree.ElementTree
2517 ns = 'http://xml.infrae.com/1'
2518 f = BytesIO('<x:a xmlns:x="%s"><x:b></x:b></x:a>' % ns)
2519 t = ElementTree(file=f)
2521 self.assertEqual('{%s}a' % ns,
2523 self.assertEqual('{%s}b' % ns,
2526 def test_ns_access2(self):
2527 ElementTree = self.etree.ElementTree
2528 ns = 'http://xml.infrae.com/1'
2529 ns2 = 'http://xml.infrae.com/2'
2530 f = BytesIO('<x:a xmlns:x="%s" xmlns:y="%s"><x:b></x:b><y:b></y:b></x:a>' % (ns, ns2))
2531 t = ElementTree(file=f)
2533 self.assertEqual('{%s}a' % ns,
2535 self.assertEqual('{%s}b' % ns,
2537 self.assertEqual('{%s}b' % ns2,
2540 def test_ns_setting(self):
2541 Element = self.etree.Element
2542 SubElement = self.etree.SubElement
2543 ns = 'http://xml.infrae.com/1'
2544 ns2 = 'http://xml.infrae.com/2'
2545 a = Element('{%s}a' % ns)
2546 b = SubElement(a, '{%s}b' % ns2)
2547 c = SubElement(a, '{%s}c' % ns)
2548 self.assertEqual('{%s}a' % ns,
2550 self.assertEqual('{%s}b' % ns2,
2552 self.assertEqual('{%s}c' % ns,
2554 self.assertEqual('{%s}a' % ns,
2556 self.assertEqual('{%s}b' % ns2,
2558 self.assertEqual('{%s}c' % ns,
2561 def test_ns_tag_parse(self):
2562 Element = self.etree.Element
2563 SubElement = self.etree.SubElement
2564 ElementTree = self.etree.ElementTree
2566 ns = 'http://xml.infrae.com/1'
2567 ns2 = 'http://xml.infrae.com/2'
2568 f = BytesIO('<a xmlns="%s" xmlns:x="%s"><x:b></x:b><b></b></a>' % (ns, ns2))
2569 t = ElementTree(file=f)
2572 self.assertEqual('{%s}a' % ns,
2574 self.assertEqual('{%s}b' % ns2,
2576 self.assertEqual('{%s}b' % ns,
2579 def test_ns_attr(self):
2580 Element = self.etree.Element
2581 ns = 'http://xml.infrae.com/1'
2582 ns2 = 'http://xml.infrae.com/2'
2584 a.set('{%s}foo' % ns, 'Foo')
2585 a.set('{%s}bar' % ns2, 'Bar')
2588 a.get('{%s}foo' % ns))
2591 a.get('{%s}bar' % ns2))
2594 _bytes('<a xmlns:ns0="%s" xmlns:ns1="%s" ns0:foo="Foo" ns1:bar="Bar"></a>' % (ns, ns2)),
2596 except AssertionError:
2598 _bytes('<a xmlns:ns0="%s" xmlns:ns1="%s" ns1:foo="Foo" ns0:bar="Bar"></a>' % (ns2, ns)),
2601 def test_ns_move(self):
2602 Element = self.etree.Element
2603 one = self.etree.fromstring(
2604 _bytes('<foo><bar xmlns:ns="http://a.b.c"><ns:baz/></bar></foo>'))
2607 two = Element('root')
2609 # removing the originating document could cause a crash/error before
2610 # as namespace is not moved along with it
2612 self.assertEqual('{http://a.b.c}baz', two[0].tag)
2614 def test_ns_decl_tostring(self):
2615 tostring = self.etree.tostring
2616 root = self.etree.XML(
2617 _bytes('<foo><bar xmlns:ns="http://a.b.c"><ns:baz/></bar></foo>'))
2620 nsdecl = re.findall(_bytes("xmlns(?::[a-z0-9]+)?=[\"']([^\"']+)[\"']"),
2622 self.assertEqual([_bytes("http://a.b.c")], nsdecl)
2624 def test_ns_decl_tostring_default(self):
2625 tostring = self.etree.tostring
2626 root = self.etree.XML(
2627 _bytes('<foo><bar xmlns="http://a.b.c"><baz/></bar></foo>'))
2630 nsdecl = re.findall(_bytes("xmlns(?::[a-z0-9]+)?=[\"']([^\"']+)[\"']"),
2632 self.assertEqual([_bytes("http://a.b.c")], nsdecl)
2634 def test_ns_decl_tostring_root(self):
2635 tostring = self.etree.tostring
2636 root = self.etree.XML(
2637 _bytes('<foo xmlns:ns="http://a.b.c"><bar><ns:baz/></bar></foo>'))
2640 nsdecl = re.findall(_bytes("xmlns(?::[a-z0-9]+)?=[\"']([^\"']+)[\"']"),
2643 self.assertEqual([_bytes("http://a.b.c")], nsdecl)
2645 def test_ns_decl_tostring_element(self):
2646 Element = self.etree.Element
2647 SubElement = self.etree.SubElement
2649 root = Element("foo")
2650 bar = SubElement(root, "{http://a.b.c}bar")
2651 baz = SubElement(bar, "{http://a.b.c}baz")
2653 nsdecl = re.findall(_bytes("xmlns(?::[a-z0-9]+)?=[\"']([^\"']+)[\"']"),
2654 self.etree.tostring(baz))
2656 self.assertEqual([_bytes("http://a.b.c")], nsdecl)
2658 def test_attribute_xmlns_move(self):
2659 Element = self.etree.Element
2661 root = Element('element')
2663 subelement = Element('subelement',
2664 {"{http://www.w3.org/XML/1998/namespace}id": "foo"})
2665 self.assertEqual(1, len(subelement.attrib))
2668 subelement.get("{http://www.w3.org/XML/1998/namespace}id"))
2670 root.append(subelement)
2671 self.assertEqual(1, len(subelement.attrib))
2673 list({"{http://www.w3.org/XML/1998/namespace}id" : "foo"}.items()),
2674 list(subelement.attrib.items()))
2677 subelement.get("{http://www.w3.org/XML/1998/namespace}id"))
2679 def test_namespaces_after_serialize(self):
2680 parse = self.etree.parse
2681 tostring = self.etree.tostring
2683 ns_href = "http://a.b.c"
2685 BytesIO('<foo><bar xmlns:ns="%s"><ns:baz/></bar></foo>' % ns_href))
2686 baz = one.getroot()[0][0]
2688 parsed = parse(BytesIO( tostring(baz) )).getroot()
2689 self.assertEqual('{%s}baz' % ns_href, parsed.tag)
2691 def test_attribute_namespace_roundtrip(self):
2692 fromstring = self.etree.fromstring
2693 tostring = self.etree.tostring
2695 ns_href = "http://a.b.c"
2696 xml = _bytes('<root xmlns="%s" xmlns:x="%s"><el x:a="test" /></root>' % (
2698 root = fromstring(xml)
2699 self.assertEqual('test', root[0].get('{%s}a' % ns_href))
2701 xml2 = tostring(root)
2702 self.assertTrue(_bytes(':a=') in xml2, xml2)
2704 root2 = fromstring(xml2)
2705 self.assertEqual('test', root2[0].get('{%s}a' % ns_href))
2707 def test_attribute_namespace_roundtrip_replaced(self):
2708 fromstring = self.etree.fromstring
2709 tostring = self.etree.tostring
2711 ns_href = "http://a.b.c"
2712 xml = _bytes('<root xmlns="%s" xmlns:x="%s"><el x:a="test" /></root>' % (
2714 root = fromstring(xml)
2715 self.assertEqual('test', root[0].get('{%s}a' % ns_href))
2717 root[0].set('{%s}a' % ns_href, 'TEST')
2719 xml2 = tostring(root)
2720 self.assertTrue(_bytes(':a=') in xml2, xml2)
2722 root2 = fromstring(xml2)
2723 self.assertEqual('TEST', root2[0].get('{%s}a' % ns_href))
2725 required_versions_ET['test_register_namespace'] = (1,3)
2726 def test_register_namespace(self):
2728 Element = self.etree.Element
2729 prefix = 'TESTPREFIX'
2730 namespace = 'http://seriously.unknown/namespace/URI'
2732 el = Element('{%s}test' % namespace)
2733 self.assertEqual(_bytes('<ns0:test xmlns:ns0="%s"></ns0:test>' % namespace),
2734 self._writeElement(el))
2736 self.etree.register_namespace(prefix, namespace)
2737 el = Element('{%s}test' % namespace)
2738 self.assertEqual(_bytes('<%s:test xmlns:%s="%s"></%s:test>' % (
2739 prefix, prefix, namespace, prefix)),
2740 self._writeElement(el))
2742 self.assertRaises(ValueError, self.etree.register_namespace, 'ns25', namespace)
2744 def test_tostring(self):
2745 tostring = self.etree.tostring
2746 Element = self.etree.Element
2747 SubElement = self.etree.SubElement
2750 b = SubElement(a, 'b')
2751 c = SubElement(a, 'c')
2753 self.assertEqual(_bytes('<a><b></b><c></c></a>'),
2754 canonicalize(tostring(a)))
2756 def test_tostring_element(self):
2757 tostring = self.etree.tostring
2758 Element = self.etree.Element
2759 SubElement = self.etree.SubElement
2762 b = SubElement(a, 'b')
2763 c = SubElement(a, 'c')
2764 d = SubElement(c, 'd')
2765 self.assertEqual(_bytes('<b></b>'),
2766 canonicalize(tostring(b)))
2767 self.assertEqual(_bytes('<c><d></d></c>'),
2768 canonicalize(tostring(c)))
2770 def test_tostring_element_tail(self):
2771 tostring = self.etree.tostring
2772 Element = self.etree.Element
2773 SubElement = self.etree.SubElement
2776 b = SubElement(a, 'b')
2777 c = SubElement(a, 'c')
2778 d = SubElement(c, 'd')
2781 self.assertTrue(tostring(b) == _bytes('<b/>Foo') or
2782 tostring(b) == _bytes('<b />Foo'))
2784 required_versions_ET['test_tostring_method_html'] = (1,3)
2785 def test_tostring_method_html(self):
2786 tostring = self.etree.tostring
2787 Element = self.etree.Element
2788 SubElement = self.etree.SubElement
2790 html = Element('html')
2791 body = SubElement(html, 'body')
2792 p = SubElement(body, 'p')
2794 SubElement(p, 'br').tail = "test"
2796 self.assertEqual(_bytes('<html><body><p>html<br>test</p></body></html>'),
2797 tostring(html, method="html"))
2799 required_versions_ET['test_tostring_method_text'] = (1,3)
2800 def test_tostring_method_text(self):
2801 tostring = self.etree.tostring
2802 Element = self.etree.Element
2803 SubElement = self.etree.SubElement
2808 b = SubElement(a, 'b')
2811 c = SubElement(a, 'c')
2814 self.assertEqual(_bytes('ABTAILCtail'),
2815 tostring(a, method="text"))
2817 def test_iterparse(self):
2818 iterparse = self.etree.iterparse
2819 f = BytesIO('<a><b></b><c/></a>')
2821 iterator = iterparse(f)
2822 self.assertEqual(None,
2824 events = list(iterator)
2825 root = iterator.root
2827 [('end', root[0]), ('end', root[1]), ('end', root)],
2830 def test_iterparse_incomplete(self):
2831 iterparse = self.etree.iterparse
2832 f = BytesIO('<a><b></b><c/></a>')
2834 iterator = iterparse(f)
2835 self.assertEqual(None,
2837 event, element = next(iter(iterator))
2838 self.assertEqual('end', event)
2839 self.assertEqual('b', element.tag)
2841 def test_iterparse_file(self):
2842 iterparse = self.etree.iterparse
2843 iterator = iterparse(fileInTestDir("test.xml"))
2844 self.assertEqual(None,
2846 events = list(iterator)
2847 root = iterator.root
2849 [('end', root[0]), ('end', root)],
2852 def test_iterparse_start(self):
2853 iterparse = self.etree.iterparse
2854 f = BytesIO('<a><b></b><c/></a>')
2856 iterator = iterparse(f, events=('start',))
2857 events = list(iterator)
2858 root = iterator.root
2860 [('start', root), ('start', root[0]), ('start', root[1])],
2863 def test_iterparse_start_end(self):
2864 iterparse = self.etree.iterparse
2865 f = BytesIO('<a><b></b><c/></a>')
2867 iterator = iterparse(f, events=('start','end'))
2868 events = list(iterator)
2869 root = iterator.root
2871 [('start', root), ('start', root[0]), ('end', root[0]),
2872 ('start', root[1]), ('end', root[1]), ('end', root)],
2875 def test_iterparse_clear(self):
2876 iterparse = self.etree.iterparse
2877 f = BytesIO('<a><b></b><c/></a>')
2879 iterator = iterparse(f)
2880 for event, elem in iterator:
2883 root = iterator.root
2887 def test_iterparse_large(self):
2888 iterparse = self.etree.iterparse
2890 f = BytesIO('<a>%s</a>' % ('<b>test</b>'*CHILD_COUNT))
2893 for key in iterparse(f):
2894 event, element = key
2896 self.assertEqual(i, CHILD_COUNT + 1)
2898 def test_iterparse_set_ns_attribute(self):
2899 iterparse = self.etree.iterparse
2900 f = BytesIO('<a xmlns="http://ns1/"><b><c xmlns="http://ns2/"/></b></a>')
2902 attr_name = '{http://testns/}bla'
2904 iterator = iterparse(f, events=('start','end','start-ns','end-ns'))
2905 for event, elem in iterator:
2906 events.append(event)
2907 if event == 'start':
2908 if elem.tag != '{http://ns1/}a':
2909 elem.set(attr_name, 'value')
2912 ['start-ns', 'start', 'start', 'start-ns', 'start',
2913 'end', 'end-ns', 'end', 'end', 'end-ns'],
2916 root = iterator.root
2919 root.get(attr_name))
2922 root[0].get(attr_name))
2924 def test_iterparse_only_end_ns(self):
2925 iterparse = self.etree.iterparse
2926 f = BytesIO('<a xmlns="http://ns1/"><b><c xmlns="http://ns2/"/></b></a>')
2928 attr_name = '{http://testns/}bla'
2930 iterator = iterparse(f, events=('start','end','start-ns','end-ns'))
2931 for event, elem in iterator:
2932 events.append(event)
2933 if event == 'start':
2934 if elem.tag != '{http://ns1/}a':
2935 elem.set(attr_name, 'value')
2938 ['start-ns', 'start', 'start', 'start-ns', 'start',
2939 'end', 'end-ns', 'end', 'end', 'end-ns'],
2942 root = iterator.root
2945 root.get(attr_name))
2948 root[0].get(attr_name))
2950 def test_iterparse_move_elements(self):
2951 iterparse = self.etree.iterparse
2952 f = BytesIO('<a><b><d/></b><c/></a>')
2954 for event, node in etree.iterparse(f): pass
2956 root = etree.Element('new_root', {})
2961 [ el.tag for el in root ])
2963 def test_iterparse_cdata(self):
2964 tostring = self.etree.tostring
2965 f = BytesIO('<root><![CDATA[test]]></root>')
2966 context = self.etree.iterparse(f)
2967 content = [ el.text for event,el in context ]
2969 self.assertEqual(['test'], content)
2970 self.assertEqual(_bytes('<root>test</root>'),
2971 tostring(context.root))
2973 def test_parse_file(self):
2974 parse = self.etree.parse
2976 tree = parse(fileInTestDir('test.xml'))
2978 _bytes('<a><b></b></a>'),
2981 def test_parse_file_nonexistent(self):
2982 parse = self.etree.parse
2983 self.assertRaises(IOError, parse, fileInTestDir('notthere.xml'))
2985 def test_parse_error_none(self):
2986 parse = self.etree.parse
2987 self.assertRaises(TypeError, parse, None)
2989 required_versions_ET['test_parse_error'] = (1,3)
2990 def test_parse_error(self):
2991 # ET < 1.3 raises ExpatError
2992 parse = self.etree.parse
2993 f = BytesIO('<a><b></c></b></a>')
2994 self.assertRaises(SyntaxError, parse, f)
2997 required_versions_ET['test_parse_error_from_file'] = (1,3)
2998 def test_parse_error_from_file(self):
2999 parse = self.etree.parse
3001 f = open(fileInTestDir('test_broken.xml'), 'rb')
3002 self.assertRaises(SyntaxError, parse, f)
3005 def test_parse_file_object(self):
3006 parse = self.etree.parse
3008 f = open(fileInTestDir('test.xml'), 'rb')
3012 _bytes('<a><b></b></a>'),
3015 def test_parse_stringio(self):
3016 parse = self.etree.parse
3017 f = BytesIO('<a><b></b></a>')
3021 _bytes('<a><b></b></a>'),
3025 def test_parse_cdata(self):
3026 tostring = self.etree.tostring
3027 root = self.etree.XML(_bytes('<root><![CDATA[test]]></root>'))
3029 self.assertEqual('test', root.text)
3030 self.assertEqual(_bytes('<root>test</root>'),
3033 def test_parse_with_encoding(self):
3034 # this can fail in libxml2 <= 2.6.22
3035 parse = self.etree.parse
3036 tree = parse(BytesIO('<?xml version="1.0" encoding="ascii"?><html/>'))
3037 self.assertXML(_bytes('<html></html>'),
3040 def test_encoding(self):
3041 Element = self.etree.Element
3044 a.text = _str('Søk på nettet')
3046 _str('<a>Søk på nettet</a>').encode('UTF-8'),
3049 def test_encoding_exact(self):
3050 ElementTree = self.etree.ElementTree
3051 Element = self.etree.Element
3054 a.text = _str('Søk på nettet')
3057 tree = ElementTree(element=a)
3058 tree.write(f, encoding='utf-8')
3059 self.assertEqual(_str('<a>Søk på nettet</a>').encode('UTF-8'),
3060 f.getvalue().replace(_bytes('\n'),_bytes('')))
3062 def test_parse_file_encoding(self):
3063 parse = self.etree.parse
3065 tree = parse(fileInTestDir('test-string.xml'))
3067 _str('<a>Søk på nettet</a>').encode('UTF-8'),
3068 tree.getroot(), 'UTF-8')
3070 def test_parse_file_object_encoding(self):
3071 parse = self.etree.parse
3073 f = open(fileInTestDir('test-string.xml'), 'rb')
3077 _str('<a>Søk på nettet</a>').encode('UTF-8'),
3078 tree.getroot(), 'UTF-8')
3080 def test_encoding_8bit_latin1(self):
3081 ElementTree = self.etree.ElementTree
3082 Element = self.etree.Element
3085 a.text = _str('Søk på nettet')
3088 tree = ElementTree(element=a)
3089 tree.write(f, encoding='iso-8859-1')
3090 result = f.getvalue()
3091 declaration = _bytes("<?xml version=\'1.0\' encoding=\'iso-8859-1\'?>")
3092 self.assertEncodingDeclaration(result, _bytes('iso-8859-1'))
3093 result = result.split(_bytes('?>'), 1)[-1].replace(_bytes('\n'),_bytes(''))
3094 self.assertEqual(_str('<a>Søk på nettet</a>').encode('iso-8859-1'),
3097 required_versions_ET['test_parse_encoding_8bit_explicit'] = (1,3)
3098 def test_parse_encoding_8bit_explicit(self):
3099 XMLParser = self.XMLParser
3101 text = _str('Søk på nettet')
3102 xml_latin1 = (_str('<a>%s</a>') % text).encode('iso-8859-1')
3104 self.assertRaises(self.etree.ParseError,
3106 BytesIO(xml_latin1))
3108 tree = self.etree.parse(BytesIO(xml_latin1),
3109 XMLParser(encoding="iso-8859-1"))
3111 self.assertEqual(a.text, text)
3113 required_versions_ET['test_parse_encoding_8bit_override'] = (1,3)
3114 def test_parse_encoding_8bit_override(self):
3115 XMLParser = self.XMLParser
3117 text = _str('Søk på nettet')
3118 wrong_declaration = _str("<?xml version='1.0' encoding='UTF-8'?>")
3119 xml_latin1 = (_str('%s<a>%s</a>') % (wrong_declaration, text)
3120 ).encode('iso-8859-1')
3122 self.assertRaises(self.etree.ParseError,
3124 BytesIO(xml_latin1))
3126 tree = self.etree.parse(BytesIO(xml_latin1),
3127 XMLParser(encoding="iso-8859-1"))
3129 self.assertEqual(a.text, text)
3131 def _test_wrong_unicode_encoding(self):
3132 # raise error on wrong encoding declaration in unicode strings
3133 XML = self.etree.XML
3134 test_utf = (_str('<?xml version="1.0" encoding="iso-8859-1"?>') +
3135 _str('<a>Søk på nettet</a>'))
3136 self.assertRaises(SyntaxError, XML, test_utf)
3138 def test_encoding_write_default_encoding(self):
3139 ElementTree = self.etree.ElementTree
3140 Element = self.etree.Element
3143 a.text = _str('Søk på nettet')
3146 tree = ElementTree(element=a)
3148 data = f.getvalue().replace(_bytes('\n'),_bytes(''))
3150 _str('<a>Søk på nettet</a>').encode('ASCII', 'xmlcharrefreplace'),
3153 def test_encoding_tostring(self):
3154 Element = self.etree.Element
3155 tostring = self.etree.tostring
3158 a.text = _str('Søk på nettet')
3159 self.assertEqual(_str('<a>Søk på nettet</a>').encode('UTF-8'),
3160 tostring(a, encoding='utf-8'))
3162 def test_encoding_tostring_unknown(self):
3163 Element = self.etree.Element
3164 tostring = self.etree.tostring
3167 a.text = _str('Søk på nettet')
3168 self.assertRaises(LookupError, tostring, a,
3169 encoding='Invalid Encoding')
3171 def test_encoding_tostring_sub(self):
3172 Element = self.etree.Element
3173 SubElement = self.etree.SubElement
3174 tostring = self.etree.tostring
3177 b = SubElement(a, 'b')
3178 b.text = _str('Søk på nettet')
3179 self.assertEqual(_str('<b>Søk på nettet</b>').encode('UTF-8'),
3180 tostring(b, encoding='utf-8'))
3182 def test_encoding_tostring_sub_tail(self):
3183 Element = self.etree.Element
3184 SubElement = self.etree.SubElement
3185 tostring = self.etree.tostring
3188 b = SubElement(a, 'b')
3189 b.text = _str('Søk på nettet')
3190 b.tail = _str('Søk')
3191 self.assertEqual(_str('<b>Søk på nettet</b>Søk').encode('UTF-8'),
3192 tostring(b, encoding='utf-8'))
3194 def test_encoding_tostring_default_encoding(self):
3195 Element = self.etree.Element
3196 SubElement = self.etree.SubElement
3197 tostring = self.etree.tostring
3200 a.text = _str('Søk på nettet')
3202 expected = _bytes('<a>Søk på nettet</a>')
3207 def test_encoding_sub_tostring_default_encoding(self):
3208 Element = self.etree.Element
3209 SubElement = self.etree.SubElement
3210 tostring = self.etree.tostring
3213 b = SubElement(a, 'b')
3214 b.text = _str('Søk på nettet')
3216 expected = _bytes('<b>Søk på nettet</b>')
3221 def test_encoding_8bit_xml(self):
3222 utext = _str('Søk på nettet')
3223 uxml = _str('<p>%s</p>') % utext
3224 prologue = _bytes('<?xml version="1.0" encoding="iso-8859-1" ?>')
3225 isoxml = prologue + uxml.encode('iso-8859-1')
3226 tree = self.etree.XML(isoxml)
3227 self.assertEqual(utext, tree.text)
3229 def test_encoding_utf8_bom(self):
3230 utext = _str('Søk på nettet')
3231 uxml = (_str('<?xml version="1.0" encoding="UTF-8"?>') +
3232 _str('<p>%s</p>') % utext)
3233 bom = _bytes('\\xEF\\xBB\\xBF').decode("unicode_escape").encode("latin1")
3234 xml = bom + uxml.encode("utf-8")
3235 tree = etree.XML(xml)
3236 self.assertEqual(utext, tree.text)
3238 def test_encoding_8bit_parse_stringio(self):
3239 utext = _str('Søk på nettet')
3240 uxml = _str('<p>%s</p>') % utext
3241 prologue = _bytes('<?xml version="1.0" encoding="iso-8859-1" ?>')
3242 isoxml = prologue + uxml.encode('iso-8859-1')
3243 el = self.etree.parse(BytesIO(isoxml)).getroot()
3244 self.assertEqual(utext, el.text)
3246 def test_deepcopy_elementtree(self):
3247 Element = self.etree.Element
3248 ElementTree = self.etree.ElementTree
3252 atree = ElementTree(a)
3254 btree = copy.deepcopy(atree)
3255 self.assertEqual("Foo", atree.getroot().text)
3256 self.assertEqual("Foo", btree.getroot().text)
3257 self.assertFalse(btree is atree)
3258 self.assertFalse(btree.getroot() is atree.getroot())
3260 def test_deepcopy(self):
3261 Element = self.etree.Element
3266 b = copy.deepcopy(a)
3267 self.assertEqual('Foo', b.text)
3270 self.assertEqual('Bar', b.text)
3271 self.assertEqual('Foo', a.text)
3274 self.assertEqual('Bar', b.text)
3276 def test_deepcopy_tail(self):
3277 Element = self.etree.Element
3282 b = copy.deepcopy(a)
3283 self.assertEqual('Foo', b.tail)
3286 self.assertEqual('Bar', b.tail)
3287 self.assertEqual('Foo', a.tail)
3290 self.assertEqual('Bar', b.tail)
3292 def test_deepcopy_subelement(self):
3293 Element = self.etree.Element
3294 SubElement = self.etree.SubElement
3296 root = Element('root')
3297 a = SubElement(root, 'a')
3301 b = copy.deepcopy(a)
3302 self.assertEqual('FooText', b.text)
3303 self.assertEqual('FooTail', b.tail)
3307 self.assertEqual('BarTail', b.tail)
3308 self.assertEqual('FooTail', a.tail)
3309 self.assertEqual('BarText', b.text)
3310 self.assertEqual('FooText', a.text)
3313 self.assertEqual('BarTail', b.tail)
3314 self.assertEqual('BarText', b.text)
3316 def test_deepcopy_namespaces(self):
3317 root = self.etree.XML(_bytes('''<doc xmlns="dns" xmlns:t="tns">
3318 <parent><node t:foo="bar" /></parent>
3321 root[0][0].get('{tns}foo'),
3322 copy.deepcopy(root[0])[0].get('{tns}foo') )
3324 root[0][0].get('{tns}foo'),
3325 copy.deepcopy(root[0][0]).get('{tns}foo') )
3327 def test_deepcopy_append(self):
3328 # previously caused a crash
3329 Element = self.etree.Element
3330 tostring = self.etree.tostring
3333 b = copy.deepcopy(a)
3334 a.append( Element('C') )
3335 b.append( Element('X') )
3337 self.assertEqual(_bytes('<a><C/></a>'),
3338 tostring(a).replace(_bytes(' '), _bytes('')))
3339 self.assertEqual(_bytes('<a><X/></a>'),
3340 tostring(b).replace(_bytes(' '), _bytes('')))
3342 def test_deepcopy_comment(self):
3343 # previously caused a crash
3344 # not supported by ET < 1.3!
3345 Comment = self.etree.Comment
3348 b = copy.deepcopy(a)
3351 self.assertEqual('ONE', a.text)
3352 self.assertEqual('ANOTHER', b.text)
3354 def test_shallowcopy(self):
3355 Element = self.etree.Element
3361 self.assertEqual('Foo', b.text)
3364 self.assertEqual('Bar', b.text)
3365 self.assertEqual('Foo', a.text)
3366 # XXX ElementTree will share nodes, but lxml.etree won't..
3368 def test_shallowcopy_elementtree(self):
3369 Element = self.etree.Element
3370 ElementTree = self.etree.ElementTree
3374 atree = ElementTree(a)
3376 btree = copy.copy(atree)
3377 self.assertFalse(btree is atree)
3378 self.assertTrue(btree.getroot() is atree.getroot())
3379 self.assertEqual('Foo', atree.getroot().text)
3381 def _test_element_boolean(self):
3382 # deprecated as of ET 1.3/lxml 2.0
3384 e = etree.Element('foo')
3385 self.assertEqual(False, bool(e))
3386 etree.SubElement(e, 'bar')
3387 self.assertEqual(True, bool(e))
3388 e = etree.Element('foo')
3390 self.assertEqual(False, bool(e))
3391 e = etree.Element('foo')
3393 self.assertEqual(False, bool(e))
3394 e = etree.Element('foo')
3396 self.assertEqual(False, bool(e))
3398 def test_multiple_elementrees(self):
3401 a = etree.Element('a')
3402 b = etree.SubElement(a, 'b')
3404 t = etree.ElementTree(a)
3405 self.assertEqual(self._rootstring(t), _bytes('<a><b/></a>'))
3407 t1 = etree.ElementTree(a)
3408 self.assertEqual(self._rootstring(t1), _bytes('<a><b/></a>'))
3409 self.assertEqual(self._rootstring(t), _bytes('<a><b/></a>'))
3411 t2 = etree.ElementTree(b)
3412 self.assertEqual(self._rootstring(t2), _bytes('<b/>'))
3413 self.assertEqual(self._rootstring(t1), _bytes('<a><b/></a>'))
3414 self.assertEqual(self._rootstring(t), _bytes('<a><b/></a>'))
3416 def test_qname(self):
3418 qname = etree.QName('myns', 'a')
3419 a1 = etree.Element(qname)
3420 a2 = etree.SubElement(a1, qname)
3421 self.assertEqual(a1.tag, "{myns}a")
3422 self.assertEqual(a2.tag, "{myns}a")
3424 def test_qname_cmp(self):
3426 qname1 = etree.QName('myns', 'a')
3427 qname2 = etree.QName('myns', 'a')
3428 self.assertEqual(qname1, "{myns}a")
3429 self.assertEqual("{myns}a", qname2)
3430 self.assertEqual(qname1, qname1)
3431 self.assertEqual(qname1, qname2)
3433 def test_qname_attribute_getset(self):
3435 qname = etree.QName('myns', 'a')
3437 a = etree.Element(qname)
3438 a.set(qname, "value")
3440 self.assertEqual(a.get(qname), "value")
3441 self.assertEqual(a.get("{myns}a"), "value")
3443 def test_qname_attrib(self):
3445 qname = etree.QName('myns', 'a')
3447 a = etree.Element(qname)
3448 a.attrib[qname] = "value"
3450 self.assertEqual(a.attrib[qname], "value")
3451 self.assertEqual(a.attrib.get(qname), "value")
3453 self.assertEqual(a.attrib["{myns}a"], "value")
3454 self.assertEqual(a.attrib.get("{myns}a"), "value")
3456 def test_qname_attribute_resolve(self):
3458 qname = etree.QName('http://myns', 'a')
3459 a = etree.Element(qname)
3463 _bytes('<ns0:a xmlns:ns0="http://myns" ns0:a="ns0:a"></ns0:a>'),
3466 def test_qname_attribute_resolve_new(self):
3468 qname = etree.QName('http://myns', 'a')
3469 a = etree.Element('a')
3473 _bytes('<a xmlns:ns0="http://myns" a="ns0:a"></a>'),
3476 def test_qname_attrib_resolve(self):
3478 qname = etree.QName('http://myns', 'a')
3479 a = etree.Element(qname)
3480 a.attrib[qname] = qname
3483 _bytes('<ns0:a xmlns:ns0="http://myns" ns0:a="ns0:a"></ns0:a>'),
3486 def test_parser_version(self):
3488 parser = etree.XMLParser()
3489 if hasattr(parser, "version"):
3490 # ElementTree 1.3+, cET
3491 self.assertTrue(re.match("[^ ]+ [0-9.]+", parser.version))
3493 # feed parser interface
3495 def test_feed_parser_bytes(self):
3496 parser = self.XMLParser()
3498 parser.feed(_bytes('<?xml version='))
3499 parser.feed(_bytes('"1.0"?><ro'))
3500 parser.feed(_bytes('ot><'))
3501 parser.feed(_bytes('a test="works"/'))
3502 parser.feed(_bytes('></root'))
3503 parser.feed(_bytes('>'))
3505 root = parser.close()
3507 self.assertEqual(root.tag, "root")
3508 self.assertEqual(root[0].tag, "a")
3509 self.assertEqual(root[0].get("test"), "works")
3511 def test_feed_parser_unicode(self):
3512 parser = self.XMLParser()
3514 parser.feed(_str('<ro'))
3515 parser.feed(_str('ot><'))
3516 parser.feed(_str('a test="works"/'))
3517 parser.feed(_str('></root'))
3518 parser.feed(_str('>'))
3520 root = parser.close()
3522 self.assertEqual(root.tag, "root")
3523 self.assertEqual(root[0].tag, "a")
3524 self.assertEqual(root[0].get("test"), "works")
3526 required_versions_ET['test_feed_parser_error_close_empty'] = (1,3)
3527 def test_feed_parser_error_close_empty(self):
3528 ParseError = self.etree.ParseError
3529 parser = self.XMLParser()
3530 self.assertRaises(ParseError, parser.close)
3532 required_versions_ET['test_feed_parser_error_close_incomplete'] = (1,3)
3533 def test_feed_parser_error_close_incomplete(self):
3534 ParseError = self.etree.ParseError
3535 parser = self.XMLParser()
3537 parser.feed('<?xml version=')
3538 parser.feed('"1.0"?><ro')
3540 self.assertRaises(ParseError, parser.close)
3542 required_versions_ET['test_feed_parser_error_broken'] = (1,3)
3543 def test_feed_parser_error_broken(self):
3544 ParseError = self.etree.ParseError
3545 parser = self.XMLParser()
3547 parser.feed('<?xml version=')
3548 parser.feed('"1.0"?><ro')
3550 parser.feed('<><><><><><><')
3552 # can raise, but not required before close()
3555 self.assertRaises(ParseError, parser.close)
3557 required_versions_ET['test_feed_parser_error_position'] = (1,3)
3558 def test_feed_parser_error_position(self):
3559 ParseError = self.etree.ParseError
3560 parser = self.XMLParser()
3564 e = sys.exc_info()[1]
3565 self.assertNotEqual(None, e.code)
3566 self.assertNotEqual(0, e.code)
3567 self.assertTrue(isinstance(e.position, tuple))
3568 self.assertTrue(e.position >= (0, 0))
3570 # parser target interface
3572 required_versions_ET['test_parser_target_property'] = (1,3)
3573 def test_parser_target_property(self):
3574 class Target(object):
3578 parser = self.XMLParser(target=target)
3580 self.assertEqual(target, parser.target)
3582 def test_parser_target_tag(self):
3583 assertEqual = self.assertEqual
3584 assertFalse = self.assertFalse
3587 class Target(object):
3588 def start(self, tag, attrib):
3589 events.append("start")
3591 assertEqual("TAG", tag)
3593 events.append("end")
3594 assertEqual("TAG", tag)
3598 parser = self.XMLParser(target=Target())
3600 parser.feed("<TAG/>")
3601 done = parser.close()
3603 self.assertEqual("DONE", done)
3604 self.assertEqual(["start", "end"], events)
3606 def test_parser_target_error_in_start(self):
3607 assertEqual = self.assertEqual
3610 class Target(object):
3611 def start(self, tag, attrib):
3612 events.append("start")
3613 assertEqual("TAG", tag)
3614 raise ValueError("TEST")
3616 events.append("end")
3617 assertEqual("TAG", tag)
3621 parser = self.XMLParser(target=Target())
3624 parser.feed("<TAG/>")
3626 self.assertTrue('TEST' in str(sys.exc_info()[1]))
3628 self.assertTrue(False)
3629 if 'lxml' in self.etree.__name__:
3630 self.assertEqual(["start"], events)
3632 # cElementTree calls end() as well
3633 self.assertTrue("start" in events)
3635 def test_parser_target_error_in_end(self):
3636 assertEqual = self.assertEqual
3639 class Target(object):
3640 def start(self, tag, attrib):
3641 events.append("start")
3642 assertEqual("TAG", tag)
3644 events.append("end")
3645 assertEqual("TAG", tag)
3646 raise ValueError("TEST")
3650 parser = self.XMLParser(target=Target())
3653 parser.feed("<TAG/>")
3655 self.assertTrue('TEST' in str(sys.exc_info()[1]))
3657 self.assertTrue(False)
3658 self.assertEqual(["start", "end"], events)
3660 def test_parser_target_error_in_close(self):
3661 assertEqual = self.assertEqual
3664 class Target(object):
3665 def start(self, tag, attrib):
3666 events.append("start")
3667 assertEqual("TAG", tag)
3669 events.append("end")
3670 assertEqual("TAG", tag)
3672 raise ValueError("TEST")
3674 parser = self.XMLParser(target=Target())
3677 parser.feed("<TAG/>")
3680 self.assertTrue('TEST' in str(sys.exc_info()[1]))
3682 self.assertTrue(False)
3683 self.assertEqual(["start", "end"], events)
3685 def test_parser_target_error_in_start_and_close(self):
3686 assertEqual = self.assertEqual
3689 class Target(object):
3690 def start(self, tag, attrib):
3691 events.append("start")
3692 assertEqual("TAG", tag)
3693 raise IndexError("TEST-IE")
3695 events.append("end")
3696 assertEqual("TAG", tag)
3698 raise ValueError("TEST-VE")
3700 parser = self.XMLParser(target=Target())
3703 parser.feed("<TAG/>")
3706 if 'lxml' in self.etree.__name__:
3707 # we try not to swallow the initial exception in Py2
3708 self.assertTrue(sys.version_info[0] < 3)
3709 self.assertTrue('TEST-IE' in str(sys.exc_info()[1]))
3711 if 'lxml' in self.etree.__name__:
3712 self.assertTrue(sys.version_info[0] >= 3)
3713 self.assertTrue('TEST-VE' in str(sys.exc_info()[1]))
3715 self.assertTrue(False)
3717 if 'lxml' in self.etree.__name__:
3718 self.assertEqual(["start"], events)
3720 # cElementTree calls end() as well
3721 self.assertTrue("start" in events)
3723 def test_elementtree_parser_target(self):
3724 assertEqual = self.assertEqual
3725 assertFalse = self.assertFalse
3726 Element = self.etree.Element
3729 class Target(object):
3730 def start(self, tag, attrib):
3731 events.append("start")
3733 assertEqual("TAG", tag)
3735 events.append("end")
3736 assertEqual("TAG", tag)
3738 return Element("DONE")
3740 parser = self.XMLParser(target=Target())
3741 tree = self.etree.ElementTree()
3742 tree.parse(BytesIO("<TAG/>"), parser=parser)
3744 self.assertEqual("DONE", tree.getroot().tag)
3745 self.assertEqual(["start", "end"], events)
3747 def test_parser_target_attrib(self):
3748 assertEqual = self.assertEqual
3751 class Target(object):
3752 def start(self, tag, attrib):
3753 events.append("start-" + tag)
3754 for name, value in attrib.items():
3755 assertEqual(tag + name, value)
3757 events.append("end-" + tag)
3761 parser = self.XMLParser(target=Target())
3763 parser.feed('<root a="roota" b="rootb"><sub c="subc"/></root>')
3764 done = parser.close()
3766 self.assertEqual("DONE", done)
3767 self.assertEqual(["start-root", "start-sub", "end-sub", "end-root"],
3770 def test_parser_target_data(self):
3772 class Target(object):
3773 def start(self, tag, attrib):
3774 events.append("start-" + tag)
3776 events.append("end-" + tag)
3777 def data(self, data):
3778 events.append("data-" + data)
3782 parser = self.XMLParser(target=Target())
3784 parser.feed('<root>A<sub/>B</root>')
3785 done = parser.close()
3787 self.assertEqual("DONE", done)
3788 self.assertEqual(["start-root", "data-A", "start-sub",
3789 "end-sub", "data-B", "end-root"],
3792 def test_parser_target_entity(self):
3794 class Target(object):
3797 def _flush_data(self):
3799 events.append("data-" + ''.join(self._data))
3801 def start(self, tag, attrib):
3803 events.append("start-" + tag)
3806 events.append("end-" + tag)
3807 def data(self, data):
3808 self._data.append(data)
3813 parser = self.XMLParser(target=Target())
3817 <!ELEMENT root (sub*)>
3818 <!ELEMENT sub (#PCDATA)>
3819 <!ENTITY ent "an entity">
3822 parser.feed(dtd+'<root><sub/><sub>this is &ent;</sub><sub/></root>')
3823 done = parser.close()
3825 self.assertEqual("DONE", done)
3826 self.assertEqual(["start-root", "start-sub", "end-sub", "start-sub",
3827 "data-this is an entity",
3828 "end-sub", "start-sub", "end-sub", "end-root"],
3831 required_versions_ET['test_parser_target_entity_unknown'] = (1,3)
3832 def test_parser_target_entity_unknown(self):
3834 class Target(object):
3837 def _flush_data(self):
3839 events.append("data-" + ''.join(self._data))
3841 def start(self, tag, attrib):
3843 events.append("start-" + tag)
3846 events.append("end-" + tag)
3847 def data(self, data):
3848 self._data.append(data)
3853 parser = self.XMLParser(target=Target())
3856 parser.feed('<root><sub/><sub>some &ent;</sub><sub/></root>')
3859 self.assertRaises(self.etree.ParseError, feed)
3861 @et_needs_pyversion(3, 8, 0, 'alpha', 4)
3862 def test_parser_target_start_end_ns(self):
3863 class Builder(list):
3864 def start(self, tag, attrib):
3865 self.append(("start", tag))
3867 self.append(("end", tag))
3868 def data(self, text):
3870 def pi(self, target, data):
3871 self.append(("pi", target, data))
3872 def comment(self, data):
3873 self.append(("comment", data))
3874 def start_ns(self, prefix, uri):
3875 self.append(("start-ns", prefix, uri))
3876 def end_ns(self, prefix):
3877 self.append(("end-ns", prefix))
3880 parser = self.etree.XMLParser(target=builder)
3881 parser.feed(textwrap.dedent("""\
3884 <root xmlns='namespace'>
3885 <element key='value'>text</element>
3886 <element>text</element>tail
3890 self.assertEqual(builder, [
3891 ('pi', 'pi', 'data'),
3892 ('comment', ' comment '),
3893 ('start-ns', '', 'namespace'),
3894 ('start', '{namespace}root'),
3895 ('start', '{namespace}element'),
3896 ('end', '{namespace}element'),
3897 ('start', '{namespace}element'),
3898 ('end', '{namespace}element'),
3899 ('start', '{namespace}empty-element'),
3900 ('end', '{namespace}empty-element'),
3901 ('end', '{namespace}root'),
3905 @et_needs_pyversion(3, 8, 0, 'alpha', 4)
3906 def test_parser_target_end_ns(self):
3907 class Builder(list):
3908 def end_ns(self, prefix):
3909 self.append(("end-ns", prefix))
3912 parser = self.etree.XMLParser(target=builder)
3913 parser.feed(textwrap.dedent("""\
3916 <root xmlns='namespace' xmlns:p='pns'>
3917 <element key='value'>text</element>
3918 <p:element>text</p:element>tail
3922 self.assertEqual(builder, [
3927 def test_treebuilder(self):
3928 builder = self.etree.TreeBuilder()
3929 el = builder.start("root", {'a':'A', 'b':'B'})
3930 self.assertEqual("root", el.tag)
3931 self.assertEqual({'a':'A', 'b':'B'}, el.attrib)
3932 builder.data("ROOTTEXT")
3933 el = builder.start("child", {'x':'X', 'y':'Y'})
3934 self.assertEqual("child", el.tag)
3935 self.assertEqual({'x':'X', 'y':'Y'}, el.attrib)
3936 builder.data("CHILDTEXT")
3937 el = builder.end("child")
3938 self.assertEqual("child", el.tag)
3939 self.assertEqual({'x':'X', 'y':'Y'}, el.attrib)
3940 self.assertEqual("CHILDTEXT", el.text)
3941 self.assertEqual(None, el.tail)
3942 builder.data("CHILDTAIL")
3943 root = builder.end("root")
3945 self.assertEqual("root", root.tag)
3946 self.assertEqual("ROOTTEXT", root.text)
3947 self.assertEqual("CHILDTEXT", root[0].text)
3948 self.assertEqual("CHILDTAIL", root[0].tail)
3950 def test_treebuilder_target(self):
3951 parser = self.XMLParser(target=self.etree.TreeBuilder())
3952 parser.feed('<root>ROOTTEXT<child>CHILDTEXT</child>CHILDTAIL</root>')
3953 root = parser.close()
3955 self.assertEqual("root", root.tag)
3956 self.assertEqual("ROOTTEXT", root.text)
3957 self.assertEqual("CHILDTEXT", root[0].text)
3958 self.assertEqual("CHILDTAIL", root[0].tail)
3960 @et_needs_pyversion(3, 8, 0, 'alpha', 4)
3961 def test_treebuilder_comment(self):
3963 b = ET.TreeBuilder()
3964 self.assertEqual(b.comment('ctext').tag, ET.Comment)
3965 self.assertEqual(b.comment('ctext').text, 'ctext')
3967 b = ET.TreeBuilder(comment_factory=ET.Comment)
3968 self.assertEqual(b.comment('ctext').tag, ET.Comment)
3969 self.assertEqual(b.comment('ctext').text, 'ctext')
3971 #b = ET.TreeBuilder(comment_factory=len)
3972 #self.assertEqual(b.comment('ctext'), len('ctext'))
3974 @et_needs_pyversion(3, 8, 0, 'alpha', 4)
3975 def test_treebuilder_pi(self):
3977 is_lxml = ET.__name__ == 'lxml.etree'
3979 b = ET.TreeBuilder()
3980 self.assertEqual(b.pi('target', None).tag, ET.PI)
3982 self.assertEqual(b.pi('target', None).target, 'target')
3984 self.assertEqual(b.pi('target', None).text, 'target')
3986 b = ET.TreeBuilder(pi_factory=ET.PI)
3987 self.assertEqual(b.pi('target').tag, ET.PI)
3989 self.assertEqual(b.pi('target').target, "target")
3991 self.assertEqual(b.pi('target').text, "target")
3992 self.assertEqual(b.pi('pitarget', ' text ').tag, ET.PI)
3994 self.assertEqual(b.pi('pitarget', ' text ').target, "pitarget")
3995 self.assertEqual(b.pi('pitarget', ' text ').text, " text ")
3997 self.assertEqual(b.pi('pitarget', ' text ').text, "pitarget text ")
3999 #b = ET.TreeBuilder(pi_factory=lambda target, text: (len(target), text))
4000 #self.assertEqual(b.pi('target'), (len('target'), None))
4001 #self.assertEqual(b.pi('pitarget', ' text '), (len('pitarget'), ' text '))
4003 def test_late_tail(self):
4004 # Issue #37399: The tail of an ignored comment could overwrite the text before it.
4006 class TreeBuilderSubclass(ET.TreeBuilder):
4009 if ET.__name__ == 'lxml.etree':
4010 def assert_content(a):
4011 self.assertEqual(a.text, "text")
4012 self.assertEqual(a[0].tail, "tail")
4014 def assert_content(a):
4015 self.assertEqual(a.text, "texttail")
4017 xml = "<a>text<!-- comment -->tail</a>"
4018 a = ET.fromstring(xml)
4021 parser = ET.XMLParser(target=TreeBuilderSubclass())
4026 xml = "<a>text<?pi data?>tail</a>"
4027 a = ET.fromstring(xml)
4030 xml = "<a>text<?pi data?>tail</a>"
4031 parser = ET.XMLParser(target=TreeBuilderSubclass())
4036 @et_needs_pyversion(3, 8, 0, 'alpha', 4)
4037 def test_late_tail_mix_pi_comments(self):
4038 # Issue #37399: The tail of an ignored comment could overwrite the text before it.
4039 # Test appending tails to comments/pis.
4041 class TreeBuilderSubclass(ET.TreeBuilder):
4044 xml = "<a>text<?pi1?> <!-- comment -->\n<?pi2?>tail</a>"
4045 parser = ET.XMLParser(target=ET.TreeBuilder(insert_comments=True, insert_pis=False))
4048 self.assertEqual(a[0].text, ' comment ')
4049 self.assertEqual(a[0].tail, '\ntail')
4050 self.assertEqual(a.text, "text ")
4052 parser = ET.XMLParser(target=TreeBuilderSubclass(insert_comments=True, insert_pis=False))
4055 self.assertEqual(a[0].text, ' comment ')
4056 self.assertEqual(a[0].tail, '\ntail')
4057 self.assertEqual(a.text, "text ")
4059 xml = "<a>text<!-- comment -->\n<?pi data?>tail</a>"
4060 parser = ET.XMLParser(target=ET.TreeBuilder(insert_pis=True, insert_comments=False))
4063 self.assertEqual(a[0].text[-4:], 'data')
4064 self.assertEqual(a[0].tail, 'tail')
4065 self.assertEqual(a.text, "text\n")
4067 parser = ET.XMLParser(target=TreeBuilderSubclass(insert_pis=True, insert_comments=False))
4070 self.assertEqual(a[0].text[-4:], 'data')
4071 self.assertEqual(a[0].tail, 'tail')
4072 self.assertEqual(a.text, "text\n")
4076 def _writeElement(self, element, encoding='us-ascii'):
4077 """Write out element for comparison.
4079 data = self.etree.tostring(element, encoding=encoding)
4080 return canonicalize(data)
4082 def _writeElementFile(self, element, encoding='us-ascii'):
4083 """Write out element for comparison, using real file.
4085 ElementTree = self.etree.ElementTree
4086 with tmpfile() as filename:
4087 with open(filename, 'wb') as f:
4088 tree = ElementTree(element=element)
4089 tree.write(f, encoding=encoding)
4090 with open(filename, 'rb') as f:
4092 return canonicalize(data)
4094 def assertXML(self, expected, element, encoding='us-ascii'):
4095 """Writes element out and checks whether it is expected.
4097 Does this two ways; once using BytesIO, once using a real file.
4099 if isinstance(expected, unicode):
4100 expected = expected.encode(encoding)
4101 self.assertEqual(expected, self._writeElement(element, encoding))
4102 self.assertEqual(expected, self._writeElementFile(element, encoding))
4104 def assertEncodingDeclaration(self, result, encoding):
4105 "Checks if the result XML byte string specifies the encoding."
4106 enc_re = r"<\?xml[^>]+ encoding=[\"']([^\"']+)[\"']"
4107 if isinstance(result, str):
4108 has_encoding = re.compile(enc_re).match
4110 has_encoding = re.compile(_bytes(enc_re)).match
4111 self.assertTrue(has_encoding(result))
4112 result_encoding = has_encoding(result).group(1)
4113 self.assertEqual(result_encoding.upper(), encoding.upper())
4115 def _rootstring(self, tree):
4116 return self.etree.tostring(tree.getroot()).replace(
4117 _bytes(' '), _bytes('')).replace(_bytes('\n'), _bytes(''))
4119 def _check_element_tree(self, tree):
4120 self._check_element(tree.getroot())
4122 def _check_element(self, element):
4123 self.assertTrue(hasattr(element, 'tag'))
4124 self.assertTrue(hasattr(element, 'attrib'))
4125 self.assertTrue(hasattr(element, 'text'))
4126 self.assertTrue(hasattr(element, 'tail'))
4127 self._check_string(element.tag)
4128 self._check_mapping(element.attrib)
4129 if element.text is not None:
4130 self._check_string(element.text)
4131 if element.tail is not None:
4132 self._check_string(element.tail)
4134 def _check_string(self, string):
4137 self.assertEqual(1, len(char))
4138 new_string = string + ""
4139 new_string = string + " "
4142 def _check_mapping(self, mapping):
4144 keys = mapping.keys()
4145 values = mapping.values()
4146 items = mapping.items()
4149 mapping["key"] = "value"
4150 self.assertEqual("value", mapping["key"])
4153 class _ElementSlicingTest(unittest.TestCase):
4156 def _elem_tags(self, elemlist):
4157 return [e.tag for e in elemlist]
4159 def _subelem_tags(self, elem):
4160 return self._elem_tags(list(elem))
4162 def _make_elem_with_children(self, numchildren):
4163 """Create an Element with a tag 'a', with the given amount of children
4164 named 'a0', 'a1' ... and so on.
4167 e = self.etree.Element('a')
4168 for i in range(numchildren):
4169 self.etree.SubElement(e, 'a%s' % i)
4172 def test_getslice_single_index(self):
4173 e = self._make_elem_with_children(10)
4175 self.assertEqual(e[1].tag, 'a1')
4176 self.assertEqual(e[-2].tag, 'a8')
4178 self.assertRaises(IndexError, lambda: e[12])
4179 self.assertRaises(IndexError, lambda: e[-12])
4181 def test_getslice_range(self):
4182 e = self._make_elem_with_children(6)
4184 self.assertEqual(self._elem_tags(e[3:]), ['a3', 'a4', 'a5'])
4185 self.assertEqual(self._elem_tags(e[3:6]), ['a3', 'a4', 'a5'])
4186 self.assertEqual(self._elem_tags(e[3:16]), ['a3', 'a4', 'a5'])
4187 self.assertEqual(self._elem_tags(e[3:5]), ['a3', 'a4'])
4188 self.assertEqual(self._elem_tags(e[3:-1]), ['a3', 'a4'])
4189 self.assertEqual(self._elem_tags(e[:2]), ['a0', 'a1'])
4191 def test_getslice_steps(self):
4192 e = self._make_elem_with_children(10)
4194 self.assertEqual(self._elem_tags(e[8:10:1]), ['a8', 'a9'])
4195 self.assertEqual(self._elem_tags(e[::3]), ['a0', 'a3', 'a6', 'a9'])
4196 self.assertEqual(self._elem_tags(e[::8]), ['a0', 'a8'])
4197 self.assertEqual(self._elem_tags(e[1::8]), ['a1', 'a9'])
4198 self.assertEqual(self._elem_tags(e[3::sys.maxsize]), ['a3'])
4199 self.assertEqual(self._elem_tags(e[3::sys.maxsize<<64]), ['a3'])
4201 def test_getslice_negative_steps(self):
4202 e = self._make_elem_with_children(4)
4204 self.assertEqual(self._elem_tags(e[::-1]), ['a3', 'a2', 'a1', 'a0'])
4205 self.assertEqual(self._elem_tags(e[::-2]), ['a3', 'a1'])
4206 self.assertEqual(self._elem_tags(e[3::-sys.maxsize]), ['a3'])
4207 self.assertEqual(self._elem_tags(e[3::-sys.maxsize-1]), ['a3'])
4208 self.assertEqual(self._elem_tags(e[3::-sys.maxsize<<64]), ['a3'])
4210 def test_delslice(self):
4211 e = self._make_elem_with_children(4)
4213 self.assertEqual(self._subelem_tags(e), ['a2', 'a3'])
4215 e = self._make_elem_with_children(4)
4217 self.assertEqual(self._subelem_tags(e), [])
4219 e = self._make_elem_with_children(4)
4221 self.assertEqual(self._subelem_tags(e), [])
4223 e = self._make_elem_with_children(4)
4225 self.assertEqual(self._subelem_tags(e), ['a0', 'a2'])
4227 e = self._make_elem_with_children(4)
4229 self.assertEqual(self._subelem_tags(e), ['a0', 'a2'])
4231 e = self._make_elem_with_children(2)
4233 self.assertEqual(self._subelem_tags(e), ['a1'])
4235 def test_setslice_single_index(self):
4236 e = self._make_elem_with_children(4)
4237 e[1] = self.etree.Element('b')
4238 self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'a2', 'a3'])
4240 e[-2] = self.etree.Element('c')
4241 self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'c', 'a3'])
4243 with self.assertRaises(IndexError):
4244 e[5] = self.etree.Element('d')
4245 with self.assertRaises(IndexError):
4246 e[-5] = self.etree.Element('d')
4247 self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'c', 'a3'])
4249 def test_setslice_range(self):
4250 e = self._make_elem_with_children(4)
4251 e[1:3] = [self.etree.Element('b%s' % i) for i in range(2)]
4252 self.assertEqual(self._subelem_tags(e), ['a0', 'b0', 'b1', 'a3'])
4254 e = self._make_elem_with_children(4)
4255 e[1:3] = [self.etree.Element('b')]
4256 self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'a3'])
4258 e = self._make_elem_with_children(4)
4259 e[1:3] = [self.etree.Element('b%s' % i) for i in range(3)]
4260 self.assertEqual(self._subelem_tags(e), ['a0', 'b0', 'b1', 'b2', 'a3'])
4262 def test_setslice_steps(self):
4263 e = self._make_elem_with_children(6)
4264 e[1:5:2] = [self.etree.Element('b%s' % i) for i in range(2)]
4265 self.assertEqual(self._subelem_tags(e), ['a0', 'b0', 'a2', 'b1', 'a4', 'a5'])
4267 e = self._make_elem_with_children(6)
4268 with self.assertRaises(ValueError):
4269 e[1:5:2] = [self.etree.Element('b')]
4270 with self.assertRaises(ValueError):
4271 e[1:5:2] = [self.etree.Element('b%s' % i) for i in range(3)]
4272 with self.assertRaises(ValueError):
4274 self.assertEqual(self._subelem_tags(e), ['a0', 'a1', 'a2', 'a3', 'a4', 'a5'])
4276 e = self._make_elem_with_children(4)
4277 e[1::sys.maxsize] = [self.etree.Element('b')]
4278 self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'a2', 'a3'])
4279 e[1::sys.maxsize<<64] = [self.etree.Element('c')]
4280 self.assertEqual(self._subelem_tags(e), ['a0', 'c', 'a2', 'a3'])
4282 def test_setslice_negative_steps(self):
4283 e = self._make_elem_with_children(4)
4284 e[2:0:-1] = [self.etree.Element('b%s' % i) for i in range(2)]
4285 self.assertEqual(self._subelem_tags(e), ['a0', 'b1', 'b0', 'a3'])
4287 e = self._make_elem_with_children(4)
4288 with self.assertRaises(ValueError):
4289 e[2:0:-1] = [self.etree.Element('b')]
4290 with self.assertRaises(ValueError):
4291 e[2:0:-1] = [self.etree.Element('b%s' % i) for i in range(3)]
4292 with self.assertRaises(ValueError):
4294 self.assertEqual(self._subelem_tags(e), ['a0', 'a1', 'a2', 'a3'])
4296 e = self._make_elem_with_children(4)
4297 e[1::-sys.maxsize] = [self.etree.Element('b')]
4298 self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'a2', 'a3'])
4299 e[1::-sys.maxsize-1] = [self.etree.Element('c')]
4300 self.assertEqual(self._subelem_tags(e), ['a0', 'c', 'a2', 'a3'])
4301 e[1::-sys.maxsize<<64] = [self.etree.Element('d')]
4302 self.assertEqual(self._subelem_tags(e), ['a0', 'd', 'a2', 'a3'])
4305 class _XMLPullParserTest(unittest.TestCase):
4308 def _close_and_return_root(self, parser):
4309 if 'ElementTree' in self.etree.__name__:
4310 # ElementTree's API is a bit unwieldy in Py3.4
4311 root = parser._close_and_return_root()
4313 root = parser.close()
4316 def _feed(self, parser, data, chunk_size=None):
4317 if chunk_size is None:
4320 for i in range(0, len(data), chunk_size):
4321 parser.feed(data[i:i+chunk_size])
4323 def assert_events(self, parser, expected, max_events=None):
4325 [(event, (elem.tag, elem.text))
4326 for event, elem in islice(parser.read_events(), max_events)],
4329 def assert_event_tuples(self, parser, expected, max_events=None):
4331 list(islice(parser.read_events(), max_events)),
4334 def assert_event_tags(self, parser, expected, max_events=None):
4335 events = islice(parser.read_events(), max_events)
4336 self.assertEqual([(action, elem.tag) for action, elem in events],
4339 def test_simple_xml(self):
4340 for chunk_size in (None, 1, 5):
4341 #with self.subTest(chunk_size=chunk_size):
4342 parser = self.etree.XMLPullParser()
4343 self.assert_event_tags(parser, [])
4344 self._feed(parser, "<!-- comment -->\n", chunk_size)
4345 self.assert_event_tags(parser, [])
4347 "<root>\n <element key='value'>text</element",
4349 self.assert_event_tags(parser, [])
4350 self._feed(parser, ">\n", chunk_size)
4351 self.assert_event_tags(parser, [('end', 'element')])
4352 self._feed(parser, "<element>text</element>tail\n", chunk_size)
4353 self._feed(parser, "<empty-element/>\n", chunk_size)
4354 self.assert_event_tags(parser, [
4356 ('end', 'empty-element'),
4358 self._feed(parser, "</root>\n", chunk_size)
4359 self.assert_event_tags(parser, [('end', 'root')])
4360 root = self._close_and_return_root(parser)
4361 self.assertEqual(root.tag, 'root')
4363 def test_feed_while_iterating(self):
4364 parser = self.etree.XMLPullParser()
4365 it = parser.read_events()
4366 self._feed(parser, "<root>\n <element key='value'>text</element>\n")
4367 action, elem = next(it)
4368 self.assertEqual((action, elem.tag), ('end', 'element'))
4369 self._feed(parser, "</root>\n")
4370 action, elem = next(it)
4371 self.assertEqual((action, elem.tag), ('end', 'root'))
4372 with self.assertRaises(StopIteration):
4375 def test_simple_xml_with_ns(self):
4376 parser = self.etree.XMLPullParser()
4377 self.assert_event_tags(parser, [])
4378 self._feed(parser, "<!-- comment -->\n")
4379 self.assert_event_tags(parser, [])
4380 self._feed(parser, "<root xmlns='namespace'>\n")
4381 self.assert_event_tags(parser, [])
4382 self._feed(parser, "<element key='value'>text</element")
4383 self.assert_event_tags(parser, [])
4384 self._feed(parser, ">\n")
4385 self.assert_event_tags(parser, [('end', '{namespace}element')])
4386 self._feed(parser, "<element>text</element>tail\n")
4387 self._feed(parser, "<empty-element/>\n")
4388 self.assert_event_tags(parser, [
4389 ('end', '{namespace}element'),
4390 ('end', '{namespace}empty-element'),
4392 self._feed(parser, "</root>\n")
4393 self.assert_event_tags(parser, [('end', '{namespace}root')])
4394 root = self._close_and_return_root(parser)
4395 self.assertEqual(root.tag, '{namespace}root')
4397 def test_ns_events(self):
4398 parser = self.etree.XMLPullParser(events=('start-ns', 'end-ns'))
4399 self._feed(parser, "<!-- comment -->\n")
4400 self._feed(parser, "<root xmlns='namespace'>\n")
4402 list(parser.read_events()),
4403 [('start-ns', ('', 'namespace'))])
4404 self._feed(parser, "<element key='value'>text</element")
4405 self._feed(parser, ">\n")
4406 self._feed(parser, "<element>text</element>tail\n")
4407 self._feed(parser, "<empty-element/>\n")
4408 self._feed(parser, "</root>\n")
4409 self.assertEqual(list(parser.read_events()), [('end-ns', None)])
4412 def test_ns_events_end_ns_only(self):
4413 parser = self.etree.XMLPullParser(events=['end-ns'])
4414 self._feed(parser, "<!-- comment -->\n")
4415 self._feed(parser, "<root xmlns='namespace' xmlns:a='abc' xmlns:b='xyz'>\n")
4416 self.assertEqual(list(parser.read_events()), [])
4417 self._feed(parser, "<a:element key='value'>text</a:element")
4418 self._feed(parser, ">\n")
4419 self._feed(parser, "<b:element>text</b:element>tail\n")
4420 self._feed(parser, "<empty-element/>\n")
4421 self.assertEqual(list(parser.read_events()), [])
4422 self._feed(parser, "</root>\n")
4423 self.assertEqual(list(parser.read_events()), [
4430 @et_needs_pyversion(3,8)
4431 def test_ns_events_start(self):
4432 parser = self.etree.XMLPullParser(events=('start-ns', 'start', 'end'))
4433 self._feed(parser, "<tag xmlns='abc' xmlns:p='xyz'>\n")
4434 self.assert_event_tuples(parser, [
4435 ('start-ns', ('', 'abc')),
4436 ('start-ns', ('p', 'xyz')),
4438 self.assert_event_tags(parser, [
4439 ('start', '{abc}tag'),
4442 self._feed(parser, "<child />\n")
4443 self.assert_event_tags(parser, [
4444 ('start', '{abc}child'),
4445 ('end', '{abc}child'),
4448 self._feed(parser, "</tag>\n")
4450 self.assert_event_tags(parser, [
4451 ('end', '{abc}tag'),
4454 @et_needs_pyversion(3,8)
4455 def test_ns_events_start_end(self):
4456 parser = self.etree.XMLPullParser(events=('start-ns', 'start', 'end', 'end-ns'))
4457 self._feed(parser, "<tag xmlns='abc' xmlns:p='xyz'>\n")
4458 self.assert_event_tuples(parser, [
4459 ('start-ns', ('', 'abc')),
4460 ('start-ns', ('p', 'xyz')),
4462 self.assert_event_tags(parser, [
4463 ('start', '{abc}tag'),
4466 self._feed(parser, "<child />\n")
4467 self.assert_event_tags(parser, [
4468 ('start', '{abc}child'),
4469 ('end', '{abc}child'),
4472 self._feed(parser, "</tag>\n")
4474 self.assert_event_tags(parser, [
4475 ('end', '{abc}tag'),
4477 self.assert_event_tuples(parser, [
4482 def test_events(self):
4483 parser = self.etree.XMLPullParser(events=())
4484 self._feed(parser, "<root/>\n")
4485 self.assert_event_tags(parser, [])
4487 parser = self.etree.XMLPullParser(events=('start', 'end'))
4488 self._feed(parser, "<!-- text here -->\n")
4489 self.assert_events(parser, [])
4491 parser = self.etree.XMLPullParser(events=('start', 'end'))
4492 self._feed(parser, "<root>\n")
4493 self.assert_event_tags(parser, [('start', 'root')])
4494 self._feed(parser, "<element key='value'>text</element")
4495 self.assert_event_tags(parser, [('start', 'element')])
4496 self._feed(parser, ">\n")
4497 self.assert_event_tags(parser, [('end', 'element')])
4499 "<element xmlns='foo'>text<empty-element/></element>tail\n")
4500 self.assert_event_tags(parser, [
4501 ('start', '{foo}element'),
4502 ('start', '{foo}empty-element'),
4503 ('end', '{foo}empty-element'),
4504 ('end', '{foo}element'),
4506 self._feed(parser, "</root>")
4507 root = self._close_and_return_root(parser)
4508 self.assert_event_tags(parser, [('end', 'root')])
4509 self.assertEqual(root.tag, 'root')
4511 parser = self.etree.XMLPullParser(events=('start',))
4512 self._feed(parser, "<!-- comment -->\n")
4513 self.assert_event_tags(parser, [])
4514 self._feed(parser, "<root>\n")
4515 self.assert_event_tags(parser, [('start', 'root')])
4516 self._feed(parser, "<element key='value'>text</element")
4517 self.assert_event_tags(parser, [('start', 'element')])
4518 self._feed(parser, ">\n")
4519 self.assert_event_tags(parser, [])
4521 "<element xmlns='foo'>text<empty-element/></element>tail\n")
4522 self.assert_event_tags(parser, [
4523 ('start', '{foo}element'),
4524 ('start', '{foo}empty-element'),
4526 self._feed(parser, "</root>")
4527 root = self._close_and_return_root(parser)
4528 self.assertEqual(root.tag, 'root')
4530 @et_needs_pyversion(3, 8, 0, 'alpha', 4)
4531 def test_events_comment(self):
4532 parser = self.etree.XMLPullParser(events=('start', 'comment', 'end'))
4533 self._feed(parser, "<!-- text here -->\n")
4534 self.assert_events(parser, [('comment', (self.etree.Comment, ' text here '))])
4535 self._feed(parser, "<!-- more text here -->\n")
4536 self.assert_events(parser, [('comment', (self.etree.Comment, ' more text here '))])
4537 self._feed(parser, "<root-tag>text")
4538 self.assert_event_tags(parser, [('start', 'root-tag')])
4539 self._feed(parser, "<!-- inner comment-->\n")
4540 self.assert_events(parser, [('comment', (self.etree.Comment, ' inner comment'))])
4541 self._feed(parser, "</root-tag>\n")
4542 self.assert_event_tags(parser, [('end', 'root-tag')])
4543 self._feed(parser, "<!-- outer comment -->\n")
4544 self.assert_events(parser, [('comment', (self.etree.Comment, ' outer comment '))])
4546 parser = self.etree.XMLPullParser(events=('comment',))
4547 self._feed(parser, "<!-- text here -->\n")
4548 self.assert_events(parser, [('comment', (self.etree.Comment, ' text here '))])
4550 @et_needs_pyversion(3, 8, 0, 'alpha', 4)
4551 def test_events_pi(self):
4552 # Note: lxml's PIs have target+text, ET's PIs have both in "text"
4553 parser = self.etree.XMLPullParser(events=('start', 'pi', 'end'))
4554 self._feed(parser, "<?pitarget?>\n")
4555 self.assert_event_tags(parser, [('pi', self.etree.PI)])
4556 parser = self.etree.XMLPullParser(events=('pi',))
4557 self._feed(parser, "<?pitarget some text ?>\n")
4558 self.assert_event_tags(parser, [('pi', self.etree.PI)])
4560 def test_events_sequence(self):
4561 # Test that events can be some sequence that's not just a tuple or list
4562 eventset = {'end', 'start'}
4563 parser = self.etree.XMLPullParser(events=eventset)
4564 self._feed(parser, "<foo>bar</foo>")
4565 self.assert_event_tags(parser, [('start', 'foo'), ('end', 'foo')])
4567 class DummyIter(object):
4569 self.events = iter(['start', 'end', 'start-ns'])
4573 return next(self.events)
4575 return next(self.events)
4577 parser = self.etree.XMLPullParser(events=DummyIter())
4578 self._feed(parser, "<foo>bar</foo>")
4579 self.assert_event_tags(parser, [('start', 'foo'), ('end', 'foo')])
4581 def test_unknown_event(self):
4582 with self.assertRaises(ValueError):
4583 self.etree.XMLPullParser(events=('start', 'end', 'bogus'))
4586 class _C14NTest(unittest.TestCase):
4590 if not hasattr(unittest.TestCase, 'subTest'):
4592 def subTest(self, name, **kwargs):
4595 except unittest.SkipTest:
4597 except Exception as e:
4598 print("Subtest {} failed: {}".format(name, e))
4601 def _canonicalize(self, input_file, **options):
4602 return self.etree.canonicalize(from_file=input_file, **options)
4605 # simple roundtrip tests (from c14n.py)
4607 def c14n_roundtrip(self, xml, **options):
4608 return self.etree.canonicalize(xml, **options)
4610 def test_simple_roundtrip(self):
4611 c14n_roundtrip = self.c14n_roundtrip
4613 self.assertEqual(c14n_roundtrip("<doc/>"), '<doc></doc>')
4614 self.assertEqual(c14n_roundtrip("<doc xmlns='uri'/>"), # FIXME
4615 '<doc xmlns="uri"></doc>')
4616 self.assertEqual(c14n_roundtrip("<prefix:doc xmlns:prefix='uri'/>"),
4617 '<prefix:doc xmlns:prefix="uri"></prefix:doc>')
4618 self.assertEqual(c14n_roundtrip("<doc xmlns:prefix='uri'><prefix:bar/></doc>"),
4619 '<doc><prefix:bar xmlns:prefix="uri"></prefix:bar></doc>')
4620 self.assertEqual(c14n_roundtrip("<elem xmlns:wsu='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' />"),
4624 self.assertEqual(c14n_roundtrip("<doc>Hello, world!<!-- Comment 1 --></doc>"),
4625 '<doc>Hello, world!</doc>')
4626 self.assertEqual(c14n_roundtrip("<value>2</value>"),
4628 self.assertEqual(c14n_roundtrip('<compute><![CDATA[value>"0" && value<"10" ?"valid":"error"]]></compute>'),
4629 '<compute>value>"0" && value<"10" ?"valid":"error"</compute>')
4630 self.assertEqual(c14n_roundtrip('''<compute expr='value>"0" && value<"10" ?"valid":"error"'>valid</compute>'''),
4631 '<compute expr="value>"0" && value<"10" ?"valid":"error"">valid</compute>')
4632 self.assertEqual(c14n_roundtrip("<norm attr=' '   
	 ' '/>"),
4633 '<norm attr=" \' 
	 \' "></norm>')
4634 self.assertEqual(c14n_roundtrip("<normNames attr=' A   
	 B '/>"),
4635 '<normNames attr=" A 
	 B "></normNames>')
4636 self.assertEqual(c14n_roundtrip("<normId id=' '   
	 ' '/>"),
4637 '<normId id=" \' 
	 \' "></normId>')
4639 # fragments from PJ's tests
4640 #self.assertEqual(c14n_roundtrip("<doc xmlns:x='http://example.com/x' xmlns='http://example.com/default'><b y:a1='1' xmlns='http://example.com/default' a3='3' xmlns:y='http://example.com/y' y:a2='2'/></doc>"),
4641 #'<doc xmlns:x="http://example.com/x"><b xmlns:y="http://example.com/y" a3="3" y:a1="1" y:a2="2"></b></doc>')
4643 def test_c14n_exclusion(self):
4644 c14n_roundtrip = self.c14n_roundtrip
4645 xml = textwrap.dedent("""\
4646 <root xmlns:x="http://example.com/x">
4657 c14n_roundtrip(xml, strip_text=True),
4659 '<a xmlns:x="http://example.com/x" x:attr="attrx"><b>abtext</b></a>'
4661 '<c><x:d xmlns:x="http://example.com/x">dtext</x:d></c>'
4664 c14n_roundtrip(xml, strip_text=True, exclude_attrs=['{http://example.com/x}attr']),
4666 '<a><b>abtext</b></a>'
4668 '<c><x:d xmlns:x="http://example.com/x">dtext</x:d></c>'
4671 c14n_roundtrip(xml, strip_text=True, exclude_tags=['{http://example.com/x}d']),
4673 '<a xmlns:x="http://example.com/x" x:attr="attrx"><b>abtext</b></a>'
4678 c14n_roundtrip(xml, strip_text=True, exclude_attrs=['{http://example.com/x}attr'],
4679 exclude_tags=['{http://example.com/x}d']),
4681 '<a><b>abtext</b></a>'
4686 c14n_roundtrip(xml, strip_text=True, exclude_tags=['a', 'b']),
4688 '<c><x:d xmlns:x="http://example.com/x">dtext</x:d></c>'
4691 c14n_roundtrip(xml, exclude_tags=['a', 'b']),
4696 ' <x:d xmlns:x="http://example.com/x">dtext</x:d>\n'
4700 c14n_roundtrip(xml, strip_text=True, exclude_tags=['{http://example.com/x}d', 'b']),
4702 '<a xmlns:x="http://example.com/x" x:attr="attrx"></a>'
4706 c14n_roundtrip(xml, exclude_tags=['{http://example.com/x}d', 'b']),
4708 ' <a xmlns:x="http://example.com/x" x:attr="attrx">\n'
4718 # basic method=c14n tests from the c14n 2.0 specification. uses
4719 # test files under xmltestdata/c14n-20.
4721 # note that this uses generated C14N versions of the standard ET.write
4722 # output, not roundtripped C14N (see above).
4724 def test_xml_c14n2(self):
4725 datadir = os.path.join(os.path.dirname(__file__), "c14n-20")
4726 full_path = partial(os.path.join, datadir)
4728 files = [filename[:-4] for filename in sorted(os.listdir(datadir))
4729 if filename.endswith('.xml')]
4731 filename for filename in files
4732 if filename.startswith('in')
4736 # <c14n2:PrefixRewrite>sequential</c14n2:PrefixRewrite>
4737 option.tag.split('}')[-1]: ((option.text or '').strip(), option)
4738 for option in self.etree.parse(full_path(filename) + ".xml").getroot()
4740 for filename in files
4741 if filename.startswith('c14n')
4746 (filename, configs[filename.rsplit('_', 1)[-1]])
4747 for filename in files
4748 if filename.startswith('out_%s_' % input_file)
4749 and filename.rsplit('_', 1)[-1] in configs
4751 for input_file in input_files
4754 # Make sure we found all test cases.
4755 self.assertEqual(30, len([
4756 output_file for output_files in tests.values()
4757 for output_file in output_files]))
4759 def get_option(config, option_name, default=None):
4760 return config.get(option_name, (default, ()))[0]
4762 for input_file, output_files in tests.items():
4763 for output_file, config in output_files:
4764 keep_comments = get_option(
4765 config, 'IgnoreComments') == 'true' # no, it's right :)
4766 strip_text = get_option(
4767 config, 'TrimTextNodes') == 'true'
4768 rewrite_prefixes = get_option(
4769 config, 'PrefixRewrite') == 'sequential'
4770 if 'QNameAware' in config:
4772 "{%s}%s" % (el.get('NS'), el.get('Name'))
4773 for el in config['QNameAware'][1].findall(
4774 '{http://www.w3.org/2010/xml-c14n2}QualifiedAttr')
4777 "{%s}%s" % (el.get('NS'), el.get('Name'))
4778 for el in config['QNameAware'][1].findall(
4779 '{http://www.w3.org/2010/xml-c14n2}Element')
4782 qtags = qattrs = None
4784 # Build subtest description from config.
4785 config_descr = ','.join(
4786 "%s=%s" % (name, value or ','.join(c.tag.split('}')[-1] for c in children))
4787 for name, (value, children) in sorted(config.items())
4790 with self.subTest("{}({})".format(output_file, config_descr)):
4791 if input_file == 'inNsRedecl' and not rewrite_prefixes:
4793 "Redeclared namespace handling is not supported in {}".format(
4795 if input_file == 'inNsSuperfluous' and not rewrite_prefixes:
4797 "Redeclared namespace handling is not supported in {}".format(
4799 if 'QNameAware' in config and config['QNameAware'][1].find(
4800 '{http://www.w3.org/2010/xml-c14n2}XPathElement') is not None:
4802 "QName rewriting in XPath text is not supported in {}".format(
4805 f = full_path(input_file + ".xml")
4806 if input_file == 'inC14N5':
4807 # Hack: avoid setting up external entity resolution in the parser.
4808 with open(full_path('world.txt'), 'rb') as entity_file:
4809 with open(f, 'rb') as f:
4810 f = io.BytesIO(f.read().replace(b'&ent2;', entity_file.read().strip()))
4812 text = self._canonicalize(
4814 with_comments=keep_comments,
4815 strip_text=strip_text,
4816 rewrite_prefixes=rewrite_prefixes,
4817 qname_aware_tags=qtags, qname_aware_attrs=qattrs)
4819 with io.open(full_path(output_file + ".xml"), 'r', encoding='utf8') as f:
4821 if input_file == 'inC14N3' and self.etree is not etree:
4822 # FIXME: cET resolves default attributes but ET does not!
4823 expected = expected.replace(' attr="default"', '')
4824 text = text.replace(' attr="default"', '')
4825 self.assertEqual(expected, text)
4829 class ETreeTestCase(_ETreeTestCaseBase):
4832 class ETreePullTestCase(_XMLPullParserTest):
4835 class ETreeElementSlicingTest(_ElementSlicingTest):
4838 class ETreeC14NTest(_C14NTest):
4841 class ETreeC14N2WriteTest(ETreeC14NTest):
4842 def _canonicalize(self, input_file, with_comments=True, strip_text=False,
4843 rewrite_prefixes=False, qname_aware_tags=None, qname_aware_attrs=None,
4845 if rewrite_prefixes or qname_aware_attrs or qname_aware_tags:
4846 self.skipTest("C14N 2.0 feature not supported with ElementTree.write()")
4848 parser = self.etree.XMLParser(attribute_defaults=True, collect_ids=False)
4849 tree = self.etree.parse(input_file, parser)
4852 out, method='c14n2',
4853 with_comments=with_comments, strip_text=strip_text,
4855 return out.getvalue().decode('utf8')
4857 class ETreeC14N2TostringTest(ETreeC14NTest):
4858 def _canonicalize(self, input_file, with_comments=True, strip_text=False,
4859 rewrite_prefixes=False, qname_aware_tags=None, qname_aware_attrs=None,
4861 if rewrite_prefixes or qname_aware_attrs or qname_aware_tags:
4862 self.skipTest("C14N 2.0 feature not supported with ElementTree.tostring()")
4864 parser = self.etree.XMLParser(attribute_defaults=True, collect_ids=False)
4865 tree = self.etree.parse(input_file, parser)
4866 return self.etree.tostring(
4867 tree, method='c14n2',
4868 with_comments=with_comments, strip_text=strip_text,
4869 **options).decode('utf8')
4873 class ElementTreeTestCase(_ETreeTestCaseBase):
4877 def setUpClass(cls):
4878 if sys.version_info >= (3, 9):
4881 # ElementTree warns about getiterator() in recent Pythons
4882 warnings.filterwarnings(
4884 r'This method will be removed.*\.iter\(\).*instead',
4885 PendingDeprecationWarning)
4888 ElementTreeTestCase,
4889 ElementTreeTestCase.required_versions_ET, ET_VERSION)
4891 if hasattr(ElementTree, 'XMLPullParser'):
4892 class ElementTreePullTestCase(_XMLPullParserTest):
4895 ElementTreePullTestCase = None
4897 if hasattr(ElementTree, 'canonicalize'):
4898 class ElementTreeC14NTest(_C14NTest):
4901 ElementTreeC14NTest = None
4903 class ElementTreeElementSlicingTest(_ElementSlicingTest):
4908 class CElementTreeTestCase(_ETreeTestCaseBase):
4909 etree = cElementTree
4912 CElementTreeTestCase,
4913 CElementTreeTestCase.required_versions_cET, CET_VERSION)
4915 class CElementTreeElementSlicingTest(_ElementSlicingTest):
4916 etree = cElementTree
4920 suite = unittest.TestSuite()
4922 suite.addTests([unittest.makeSuite(ETreeTestCase)])
4923 suite.addTests([unittest.makeSuite(ETreePullTestCase)])
4924 suite.addTests([unittest.makeSuite(ETreeElementSlicingTest)])
4925 suite.addTests([unittest.makeSuite(ETreeC14NTest)])
4926 suite.addTests([unittest.makeSuite(ETreeC14N2WriteTest)])
4927 suite.addTests([unittest.makeSuite(ETreeC14N2TostringTest)])
4929 suite.addTests([unittest.makeSuite(ElementTreeTestCase)])
4930 if ElementTreePullTestCase:
4931 suite.addTests([unittest.makeSuite(ElementTreePullTestCase)])
4932 if ElementTreeC14NTest:
4933 suite.addTests([unittest.makeSuite(ElementTreeC14NTest)])
4934 suite.addTests([unittest.makeSuite(ElementTreeElementSlicingTest)])
4936 suite.addTests([unittest.makeSuite(CElementTreeTestCase)])
4937 suite.addTests([unittest.makeSuite(CElementTreeElementSlicingTest)])
4940 if __name__ == '__main__':
4941 print('to test use test.py %s' % __file__)