1
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)
12
13 from common_imports import etree, HelperTestCase, _bytes, BytesIO
14 from common_imports import doctest, make_doctest
15
17 """XPath tests etree"""
18
23
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
35 tree = self.parse('<a>Foo</a>')
36 self.assertEquals('Foo',
37 tree.xpath('string(/a/text())'))
38
40 tree = self.parse('<a><b/></a>')
41 self.assertEquals([],
42 tree.xpath('/'))
43
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
59
61 tree = self.parse('<a><b/></a>')
62 self.assertEquals([],
63 tree.xpath('/a/c'))
64
65 self.assertEquals([],
66 tree.xpath('/a/c/text()'))
67
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
108
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
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
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
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
154
158
162
167
180
193
201
213
228
236
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
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
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
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
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
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
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
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
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
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
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
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
405
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
470 "Tests for the XPath class"
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
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
510 self.assertRaises(SyntaxError, etree.XPath, '\\fad')
511
514
516 "Tests for the ETXPath class"
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
557
560
563
566
569
572
574 return ", ".join(map(str, (s, f, b, list(map(tag, st)))))
575
577 st1.extend(st2)
578 return st1
579
582
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
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
651
652 if __name__ == '__main__':
653 print('to test use test.py %s' % __file__)
654