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

Source Code for Module lxml.tests.test_xpathevaluator

  1  # -*- coding: utf-8 -*- 
  2   
  3  """ 
  4  Test cases related to XPath evaluation and the XPath class 
  5  """ 
  6   
  7  import unittest, sys, os.path 
  8   
  9  this_dir = os.path.dirname(__file__) 
 10  if this_dir not in sys.path: 
 11      sys.path.insert(0, this_dir) # needed for Py3 
 12   
 13  from common_imports import etree, HelperTestCase, _bytes, BytesIO 
 14  from common_imports import doctest, make_doctest 
 15   
16 -class ETreeXPathTestCase(HelperTestCase):
17 """XPath tests etree""" 18
19 - def test_xpath_boolean(self):
20 tree = self.parse('<a><b></b><b></b></a>') 21 self.assert_(tree.xpath('boolean(/a/b)')) 22 self.assert_(not tree.xpath('boolean(/a/c)'))
23
24 - def test_xpath_number(self):
25 tree = self.parse('<a>1</a>') 26 self.assertEquals(1., 27 tree.xpath('number(/a)')) 28 tree = self.parse('<a>A</a>') 29 actual = str(tree.xpath('number(/a)')) 30 expected = ['nan', '1.#qnan', 'nanq'] 31 if not actual.lower() in expected: 32 self.fail('Expected a NAN value, got %s' % actual)
33
34 - def test_xpath_string(self):
35 tree = self.parse('<a>Foo</a>') 36 self.assertEquals('Foo', 37 tree.xpath('string(/a/text())'))
38
39 - def test_xpath_document_root(self):
40 tree = self.parse('<a><b/></a>') 41 self.assertEquals([], 42 tree.xpath('/'))
43
44 - def test_xpath_namespace(self):
45 tree = self.parse('<a xmlns="test" xmlns:p="myURI"/>') 46 self.assert_((None, "test") in tree.xpath('namespace::*')) 47 self.assert_(('p', 'myURI') in tree.xpath('namespace::*'))
48
50 tree = self.parse('<a/>') 51 self.assertEquals([('xml', 'http://www.w3.org/XML/1998/namespace')], 52 tree.xpath('namespace::*'))
53
54 - def test_xpath_list_elements(self):
55 tree = self.parse('<a><b>Foo</b><b>Bar</b></a>') 56 root = tree.getroot() 57 self.assertEquals([root[0], root[1]], 58 tree.xpath('/a/b'))
59
60 - def test_xpath_list_nothing(self):
61 tree = self.parse('<a><b/></a>') 62 self.assertEquals([], 63 tree.xpath('/a/c')) 64 # this seems to pass a different code path, also should return nothing 65 self.assertEquals([], 66 tree.xpath('/a/c/text()'))
67
68 - def test_xpath_list_text(self):
69 tree = self.parse('<a><b>Foo</b><b>Bar</b></a>') 70 root = tree.getroot() 71 self.assertEquals(['Foo', 'Bar'], 72 tree.xpath('/a/b/text()'))
73
75 tree = self.parse('<a><b>FooBar</b><b>BarFoo</b></a>') 76 root = tree.getroot() 77 self.assertEquals(['FooBar', 'BarFoo'], 78 tree.xpath('/a/b/text()')) 79 self.assertEquals([root[0], root[1]], 80 [r.getparent() for r in tree.xpath('/a/b/text()')])
81
83 xml = _bytes('<a><b>FooBar\\u0680\\u3120</b><b>BarFoo\\u0680\\u3120</b></a>').decode("unicode_escape") 84 tree = self.parse(xml.encode('utf-8')) 85 root = tree.getroot() 86 self.assertEquals([_bytes('FooBar\\u0680\\u3120').decode("unicode_escape"), 87 _bytes('BarFoo\\u0680\\u3120').decode("unicode_escape")], 88 tree.xpath('/a/b/text()')) 89 self.assertEquals([root[0], root[1]], 90 [r.getparent() for r in tree.xpath('/a/b/text()')])
91
93 tree = self.parse('<a b="B" c="C"/>') 94 self.assertEquals(['B'], 95 tree.xpath('/a/@b'))
96
98 tree = self.parse('<a b="BaSdFgHjKl" c="CqWeRtZuI"/>') 99 results = tree.xpath('/a/@c') 100 self.assertEquals(1, len(results)) 101 self.assertEquals('CqWeRtZuI', results[0]) 102 self.assertEquals(tree.getroot().tag, results[0].getparent().tag)
103
104 - def test_xpath_list_comment(self):
105 tree = self.parse('<a><!-- Foo --></a>') 106 self.assertEquals(['<!-- Foo -->'], 107 list(map(repr, tree.xpath('/a/node()'))))
108
109 - def test_rel_xpath_boolean(self):
110 root = etree.XML('<a><b><c/></b></a>') 111 el = root[0] 112 self.assert_(el.xpath('boolean(c)')) 113 self.assert_(not el.xpath('boolean(d)'))
114
116 tree = self.parse('<a><c><b>Foo</b><b>Bar</b></c><c><b>Hey</b></c></a>') 117 root = tree.getroot() 118 c = root[0] 119 self.assertEquals([c[0], c[1]], 120 c.xpath('b')) 121 self.assertEquals([c[0], c[1], root[1][0]], 122 c.xpath('//b'))
123
124 - def test_xpath_ns(self):
125 tree = self.parse('<a xmlns="uri:a"><b></b></a>') 126 root = tree.getroot() 127 self.assertEquals( 128 [root[0]], 129 tree.xpath('//foo:b', namespaces={'foo': 'uri:a'})) 130 self.assertEquals( 131 [], 132 tree.xpath('//foo:b', namespaces={'foo': 'uri:c'})) 133 self.assertEquals( 134 [root[0]], 135 root.xpath('//baz:b', namespaces={'baz': 'uri:a'}))
136
137 - def test_xpath_ns_none(self):
138 tree = self.parse('<a xmlns="uri:a"><b></b></a>') 139 root = tree.getroot() 140 self.assertRaises( 141 TypeError, 142 root.xpath, '//b', namespaces={None: 'uri:a'})
143
144 - def test_xpath_ns_empty(self):
145 tree = self.parse('<a xmlns="uri:a"><b></b></a>') 146 root = tree.getroot() 147 self.assertRaises( 148 TypeError, 149 root.xpath, '//b', namespaces={'': 'uri:a'})
150
151 - def test_xpath_error(self):
152 tree = self.parse('<a/>') 153 self.assertRaises(etree.XPathEvalError, tree.xpath, '\\fad')
154
155 - def test_xpath_class_error(self):
156 self.assertRaises(SyntaxError, etree.XPath, '\\fad') 157 self.assertRaises(etree.XPathSyntaxError, etree.XPath, '\\fad')
158
159 - def test_xpath_prefix_error(self):
160 tree = self.parse('<a/>') 161 self.assertRaises(etree.XPathEvalError, tree.xpath, '/fa:d')
162
164 tree = self.parse('<a/>') 165 xpath = etree.XPath("/fa:d") 166 self.assertRaises(etree.XPathEvalError, xpath, tree)
167
168 - def test_elementtree_getpath(self):
169 a = etree.Element("a") 170 b = etree.SubElement(a, "b") 171 c = etree.SubElement(a, "c") 172 d1 = etree.SubElement(c, "d") 173 d2 = etree.SubElement(c, "d") 174 175 tree = etree.ElementTree(a) 176 self.assertEqual('/a/c/d', 177 tree.getpath(d2)[:6]) 178 self.assertEqual([d2], 179 tree.xpath(tree.getpath(d2)))
180
182 a = etree.Element("a") 183 b = etree.SubElement(a, "b") 184 c = etree.SubElement(a, "c") 185 d1 = etree.SubElement(c, "d") 186 d2 = etree.SubElement(c, "d") 187 188 tree = etree.ElementTree(c) 189 self.assertEqual('/c/d', 190 tree.getpath(d2)[:4]) 191 self.assertEqual([d2], 192 tree.xpath(tree.getpath(d2)))
193
194 - def test_xpath_evaluator(self):
195 tree = self.parse('<a><b><c></c></b></a>') 196 e = etree.XPathEvaluator(tree) 197 root = tree.getroot() 198 self.assertEquals( 199 [root], 200 e('//a'))
201
203 tree = self.parse('<a><b><c></c></b></a>') 204 child_tree = etree.ElementTree(tree.getroot()[0]) 205 e = etree.XPathEvaluator(child_tree) 206 self.assertEquals( 207 [], 208 e('a')) 209 root = child_tree.getroot() 210 self.assertEquals( 211 [root[0]], 212 e('c'))
213
215 tree = self.parse('<a><b><c></c></b></a>') 216 child_tree = etree.ElementTree(tree.getroot()[0]) 217 e = etree.XPathEvaluator(child_tree) 218 self.assertEquals( 219 [], 220 e('/a')) 221 root = child_tree.getroot() 222 self.assertEquals( 223 [root], 224 e('/b')) 225 self.assertEquals( 226 [], 227 e('/c'))
228
230 tree = self.parse('<a><b><c></c></b></a>') 231 root = tree.getroot() 232 e = etree.XPathEvaluator(root[0]) 233 self.assertEquals( 234 [root[0][0]], 235 e('c'))
236
237 - def test_xpath_extensions(self):
238 def foo(evaluator, a): 239 return 'hello %s' % a
240 extension = {(None, 'foo'): foo} 241 tree = self.parse('<a><b></b></a>') 242 e = etree.XPathEvaluator(tree, extensions=[extension]) 243 self.assertEquals( 244 "hello you", e("foo('you')"))
245
246 - def test_xpath_extensions_wrong_args(self):
247 def foo(evaluator, a, b): 248 return "hello %s and %s" % (a, b)
249 extension = {(None, 'foo'): foo} 250 tree = self.parse('<a><b></b></a>') 251 e = etree.XPathEvaluator(tree, extensions=[extension]) 252 self.assertRaises(TypeError, e, "foo('you')") 253
254 - def test_xpath_extensions_error(self):
255 def foo(evaluator, a): 256 return 1/0
257 extension = {(None, 'foo'): foo} 258 tree = self.parse('<a/>') 259 e = etree.XPathEvaluator(tree, extensions=[extension]) 260 self.assertRaises(ZeroDivisionError, e, "foo('test')") 261
262 - def test_xpath_extensions_nodes(self):
263 def f(evaluator, arg): 264 r = etree.Element('results') 265 b = etree.SubElement(r, 'result') 266 b.text = 'Hoi' 267 b = etree.SubElement(r, 'result') 268 b.text = 'Dag' 269 return r
270 271 x = self.parse('<a/>') 272 e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}]) 273 r = e("foo('World')/result") 274 self.assertEquals(2, len(r)) 275 self.assertEquals('Hoi', r[0].text) 276 self.assertEquals('Dag', r[1].text) 277
278 - def test_xpath_extensions_nodes_append(self):
279 def f(evaluator, nodes): 280 r = etree.SubElement(nodes[0], 'results') 281 b = etree.SubElement(r, 'result') 282 b.text = 'Hoi' 283 b = etree.SubElement(r, 'result') 284 b.text = 'Dag' 285 return r
286 287 x = self.parse('<a/>') 288 e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}]) 289 r = e("foo(/*)/result") 290 self.assertEquals(2, len(r)) 291 self.assertEquals('Hoi', r[0].text) 292 self.assertEquals('Dag', r[1].text) 293
294 - def test_xpath_extensions_nodes_append2(self):
295 def f(evaluator, nodes): 296 r = etree.Element('results') 297 b = etree.SubElement(r, 'result') 298 b.text = 'Hoi' 299 b = etree.SubElement(r, 'result') 300 b.text = 'Dag' 301 r.append(nodes[0]) 302 return r
303 304 x = self.parse('<result>Honk</result>') 305 e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}]) 306 r = e("foo(/*)/result") 307 self.assertEquals(3, len(r)) 308 self.assertEquals('Hoi', r[0].text) 309 self.assertEquals('Dag', r[1].text) 310 self.assertEquals('Honk', r[2].text) 311
312 - def test_xpath_context_node(self):
313 tree = self.parse('<root><a/><b><c/></b></root>') 314 315 check_call = [] 316 def check_context(ctxt, nodes): 317 self.assertEquals(len(nodes), 1) 318 check_call.append(nodes[0].tag) 319 self.assertEquals(ctxt.context_node, nodes[0]) 320 return True
321 322 find = etree.XPath("//*[p:foo(.)]", 323 namespaces={'p' : 'ns'}, 324 extensions=[{('ns', 'foo') : check_context}]) 325 find(tree) 326 327 check_call.sort() 328 self.assertEquals(check_call, ["a", "b", "c", "root"]) 329
330 - def test_xpath_eval_context_propagation(self):
331 tree = self.parse('<root><a/><b><c/></b></root>') 332 333 check_call = {} 334 def check_context(ctxt, nodes): 335 self.assertEquals(len(nodes), 1) 336 tag = nodes[0].tag 337 # empty during the "b" call, a "b" during the "c" call 338 check_call[tag] = ctxt.eval_context.get("b") 339 ctxt.eval_context[tag] = tag 340 return True
341 342 find = etree.XPath("//b[p:foo(.)]/c[p:foo(.)]", 343 namespaces={'p' : 'ns'}, 344 extensions=[{('ns', 'foo') : check_context}]) 345 result = find(tree) 346 347 self.assertEquals(result, [tree.getroot()[1][0]]) 348 self.assertEquals(check_call, {'b':None, 'c':'b'}) 349
350 - def test_xpath_eval_context_clear(self):
351 tree = self.parse('<root><a/><b><c/></b></root>') 352 353 check_call = {} 354 def check_context(ctxt): 355 check_call["done"] = True 356 # context must be empty for each new evaluation 357 self.assertEquals(len(ctxt.eval_context), 0) 358 ctxt.eval_context["test"] = True 359 return True
360 361 find = etree.XPath("//b[p:foo()]", 362 namespaces={'p' : 'ns'}, 363 extensions=[{('ns', 'foo') : check_context}]) 364 result = find(tree) 365 366 self.assertEquals(result, [tree.getroot()[1]]) 367 self.assertEquals(check_call["done"], True) 368 369 check_call.clear() 370 find = etree.XPath("//b[p:foo()]", 371 namespaces={'p' : 'ns'}, 372 extensions=[{('ns', 'foo') : check_context}]) 373 result = find(tree) 374 375 self.assertEquals(result, [tree.getroot()[1]]) 376 self.assertEquals(check_call["done"], True) 377
378 - def test_xpath_variables(self):
379 x = self.parse('<a attr="true"/>') 380 e = etree.XPathEvaluator(x) 381 382 expr = "/a[@attr=$aval]" 383 r = e(expr, aval=1) 384 self.assertEquals(0, len(r)) 385 386 r = e(expr, aval="true") 387 self.assertEquals(1, len(r)) 388 self.assertEquals("true", r[0].get('attr')) 389 390 r = e(expr, aval=True) 391 self.assertEquals(1, len(r)) 392 self.assertEquals("true", r[0].get('attr'))
393
394 - def test_xpath_variables_nodeset(self):
395 x = self.parse('<a attr="true"/>') 396 e = etree.XPathEvaluator(x) 397 398 element = etree.Element("test-el") 399 etree.SubElement(element, "test-sub") 400 expr = "$value" 401 r = e(expr, value=element) 402 self.assertEquals(1, len(r)) 403 self.assertEquals(element.tag, r[0].tag) 404 self.assertEquals(element[0].tag, r[0][0].tag)
405
406 - def test_xpath_extensions_mix(self):
407 x = self.parse('<a attr="true"><test/></a>') 408 409 class LocalException(Exception): 410 pass
411 412 def foo(evaluator, a, varval): 413 etree.Element("DUMMY") 414 if varval == 0: 415 raise LocalException 416 elif varval == 1: 417 return () 418 elif varval == 2: 419 return None 420 elif varval == 3: 421 return a[0][0] 422 a = a[0] 423 if a.get("attr") == str(varval): 424 return a 425 else: 426 return etree.Element("NODE") 427 428 extension = {(None, 'foo'): foo} 429 e = etree.XPathEvaluator(x, extensions=[extension]) 430 del x 431 432 self.assertRaises(LocalException, e, "foo(., 0)") 433 self.assertRaises(LocalException, e, "foo(., $value)", value=0) 434 435 r = e("foo(., $value)", value=1) 436 self.assertEqual(len(r), 0) 437 438 r = e("foo(., 1)") 439 self.assertEqual(len(r), 0) 440 441 r = e("foo(., $value)", value=2) 442 self.assertEqual(len(r), 0) 443 444 r = e("foo(., $value)", value=3) 445 self.assertEqual(len(r), 1) 446 self.assertEqual(r[0].tag, "test") 447 448 r = e("foo(., $value)", value="false") 449 self.assertEqual(len(r), 1) 450 self.assertEqual(r[0].tag, "NODE") 451 452 r = e("foo(., 'false')") 453 self.assertEqual(len(r), 1) 454 self.assertEqual(r[0].tag, "NODE") 455 456 r = e("foo(., 'true')") 457 self.assertEqual(len(r), 1) 458 self.assertEqual(r[0].tag, "a") 459 self.assertEqual(r[0][0].tag, "test") 460 461 r = e("foo(., $value)", value="true") 462 self.assertEqual(len(r), 1) 463 self.assertEqual(r[0].tag, "a") 464 465 self.assertRaises(LocalException, e, "foo(., 0)") 466 self.assertRaises(LocalException, e, "foo(., $value)", value=0) 467 468
469 -class ETreeXPathClassTestCase(HelperTestCase):
470 "Tests for the XPath class"
471 - def test_xpath_compile_doc(self):
472 x = self.parse('<a attr="true"/>') 473 474 expr = etree.XPath("/a[@attr != 'true']") 475 r = expr(x) 476 self.assertEquals(0, len(r)) 477 478 expr = etree.XPath("/a[@attr = 'true']") 479 r = expr(x) 480 self.assertEquals(1, len(r)) 481 482 expr = etree.XPath( expr.path ) 483 r = expr(x) 484 self.assertEquals(1, len(r))
485
487 x = self.parse('<a><b/><c/></a>') 488 root = x.getroot() 489 490 expr = etree.XPath("./b") 491 r = expr(root) 492 self.assertEquals(1, len(r)) 493 self.assertEquals('b', r[0].tag) 494 495 expr = etree.XPath("./*") 496 r = expr(root) 497 self.assertEquals(2, len(r))
498
499 - def test_xpath_compile_vars(self):
500 x = self.parse('<a attr="true"/>') 501 502 expr = etree.XPath("/a[@attr=$aval]") 503 r = expr(x, aval=False) 504 self.assertEquals(0, len(r)) 505 506 r = expr(x, aval=True) 507 self.assertEquals(1, len(r))
508
509 - def test_xpath_compile_error(self):
510 self.assertRaises(SyntaxError, etree.XPath, '\\fad')
511
513 self.assertRaises(ValueError, etree.XPath('*'), etree.ElementTree())
514
515 -class ETreeETXPathClassTestCase(HelperTestCase):
516 "Tests for the ETXPath class"
517 - def test_xpath_compile_ns(self):
518 x = self.parse('<a><b xmlns="nsa"/><b xmlns="nsb"/></a>') 519 520 expr = etree.ETXPath("/a/{nsa}b") 521 r = expr(x) 522 self.assertEquals(1, len(r)) 523 self.assertEquals('{nsa}b', r[0].tag) 524 525 expr = etree.ETXPath("/a/{nsb}b") 526 r = expr(x) 527 self.assertEquals(1, len(r)) 528 self.assertEquals('{nsb}b', r[0].tag)
529
531 x = self.parse(_bytes('<a><b xmlns="nsa\\uf8d2"/><b xmlns="nsb\\uf8d1"/></a>' 532 ).decode("unicode_escape")) 533 534 expr = etree.ETXPath(_bytes("/a/{nsa\\uf8d2}b").decode("unicode_escape")) 535 r = expr(x) 536 self.assertEquals(1, len(r)) 537 self.assertEquals(_bytes('{nsa\\uf8d2}b').decode("unicode_escape"), r[0].tag) 538 539 expr = etree.ETXPath(_bytes("/a/{nsb\\uf8d1}b").decode("unicode_escape")) 540 r = expr(x) 541 self.assertEquals(1, len(r)) 542 self.assertEquals(_bytes('{nsb\\uf8d1}b').decode("unicode_escape"), r[0].tag)
543 544 SAMPLE_XML = etree.parse(BytesIO(""" 545 <body> 546 <tag>text</tag> 547 <section> 548 <tag>subtext</tag> 549 </section> 550 <tag /> 551 <tag /> 552 </body> 553 """)) 554
555 -def tag(elem):
556 return elem.tag
557
558 -def stringTest(ctxt, s1):
559 return "Hello "+s1
560
561 -def floatTest(ctxt, f1):
562 return f1+4
563
564 -def booleanTest(ctxt, b1):
565 return not b1
566
567 -def setTest(ctxt, st1):
568 return st1[0]
569
570 -def setTest2(ctxt, st1):
571 return st1[0:2]
572
573 -def argsTest1(ctxt, s, f, b, st):
574 return ", ".join(map(str, (s, f, b, list(map(tag, st)))))
575
576 -def argsTest2(ctxt, st1, st2):
577 st1.extend(st2) 578 return st1
579
580 -def resultTypesTest(ctxt):
581 return ["x","y"]
582
583 -def resultTypesTest2(ctxt):
584 return resultTypesTest
585 586 uri = "http://www.example.com/" 587 588 extension = {(None, 'stringTest'): stringTest, 589 (None, 'floatTest'): floatTest, 590 (None, 'booleanTest'): booleanTest, 591 (None, 'setTest'): setTest, 592 (None, 'setTest2'): setTest2, 593 (None, 'argsTest1'): argsTest1, 594 (None, 'argsTest2'): argsTest2, 595 (None, 'resultTypesTest'): resultTypesTest, 596 (None, 'resultTypesTest2'): resultTypesTest2,} 597
598 -def xpath():
599 """ 600 Test xpath extension functions. 601 602 >>> root = SAMPLE_XML 603 >>> e = etree.XPathEvaluator(root, extensions=[extension]) 604 >>> e("stringTest('you')") 605 'Hello you' 606 >>> e(_bytes("stringTest('\\\\xe9lan')").decode("unicode_escape")) 607 u'Hello \\xe9lan' 608 >>> e("stringTest('you','there')") 609 Traceback (most recent call last): 610 ... 611 TypeError: stringTest() takes exactly 2 arguments (3 given) 612 >>> e("floatTest(2)") 613 6.0 614 >>> e("booleanTest(true())") 615 False 616 >>> list(map(tag, e("setTest(/body/tag)"))) 617 ['tag'] 618 >>> list(map(tag, e("setTest2(/body/*)"))) 619 ['tag', 'section'] 620 >>> e("argsTest1('a',1.5,true(),/body/tag)") 621 "a, 1.5, True, ['tag', 'tag', 'tag']" 622 >>> list(map(tag, e("argsTest2(/body/tag, /body/section)"))) 623 ['tag', 'section', 'tag', 'tag'] 624 >>> e("resultTypesTest()") 625 Traceback (most recent call last): 626 ... 627 XPathResultError: This is not a node: 'x' 628 >>> try: 629 ... e("resultTypesTest2()") 630 ... except etree.XPathResultError: 631 ... print("Got error") 632 Got error 633 """
634 635 if sys.version_info[0] >= 3: 636 xpath.__doc__ = xpath.__doc__.replace(" u'", " '") 637 xpath.__doc__ = xpath.__doc__.replace(" XPathResultError", 638 " lxml.etree.XPathResultError") 639 xpath.__doc__ = xpath.__doc__.replace(" exactly 2 arguments", 640 " exactly 2 positional arguments") 641
642 -def test_suite():
643 suite = unittest.TestSuite() 644 suite.addTests([unittest.makeSuite(ETreeXPathTestCase)]) 645 suite.addTests([unittest.makeSuite(ETreeXPathClassTestCase)]) 646 suite.addTests([unittest.makeSuite(ETreeETXPathClassTestCase)]) 647 suite.addTests([doctest.DocTestSuite()]) 648 suite.addTests( 649 [make_doctest('../../../doc/xpathxslt.txt')]) 650 return suite
651 652 if __name__ == '__main__': 653 print('to test use test.py %s' % __file__) 654