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