Package lxml :: Package tests :: Module test_etree
[hide private]
[frames] | no frames]

Source Code for Module lxml.tests.test_etree

   1  # -*- coding: utf-8 -*- 
   2   
   3  """ 
   4  Tests specific to the extended etree API 
   5   
   6  Tests that apply to the general ElementTree API should go into 
   7  test_elementtree 
   8  """ 
   9   
  10  from __future__ import absolute_import 
  11   
  12  import os.path 
  13  import unittest 
  14  import copy 
  15  import sys 
  16  import re 
  17  import gc 
  18  import operator 
  19  import tempfile 
  20  import textwrap 
  21  import zlib 
  22  import gzip 
  23   
  24  from .common_imports import etree, StringIO, BytesIO, HelperTestCase 
  25  from .common_imports import fileInTestDir, fileUrlInTestDir, read_file, path2url 
  26  from .common_imports import SillyFileLike, LargeFileLikeUnicode, doctest, make_doctest 
  27  from .common_imports import canonicalize, sorted, _str, _bytes 
  28   
  29  print("") 
  30  print("TESTED VERSION: %s" % etree.__version__) 
  31  print("    Python:           " + repr(sys.version_info)) 
  32  print("    lxml.etree:       " + repr(etree.LXML_VERSION)) 
  33  print("    libxml used:      " + repr(etree.LIBXML_VERSION)) 
  34  print("    libxml compiled:  " + repr(etree.LIBXML_COMPILED_VERSION)) 
  35  print("    libxslt used:     " + repr(etree.LIBXSLT_VERSION)) 
  36  print("    libxslt compiled: " + repr(etree.LIBXSLT_COMPILED_VERSION)) 
  37  print("") 
  38   
  39  try: 
  40      _unicode = unicode 
  41  except NameError: 
  42      # Python 3 
  43      _unicode = str 
  44   
  45   
46 -class ETreeOnlyTestCase(HelperTestCase):
47 """Tests only for etree, not ElementTree""" 48 etree = etree 49
50 - def test_version(self):
51 self.assertTrue(isinstance(etree.__version__, _unicode)) 52 self.assertTrue(isinstance(etree.LXML_VERSION, tuple)) 53 self.assertEqual(len(etree.LXML_VERSION), 4) 54 self.assertTrue(isinstance(etree.LXML_VERSION[0], int)) 55 self.assertTrue(isinstance(etree.LXML_VERSION[1], int)) 56 self.assertTrue(isinstance(etree.LXML_VERSION[2], int)) 57 self.assertTrue(isinstance(etree.LXML_VERSION[3], int)) 58 self.assertTrue(etree.__version__.startswith( 59 str(etree.LXML_VERSION[0])))
60
61 - def test_c_api(self):
62 if hasattr(self.etree, '__pyx_capi__'): 63 # newer Pyrex compatible C-API 64 self.assertTrue(isinstance(self.etree.__pyx_capi__, dict)) 65 self.assertTrue(len(self.etree.__pyx_capi__) > 0) 66 else: 67 # older C-API mechanism 68 self.assertTrue(hasattr(self.etree, '_import_c_api'))
69
70 - def test_include_paths(self):
71 import lxml 72 includes = lxml.get_include() 73 self.assertTrue(includes) 74 self.assertTrue(len(includes) >= 2) 75 self.assertTrue(os.path.join(os.path.dirname(lxml.__file__), 'includes') in includes, 76 includes)
77
78 - def test_element_names(self):
79 Element = self.etree.Element 80 el = Element('name') 81 self.assertEqual(el.tag, 'name') 82 el = Element('{}name') 83 self.assertEqual(el.tag, 'name')
84
85 - def test_element_name_empty(self):
86 Element = self.etree.Element 87 el = Element('name') 88 self.assertRaises(ValueError, Element, '{}') 89 self.assertRaises(ValueError, setattr, el, 'tag', '{}') 90 91 self.assertRaises(ValueError, Element, '{test}') 92 self.assertRaises(ValueError, setattr, el, 'tag', '{test}')
93
94 - def test_element_name_colon(self):
95 Element = self.etree.Element 96 self.assertRaises(ValueError, Element, 'p:name') 97 self.assertRaises(ValueError, Element, '{test}p:name') 98 99 el = Element('name') 100 self.assertRaises(ValueError, setattr, el, 'tag', 'p:name')
101
102 - def test_element_name_quote(self):
103 Element = self.etree.Element 104 self.assertRaises(ValueError, Element, "p'name") 105 self.assertRaises(ValueError, Element, 'p"name') 106 107 self.assertRaises(ValueError, Element, "{test}p'name") 108 self.assertRaises(ValueError, Element, '{test}p"name') 109 110 el = Element('name') 111 self.assertRaises(ValueError, setattr, el, 'tag', "p'name") 112 self.assertRaises(ValueError, setattr, el, 'tag', 'p"name')
113
114 - def test_element_name_space(self):
115 Element = self.etree.Element 116 self.assertRaises(ValueError, Element, ' name ') 117 self.assertRaises(ValueError, Element, 'na me') 118 self.assertRaises(ValueError, Element, '{test} name') 119 120 el = Element('name') 121 self.assertRaises(ValueError, setattr, el, 'tag', ' name ')
122
123 - def test_subelement_name_empty(self):
124 Element = self.etree.Element 125 SubElement = self.etree.SubElement 126 127 el = Element('name') 128 self.assertRaises(ValueError, SubElement, el, '{}') 129 self.assertRaises(ValueError, SubElement, el, '{test}')
130
131 - def test_subelement_name_colon(self):
132 Element = self.etree.Element 133 SubElement = self.etree.SubElement 134 135 el = Element('name') 136 self.assertRaises(ValueError, SubElement, el, 'p:name') 137 self.assertRaises(ValueError, SubElement, el, '{test}p:name')
138
139 - def test_subelement_name_quote(self):
140 Element = self.etree.Element 141 SubElement = self.etree.SubElement 142 143 el = Element('name') 144 self.assertRaises(ValueError, SubElement, el, "p'name") 145 self.assertRaises(ValueError, SubElement, el, "{test}p'name") 146 147 self.assertRaises(ValueError, SubElement, el, 'p"name') 148 self.assertRaises(ValueError, SubElement, el, '{test}p"name')
149
150 - def test_subelement_name_space(self):
151 Element = self.etree.Element 152 SubElement = self.etree.SubElement 153 154 el = Element('name') 155 self.assertRaises(ValueError, SubElement, el, ' name ') 156 self.assertRaises(ValueError, SubElement, el, 'na me') 157 self.assertRaises(ValueError, SubElement, el, '{test} name')
158
160 Element = self.etree.Element 161 SubElement = self.etree.SubElement 162 163 el = Element('name') 164 self.assertRaises(ValueError, SubElement, el, 'name', {'a b c' : 'abc'}) 165 self.assertRaises(ValueError, SubElement, el, 'name', {'a' : 'a\0\n'}) 166 self.assertEqual(0, len(el))
167
168 - def test_qname_empty(self):
169 QName = self.etree.QName 170 self.assertRaises(ValueError, QName, '') 171 self.assertRaises(ValueError, QName, 'test', '')
172
173 - def test_qname_colon(self):
174 QName = self.etree.QName 175 self.assertRaises(ValueError, QName, 'p:name') 176 self.assertRaises(ValueError, QName, 'test', 'p:name')
177
178 - def test_qname_space(self):
179 QName = self.etree.QName 180 self.assertRaises(ValueError, QName, ' name ') 181 self.assertRaises(ValueError, QName, 'na me') 182 self.assertRaises(ValueError, QName, 'test', ' name')
183
185 # ET doesn't have namespace/localname properties on QNames 186 QName = self.etree.QName 187 namespace, localname = 'http://myns', 'a' 188 qname = QName(namespace, localname) 189 self.assertEqual(namespace, qname.namespace) 190 self.assertEqual(localname, qname.localname)
191
192 - def test_qname_element(self):
193 # ET doesn't have namespace/localname properties on QNames 194 QName = self.etree.QName 195 qname1 = QName('http://myns', 'a') 196 a = self.etree.Element(qname1, nsmap={'p' : 'http://myns'}) 197 198 qname2 = QName(a) 199 self.assertEqual(a.tag, qname1.text) 200 self.assertEqual(qname1.text, qname2.text) 201 self.assertEqual(qname1, qname2)
202
203 - def test_qname_text_resolve(self):
204 # ET doesn't resove QNames as text values 205 etree = self.etree 206 qname = etree.QName('http://myns', 'a') 207 a = etree.Element(qname, nsmap={'p' : 'http://myns'}) 208 a.text = qname 209 210 self.assertEqual("p:a", a.text)
211
212 - def test_nsmap_prefix_invalid(self):
213 etree = self.etree 214 self.assertRaises(ValueError, 215 etree.Element, "root", nsmap={'"' : 'testns'}) 216 self.assertRaises(ValueError, 217 etree.Element, "root", nsmap={'&' : 'testns'}) 218 self.assertRaises(ValueError, 219 etree.Element, "root", nsmap={'a:b' : 'testns'})
220
221 - def test_attribute_has_key(self):
222 # ET in Py 3.x has no "attrib.has_key()" method 223 XML = self.etree.XML 224 225 root = XML(_bytes('<foo bar="Bar" xmlns:ns="http://ns.codespeak.net/test" ns:baz="Baz" />')) 226 self.assertEqual( 227 True, root.attrib.has_key('bar')) 228 self.assertEqual( 229 False, root.attrib.has_key('baz')) 230 self.assertEqual( 231 False, root.attrib.has_key('hah')) 232 self.assertEqual( 233 True, 234 root.attrib.has_key('{http://ns.codespeak.net/test}baz'))
235
236 - def test_attribute_set(self):
237 Element = self.etree.Element 238 root = Element("root") 239 root.set("attr", "TEST") 240 self.assertEqual("TEST", root.get("attr"))
241
243 # ElementTree accepts arbitrary attribute values 244 # lxml.etree allows only strings 245 Element = self.etree.Element 246 247 root = Element("root") 248 root.set("attr", "TEST") 249 self.assertEqual("TEST", root.get("attr")) 250 self.assertRaises(TypeError, root.set, "newattr", 5)
251
252 - def test_attrib_and_keywords(self):
253 Element = self.etree.Element 254 255 root = Element("root") 256 root.set("attr", "TEST") 257 self.assertEqual("TEST", root.attrib["attr"]) 258 259 root2 = Element("root2", root.attrib, attr2='TOAST') 260 self.assertEqual("TEST", root2.attrib["attr"]) 261 self.assertEqual("TOAST", root2.attrib["attr2"]) 262 self.assertEqual(None, root.attrib.get("attr2"))
263
264 - def test_attrib_order(self):
265 Element = self.etree.Element 266 267 keys = ["attr%d" % i for i in range(10)] 268 values = ["TEST-%d" % i for i in range(10)] 269 items = list(zip(keys, values)) 270 271 root = Element("root") 272 for key, value in items: 273 root.set(key, value) 274 self.assertEqual(keys, root.attrib.keys()) 275 self.assertEqual(values, root.attrib.values()) 276 277 root2 = Element("root2", root.attrib, 278 attr_99='TOAST-1', attr_98='TOAST-2') 279 self.assertEqual(['attr_98', 'attr_99'] + keys, 280 root2.attrib.keys()) 281 self.assertEqual(['TOAST-2', 'TOAST-1'] + values, 282 root2.attrib.values()) 283 284 self.assertEqual(keys, root.attrib.keys()) 285 self.assertEqual(values, root.attrib.values())
286
287 - def test_attribute_set_invalid(self):
288 # ElementTree accepts arbitrary attribute values 289 # lxml.etree allows only strings, or None for (html5) boolean attributes 290 Element = self.etree.Element 291 root = Element("root") 292 self.assertRaises(TypeError, root.set, "newattr", 5) 293 self.assertRaises(TypeError, root.set, "newattr", object) 294 self.assertRaises(TypeError, root.set, "newattr", None) 295 self.assertRaises(TypeError, root.set, "newattr")
296
297 - def test_strip_attributes(self):
298 XML = self.etree.XML 299 xml = _bytes('<test a="5" b="10" c="20"><x a="4" b="2"/></test>') 300 301 root = XML(xml) 302 self.etree.strip_attributes(root, 'a') 303 self.assertEqual(_bytes('<test b="10" c="20"><x b="2"></x></test>'), 304 self._writeElement(root)) 305 306 root = XML(xml) 307 self.etree.strip_attributes(root, 'b', 'c') 308 self.assertEqual(_bytes('<test a="5"><x a="4"></x></test>'), 309 self._writeElement(root))
310
311 - def test_strip_attributes_ns(self):
312 XML = self.etree.XML 313 xml = _bytes('<test xmlns:n="http://test/ns" a="6" b="10" c="20" n:a="5"><x a="4" n:b="2"/></test>') 314 315 root = XML(xml) 316 self.etree.strip_attributes(root, 'a') 317 self.assertEqual( 318 _bytes('<test xmlns:n="http://test/ns" b="10" c="20" n:a="5"><x n:b="2"></x></test>'), 319 self._writeElement(root)) 320 321 root = XML(xml) 322 self.etree.strip_attributes(root, '{http://test/ns}a', 'c') 323 self.assertEqual( 324 _bytes('<test xmlns:n="http://test/ns" a="6" b="10"><x a="4" n:b="2"></x></test>'), 325 self._writeElement(root)) 326 327 root = XML(xml) 328 self.etree.strip_attributes(root, '{http://test/ns}*') 329 self.assertEqual( 330 _bytes('<test xmlns:n="http://test/ns" a="6" b="10" c="20"><x a="4"></x></test>'), 331 self._writeElement(root))
332
333 - def test_strip_elements(self):
334 XML = self.etree.XML 335 xml = _bytes('<test><a><b><c/></b></a><x><a><b/><c/></a></x></test>') 336 337 root = XML(xml) 338 self.etree.strip_elements(root, 'a') 339 self.assertEqual(_bytes('<test><x></x></test>'), 340 self._writeElement(root)) 341 342 root = XML(xml) 343 self.etree.strip_elements(root, 'b', 'c', 'X', 'Y', 'Z') 344 self.assertEqual(_bytes('<test><a></a><x><a></a></x></test>'), 345 self._writeElement(root)) 346 347 root = XML(xml) 348 self.etree.strip_elements(root, 'c') 349 self.assertEqual(_bytes('<test><a><b></b></a><x><a><b></b></a></x></test>'), 350 self._writeElement(root))
351
352 - def test_strip_elements_ns(self):
353 XML = self.etree.XML 354 xml = _bytes('<test>TEST<n:a xmlns:n="urn:a">A<b>B<c xmlns="urn:c"/>C</b>BT</n:a>AT<x>X<a>A<b xmlns="urn:a"/>BT<c xmlns="urn:x"/>CT</a>AT</x>XT</test>') 355 356 root = XML(xml) 357 self.etree.strip_elements(root, 'a') 358 self.assertEqual(_bytes('<test>TEST<n:a xmlns:n="urn:a">A<b>B<c xmlns="urn:c"></c>C</b>BT</n:a>AT<x>X</x>XT</test>'), 359 self._writeElement(root)) 360 361 root = XML(xml) 362 self.etree.strip_elements(root, '{urn:a}b', 'c') 363 self.assertEqual(_bytes('<test>TEST<n:a xmlns:n="urn:a">A<b>B<c xmlns="urn:c"></c>C</b>BT</n:a>AT<x>X<a>A<c xmlns="urn:x"></c>CT</a>AT</x>XT</test>'), 364 self._writeElement(root)) 365 366 root = XML(xml) 367 self.etree.strip_elements(root, '{urn:a}*', 'c') 368 self.assertEqual(_bytes('<test>TEST<x>X<a>A<c xmlns="urn:x"></c>CT</a>AT</x>XT</test>'), 369 self._writeElement(root)) 370 371 root = XML(xml) 372 self.etree.strip_elements(root, '{urn:a}*', 'c', with_tail=False) 373 self.assertEqual(_bytes('<test>TESTAT<x>X<a>ABT<c xmlns="urn:x"></c>CT</a>AT</x>XT</test>'), 374 self._writeElement(root))
375
376 - def test_strip_tags(self):
377 XML = self.etree.XML 378 xml = _bytes('<test>TEST<a>A<b>B<c/>CT</b>BT</a>AT<x>X<a>A<b/>BT<c/>CT</a>AT</x>XT</test>') 379 380 root = XML(xml) 381 self.etree.strip_tags(root, 'a') 382 self.assertEqual(_bytes('<test>TESTA<b>B<c></c>CT</b>BTAT<x>XA<b></b>BT<c></c>CTAT</x>XT</test>'), 383 self._writeElement(root)) 384 385 root = XML(xml) 386 self.etree.strip_tags(root, 'b', 'c', 'X', 'Y', 'Z') 387 self.assertEqual(_bytes('<test>TEST<a>ABCTBT</a>AT<x>X<a>ABTCT</a>AT</x>XT</test>'), 388 self._writeElement(root)) 389 390 root = XML(xml) 391 self.etree.strip_tags(root, 'c') 392 self.assertEqual(_bytes('<test>TEST<a>A<b>BCT</b>BT</a>AT<x>X<a>A<b></b>BTCT</a>AT</x>XT</test>'), 393 self._writeElement(root))
394
395 - def test_strip_tags_pi_comment(self):
396 XML = self.etree.XML 397 PI = self.etree.ProcessingInstruction 398 Comment = self.etree.Comment 399 xml = _bytes('<!--comment1-->\n<?PI1?>\n<test>TEST<!--comment2-->XT<?PI2?></test>\n<!--comment3-->\n<?PI1?>') 400 401 root = XML(xml) 402 self.etree.strip_tags(root, PI) 403 self.assertEqual(_bytes('<!--comment1-->\n<?PI1?>\n<test>TEST<!--comment2-->XT</test>\n<!--comment3-->\n<?PI1?>'), 404 self._writeElement(root)) 405 406 root = XML(xml) 407 self.etree.strip_tags(root, Comment) 408 self.assertEqual(_bytes('<!--comment1-->\n<?PI1?>\n<test>TESTXT<?PI2?></test>\n<!--comment3-->\n<?PI1?>'), 409 self._writeElement(root)) 410 411 root = XML(xml) 412 self.etree.strip_tags(root, PI, Comment) 413 self.assertEqual(_bytes('<!--comment1-->\n<?PI1?>\n<test>TESTXT</test>\n<!--comment3-->\n<?PI1?>'), 414 self._writeElement(root)) 415 416 root = XML(xml) 417 self.etree.strip_tags(root, Comment, PI) 418 self.assertEqual(_bytes('<!--comment1-->\n<?PI1?>\n<test>TESTXT</test>\n<!--comment3-->\n<?PI1?>'), 419 self._writeElement(root))
420
422 XML = self.etree.XML 423 ElementTree = self.etree.ElementTree 424 PI = self.etree.ProcessingInstruction 425 Comment = self.etree.Comment 426 xml = _bytes('<!--comment1-->\n<?PI1?>\n<test>TEST<!--comment2-->XT<?PI2?></test>\n<!--comment3-->\n<?PI1?>') 427 428 root = XML(xml) 429 self.etree.strip_tags(ElementTree(root), PI) 430 self.assertEqual(_bytes('<!--comment1-->\n<test>TEST<!--comment2-->XT</test>\n<!--comment3-->'), 431 self._writeElement(root)) 432 433 root = XML(xml) 434 self.etree.strip_tags(ElementTree(root), Comment) 435 self.assertEqual(_bytes('<?PI1?>\n<test>TESTXT<?PI2?></test>\n<?PI1?>'), 436 self._writeElement(root)) 437 438 root = XML(xml) 439 self.etree.strip_tags(ElementTree(root), PI, Comment) 440 self.assertEqual(_bytes('<test>TESTXT</test>'), 441 self._writeElement(root)) 442 443 root = XML(xml) 444 self.etree.strip_tags(ElementTree(root), Comment, PI) 445 self.assertEqual(_bytes('<test>TESTXT</test>'), 446 self._writeElement(root))
447
448 - def test_strip_tags_doc_style(self):
449 XML = self.etree.XML 450 xml = _bytes(''' 451 <div> 452 <div> 453 I like <strong>sheep</strong>. 454 <br/> 455 I like lots of <strong>sheep</strong>. 456 <br/> 457 Click <a href="http://www.sheep.com">here</a> 458 for <a href="http://www.sheep.com">those</a> sheep. 459 <br/> 460 </div> 461 </div> 462 '''.strip()) 463 464 root = XML(xml) 465 self.etree.strip_tags(root, 'a') 466 self.assertEqual(re.sub(_bytes('</?a[^>]*>'), _bytes(''), xml).replace(_bytes('<br/>'), _bytes('<br></br>')), 467 self._writeElement(root)) 468 469 root = XML(xml) 470 self.etree.strip_tags(root, 'a', 'br') 471 self.assertEqual(re.sub(_bytes('</?a[^>]*>'), _bytes(''), 472 re.sub(_bytes('<br[^>]*>'), _bytes(''), xml)), 473 self._writeElement(root))
474
475 - def test_strip_tags_ns(self):
476 XML = self.etree.XML 477 xml = _bytes('<test>TEST<n:a xmlns:n="urn:a">A<b>B<c xmlns="urn:c"/>CT</b>BT</n:a>AT<x>X<a>A<b xmlns="urn:a"/>BT<c xmlns="urn:x"/>CT</a>AT</x>XT</test>') 478 479 root = XML(xml) 480 self.etree.strip_tags(root, 'a') 481 self.assertEqual(_bytes('<test>TEST<n:a xmlns:n="urn:a">A<b>B<c xmlns="urn:c"></c>CT</b>BT</n:a>AT<x>XA<b xmlns="urn:a"></b>BT<c xmlns="urn:x"></c>CTAT</x>XT</test>'), 482 self._writeElement(root)) 483 484 root = XML(xml) 485 self.etree.strip_tags(root, '{urn:a}b', 'c') 486 self.assertEqual(_bytes('<test>TEST<n:a xmlns:n="urn:a">A<b>B<c xmlns="urn:c"></c>CT</b>BT</n:a>AT<x>X<a>ABT<c xmlns="urn:x"></c>CT</a>AT</x>XT</test>'), 487 self._writeElement(root)) 488 489 root = XML(xml) 490 self.etree.strip_tags(root, '{urn:a}*', 'c') 491 self.assertEqual(_bytes('<test>TESTA<b>B<c xmlns="urn:c"></c>CT</b>BTAT<x>X<a>ABT<c xmlns="urn:x"></c>CT</a>AT</x>XT</test>'), 492 self._writeElement(root))
493
494 - def test_strip_tags_and_remove(self):
495 # previously crashed 496 HTML = self.etree.HTML 497 root = HTML(_bytes('<div><h1>title</h1> <b>foo</b> <p>boo</p></div>'))[0][0] 498 self.assertEqual(_bytes('<div><h1>title</h1> <b>foo</b> <p>boo</p></div>'), 499 self.etree.tostring(root)) 500 self.etree.strip_tags(root, 'b') 501 self.assertEqual(_bytes('<div><h1>title</h1> foo <p>boo</p></div>'), 502 self.etree.tostring(root)) 503 root.remove(root[0]) 504 self.assertEqual(_bytes('<div><p>boo</p></div>'), 505 self.etree.tostring(root))
506
507 - def test_pi(self):
508 # lxml.etree separates target and text 509 Element = self.etree.Element 510 SubElement = self.etree.SubElement 511 ProcessingInstruction = self.etree.ProcessingInstruction 512 513 a = Element('a') 514 a.append(ProcessingInstruction('foo', 'some more text')) 515 self.assertEqual(a[0].target, 'foo') 516 self.assertEqual(a[0].text, 'some more text')
517
518 - def test_pi_parse(self):
519 XML = self.etree.XML 520 root = XML(_bytes("<test><?mypi my test ?></test>")) 521 self.assertEqual(root[0].target, "mypi") 522 self.assertEqual(root[0].text, "my test ")
523
525 XML = self.etree.XML 526 root = XML(_bytes("<test><?mypi my='1' test=\" abc \" quotes=\"' '\" only names ?></test>")) 527 self.assertEqual(root[0].target, "mypi") 528 self.assertEqual(root[0].get('my'), "1") 529 self.assertEqual(root[0].get('test'), " abc ") 530 self.assertEqual(root[0].get('quotes'), "' '") 531 self.assertEqual(root[0].get('only'), None) 532 self.assertEqual(root[0].get('names'), None) 533 self.assertEqual(root[0].get('nope'), None)
534
536 XML = self.etree.XML 537 root = XML(_bytes("<test><?mypi my='1' test=\" abc \" quotes=\"' '\" only names ?></test>")) 538 self.assertEqual(root[0].target, "mypi") 539 self.assertEqual(root[0].attrib['my'], "1") 540 self.assertEqual(root[0].attrib['test'], " abc ") 541 self.assertEqual(root[0].attrib['quotes'], "' '") 542 self.assertRaises(KeyError, root[0].attrib.__getitem__, 'only') 543 self.assertRaises(KeyError, root[0].attrib.__getitem__, 'names') 544 self.assertRaises(KeyError, root[0].attrib.__getitem__, 'nope')
545
546 - def test_deepcopy_pi(self):
547 # previously caused a crash 548 ProcessingInstruction = self.etree.ProcessingInstruction 549 550 a = ProcessingInstruction("PI", "ONE") 551 b = copy.deepcopy(a) 552 b.text = "ANOTHER" 553 554 self.assertEqual('ONE', a.text) 555 self.assertEqual('ANOTHER', b.text)
556
558 XML = self.etree.XML 559 tostring = self.etree.tostring 560 root = XML(_bytes("<?mypi my test ?><test/><!--comment -->")) 561 tree1 = self.etree.ElementTree(root) 562 self.assertEqual(_bytes("<?mypi my test ?><test/><!--comment -->"), 563 tostring(tree1)) 564 565 tree2 = copy.deepcopy(tree1) 566 self.assertEqual(_bytes("<?mypi my test ?><test/><!--comment -->"), 567 tostring(tree2)) 568 569 root2 = copy.deepcopy(tree1.getroot()) 570 self.assertEqual(_bytes("<test/>"), 571 tostring(root2))
572
574 XML = self.etree.XML 575 tostring = self.etree.tostring 576 xml = _bytes('<!DOCTYPE test [\n<!ENTITY entity "tasty">\n]>\n<test/>') 577 root = XML(xml) 578 tree1 = self.etree.ElementTree(root) 579 self.assertEqual(xml, tostring(tree1)) 580 581 tree2 = copy.deepcopy(tree1) 582 self.assertEqual(xml, tostring(tree2)) 583 584 root2 = copy.deepcopy(tree1.getroot()) 585 self.assertEqual(_bytes("<test/>"), 586 tostring(root2))
587
588 - def test_deepcopy_pi_dtd(self):
589 XML = self.etree.XML 590 tostring = self.etree.tostring 591 xml = _bytes('<!-- comment --><!DOCTYPE test [\n<!ENTITY entity "tasty">\n]>\n<test/>') 592 root = XML(xml) 593 tree1 = self.etree.ElementTree(root) 594 self.assertEqual(xml, tostring(tree1)) 595 596 tree2 = copy.deepcopy(tree1) 597 self.assertEqual(xml, tostring(tree2))
598
599 - def test_parse_remove_comments(self):
600 fromstring = self.etree.fromstring 601 tostring = self.etree.tostring 602 XMLParser = self.etree.XMLParser 603 604 xml = _bytes('<a><!--A--><b><!-- B --><c/></b><!--C--></a>') 605 parser = XMLParser(remove_comments=True) 606 root = fromstring(xml, parser) 607 self.assertEqual( 608 _bytes('<a><b><c/></b></a>'), 609 tostring(root))
610
611 - def test_parse_remove_pis(self):
612 parse = self.etree.parse 613 tostring = self.etree.tostring 614 XMLParser = self.etree.XMLParser 615 616 xml = _bytes('<?test?><a><?A?><b><?B?><c/></b><?C?></a><?tail?>') 617 618 f = BytesIO(xml) 619 tree = parse(f) 620 self.assertEqual( 621 xml, 622 tostring(tree)) 623 624 parser = XMLParser(remove_pis=True) 625 tree = parse(f, parser) 626 self.assertEqual( 627 _bytes('<a><b><c/></b></a>'), 628 tostring(tree))
629
631 # ET raises IOError only 632 parse = self.etree.parse 633 self.assertRaises(TypeError, parse, 'notthere.xml', object())
634
636 # ET removes comments 637 iterparse = self.etree.iterparse 638 tostring = self.etree.tostring 639 640 f = BytesIO('<a><!--A--><b><!-- B --><c/></b><!--C--></a>') 641 events = list(iterparse(f)) 642 root = events[-1][1] 643 self.assertEqual(3, len(events)) 644 self.assertEqual( 645 _bytes('<a><!--A--><b><!-- B --><c/></b><!--C--></a>'), 646 tostring(root))
647
648 - def test_iterparse_comments(self):
649 # ET removes comments 650 iterparse = self.etree.iterparse 651 tostring = self.etree.tostring 652 653 def name(event, el): 654 if event == 'comment': 655 return el.text 656 else: 657 return el.tag
658 659 f = BytesIO('<a><!--A--><b><!-- B --><c/></b><!--C--></a>') 660 events = list(iterparse(f, events=('end', 'comment'))) 661 root = events[-1][1] 662 self.assertEqual(6, len(events)) 663 self.assertEqual(['A', ' B ', 'c', 'b', 'C', 'a'], 664 [ name(*item) for item in events ]) 665 self.assertEqual( 666 _bytes('<a><!--A--><b><!-- B --><c/></b><!--C--></a>'), 667 tostring(root))
668
669 - def test_iterparse_pis(self):
670 # ET removes pis 671 iterparse = self.etree.iterparse 672 tostring = self.etree.tostring 673 ElementTree = self.etree.ElementTree 674 675 def name(event, el): 676 if event == 'pi': 677 return (el.target, el.text) 678 else: 679 return el.tag
680 681 f = BytesIO('<?pia a?><a><?pib b?><b><?pic c?><c/></b><?pid d?></a><?pie e?>') 682 events = list(iterparse(f, events=('end', 'pi'))) 683 root = events[-2][1] 684 self.assertEqual(8, len(events)) 685 self.assertEqual([('pia','a'), ('pib','b'), ('pic','c'), 'c', 'b', 686 ('pid','d'), 'a', ('pie','e')], 687 [ name(*item) for item in events ]) 688 self.assertEqual( 689 _bytes('<?pia a?><a><?pib b?><b><?pic c?><c/></b><?pid d?></a><?pie e?>'), 690 tostring(ElementTree(root))) 691
692 - def test_iterparse_remove_comments(self):
693 iterparse = self.etree.iterparse 694 tostring = self.etree.tostring 695 696 f = BytesIO('<a><!--A--><b><!-- B --><c/></b><!--C--></a>') 697 events = list(iterparse(f, remove_comments=True, 698 events=('end', 'comment'))) 699 root = events[-1][1] 700 self.assertEqual(3, len(events)) 701 self.assertEqual(['c', 'b', 'a'], 702 [ el.tag for (event, el) in events ]) 703 self.assertEqual( 704 _bytes('<a><b><c/></b></a>'), 705 tostring(root))
706
707 - def test_iterparse_broken(self):
708 iterparse = self.etree.iterparse 709 f = BytesIO('<a><b><c/></a>') 710 # ET raises ExpatError, lxml raises XMLSyntaxError 711 self.assertRaises(self.etree.XMLSyntaxError, list, iterparse(f))
712
713 - def test_iterparse_broken_recover(self):
714 iterparse = self.etree.iterparse 715 f = BytesIO('<a><b><c/></a>') 716 it = iterparse(f, events=('start', 'end'), recover=True) 717 events = [(ev, el.tag) for ev, el in it] 718 root = it.root 719 self.assertTrue(root is not None) 720 721 self.assertEqual(1, events.count(('start', 'a'))) 722 self.assertEqual(1, events.count(('end', 'a'))) 723 724 self.assertEqual(1, events.count(('start', 'b'))) 725 self.assertEqual(1, events.count(('end', 'b'))) 726 727 self.assertEqual(1, events.count(('start', 'c'))) 728 self.assertEqual(1, events.count(('end', 'c')))
729
730 - def test_iterparse_broken_multi_recover(self):
731 iterparse = self.etree.iterparse 732 f = BytesIO('<a><b><c/></d><b><c/></a></b>') 733 it = iterparse(f, events=('start', 'end'), recover=True) 734 events = [(ev, el.tag) for ev, el in it] 735 root = it.root 736 self.assertTrue(root is not None) 737 738 self.assertEqual(1, events.count(('start', 'a'))) 739 self.assertEqual(1, events.count(('end', 'a'))) 740 741 self.assertEqual(2, events.count(('start', 'b'))) 742 self.assertEqual(2, events.count(('end', 'b'))) 743 744 self.assertEqual(2, events.count(('start', 'c'))) 745 self.assertEqual(2, events.count(('end', 'c')))
746
747 - def test_iterparse_strip(self):
748 iterparse = self.etree.iterparse 749 f = BytesIO(""" 750 <a> \n \n <b> b test </b> \n 751 752 \n\t <c> \n </c> </a> \n """) 753 iterator = iterparse(f, remove_blank_text=True) 754 text = [ (element.text, element.tail) 755 for event, element in iterator ] 756 self.assertEqual( 757 [(" b test ", None), (" \n ", None), (None, None)], 758 text)
759
760 - def test_iterparse_tag(self):
761 iterparse = self.etree.iterparse 762 f = BytesIO('<a><b><d/></b><c/></a>') 763 764 iterator = iterparse(f, tag="b", events=('start', 'end')) 765 events = list(iterator) 766 root = iterator.root 767 self.assertEqual( 768 [('start', root[0]), ('end', root[0])], 769 events)
770
771 - def test_iterparse_tag_all(self):
772 iterparse = self.etree.iterparse 773 f = BytesIO('<a><b><d/></b><c/></a>') 774 775 iterator = iterparse(f, tag="*", events=('start', 'end')) 776 events = list(iterator) 777 self.assertEqual( 778 8, 779 len(events))
780
781 - def test_iterparse_tag_ns(self):
782 iterparse = self.etree.iterparse 783 f = BytesIO('<a xmlns="urn:test:1"><b><d/></b><c/></a>') 784 785 iterator = iterparse(f, tag="{urn:test:1}b", events=('start', 'end')) 786 events = list(iterator) 787 root = iterator.root 788 self.assertEqual( 789 [('start', root[0]), ('end', root[0])], 790 events)
791
792 - def test_iterparse_tag_ns_empty(self):
793 iterparse = self.etree.iterparse 794 f = BytesIO('<a><b><d/></b><c/></a>') 795 iterator = iterparse(f, tag="{}b", events=('start', 'end')) 796 events = list(iterator) 797 root = iterator.root 798 self.assertEqual( 799 [('start', root[0]), ('end', root[0])], 800 events) 801 802 f = BytesIO('<a xmlns="urn:test:1"><b><d/></b><c/></a>') 803 iterator = iterparse(f, tag="{}b", events=('start', 'end')) 804 events = list(iterator) 805 root = iterator.root 806 self.assertEqual([], events)
807
808 - def test_iterparse_tag_ns_all(self):
809 iterparse = self.etree.iterparse 810 f = BytesIO('<a xmlns="urn:test:1"><b><d/></b><c/></a>') 811 iterator = iterparse(f, tag="{urn:test:1}*", events=('start', 'end')) 812 events = list(iterator) 813 self.assertEqual(8, len(events))
814
815 - def test_iterparse_tag_ns_empty_all(self):
816 iterparse = self.etree.iterparse 817 f = BytesIO('<a xmlns="urn:test:1"><b><d/></b><c/></a>') 818 iterator = iterparse(f, tag="{}*", events=('start', 'end')) 819 events = list(iterator) 820 self.assertEqual([], events) 821 822 f = BytesIO('<a><b><d/></b><c/></a>') 823 iterator = iterparse(f, tag="{}*", events=('start', 'end')) 824 events = list(iterator) 825 self.assertEqual(8, len(events))
826
827 - def test_iterparse_encoding_error(self):
828 text = _str('Søk på nettet') 829 wrong_declaration = "<?xml version='1.0' encoding='UTF-8'?>" 830 xml_latin1 = (_str('%s<a>%s</a>') % (wrong_declaration, text) 831 ).encode('iso-8859-1') 832 833 self.assertRaises(self.etree.ParseError, 834 list, self.etree.iterparse(BytesIO(xml_latin1)))
835
836 - def test_iterparse_encoding_8bit_override(self):
837 text = _str('Søk på nettet', encoding="UTF-8") 838 wrong_declaration = "<?xml version='1.0' encoding='UTF-8'?>" 839 xml_latin1 = (_str('%s<a>%s</a>') % (wrong_declaration, text) 840 ).encode('iso-8859-1') 841 842 iterator = self.etree.iterparse(BytesIO(xml_latin1), 843 encoding="iso-8859-1") 844 self.assertEqual(1, len(list(iterator))) 845 846 a = iterator.root 847 self.assertEqual(a.text, text)
848
849 - def test_iterparse_keep_cdata(self):
850 tostring = self.etree.tostring 851 f = BytesIO('<root><![CDATA[test]]></root>') 852 context = self.etree.iterparse(f, strip_cdata=False) 853 content = [ el.text for event,el in context ] 854 855 self.assertEqual(['test'], content) 856 self.assertEqual(_bytes('<root><![CDATA[test]]></root>'), 857 tostring(context.root))
858
859 - def test_parser_encoding_unknown(self):
860 self.assertRaises( 861 LookupError, self.etree.XMLParser, encoding="hopefully unknown")
862
863 - def test_parser_encoding(self):
864 self.etree.XMLParser(encoding="ascii") 865 self.etree.XMLParser(encoding="utf-8") 866 self.etree.XMLParser(encoding="iso-8859-1")
867
868 - def test_feed_parser_recover(self):
869 parser = self.etree.XMLParser(recover=True) 870 871 parser.feed('<?xml version=') 872 parser.feed('"1.0"?><ro') 873 parser.feed('ot><') 874 parser.feed('a test="works"') 875 parser.feed('><othertag/></root') # <a> not closed! 876 parser.feed('>') 877 878 root = parser.close() 879 880 self.assertEqual(root.tag, "root") 881 self.assertEqual(len(root), 1) 882 self.assertEqual(root[0].tag, "a") 883 self.assertEqual(root[0].get("test"), "works") 884 self.assertEqual(len(root[0]), 1) 885 self.assertEqual(root[0][0].tag, "othertag")
886 # FIXME: would be nice to get some errors logged ... 887 #self.assertTrue(len(parser.error_log) > 0, "error log is empty") 888
889 - def test_feed_parser_recover_no_id_dict(self):
890 # test that recover mode plays nicely with the no-id-dict setup 891 parser = self.etree.XMLParser(recover=True, collect_ids=False) 892 893 parser.feed('<?xml version=') 894 parser.feed('"1.0"?><ro') 895 parser.feed('ot xml:id="123"><') 896 parser.feed('a test="works" xml:id=') 897 parser.feed('"321"><othertag/></root') # <a> not closed! 898 parser.feed('>') 899 900 root = parser.close() 901 902 self.assertEqual(root.tag, "root") 903 self.assertEqual(len(root), 1) 904 self.assertEqual(root[0].tag, "a") 905 self.assertEqual(root[0].get("test"), "works") 906 self.assertEqual(root[0].attrib, { 907 'test': 'works', 908 '{http://www.w3.org/XML/1998/namespace}id': '321'}) 909 self.assertEqual(len(root[0]), 1) 910 self.assertEqual(root[0][0].tag, "othertag")
911 # FIXME: would be nice to get some errors logged ... 912 #self.assertTrue(len(parser.error_log) > 0, "error log is empty") 913
914 - def test_elementtree_parser_target_type_error(self):
915 assertEqual = self.assertEqual 916 assertFalse = self.assertFalse 917 918 events = [] 919 class Target(object): 920 def start(self, tag, attrib): 921 events.append("start") 922 assertFalse(attrib) 923 assertEqual("TAG", tag)
924 def end(self, tag): 925 events.append("end") 926 assertEqual("TAG", tag) 927 def close(self): 928 return "DONE" # no Element! 929 930 parser = self.etree.XMLParser(target=Target()) 931 tree = self.etree.ElementTree() 932 933 self.assertRaises(TypeError, 934 tree.parse, BytesIO("<TAG/>"), parser=parser) 935 self.assertEqual(["start", "end"], events) 936
937 - def test_parser_target_feed_exception(self):
938 # ET doesn't call .close() on errors 939 events = [] 940 class Target(object): 941 def start(self, tag, attrib): 942 events.append("start-" + tag)
943 def end(self, tag): 944 events.append("end-" + tag) 945 if tag == 'a': 946 raise ValueError("dead and gone") 947 def data(self, data): 948 events.append("data-" + data) 949 def close(self): 950 events.append("close") 951 return "DONE" 952 953 parser = self.etree.XMLParser(target=Target()) 954 955 try: 956 parser.feed(_bytes('<root>A<a>ca</a>B</root>')) 957 done = parser.close() 958 self.fail("error expected, but parsing succeeded") 959 except ValueError: 960 done = 'value error received as expected' 961 962 self.assertEqual(["start-root", "data-A", "start-a", 963 "data-ca", "end-a", "close"], 964 events) 965
966 - def test_parser_target_fromstring_exception(self):
967 # ET doesn't call .close() on errors 968 events = [] 969 class Target(object): 970 def start(self, tag, attrib): 971 events.append("start-" + tag)
972 def end(self, tag): 973 events.append("end-" + tag) 974 if tag == 'a': 975 raise ValueError("dead and gone") 976 def data(self, data): 977 events.append("data-" + data) 978 def close(self): 979 events.append("close") 980 return "DONE" 981 982 parser = self.etree.XMLParser(target=Target()) 983 984 try: 985 done = self.etree.fromstring(_bytes('<root>A<a>ca</a>B</root>'), 986 parser=parser) 987 self.fail("error expected, but parsing succeeded") 988 except ValueError: 989 done = 'value error received as expected' 990 991 self.assertEqual(["start-root", "data-A", "start-a", 992 "data-ca", "end-a", "close"], 993 events) 994
995 - def test_parser_target_feed_no_id_dict(self):
996 # test that target parsing works nicely with the no-id-hash setup 997 events = [] 998 class Target(object): 999 def start(self, tag, attrib): 1000 events.append("start-" + tag)
1001 def end(self, tag): 1002 events.append("end-" + tag) 1003 def data(self, data): 1004 events.append("data-" + data) 1005 def comment(self, text): 1006 events.append("comment-" + text) 1007 def close(self): 1008 return "DONE" 1009 1010 parser = self.etree.XMLParser(target=Target(), collect_ids=False) 1011 1012 parser.feed(_bytes('<!--a--><root xml:id="123">A<!--b-->')) 1013 parser.feed(_bytes('<sub xml:id="321"/>B</root>')) 1014 done = parser.close() 1015 1016 self.assertEqual("DONE", done) 1017 self.assertEqual(["comment-a", "start-root", "data-A", "comment-b", 1018 "start-sub", "end-sub", "data-B", "end-root"], 1019 events) 1020
1021 - def test_parser_target_comment(self):
1022 events = [] 1023 class Target(object): 1024 def start(self, tag, attrib): 1025 events.append("start-" + tag)
1026 def end(self, tag): 1027 events.append("end-" + tag) 1028 def data(self, data): 1029 events.append("data-" + data) 1030 def comment(self, text): 1031 events.append("comment-" + text) 1032 def close(self): 1033 return "DONE" 1034 1035 parser = self.etree.XMLParser(target=Target()) 1036 1037 parser.feed(_bytes('<!--a--><root>A<!--b--><sub/><!--c-->B</root><!--d-->')) 1038 done = parser.close() 1039 1040 self.assertEqual("DONE", done) 1041 self.assertEqual(["comment-a", "start-root", "data-A", "comment-b", 1042 "start-sub", "end-sub", "comment-c", "data-B", 1043 "end-root", "comment-d"], 1044 events) 1045
1046 - def test_parser_target_pi(self):
1047 events = [] 1048 class Target(object): 1049 def start(self, tag, attrib): 1050 events.append("start-" + tag)
1051 def end(self, tag): 1052 events.append("end-" + tag) 1053 def data(self, data): 1054 events.append("data-" + data) 1055 def pi(self, target, data): 1056 events.append("pi-" + target + "-" + data) 1057 def close(self): 1058 return "DONE" 1059 1060 parser = self.etree.XMLParser(target=Target()) 1061 1062 parser.feed(_bytes('<?test a?><root>A<?test b?>B</root><?test c?>')) 1063 done = parser.close() 1064 1065 self.assertEqual("DONE", done) 1066 self.assertEqual(["pi-test-a", "start-root", "data-A", "pi-test-b", 1067 "data-B", "end-root", "pi-test-c"], 1068 events) 1069
1070 - def test_parser_target_cdata(self):
1071 events = [] 1072 class Target(object): 1073 def start(self, tag, attrib): 1074 events.append("start-" + tag)
1075 def end(self, tag): 1076 events.append("end-" + tag) 1077 def data(self, data): 1078 events.append("data-" + data) 1079 def close(self): 1080 return "DONE" 1081 1082 parser = self.etree.XMLParser(target=Target(), 1083 strip_cdata=False) 1084 1085 parser.feed(_bytes('<root>A<a><![CDATA[ca]]></a>B</root>')) 1086 done = parser.close() 1087 1088 self.assertEqual("DONE", done) 1089 self.assertEqual(["start-root", "data-A", "start-a", 1090 "data-ca", "end-a", "data-B", "end-root"], 1091 events) 1092
1093 - def test_parser_target_recover(self):
1094 events = [] 1095 class Target(object): 1096 def start(self, tag, attrib): 1097 events.append("start-" + tag)
1098 def end(self, tag): 1099 events.append("end-" + tag) 1100 def data(self, data): 1101 events.append("data-" + data) 1102 def close(self): 1103 events.append("close") 1104 return "DONE" 1105 1106 parser = self.etree.XMLParser(target=Target(), 1107 recover=True) 1108 1109 parser.feed(_bytes('<root>A<a>ca</a>B</not-root>')) 1110 done = parser.close() 1111 1112 self.assertEqual("DONE", done) 1113 self.assertEqual(["start-root", "data-A", "start-a", 1114 "data-ca", "end-a", "data-B", 1115 "end-root", "close"], 1116 events) 1117
1118 - def test_iterwalk_tag(self):
1119 iterwalk = self.etree.iterwalk 1120 root = self.etree.XML(_bytes('<a><b><d/></b><c/></a>')) 1121 1122 iterator = iterwalk(root, tag="b", events=('start', 'end')) 1123 events = list(iterator) 1124 self.assertEqual( 1125 [('start', root[0]), ('end', root[0])], 1126 events)
1127
1128 - def test_iterwalk_tag_all(self):
1129 iterwalk = self.etree.iterwalk 1130 root = self.etree.XML(_bytes('<a><b><d/></b><c/></a>')) 1131 1132 iterator = iterwalk(root, tag="*", events=('start', 'end')) 1133 events = list(iterator) 1134 self.assertEqual( 1135 8, 1136 len(events))
1137
1138 - def test_iterwalk(self):
1139 iterwalk = self.etree.iterwalk 1140 root = self.etree.XML(_bytes('<a><b></b><c/></a>')) 1141 1142 events = list(iterwalk(root)) 1143 self.assertEqual( 1144 [('end', root[0]), ('end', root[1]), ('end', root)], 1145 events)
1146
1147 - def test_iterwalk_start(self):
1148 iterwalk = self.etree.iterwalk 1149 root = self.etree.XML(_bytes('<a><b></b><c/></a>')) 1150 1151 iterator = iterwalk(root, events=('start',)) 1152 events = list(iterator) 1153 self.assertEqual( 1154 [('start', root), ('start', root[0]), ('start', root[1])], 1155 events)
1156
1157 - def test_iterwalk_start_end(self):
1158 iterwalk = self.etree.iterwalk 1159 root = self.etree.XML(_bytes('<a><b></b><c/></a>')) 1160 1161 iterator = iterwalk(root, events=('start','end')) 1162 events = list(iterator) 1163 self.assertEqual( 1164 [('start', root), ('start', root[0]), ('end', root[0]), 1165 ('start', root[1]), ('end', root[1]), ('end', root)], 1166 events)
1167
1168 - def test_iterwalk_clear(self):
1169 iterwalk = self.etree.iterwalk 1170 root = self.etree.XML(_bytes('<a><b></b><c/></a>')) 1171 1172 iterator = iterwalk(root) 1173 for event, elem in iterator: 1174 elem.clear() 1175 1176 self.assertEqual(0, 1177 len(root))
1178
1179 - def test_iterwalk_attrib_ns(self):
1180 iterwalk = self.etree.iterwalk 1181 root = self.etree.XML(_bytes('<a xmlns="ns1"><b><c xmlns="ns2"/></b></a>')) 1182 1183 attr_name = '{testns}bla' 1184 events = [] 1185 iterator = iterwalk(root, events=('start','end','start-ns','end-ns')) 1186 for event, elem in iterator: 1187 events.append(event) 1188 if event == 'start': 1189 if elem.tag != '{ns1}a': 1190 elem.set(attr_name, 'value') 1191 1192 self.assertEqual( 1193 ['start-ns', 'start', 'start', 'start-ns', 'start', 1194 'end', 'end-ns', 'end', 'end', 'end-ns'], 1195 events) 1196 1197 self.assertEqual( 1198 None, 1199 root.get(attr_name)) 1200 self.assertEqual( 1201 'value', 1202 root[0].get(attr_name))
1203
1204 - def test_iterwalk_getiterator(self):
1205 iterwalk = self.etree.iterwalk 1206 root = self.etree.XML(_bytes('<a><b><d/></b><c/></a>')) 1207 1208 counts = [] 1209 for event, elem in iterwalk(root): 1210 counts.append(len(list(elem.getiterator()))) 1211 self.assertEqual( 1212 [1,2,1,4], 1213 counts)
1214
1215 - def test_resolve_string_dtd(self):
1216 parse = self.etree.parse 1217 parser = self.etree.XMLParser(dtd_validation=True) 1218 assertEqual = self.assertEqual 1219 test_url = _str("__nosuch.dtd") 1220 1221 class MyResolver(self.etree.Resolver): 1222 def resolve(self, url, id, context): 1223 assertEqual(url, test_url) 1224 return self.resolve_string( 1225 _str('''<!ENTITY myentity "%s"> 1226 <!ELEMENT doc ANY>''') % url, context)
1227 1228 parser.resolvers.add(MyResolver()) 1229 1230 xml = _str('<!DOCTYPE doc SYSTEM "%s"><doc>&myentity;</doc>') % test_url 1231 tree = parse(StringIO(xml), parser) 1232 root = tree.getroot() 1233 self.assertEqual(root.text, test_url) 1234
1235 - def test_resolve_bytes_dtd(self):
1236 parse = self.etree.parse 1237 parser = self.etree.XMLParser(dtd_validation=True) 1238 assertEqual = self.assertEqual 1239 test_url = _str("__nosuch.dtd") 1240 1241 class MyResolver(self.etree.Resolver): 1242 def resolve(self, url, id, context): 1243 assertEqual(url, test_url) 1244 return self.resolve_string( 1245 (_str('''<!ENTITY myentity "%s"> 1246 <!ELEMENT doc ANY>''') % url).encode('utf-8'), 1247 context)
1248 1249 parser.resolvers.add(MyResolver()) 1250 1251 xml = _str('<!DOCTYPE doc SYSTEM "%s"><doc>&myentity;</doc>') % test_url 1252 tree = parse(StringIO(xml), parser) 1253 root = tree.getroot() 1254 self.assertEqual(root.text, test_url) 1255
1256 - def test_resolve_filelike_dtd(self):
1257 parse = self.etree.parse 1258 parser = self.etree.XMLParser(dtd_validation=True) 1259 assertEqual = self.assertEqual 1260 test_url = _str("__nosuch.dtd") 1261 1262 class MyResolver(self.etree.Resolver): 1263 def resolve(self, url, id, context): 1264 assertEqual(url, test_url) 1265 return self.resolve_file( 1266 SillyFileLike( 1267 _str('''<!ENTITY myentity "%s"> 1268 <!ELEMENT doc ANY>''') % url), context)
1269 1270 parser.resolvers.add(MyResolver()) 1271 1272 xml = _str('<!DOCTYPE doc SYSTEM "%s"><doc>&myentity;</doc>') % test_url 1273 tree = parse(StringIO(xml), parser) 1274 root = tree.getroot() 1275 self.assertEqual(root.text, test_url) 1276
1277 - def test_resolve_filename_dtd(self):
1278 parse = self.etree.parse 1279 parser = self.etree.XMLParser(attribute_defaults=True) 1280 assertEqual = self.assertEqual 1281 test_url = _str("__nosuch.dtd") 1282 1283 class MyResolver(self.etree.Resolver): 1284 def resolve(self, url, id, context): 1285 assertEqual(url, test_url) 1286 return self.resolve_filename( 1287 fileInTestDir('test.dtd'), context)
1288 1289 parser.resolvers.add(MyResolver()) 1290 1291 xml = _str('<!DOCTYPE a SYSTEM "%s"><a><b/></a>') % test_url 1292 tree = parse(StringIO(xml), parser) 1293 root = tree.getroot() 1294 self.assertEqual( 1295 root.attrib, {'default': 'valueA'}) 1296 self.assertEqual( 1297 root[0].attrib, {'default': 'valueB'}) 1298
1299 - def test_resolve_filename_dtd_relative(self):
1300 parse = self.etree.parse 1301 parser = self.etree.XMLParser(attribute_defaults=True) 1302 assertEqual = self.assertEqual 1303 test_url = _str("__nosuch.dtd") 1304 1305 class MyResolver(self.etree.Resolver): 1306 def resolve(self, url, id, context): 1307 expected = fileUrlInTestDir(test_url) 1308 url = url.replace('file://', 'file:') # depends on libxml2 version 1309 expected = expected.replace('file://', 'file:') 1310 assertEqual(url, expected) 1311 return self.resolve_filename( 1312 fileUrlInTestDir('test.dtd'), context)
1313 1314 parser.resolvers.add(MyResolver()) 1315 1316 xml = _str('<!DOCTYPE a SYSTEM "%s"><a><b/></a>') % test_url 1317 tree = parse(StringIO(xml), parser, 1318 base_url=fileUrlInTestDir('__test.xml')) 1319 root = tree.getroot() 1320 self.assertEqual( 1321 root.attrib, {'default': 'valueA'}) 1322 self.assertEqual( 1323 root[0].attrib, {'default': 'valueB'}) 1324
1325 - def test_resolve_file_dtd(self):
1326 parse = self.etree.parse 1327 parser = self.etree.XMLParser(attribute_defaults=True) 1328 assertEqual = self.assertEqual 1329 test_url = _str("__nosuch.dtd") 1330 1331 class MyResolver(self.etree.Resolver): 1332 def resolve(self, url, id, context): 1333 assertEqual(url, test_url) 1334 return self.resolve_file( 1335 open(fileInTestDir('test.dtd'), 'rb'), context)
1336 1337 parser.resolvers.add(MyResolver()) 1338 1339 xml = _str('<!DOCTYPE a SYSTEM "%s"><a><b/></a>') % test_url 1340 tree = parse(StringIO(xml), parser) 1341 root = tree.getroot() 1342 self.assertEqual( 1343 root.attrib, {'default': 'valueA'}) 1344 self.assertEqual( 1345 root[0].attrib, {'default': 'valueB'}) 1346
1347 - def test_resolve_empty(self):
1348 parse = self.etree.parse 1349 parser = self.etree.XMLParser(load_dtd=True) 1350 assertEqual = self.assertEqual 1351 test_url = _str("__nosuch.dtd") 1352 1353 class check(object): 1354 resolved = False
1355 1356 class MyResolver(self.etree.Resolver): 1357 def resolve(self, url, id, context): 1358 assertEqual(url, test_url) 1359 check.resolved = True 1360 return self.resolve_empty(context) 1361 1362 parser.resolvers.add(MyResolver()) 1363 1364 xml = _str('<!DOCTYPE doc SYSTEM "%s"><doc>&myentity;</doc>') % test_url 1365 self.assertRaises(etree.XMLSyntaxError, parse, StringIO(xml), parser) 1366 self.assertTrue(check.resolved) 1367
1368 - def test_resolve_error(self):
1369 parse = self.etree.parse 1370 parser = self.etree.XMLParser(dtd_validation=True) 1371 1372 class _LocalException(Exception): 1373 pass
1374 1375 class MyResolver(self.etree.Resolver): 1376 def resolve(self, url, id, context): 1377 raise _LocalException 1378 1379 parser.resolvers.add(MyResolver()) 1380 1381 xml = '<!DOCTYPE doc SYSTEM "test"><doc>&myentity;</doc>' 1382 self.assertRaises(_LocalException, parse, BytesIO(xml), parser) 1383 1384 if etree.LIBXML_VERSION > (2,6,20):
1385 - def test_entity_parse(self):
1386 parse = self.etree.parse 1387 tostring = self.etree.tostring 1388 parser = self.etree.XMLParser(resolve_entities=False) 1389 Entity = self.etree.Entity 1390 1391 xml = _bytes('<!DOCTYPE doc SYSTEM "test"><doc>&myentity;</doc>') 1392 tree = parse(BytesIO(xml), parser) 1393 root = tree.getroot() 1394 self.assertEqual(root[0].tag, Entity) 1395 self.assertEqual(root[0].text, "&myentity;") 1396 self.assertEqual(root[0].tail, None) 1397 self.assertEqual(root[0].name, "myentity") 1398 1399 self.assertEqual(_bytes('<doc>&myentity;</doc>'), 1400 tostring(root))
1401
1402 - def test_entity_restructure(self):
1403 xml = _bytes('''<!DOCTYPE root [ <!ENTITY nbsp "&#160;"> ]> 1404 <root> 1405 <child1/> 1406 <child2/> 1407 <child3>&nbsp;</child3> 1408 </root>''') 1409 1410 parser = self.etree.XMLParser(resolve_entities=False) 1411 root = etree.fromstring(xml, parser) 1412 self.assertEqual([ el.tag for el in root ], 1413 ['child1', 'child2', 'child3']) 1414 1415 root[0] = root[-1] 1416 self.assertEqual([ el.tag for el in root ], 1417 ['child3', 'child2']) 1418 self.assertEqual(root[0][0].text, '&nbsp;') 1419 self.assertEqual(root[0][0].name, 'nbsp')
1420
1421 - def test_entity_append(self):
1422 Entity = self.etree.Entity 1423 Element = self.etree.Element 1424 tostring = self.etree.tostring 1425 1426 root = Element("root") 1427 root.append( Entity("test") ) 1428 1429 self.assertEqual(root[0].tag, Entity) 1430 self.assertEqual(root[0].text, "&test;") 1431 self.assertEqual(root[0].tail, None) 1432 self.assertEqual(root[0].name, "test") 1433 1434 self.assertEqual(_bytes('<root>&test;</root>'), 1435 tostring(root))
1436
1437 - def test_entity_values(self):
1438 Entity = self.etree.Entity 1439 self.assertEqual(Entity("test").text, '&test;') 1440 self.assertEqual(Entity("#17683").text, '&#17683;') 1441 self.assertEqual(Entity("#x1768").text, '&#x1768;') 1442 self.assertEqual(Entity("#x98AF").text, '&#x98AF;')
1443
1444 - def test_entity_error(self):
1445 Entity = self.etree.Entity 1446 self.assertRaises(ValueError, Entity, 'a b c') 1447 self.assertRaises(ValueError, Entity, 'a,b') 1448 self.assertRaises(ValueError, Entity, 'a\0b') 1449 self.assertRaises(ValueError, Entity, '#abc') 1450 self.assertRaises(ValueError, Entity, '#xxyz')
1451
1452 - def test_cdata(self):
1453 CDATA = self.etree.CDATA 1454 Element = self.etree.Element 1455 tostring = self.etree.tostring 1456 1457 root = Element("root") 1458 root.text = CDATA('test') 1459 1460 self.assertEqual('test', 1461 root.text) 1462 self.assertEqual(_bytes('<root><![CDATA[test]]></root>'), 1463 tostring(root))
1464
1465 - def test_cdata_tail(self):
1466 CDATA = self.etree.CDATA 1467 Element = self.etree.Element 1468 SubElement = self.etree.SubElement 1469 tostring = self.etree.tostring 1470 1471 root = Element("root") 1472 child = SubElement(root, 'child') 1473 child.tail = CDATA('test') 1474 1475 self.assertEqual('test', child.tail) 1476 self.assertEqual(_bytes('<root><child/><![CDATA[test]]></root>'), 1477 tostring(root)) 1478 1479 root = Element("root") 1480 root.tail = CDATA('test') 1481 1482 self.assertEqual('test', root.tail) 1483 self.assertEqual(_bytes('<root/><![CDATA[test]]>'), 1484 tostring(root))
1485
1486 - def test_cdata_type(self):
1487 CDATA = self.etree.CDATA 1488 Element = self.etree.Element 1489 root = Element("root") 1490 1491 root.text = CDATA("test") 1492 self.assertEqual('test', root.text) 1493 1494 root.text = CDATA(_str("test")) 1495 self.assertEqual('test', root.text) 1496 1497 self.assertRaises(TypeError, CDATA, 1)
1498
1499 - def test_cdata_errors(self):
1500 CDATA = self.etree.CDATA 1501 Element = self.etree.Element 1502 1503 root = Element("root") 1504 cdata = CDATA('test') 1505 1506 self.assertRaises(TypeError, 1507 root.set, 'attr', cdata) 1508 self.assertRaises(TypeError, 1509 operator.setitem, root.attrib, 'attr', cdata)
1510
1511 - def test_cdata_parser(self):
1512 tostring = self.etree.tostring 1513 parser = self.etree.XMLParser(strip_cdata=False) 1514 root = self.etree.XML(_bytes('<root><![CDATA[test]]></root>'), parser) 1515 1516 self.assertEqual('test', root.text) 1517 self.assertEqual(_bytes('<root><![CDATA[test]]></root>'), 1518 tostring(root))
1519
1520 - def test_cdata_xpath(self):
1521 tostring = self.etree.tostring 1522 parser = self.etree.XMLParser(strip_cdata=False) 1523 root = self.etree.XML(_bytes('<root><![CDATA[test]]></root>'), parser) 1524 self.assertEqual(_bytes('<root><![CDATA[test]]></root>'), 1525 tostring(root)) 1526 1527 self.assertEqual(['test'], root.xpath('//text()'))
1528 1529 # TypeError in etree, AssertionError in ElementTree;
1530 - def test_setitem_assert(self):
1531 Element = self.etree.Element 1532 SubElement = self.etree.SubElement 1533 1534 a = Element('a') 1535 b = SubElement(a, 'b') 1536 1537 self.assertRaises(TypeError, 1538 a.__setitem__, 0, 'foo')
1539
1540 - def test_append_error(self):
1541 Element = self.etree.Element 1542 root = Element('root') 1543 # raises AssertionError in ElementTree 1544 self.assertRaises(TypeError, root.append, None) 1545 self.assertRaises(TypeError, root.extend, [None]) 1546 self.assertRaises(TypeError, root.extend, [Element('one'), None]) 1547 self.assertEqual('one', root[0].tag)
1548
1549 - def test_append_recursive_error(self):
1550 Element = self.etree.Element 1551 SubElement = self.etree.SubElement 1552 root = Element('root') 1553 self.assertRaises(ValueError, root.append, root) 1554 child = SubElement(root, 'child') 1555 self.assertRaises(ValueError, child.append, root) 1556 child2 = SubElement(child, 'child2') 1557 self.assertRaises(ValueError, child2.append, root) 1558 self.assertRaises(ValueError, child2.append, child) 1559 self.assertEqual('child2', root[0][0].tag)
1560
1561 - def test_addnext(self):
1562 Element = self.etree.Element 1563 SubElement = self.etree.SubElement 1564 root = Element('root') 1565 SubElement(root, 'a') 1566 SubElement(root, 'b') 1567 1568 self.assertEqual(['a', 'b'], 1569 [c.tag for c in root]) 1570 root[1].addnext(root[0]) 1571 self.assertEqual(['b', 'a'], 1572 [c.tag for c in root])
1573
1574 - def test_addprevious(self):
1575 Element = self.etree.Element 1576 SubElement = self.etree.SubElement 1577 root = Element('root') 1578 SubElement(root, 'a') 1579 SubElement(root, 'b') 1580 1581 self.assertEqual(['a', 'b'], 1582 [c.tag for c in root]) 1583 root[0].addprevious(root[1]) 1584 self.assertEqual(['b', 'a'], 1585 [c.tag for c in root])
1586
1587 - def test_addnext_cycle(self):
1588 Element = self.etree.Element 1589 SubElement = self.etree.SubElement 1590 root = Element('root') 1591 a = SubElement(root, 'a') 1592 b = SubElement(a, 'b') 1593 # appending parent as sibling is forbidden 1594 self.assertRaises(ValueError, b.addnext, a) 1595 self.assertEqual(['a'], [c.tag for c in root]) 1596 self.assertEqual(['b'], [c.tag for c in a])
1597
1598 - def test_addprevious_cycle(self):
1599 Element = self.etree.Element 1600 SubElement = self.etree.SubElement 1601 root = Element('root') 1602 a = SubElement(root, 'a') 1603 b = SubElement(a, 'b') 1604 # appending parent as sibling is forbidden 1605 self.assertRaises(ValueError, b.addprevious, a) 1606 self.assertEqual(['a'], [c.tag for c in root]) 1607 self.assertEqual(['b'], [c.tag for c in a])
1608
1609 - def test_addnext_cycle_long(self):
1610 Element = self.etree.Element 1611 SubElement = self.etree.SubElement 1612 root = Element('root') 1613 a = SubElement(root, 'a') 1614 b = SubElement(a, 'b') 1615 c = SubElement(b, 'c') 1616 # appending parent as sibling is forbidden 1617 self.assertRaises(ValueError, c.addnext, a)
1618
1619 - def test_addprevious_cycle_long(self):
1620 Element = self.etree.Element 1621 SubElement = self.etree.SubElement 1622 root = Element('root') 1623 a = SubElement(root, 'a') 1624 b = SubElement(a, 'b') 1625 c = SubElement(b, 'c') 1626 # appending parent as sibling is forbidden 1627 self.assertRaises(ValueError, c.addprevious, a)
1628
1629 - def test_addprevious_noops(self):
1630 Element = self.etree.Element 1631 SubElement = self.etree.SubElement 1632 root = Element('root') 1633 a = SubElement(root, 'a') 1634 b = SubElement(root, 'b') 1635 a.addprevious(a) 1636 self.assertEqual('a', root[0].tag) 1637 self.assertEqual('b', root[1].tag) 1638 b.addprevious(b) 1639 self.assertEqual('a', root[0].tag) 1640 self.assertEqual('b', root[1].tag) 1641 b.addprevious(a) 1642 self.assertEqual('a', root[0].tag) 1643 self.assertEqual('b', root[1].tag)
1644
1645 - def test_addnext_noops(self):
1646 Element = self.etree.Element 1647 SubElement = self.etree.SubElement 1648 root = Element('root') 1649 a = SubElement(root, 'a') 1650 b = SubElement(root, 'b') 1651 a.addnext(a) 1652 self.assertEqual('a', root[0].tag) 1653 self.assertEqual('b', root[1].tag) 1654 b.addnext(b) 1655 self.assertEqual('a', root[0].tag) 1656 self.assertEqual('b', root[1].tag) 1657 a.addnext(b) 1658 self.assertEqual('a', root[0].tag) 1659 self.assertEqual('b', root[1].tag)
1660
1661 - def test_addnext_root(self):
1662 Element = self.etree.Element 1663 a = Element('a') 1664 b = Element('b') 1665 self.assertRaises(TypeError, a.addnext, b)
1666
1667 - def test_addprevious_pi(self):
1668 Element = self.etree.Element 1669 SubElement = self.etree.SubElement 1670 PI = self.etree.PI 1671 root = Element('root') 1672 SubElement(root, 'a') 1673 pi = PI('TARGET', 'TEXT') 1674 pi.tail = "TAIL" 1675 1676 self.assertEqual(_bytes('<root><a></a></root>'), 1677 self._writeElement(root)) 1678 root[0].addprevious(pi) 1679 self.assertEqual(_bytes('<root><?TARGET TEXT?>TAIL<a></a></root>'), 1680 self._writeElement(root))
1681
1682 - def test_addprevious_root_pi(self):
1683 Element = self.etree.Element 1684 PI = self.etree.PI 1685 root = Element('root') 1686 pi = PI('TARGET', 'TEXT') 1687 pi.tail = "TAIL" 1688 1689 self.assertEqual(_bytes('<root></root>'), 1690 self._writeElement(root)) 1691 root.addprevious(pi) 1692 self.assertEqual(_bytes('<?TARGET TEXT?>\n<root></root>'), 1693 self._writeElement(root))
1694
1695 - def test_addnext_pi(self):
1696 Element = self.etree.Element 1697 SubElement = self.etree.SubElement 1698 PI = self.etree.PI 1699 root = Element('root') 1700 SubElement(root, 'a') 1701 pi = PI('TARGET', 'TEXT') 1702 pi.tail = "TAIL" 1703 1704 self.assertEqual(_bytes('<root><a></a></root>'), 1705 self._writeElement(root)) 1706 root[0].addnext(pi) 1707 self.assertEqual(_bytes('<root><a></a><?TARGET TEXT?>TAIL</root>'), 1708 self._writeElement(root))
1709
1710 - def test_addnext_root_pi(self):
1711 Element = self.etree.Element 1712 PI = self.etree.PI 1713 root = Element('root') 1714 pi = PI('TARGET', 'TEXT') 1715 pi.tail = "TAIL" 1716 1717 self.assertEqual(_bytes('<root></root>'), 1718 self._writeElement(root)) 1719 root.addnext(pi) 1720 self.assertEqual(_bytes('<root></root>\n<?TARGET TEXT?>'), 1721 self._writeElement(root))
1722
1723 - def test_addnext_comment(self):
1724 Element = self.etree.Element 1725 SubElement = self.etree.SubElement 1726 Comment = self.etree.Comment 1727 root = Element('root') 1728 SubElement(root, 'a') 1729 comment = Comment('TEXT ') 1730 comment.tail = "TAIL" 1731 1732 self.assertEqual(_bytes('<root><a></a></root>'), 1733 self._writeElement(root)) 1734 root[0].addnext(comment) 1735 self.assertEqual(_bytes('<root><a></a><!--TEXT -->TAIL</root>'), 1736 self._writeElement(root))
1737
1738 - def test_addnext_root_comment(self):
1739 Element = self.etree.Element 1740 Comment = self.etree.Comment 1741 root = Element('root') 1742 comment = Comment('TEXT ') 1743 comment.tail = "TAIL" 1744 1745 self.assertEqual(_bytes('<root></root>'), 1746 self._writeElement(root)) 1747 root.addnext(comment) 1748 self.assertEqual(_bytes('<root></root>\n<!--TEXT -->'), 1749 self._writeElement(root))
1750
1751 - def test_addprevious_comment(self):
1752 Element = self.etree.Element 1753 SubElement = self.etree.SubElement 1754 Comment = self.etree.Comment 1755 root = Element('root') 1756 SubElement(root, 'a') 1757 comment = Comment('TEXT ') 1758 comment.tail = "TAIL" 1759 1760 self.assertEqual(_bytes('<root><a></a></root>'), 1761 self._writeElement(root)) 1762 root[0].addprevious(comment) 1763 self.assertEqual(_bytes('<root><!--TEXT -->TAIL<a></a></root>'), 1764 self._writeElement(root))
1765
1766 - def test_addprevious_root_comment(self):
1767 Element = self.etree.Element 1768 Comment = self.etree.Comment 1769 root = Element('root') 1770 comment = Comment('TEXT ') 1771 comment.tail = "TAIL" 1772 1773 self.assertEqual(_bytes('<root></root>'), 1774 self._writeElement(root)) 1775 root.addprevious(comment) 1776 self.assertEqual(_bytes('<!--TEXT -->\n<root></root>'), 1777 self._writeElement(root))
1778 1779 # ET's Elements have items() and key(), but not values()
1780 - def test_attribute_values(self):
1781 XML = self.etree.XML 1782 1783 root = XML(_bytes('<doc alpha="Alpha" beta="Beta" gamma="Gamma"/>')) 1784 values = root.values() 1785 values.sort() 1786 self.assertEqual(['Alpha', 'Beta', 'Gamma'], values)
1787 1788 # gives error in ElementTree
1789 - def test_comment_empty(self):
1790 Element = self.etree.Element 1791 Comment = self.etree.Comment 1792 1793 a = Element('a') 1794 a.append(Comment()) 1795 self.assertEqual( 1796 _bytes('<a><!----></a>'), 1797 self._writeElement(a))
1798 1799 # ElementTree ignores comments
1800 - def test_comment_parse_empty(self):
1801 ElementTree = self.etree.ElementTree 1802 tostring = self.etree.tostring 1803 1804 xml = _bytes('<a><b/><!----><c/></a>') 1805 f = BytesIO(xml) 1806 doc = ElementTree(file=f) 1807 a = doc.getroot() 1808 self.assertEqual( 1809 '', 1810 a[1].text) 1811 self.assertEqual( 1812 xml, 1813 tostring(a))
1814 1815 # ElementTree ignores comments
1816 - def test_comment_no_proxy_yet(self):
1817 ElementTree = self.etree.ElementTree 1818 1819 f = BytesIO('<a><b></b><!-- hoi --><c></c></a>') 1820 doc = ElementTree(file=f) 1821 a = doc.getroot() 1822 self.assertEqual( 1823 ' hoi ', 1824 a[1].text)
1825 1826 # does not raise an exception in ElementTree
1827 - def test_comment_immutable(self):
1828 Element = self.etree.Element 1829 Comment = self.etree.Comment 1830 1831 c = Comment() 1832 el = Element('myel') 1833 1834 self.assertRaises(TypeError, c.append, el) 1835 self.assertRaises(TypeError, c.insert, 0, el) 1836 self.assertRaises(TypeError, c.set, "myattr", "test")
1837
1838 - def test_comment_immutable_attrib(self):
1839 c = self.etree.Comment() 1840 self.assertEqual(0, len(c.attrib)) 1841 1842 self.assertFalse(c.attrib.__contains__('nope')) <