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