78d8964dc4e60135799e41aaa66dd33f38778e69
[platform/upstream/python-lxml.git] / src / lxml / tests / test_elementtree.py
1 # -*- coding: utf-8 -*-
2
3 """
4 Tests for the ElementTree API
5
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.
9 """
10
11 from __future__ import absolute_import
12
13 import copy
14 import io
15 import operator
16 import os
17 import re
18 import sys
19 import textwrap
20 import unittest
21 from contextlib import contextmanager
22 from functools import wraps, partial
23 from itertools import islice
24
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
30 )
31
32 if cElementTree is not None and (CET_VERSION <= (1,0,7) or sys.version_info[0] >= 3):
33     cElementTree = None
34
35 if ElementTree is not None:
36     print("Comparing with ElementTree %s" % getattr(ElementTree, "VERSION", "?"))
37
38 if cElementTree is not None:
39     print("Comparing with cElementTree %s" % getattr(cElementTree, "VERSION", "?"))
40
41
42 def et_needs_pyversion(*version):
43     def wrap(method):
44         @wraps(method)
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)
49         return testfunc
50     return wrap
51
52
53 class _ETreeTestCaseBase(HelperTestCase):
54     etree = None
55     required_versions_ET = {}
56     required_versions_cET = {}
57
58     def XMLParser(self, **kwargs):
59         try:
60             XMLParser = self.etree.XMLParser
61         except AttributeError:
62             assert 'ElementTree' in self.etree.__name__
63             XMLParser = self.etree.TreeBuilder
64         return XMLParser(**kwargs)
65
66     try:
67         HelperTestCase.assertRegex
68     except AttributeError:
69         def assertRegex(self, *args, **kwargs):
70             return self.assertRegexpMatches(*args, **kwargs)
71
72     @et_needs_pyversion(3, 6)
73     def test_interface(self):
74         # Test element tree interface.
75
76         def check_string(string):
77             len(string)
78             for char in string:
79                 self.assertEqual(len(char), 1,
80                         msg="expected one-character string, got %r" % char)
81             new_string = string + ""
82             new_string = string + " "
83             string[:0]
84
85         def check_mapping(mapping):
86             len(mapping)
87             keys = mapping.keys()
88             items = mapping.items()
89             for key in keys:
90                 item = mapping[key]
91             mapping["key"] = "value"
92             self.assertEqual(mapping["key"], "value",
93                     msg="expected value string, got %r" % mapping["key"])
94
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)
103
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)
110             for elem in element:
111                 check_element(elem)
112
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")
122
123         # Make sure all standard element methods exist.
124
125         def check_method(method):
126             self.assertTrue(hasattr(method, '__call__'),
127                     msg="%s not callable" % method)
128
129         check_method(element.append)
130         check_method(element.extend)
131         check_method(element.insert)
132         check_method(element.remove)
133         check_method(element.getchildren)
134         check_method(element.find)
135         check_method(element.iterfind)
136         check_method(element.findall)
137         check_method(element.findtext)
138         check_method(element.clear)
139         check_method(element.get)
140         check_method(element.set)
141         check_method(element.keys)
142         check_method(element.items)
143         check_method(element.iter)
144         check_method(element.itertext)
145         check_method(element.getiterator)
146
147         # These methods return an iterable. See bug 6472.
148
149         def check_iter(it):
150             check_method(it.next if IS_PYTHON2 else it.__next__)
151
152         check_iter(element.iterfind("tag"))
153         check_iter(element.iterfind("*"))
154         check_iter(tree.iterfind("tag"))
155         check_iter(tree.iterfind("*"))
156
157         # These aliases are provided:
158
159         # not an alias in lxml
160         #self.assertEqual(self.etree.XML, self.etree.fromstring)
161         self.assertEqual(self.etree.PI, self.etree.ProcessingInstruction)
162
163     def test_element(self):
164         for i in range(10):
165             e = self.etree.Element('foo')
166             self.assertEqual(e.tag, 'foo')
167             self.assertEqual(e.text, None)
168             self.assertEqual(e.tail, None)
169
170     def test_simple(self):
171         Element = self.etree.Element
172
173         root = Element('root')
174         root.append(Element('one'))
175         root.append(Element('two'))
176         root.append(Element('three'))
177         self.assertEqual(3, len(root))
178         self.assertEqual('one', root[0].tag)
179         self.assertEqual('two', root[1].tag)
180         self.assertEqual('three', root[2].tag)
181         self.assertRaises(IndexError, operator.getitem, root, 3)
182
183     # test weird dictionary interaction leading to segfault previously
184     def test_weird_dict_interaction(self):
185         root = self.etree.Element('root')
186         self.assertEqual(root.tag, "root")
187         add = self.etree.ElementTree(file=BytesIO('<foo>Foo</foo>'))
188         self.assertEqual(add.getroot().tag, "foo")
189         self.assertEqual(add.getroot().text, "Foo")
190         root.append(self.etree.Element('baz'))
191         self.assertEqual(root.tag, "root")
192         self.assertEqual(root[0].tag, "baz")
193
194     def test_subelement(self):
195         Element = self.etree.Element
196         SubElement = self.etree.SubElement
197
198         root = Element('root')
199         SubElement(root, 'one')
200         SubElement(root, 'two')
201         SubElement(root, 'three')
202         self.assertEqual(3, len(root))
203         self.assertEqual('one', root[0].tag)
204         self.assertEqual('two', root[1].tag)
205         self.assertEqual('three', root[2].tag)
206
207     def test_element_contains(self):
208         Element = self.etree.Element
209         SubElement = self.etree.SubElement
210
211         root1 = Element('root')
212         SubElement(root1, 'one')
213         self.assertTrue(root1[0] in root1)
214
215         root2 = Element('root')
216         SubElement(root2, 'two')
217         SubElement(root2, 'three')
218         self.assertTrue(root2[0] in root2)
219         self.assertTrue(root2[1] in root2)
220
221         self.assertFalse(root1[0] in root2)
222         self.assertFalse(root2[0] in root1)
223         self.assertFalse(None in root2)
224
225     def test_element_indexing_with_text(self):
226         ElementTree = self.etree.ElementTree
227
228         f = BytesIO('<doc>Test<one>One</one></doc>')
229         doc = ElementTree(file=f)
230         root = doc.getroot()
231         self.assertEqual(1, len(root))
232         self.assertEqual('one', root[0].tag)
233         self.assertRaises(IndexError, operator.getitem, root, 1)
234
235     def test_element_indexing_with_text2(self):
236         ElementTree = self.etree.ElementTree
237
238         f = BytesIO('<doc><one>One</one><two>Two</two>hm<three>Three</three></doc>')
239         doc = ElementTree(file=f)
240         root = doc.getroot()
241         self.assertEqual(3, len(root))
242         self.assertEqual('one', root[0].tag)
243         self.assertEqual('two', root[1].tag)
244         self.assertEqual('three', root[2].tag)
245
246     def test_element_indexing_only_text(self):
247         ElementTree = self.etree.ElementTree
248
249         f = BytesIO('<doc>Test</doc>')
250         doc = ElementTree(file=f)
251         root = doc.getroot()
252         self.assertEqual(0, len(root))
253
254     def test_element_indexing_negative(self):
255         Element = self.etree.Element
256         SubElement = self.etree.SubElement
257         a = Element('a')
258         b = SubElement(a, 'b')
259         c = SubElement(a, 'c')
260         d = SubElement(a, 'd')
261         self.assertEqual(d, a[-1])
262         self.assertEqual(c, a[-2])
263         self.assertEqual(b, a[-3])
264         self.assertRaises(IndexError, operator.getitem, a, -4)
265         a[-1] = e = Element('e')
266         self.assertEqual(e, a[-1])
267         del a[-1]
268         self.assertEqual(2, len(a))
269
270     def test_elementtree(self):
271         ElementTree = self.etree.ElementTree
272
273         f = BytesIO('<doc><one>One</one><two>Two</two></doc>')
274         doc = ElementTree(file=f)
275         root = doc.getroot()
276         self.assertEqual(2, len(root))
277         self.assertEqual('one', root[0].tag)
278         self.assertEqual('two', root[1].tag)
279
280     def test_text(self):
281         ElementTree = self.etree.ElementTree
282
283         f = BytesIO('<doc>This is a text</doc>')
284         doc = ElementTree(file=f)
285         root = doc.getroot()
286         self.assertEqual('This is a text', root.text)
287
288     def test_text_empty(self):
289         ElementTree = self.etree.ElementTree
290
291         f = BytesIO('<doc></doc>')
292         doc = ElementTree(file=f)
293         root = doc.getroot()
294         self.assertEqual(None, root.text)
295
296     def test_text_other(self):
297         ElementTree = self.etree.ElementTree
298
299         f = BytesIO('<doc><one>One</one></doc>')
300         doc = ElementTree(file=f)
301         root = doc.getroot()
302         self.assertEqual(None, root.text)
303         self.assertEqual('One', root[0].text)
304
305     def test_text_escape_in(self):
306         ElementTree = self.etree.ElementTree
307
308         f = BytesIO('<doc>This is &gt; than a text</doc>')
309         doc = ElementTree(file=f)
310         root = doc.getroot()
311         self.assertEqual('This is > than a text', root.text)
312
313     def test_text_escape_out(self):
314         Element = self.etree.Element
315
316         a = Element("a")
317         a.text = "<>&"
318         self.assertXML(_bytes('<a>&lt;&gt;&amp;</a>'),
319                        a)
320
321     def test_text_escape_tostring(self):
322         tostring = self.etree.tostring
323         Element  = self.etree.Element
324
325         a = Element("a")
326         a.text = "<>&"
327         self.assertEqual(_bytes('<a>&lt;&gt;&amp;</a>'),
328                          tostring(a))
329
330     def test_text_str_subclass(self):
331         Element = self.etree.Element
332
333         class strTest(str):
334             pass
335
336         a = Element("a")
337         a.text = strTest("text")
338         self.assertXML(_bytes('<a>text</a>'),
339                        a)
340
341     def test_tail(self):
342         ElementTree = self.etree.ElementTree
343
344         f = BytesIO('<doc>This is <i>mixed</i> content.</doc>')
345         doc = ElementTree(file=f)
346         root = doc.getroot()
347         self.assertEqual(1, len(root))
348         self.assertEqual('This is ', root.text)
349         self.assertEqual(None, root.tail)
350         self.assertEqual('mixed', root[0].text)
351         self.assertEqual(' content.', root[0].tail)
352
353     def test_tail_str_subclass(self):
354         Element = self.etree.Element
355         SubElement = self.etree.SubElement
356
357         class strTest(str):
358             pass
359
360         a = Element("a")
361         SubElement(a, "t").tail = strTest("tail")
362         self.assertXML(_bytes('<a><t></t>tail</a>'),
363                        a)
364
365     def _test_del_tail(self):
366         # this is discouraged for ET compat, should not be tested...
367         XML = self.etree.XML
368
369         root = XML(_bytes('<doc>This is <i>mixed</i> content.</doc>'))
370         self.assertEqual(1, len(root))
371         self.assertEqual('This is ', root.text)
372         self.assertEqual(None, root.tail)
373         self.assertEqual('mixed', root[0].text)
374         self.assertEqual(' content.', root[0].tail)
375
376         del root[0].tail
377
378         self.assertEqual(1, len(root))
379         self.assertEqual('This is ', root.text)
380         self.assertEqual(None, root.tail)
381         self.assertEqual('mixed', root[0].text)
382         self.assertEqual(None, root[0].tail)
383
384         root[0].tail = "TAIL"
385
386         self.assertEqual(1, len(root))
387         self.assertEqual('This is ', root.text)
388         self.assertEqual(None, root.tail)
389         self.assertEqual('mixed', root[0].text)
390         self.assertEqual('TAIL', root[0].tail)
391
392     def test_ElementTree(self):
393         Element = self.etree.Element
394         ElementTree = self.etree.ElementTree
395
396         el = Element('hoi')
397         doc = ElementTree(el)
398         root = doc.getroot()
399         self.assertEqual(None, root.text)
400         self.assertEqual('hoi', root.tag)
401
402     def test_attrib(self):
403         ElementTree = self.etree.ElementTree
404
405         f = BytesIO('<doc one="One" two="Two"/>')
406         doc = ElementTree(file=f)
407         root = doc.getroot()
408         self.assertEqual('One', root.attrib['one'])
409         self.assertEqual('Two', root.attrib['two'])
410         self.assertRaises(KeyError, operator.getitem, root.attrib, 'three')
411
412     def test_attrib_get(self):
413         ElementTree = self.etree.ElementTree
414
415         f = BytesIO('<doc one="One" two="Two"/>')
416         doc = ElementTree(file=f)
417         root = doc.getroot()
418         self.assertEqual('One', root.attrib.get('one'))
419         self.assertEqual('Two', root.attrib.get('two'))
420         self.assertEqual(None, root.attrib.get('three'))
421         self.assertEqual('foo', root.attrib.get('three', 'foo'))
422
423     def test_attrib_dict(self):
424         ElementTree = self.etree.ElementTree
425
426         f = BytesIO('<doc one="One" two="Two"/>')
427         doc = ElementTree(file=f)
428         root = doc.getroot()
429         attrib = dict(root.attrib)
430         self.assertEqual('One', attrib['one'])
431         self.assertEqual('Two', attrib['two'])
432         self.assertRaises(KeyError, operator.getitem, attrib, 'three')
433
434     def test_attrib_copy(self):
435         ElementTree = self.etree.ElementTree
436
437         f = BytesIO('<doc one="One" two="Two"/>')
438         doc = ElementTree(file=f)
439         root = doc.getroot()
440         attrib = copy.copy(root.attrib)
441         self.assertEqual('One', attrib['one'])
442         self.assertEqual('Two', attrib['two'])
443         self.assertRaises(KeyError, operator.getitem, attrib, 'three')
444
445     def test_attrib_deepcopy(self):
446         ElementTree = self.etree.ElementTree
447
448         f = BytesIO('<doc one="One" two="Two"/>')
449         doc = ElementTree(file=f)
450         root = doc.getroot()
451         attrib = copy.deepcopy(root.attrib)
452         self.assertEqual('One', attrib['one'])
453         self.assertEqual('Two', attrib['two'])
454         self.assertRaises(KeyError, operator.getitem, attrib, 'three')
455
456     def test_attributes_get(self):
457         ElementTree = self.etree.ElementTree
458
459         f = BytesIO('<doc one="One" two="Two"/>')
460         doc = ElementTree(file=f)
461         root = doc.getroot()
462         self.assertEqual('One', root.get('one'))
463         self.assertEqual('Two', root.get('two'))
464         self.assertEqual(None, root.get('three'))
465         self.assertEqual('foo', root.get('three', 'foo'))
466
467     def test_attrib_clear(self):
468         XML = self.etree.XML
469
470         root = XML(_bytes('<doc one="One" two="Two"/>'))
471         self.assertEqual('One', root.get('one'))
472         self.assertEqual('Two', root.get('two'))
473         root.attrib.clear()
474         self.assertEqual(None, root.get('one'))
475         self.assertEqual(None, root.get('two'))
476
477     def test_attrib_set_clear(self):
478         Element = self.etree.Element
479
480         root = Element("root", one="One")
481         root.set("two", "Two")
482         self.assertEqual('One', root.get('one'))
483         self.assertEqual('Two', root.get('two'))
484         root.attrib.clear()
485         self.assertEqual(None, root.get('one'))
486         self.assertEqual(None, root.get('two'))
487
488     def test_attrib_ns_clear(self):
489         Element = self.etree.Element
490         SubElement = self.etree.SubElement
491
492         attribNS = '{http://foo/bar}x'
493
494         parent = Element('parent')
495         parent.set(attribNS, 'a')
496         child = SubElement(parent, 'child')
497         child.set(attribNS, 'b')
498
499         self.assertEqual('a', parent.get(attribNS))
500         self.assertEqual('b', child.get(attribNS))
501
502         parent.clear()
503         self.assertEqual(None, parent.get(attribNS))
504         self.assertEqual('b', child.get(attribNS))
505
506     def test_attrib_pop(self):
507         ElementTree = self.etree.ElementTree
508
509         f = BytesIO('<doc one="One" two="Two"/>')
510         doc = ElementTree(file=f)
511         root = doc.getroot()
512         self.assertEqual('One', root.attrib['one'])
513         self.assertEqual('Two', root.attrib['two'])
514
515         self.assertEqual('One', root.attrib.pop('one'))
516
517         self.assertEqual(None, root.attrib.get('one'))
518         self.assertEqual('Two', root.attrib['two'])
519
520     def test_attrib_pop_unknown(self):
521         root = self.etree.XML(_bytes('<doc one="One" two="Two"/>'))
522         self.assertRaises(KeyError, root.attrib.pop, 'NONE')
523
524         self.assertEqual('One', root.attrib['one'])
525         self.assertEqual('Two', root.attrib['two'])
526
527     def test_attrib_pop_default(self):
528         root = self.etree.XML(_bytes('<doc one="One" two="Two"/>'))
529         self.assertEqual('Three', root.attrib.pop('three', 'Three'))
530
531     def test_attrib_pop_empty_default(self):
532         root = self.etree.XML(_bytes('<doc/>'))
533         self.assertEqual('Three', root.attrib.pop('three', 'Three'))
534
535     def test_attrib_pop_invalid_args(self):
536         root = self.etree.XML(_bytes('<doc one="One" two="Two"/>'))
537         self.assertRaises(TypeError, root.attrib.pop, 'One', None, None)
538
539     def test_attribute_update_dict(self):
540         XML = self.etree.XML
541
542         root = XML(_bytes('<doc alpha="Alpha" beta="Beta"/>'))
543         items = list(root.attrib.items())
544         items.sort()
545         self.assertEqual(
546             [('alpha', 'Alpha'), ('beta', 'Beta')],
547             items)
548
549         root.attrib.update({'alpha' : 'test', 'gamma' : 'Gamma'})
550
551         items = list(root.attrib.items())
552         items.sort()
553         self.assertEqual(
554             [('alpha', 'test'), ('beta', 'Beta'), ('gamma', 'Gamma')],
555             items)
556
557     def test_attribute_update_sequence(self):
558         XML = self.etree.XML
559
560         root = XML(_bytes('<doc alpha="Alpha" beta="Beta"/>'))
561         items = list(root.attrib.items())
562         items.sort()
563         self.assertEqual(
564             [('alpha', 'Alpha'), ('beta', 'Beta')],
565             items)
566
567         root.attrib.update({'alpha' : 'test', 'gamma' : 'Gamma'}.items())
568
569         items = list(root.attrib.items())
570         items.sort()
571         self.assertEqual(
572             [('alpha', 'test'), ('beta', 'Beta'), ('gamma', 'Gamma')],
573             items)
574
575     def test_attribute_update_iter(self):
576         XML = self.etree.XML
577
578         root = XML(_bytes('<doc alpha="Alpha" beta="Beta"/>'))
579         items = list(root.attrib.items())
580         items.sort()
581         self.assertEqual(
582             [('alpha', 'Alpha'), ('beta', 'Beta')],
583             items)
584
585         root.attrib.update(iter({'alpha' : 'test', 'gamma' : 'Gamma'}.items()))
586
587         items = list(root.attrib.items())
588         items.sort()
589         self.assertEqual(
590             [('alpha', 'test'), ('beta', 'Beta'), ('gamma', 'Gamma')],
591             items)
592
593     def test_attribute_update_attrib(self):
594         XML = self.etree.XML
595
596         root = XML(_bytes('<doc alpha="Alpha" beta="Beta"/>'))
597         items = list(root.attrib.items())
598         items.sort()
599         self.assertEqual(
600             [('alpha', 'Alpha'), ('beta', 'Beta')],
601                                                   items)
602
603         other = XML(_bytes('<doc alpha="test" gamma="Gamma"/>'))
604         root.attrib.update(other.attrib)
605
606         items = list(root.attrib.items())
607         items.sort()
608         self.assertEqual(
609             [('alpha', 'test'), ('beta', 'Beta'), ('gamma', 'Gamma')],
610                                                                      items)
611
612     def test_attribute_keys(self):
613         XML = self.etree.XML
614
615         root = XML(_bytes('<doc alpha="Alpha" beta="Beta" gamma="Gamma"/>'))
616         keys = list(root.attrib.keys())
617         keys.sort()
618         self.assertEqual(['alpha', 'beta', 'gamma'], keys)
619
620     def test_attribute_keys2(self):
621         XML = self.etree.XML
622
623         root = XML(_bytes('<doc alpha="Alpha" beta="Beta" gamma="Gamma"/>'))
624         keys = list(root.keys())
625         keys.sort()
626         self.assertEqual(['alpha', 'beta', 'gamma'], keys)
627
628     def test_attribute_items2(self):
629         XML = self.etree.XML
630
631         root = XML(_bytes('<doc alpha="Alpha" beta="Beta" gamma="Gamma"/>'))
632         items = list(root.items())
633         items.sort()
634         self.assertEqual(
635             [('alpha','Alpha'), ('beta','Beta'), ('gamma','Gamma')],
636             items)
637
638     def test_attribute_keys_ns(self):
639         XML = self.etree.XML
640
641         root = XML(_bytes('<foo bar="Bar" xmlns:ns="http://ns.codespeak.net/test" ns:baz="Baz" />'))
642         keys = list(root.keys())
643         keys.sort()
644         self.assertEqual(['bar', '{http://ns.codespeak.net/test}baz'],
645                           keys)
646
647     def test_attribute_values(self):
648         XML = self.etree.XML
649
650         root = XML(_bytes('<doc alpha="Alpha" beta="Beta" gamma="Gamma"/>'))
651         values = list(root.attrib.values())
652         values.sort()
653         self.assertEqual(['Alpha', 'Beta', 'Gamma'], values)
654
655     def test_attribute_values_ns(self):
656         XML = self.etree.XML
657
658         root = XML(_bytes('<foo bar="Bar" xmlns:ns="http://ns.codespeak.net/test" ns:baz="Baz" />'))
659         values = list(root.attrib.values())
660         values.sort()
661         self.assertEqual(
662             ['Bar', 'Baz'], values)
663
664     def test_attribute_items(self):
665         XML = self.etree.XML
666
667         root = XML(_bytes('<doc alpha="Alpha" beta="Beta" gamma="Gamma"/>'))
668         items = list(root.attrib.items())
669         items.sort()
670         self.assertEqual([
671             ('alpha', 'Alpha'),
672             ('beta', 'Beta'),
673             ('gamma', 'Gamma'),
674             ], 
675             items)
676
677     def test_attribute_items_ns(self):
678         XML = self.etree.XML
679
680         root = XML(_bytes('<foo bar="Bar" xmlns:ns="http://ns.codespeak.net/test" ns:baz="Baz" />'))
681         items = list(root.attrib.items())
682         items.sort()
683         self.assertEqual(
684             [('bar', 'Bar'), ('{http://ns.codespeak.net/test}baz', 'Baz')],
685             items)
686
687     def test_attribute_str(self):
688         XML = self.etree.XML
689
690         expected = "{'{http://ns.codespeak.net/test}baz': 'Baz', 'bar': 'Bar'}"
691         alternative = "{'bar': 'Bar', '{http://ns.codespeak.net/test}baz': 'Baz'}"
692
693         root = XML(_bytes('<foo bar="Bar" xmlns:ns="http://ns.codespeak.net/test" ns:baz="Baz" />'))
694         try:
695             self.assertEqual(expected, str(root.attrib))
696         except AssertionError:
697             self.assertEqual(alternative, str(root.attrib))
698
699     def test_attribute_contains(self):
700         XML = self.etree.XML
701
702         root = XML(_bytes('<foo bar="Bar" xmlns:ns="http://ns.codespeak.net/test" ns:baz="Baz" />'))
703         self.assertEqual(
704             True, 'bar' in root.attrib)
705         self.assertEqual(
706             False, 'baz' in root.attrib)
707         self.assertEqual(
708             False, 'hah' in root.attrib)
709         self.assertEqual(
710             True,
711             '{http://ns.codespeak.net/test}baz' in root.attrib)
712
713     def test_attribute_set(self):
714         Element = self.etree.Element
715
716         root = Element("root")
717         root.set("attr", "TEST")
718         self.assertEqual("TEST", root.get("attr"))
719
720     def test_attrib_as_attrib(self):
721         Element = self.etree.Element
722
723         root = Element("root")
724         root.set("attr", "TEST")
725         self.assertEqual("TEST", root.attrib["attr"])
726
727         root2 = Element("root2", root.attrib)
728         self.assertEqual("TEST", root2.attrib["attr"])
729
730     def test_attribute_iterator(self):
731         XML = self.etree.XML
732
733         root = XML(_bytes('<doc alpha="Alpha" beta="Beta" gamma="Gamma" />'))
734         result = []
735         for key in root.attrib:
736             result.append(key)
737         result.sort()
738         self.assertEqual(['alpha', 'beta', 'gamma'], result)
739
740     def test_attribute_manipulation(self):
741         Element = self.etree.Element
742
743         a = Element('a')
744         a.attrib['foo'] = 'Foo'
745         a.attrib['bar'] = 'Bar'
746         self.assertEqual('Foo', a.attrib['foo'])
747         del a.attrib['foo']
748         self.assertRaises(KeyError, operator.getitem, a.attrib, 'foo')
749
750     def test_del_attribute_ns(self):
751         Element = self.etree.Element
752
753         a = Element('a')
754         a.attrib['{http://a/}foo'] = 'Foo'
755         a.attrib['{http://a/}bar'] = 'Bar'
756         self.assertEqual(None, a.get('foo'))
757         self.assertEqual('Foo', a.get('{http://a/}foo'))
758         self.assertEqual('Foo', a.attrib['{http://a/}foo'])
759
760         self.assertRaises(KeyError, operator.delitem, a.attrib, 'foo')
761         self.assertEqual('Foo', a.attrib['{http://a/}foo'])
762
763         del a.attrib['{http://a/}foo']
764         self.assertRaises(KeyError, operator.getitem, a.attrib, 'foo')
765
766     def test_del_attribute_ns_parsed(self):
767         XML = self.etree.XML
768
769         a = XML(_bytes('<a xmlns:nsa="http://a/" nsa:foo="FooNS" foo="Foo" />'))
770
771         self.assertEqual('Foo', a.attrib['foo'])
772         self.assertEqual('FooNS', a.attrib['{http://a/}foo'])
773
774         del a.attrib['foo']
775         self.assertEqual('FooNS', a.attrib['{http://a/}foo'])
776         self.assertRaises(KeyError, operator.getitem, a.attrib, 'foo')
777         self.assertRaises(KeyError, operator.delitem, a.attrib, 'foo')
778
779         del a.attrib['{http://a/}foo']
780         self.assertRaises(KeyError, operator.getitem, a.attrib, '{http://a/}foo')
781         self.assertRaises(KeyError, operator.getitem, a.attrib, 'foo')
782
783         a = XML(_bytes('<a xmlns:nsa="http://a/" foo="Foo" nsa:foo="FooNS" />'))
784
785         self.assertEqual('Foo', a.attrib['foo'])
786         self.assertEqual('FooNS', a.attrib['{http://a/}foo'])
787
788         del a.attrib['foo']
789         self.assertEqual('FooNS', a.attrib['{http://a/}foo'])
790         self.assertRaises(KeyError, operator.getitem, a.attrib, 'foo')
791
792         del a.attrib['{http://a/}foo']
793         self.assertRaises(KeyError, operator.getitem, a.attrib, '{http://a/}foo')
794         self.assertRaises(KeyError, operator.getitem, a.attrib, 'foo')
795
796     def test_XML(self):
797         XML = self.etree.XML
798
799         root = XML(_bytes('<doc>This is a text.</doc>'))
800         self.assertEqual(0, len(root))
801         self.assertEqual('This is a text.', root.text)
802
803     def test_XMLID(self):
804         XMLID = self.etree.XMLID
805         XML   = self.etree.XML
806         xml_text = _bytes('''
807         <document>
808           <h1 id="chapter1">...</h1>
809           <p id="note1" class="note">...</p>
810           <p>Regular paragraph.</p>
811           <p xml:id="xmlid">XML:ID paragraph.</p>
812           <p id="warn1" class="warning">...</p>
813         </document>
814         ''')
815
816         root, dic = XMLID(xml_text)
817         root2 = XML(xml_text)
818         self.assertEqual(self._writeElement(root),
819                           self._writeElement(root2))
820         expected = {
821             "chapter1" : root[0],
822             "note1"    : root[1],
823             "warn1"    : root[4]
824             }
825         self.assertEqual(dic, expected)
826
827     def test_fromstring(self):
828         fromstring = self.etree.fromstring
829
830         root = fromstring('<doc>This is a text.</doc>')
831         self.assertEqual(0, len(root))
832         self.assertEqual('This is a text.', root.text)
833
834     required_versions_ET['test_fromstringlist'] = (1,3)
835     def test_fromstringlist(self):
836         fromstringlist = self.etree.fromstringlist
837
838         root = fromstringlist(["<do", "c>T", "hi", "s is",
839                                " a text.<", "/doc", ">"])
840         self.assertEqual(0, len(root))
841         self.assertEqual('This is a text.', root.text)
842
843     required_versions_ET['test_fromstringlist_characters'] = (1,3)
844     def test_fromstringlist_characters(self):
845         fromstringlist = self.etree.fromstringlist
846
847         root = fromstringlist(list('<doc>This is a text.</doc>'))
848         self.assertEqual(0, len(root))
849         self.assertEqual('This is a text.', root.text)
850
851     required_versions_ET['test_fromstringlist_single'] = (1,3)
852     def test_fromstringlist_single(self):
853         fromstringlist = self.etree.fromstringlist
854
855         root = fromstringlist(['<doc>This is a text.</doc>'])
856         self.assertEqual(0, len(root))
857         self.assertEqual('This is a text.', root.text)
858
859     def test_iselement(self):
860         iselement = self.etree.iselement
861         Element = self.etree.Element
862         ElementTree = self.etree.ElementTree
863         XML = self.etree.XML
864         Comment = self.etree.Comment
865         ProcessingInstruction = self.etree.ProcessingInstruction
866
867         el = Element('hoi')
868         self.assertTrue(iselement(el))
869
870         el2 = XML(_bytes('<foo/>'))
871         self.assertTrue(iselement(el2))
872
873         tree = ElementTree(element=Element('dag'))
874         self.assertTrue(not iselement(tree))
875         self.assertTrue(iselement(tree.getroot()))
876
877         c = Comment('test')
878         self.assertTrue(iselement(c))
879
880         p = ProcessingInstruction("test", "some text")
881         self.assertTrue(iselement(p))
882
883     def test_iteration(self):
884         XML = self.etree.XML
885
886         root = XML(_bytes('<doc><one/><two>Two</two>Hm<three/></doc>'))
887         result = []
888         for el in root:
889             result.append(el.tag)
890         self.assertEqual(['one', 'two', 'three'], result)
891
892     def test_iteration_empty(self):
893         XML = self.etree.XML
894
895         root = XML(_bytes('<doc></doc>'))
896         result = []
897         for el in root:
898             result.append(el.tag)
899         self.assertEqual([], result)
900
901     def test_iteration_text_only(self):
902         XML = self.etree.XML
903
904         root = XML(_bytes('<doc>Text</doc>'))
905         result = []
906         for el in root:
907             result.append(el.tag)
908         self.assertEqual([], result)
909
910     def test_iteration_set_tail_empty(self):
911         # this would cause a crash in the past
912         fromstring = self.etree.fromstring
913         root = fromstring('<html><p></p>x</html>')
914         for elem in root:
915             elem.tail = ''
916
917     def test_iteration_clear_tail(self):
918         # this would cause a crash in the past
919         fromstring = self.etree.fromstring
920         root = fromstring('<html><p></p>x</html>')
921         for elem in root:
922             elem.tail = None
923
924     def test_iteration_reversed(self):
925         XML = self.etree.XML
926         root = XML(_bytes('<doc><one/><two>Two</two>Hm<three/></doc>'))
927         result = []
928         for el in reversed(root):
929             result.append(el.tag)
930         self.assertEqual(['three', 'two', 'one'], result)
931
932     def test_iteration_subelement(self):
933         XML = self.etree.XML
934
935         root = XML(_bytes('<doc><one/><two>Two</two>Hm<three/></doc>'))
936         result = []
937         add = True
938         for el in root:
939             result.append(el.tag)
940             if add:
941                 self.etree.SubElement(root, 'four')
942                 add = False
943         self.assertEqual(['one', 'two', 'three', 'four'], result)
944
945     def test_iteration_del_child(self):
946         XML = self.etree.XML
947
948         root = XML(_bytes('<doc><one/><two>Two</two>Hm<three/></doc>'))
949         result = []
950         for el in root:
951             result.append(el.tag)
952             del root[-1]
953         self.assertEqual(['one', 'two'], result)
954
955     def test_iteration_double(self):
956         XML = self.etree.XML
957
958         root = XML(_bytes('<doc><one/><two/></doc>'))
959         result = []
960         for el0 in root:
961             result.append(el0.tag)
962             for el1 in root:
963                 result.append(el1.tag)
964         self.assertEqual(['one','one', 'two', 'two', 'one', 'two'], result)
965
966     required_versions_ET['test_itertext'] = (1,3)
967     def test_itertext(self):
968         # ET 1.3+
969         XML = self.etree.XML
970         root = XML(_bytes("<root>RTEXT<a></a>ATAIL<b/><c>CTEXT</c>CTAIL</root>"))
971
972         text = list(root.itertext())
973         self.assertEqual(["RTEXT", "ATAIL", "CTEXT", "CTAIL"],
974                           text)
975
976     required_versions_ET['test_itertext_child'] = (1,3)
977     def test_itertext_child(self):
978         # ET 1.3+
979         XML = self.etree.XML
980         root = XML(_bytes("<root>RTEXT<a></a>ATAIL<b/><c>CTEXT</c>CTAIL</root>"))
981
982         text = list(root[2].itertext())
983         self.assertEqual(["CTEXT"],
984                           text)
985
986     def test_findall(self):
987         XML = self.etree.XML
988         root = XML(_bytes('<a><b><c/></b><b/><c><b/></c></a>'))
989         self.assertEqual(len(list(root.findall("c"))), 1)
990         self.assertEqual(len(list(root.findall(".//c"))), 2)
991         self.assertEqual(len(list(root.findall(".//b"))), 3)
992         self.assertEqual(len(list(root.findall(".//b"))[0]), 1)
993         self.assertEqual(len(list(root.findall(".//b"))[1]), 0)
994         self.assertEqual(len(list(root.findall(".//b"))[2]), 0)
995
996     def test_findall_ns(self):
997         XML = self.etree.XML
998         root = XML(_bytes('<a xmlns:x="X" xmlns:y="Y"><x:b><c/></x:b><b/><c><x:b/><b/></c><b/></a>'))
999         self.assertEqual(len(list(root.findall(".//{X}b"))), 2)
1000         self.assertEqual(len(list(root.findall(".//b"))), 3)
1001         self.assertEqual(len(list(root.findall("b"))), 2)
1002
1003     @et_needs_pyversion(3, 8, 0, 'alpha', 4)
1004     def test_findall_wildcard(self):
1005         def summarize_list(l):
1006             return [el.tag for el in l]
1007
1008         root = self.etree.XML('''
1009             <a xmlns:x="X" xmlns:y="Y">
1010                 <x:b><c/></x:b>
1011                 <b/>
1012                 <c><x:b/><b/></c><y:b/>
1013             </a>''')
1014         root.append(self.etree.Comment('test'))
1015
1016         self.assertEqual(summarize_list(root.findall("{*}b")),
1017                          ['{X}b', 'b', '{Y}b'])
1018         self.assertEqual(summarize_list(root.findall("{*}c")),
1019                          ['c'])
1020         self.assertEqual(summarize_list(root.findall("{X}*")),
1021                          ['{X}b'])
1022         self.assertEqual(summarize_list(root.findall("{Y}*")),
1023                          ['{Y}b'])
1024         self.assertEqual(summarize_list(root.findall("{}*")),
1025                          ['b', 'c'])
1026         self.assertEqual(summarize_list(root.findall("{}b")),  # only for consistency
1027                          ['b'])
1028         self.assertEqual(summarize_list(root.findall("{}b")),
1029                          summarize_list(root.findall("b")))
1030         self.assertEqual(summarize_list(root.findall("{*}*")),
1031                          ['{X}b', 'b', 'c', '{Y}b'])
1032         self.assertEqual(summarize_list(root.findall("{*}*")
1033                          + ([] if self.etree is etree else [root[-1]])),
1034                          summarize_list(root.findall("*")))
1035
1036         self.assertEqual(summarize_list(root.findall(".//{*}b")),
1037                          ['{X}b', 'b', '{X}b', 'b', '{Y}b'])
1038         self.assertEqual(summarize_list(root.findall(".//{*}c")),
1039                          ['c', 'c'])
1040         self.assertEqual(summarize_list(root.findall(".//{X}*")),
1041                          ['{X}b', '{X}b'])
1042         self.assertEqual(summarize_list(root.findall(".//{Y}*")),
1043                          ['{Y}b'])
1044         self.assertEqual(summarize_list(root.findall(".//{}*")),
1045                          ['c', 'b', 'c', 'b'])
1046         self.assertEqual(summarize_list(root.findall(".//{}b")),
1047                          ['b', 'b'])
1048
1049     def test_element_with_attributes_keywords(self):
1050         Element = self.etree.Element
1051
1052         el = Element('tag', foo='Foo', bar='Bar')
1053         self.assertEqual('Foo', el.attrib['foo'])
1054         self.assertEqual('Bar', el.attrib['bar'])
1055
1056     def test_element_with_attributes(self):
1057         Element = self.etree.Element
1058
1059         el = Element('tag', {'foo': 'Foo', 'bar': 'Bar'})
1060         self.assertEqual('Foo', el.attrib['foo'])
1061         self.assertEqual('Bar', el.attrib['bar'])
1062
1063     def test_element_with_attributes_extra(self):
1064         Element = self.etree.Element
1065
1066         el = Element('tag', {'foo': 'Foo', 'bar': 'Bar'}, baz='Baz')
1067         self.assertEqual('Foo', el.attrib['foo'])
1068         self.assertEqual('Bar', el.attrib['bar'])
1069         self.assertEqual('Baz', el.attrib['baz'])
1070
1071     def test_element_with_attributes_extra_duplicate(self):
1072         Element = self.etree.Element
1073
1074         el = Element('tag', {'foo': 'Foo', 'bar': 'Bar'}, bar='Baz')
1075         self.assertEqual('Foo', el.attrib['foo'])
1076         self.assertEqual('Baz', el.attrib['bar'])
1077
1078     def test_element_with_attributes_ns(self):
1079         Element = self.etree.Element
1080
1081         el = Element('tag', {'{ns1}foo':'Foo', '{ns2}bar':'Bar'})
1082         self.assertEqual('Foo', el.attrib['{ns1}foo'])
1083         self.assertEqual('Bar', el.attrib['{ns2}bar'])
1084
1085     def test_subelement_with_attributes(self):
1086         Element =  self.etree.Element
1087         SubElement = self.etree.SubElement
1088
1089         el = Element('tag')
1090         SubElement(el, 'foo', {'foo':'Foo'}, baz="Baz")
1091         self.assertEqual("Baz", el[0].attrib['baz'])
1092         self.assertEqual('Foo', el[0].attrib['foo'])
1093
1094     def test_subelement_with_attributes_ns(self):
1095         Element = self.etree.Element
1096         SubElement = self.etree.SubElement
1097
1098         el = Element('tag')
1099         SubElement(el, 'foo', {'{ns1}foo':'Foo', '{ns2}bar':'Bar'})
1100         self.assertEqual('Foo', el[0].attrib['{ns1}foo'])
1101         self.assertEqual('Bar', el[0].attrib['{ns2}bar'])
1102
1103     def test_write(self):
1104         ElementTree = self.etree.ElementTree
1105         XML = self.etree.XML
1106
1107         for i in range(10):
1108             f = BytesIO() 
1109             root = XML(_bytes('<doc%s>This is a test.</doc%s>' % (i, i)))
1110             tree = ElementTree(element=root)
1111             tree.write(f)
1112             data = f.getvalue()
1113             self.assertEqual(
1114                 _bytes('<doc%s>This is a test.</doc%s>' % (i, i)),
1115                 canonicalize(data))
1116
1117     required_versions_ET['test_write_method_html'] = (1,3)
1118     def test_write_method_html(self):
1119         ElementTree = self.etree.ElementTree
1120         Element = self.etree.Element
1121         SubElement = self.etree.SubElement
1122
1123         html = Element('html')
1124         body = SubElement(html, 'body')
1125         p = SubElement(body, 'p')
1126         p.text = "html"
1127         SubElement(p, 'br').tail = "test"
1128
1129         tree = ElementTree(element=html)
1130         f = BytesIO() 
1131         tree.write(f, method="html")
1132         data = f.getvalue().replace(_bytes('\n'),_bytes(''))
1133
1134         self.assertEqual(_bytes('<html><body><p>html<br>test</p></body></html>'),
1135                           data)
1136
1137     required_versions_ET['test_write_method_text'] = (1,3)
1138     def test_write_method_text(self):
1139         ElementTree = self.etree.ElementTree
1140         Element = self.etree.Element
1141         SubElement = self.etree.SubElement
1142
1143         a = Element('a')
1144         a.text = "A"
1145         a.tail = "tail"
1146         b = SubElement(a, 'b')
1147         b.text = "B"
1148         b.tail = "TAIL"
1149         c = SubElement(a, 'c')
1150         c.text = "C"
1151
1152         tree = ElementTree(element=a)
1153         f = BytesIO() 
1154         tree.write(f, method="text")
1155         data = f.getvalue()
1156
1157         self.assertEqual(_bytes('ABTAILCtail'),
1158                           data)
1159
1160     def test_write_fail(self):
1161         ElementTree = self.etree.ElementTree
1162         XML = self.etree.XML
1163
1164         tree = ElementTree( XML(_bytes('<doc>This is a test.</doc>')) )
1165         self.assertRaises(IOError, tree.write,
1166                           "definitely////\\-\\nonexisting\\-\\////FILE")
1167
1168     # this could trigger a crash, apparently because the document
1169     # reference was prematurely garbage collected
1170     def test_crash(self):
1171         Element = self.etree.Element
1172
1173         element = Element('tag')
1174         for i in range(10):
1175             element.attrib['key'] = 'value'
1176             value = element.attrib['key']
1177             self.assertEqual(value, 'value')
1178
1179     # from doctest; for some reason this caused crashes too
1180     def test_write_ElementTreeDoctest(self):
1181         Element = self.etree.Element
1182         ElementTree = self.etree.ElementTree
1183
1184         f = BytesIO()
1185         for i in range(10):
1186             element = Element('tag%s' % i)
1187             self._check_element(element)
1188             tree = ElementTree(element)
1189             tree.write(f)
1190             self._check_element_tree(tree)
1191
1192     def test_subelement_reference(self):
1193         Element = self.etree.Element
1194         SubElement = self.etree.SubElement
1195
1196         el = Element('foo')
1197         el2 = SubElement(el, 'bar')
1198         el3 = SubElement(el2, 'baz')
1199
1200         al = Element('foo2')
1201         al2 = SubElement(al, 'bar2')
1202         al3 = SubElement(al2, 'baz2')
1203
1204         # now move al2 into el
1205         el.append(al2)
1206
1207         # now change al3 directly
1208         al3.text = 'baz2-modified'
1209
1210         # it should have changed through this route too
1211         self.assertEqual(
1212             'baz2-modified',
1213             el[1][0].text)
1214
1215     def test_set_text(self):
1216         Element = self.etree.Element
1217         SubElement = self.etree.SubElement
1218
1219         a = Element('a')
1220         b = SubElement(a, 'b')
1221         a.text = 'hoi'
1222         self.assertEqual(
1223             'hoi',
1224             a.text)
1225         self.assertEqual(
1226             'b',
1227             a[0].tag)
1228
1229     def test_set_text2(self):
1230         Element = self.etree.Element
1231         SubElement = self.etree.SubElement
1232
1233         a = Element('a')
1234         a.text = 'hoi'
1235         b = SubElement(a ,'b')
1236         self.assertEqual(
1237             'hoi',
1238             a.text)
1239         self.assertEqual(
1240             'b',
1241             a[0].tag)
1242
1243     def test_set_text_none(self):
1244         Element = self.etree.Element
1245
1246         a = Element('a')
1247
1248         a.text = 'foo'
1249         a.text = None
1250
1251         self.assertEqual(
1252             None,
1253             a.text)
1254         self.assertXML(_bytes('<a></a>'), a)
1255
1256     def test_set_text_empty(self):
1257         Element = self.etree.Element
1258
1259         a = Element('a')
1260         self.assertEqual(None, a.text)
1261
1262         a.text = ''
1263         self.assertEqual('', a.text)
1264         self.assertXML(_bytes('<a></a>'), a)
1265
1266     def test_tail1(self):
1267         Element = self.etree.Element
1268         SubElement = self.etree.SubElement
1269
1270         a = Element('a')
1271         a.tail = 'dag'
1272         self.assertEqual('dag',
1273                           a.tail)
1274         b = SubElement(a, 'b')
1275         b.tail = 'hoi'
1276         self.assertEqual('hoi',
1277                           b.tail)
1278         self.assertEqual('dag',
1279                           a.tail)
1280
1281     def test_tail_append(self):
1282         Element = self.etree.Element
1283
1284         a = Element('a')
1285         b = Element('b')
1286         b.tail = 'b_tail'
1287         a.append(b)
1288         self.assertEqual('b_tail',
1289                           b.tail)
1290
1291     def test_tail_set_twice(self):
1292         Element = self.etree.Element
1293         SubElement = self.etree.SubElement
1294
1295         a = Element('a')
1296         b = SubElement(a, 'b')
1297         b.tail = 'foo'
1298         b.tail = 'bar'
1299         self.assertEqual('bar',
1300                           b.tail)
1301         self.assertXML(_bytes('<a><b></b>bar</a>'), a)
1302
1303     def test_tail_set_none(self):
1304         Element = self.etree.Element
1305         a = Element('a')
1306         a.tail = 'foo'
1307         a.tail = None
1308         self.assertEqual(
1309             None,
1310             a.tail)
1311         self.assertXML(_bytes('<a></a>'), a)
1312
1313     required_versions_ET['test_extend'] = (1,3)
1314     def test_extend(self):
1315         root = self.etree.Element('foo')
1316         for i in range(3):
1317             element = self.etree.SubElement(root, 'a%s' % i)
1318             element.text = "text%d" % i
1319             element.tail = "tail%d" % i
1320
1321         elements = []
1322         for i in range(3):
1323             new_element = self.etree.Element("test%s" % i)
1324             new_element.text = "TEXT%s" % i
1325             new_element.tail = "TAIL%s" % i
1326             elements.append(new_element)
1327
1328         root.extend(elements)
1329
1330         self.assertEqual(
1331             ["a0", "a1", "a2", "test0", "test1", "test2"],
1332             [ el.tag for el in root ])
1333         self.assertEqual(
1334             ["text0", "text1", "text2", "TEXT0", "TEXT1", "TEXT2"],
1335             [ el.text for el in root ])
1336         self.assertEqual(
1337             ["tail0", "tail1", "tail2", "TAIL0", "TAIL1", "TAIL2"],
1338             [ el.tail for el in root ])
1339
1340     def test_comment(self):
1341         Element = self.etree.Element
1342         SubElement = self.etree.SubElement
1343         Comment = self.etree.Comment
1344
1345         a = Element('a')
1346         a.append(Comment('foo'))
1347         self.assertEqual(a[0].tag, Comment)
1348         self.assertEqual(a[0].text, 'foo')
1349
1350     # ElementTree < 1.3 adds whitespace around comments
1351     required_versions_ET['test_comment_text'] = (1,3)
1352     def test_comment_text(self):
1353         Element = self.etree.Element
1354         SubElement = self.etree.SubElement
1355         Comment = self.etree.Comment
1356         tostring = self.etree.tostring
1357
1358         a = Element('a')
1359         a.append(Comment('foo'))
1360         self.assertEqual(a[0].text, 'foo')
1361
1362         self.assertEqual(
1363             _bytes('<a><!--foo--></a>'),
1364             tostring(a))
1365
1366         a[0].text = "TEST"
1367         self.assertEqual(a[0].text, 'TEST')
1368
1369         self.assertEqual(
1370             _bytes('<a><!--TEST--></a>'),
1371             tostring(a))
1372
1373     # ElementTree < 1.3 adds whitespace around comments
1374     required_versions_ET['test_comment_whitespace'] = (1,3)
1375     def test_comment_whitespace(self):
1376         Element = self.etree.Element
1377         SubElement = self.etree.SubElement
1378         Comment = self.etree.Comment
1379         tostring = self.etree.tostring
1380
1381         a = Element('a')
1382         a.append(Comment(' foo  '))
1383         self.assertEqual(a[0].text, ' foo  ')
1384         self.assertEqual(
1385             _bytes('<a><!-- foo  --></a>'),
1386             tostring(a))
1387
1388     def test_comment_nonsense(self):
1389         Comment = self.etree.Comment
1390         c = Comment('foo')
1391         self.assertEqual({}, c.attrib)
1392         self.assertEqual([], list(c.keys()))
1393         self.assertEqual([], list(c.items()))
1394         self.assertEqual(None, c.get('hoi'))
1395         self.assertEqual(0, len(c))
1396         # should not iterate
1397         for i in c:
1398             pass
1399
1400     def test_pi(self):
1401         # lxml.etree separates target and text
1402         Element = self.etree.Element
1403         SubElement = self.etree.SubElement
1404         ProcessingInstruction = self.etree.ProcessingInstruction
1405
1406         a = Element('a')
1407         a.append(ProcessingInstruction('foo', 'some more text'))
1408         self.assertEqual(a[0].tag, ProcessingInstruction)
1409         self.assertXML(_bytes("<a><?foo some more text?></a>"),
1410                        a)
1411
1412     def test_processinginstruction(self):
1413         # lxml.etree separates target and text
1414         Element = self.etree.Element
1415         SubElement = self.etree.SubElement
1416         ProcessingInstruction = self.etree.PI
1417
1418         a = Element('a')
1419         a.append(ProcessingInstruction('foo', 'some more text'))
1420         self.assertEqual(a[0].tag, ProcessingInstruction)
1421         self.assertXML(_bytes("<a><?foo some more text?></a>"),
1422                        a)
1423
1424     def test_pi_nonsense(self):
1425         ProcessingInstruction = self.etree.ProcessingInstruction
1426         pi = ProcessingInstruction('foo')
1427         self.assertEqual({}, pi.attrib)
1428         self.assertEqual([], list(pi.keys()))
1429         self.assertEqual([], list(pi.items()))
1430         self.assertEqual(None, pi.get('hoi'))
1431         self.assertEqual(0, len(pi))
1432         # should not iterate
1433         for i in pi:
1434             pass
1435
1436     def test_setitem(self):
1437         Element = self.etree.Element
1438         SubElement = self.etree.SubElement
1439
1440         a = Element('a')
1441         b = SubElement(a, 'b')
1442         c = Element('c')
1443         a[0] = c
1444         self.assertEqual(
1445             c,
1446             a[0])
1447         self.assertXML(_bytes('<a><c></c></a>'),
1448                        a)
1449         self.assertXML(_bytes('<b></b>'),
1450                        b)
1451
1452     def test_setitem2(self):
1453         Element = self.etree.Element
1454         SubElement = self.etree.SubElement
1455
1456         a = Element('a')
1457         for i in range(5):
1458             b = SubElement(a, 'b%s' % i)
1459             c = SubElement(b, 'c')
1460         for i in range(5):
1461             d = Element('d')
1462             e = SubElement(d, 'e')
1463             a[i] = d
1464         self.assertXML(
1465             _bytes('<a><d><e></e></d><d><e></e></d><d><e></e></d><d><e></e></d><d><e></e></d></a>'),
1466             a)
1467         self.assertXML(_bytes('<c></c>'),
1468                        c)
1469
1470     def test_setitem_replace(self):
1471         Element = self.etree.Element
1472         SubElement = self.etree.SubElement
1473
1474         a = Element('a')
1475         SubElement(a, 'b')
1476         d = Element('d')
1477         a[0] = d
1478         self.assertXML(_bytes('<a><d></d></a>'), a)
1479
1480     def test_setitem_indexerror(self):
1481         Element = self.etree.Element
1482         SubElement = self.etree.SubElement
1483
1484         a = Element('a')
1485         b = SubElement(a, 'b')
1486
1487         self.assertRaises(IndexError, operator.setitem, a, 1, Element('c'))
1488
1489     def test_setitem_tail(self):
1490         Element = self.etree.Element
1491         SubElement = self.etree.SubElement
1492
1493         a = Element('a')
1494         b = SubElement(a, 'b')
1495         b.tail = 'B2'
1496         c = Element('c')
1497         c.tail = 'C2'
1498
1499         a[0] = c
1500         self.assertXML(
1501             _bytes('<a><c></c>C2</a>'),
1502             a)
1503
1504     def test_tag_write(self):
1505         Element = self.etree.Element
1506         SubElement = self.etree.SubElement
1507
1508         a = Element('a')
1509         b = SubElement(a, 'b')
1510
1511         a.tag = 'c'
1512
1513         self.assertEqual(
1514             'c',
1515             a.tag)
1516
1517         self.assertXML(
1518             _bytes('<c><b></b></c>'),
1519             a)
1520
1521     def test_tag_reset_ns(self):
1522         Element = self.etree.Element
1523         SubElement = self.etree.SubElement
1524         tostring = self.etree.tostring
1525
1526         a = Element('{a}a')
1527         b1 = SubElement(a, '{a}b')
1528         b2 = SubElement(a, '{b}b')
1529
1530         self.assertEqual('{a}b',  b1.tag)
1531
1532         b1.tag = 'c'
1533
1534         # can't use C14N here!
1535         self.assertEqual('c', b1.tag)
1536         self.assertEqual(_bytes('<c'), tostring(b1)[:2])
1537         self.assertTrue(_bytes('<c') in tostring(a))
1538
1539     def test_tag_reset_root_ns(self):
1540         Element = self.etree.Element
1541         SubElement = self.etree.SubElement
1542         tostring = self.etree.tostring
1543
1544         a = Element('{a}a')
1545         b1 = SubElement(a, '{a}b')
1546         b2 = SubElement(a, '{b}b')
1547
1548         a.tag = 'c'
1549
1550         self.assertEqual(
1551             'c',
1552             a.tag)
1553
1554         # can't use C14N here!
1555         self.assertEqual('c',  a.tag)
1556         self.assertEqual(_bytes('<c'), tostring(a)[:2])
1557
1558     def test_tag_str_subclass(self):
1559         Element = self.etree.Element
1560
1561         class strTest(str):
1562             pass
1563
1564         a = Element("a")
1565         a.tag = strTest("TAG")
1566         self.assertXML(_bytes('<TAG></TAG>'),
1567                        a)
1568
1569     def test_delitem(self):
1570         Element = self.etree.Element
1571         SubElement = self.etree.SubElement
1572
1573         a = Element('a')
1574         b = SubElement(a, 'b')
1575         c = SubElement(a, 'c')
1576         d = SubElement(a, 'd')
1577
1578         del a[1]
1579         self.assertXML(
1580             _bytes('<a><b></b><d></d></a>'),
1581             a)
1582
1583         del a[0]
1584         self.assertXML(
1585             _bytes('<a><d></d></a>'),
1586             a)
1587
1588         del a[0]
1589         self.assertXML(
1590             _bytes('<a></a>'),
1591             a)
1592         # move deleted element into other tree afterwards
1593         other = Element('other')
1594         other.append(c)
1595         self.assertXML(
1596             _bytes('<other><c></c></other>'),
1597             other)
1598
1599     def test_del_insert(self):
1600         Element = self.etree.Element
1601         SubElement = self.etree.SubElement
1602
1603         a = Element('a')
1604         b = SubElement(a, 'b')
1605         bs = SubElement(b, 'bs')
1606         c = SubElement(a, 'c')
1607         cs = SubElement(c, 'cs')
1608
1609         el = a[0]
1610         self.assertXML(
1611             _bytes('<a><b><bs></bs></b><c><cs></cs></c></a>'),
1612             a)
1613         self.assertXML(_bytes('<b><bs></bs></b>'), b)
1614         self.assertXML(_bytes('<c><cs></cs></c>'), c)
1615
1616         del a[0]
1617         self.assertXML(
1618             _bytes('<a><c><cs></cs></c></a>'),
1619             a)
1620         self.assertXML(_bytes('<b><bs></bs></b>'), b)
1621         self.assertXML(_bytes('<c><cs></cs></c>'), c)
1622
1623         a.insert(0, el)
1624         self.assertXML(
1625             _bytes('<a><b><bs></bs></b><c><cs></cs></c></a>'),
1626             a)
1627         self.assertXML(_bytes('<b><bs></bs></b>'), b)
1628         self.assertXML(_bytes('<c><cs></cs></c>'), c)
1629
1630     def test_del_setitem(self):
1631         Element = self.etree.Element
1632         SubElement = self.etree.SubElement
1633
1634         a = Element('a')
1635         b = SubElement(a, 'b')
1636         bs = SubElement(b, 'bs')
1637         c = SubElement(a, 'c')
1638         cs = SubElement(c, 'cs')
1639
1640         el = a[0]
1641         del a[0]
1642         a[0] = el
1643         self.assertXML(
1644             _bytes('<a><b><bs></bs></b></a>'),
1645             a)
1646         self.assertXML(_bytes('<b><bs></bs></b>'), b)
1647         self.assertXML(_bytes('<c><cs></cs></c>'), c)
1648
1649     def test_del_setslice(self):
1650         Element = self.etree.Element
1651         SubElement = self.etree.SubElement
1652
1653         a = Element('a')
1654         b = SubElement(a, 'b')
1655         bs = SubElement(b, 'bs')
1656         c = SubElement(a, 'c')
1657         cs = SubElement(c, 'cs')
1658
1659         el = a[0]
1660         del a[0]
1661         a[0:0] = [el]
1662         self.assertXML(
1663             _bytes('<a><b><bs></bs></b><c><cs></cs></c></a>'),
1664             a)
1665         self.assertXML(_bytes('<b><bs></bs></b>'), b)
1666         self.assertXML(_bytes('<c><cs></cs></c>'), c)
1667
1668     def test_replace_slice_tail(self):
1669         XML = self.etree.XML
1670         a = XML(_bytes('<a><b></b>B2<c></c>C2</a>'))
1671         b, c = a
1672
1673         a[:] = []
1674
1675         self.assertEqual("B2", b.tail)
1676         self.assertEqual("C2", c.tail)
1677
1678     def test_merge_namespaced_subtree_as_slice(self):
1679         XML = self.etree.XML
1680         root = XML(_bytes(
1681             '<foo><bar xmlns:baz="http://huhu"><puh><baz:bump1 /><baz:bump2 /></puh></bar></foo>'))
1682         root[:] = root.findall('.//puh') # delete bar from hierarchy
1683
1684         # previously, this lost a namespace declaration on bump2
1685         result = self.etree.tostring(root)
1686         foo = self.etree.fromstring(result)
1687
1688         self.assertEqual('puh', foo[0].tag)
1689         self.assertEqual('{http://huhu}bump1', foo[0][0].tag)
1690         self.assertEqual('{http://huhu}bump2', foo[0][1].tag)
1691
1692     def test_delitem_tail_dealloc(self):
1693         ElementTree = self.etree.ElementTree
1694         f = BytesIO('<a><b></b>B2<c></c>C2</a>')
1695         doc = ElementTree(file=f)
1696         a = doc.getroot()
1697         del a[0]
1698         self.assertXML(
1699             _bytes('<a><c></c>C2</a>'),
1700             a)
1701
1702     def test_delitem_tail(self):
1703         ElementTree = self.etree.ElementTree
1704         f = BytesIO('<a><b></b>B2<c></c>C2</a>')
1705         doc = ElementTree(file=f)
1706         a = doc.getroot()
1707         b, c = a
1708         del a[0]
1709         self.assertXML(
1710             _bytes('<a><c></c>C2</a>'),
1711             a)
1712         self.assertEqual("B2", b.tail)
1713         self.assertEqual("C2", c.tail)
1714
1715     def test_clear(self):
1716         Element = self.etree.Element
1717
1718         a = Element('a')
1719         a.text = 'foo'
1720         a.tail = 'bar'
1721         a.set('hoi', 'dag')
1722         a.clear()
1723         self.assertEqual(None, a.text)
1724         self.assertEqual(None, a.tail)
1725         self.assertEqual(None, a.get('hoi'))
1726         self.assertEqual('a', a.tag)
1727
1728     def test_clear_sub(self):
1729         Element = self.etree.Element
1730         SubElement = self.etree.SubElement
1731
1732         a = Element('a')
1733         a.text = 'foo'
1734         a.tail = 'bar'
1735         a.set('hoi', 'dag')
1736         b = SubElement(a, 'b')
1737         c = SubElement(b, 'c')
1738         a.clear()
1739         self.assertEqual(None, a.text)
1740         self.assertEqual(None, a.tail)
1741         self.assertEqual(None, a.get('hoi'))
1742         self.assertEqual('a', a.tag)
1743         self.assertEqual(0, len(a))
1744         self.assertXML(_bytes('<a></a>'),
1745                        a)
1746         self.assertXML(_bytes('<b><c></c></b>'),
1747                        b)
1748
1749     def test_clear_tail(self):
1750         ElementTree = self.etree.ElementTree
1751         f = BytesIO('<a><b></b>B2<c></c>C2</a>')
1752         doc = ElementTree(file=f)
1753         a = doc.getroot()
1754         a.clear()
1755         self.assertXML(
1756             _bytes('<a></a>'),
1757             a)
1758
1759     def test_insert(self):
1760         Element = self.etree.Element
1761         SubElement = self.etree.SubElement
1762
1763         a = Element('a')
1764         b = SubElement(a, 'b')
1765         c = SubElement(a, 'c')
1766         d = Element('d')
1767         a.insert(0, d)
1768
1769         self.assertEqual(
1770             d,
1771             a[0])
1772
1773         self.assertXML(
1774             _bytes('<a><d></d><b></b><c></c></a>'),
1775             a)
1776
1777         e = Element('e')
1778         a.insert(2, e)
1779         self.assertEqual(
1780             e,
1781             a[2])
1782         self.assertXML(
1783             _bytes('<a><d></d><b></b><e></e><c></c></a>'),
1784             a)
1785
1786     def test_insert_name_interning(self):
1787         # See GH#268 / LP#1773749.
1788         Element = self.etree.Element
1789         SubElement = self.etree.SubElement
1790
1791         # Use unique names to make sure they are new in the tag name dict.
1792         import uuid
1793         names = dict((k, 'tag-' + str(uuid.uuid4())) for k in 'abcde')
1794
1795         a = Element(names['a'])
1796         b = SubElement(a, names['b'])
1797         c = SubElement(a, names['c'])
1798         d = Element(names['d'])
1799         a.insert(0, d)
1800
1801         self.assertEqual(
1802             d,
1803             a[0])
1804
1805         self.assertXML(
1806             _bytes('<%(a)s><%(d)s></%(d)s><%(b)s></%(b)s><%(c)s></%(c)s></%(a)s>' % names),
1807             a)
1808
1809         e = Element(names['e'])
1810         a.insert(2, e)
1811         self.assertEqual(
1812             e,
1813             a[2])
1814         self.assertXML(
1815             _bytes('<%(a)s><%(d)s></%(d)s><%(b)s></%(b)s><%(e)s></%(e)s><%(c)s></%(c)s></%(a)s>' % names),
1816             a)
1817
1818     def test_insert_beyond_index(self):
1819         Element = self.etree.Element
1820         SubElement = self.etree.SubElement
1821
1822         a = Element('a')
1823         b = SubElement(a, 'b')
1824         c = Element('c')
1825
1826         a.insert(2, c)
1827         self.assertEqual(
1828             c,
1829             a[1])
1830         self.assertXML(
1831             _bytes('<a><b></b><c></c></a>'),
1832             a)
1833
1834     def test_insert_negative(self):
1835         Element = self.etree.Element
1836         SubElement = self.etree.SubElement
1837
1838         a = Element('a')
1839         b = SubElement(a, 'b')
1840         c = SubElement(a, 'c')
1841
1842         d = Element('d')
1843         a.insert(-1, d)
1844         self.assertEqual(
1845             d,
1846             a[-2])
1847         self.assertXML(
1848             _bytes('<a><b></b><d></d><c></c></a>'),
1849             a)
1850
1851     def test_insert_tail(self):
1852         Element = self.etree.Element
1853         SubElement = self.etree.SubElement
1854
1855         a = Element('a')
1856         b = SubElement(a, 'b')
1857
1858         c = Element('c')
1859         c.tail = 'C2'
1860
1861         a.insert(0, c)
1862         self.assertXML(
1863             _bytes('<a><c></c>C2<b></b></a>'),
1864             a)
1865
1866     def test_remove(self):
1867         Element = self.etree.Element
1868         SubElement = self.etree.SubElement
1869
1870         a = Element('a')
1871         b = SubElement(a, 'b')
1872         c = SubElement(a, 'c')
1873
1874         a.remove(b)
1875         self.assertEqual(
1876             c,
1877             a[0])
1878         self.assertXML(
1879             _bytes('<a><c></c></a>'),
1880             a)
1881
1882     def test_remove_ns(self):
1883         Element = self.etree.Element
1884         SubElement = self.etree.SubElement
1885
1886         a = Element('{http://test}a')
1887         b = SubElement(a, '{http://test}b')
1888         c = SubElement(a, '{http://test}c')
1889
1890         a.remove(b)
1891         self.assertXML(
1892             _bytes('<ns0:a xmlns:ns0="http://test"><ns0:c></ns0:c></ns0:a>'),
1893             a)
1894         self.assertXML(
1895             _bytes('<ns0:b xmlns:ns0="http://test"></ns0:b>'),
1896             b)
1897
1898     def test_remove_nonexisting(self):
1899         Element = self.etree.Element
1900         SubElement = self.etree.SubElement
1901
1902         a = Element('a')
1903         b = SubElement(a, 'b')
1904         c = SubElement(a, 'c')
1905         d = Element('d')
1906         self.assertRaises(
1907             ValueError, a.remove, d)
1908
1909     def test_remove_tail(self):
1910         Element = self.etree.Element
1911         SubElement = self.etree.SubElement
1912
1913         a = Element('a')
1914         b = SubElement(a, 'b')
1915         b.tail = 'b2'
1916         a.remove(b)
1917         self.assertXML(
1918             _bytes('<a></a>'),
1919             a)
1920         self.assertEqual('b2', b.tail)
1921
1922     def test_remove_while_iterating(self):
1923         # There is no guarantee that this "works", but it should
1924         # remove at least one child and not crash.
1925         Element = self.etree.Element
1926         SubElement = self.etree.SubElement
1927
1928         a = Element('a')
1929         SubElement(a, 'b')
1930         SubElement(a, 'c')
1931         SubElement(a, 'd')
1932         for el in a:
1933             a.remove(el)
1934         self.assertLess(len(a), 3)
1935
1936     def test_getchildren(self):
1937         Element = self.etree.Element
1938         SubElement = self.etree.SubElement
1939
1940         a = Element('a')
1941         b = SubElement(a, 'b')
1942         c = SubElement(a, 'c')
1943         d = SubElement(b, 'd')
1944         e = SubElement(c, 'e')
1945         self.assertXML(
1946             _bytes('<a><b><d></d></b><c><e></e></c></a>'),
1947             a)
1948         self.assertEqual(
1949             [b, c],
1950             a.getchildren())
1951         self.assertEqual(
1952             [d],
1953             b.getchildren())
1954         self.assertEqual(
1955             [],
1956             d.getchildren())
1957
1958     def test_makeelement(self):
1959         Element = self.etree.Element
1960
1961         a = Element('a')
1962         b = a.makeelement('c', {'hoi':'dag'})
1963         self.assertXML(
1964             _bytes('<c hoi="dag"></c>'),
1965             b)
1966
1967     required_versions_ET['test_iter'] = (1,3)
1968     def test_iter(self):
1969         Element = self.etree.Element
1970         SubElement = self.etree.SubElement
1971
1972         a = Element('a')
1973         b = SubElement(a, 'b')
1974         c = SubElement(a, 'c')
1975         d = SubElement(b, 'd')
1976         e = SubElement(c, 'e')
1977
1978         self.assertEqual(
1979             [a, b, d, c, e],
1980             list(a.iter()))
1981         self.assertEqual(
1982             [d],
1983             list(d.iter()))
1984
1985     def test_iter_remove_tail(self):
1986         Element = self.etree.Element
1987         SubElement = self.etree.SubElement
1988
1989         a = Element('a')
1990         a.text = 'a'
1991         a.tail = 'a1' * 100
1992         b = SubElement(a, 'b')
1993         b.text = 'b'
1994         b.tail = 'b1' * 100
1995         c = SubElement(a, 'c')
1996         c.text = 'c'
1997         c.tail = 'c1' * 100
1998         d = SubElement(b, 'd')
1999         d.text = 'd'
2000         d.tail = 'd1' * 100
2001         e = SubElement(c, 'e')
2002         e.text = 'e'
2003         e.tail = 'e1' * 100
2004
2005         for el in a.iter():
2006             el.tail = None
2007         el = None
2008
2009         self.assertEqual(
2010             [None] * 5,
2011             [el.tail for el in a.iter()])
2012
2013     def test_getiterator(self):
2014         Element = self.etree.Element
2015         SubElement = self.etree.SubElement
2016
2017         a = Element('a')
2018         b = SubElement(a, 'b')
2019         c = SubElement(a, 'c')
2020         d = SubElement(b, 'd')
2021         e = SubElement(c, 'e')
2022
2023         self.assertEqual(
2024             [a, b, d, c, e],
2025             list(a.getiterator()))
2026         self.assertEqual(
2027             [d],
2028             list(d.getiterator()))
2029
2030     def test_getiterator_empty(self):
2031         Element = self.etree.Element
2032         SubElement = self.etree.SubElement
2033
2034         a = Element('a')
2035         b = SubElement(a, 'b')
2036         c = SubElement(a, 'c')
2037         d = SubElement(b, 'd')
2038         e = SubElement(c, 'e')
2039
2040         self.assertEqual(
2041             [],
2042             list(a.getiterator('none')))
2043         self.assertEqual(
2044             [],
2045             list(e.getiterator('none')))
2046         self.assertEqual(
2047             [e],
2048             list(e.getiterator()))
2049
2050     def test_getiterator_filter(self):
2051         Element = self.etree.Element
2052         SubElement = self.etree.SubElement
2053
2054         a = Element('a')
2055         b = SubElement(a, 'b')
2056         c = SubElement(a, 'c')
2057         d = SubElement(b, 'd')
2058         e = SubElement(c, 'e')
2059
2060         self.assertEqual(
2061             [a],
2062             list(a.getiterator('a')))
2063         a2 = SubElement(e, 'a')
2064         self.assertEqual(
2065             [a, a2],
2066             list(a.getiterator('a')))
2067         self.assertEqual(
2068             [a2],
2069             list(c.getiterator('a')))
2070
2071     def test_getiterator_filter_all(self):
2072         Element = self.etree.Element
2073         SubElement = self.etree.SubElement
2074
2075         a = Element('a')
2076         b = SubElement(a, 'b')
2077         c = SubElement(a, 'c')
2078         d = SubElement(b, 'd')
2079         e = SubElement(c, 'e')
2080
2081         self.assertEqual(
2082             [a, b, d, c, e],
2083             list(a.getiterator('*')))
2084
2085     def test_getiterator_filter_comment(self):
2086         Element = self.etree.Element
2087         Comment = self.etree.Comment
2088         SubElement = self.etree.SubElement
2089
2090         a = Element('a')
2091         b = SubElement(a, 'b')
2092         comment_b = Comment("TEST-b")
2093         b.append(comment_b)
2094
2095         self.assertEqual(
2096             [comment_b],
2097             list(a.getiterator(Comment)))
2098
2099         comment_a = Comment("TEST-a")
2100         a.append(comment_a)
2101
2102         self.assertEqual(
2103             [comment_b, comment_a],
2104             list(a.getiterator(Comment)))
2105
2106         self.assertEqual(
2107             [comment_b],
2108             list(b.getiterator(Comment)))
2109
2110     def test_getiterator_filter_pi(self):
2111         Element = self.etree.Element
2112         PI = self.etree.ProcessingInstruction
2113         SubElement = self.etree.SubElement
2114
2115         a = Element('a')
2116         b = SubElement(a, 'b')
2117         pi_b = PI("TEST-b")
2118         b.append(pi_b)
2119
2120         self.assertEqual(
2121             [pi_b],
2122             list(a.getiterator(PI)))
2123
2124         pi_a = PI("TEST-a")
2125         a.append(pi_a)
2126
2127         self.assertEqual(
2128             [pi_b, pi_a],
2129             list(a.getiterator(PI)))
2130
2131         self.assertEqual(
2132             [pi_b],
2133             list(b.getiterator(PI)))
2134
2135     def test_getiterator_with_text(self):
2136         Element = self.etree.Element
2137         SubElement = self.etree.SubElement
2138
2139         a = Element('a')
2140         a.text = 'a'
2141         b = SubElement(a, 'b')
2142         b.text = 'b'
2143         b.tail = 'b1'
2144         c = SubElement(a, 'c')
2145         c.text = 'c'
2146         c.tail = 'c1'
2147         d = SubElement(b, 'd')
2148         d.text = 'd'
2149         d.tail = 'd1'
2150         e = SubElement(c, 'e')
2151         e.text = 'e'
2152         e.tail = 'e1'
2153
2154         self.assertEqual(
2155             [a, b, d, c, e],
2156             list(a.getiterator()))
2157         #self.assertEqual(
2158         #    [d],
2159         #    list(d.getiterator()))
2160
2161     def test_getiterator_filter_with_text(self):
2162         Element = self.etree.Element
2163         SubElement = self.etree.SubElement
2164
2165         a = Element('a')
2166         a.text = 'a'
2167         b = SubElement(a, 'b')
2168         b.text = 'b'
2169         b.tail = 'b1'
2170         c = SubElement(a, 'c')
2171         c.text = 'c'
2172         c.tail = 'c1'
2173         d = SubElement(b, 'd')
2174         d.text = 'd'
2175         d.tail = 'd1'
2176         e = SubElement(c, 'e')
2177         e.text = 'e'
2178         e.tail = 'e1'
2179
2180         self.assertEqual(
2181             [a],
2182             list(a.getiterator('a')))
2183         a2 = SubElement(e, 'a')
2184         self.assertEqual(
2185             [a, a2],
2186             list(a.getiterator('a')))   
2187         self.assertEqual(
2188             [a2],
2189             list(e.getiterator('a')))
2190
2191     def test_getslice(self):
2192         Element = self.etree.Element
2193         SubElement = self.etree.SubElement
2194
2195         a = Element('a')
2196         b = SubElement(a, 'b')
2197         c = SubElement(a, 'c')
2198         d = SubElement(a, 'd')
2199
2200         self.assertEqual(
2201             [b, c],
2202             a[0:2])
2203         self.assertEqual(
2204             [b, c, d],
2205             a[:])
2206         self.assertEqual(
2207             [b, c, d],
2208             a[:10])
2209         self.assertEqual(
2210             [b],
2211             a[0:1])
2212         self.assertEqual(
2213             [],
2214             a[10:12])
2215
2216     def test_getslice_negative(self):
2217         Element = self.etree.Element
2218         SubElement = self.etree.SubElement
2219
2220         a = Element('a')
2221         b = SubElement(a, 'b')
2222         c = SubElement(a, 'c')
2223         d = SubElement(a, 'd')
2224
2225         self.assertEqual(
2226             [d],
2227             a[-1:])
2228         self.assertEqual(
2229             [c, d],
2230             a[-2:])
2231         self.assertEqual(
2232             [c],
2233             a[-2:-1])
2234         self.assertEqual(
2235             [b, c],
2236             a[-3:-1])
2237         self.assertEqual(
2238             [b, c],
2239             a[-3:2])
2240
2241     def test_getslice_step(self):
2242         Element = self.etree.Element
2243         SubElement = self.etree.SubElement
2244
2245         a = Element('a')
2246         b = SubElement(a, 'b')
2247         c = SubElement(a, 'c')
2248         d = SubElement(a, 'd')
2249         e = SubElement(a, 'e')
2250
2251         self.assertEqual(
2252             [e,d,c,b],
2253             a[::-1])
2254         self.assertEqual(
2255             [b,d],
2256             a[::2])
2257         self.assertEqual(
2258             [e,c],
2259             a[::-2])
2260         self.assertEqual(
2261             [d,c],
2262             a[-2:0:-1])
2263         self.assertEqual(
2264             [e],
2265             a[:1:-2])
2266
2267     def test_getslice_text(self):
2268         ElementTree = self.etree.ElementTree
2269
2270         f = BytesIO('<a><b>B</b>B1<c>C</c>C1</a>')
2271         doc = ElementTree(file=f)
2272         a = doc.getroot()
2273         b = a[0]
2274         c = a[1]
2275         self.assertEqual(
2276             [b, c],
2277             a[:])
2278         self.assertEqual(
2279             [b],
2280             a[0:1])
2281         self.assertEqual(
2282             [c],
2283             a[1:])
2284
2285     def test_comment_getitem_getslice(self):
2286         Element = self.etree.Element
2287         Comment = self.etree.Comment
2288         SubElement = self.etree.SubElement
2289
2290         a = Element('a')
2291         b = SubElement(a, 'b')
2292         foo = Comment('foo')
2293         a.append(foo)
2294         c = SubElement(a, 'c')
2295         self.assertEqual(
2296             [b, foo, c],
2297             a[:])
2298         self.assertEqual(
2299             foo,
2300             a[1])
2301         a[1] = new = Element('new')
2302         self.assertEqual(
2303             new,
2304             a[1])
2305         self.assertXML(
2306             _bytes('<a><b></b><new></new><c></c></a>'),
2307             a)
2308
2309     def test_delslice(self):
2310         Element = self.etree.Element
2311         SubElement = self.etree.SubElement
2312
2313         a = Element('a')
2314         b = SubElement(a, 'b')
2315         c = SubElement(a, 'c')
2316         d = SubElement(a, 'd')
2317         e = SubElement(a, 'e')
2318
2319         del a[1:3]
2320         self.assertEqual(
2321             [b, e],
2322             list(a))
2323
2324     def test_delslice_negative1(self):
2325         Element = self.etree.Element
2326         SubElement = self.etree.SubElement
2327
2328         a = Element('a')
2329         b = SubElement(a, 'b')
2330         c = SubElement(a, 'c')
2331         d = SubElement(a, 'd')
2332         e = SubElement(a, 'e')
2333
2334         del a[1:-1]
2335         self.assertEqual(
2336             [b, e],
2337             list(a))
2338
2339     def test_delslice_negative2(self):
2340         Element = self.etree.Element
2341         SubElement = self.etree.SubElement
2342
2343         a = Element('a')
2344         b = SubElement(a, 'b')
2345         c = SubElement(a, 'c')
2346         d = SubElement(a, 'd')
2347         e = SubElement(a, 'e')
2348
2349         del a[-3:-1]
2350         self.assertEqual(
2351             [b, e],
2352             list(a))
2353
2354     def test_delslice_step(self):
2355         Element = self.etree.Element
2356         SubElement = self.etree.SubElement
2357
2358         a = Element('a')
2359         b = SubElement(a, 'b')
2360         c = SubElement(a, 'c')
2361         d = SubElement(a, 'd')
2362         e = SubElement(a, 'e')
2363
2364         del a[1::2]
2365         self.assertEqual(
2366             [b, d],
2367             list(a))
2368
2369     def test_delslice_step_negative(self):
2370         Element = self.etree.Element
2371         SubElement = self.etree.SubElement
2372
2373         a = Element('a')
2374         b = SubElement(a, 'b')
2375         c = SubElement(a, 'c')
2376         d = SubElement(a, 'd')
2377         e = SubElement(a, 'e')
2378
2379         del a[::-1]
2380         self.assertEqual(
2381             [],
2382             list(a))
2383
2384     def test_delslice_step_negative2(self):
2385         Element = self.etree.Element
2386         SubElement = self.etree.SubElement
2387
2388         a = Element('a')
2389         b = SubElement(a, 'b')
2390         c = SubElement(a, 'c')
2391         d = SubElement(a, 'd')
2392         e = SubElement(a, 'e')
2393
2394         del a[::-2]
2395         self.assertEqual(
2396             [b, d],
2397             list(a))
2398
2399     def test_delslice_child_tail_dealloc(self):
2400         ElementTree = self.etree.ElementTree
2401         f = BytesIO('<a><b></b>B2<c></c>C2<d></d>D2<e></e>E2</a>')
2402         doc = ElementTree(file=f)
2403         a = doc.getroot()
2404         del a[1:3]
2405         self.assertXML(
2406             _bytes('<a><b></b>B2<e></e>E2</a>'),
2407             a)
2408
2409     def test_delslice_child_tail(self):
2410         ElementTree = self.etree.ElementTree
2411         f = BytesIO('<a><b></b>B2<c></c>C2<d></d>D2<e></e>E2</a>')
2412         doc = ElementTree(file=f)
2413         a = doc.getroot()
2414         b, c, d, e = a
2415         del a[1:3]
2416         self.assertXML(
2417             _bytes('<a><b></b>B2<e></e>E2</a>'),
2418             a)
2419         self.assertEqual("B2", b.tail)
2420         self.assertEqual("C2", c.tail)
2421         self.assertEqual("D2", d.tail)
2422         self.assertEqual("E2", e.tail)
2423
2424     def test_delslice_tail(self):
2425         XML = self.etree.XML
2426         a = XML(_bytes('<a><b></b>B2<c></c>C2</a>'))
2427         b, c = a
2428
2429         del a[:]
2430
2431         self.assertEqual("B2", b.tail)
2432         self.assertEqual("C2", c.tail)
2433
2434     def test_delslice_memory(self):
2435         # this could trigger a crash
2436         Element = self.etree.Element
2437         SubElement = self.etree.SubElement
2438         a = Element('a')
2439         b = SubElement(a, 'b')
2440         c = SubElement(b, 'c')
2441         del b # no more reference to b
2442         del a[:]
2443         self.assertEqual('c', c.tag)
2444
2445     def test_setslice(self):
2446         Element = self.etree.Element
2447         SubElement = self.etree.SubElement
2448
2449         a = Element('a')
2450         b = SubElement(a, 'b')
2451         c = SubElement(a, 'c')
2452         d = SubElement(a, 'd')
2453
2454         e = Element('e')
2455         f = Element('f')
2456         g = Element('g')
2457
2458         s = [e, f, g]
2459         a[1:2] = s
2460         self.assertEqual(
2461             [b, e, f, g, d],
2462             list(a))
2463
2464     def test_setslice_all(self):
2465         Element = self.etree.Element
2466         SubElement = self.etree.SubElement
2467
2468         a = Element('a')
2469         b = SubElement(a, 'b')
2470         c = SubElement(a, 'c')
2471
2472         e = Element('e')
2473         f = Element('f')
2474         g = Element('g')
2475
2476         s = [e, f, g]
2477         a[:] = s
2478         self.assertEqual(
2479             [e, f, g],
2480             list(a))
2481
2482     def test_setslice_all_empty(self):
2483         Element = self.etree.Element
2484         SubElement = self.etree.SubElement
2485
2486         a = Element('a')
2487
2488         e = Element('e')
2489         f = Element('f')
2490         g = Element('g')
2491
2492         s = [e, f, g]
2493         a[:] = s
2494         self.assertEqual(
2495             [e, f, g],
2496             list(a))
2497
2498     def test_setslice_all_replace(self):
2499         Element = self.etree.Element
2500         SubElement = self.etree.SubElement
2501
2502         a = Element('a')
2503         b = SubElement(a, 'b')
2504         c = SubElement(a, 'c')
2505         d = SubElement(a, 'd')
2506
2507         s = [b, c, d]
2508         a[:] = s
2509         self.assertEqual(
2510             [b, c, d],
2511             list(a))
2512
2513     def test_setslice_all_replace_reversed(self):
2514         Element = self.etree.Element
2515         SubElement = self.etree.SubElement
2516
2517         a = Element('a')
2518         b = SubElement(a, 'b')
2519         c = SubElement(a, 'c')
2520         d = SubElement(a, 'd')
2521
2522         s = [d, c, b]
2523         a[:] = s
2524         self.assertEqual(
2525             [d, c, b],
2526             list(a))
2527
2528     def test_setslice_all_replace_reversed_ns1(self):
2529         Element = self.etree.Element
2530         SubElement = self.etree.SubElement
2531
2532         a = Element('{ns}a')
2533         b = SubElement(a, '{ns}b', {'{ns1}a1': 'test'})
2534         c = SubElement(a, '{ns}c', {'{ns2}a2': 'test'})
2535         d = SubElement(a, '{ns}d', {'{ns3}a3': 'test'})
2536
2537         s = [d, c, b]
2538         a[:] = s
2539         self.assertEqual(
2540             [d, c, b],
2541             list(a))
2542         self.assertEqual(
2543             ['{ns}d', '{ns}c', '{ns}b'],
2544             [ child.tag for child in a ])
2545
2546         self.assertEqual(
2547             [['{ns3}a3'], ['{ns2}a2'], ['{ns1}a1']],
2548             [ list(child.attrib.keys()) for child in a ])
2549
2550     def test_setslice_all_replace_reversed_ns2(self):
2551         Element = self.etree.Element
2552         SubElement = self.etree.SubElement
2553
2554         a = Element('{ns}a')
2555         b = SubElement(a, '{ns1}b', {'{ns}a1': 'test'})
2556         c = SubElement(a, '{ns2}c', {'{ns}a2': 'test'})
2557         d = SubElement(a, '{ns3}d', {'{ns}a3': 'test'})
2558
2559         s = [d, c, b]
2560         a[:] = s
2561         self.assertEqual(
2562             [d, c, b],
2563             list(a))
2564         self.assertEqual(
2565             ['{ns3}d', '{ns2}c', '{ns1}b'],
2566             [ child.tag for child in a ])
2567
2568         self.assertEqual(
2569             [['{ns}a3'], ['{ns}a2'], ['{ns}a1']],
2570             [ list(child.attrib.keys()) for child in a ])
2571
2572     def test_setslice_end(self):
2573         Element = self.etree.Element
2574         SubElement = self.etree.SubElement
2575
2576         a = Element('a')
2577         b = SubElement(a, 'b')
2578         c = SubElement(a, 'c')
2579
2580         e = Element('e')
2581         f = Element('f')
2582         g = Element('g')
2583         h = Element('h')
2584
2585         s = [e, f]
2586         a[99:] = s
2587         self.assertEqual(
2588             [b, c, e, f],
2589             list(a))
2590
2591         s = [g, h]
2592         a[:0] = s
2593         self.assertEqual(
2594             [g, h, b, c, e, f],
2595             list(a))
2596
2597     def test_setslice_end_exact(self):
2598         Element = self.etree.Element
2599         SubElement = self.etree.SubElement
2600
2601         a = Element('a')
2602         b = SubElement(a, 'b')
2603         c = SubElement(a, 'c')
2604         d = SubElement(a, 'd')
2605
2606         e = Element('e')
2607         f = Element('f')
2608         g = Element('g')
2609
2610         s = [e, f, g]
2611         a[3:] = s
2612         self.assertEqual(
2613             [b, c, d, e, f, g],
2614             list(a))
2615
2616     def test_setslice_single(self):
2617         Element = self.etree.Element
2618         SubElement = self.etree.SubElement
2619
2620         a = Element('a')
2621         b = SubElement(a, 'b')
2622         c = SubElement(a, 'c')
2623
2624         e = Element('e')
2625         f = Element('f')
2626
2627         s = [e]
2628         a[0:1] = s
2629         self.assertEqual(
2630             [e, c],
2631             list(a))
2632
2633         s = [f]
2634         a[1:2] = s
2635         self.assertEqual(
2636             [e, f],
2637             list(a))
2638
2639     def test_setslice_tail(self):
2640         ElementTree = self.etree.ElementTree
2641         Element = self.etree.Element
2642         f = BytesIO('<a><b></b>B2<c></c>C2<d></d>D2<e></e>E2</a>')
2643         doc = ElementTree(file=f)
2644         a = doc.getroot()
2645         x = Element('x')
2646         y = Element('y')
2647         z = Element('z')
2648         x.tail = 'X2'
2649         y.tail = 'Y2'
2650         z.tail = 'Z2'
2651         a[1:3] = [x, y, z]
2652         self.assertXML(
2653             _bytes('<a><b></b>B2<x></x>X2<y></y>Y2<z></z>Z2<e></e>E2</a>'),
2654             a)
2655
2656     def test_setslice_negative(self):
2657         Element = self.etree.Element
2658         SubElement = self.etree.SubElement
2659
2660         a = Element('a')
2661         b = SubElement(a, 'b')
2662         c = SubElement(a, 'c')
2663         d = SubElement(a, 'd')
2664
2665         x = Element('x')
2666         y = Element('y')
2667
2668         a[1:-1] = [x, y]
2669         self.assertEqual(
2670             [b, x, y, d],
2671             list(a))
2672
2673     def test_setslice_negative2(self):
2674         Element = self.etree.Element
2675         SubElement = self.etree.SubElement
2676
2677         a = Element('a')
2678         b = SubElement(a, 'b')
2679         c = SubElement(a, 'c')
2680         d = SubElement(a, 'd')
2681
2682         x = Element('x')
2683         y = Element('y')
2684
2685         a[1:-2] = [x, y]
2686         self.assertEqual(
2687             [b, x, y, c, d],
2688             list(a))
2689
2690     def test_setslice_empty(self):
2691         Element = self.etree.Element
2692
2693         a = Element('a')
2694
2695         b = Element('b')
2696         c = Element('c')
2697
2698         a[:] = [b, c]
2699         self.assertEqual(
2700             [b, c],
2701             list(a))
2702
2703     def test_tail_elementtree_root(self):
2704         Element = self.etree.Element
2705         ElementTree = self.etree.ElementTree
2706
2707         a = Element('a')
2708         a.tail = 'A2'
2709         t = ElementTree(element=a)
2710         self.assertEqual('A2',
2711                           a.tail)
2712
2713     def test_elementtree_getiterator(self):
2714         Element = self.etree.Element
2715         SubElement = self.etree.SubElement
2716         ElementTree = self.etree.ElementTree
2717
2718         a = Element('a')
2719         b = SubElement(a, 'b')
2720         c = SubElement(a, 'c')
2721         d = SubElement(b, 'd')
2722         e = SubElement(c, 'e')
2723         t = ElementTree(element=a)
2724
2725         self.assertEqual(
2726             [a, b, d, c, e],
2727             list(t.getiterator()))
2728
2729     def test_elementtree_getiterator_filter(self):
2730         Element = self.etree.Element
2731         SubElement = self.etree.SubElement
2732         ElementTree = self.etree.ElementTree
2733         a = Element('a')
2734         b = SubElement(a, 'b')
2735         c = SubElement(a, 'c')
2736         d = SubElement(b, 'd')
2737         e = SubElement(c, 'e')
2738         t = ElementTree(element=a)
2739
2740         self.assertEqual(
2741             [a],
2742             list(t.getiterator('a')))
2743         a2 = SubElement(e, 'a')
2744         self.assertEqual(
2745             [a, a2],
2746             list(t.getiterator('a')))
2747
2748     def test_ns_access(self):
2749         ElementTree = self.etree.ElementTree
2750         ns = 'http://xml.infrae.com/1'
2751         f = BytesIO('<x:a xmlns:x="%s"><x:b></x:b></x:a>' % ns)
2752         t = ElementTree(file=f)
2753         a = t.getroot()
2754         self.assertEqual('{%s}a' % ns,
2755                           a.tag)
2756         self.assertEqual('{%s}b' % ns,
2757                           a[0].tag)
2758
2759     def test_ns_access2(self):
2760         ElementTree = self.etree.ElementTree
2761         ns = 'http://xml.infrae.com/1'
2762         ns2 = 'http://xml.infrae.com/2'
2763         f = BytesIO('<x:a xmlns:x="%s" xmlns:y="%s"><x:b></x:b><y:b></y:b></x:a>' % (ns, ns2))
2764         t = ElementTree(file=f)
2765         a = t.getroot()
2766         self.assertEqual('{%s}a' % ns,
2767                           a.tag)
2768         self.assertEqual('{%s}b' % ns,
2769                           a[0].tag)
2770         self.assertEqual('{%s}b' % ns2,
2771                           a[1].tag)
2772
2773     def test_ns_setting(self):
2774         Element = self.etree.Element
2775         SubElement = self.etree.SubElement
2776         ns = 'http://xml.infrae.com/1'
2777         ns2 = 'http://xml.infrae.com/2'
2778         a = Element('{%s}a' % ns)
2779         b = SubElement(a, '{%s}b' % ns2)
2780         c = SubElement(a, '{%s}c' % ns)
2781         self.assertEqual('{%s}a' % ns,
2782                           a.tag)
2783         self.assertEqual('{%s}b' % ns2,
2784                           b.tag)
2785         self.assertEqual('{%s}c' % ns,
2786                           c.tag)
2787         self.assertEqual('{%s}a' % ns,
2788                           a.tag)
2789         self.assertEqual('{%s}b' % ns2,
2790                           b.tag)
2791         self.assertEqual('{%s}c' % ns,
2792                           c.tag)
2793
2794     def test_ns_tag_parse(self):
2795         Element = self.etree.Element
2796         SubElement = self.etree.SubElement
2797         ElementTree = self.etree.ElementTree
2798
2799         ns = 'http://xml.infrae.com/1'
2800         ns2 = 'http://xml.infrae.com/2'
2801         f = BytesIO('<a xmlns="%s" xmlns:x="%s"><x:b></x:b><b></b></a>' % (ns, ns2))
2802         t = ElementTree(file=f)
2803
2804         a = t.getroot()
2805         self.assertEqual('{%s}a' % ns,
2806                           a.tag)
2807         self.assertEqual('{%s}b' % ns2,
2808                           a[0].tag)
2809         self.assertEqual('{%s}b' % ns,
2810                           a[1].tag)
2811
2812     def test_ns_attr(self):
2813         Element = self.etree.Element
2814         ns = 'http://xml.infrae.com/1'
2815         ns2 = 'http://xml.infrae.com/2'
2816         a = Element('a')
2817         a.set('{%s}foo' % ns, 'Foo')
2818         a.set('{%s}bar' % ns2, 'Bar')
2819         self.assertEqual(
2820             'Foo',
2821             a.get('{%s}foo' % ns))
2822         self.assertEqual(
2823             'Bar',
2824             a.get('{%s}bar' % ns2))
2825         try:
2826             self.assertXML(
2827                 _bytes('<a xmlns:ns0="%s" xmlns:ns1="%s" ns0:foo="Foo" ns1:bar="Bar"></a>' % (ns, ns2)),
2828                 a)
2829         except AssertionError:
2830             self.assertXML(
2831                 _bytes('<a xmlns:ns0="%s" xmlns:ns1="%s" ns1:foo="Foo" ns0:bar="Bar"></a>' % (ns2, ns)),
2832                 a)
2833
2834     def test_ns_move(self):
2835         Element = self.etree.Element
2836         one = self.etree.fromstring(
2837             _bytes('<foo><bar xmlns:ns="http://a.b.c"><ns:baz/></bar></foo>'))
2838         baz = one[0][0]
2839
2840         two = Element('root')
2841         two.append(baz)
2842         # removing the originating document could cause a crash/error before
2843         # as namespace is not moved along with it
2844         del one, baz
2845         self.assertEqual('{http://a.b.c}baz', two[0].tag)
2846
2847     def test_ns_decl_tostring(self):
2848         tostring = self.etree.tostring
2849         root = self.etree.XML(
2850             _bytes('<foo><bar xmlns:ns="http://a.b.c"><ns:baz/></bar></foo>'))
2851         baz = root[0][0]
2852
2853         nsdecl = re.findall(_bytes("xmlns(?::[a-z0-9]+)?=[\"']([^\"']+)[\"']"),
2854                             tostring(baz))
2855         self.assertEqual([_bytes("http://a.b.c")], nsdecl)
2856
2857     def test_ns_decl_tostring_default(self):
2858         tostring = self.etree.tostring
2859         root = self.etree.XML(
2860             _bytes('<foo><bar xmlns="http://a.b.c"><baz/></bar></foo>'))
2861         baz = root[0][0]
2862
2863         nsdecl = re.findall(_bytes("xmlns(?::[a-z0-9]+)?=[\"']([^\"']+)[\"']"),
2864                             tostring(baz))
2865         self.assertEqual([_bytes("http://a.b.c")], nsdecl)
2866
2867     def test_ns_decl_tostring_root(self):
2868         tostring = self.etree.tostring
2869         root = self.etree.XML(
2870             _bytes('<foo xmlns:ns="http://a.b.c"><bar><ns:baz/></bar></foo>'))
2871         baz = root[0][0]
2872
2873         nsdecl = re.findall(_bytes("xmlns(?::[a-z0-9]+)?=[\"']([^\"']+)[\"']"),
2874                             tostring(baz))
2875
2876         self.assertEqual([_bytes("http://a.b.c")], nsdecl)
2877
2878     def test_ns_decl_tostring_element(self):
2879         Element = self.etree.Element
2880         SubElement = self.etree.SubElement
2881
2882         root = Element("foo")
2883         bar = SubElement(root, "{http://a.b.c}bar")
2884         baz = SubElement(bar, "{http://a.b.c}baz")
2885
2886         nsdecl = re.findall(_bytes("xmlns(?::[a-z0-9]+)?=[\"']([^\"']+)[\"']"),
2887                             self.etree.tostring(baz))
2888
2889         self.assertEqual([_bytes("http://a.b.c")], nsdecl)
2890
2891     def test_attribute_xmlns_move(self):
2892         Element = self.etree.Element
2893
2894         root = Element('element')
2895
2896         subelement = Element('subelement',
2897                              {"{http://www.w3.org/XML/1998/namespace}id": "foo"})
2898         self.assertEqual(1, len(subelement.attrib))
2899         self.assertEqual(
2900             "foo",
2901             subelement.get("{http://www.w3.org/XML/1998/namespace}id"))
2902
2903         root.append(subelement)
2904         self.assertEqual(1, len(subelement.attrib))
2905         self.assertEqual(
2906             list({"{http://www.w3.org/XML/1998/namespace}id" : "foo"}.items()),
2907             list(subelement.attrib.items()))
2908         self.assertEqual(
2909             "foo",
2910             subelement.get("{http://www.w3.org/XML/1998/namespace}id"))
2911
2912     def test_namespaces_after_serialize(self):
2913         parse = self.etree.parse
2914         tostring = self.etree.tostring
2915
2916         ns_href = "http://a.b.c"
2917         one = parse(
2918             BytesIO('<foo><bar xmlns:ns="%s"><ns:baz/></bar></foo>' % ns_href))
2919         baz = one.getroot()[0][0]
2920
2921         parsed = parse(BytesIO( tostring(baz) )).getroot()
2922         self.assertEqual('{%s}baz' % ns_href, parsed.tag)
2923
2924     def test_attribute_namespace_roundtrip(self):
2925         fromstring = self.etree.fromstring
2926         tostring = self.etree.tostring
2927
2928         ns_href = "http://a.b.c"
2929         xml = _bytes('<root xmlns="%s" xmlns:x="%s"><el x:a="test" /></root>' % (
2930                 ns_href,ns_href))
2931         root = fromstring(xml)
2932         self.assertEqual('test', root[0].get('{%s}a' % ns_href))
2933
2934         xml2 = tostring(root)
2935         self.assertTrue(_bytes(':a=') in xml2, xml2)
2936
2937         root2 = fromstring(xml2)
2938         self.assertEqual('test', root2[0].get('{%s}a' % ns_href))
2939
2940     def test_attribute_namespace_roundtrip_replaced(self):
2941         fromstring = self.etree.fromstring
2942         tostring = self.etree.tostring
2943
2944         ns_href = "http://a.b.c"
2945         xml = _bytes('<root xmlns="%s" xmlns:x="%s"><el x:a="test" /></root>' % (
2946                 ns_href,ns_href))
2947         root = fromstring(xml)
2948         self.assertEqual('test', root[0].get('{%s}a' % ns_href))
2949
2950         root[0].set('{%s}a' % ns_href, 'TEST')
2951
2952         xml2 = tostring(root)
2953         self.assertTrue(_bytes(':a=') in xml2, xml2)
2954
2955         root2 = fromstring(xml2)
2956         self.assertEqual('TEST', root2[0].get('{%s}a' % ns_href))
2957
2958     required_versions_ET['test_register_namespace'] = (1,3)
2959     def test_register_namespace(self):
2960         # ET 1.3+
2961         Element = self.etree.Element
2962         prefix = 'TESTPREFIX'
2963         namespace = 'http://seriously.unknown/namespace/URI'
2964
2965         el = Element('{%s}test' % namespace)
2966         self.assertEqual(_bytes('<ns0:test xmlns:ns0="%s"></ns0:test>' % namespace),
2967             self._writeElement(el))
2968
2969         self.etree.register_namespace(prefix, namespace)
2970         el = Element('{%s}test' % namespace)
2971         self.assertEqual(_bytes('<%s:test xmlns:%s="%s"></%s:test>' % (
2972             prefix, prefix, namespace, prefix)),
2973             self._writeElement(el))
2974
2975         self.assertRaises(ValueError, self.etree.register_namespace, 'ns25', namespace)
2976
2977     def test_tostring(self):
2978         tostring = self.etree.tostring
2979         Element = self.etree.Element
2980         SubElement = self.etree.SubElement
2981
2982         a = Element('a')
2983         b = SubElement(a, 'b')
2984         c = SubElement(a, 'c')
2985
2986         self.assertEqual(_bytes('<a><b></b><c></c></a>'),
2987                           canonicalize(tostring(a)))
2988
2989     def test_tostring_element(self):
2990         tostring = self.etree.tostring
2991         Element = self.etree.Element
2992         SubElement = self.etree.SubElement
2993
2994         a = Element('a')
2995         b = SubElement(a, 'b')
2996         c = SubElement(a, 'c')
2997         d = SubElement(c, 'd')
2998         self.assertEqual(_bytes('<b></b>'),
2999                           canonicalize(tostring(b)))
3000         self.assertEqual(_bytes('<c><d></d></c>'),
3001                           canonicalize(tostring(c)))
3002
3003     def test_tostring_element_tail(self):
3004         tostring = self.etree.tostring
3005         Element = self.etree.Element
3006         SubElement = self.etree.SubElement
3007
3008         a = Element('a')
3009         b = SubElement(a, 'b')
3010         c = SubElement(a, 'c')
3011         d = SubElement(c, 'd')
3012         b.tail = 'Foo'
3013
3014         self.assertTrue(tostring(b) == _bytes('<b/>Foo') or
3015                      tostring(b) == _bytes('<b />Foo'))
3016
3017     required_versions_ET['test_tostring_method_html'] = (1,3)
3018     def test_tostring_method_html(self):
3019         tostring = self.etree.tostring
3020         Element = self.etree.Element
3021         SubElement = self.etree.SubElement
3022
3023         html = Element('html')
3024         body = SubElement(html, 'body')
3025         p = SubElement(body, 'p')
3026         p.text = "html"
3027         SubElement(p, 'br').tail = "test"
3028
3029         self.assertEqual(_bytes('<html><body><p>html<br>test</p></body></html>'),
3030                           tostring(html, method="html"))
3031
3032     required_versions_ET['test_tostring_method_text'] = (1,3)
3033     def test_tostring_method_text(self):
3034         tostring = self.etree.tostring
3035         Element = self.etree.Element
3036         SubElement = self.etree.SubElement
3037
3038         a = Element('a')
3039         a.text = "A"
3040         a.tail = "tail"
3041         b = SubElement(a, 'b')
3042         b.text = "B"
3043         b.tail = "TAIL"
3044         c = SubElement(a, 'c')
3045         c.text = "C"
3046
3047         self.assertEqual(_bytes('ABTAILCtail'),
3048                           tostring(a, method="text"))
3049
3050     def test_iterparse(self):
3051         iterparse = self.etree.iterparse
3052         f = BytesIO('<a><b></b><c/></a>')
3053
3054         iterator = iterparse(f)
3055         self.assertEqual(None,
3056                           iterator.root)
3057         events = list(iterator)
3058         root = iterator.root
3059         self.assertEqual(
3060             [('end', root[0]), ('end', root[1]), ('end', root)],
3061             events)
3062
3063     def test_iterparse_incomplete(self):
3064         iterparse = self.etree.iterparse
3065         f = BytesIO('<a><b></b><c/></a>')
3066
3067         iterator = iterparse(f)
3068         self.assertEqual(None,
3069                           iterator.root)
3070         event, element = next(iter(iterator))
3071         self.assertEqual('end', event)
3072         self.assertEqual('b', element.tag)
3073
3074     def test_iterparse_file(self):
3075         iterparse = self.etree.iterparse
3076         iterator = iterparse(fileInTestDir("test.xml"))
3077         self.assertEqual(None,
3078                           iterator.root)
3079         events = list(iterator)
3080         root = iterator.root
3081         self.assertEqual(
3082             [('end', root[0]), ('end', root)],
3083             events)
3084
3085     def test_iterparse_start(self):
3086         iterparse = self.etree.iterparse
3087         f = BytesIO('<a><b></b><c/></a>')
3088
3089         iterator = iterparse(f, events=('start',))
3090         events = list(iterator)
3091         root = iterator.root
3092         self.assertEqual(
3093             [('start', root), ('start', root[0]), ('start', root[1])],
3094             events)
3095
3096     def test_iterparse_start_end(self):
3097         iterparse = self.etree.iterparse
3098         f = BytesIO('<a><b></b><c/></a>')
3099
3100         iterator = iterparse(f, events=('start','end'))
3101         events = list(iterator)
3102         root = iterator.root
3103         self.assertEqual(
3104             [('start', root), ('start', root[0]), ('end', root[0]),
3105              ('start', root[1]), ('end', root[1]), ('end', root)],
3106             events)
3107
3108     def test_iterparse_clear(self):
3109         iterparse = self.etree.iterparse
3110         f = BytesIO('<a><b></b><c/></a>')
3111
3112         iterator = iterparse(f)
3113         for event, elem in iterator:
3114             elem.clear()
3115
3116         root = iterator.root
3117         self.assertEqual(0,
3118                           len(root))
3119
3120     def test_iterparse_large(self):
3121         iterparse = self.etree.iterparse
3122         CHILD_COUNT = 12345
3123         f = BytesIO('<a>%s</a>' % ('<b>test</b>'*CHILD_COUNT))
3124
3125         i = 0
3126         for key in iterparse(f):
3127             event, element = key
3128             i += 1
3129         self.assertEqual(i, CHILD_COUNT + 1)
3130
3131     def test_iterparse_set_ns_attribute(self):
3132         iterparse = self.etree.iterparse
3133         f = BytesIO('<a xmlns="http://ns1/"><b><c xmlns="http://ns2/"/></b></a>')
3134
3135         attr_name = '{http://testns/}bla'
3136         events = []
3137         iterator = iterparse(f, events=('start','end','start-ns','end-ns'))
3138         for event, elem in iterator:
3139             events.append(event)
3140             if event == 'start':
3141                 if elem.tag != '{http://ns1/}a':
3142                     elem.set(attr_name, 'value')
3143
3144         self.assertEqual(
3145             ['start-ns', 'start', 'start', 'start-ns', 'start',
3146              'end', 'end-ns', 'end', 'end', 'end-ns'],
3147             events)
3148
3149         root = iterator.root
3150         self.assertEqual(
3151             None,
3152             root.get(attr_name))
3153         self.assertEqual(
3154             'value',
3155             root[0].get(attr_name))
3156
3157     def test_iterparse_only_end_ns(self):
3158         iterparse = self.etree.iterparse
3159         f = BytesIO('<a xmlns="http://ns1/"><b><c xmlns="http://ns2/"/></b></a>')
3160
3161         attr_name = '{http://testns/}bla'
3162         events = []
3163         iterator = iterparse(f, events=('start','end','start-ns','end-ns'))
3164         for event, elem in iterator:
3165             events.append(event)
3166             if event == 'start':
3167                 if elem.tag != '{http://ns1/}a':
3168                     elem.set(attr_name, 'value')
3169
3170         self.assertEqual(
3171             ['start-ns', 'start', 'start', 'start-ns', 'start',
3172              'end', 'end-ns', 'end', 'end', 'end-ns'],
3173             events)
3174
3175         root = iterator.root
3176         self.assertEqual(
3177             None,
3178             root.get(attr_name))
3179         self.assertEqual(
3180             'value',
3181             root[0].get(attr_name))
3182
3183     def test_iterparse_getiterator(self):
3184         iterparse = self.etree.iterparse
3185         f = BytesIO('<a><b><d/></b><c/></a>')
3186
3187         counts = []
3188         for event, elem in iterparse(f):
3189             counts.append(len(list(elem.getiterator())))
3190         self.assertEqual(
3191             [1,2,1,4],
3192             counts)
3193
3194     def test_iterparse_move_elements(self):
3195         iterparse = self.etree.iterparse
3196         f = BytesIO('<a><b><d/></b><c/></a>')
3197
3198         for event, node in etree.iterparse(f): pass
3199
3200         root = etree.Element('new_root', {})
3201         root[:] = node[:]
3202
3203         self.assertEqual(
3204             ['b', 'c'],
3205             [ el.tag for el in root ])
3206
3207     def test_iterparse_cdata(self):
3208         tostring = self.etree.tostring
3209         f = BytesIO('<root><![CDATA[test]]></root>')
3210         context = self.etree.iterparse(f)
3211         content = [ el.text for event,el in context ]
3212
3213         self.assertEqual(['test'], content)
3214         self.assertEqual(_bytes('<root>test</root>'),
3215                           tostring(context.root))
3216
3217     def test_parse_file(self):
3218         parse = self.etree.parse
3219         # from file
3220         tree = parse(fileInTestDir('test.xml'))
3221         self.assertXML(
3222             _bytes('<a><b></b></a>'),
3223             tree.getroot())
3224
3225     def test_parse_file_nonexistent(self):
3226         parse = self.etree.parse
3227         self.assertRaises(IOError, parse, fileInTestDir('notthere.xml'))  
3228
3229     def test_parse_error_none(self):
3230         parse = self.etree.parse
3231         self.assertRaises(TypeError, parse, None)
3232
3233     required_versions_ET['test_parse_error'] = (1,3)
3234     def test_parse_error(self):
3235         # ET < 1.3 raises ExpatError
3236         parse = self.etree.parse
3237         f = BytesIO('<a><b></c></b></a>')
3238         self.assertRaises(SyntaxError, parse, f)
3239         f.close()
3240
3241     required_versions_ET['test_parse_error_from_file'] = (1,3)
3242     def test_parse_error_from_file(self):
3243         parse = self.etree.parse
3244         # from file
3245         f = open(fileInTestDir('test_broken.xml'), 'rb')
3246         self.assertRaises(SyntaxError, parse, f)
3247         f.close()
3248
3249     def test_parse_file_object(self):
3250         parse = self.etree.parse
3251         # from file object
3252         f = open(fileInTestDir('test.xml'), 'rb')
3253         tree = parse(f)
3254         f.close()
3255         self.assertXML(
3256             _bytes('<a><b></b></a>'),
3257             tree.getroot())
3258
3259     def test_parse_stringio(self):
3260         parse = self.etree.parse
3261         f = BytesIO('<a><b></b></a>')
3262         tree = parse(f)
3263         f.close()
3264         self.assertXML(
3265             _bytes('<a><b></b></a>'),
3266             tree.getroot()
3267            )
3268
3269     def test_parse_cdata(self):
3270         tostring = self.etree.tostring
3271         root = self.etree.XML(_bytes('<root><![CDATA[test]]></root>'))
3272
3273         self.assertEqual('test', root.text)
3274         self.assertEqual(_bytes('<root>test</root>'),
3275                           tostring(root))
3276
3277     def test_parse_with_encoding(self):
3278         # this can fail in libxml2 <= 2.6.22
3279         parse = self.etree.parse
3280         tree = parse(BytesIO('<?xml version="1.0" encoding="ascii"?><html/>'))
3281         self.assertXML(_bytes('<html></html>'),
3282                        tree.getroot())
3283
3284     def test_encoding(self):
3285         Element = self.etree.Element
3286
3287         a = Element('a')
3288         a.text = _str('Søk pÃ¥ nettet')
3289         self.assertXML(
3290             _str('<a>Søk pÃ¥ nettet</a>').encode('UTF-8'),
3291             a, 'utf-8')
3292
3293     def test_encoding_exact(self):
3294         ElementTree = self.etree.ElementTree
3295         Element = self.etree.Element
3296
3297         a = Element('a')
3298         a.text = _str('Søk pÃ¥ nettet')
3299
3300         f = BytesIO()
3301         tree = ElementTree(element=a)
3302         tree.write(f, encoding='utf-8')
3303         self.assertEqual(_str('<a>Søk pÃ¥ nettet</a>').encode('UTF-8'),
3304                           f.getvalue().replace(_bytes('\n'),_bytes('')))
3305
3306     def test_parse_file_encoding(self):
3307         parse = self.etree.parse
3308         # from file
3309         tree = parse(fileInTestDir('test-string.xml'))
3310         self.assertXML(
3311             _str('<a>Søk pÃ¥ nettet</a>').encode('UTF-8'),
3312             tree.getroot(), 'UTF-8')
3313
3314     def test_parse_file_object_encoding(self):
3315         parse = self.etree.parse
3316         # from file object
3317         f = open(fileInTestDir('test-string.xml'), 'rb')
3318         tree = parse(f)
3319         f.close()
3320         self.assertXML(
3321             _str('<a>Søk pÃ¥ nettet</a>').encode('UTF-8'),
3322             tree.getroot(), 'UTF-8')
3323
3324     def test_encoding_8bit_latin1(self):
3325         ElementTree = self.etree.ElementTree
3326         Element = self.etree.Element
3327
3328         a = Element('a')
3329         a.text = _str('Søk pÃ¥ nettet')
3330
3331         f = BytesIO()
3332         tree = ElementTree(element=a)
3333         tree.write(f, encoding='iso-8859-1')
3334         result = f.getvalue()
3335         declaration = _bytes("<?xml version=\'1.0\' encoding=\'iso-8859-1\'?>")
3336         self.assertEncodingDeclaration(result, _bytes('iso-8859-1'))
3337         result = result.split(_bytes('?>'), 1)[-1].replace(_bytes('\n'),_bytes(''))
3338         self.assertEqual(_str('<a>Søk pÃ¥ nettet</a>').encode('iso-8859-1'),
3339                           result)
3340
3341     required_versions_ET['test_parse_encoding_8bit_explicit'] = (1,3)
3342     def test_parse_encoding_8bit_explicit(self):
3343         XMLParser = self.XMLParser
3344
3345         text = _str('Søk pÃ¥ nettet')
3346         xml_latin1 = (_str('<a>%s</a>') % text).encode('iso-8859-1')
3347
3348         self.assertRaises(self.etree.ParseError,
3349                           self.etree.parse,
3350                           BytesIO(xml_latin1))
3351
3352         tree = self.etree.parse(BytesIO(xml_latin1),
3353                                 XMLParser(encoding="iso-8859-1"))
3354         a = tree.getroot()
3355         self.assertEqual(a.text, text)
3356
3357     required_versions_ET['test_parse_encoding_8bit_override'] = (1,3)
3358     def test_parse_encoding_8bit_override(self):
3359         XMLParser = self.XMLParser
3360
3361         text = _str('Søk pÃ¥ nettet')
3362         wrong_declaration = _str("<?xml version='1.0' encoding='UTF-8'?>")
3363         xml_latin1 = (_str('%s<a>%s</a>') % (wrong_declaration, text)
3364                       ).encode('iso-8859-1')
3365
3366         self.assertRaises(self.etree.ParseError,
3367                           self.etree.parse,
3368                           BytesIO(xml_latin1))
3369
3370         tree = self.etree.parse(BytesIO(xml_latin1),
3371                                 XMLParser(encoding="iso-8859-1"))
3372         a = tree.getroot()
3373         self.assertEqual(a.text, text)
3374
3375     def _test_wrong_unicode_encoding(self):
3376         # raise error on wrong encoding declaration in unicode strings
3377         XML = self.etree.XML
3378         test_utf = (_str('<?xml version="1.0" encoding="iso-8859-1"?>') +
3379                     _str('<a>Søk pÃ¥ nettet</a>'))
3380         self.assertRaises(SyntaxError, XML, test_utf)
3381
3382     def test_encoding_write_default_encoding(self):
3383         ElementTree = self.etree.ElementTree
3384         Element = self.etree.Element
3385
3386         a = Element('a')
3387         a.text = _str('Søk pÃ¥ nettet')
3388
3389         f = BytesIO()
3390         tree = ElementTree(element=a)
3391         tree.write(f)
3392         data = f.getvalue().replace(_bytes('\n'),_bytes(''))
3393         self.assertEqual(
3394             _str('<a>Søk pÃ¥ nettet</a>').encode('ASCII', 'xmlcharrefreplace'),
3395             data)
3396
3397     def test_encoding_tostring(self):
3398         Element = self.etree.Element
3399         tostring = self.etree.tostring
3400
3401         a = Element('a')
3402         a.text = _str('Søk pÃ¥ nettet')
3403         self.assertEqual(_str('<a>Søk pÃ¥ nettet</a>').encode('UTF-8'),
3404                          tostring(a, encoding='utf-8'))
3405
3406     def test_encoding_tostring_unknown(self):
3407         Element = self.etree.Element
3408         tostring = self.etree.tostring
3409
3410         a = Element('a')
3411         a.text = _str('Søk pÃ¥ nettet')
3412         self.assertRaises(LookupError, tostring, a,
3413                           encoding='Invalid Encoding')
3414
3415     def test_encoding_tostring_sub(self):
3416         Element = self.etree.Element
3417         SubElement = self.etree.SubElement
3418         tostring = self.etree.tostring
3419
3420         a = Element('a')
3421         b = SubElement(a, 'b')
3422         b.text = _str('Søk pÃ¥ nettet')
3423         self.assertEqual(_str('<b>Søk pÃ¥ nettet</b>').encode('UTF-8'),
3424                          tostring(b, encoding='utf-8'))
3425
3426     def test_encoding_tostring_sub_tail(self):
3427         Element = self.etree.Element
3428         SubElement = self.etree.SubElement
3429         tostring = self.etree.tostring
3430
3431         a = Element('a')
3432         b = SubElement(a, 'b')
3433         b.text = _str('Søk pÃ¥ nettet')
3434         b.tail = _str('Søk')
3435         self.assertEqual(_str('<b>Søk pÃ¥ nettet</b>Søk').encode('UTF-8'),
3436                          tostring(b, encoding='utf-8'))
3437
3438     def test_encoding_tostring_default_encoding(self):
3439         Element = self.etree.Element
3440         SubElement = self.etree.SubElement
3441         tostring = self.etree.tostring
3442
3443         a = Element('a')
3444         a.text = _str('Søk pÃ¥ nettet')
3445
3446         expected = _bytes('<a>S&#248;k p&#229; nettet</a>')
3447         self.assertEqual(
3448             expected,
3449             tostring(a))
3450
3451     def test_encoding_sub_tostring_default_encoding(self):
3452         Element = self.etree.Element
3453         SubElement = self.etree.SubElement
3454         tostring = self.etree.tostring
3455
3456         a = Element('a')
3457         b = SubElement(a, 'b')
3458         b.text = _str('Søk pÃ¥ nettet')
3459
3460         expected = _bytes('<b>S&#248;k p&#229; nettet</b>')
3461         self.assertEqual(
3462             expected,
3463             tostring(b))
3464
3465     def test_encoding_8bit_xml(self):
3466         utext = _str('Søk pÃ¥ nettet')
3467         uxml = _str('<p>%s</p>') % utext
3468         prologue = _bytes('<?xml version="1.0" encoding="iso-8859-1" ?>')
3469         isoxml = prologue + uxml.encode('iso-8859-1')
3470         tree = self.etree.XML(isoxml)
3471         self.assertEqual(utext, tree.text)
3472
3473     def test_encoding_utf8_bom(self):
3474         utext = _str('Søk pÃ¥ nettet')
3475         uxml = (_str('<?xml version="1.0" encoding="UTF-8"?>') +
3476                 _str('<p>%s</p>') % utext)
3477         bom = _bytes('\\xEF\\xBB\\xBF').decode("unicode_escape").encode("latin1")
3478         xml = bom + uxml.encode("utf-8")
3479         tree = etree.XML(xml)
3480         self.assertEqual(utext, tree.text)
3481
3482     def test_encoding_8bit_parse_stringio(self):
3483         utext = _str('Søk pÃ¥ nettet')
3484         uxml = _str('<p>%s</p>') % utext
3485         prologue = _bytes('<?xml version="1.0" encoding="iso-8859-1" ?>')
3486         isoxml = prologue + uxml.encode('iso-8859-1')
3487         el = self.etree.parse(BytesIO(isoxml)).getroot()
3488         self.assertEqual(utext, el.text)
3489
3490     def test_deepcopy_elementtree(self):
3491         Element = self.etree.Element
3492         ElementTree = self.etree.ElementTree
3493
3494         a = Element('a')
3495         a.text = "Foo"
3496         atree = ElementTree(a)
3497
3498         btree = copy.deepcopy(atree)
3499         self.assertEqual("Foo", atree.getroot().text)
3500         self.assertEqual("Foo", btree.getroot().text)
3501         self.assertFalse(btree is atree)
3502         self.assertFalse(btree.getroot() is atree.getroot())
3503
3504     def test_deepcopy(self):
3505         Element = self.etree.Element
3506
3507         a = Element('a')
3508         a.text = 'Foo'
3509
3510         b = copy.deepcopy(a)
3511         self.assertEqual('Foo', b.text)
3512
3513         b.text = 'Bar'
3514         self.assertEqual('Bar', b.text)
3515         self.assertEqual('Foo', a.text)
3516
3517         del a
3518         self.assertEqual('Bar', b.text)
3519
3520     def test_deepcopy_tail(self):
3521         Element = self.etree.Element
3522
3523         a = Element('a')
3524         a.tail = 'Foo'
3525
3526         b = copy.deepcopy(a)
3527         self.assertEqual('Foo', b.tail)
3528
3529         b.tail = 'Bar'
3530         self.assertEqual('Bar', b.tail)
3531         self.assertEqual('Foo', a.tail)
3532
3533         del a
3534         self.assertEqual('Bar', b.tail)
3535
3536     def test_deepcopy_subelement(self):
3537         Element = self.etree.Element
3538         SubElement = self.etree.SubElement
3539
3540         root = Element('root')
3541         a = SubElement(root, 'a')
3542         a.text = 'FooText'
3543         a.tail = 'FooTail'
3544
3545         b = copy.deepcopy(a)
3546         self.assertEqual('FooText', b.text)
3547         self.assertEqual('FooTail', b.tail)
3548
3549         b.text = 'BarText'
3550         b.tail = 'BarTail'
3551         self.assertEqual('BarTail', b.tail)
3552         self.assertEqual('FooTail', a.tail)
3553         self.assertEqual('BarText', b.text)
3554         self.assertEqual('FooText', a.text)
3555
3556         del a
3557         self.assertEqual('BarTail', b.tail)
3558         self.assertEqual('BarText', b.text)
3559
3560     def test_deepcopy_namespaces(self):
3561         root = self.etree.XML(_bytes('''<doc xmlns="dns" xmlns:t="tns">
3562         <parent><node t:foo="bar" /></parent>
3563         </doc>'''))
3564         self.assertEqual(
3565             root[0][0].get('{tns}foo'),
3566             copy.deepcopy(root[0])[0].get('{tns}foo') )
3567         self.assertEqual(
3568             root[0][0].get('{tns}foo'),
3569             copy.deepcopy(root[0][0]).get('{tns}foo') )
3570
3571     def test_deepcopy_append(self):
3572         # previously caused a crash
3573         Element = self.etree.Element
3574         tostring = self.etree.tostring
3575
3576         a = Element('a')
3577         b = copy.deepcopy(a)
3578         a.append( Element('C') )
3579         b.append( Element('X') )
3580
3581         self.assertEqual(_bytes('<a><C/></a>'),
3582                           tostring(a).replace(_bytes(' '), _bytes('')))
3583         self.assertEqual(_bytes('<a><X/></a>'),
3584                           tostring(b).replace(_bytes(' '), _bytes('')))
3585
3586     def test_deepcopy_comment(self):
3587         # previously caused a crash
3588         # not supported by ET < 1.3!
3589         Comment = self.etree.Comment
3590
3591         a = Comment("ONE")
3592         b = copy.deepcopy(a)
3593         b.text = "ANOTHER"
3594
3595         self.assertEqual('ONE',     a.text)
3596         self.assertEqual('ANOTHER', b.text)
3597
3598     def test_shallowcopy(self):
3599         Element = self.etree.Element
3600
3601         a = Element('a')
3602         a.text = 'Foo'
3603
3604         b = copy.copy(a)
3605         self.assertEqual('Foo', b.text)
3606
3607         b.text = 'Bar'
3608         self.assertEqual('Bar', b.text)
3609         self.assertEqual('Foo', a.text)
3610         # XXX ElementTree will share nodes, but lxml.etree won't..
3611
3612     def test_shallowcopy_elementtree(self):
3613         Element = self.etree.Element
3614         ElementTree = self.etree.ElementTree
3615
3616         a = Element('a')
3617         a.text = 'Foo'
3618         atree = ElementTree(a)
3619
3620         btree = copy.copy(atree)
3621         self.assertFalse(btree is atree)
3622         self.assertTrue(btree.getroot() is atree.getroot())
3623         self.assertEqual('Foo', atree.getroot().text)
3624
3625     def _test_element_boolean(self):
3626         # deprecated as of ET 1.3/lxml 2.0
3627         etree = self.etree
3628         e = etree.Element('foo')
3629         self.assertEqual(False, bool(e))
3630         etree.SubElement(e, 'bar')
3631         self.assertEqual(True, bool(e))
3632         e = etree.Element('foo')
3633         e.text = 'hey'
3634         self.assertEqual(False, bool(e))
3635         e = etree.Element('foo')
3636         e.tail = 'bar'
3637         self.assertEqual(False, bool(e))
3638         e = etree.Element('foo')
3639         e.set('bar', 'Bar')
3640         self.assertEqual(False, bool(e))
3641
3642     def test_multiple_elementrees(self):
3643         etree = self.etree
3644
3645         a = etree.Element('a')
3646         b = etree.SubElement(a, 'b')
3647
3648         t = etree.ElementTree(a)
3649         self.assertEqual(self._rootstring(t), _bytes('<a><b/></a>'))
3650
3651         t1 = etree.ElementTree(a)
3652         self.assertEqual(self._rootstring(t1), _bytes('<a><b/></a>'))
3653         self.assertEqual(self._rootstring(t),  _bytes('<a><b/></a>'))
3654
3655         t2 = etree.ElementTree(b)
3656         self.assertEqual(self._rootstring(t2), _bytes('<b/>'))
3657         self.assertEqual(self._rootstring(t1), _bytes('<a><b/></a>'))
3658         self.assertEqual(self._rootstring(t),  _bytes('<a><b/></a>'))
3659
3660     def test_qname(self):
3661         etree = self.etree
3662         qname = etree.QName('myns', 'a')
3663         a1 = etree.Element(qname)
3664         a2 = etree.SubElement(a1, qname)
3665         self.assertEqual(a1.tag, "{myns}a")
3666         self.assertEqual(a2.tag, "{myns}a")
3667
3668     def test_qname_cmp(self):
3669         etree = self.etree
3670         qname1 = etree.QName('myns', 'a')
3671         qname2 = etree.QName('myns', 'a')
3672         self.assertEqual(qname1, "{myns}a")
3673         self.assertEqual("{myns}a", qname2)
3674         self.assertEqual(qname1, qname1)
3675         self.assertEqual(qname1, qname2)
3676
3677     def test_qname_attribute_getset(self):
3678         etree = self.etree
3679         qname = etree.QName('myns', 'a')
3680
3681         a = etree.Element(qname)
3682         a.set(qname, "value")
3683
3684         self.assertEqual(a.get(qname), "value")
3685         self.assertEqual(a.get("{myns}a"), "value")
3686
3687     def test_qname_attrib(self):
3688         etree = self.etree
3689         qname = etree.QName('myns', 'a')
3690
3691         a = etree.Element(qname)
3692         a.attrib[qname] = "value"
3693
3694         self.assertEqual(a.attrib[qname], "value")
3695         self.assertEqual(a.attrib.get(qname), "value")
3696
3697         self.assertEqual(a.attrib["{myns}a"], "value")
3698         self.assertEqual(a.attrib.get("{myns}a"), "value")
3699
3700     def test_qname_attribute_resolve(self):
3701         etree = self.etree
3702         qname = etree.QName('http://myns', 'a')
3703         a = etree.Element(qname)
3704         a.set(qname, qname)
3705
3706         self.assertXML(
3707             _bytes('<ns0:a xmlns:ns0="http://myns" ns0:a="ns0:a"></ns0:a>'),
3708             a)
3709
3710     def test_qname_attribute_resolve_new(self):
3711         etree = self.etree
3712         qname = etree.QName('http://myns', 'a')
3713         a = etree.Element('a')
3714         a.set('a', qname)
3715
3716         self.assertXML(
3717             _bytes('<a xmlns:ns0="http://myns" a="ns0:a"></a>'),
3718             a)
3719
3720     def test_qname_attrib_resolve(self):
3721         etree = self.etree
3722         qname = etree.QName('http://myns', 'a')
3723         a = etree.Element(qname)
3724         a.attrib[qname] = qname
3725
3726         self.assertXML(
3727             _bytes('<ns0:a xmlns:ns0="http://myns" ns0:a="ns0:a"></ns0:a>'),
3728             a)
3729
3730     def test_parser_version(self):
3731         etree = self.etree
3732         parser = etree.XMLParser()
3733         if hasattr(parser, "version"):
3734             # ElementTree 1.3+, cET
3735             self.assertTrue(re.match("[^ ]+ [0-9.]+", parser.version))
3736
3737     # feed parser interface
3738
3739     def test_feed_parser_bytes(self):
3740         parser = self.XMLParser()
3741
3742         parser.feed(_bytes('<?xml version='))
3743         parser.feed(_bytes('"1.0"?><ro'))
3744         parser.feed(_bytes('ot><'))
3745         parser.feed(_bytes('a test="works"/'))
3746         parser.feed(_bytes('></root'))
3747         parser.feed(_bytes('>'))
3748
3749         root = parser.close()
3750
3751         self.assertEqual(root.tag, "root")
3752         self.assertEqual(root[0].tag, "a")
3753         self.assertEqual(root[0].get("test"), "works")
3754
3755     def test_feed_parser_unicode(self):
3756         parser = self.XMLParser()
3757
3758         parser.feed(_str('<ro'))
3759         parser.feed(_str('ot><'))
3760         parser.feed(_str('a test="works"/'))
3761         parser.feed(_str('></root'))
3762         parser.feed(_str('>'))
3763
3764         root = parser.close()
3765
3766         self.assertEqual(root.tag, "root")
3767         self.assertEqual(root[0].tag, "a")
3768         self.assertEqual(root[0].get("test"), "works")
3769
3770     required_versions_ET['test_feed_parser_error_close_empty'] = (1,3)
3771     def test_feed_parser_error_close_empty(self):
3772         ParseError = self.etree.ParseError
3773         parser = self.XMLParser()
3774         self.assertRaises(ParseError, parser.close)
3775
3776     required_versions_ET['test_feed_parser_error_close_incomplete'] = (1,3)
3777     def test_feed_parser_error_close_incomplete(self):
3778         ParseError = self.etree.ParseError
3779         parser = self.XMLParser()
3780
3781         parser.feed('<?xml version=')
3782         parser.feed('"1.0"?><ro')
3783
3784         self.assertRaises(ParseError, parser.close)
3785
3786     required_versions_ET['test_feed_parser_error_broken'] = (1,3)
3787     def test_feed_parser_error_broken(self):
3788         ParseError = self.etree.ParseError
3789         parser = self.XMLParser()
3790
3791         parser.feed('<?xml version=')
3792         parser.feed('"1.0"?><ro')
3793         try:
3794             parser.feed('<><><><><><><')
3795         except ParseError:
3796             # can raise, but not required before close()
3797             pass
3798
3799         self.assertRaises(ParseError, parser.close)
3800
3801     required_versions_ET['test_feed_parser_error_position'] = (1,3)
3802     def test_feed_parser_error_position(self):
3803         ParseError = self.etree.ParseError
3804         parser = self.XMLParser()
3805         try:
3806             parser.close()
3807         except ParseError:
3808             e = sys.exc_info()[1]
3809             self.assertNotEqual(None, e.code)
3810             self.assertNotEqual(0, e.code)
3811             self.assertTrue(isinstance(e.position, tuple))
3812             self.assertTrue(e.position >= (0, 0))
3813
3814     # parser target interface
3815
3816     required_versions_ET['test_parser_target_property'] = (1,3)
3817     def test_parser_target_property(self):
3818         class Target(object):
3819             pass
3820
3821         target = Target()
3822         parser = self.XMLParser(target=target)
3823
3824         self.assertEqual(target, parser.target)
3825
3826     def test_parser_target_tag(self):
3827         assertEqual = self.assertEqual
3828         assertFalse  = self.assertFalse
3829
3830         events = []
3831         class Target(object):
3832             def start(self, tag, attrib):
3833                 events.append("start")
3834                 assertFalse(attrib)
3835                 assertEqual("TAG", tag)
3836             def end(self, tag):
3837                 events.append("end")
3838                 assertEqual("TAG", tag)
3839             def close(self):
3840                 return "DONE"
3841
3842         parser = self.XMLParser(target=Target())
3843
3844         parser.feed("<TAG/>")
3845         done = parser.close()
3846
3847         self.assertEqual("DONE", done)
3848         self.assertEqual(["start", "end"], events)
3849
3850     def test_parser_target_error_in_start(self):
3851         assertEqual = self.assertEqual
3852
3853         events = []
3854         class Target(object):
3855             def start(self, tag, attrib):
3856                 events.append("start")
3857                 assertEqual("TAG", tag)
3858                 raise ValueError("TEST")
3859             def end(self, tag):
3860                 events.append("end")
3861                 assertEqual("TAG", tag)
3862             def close(self):
3863                 return "DONE"
3864
3865         parser = self.XMLParser(target=Target())
3866
3867         try:
3868             parser.feed("<TAG/>")
3869         except ValueError:
3870             self.assertTrue('TEST' in str(sys.exc_info()[1]))
3871         else:
3872             self.assertTrue(False)
3873         if 'lxml' in self.etree.__name__:
3874             self.assertEqual(["start"], events)
3875         else:
3876             # cElementTree calls end() as well
3877             self.assertTrue("start" in events)
3878
3879     def test_parser_target_error_in_end(self):
3880         assertEqual = self.assertEqual
3881
3882         events = []
3883         class Target(object):
3884             def start(self, tag, attrib):
3885                 events.append("start")
3886                 assertEqual("TAG", tag)
3887             def end(self, tag):
3888                 events.append("end")
3889                 assertEqual("TAG", tag)
3890                 raise ValueError("TEST")
3891             def close(self):
3892                 return "DONE"
3893
3894         parser = self.XMLParser(target=Target())
3895
3896         try:
3897             parser.feed("<TAG/>")
3898         except ValueError:
3899             self.assertTrue('TEST' in str(sys.exc_info()[1]))
3900         else:
3901             self.assertTrue(False)
3902         self.assertEqual(["start", "end"], events)
3903
3904     def test_parser_target_error_in_close(self):
3905         assertEqual = self.assertEqual
3906
3907         events = []
3908         class Target(object):
3909             def start(self, tag, attrib):
3910                 events.append("start")
3911                 assertEqual("TAG", tag)
3912             def end(self, tag):
3913                 events.append("end")
3914                 assertEqual("TAG", tag)
3915             def close(self):
3916                 raise ValueError("TEST")
3917
3918         parser = self.XMLParser(target=Target())
3919
3920         try:
3921             parser.feed("<TAG/>")
3922             parser.close()
3923         except ValueError:
3924             self.assertTrue('TEST' in str(sys.exc_info()[1]))
3925         else:
3926             self.assertTrue(False)
3927         self.assertEqual(["start", "end"], events)
3928
3929     def test_parser_target_error_in_start_and_close(self):
3930         assertEqual = self.assertEqual
3931
3932         events = []
3933         class Target(object):
3934             def start(self, tag, attrib):
3935                 events.append("start")
3936                 assertEqual("TAG", tag)
3937                 raise IndexError("TEST-IE")
3938             def end(self, tag):
3939                 events.append("end")
3940                 assertEqual("TAG", tag)
3941             def close(self):
3942                 raise ValueError("TEST-VE")
3943
3944         parser = self.XMLParser(target=Target())
3945
3946         try:
3947             parser.feed("<TAG/>")
3948             parser.close()
3949         except IndexError:
3950             if 'lxml' in self.etree.__name__:
3951                 # we try not to swallow the initial exception in Py2
3952                 self.assertTrue(sys.version_info[0] < 3)
3953             self.assertTrue('TEST-IE' in str(sys.exc_info()[1]))
3954         except ValueError:
3955             if 'lxml' in self.etree.__name__:
3956                 self.assertTrue(sys.version_info[0] >= 3)
3957             self.assertTrue('TEST-VE' in str(sys.exc_info()[1]))
3958         else:
3959             self.assertTrue(False)
3960
3961         if 'lxml' in self.etree.__name__:
3962             self.assertEqual(["start"], events)
3963         else:
3964             # cElementTree calls end() as well
3965             self.assertTrue("start" in events)
3966
3967     def test_elementtree_parser_target(self):
3968         assertEqual = self.assertEqual
3969         assertFalse  = self.assertFalse
3970         Element = self.etree.Element
3971
3972         events = []
3973         class Target(object):
3974             def start(self, tag, attrib):
3975                 events.append("start")
3976                 assertFalse(attrib)
3977                 assertEqual("TAG", tag)
3978             def end(self, tag):
3979                 events.append("end")
3980                 assertEqual("TAG", tag)
3981             def close(self):
3982                 return Element("DONE")
3983
3984         parser = self.XMLParser(target=Target())
3985         tree = self.etree.ElementTree()
3986         tree.parse(BytesIO("<TAG/>"), parser=parser)
3987
3988         self.assertEqual("DONE", tree.getroot().tag)
3989         self.assertEqual(["start", "end"], events)
3990
3991     def test_parser_target_attrib(self):
3992         assertEqual = self.assertEqual
3993
3994         events = []
3995         class Target(object):
3996             def start(self, tag, attrib):
3997                 events.append("start-" + tag)
3998                 for name, value in attrib.items():
3999                     assertEqual(tag + name, value)
4000             def end(self, tag):
4001                 events.append("end-" + tag)
4002             def close(self):
4003                 return "DONE"
4004
4005         parser = self.XMLParser(target=Target())
4006
4007         parser.feed('<root a="roota" b="rootb"><sub c="subc"/></root>')
4008         done = parser.close()
4009
4010         self.assertEqual("DONE", done)
4011         self.assertEqual(["start-root", "start-sub", "end-sub", "end-root"],
4012                           events)
4013
4014     def test_parser_target_data(self):
4015         events = []
4016         class Target(object):
4017             def start(self, tag, attrib):
4018                 events.append("start-" + tag)
4019             def end(self, tag):
4020                 events.append("end-" + tag)
4021             def data(self, data):
4022                 events.append("data-" + data)
4023             def close(self):
4024                 return "DONE"
4025
4026         parser = self.XMLParser(target=Target())
4027
4028         parser.feed('<root>A<sub/>B</root>')
4029         done = parser.close()
4030
4031         self.assertEqual("DONE", done)
4032         self.assertEqual(["start-root", "data-A", "start-sub",
4033                            "end-sub", "data-B", "end-root"],
4034                           events)
4035
4036     def test_parser_target_entity(self):
4037         events = []
4038         class Target(object):
4039             def __init__(self):
4040                 self._data = []
4041             def _flush_data(self):
4042                 if self._data:
4043                     events.append("data-" + ''.join(self._data))
4044                     del self._data[:]
4045             def start(self, tag, attrib):
4046                 self._flush_data()
4047                 events.append("start-" + tag)
4048             def end(self, tag):
4049                 self._flush_data()
4050                 events.append("end-" + tag)
4051             def data(self, data):
4052                 self._data.append(data)
4053             def close(self):
4054                 self._flush_data()
4055                 return "DONE"
4056
4057         parser = self.XMLParser(target=Target())
4058
4059         dtd = '''
4060             <!DOCTYPE root [
4061             <!ELEMENT root (sub*)>
4062             <!ELEMENT sub (#PCDATA)>
4063             <!ENTITY ent "an entity">
4064         ]>
4065         '''
4066         parser.feed(dtd+'<root><sub/><sub>this is &ent;</sub><sub/></root>')
4067         done = parser.close()
4068
4069         self.assertEqual("DONE", done)
4070         self.assertEqual(["start-root", "start-sub", "end-sub", "start-sub",
4071                            "data-this is an entity",
4072                            "end-sub", "start-sub", "end-sub", "end-root"],
4073                           events)
4074
4075     required_versions_ET['test_parser_target_entity_unknown'] = (1,3)
4076     def test_parser_target_entity_unknown(self):
4077         events = []
4078         class Target(object):
4079             def __init__(self):
4080                 self._data = []
4081             def _flush_data(self):
4082                 if self._data:
4083                     events.append("data-" + ''.join(self._data))
4084                     del self._data[:]
4085             def start(self, tag, attrib):
4086                 self._flush_data()
4087                 events.append("start-" + tag)
4088             def end(self, tag):
4089                 self._flush_data()
4090                 events.append("end-" + tag)
4091             def data(self, data):
4092                 self._data.append(data)
4093             def close(self):
4094                 self._flush_data()
4095                 return "DONE"
4096
4097         parser = self.XMLParser(target=Target())
4098
4099         def feed():
4100             parser.feed('<root><sub/><sub>some &ent;</sub><sub/></root>')
4101             parser.close()
4102
4103         self.assertRaises(self.etree.ParseError, feed)
4104
4105     @et_needs_pyversion(3, 8, 0, 'alpha', 4)
4106     def test_parser_target_start_end_ns(self):
4107         class Builder(list):
4108             def start(self, tag, attrib):
4109                 self.append(("start", tag))
4110             def end(self, tag):
4111                 self.append(("end", tag))
4112             def data(self, text):
4113                 pass
4114             def pi(self, target, data):
4115                 self.append(("pi", target, data))
4116             def comment(self, data):
4117                 self.append(("comment", data))
4118             def start_ns(self, prefix, uri):
4119                 self.append(("start-ns", prefix, uri))
4120             def end_ns(self, prefix):
4121                 self.append(("end-ns", prefix))
4122
4123         builder = Builder()
4124         parser = self.etree.XMLParser(target=builder)
4125         parser.feed(textwrap.dedent("""\
4126             <?pi data?>
4127             <!-- comment -->
4128             <root xmlns='namespace'>
4129                <element key='value'>text</element>
4130                <element>text</element>tail
4131                <empty-element/>
4132             </root>
4133             """))
4134         self.assertEqual(builder, [
4135                 ('pi', 'pi', 'data'),
4136                 ('comment', ' comment '),
4137                 ('start-ns', '', 'namespace'),
4138                 ('start', '{namespace}root'),
4139                 ('start', '{namespace}element'),
4140                 ('end', '{namespace}element'),
4141                 ('start', '{namespace}element'),
4142                 ('end', '{namespace}element'),
4143                 ('start', '{namespace}empty-element'),
4144                 ('end', '{namespace}empty-element'),
4145                 ('end', '{namespace}root'),
4146                 ('end-ns', ''),
4147             ])
4148
4149     @et_needs_pyversion(3, 8, 0, 'alpha', 4)
4150     def test_parser_target_end_ns(self):
4151         class Builder(list):
4152             def end_ns(self, prefix):
4153                 self.append(("end-ns", prefix))
4154
4155         builder = Builder()
4156         parser = self.etree.XMLParser(target=builder)
4157         parser.feed(textwrap.dedent("""\
4158             <?pi data?>
4159             <!-- comment -->
4160             <root xmlns='namespace' xmlns:p='pns'>
4161                <element key='value'>text</element>
4162                <p:element>text</p:element>tail
4163                <empty-element/>
4164             </root>
4165             """))
4166         self.assertEqual(builder, [
4167                 ('end-ns', 'p'),
4168                 ('end-ns', ''),
4169             ])
4170
4171     def test_treebuilder(self):
4172         builder = self.etree.TreeBuilder()
4173         el = builder.start("root", {'a':'A', 'b':'B'})
4174         self.assertEqual("root", el.tag)
4175         self.assertEqual({'a':'A', 'b':'B'}, el.attrib)
4176         builder.data("ROOTTEXT")
4177         el = builder.start("child", {'x':'X', 'y':'Y'})
4178         self.assertEqual("child", el.tag)
4179         self.assertEqual({'x':'X', 'y':'Y'}, el.attrib)
4180         builder.data("CHILDTEXT")
4181         el = builder.end("child")
4182         self.assertEqual("child", el.tag)
4183         self.assertEqual({'x':'X', 'y':'Y'}, el.attrib)
4184         self.assertEqual("CHILDTEXT", el.text)
4185         self.assertEqual(None, el.tail)
4186         builder.data("CHILDTAIL")
4187         root = builder.end("root")
4188
4189         self.assertEqual("root", root.tag)
4190         self.assertEqual("ROOTTEXT", root.text)
4191         self.assertEqual("CHILDTEXT", root[0].text)
4192         self.assertEqual("CHILDTAIL", root[0].tail)
4193
4194     def test_treebuilder_target(self):
4195         parser = self.XMLParser(target=self.etree.TreeBuilder())
4196         parser.feed('<root>ROOTTEXT<child>CHILDTEXT</child>CHILDTAIL</root>')
4197         root = parser.close()
4198
4199         self.assertEqual("root", root.tag)
4200         self.assertEqual("ROOTTEXT", root.text)
4201         self.assertEqual("CHILDTEXT", root[0].text)
4202         self.assertEqual("CHILDTAIL", root[0].tail)
4203
4204     @et_needs_pyversion(3, 8, 0, 'alpha', 4)
4205     def test_treebuilder_comment(self):
4206         ET = self.etree
4207         b = ET.TreeBuilder()
4208         self.assertEqual(b.comment('ctext').tag, ET.Comment)
4209         self.assertEqual(b.comment('ctext').text, 'ctext')
4210
4211         b = ET.TreeBuilder(comment_factory=ET.Comment)
4212         self.assertEqual(b.comment('ctext').tag, ET.Comment)
4213         self.assertEqual(b.comment('ctext').text, 'ctext')
4214
4215         #b = ET.TreeBuilder(comment_factory=len)
4216         #self.assertEqual(b.comment('ctext'), len('ctext'))
4217
4218     @et_needs_pyversion(3, 8, 0, 'alpha', 4)
4219     def test_treebuilder_pi(self):
4220         ET = self.etree
4221         is_lxml = ET.__name__ == 'lxml.etree'
4222
4223         b = ET.TreeBuilder()
4224         self.assertEqual(b.pi('target', None).tag, ET.PI)
4225         if is_lxml:
4226             self.assertEqual(b.pi('target', None).target, 'target')
4227         else:
4228             self.assertEqual(b.pi('target', None).text, 'target')
4229
4230         b = ET.TreeBuilder(pi_factory=ET.PI)
4231         self.assertEqual(b.pi('target').tag, ET.PI)
4232         if is_lxml:
4233             self.assertEqual(b.pi('target').target, "target")
4234         else:
4235             self.assertEqual(b.pi('target').text, "target")
4236         self.assertEqual(b.pi('pitarget', ' text ').tag, ET.PI)
4237         if is_lxml:
4238             self.assertEqual(b.pi('pitarget', ' text ').target, "pitarget")
4239             self.assertEqual(b.pi('pitarget', ' text ').text, " text ")
4240         else:
4241             self.assertEqual(b.pi('pitarget', ' text ').text, "pitarget  text ")
4242
4243         #b = ET.TreeBuilder(pi_factory=lambda target, text: (len(target), text))
4244         #self.assertEqual(b.pi('target'), (len('target'), None))
4245         #self.assertEqual(b.pi('pitarget', ' text '), (len('pitarget'), ' text '))
4246
4247     def test_late_tail(self):
4248         # Issue #37399: The tail of an ignored comment could overwrite the text before it.
4249         ET = self.etree
4250         class TreeBuilderSubclass(ET.TreeBuilder):
4251             pass
4252
4253         if ET.__name__ == 'lxml.etree':
4254             def assert_content(a):
4255                 self.assertEqual(a.text, "text")
4256                 self.assertEqual(a[0].tail, "tail")
4257         else:
4258             def assert_content(a):
4259                 self.assertEqual(a.text, "texttail")
4260
4261         xml = "<a>text<!-- comment -->tail</a>"
4262         a = ET.fromstring(xml)
4263         assert_content(a)
4264
4265         parser = ET.XMLParser(target=TreeBuilderSubclass())
4266         parser.feed(xml)
4267         a = parser.close()
4268         assert_content(a)
4269
4270         xml = "<a>text<?pi data?>tail</a>"
4271         a = ET.fromstring(xml)
4272         assert_content(a)
4273
4274         xml = "<a>text<?pi data?>tail</a>"
4275         parser = ET.XMLParser(target=TreeBuilderSubclass())
4276         parser.feed(xml)
4277         a = parser.close()
4278         assert_content(a)
4279
4280     @et_needs_pyversion(3, 8, 0, 'alpha', 4)
4281     def test_late_tail_mix_pi_comments(self):
4282         # Issue #37399: The tail of an ignored comment could overwrite the text before it.
4283         # Test appending tails to comments/pis.
4284         ET = self.etree
4285         class TreeBuilderSubclass(ET.TreeBuilder):
4286             pass
4287
4288         xml = "<a>text<?pi1?> <!-- comment -->\n<?pi2?>tail</a>"
4289         parser = ET.XMLParser(target=ET.TreeBuilder(insert_comments=True, insert_pis=False))
4290         parser.feed(xml)
4291         a = parser.close()
4292         self.assertEqual(a[0].text, ' comment ')
4293         self.assertEqual(a[0].tail, '\ntail')
4294         self.assertEqual(a.text, "text ")
4295
4296         parser = ET.XMLParser(target=TreeBuilderSubclass(insert_comments=True, insert_pis=False))
4297         parser.feed(xml)
4298         a = parser.close()
4299         self.assertEqual(a[0].text, ' comment ')
4300         self.assertEqual(a[0].tail, '\ntail')
4301         self.assertEqual(a.text, "text ")
4302
4303         xml = "<a>text<!-- comment -->\n<?pi data?>tail</a>"
4304         parser = ET.XMLParser(target=ET.TreeBuilder(insert_pis=True, insert_comments=False))
4305         parser.feed(xml)
4306         a = parser.close()
4307         self.assertEqual(a[0].text[-4:], 'data')
4308         self.assertEqual(a[0].tail, 'tail')
4309         self.assertEqual(a.text, "text\n")
4310
4311         parser = ET.XMLParser(target=TreeBuilderSubclass(insert_pis=True, insert_comments=False))
4312         parser.feed(xml)
4313         a = parser.close()
4314         self.assertEqual(a[0].text[-4:], 'data')
4315         self.assertEqual(a[0].tail, 'tail')
4316         self.assertEqual(a.text, "text\n")
4317
4318     # helper methods
4319
4320     def _writeElement(self, element, encoding='us-ascii'):
4321         """Write out element for comparison.
4322         """
4323         data = self.etree.tostring(element, encoding=encoding)
4324         return canonicalize(data)
4325
4326     def _writeElementFile(self, element, encoding='us-ascii'):
4327         """Write out element for comparison, using real file.
4328         """
4329         ElementTree = self.etree.ElementTree
4330         with tmpfile() as filename:
4331             with open(filename, 'wb') as f:
4332                 tree = ElementTree(element=element)
4333                 tree.write(f, encoding=encoding)
4334             with open(filename, 'rb') as f:
4335                 data = f.read()
4336         return canonicalize(data)
4337
4338     def assertXML(self, expected, element, encoding='us-ascii'):
4339         """Writes element out and checks whether it is expected.
4340
4341         Does this two ways; once using BytesIO, once using a real file.
4342         """
4343         if isinstance(expected, unicode):
4344             expected = expected.encode(encoding)
4345         self.assertEqual(expected, self._writeElement(element, encoding))
4346         self.assertEqual(expected, self._writeElementFile(element, encoding))
4347
4348     def assertEncodingDeclaration(self, result, encoding):
4349         "Checks if the result XML byte string specifies the encoding."
4350         enc_re = r"<\?xml[^>]+ encoding=[\"']([^\"']+)[\"']"
4351         if isinstance(result, str):
4352             has_encoding = re.compile(enc_re).match
4353         else:
4354             has_encoding = re.compile(_bytes(enc_re)).match
4355         self.assertTrue(has_encoding(result))
4356         result_encoding = has_encoding(result).group(1)
4357         self.assertEqual(result_encoding.upper(), encoding.upper())
4358
4359     def _rootstring(self, tree):
4360         return self.etree.tostring(tree.getroot()).replace(
4361             _bytes(' '), _bytes('')).replace(_bytes('\n'), _bytes(''))
4362
4363     def _check_element_tree(self, tree):
4364         self._check_element(tree.getroot())
4365
4366     def _check_element(self, element):
4367         self.assertTrue(hasattr(element, 'tag'))
4368         self.assertTrue(hasattr(element, 'attrib'))
4369         self.assertTrue(hasattr(element, 'text'))
4370         self.assertTrue(hasattr(element, 'tail'))
4371         self._check_string(element.tag)
4372         self._check_mapping(element.attrib)
4373         if element.text is not None:
4374             self._check_string(element.text)
4375         if element.tail is not None:
4376             self._check_string(element.tail)
4377
4378     def _check_string(self, string):
4379         len(string)
4380         for char in string:
4381             self.assertEqual(1, len(char))
4382         new_string = string + ""
4383         new_string = string + " "
4384         string[:0]
4385
4386     def _check_mapping(self, mapping):
4387         len(mapping)
4388         keys = mapping.keys()
4389         values = mapping.values()
4390         items = mapping.items()
4391         for key in keys:
4392             item = mapping[key]
4393         mapping["key"] = "value"
4394         self.assertEqual("value", mapping["key"])
4395
4396
4397 class _ElementSlicingTest(unittest.TestCase):
4398     etree = None
4399
4400     def _elem_tags(self, elemlist):
4401         return [e.tag for e in elemlist]
4402
4403     def _subelem_tags(self, elem):
4404         return self._elem_tags(list(elem))
4405
4406     def _make_elem_with_children(self, numchildren):
4407         """Create an Element with a tag 'a', with the given amount of children
4408            named 'a0', 'a1' ... and so on.
4409
4410         """
4411         e = self.etree.Element('a')
4412         for i in range(numchildren):
4413             self.etree.SubElement(e, 'a%s' % i)
4414         return e
4415
4416     def test_getslice_single_index(self):
4417         e = self._make_elem_with_children(10)
4418
4419         self.assertEqual(e[1].tag, 'a1')
4420         self.assertEqual(e[-2].tag, 'a8')
4421
4422         self.assertRaises(IndexError, lambda: e[12])
4423         self.assertRaises(IndexError, lambda: e[-12])
4424
4425     def test_getslice_range(self):
4426         e = self._make_elem_with_children(6)
4427
4428         self.assertEqual(self._elem_tags(e[3:]), ['a3', 'a4', 'a5'])
4429         self.assertEqual(self._elem_tags(e[3:6]), ['a3', 'a4', 'a5'])
4430         self.assertEqual(self._elem_tags(e[3:16]), ['a3', 'a4', 'a5'])
4431         self.assertEqual(self._elem_tags(e[3:5]), ['a3', 'a4'])
4432         self.assertEqual(self._elem_tags(e[3:-1]), ['a3', 'a4'])
4433         self.assertEqual(self._elem_tags(e[:2]), ['a0', 'a1'])
4434
4435     def test_getslice_steps(self):
4436         e = self._make_elem_with_children(10)
4437
4438         self.assertEqual(self._elem_tags(e[8:10:1]), ['a8', 'a9'])
4439         self.assertEqual(self._elem_tags(e[::3]), ['a0', 'a3', 'a6', 'a9'])
4440         self.assertEqual(self._elem_tags(e[::8]), ['a0', 'a8'])
4441         self.assertEqual(self._elem_tags(e[1::8]), ['a1', 'a9'])
4442         self.assertEqual(self._elem_tags(e[3::sys.maxsize]), ['a3'])
4443         self.assertEqual(self._elem_tags(e[3::sys.maxsize<<64]), ['a3'])
4444
4445     def test_getslice_negative_steps(self):
4446         e = self._make_elem_with_children(4)
4447
4448         self.assertEqual(self._elem_tags(e[::-1]), ['a3', 'a2', 'a1', 'a0'])
4449         self.assertEqual(self._elem_tags(e[::-2]), ['a3', 'a1'])
4450         self.assertEqual(self._elem_tags(e[3::-sys.maxsize]), ['a3'])
4451         self.assertEqual(self._elem_tags(e[3::-sys.maxsize-1]), ['a3'])
4452         self.assertEqual(self._elem_tags(e[3::-sys.maxsize<<64]), ['a3'])
4453
4454     def test_delslice(self):
4455         e = self._make_elem_with_children(4)
4456         del e[0:2]
4457         self.assertEqual(self._subelem_tags(e), ['a2', 'a3'])
4458
4459         e = self._make_elem_with_children(4)
4460         del e[0:]
4461         self.assertEqual(self._subelem_tags(e), [])
4462
4463         e = self._make_elem_with_children(4)
4464         del e[::-1]
4465         self.assertEqual(self._subelem_tags(e), [])
4466
4467         e = self._make_elem_with_children(4)
4468         del e[::-2]
4469         self.assertEqual(self._subelem_tags(e), ['a0', 'a2'])
4470
4471         e = self._make_elem_with_children(4)
4472         del e[1::2]
4473         self.assertEqual(self._subelem_tags(e), ['a0', 'a2'])
4474
4475         e = self._make_elem_with_children(2)
4476         del e[::2]
4477         self.assertEqual(self._subelem_tags(e), ['a1'])
4478
4479     def test_setslice_single_index(self):
4480         e = self._make_elem_with_children(4)
4481         e[1] = self.etree.Element('b')
4482         self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'a2', 'a3'])
4483
4484         e[-2] = self.etree.Element('c')
4485         self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'c', 'a3'])
4486
4487         with self.assertRaises(IndexError):
4488             e[5] = self.etree.Element('d')
4489         with self.assertRaises(IndexError):
4490             e[-5] = self.etree.Element('d')
4491         self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'c', 'a3'])
4492
4493     def test_setslice_range(self):
4494         e = self._make_elem_with_children(4)
4495         e[1:3] = [self.etree.Element('b%s' % i) for i in range(2)]
4496         self.assertEqual(self._subelem_tags(e), ['a0', 'b0', 'b1', 'a3'])
4497
4498         e = self._make_elem_with_children(4)
4499         e[1:3] = [self.etree.Element('b')]
4500         self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'a3'])
4501
4502         e = self._make_elem_with_children(4)
4503         e[1:3] = [self.etree.Element('b%s' % i) for i in range(3)]
4504         self.assertEqual(self._subelem_tags(e), ['a0', 'b0', 'b1', 'b2', 'a3'])
4505
4506     def test_setslice_steps(self):
4507         e = self._make_elem_with_children(6)
4508         e[1:5:2] = [self.etree.Element('b%s' % i) for i in range(2)]
4509         self.assertEqual(self._subelem_tags(e), ['a0', 'b0', 'a2', 'b1', 'a4', 'a5'])
4510
4511         e = self._make_elem_with_children(6)
4512         with self.assertRaises(ValueError):
4513             e[1:5:2] = [self.etree.Element('b')]
4514         with self.assertRaises(ValueError):
4515             e[1:5:2] = [self.etree.Element('b%s' % i) for i in range(3)]
4516         with self.assertRaises(ValueError):
4517             e[1:5:2] = []
4518         self.assertEqual(self._subelem_tags(e), ['a0', 'a1', 'a2', 'a3', 'a4', 'a5'])
4519
4520         e = self._make_elem_with_children(4)
4521         e[1::sys.maxsize] = [self.etree.Element('b')]
4522         self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'a2', 'a3'])
4523         e[1::sys.maxsize<<64] = [self.etree.Element('c')]
4524         self.assertEqual(self._subelem_tags(e), ['a0', 'c', 'a2', 'a3'])
4525
4526     def test_setslice_negative_steps(self):
4527         e = self._make_elem_with_children(4)
4528         e[2:0:-1] = [self.etree.Element('b%s' % i) for i in range(2)]
4529         self.assertEqual(self._subelem_tags(e), ['a0', 'b1', 'b0', 'a3'])
4530
4531         e = self._make_elem_with_children(4)
4532         with self.assertRaises(ValueError):
4533             e[2:0:-1] = [self.etree.Element('b')]
4534         with self.assertRaises(ValueError):
4535             e[2:0:-1] = [self.etree.Element('b%s' % i) for i in range(3)]
4536         with self.assertRaises(ValueError):
4537             e[2:0:-1] = []
4538         self.assertEqual(self._subelem_tags(e), ['a0', 'a1', 'a2', 'a3'])
4539
4540         e = self._make_elem_with_children(4)
4541         e[1::-sys.maxsize] = [self.etree.Element('b')]
4542         self.assertEqual(self._subelem_tags(e), ['a0', 'b', 'a2', 'a3'])
4543         e[1::-sys.maxsize-1] = [self.etree.Element('c')]
4544         self.assertEqual(self._subelem_tags(e), ['a0', 'c', 'a2', 'a3'])
4545         e[1::-sys.maxsize<<64] = [self.etree.Element('d')]
4546         self.assertEqual(self._subelem_tags(e), ['a0', 'd', 'a2', 'a3'])
4547
4548
4549 class _XMLPullParserTest(unittest.TestCase):
4550     etree = None
4551
4552     def _close_and_return_root(self, parser):
4553         if 'ElementTree' in self.etree.__name__:
4554             # ElementTree's API is a bit unwieldy in Py3.4
4555             root = parser._close_and_return_root()
4556         else:
4557             root = parser.close()
4558         return root
4559
4560     def _feed(self, parser, data, chunk_size=None):
4561         if chunk_size is None:
4562             parser.feed(data)
4563         else:
4564             for i in range(0, len(data), chunk_size):
4565                 parser.feed(data[i:i+chunk_size])
4566
4567     def assert_events(self, parser, expected, max_events=None):
4568         self.assertEqual(
4569             [(event, (elem.tag, elem.text))
4570              for event, elem in islice(parser.read_events(), max_events)],
4571             expected)
4572
4573     def assert_event_tuples(self, parser, expected, max_events=None):
4574         self.assertEqual(
4575             list(islice(parser.read_events(), max_events)),
4576             expected)
4577
4578     def assert_event_tags(self, parser, expected, max_events=None):
4579         events = islice(parser.read_events(), max_events)
4580         self.assertEqual([(action, elem.tag) for action, elem in events],
4581                          expected)
4582
4583     def test_simple_xml(self):
4584         for chunk_size in (None, 1, 5):
4585             #with self.subTest(chunk_size=chunk_size):
4586                 parser = self.etree.XMLPullParser()
4587                 self.assert_event_tags(parser, [])
4588                 self._feed(parser, "<!-- comment -->\n", chunk_size)
4589                 self.assert_event_tags(parser, [])
4590                 self._feed(parser,
4591                            "<root>\n  <element key='value'>text</element",
4592                            chunk_size)
4593                 self.assert_event_tags(parser, [])
4594                 self._feed(parser, ">\n", chunk_size)
4595                 self.assert_event_tags(parser, [('end', 'element')])
4596                 self._feed(parser, "<element>text</element>tail\n", chunk_size)
4597                 self._feed(parser, "<empty-element/>\n", chunk_size)
4598                 self.assert_event_tags(parser, [
4599                     ('end', 'element'),
4600                     ('end', 'empty-element'),
4601                     ])
4602                 self._feed(parser, "</root>\n", chunk_size)
4603                 self.assert_event_tags(parser, [('end', 'root')])
4604                 root = self._close_and_return_root(parser)
4605                 self.assertEqual(root.tag, 'root')
4606
4607     def test_feed_while_iterating(self):
4608         parser = self.etree.XMLPullParser()
4609         it = parser.read_events()
4610         self._feed(parser, "<root>\n  <element key='value'>text</element>\n")
4611         action, elem = next(it)
4612         self.assertEqual((action, elem.tag), ('end', 'element'))
4613         self._feed(parser, "</root>\n")
4614         action, elem = next(it)
4615         self.assertEqual((action, elem.tag), ('end', 'root'))
4616         with self.assertRaises(StopIteration):
4617             next(it)
4618
4619     def test_simple_xml_with_ns(self):
4620         parser = self.etree.XMLPullParser()
4621         self.assert_event_tags(parser, [])
4622         self._feed(parser, "<!-- comment -->\n")
4623         self.assert_event_tags(parser, [])
4624         self._feed(parser, "<root xmlns='namespace'>\n")
4625         self.assert_event_tags(parser, [])
4626         self._feed(parser, "<element key='value'>text</element")
4627         self.assert_event_tags(parser, [])
4628         self._feed(parser, ">\n")
4629         self.assert_event_tags(parser, [('end', '{namespace}element')])
4630         self._feed(parser, "<element>text</element>tail\n")
4631         self._feed(parser, "<empty-element/>\n")
4632         self.assert_event_tags(parser, [
4633             ('end', '{namespace}element'),
4634             ('end', '{namespace}empty-element'),
4635             ])
4636         self._feed(parser, "</root>\n")
4637         self.assert_event_tags(parser, [('end', '{namespace}root')])
4638         root = self._close_and_return_root(parser)
4639         self.assertEqual(root.tag, '{namespace}root')
4640
4641     def test_ns_events(self):
4642         parser = self.etree.XMLPullParser(events=('start-ns', 'end-ns'))
4643         self._feed(parser, "<!-- comment -->\n")
4644         self._feed(parser, "<root xmlns='namespace'>\n")
4645         self.assertEqual(
4646             list(parser.read_events()),
4647             [('start-ns', ('', 'namespace'))])
4648         self._feed(parser, "<element key='value'>text</element")
4649         self._feed(parser, ">\n")
4650         self._feed(parser, "<element>text</element>tail\n")
4651         self._feed(parser, "<empty-element/>\n")
4652         self._feed(parser, "</root>\n")
4653         self.assertEqual(list(parser.read_events()), [('end-ns', None)])
4654         parser.close()
4655
4656     def test_ns_events_end_ns_only(self):
4657         parser = self.etree.XMLPullParser(events=['end-ns'])
4658         self._feed(parser, "<!-- comment -->\n")
4659         self._feed(parser, "<root xmlns='namespace' xmlns:a='abc' xmlns:b='xyz'>\n")
4660         self.assertEqual(list(parser.read_events()), [])
4661         self._feed(parser, "<a:element key='value'>text</a:element")
4662         self._feed(parser, ">\n")
4663         self._feed(parser, "<b:element>text</b:element>tail\n")
4664         self._feed(parser, "<empty-element/>\n")
4665         self.assertEqual(list(parser.read_events()), [])
4666         self._feed(parser, "</root>\n")
4667         self.assertEqual(list(parser.read_events()), [
4668             ('end-ns', None),
4669             ('end-ns', None),
4670             ('end-ns', None),
4671         ])
4672         parser.close()
4673
4674     @et_needs_pyversion(3,8)
4675     def test_ns_events_start(self):
4676         parser = self.etree.XMLPullParser(events=('start-ns', 'start', 'end'))
4677         self._feed(parser, "<tag xmlns='abc' xmlns:p='xyz'>\n")
4678         self.assert_event_tuples(parser, [
4679             ('start-ns', ('', 'abc')),
4680             ('start-ns', ('p', 'xyz')),
4681         ], max_events=2)
4682         self.assert_event_tags(parser, [
4683             ('start', '{abc}tag'),
4684         ], max_events=1)
4685
4686         self._feed(parser, "<child />\n")
4687         self.assert_event_tags(parser, [
4688             ('start', '{abc}child'),
4689             ('end', '{abc}child'),
4690         ])
4691
4692         self._feed(parser, "</tag>\n")
4693         parser.close()
4694         self.assert_event_tags(parser, [
4695             ('end', '{abc}tag'),
4696         ])
4697
4698     @et_needs_pyversion(3,8)
4699     def test_ns_events_start_end(self):
4700         parser = self.etree.XMLPullParser(events=('start-ns', 'start', 'end', 'end-ns'))
4701         self._feed(parser, "<tag xmlns='abc' xmlns:p='xyz'>\n")
4702         self.assert_event_tuples(parser, [
4703             ('start-ns', ('', 'abc')),
4704             ('start-ns', ('p', 'xyz')),
4705         ], max_events=2)
4706         self.assert_event_tags(parser, [
4707             ('start', '{abc}tag'),
4708         ], max_events=1)
4709
4710         self._feed(parser, "<child />\n")
4711         self.assert_event_tags(parser, [
4712             ('start', '{abc}child'),
4713             ('end', '{abc}child'),
4714         ])
4715
4716         self._feed(parser, "</tag>\n")
4717         parser.close()
4718         self.assert_event_tags(parser, [
4719             ('end', '{abc}tag'),
4720         ], max_events=1)
4721         self.assert_event_tuples(parser, [
4722             ('end-ns', None),
4723             ('end-ns', None),
4724         ])
4725
4726     def test_events(self):
4727         parser = self.etree.XMLPullParser(events=())
4728         self._feed(parser, "<root/>\n")
4729         self.assert_event_tags(parser, [])
4730
4731         parser = self.etree.XMLPullParser(events=('start', 'end'))
4732         self._feed(parser, "<!-- text here -->\n")
4733         self.assert_events(parser, [])
4734
4735         parser = self.etree.XMLPullParser(events=('start', 'end'))
4736         self._feed(parser, "<root>\n")
4737         self.assert_event_tags(parser, [('start', 'root')])
4738         self._feed(parser, "<element key='value'>text</element")
4739         self.assert_event_tags(parser, [('start', 'element')])
4740         self._feed(parser, ">\n")
4741         self.assert_event_tags(parser, [('end', 'element')])
4742         self._feed(parser,
4743                    "<element xmlns='foo'>text<empty-element/></element>tail\n")
4744         self.assert_event_tags(parser, [
4745             ('start', '{foo}element'),
4746             ('start', '{foo}empty-element'),
4747             ('end', '{foo}empty-element'),
4748             ('end', '{foo}element'),
4749             ])
4750         self._feed(parser, "</root>")
4751         root = self._close_and_return_root(parser)
4752         self.assert_event_tags(parser, [('end', 'root')])
4753         self.assertEqual(root.tag, 'root')
4754
4755         parser = self.etree.XMLPullParser(events=('start',))
4756         self._feed(parser, "<!-- comment -->\n")
4757         self.assert_event_tags(parser, [])
4758         self._feed(parser, "<root>\n")
4759         self.assert_event_tags(parser, [('start', 'root')])
4760         self._feed(parser, "<element key='value'>text</element")
4761         self.assert_event_tags(parser, [('start', 'element')])
4762         self._feed(parser, ">\n")
4763         self.assert_event_tags(parser, [])
4764         self._feed(parser,
4765                    "<element xmlns='foo'>text<empty-element/></element>tail\n")
4766         self.assert_event_tags(parser, [
4767             ('start', '{foo}element'),
4768             ('start', '{foo}empty-element'),
4769             ])
4770         self._feed(parser, "</root>")
4771         root = self._close_and_return_root(parser)
4772         self.assertEqual(root.tag, 'root')
4773
4774     @et_needs_pyversion(3, 8, 0, 'alpha', 4)
4775     def test_events_comment(self):
4776         parser = self.etree.XMLPullParser(events=('start', 'comment', 'end'))
4777         self._feed(parser, "<!-- text here -->\n")
4778         self.assert_events(parser, [('comment', (self.etree.Comment, ' text here '))])
4779         self._feed(parser, "<!-- more text here -->\n")
4780         self.assert_events(parser, [('comment', (self.etree.Comment, ' more text here '))])
4781         self._feed(parser, "<root-tag>text")
4782         self.assert_event_tags(parser, [('start', 'root-tag')])
4783         self._feed(parser, "<!-- inner comment-->\n")
4784         self.assert_events(parser, [('comment', (self.etree.Comment, ' inner comment'))])
4785         self._feed(parser, "</root-tag>\n")
4786         self.assert_event_tags(parser, [('end', 'root-tag')])
4787         self._feed(parser, "<!-- outer comment -->\n")
4788         self.assert_events(parser, [('comment', (self.etree.Comment, ' outer comment '))])
4789
4790         parser = self.etree.XMLPullParser(events=('comment',))
4791         self._feed(parser, "<!-- text here -->\n")
4792         self.assert_events(parser, [('comment', (self.etree.Comment, ' text here '))])
4793
4794     @et_needs_pyversion(3, 8, 0, 'alpha', 4)
4795     def test_events_pi(self):
4796         # Note: lxml's PIs have target+text, ET's PIs have both in "text"
4797         parser = self.etree.XMLPullParser(events=('start', 'pi', 'end'))
4798         self._feed(parser, "<?pitarget?>\n")
4799         self.assert_event_tags(parser, [('pi', self.etree.PI)])
4800         parser = self.etree.XMLPullParser(events=('pi',))
4801         self._feed(parser, "<?pitarget some text ?>\n")
4802         self.assert_event_tags(parser, [('pi', self.etree.PI)])
4803
4804     def test_events_sequence(self):
4805         # Test that events can be some sequence that's not just a tuple or list
4806         eventset = {'end', 'start'}
4807         parser = self.etree.XMLPullParser(events=eventset)
4808         self._feed(parser, "<foo>bar</foo>")
4809         self.assert_event_tags(parser, [('start', 'foo'), ('end', 'foo')])
4810
4811         class DummyIter(object):
4812             def __init__(self):
4813                 self.events = iter(['start', 'end', 'start-ns'])
4814             def __iter__(self):
4815                 return self
4816             def __next__(self):
4817                 return next(self.events)
4818             def next(self):
4819                 return next(self.events)
4820
4821         parser = self.etree.XMLPullParser(events=DummyIter())
4822         self._feed(parser, "<foo>bar</foo>")
4823         self.assert_event_tags(parser, [('start', 'foo'), ('end', 'foo')])
4824
4825     def test_unknown_event(self):
4826         with self.assertRaises(ValueError):
4827             self.etree.XMLPullParser(events=('start', 'end', 'bogus'))
4828
4829
4830 class _C14NTest(unittest.TestCase):
4831     etree = None
4832     maxDiff = None
4833
4834     if not hasattr(unittest.TestCase, 'subTest'):
4835         @contextmanager
4836         def subTest(self, name, **kwargs):
4837             try:
4838                 yield
4839             except unittest.SkipTest:
4840                 raise
4841             except Exception as e:
4842                 print("Subtest {} failed: {}".format(name, e))
4843                 raise
4844
4845     def _canonicalize(self, input_file, **options):
4846         return self.etree.canonicalize(from_file=input_file, **options)
4847
4848     #
4849     # simple roundtrip tests (from c14n.py)
4850
4851     def c14n_roundtrip(self, xml, **options):
4852         return self.etree.canonicalize(xml, **options)
4853
4854     def test_simple_roundtrip(self):
4855         c14n_roundtrip = self.c14n_roundtrip
4856         # Basics
4857         self.assertEqual(c14n_roundtrip("<doc/>"), '<doc></doc>')
4858         self.assertEqual(c14n_roundtrip("<doc xmlns='uri'/>"), # FIXME
4859                 '<doc xmlns="uri"></doc>')
4860         self.assertEqual(c14n_roundtrip("<prefix:doc xmlns:prefix='uri'/>"),
4861             '<prefix:doc xmlns:prefix="uri"></prefix:doc>')
4862         self.assertEqual(c14n_roundtrip("<doc xmlns:prefix='uri'><prefix:bar/></doc>"),
4863             '<doc><prefix:bar xmlns:prefix="uri"></prefix:bar></doc>')
4864         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/' />"),
4865             '<elem></elem>')
4866
4867         # C14N spec
4868         self.assertEqual(c14n_roundtrip("<doc>Hello, world!<!-- Comment 1 --></doc>"),
4869             '<doc>Hello, world!</doc>')
4870         self.assertEqual(c14n_roundtrip("<value>&#x32;</value>"),
4871             '<value>2</value>')
4872         self.assertEqual(c14n_roundtrip('<compute><![CDATA[value>"0" && value<"10" ?"valid":"error"]]></compute>'),
4873             '<compute>value&gt;"0" &amp;&amp; value&lt;"10" ?"valid":"error"</compute>')
4874         self.assertEqual(c14n_roundtrip('''<compute expr='value>"0" &amp;&amp; value&lt;"10" ?"valid":"error"'>valid</compute>'''),
4875             '<compute expr="value>&quot;0&quot; &amp;&amp; value&lt;&quot;10&quot; ?&quot;valid&quot;:&quot;error&quot;">valid</compute>')
4876         self.assertEqual(c14n_roundtrip("<norm attr=' &apos;   &#x20;&#13;&#xa;&#9;   &apos; '/>"),
4877             '<norm attr=" \'    &#xD;&#xA;&#x9;   \' "></norm>')
4878         self.assertEqual(c14n_roundtrip("<normNames attr='   A   &#x20;&#13;&#xa;&#9;   B   '/>"),
4879             '<normNames attr="   A    &#xD;&#xA;&#x9;   B   "></normNames>')
4880         self.assertEqual(c14n_roundtrip("<normId id=' &apos;   &#x20;&#13;&#xa;&#9;   &apos; '/>"),
4881             '<normId id=" \'    &#xD;&#xA;&#x9;   \' "></normId>')
4882
4883         # fragments from PJ's tests
4884         #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>"),
4885         #'<doc xmlns:x="http://example.com/x"><b xmlns:y="http://example.com/y" a3="3" y:a1="1" y:a2="2"></b></doc>')
4886
4887     def test_c14n_exclusion(self):
4888         c14n_roundtrip = self.c14n_roundtrip
4889         xml = textwrap.dedent("""\
4890         <root xmlns:x="http://example.com/x">
4891             <a x:attr="attrx">
4892                 <b>abtext</b>
4893             </a>
4894             <b>btext</b>
4895             <c>
4896                 <x:d>dtext</x:d>
4897             </c>
4898         </root>
4899         """)
4900         self.assertEqual(
4901             c14n_roundtrip(xml, strip_text=True),
4902             '<root>'
4903             '<a xmlns:x="http://example.com/x" x:attr="attrx"><b>abtext</b></a>'
4904             '<b>btext</b>'
4905             '<c><x:d xmlns:x="http://example.com/x">dtext</x:d></c>'
4906             '</root>')
4907         self.assertEqual(
4908             c14n_roundtrip(xml, strip_text=True, exclude_attrs=['{http://example.com/x}attr']),
4909             '<root>'
4910             '<a><b>abtext</b></a>'
4911             '<b>btext</b>'
4912             '<c><x:d xmlns:x="http://example.com/x">dtext</x:d></c>'
4913             '</root>')
4914         self.assertEqual(
4915             c14n_roundtrip(xml, strip_text=True, exclude_tags=['{http://example.com/x}d']),
4916             '<root>'
4917             '<a xmlns:x="http://example.com/x" x:attr="attrx"><b>abtext</b></a>'
4918             '<b>btext</b>'
4919             '<c></c>'
4920             '</root>')
4921         self.assertEqual(
4922             c14n_roundtrip(xml, strip_text=True, exclude_attrs=['{http://example.com/x}attr'],
4923                            exclude_tags=['{http://example.com/x}d']),
4924             '<root>'
4925             '<a><b>abtext</b></a>'
4926             '<b>btext</b>'
4927             '<c></c>'
4928             '</root>')
4929         self.assertEqual(
4930             c14n_roundtrip(xml, strip_text=True, exclude_tags=['a', 'b']),
4931             '<root>'
4932             '<c><x:d xmlns:x="http://example.com/x">dtext</x:d></c>'
4933             '</root>')
4934         self.assertEqual(
4935             c14n_roundtrip(xml, exclude_tags=['a', 'b']),
4936             '<root>\n'
4937             '    \n'
4938             '    \n'
4939             '    <c>\n'
4940             '        <x:d xmlns:x="http://example.com/x">dtext</x:d>\n'
4941             '    </c>\n'
4942             '</root>')
4943         self.assertEqual(
4944             c14n_roundtrip(xml, strip_text=True, exclude_tags=['{http://example.com/x}d', 'b']),
4945             '<root>'
4946             '<a xmlns:x="http://example.com/x" x:attr="attrx"></a>'
4947             '<c></c>'
4948             '</root>')
4949         self.assertEqual(
4950             c14n_roundtrip(xml, exclude_tags=['{http://example.com/x}d', 'b']),
4951             '<root>\n'
4952             '    <a xmlns:x="http://example.com/x" x:attr="attrx">\n'
4953             '        \n'
4954             '    </a>\n'
4955             '    \n'
4956             '    <c>\n'
4957             '        \n'
4958             '    </c>\n'
4959             '</root>')
4960
4961     #
4962     # basic method=c14n tests from the c14n 2.0 specification.  uses
4963     # test files under xmltestdata/c14n-20.
4964
4965     # note that this uses generated C14N versions of the standard ET.write
4966     # output, not roundtripped C14N (see above).
4967
4968     def test_xml_c14n2(self):
4969         datadir = os.path.join(os.path.dirname(__file__), "c14n-20")
4970         full_path = partial(os.path.join, datadir)
4971
4972         files = [filename[:-4] for filename in sorted(os.listdir(datadir))
4973                  if filename.endswith('.xml')]
4974         input_files = [
4975             filename for filename in files
4976             if filename.startswith('in')
4977         ]
4978         configs = {
4979             filename: {
4980                 # <c14n2:PrefixRewrite>sequential</c14n2:PrefixRewrite>
4981                 option.tag.split('}')[-1]: ((option.text or '').strip(), option)
4982                 for option in self.etree.parse(full_path(filename) + ".xml").getroot()
4983             }
4984             for filename in files
4985             if filename.startswith('c14n')
4986         }
4987
4988         tests = {
4989             input_file: [
4990                 (filename, configs[filename.rsplit('_', 1)[-1]])
4991                 for filename in files
4992                 if filename.startswith('out_%s_' % input_file)
4993                 and filename.rsplit('_', 1)[-1] in configs
4994             ]
4995             for input_file in input_files
4996         }
4997
4998         # Make sure we found all test cases.
4999         self.assertEqual(30, len([
5000             output_file for output_files in tests.values()
5001             for output_file in output_files]))
5002
5003         def get_option(config, option_name, default=None):
5004             return config.get(option_name, (default, ()))[0]
5005
5006         for input_file, output_files in tests.items():
5007             for output_file, config in output_files:
5008                 keep_comments = get_option(
5009                     config, 'IgnoreComments') == 'true'  # no, it's right :)
5010                 strip_text = get_option(
5011                     config, 'TrimTextNodes') == 'true'
5012                 rewrite_prefixes = get_option(
5013                     config, 'PrefixRewrite') == 'sequential'
5014                 if 'QNameAware' in config:
5015                     qattrs = [
5016                         "{%s}%s" % (el.get('NS'), el.get('Name'))
5017                         for el in config['QNameAware'][1].findall(
5018                             '{http://www.w3.org/2010/xml-c14n2}QualifiedAttr')
5019                     ]
5020                     qtags = [
5021                         "{%s}%s" % (el.get('NS'), el.get('Name'))
5022                         for el in config['QNameAware'][1].findall(
5023                             '{http://www.w3.org/2010/xml-c14n2}Element')
5024                     ]
5025                 else:
5026                     qtags = qattrs = None
5027
5028                 # Build subtest description from config.
5029                 config_descr = ','.join(
5030                     "%s=%s" % (name, value or ','.join(c.tag.split('}')[-1] for c in children))
5031                     for name, (value, children) in sorted(config.items())
5032                 )
5033
5034                 with self.subTest("{}({})".format(output_file, config_descr)):
5035                     if input_file == 'inNsRedecl' and not rewrite_prefixes:
5036                         self.skipTest(
5037                             "Redeclared namespace handling is not supported in {}".format(
5038                                 output_file))
5039                     if input_file == 'inNsSuperfluous' and not rewrite_prefixes:
5040                         self.skipTest(
5041                             "Redeclared namespace handling is not supported in {}".format(
5042                                 output_file))
5043                     if 'QNameAware' in config and config['QNameAware'][1].find(
5044                             '{http://www.w3.org/2010/xml-c14n2}XPathElement') is not None:
5045                         self.skipTest(
5046                             "QName rewriting in XPath text is not supported in {}".format(
5047                                 output_file))
5048
5049                     f = full_path(input_file + ".xml")
5050                     if input_file == 'inC14N5':
5051                         # Hack: avoid setting up external entity resolution in the parser.
5052                         with open(full_path('world.txt'), 'rb') as entity_file:
5053                             with open(f, 'rb') as f:
5054                                 f = io.BytesIO(f.read().replace(b'&ent2;', entity_file.read().strip()))
5055
5056                     text = self._canonicalize(
5057                         f,
5058                         with_comments=keep_comments,
5059                         strip_text=strip_text,
5060                         rewrite_prefixes=rewrite_prefixes,
5061                         qname_aware_tags=qtags, qname_aware_attrs=qattrs)
5062
5063                     with io.open(full_path(output_file + ".xml"), 'r', encoding='utf8') as f:
5064                         expected = f.read()
5065                     if input_file == 'inC14N3' and self.etree is not etree:
5066                         # FIXME: cET resolves default attributes but ET does not!
5067                         expected = expected.replace(' attr="default"', '')
5068                         text = text.replace(' attr="default"', '')
5069                     self.assertEqual(expected, text)
5070
5071
5072 if etree:
5073     class ETreeTestCase(_ETreeTestCaseBase):
5074         etree = etree
5075
5076     class ETreePullTestCase(_XMLPullParserTest):
5077         etree = etree
5078
5079     class ETreeElementSlicingTest(_ElementSlicingTest):
5080         etree = etree
5081
5082     class ETreeC14NTest(_C14NTest):
5083         etree = etree
5084
5085     class ETreeC14N2WriteTest(ETreeC14NTest):
5086         def _canonicalize(self, input_file, with_comments=True, strip_text=False,
5087                           rewrite_prefixes=False, qname_aware_tags=None, qname_aware_attrs=None,
5088                           **options):
5089             if rewrite_prefixes or qname_aware_attrs or qname_aware_tags:
5090                 self.skipTest("C14N 2.0 feature not supported with ElementTree.write()")
5091
5092             parser = self.etree.XMLParser(attribute_defaults=True, collect_ids=False)
5093             tree = self.etree.parse(input_file, parser)
5094             out = io.BytesIO()
5095             tree.write(
5096                 out, method='c14n2',
5097                 with_comments=with_comments, strip_text=strip_text,
5098                 **options)
5099             return out.getvalue().decode('utf8')
5100
5101     class ETreeC14N2TostringTest(ETreeC14NTest):
5102         def _canonicalize(self, input_file, with_comments=True, strip_text=False,
5103                           rewrite_prefixes=False, qname_aware_tags=None, qname_aware_attrs=None,
5104                           **options):
5105             if rewrite_prefixes or qname_aware_attrs or qname_aware_tags:
5106                 self.skipTest("C14N 2.0 feature not supported with ElementTree.tostring()")
5107
5108             parser = self.etree.XMLParser(attribute_defaults=True, collect_ids=False)
5109             tree = self.etree.parse(input_file, parser)
5110             return self.etree.tostring(
5111                 tree, method='c14n2',
5112                 with_comments=with_comments, strip_text=strip_text,
5113                 **options).decode('utf8')
5114
5115
5116 if ElementTree:
5117     class ElementTreeTestCase(_ETreeTestCaseBase):
5118         etree = ElementTree
5119
5120         @classmethod
5121         def setUpClass(cls):
5122             import warnings
5123             # ElementTree warns about getiterator() in recent Pythons
5124             warnings.filterwarnings(
5125                 'ignore',
5126                 r'This method will be removed.*\.iter\(\).*instead',
5127                 PendingDeprecationWarning)
5128
5129     filter_by_version(
5130         ElementTreeTestCase,
5131         ElementTreeTestCase.required_versions_ET, ET_VERSION)
5132
5133     if hasattr(ElementTree, 'XMLPullParser'):
5134         class ElementTreePullTestCase(_XMLPullParserTest):
5135             etree = ElementTree
5136     else:
5137         ElementTreePullTestCase = None
5138
5139     if hasattr(ElementTree, 'canonicalize'):
5140         class ElementTreeC14NTest(_C14NTest):
5141             etree = ElementTree
5142     else:
5143         ElementTreeC14NTest = None
5144
5145     class ElementTreeElementSlicingTest(_ElementSlicingTest):
5146         etree = ElementTree
5147
5148
5149 if cElementTree:
5150     class CElementTreeTestCase(_ETreeTestCaseBase):
5151         etree = cElementTree
5152
5153     filter_by_version(
5154         CElementTreeTestCase,
5155         CElementTreeTestCase.required_versions_cET, CET_VERSION)
5156
5157     class CElementTreeElementSlicingTest(_ElementSlicingTest):
5158         etree = cElementTree
5159
5160
5161 def test_suite():
5162     suite = unittest.TestSuite()
5163     if etree:
5164         suite.addTests([unittest.makeSuite(ETreeTestCase)])
5165         suite.addTests([unittest.makeSuite(ETreePullTestCase)])
5166         suite.addTests([unittest.makeSuite(ETreeElementSlicingTest)])
5167         suite.addTests([unittest.makeSuite(ETreeC14NTest)])
5168         suite.addTests([unittest.makeSuite(ETreeC14N2WriteTest)])
5169         suite.addTests([unittest.makeSuite(ETreeC14N2TostringTest)])
5170     if ElementTree:
5171         suite.addTests([unittest.makeSuite(ElementTreeTestCase)])
5172         if ElementTreePullTestCase:
5173             suite.addTests([unittest.makeSuite(ElementTreePullTestCase)])
5174         if ElementTreeC14NTest:
5175             suite.addTests([unittest.makeSuite(ElementTreeC14NTest)])
5176         suite.addTests([unittest.makeSuite(ElementTreeElementSlicingTest)])
5177     if cElementTree:
5178         suite.addTests([unittest.makeSuite(CElementTreeTestCase)])
5179         suite.addTests([unittest.makeSuite(CElementTreeElementSlicingTest)])
5180     return suite
5181
5182 if __name__ == '__main__':
5183     print('to test use test.py %s' % __file__)