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