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