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