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 tree = self.parse('<a><b>FooBar</b><b>BarFoo</b></a>')
84 root = tree.getroot()
85 self.assertEquals(['FooBar', 'BarFoo'],
86 tree.xpath('/a/b/text()', smart_strings=True))
87 self.assertEquals([root[0], root[1]],
88 [r.getparent() for r in
89 tree.xpath('/a/b/text()', smart_strings=True)])
90
91 self.assertEquals(['FooBar', 'BarFoo'],
92 tree.xpath('/a/b/text()', smart_strings=False))
93 self.assertEquals([False, False],
94 [hasattr(r, 'getparent') for r in
95 tree.xpath('/a/b/text()', smart_strings=False)])
96
98 xml = _bytes('<a><b>FooBar\\u0680\\u3120</b><b>BarFoo\\u0680\\u3120</b></a>').decode("unicode_escape")
99 tree = self.parse(xml.encode('utf-8'))
100 root = tree.getroot()
101 self.assertEquals([_bytes('FooBar\\u0680\\u3120').decode("unicode_escape"),
102 _bytes('BarFoo\\u0680\\u3120').decode("unicode_escape")],
103 tree.xpath('/a/b/text()'))
104 self.assertEquals([root[0], root[1]],
105 [r.getparent() for r in tree.xpath('/a/b/text()')])
106
108 tree = self.parse('<a b="B" c="C"/>')
109 self.assertEquals(['B'],
110 tree.xpath('/a/@b'))
111
113 tree = self.parse('<a b="BaSdFgHjKl" c="CqWeRtZuI"/>')
114 results = tree.xpath('/a/@c')
115 self.assertEquals(1, len(results))
116 self.assertEquals('CqWeRtZuI', results[0])
117 self.assertEquals(tree.getroot().tag, results[0].getparent().tag)
118
120 tree = self.parse('<a b="BaSdFgHjKl" c="CqWeRtZuI"/>')
121
122 results = tree.xpath('/a/@c', smart_strings=True)
123 self.assertEquals(1, len(results))
124 self.assertEquals('CqWeRtZuI', results[0])
125 self.assertEquals(tree.getroot().tag, results[0].getparent().tag)
126
127 results = tree.xpath('/a/@c', smart_strings=False)
128 self.assertEquals(1, len(results))
129 self.assertEquals('CqWeRtZuI', results[0])
130 self.assertEquals(False, hasattr(results[0], 'getparent'))
131
136
142
144 tree = self.parse('<a><c><b>Foo</b><b>Bar</b></c><c><b>Hey</b></c></a>')
145 root = tree.getroot()
146 c = root[0]
147 self.assertEquals([c[0], c[1]],
148 c.xpath('b'))
149 self.assertEquals([c[0], c[1], root[1][0]],
150 c.xpath('//b'))
151
153 tree = self.parse('<a xmlns="uri:a"><b></b></a>')
154 root = tree.getroot()
155 self.assertEquals(
156 [root[0]],
157 tree.xpath('//foo:b', namespaces={'foo': 'uri:a'}))
158 self.assertEquals(
159 [],
160 tree.xpath('//foo:b', namespaces={'foo': 'uri:c'}))
161 self.assertEquals(
162 [root[0]],
163 root.xpath('//baz:b', namespaces={'baz': 'uri:a'}))
164
166 tree = self.parse('<a xmlns="uri:a"><b></b></a>')
167 root = tree.getroot()
168 self.assertRaises(
169 TypeError,
170 root.xpath, '//b', namespaces={None: 'uri:a'})
171
173 tree = self.parse('<a xmlns="uri:a"><b></b></a>')
174 root = tree.getroot()
175 self.assertRaises(
176 TypeError,
177 root.xpath, '//b', namespaces={'': 'uri:a'})
178
182
186
190
195
208
221
229
241
256
264
266 def foo(evaluator, a):
267 return 'hello %s' % a
268 extension = {(None, 'foo'): foo}
269 tree = self.parse('<a><b></b></a>')
270 e = etree.XPathEvaluator(tree, extensions=[extension])
271 self.assertEquals(
272 "hello you", e("foo('you')"))
273
275 def foo(evaluator, a, b):
276 return "hello %s and %s" % (a, b)
277 extension = {(None, 'foo'): foo}
278 tree = self.parse('<a><b></b></a>')
279 e = etree.XPathEvaluator(tree, extensions=[extension])
280 self.assertRaises(TypeError, e, "foo('you')")
281
283 def foo(evaluator, a):
284 return 1/0
285 extension = {(None, 'foo'): foo}
286 tree = self.parse('<a/>')
287 e = etree.XPathEvaluator(tree, extensions=[extension])
288 self.assertRaises(ZeroDivisionError, e, "foo('test')")
289
298
299 x = self.parse('<a/>')
300 e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}])
301 r = e("foo('World')/result")
302 self.assertEquals(2, len(r))
303 self.assertEquals('Hoi', r[0].text)
304 self.assertEquals('Dag', r[1].text)
305
314
315 x = self.parse('<a/>')
316 e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}])
317 r = e("foo(/*)/result")
318 self.assertEquals(2, len(r))
319 self.assertEquals('Hoi', r[0].text)
320 self.assertEquals('Dag', r[1].text)
321
331
332 x = self.parse('<result>Honk</result>')
333 e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}])
334 r = e("foo(/*)/result")
335 self.assertEquals(3, len(r))
336 self.assertEquals('Hoi', r[0].text)
337 self.assertEquals('Dag', r[1].text)
338 self.assertEquals('Honk', r[2].text)
339
341 tree = self.parse('<root><a/><b><c/></b></root>')
342
343 check_call = []
344 def check_context(ctxt, nodes):
345 self.assertEquals(len(nodes), 1)
346 check_call.append(nodes[0].tag)
347 self.assertEquals(ctxt.context_node, nodes[0])
348 return True
349
350 find = etree.XPath("//*[p:foo(.)]",
351 namespaces={'p' : 'ns'},
352 extensions=[{('ns', 'foo') : check_context}])
353 find(tree)
354
355 check_call.sort()
356 self.assertEquals(check_call, ["a", "b", "c", "root"])
357
359 tree = self.parse('<root><a/><b><c/></b></root>')
360
361 check_call = {}
362 def check_context(ctxt, nodes):
363 self.assertEquals(len(nodes), 1)
364 tag = nodes[0].tag
365
366 check_call[tag] = ctxt.eval_context.get("b")
367 ctxt.eval_context[tag] = tag
368 return True
369
370 find = etree.XPath("//b[p:foo(.)]/c[p:foo(.)]",
371 namespaces={'p' : 'ns'},
372 extensions=[{('ns', 'foo') : check_context}])
373 result = find(tree)
374
375 self.assertEquals(result, [tree.getroot()[1][0]])
376 self.assertEquals(check_call, {'b':None, 'c':'b'})
377
379 tree = self.parse('<root><a/><b><c/></b></root>')
380
381 check_call = {}
382 def check_context(ctxt):
383 check_call["done"] = True
384
385 self.assertEquals(len(ctxt.eval_context), 0)
386 ctxt.eval_context["test"] = True
387 return True
388
389 find = etree.XPath("//b[p:foo()]",
390 namespaces={'p' : 'ns'},
391 extensions=[{('ns', 'foo') : check_context}])
392 result = find(tree)
393
394 self.assertEquals(result, [tree.getroot()[1]])
395 self.assertEquals(check_call["done"], True)
396
397 check_call.clear()
398 find = etree.XPath("//b[p:foo()]",
399 namespaces={'p' : 'ns'},
400 extensions=[{('ns', 'foo') : check_context}])
401 result = find(tree)
402
403 self.assertEquals(result, [tree.getroot()[1]])
404 self.assertEquals(check_call["done"], True)
405
407 x = self.parse('<a attr="true"/>')
408 e = etree.XPathEvaluator(x)
409
410 expr = "/a[@attr=$aval]"
411 r = e(expr, aval=1)
412 self.assertEquals(0, len(r))
413
414 r = e(expr, aval="true")
415 self.assertEquals(1, len(r))
416 self.assertEquals("true", r[0].get('attr'))
417
418 r = e(expr, aval=True)
419 self.assertEquals(1, len(r))
420 self.assertEquals("true", r[0].get('attr'))
421
433
435 x = self.parse('<a attr="true"><test/></a>')
436
437 class LocalException(Exception):
438 pass
439
440 def foo(evaluator, a, varval):
441 etree.Element("DUMMY")
442 if varval == 0:
443 raise LocalException
444 elif varval == 1:
445 return ()
446 elif varval == 2:
447 return None
448 elif varval == 3:
449 return a[0][0]
450 a = a[0]
451 if a.get("attr") == str(varval):
452 return a
453 else:
454 return etree.Element("NODE")
455
456 extension = {(None, 'foo'): foo}
457 e = etree.XPathEvaluator(x, extensions=[extension])
458 del x
459
460 self.assertRaises(LocalException, e, "foo(., 0)")
461 self.assertRaises(LocalException, e, "foo(., $value)", value=0)
462
463 r = e("foo(., $value)", value=1)
464 self.assertEqual(len(r), 0)
465
466 r = e("foo(., 1)")
467 self.assertEqual(len(r), 0)
468
469 r = e("foo(., $value)", value=2)
470 self.assertEqual(len(r), 0)
471
472 r = e("foo(., $value)", value=3)
473 self.assertEqual(len(r), 1)
474 self.assertEqual(r[0].tag, "test")
475
476 r = e("foo(., $value)", value="false")
477 self.assertEqual(len(r), 1)
478 self.assertEqual(r[0].tag, "NODE")
479
480 r = e("foo(., 'false')")
481 self.assertEqual(len(r), 1)
482 self.assertEqual(r[0].tag, "NODE")
483
484 r = e("foo(., 'true')")
485 self.assertEqual(len(r), 1)
486 self.assertEqual(r[0].tag, "a")
487 self.assertEqual(r[0][0].tag, "test")
488
489 r = e("foo(., $value)", value="true")
490 self.assertEqual(len(r), 1)
491 self.assertEqual(r[0].tag, "a")
492
493 self.assertRaises(LocalException, e, "foo(., 0)")
494 self.assertRaises(LocalException, e, "foo(., $value)", value=0)
495
496
498 "Tests for the XPath class"
500 x = self.parse('<a attr="true"/>')
501
502 expr = etree.XPath("/a[@attr != 'true']")
503 r = expr(x)
504 self.assertEquals(0, len(r))
505
506 expr = etree.XPath("/a[@attr = 'true']")
507 r = expr(x)
508 self.assertEquals(1, len(r))
509
510 expr = etree.XPath( expr.path )
511 r = expr(x)
512 self.assertEquals(1, len(r))
513
515 x = self.parse('<a><b/><c/></a>')
516 root = x.getroot()
517
518 expr = etree.XPath("./b")
519 r = expr(root)
520 self.assertEquals(1, len(r))
521 self.assertEquals('b', r[0].tag)
522
523 expr = etree.XPath("./*")
524 r = expr(root)
525 self.assertEquals(2, len(r))
526
528 x = self.parse('<a attr="true"/>')
529
530 expr = etree.XPath("/a[@attr=$aval]")
531 r = expr(x, aval=False)
532 self.assertEquals(0, len(r))
533
534 r = expr(x, aval=True)
535 self.assertEquals(1, len(r))
536
538 self.assertRaises(SyntaxError, etree.XPath, '\\fad')
539
542
544 "Tests for the ETXPath class"
546 x = self.parse('<a><b xmlns="nsa"/><b xmlns="nsb"/></a>')
547
548 expr = etree.ETXPath("/a/{nsa}b")
549 r = expr(x)
550 self.assertEquals(1, len(r))
551 self.assertEquals('{nsa}b', r[0].tag)
552
553 expr = etree.ETXPath("/a/{nsb}b")
554 r = expr(x)
555 self.assertEquals(1, len(r))
556 self.assertEquals('{nsb}b', r[0].tag)
557
559 x = self.parse(_bytes('<a><b xmlns="nsa\\uf8d2"/><b xmlns="nsb\\uf8d1"/></a>'
560 ).decode("unicode_escape"))
561
562 expr = etree.ETXPath(_bytes("/a/{nsa\\uf8d2}b").decode("unicode_escape"))
563 r = expr(x)
564 self.assertEquals(1, len(r))
565 self.assertEquals(_bytes('{nsa\\uf8d2}b').decode("unicode_escape"), r[0].tag)
566
567 expr = etree.ETXPath(_bytes("/a/{nsb\\uf8d1}b").decode("unicode_escape"))
568 r = expr(x)
569 self.assertEquals(1, len(r))
570 self.assertEquals(_bytes('{nsb\\uf8d1}b').decode("unicode_escape"), r[0].tag)
571
572 SAMPLE_XML = etree.parse(BytesIO("""
573 <body>
574 <tag>text</tag>
575 <section>
576 <tag>subtext</tag>
577 </section>
578 <tag />
579 <tag />
580 </body>
581 """))
582
585
588
591
594
597
600
602 return ", ".join(map(str, (s, f, b, list(map(tag, st)))))
603
605 st1.extend(st2)
606 return st1
607
610
613
614 uri = "http://www.example.com/"
615
616 extension = {(None, 'stringTest'): stringTest,
617 (None, 'floatTest'): floatTest,
618 (None, 'booleanTest'): booleanTest,
619 (None, 'setTest'): setTest,
620 (None, 'setTest2'): setTest2,
621 (None, 'argsTest1'): argsTest1,
622 (None, 'argsTest2'): argsTest2,
623 (None, 'resultTypesTest'): resultTypesTest,
624 (None, 'resultTypesTest2'): resultTypesTest2,}
625
627 """
628 Test xpath extension functions.
629
630 >>> root = SAMPLE_XML
631 >>> e = etree.XPathEvaluator(root, extensions=[extension])
632 >>> e("stringTest('you')")
633 'Hello you'
634 >>> e(_bytes("stringTest('\\\\xe9lan')").decode("unicode_escape"))
635 u'Hello \\xe9lan'
636 >>> e("stringTest('you','there')")
637 Traceback (most recent call last):
638 ...
639 TypeError: stringTest() takes exactly 2 arguments (3 given)
640 >>> e("floatTest(2)")
641 6.0
642 >>> e("booleanTest(true())")
643 False
644 >>> list(map(tag, e("setTest(/body/tag)")))
645 ['tag']
646 >>> list(map(tag, e("setTest2(/body/*)")))
647 ['tag', 'section']
648 >>> e("argsTest1('a',1.5,true(),/body/tag)")
649 "a, 1.5, True, ['tag', 'tag', 'tag']"
650 >>> list(map(tag, e("argsTest2(/body/tag, /body/section)")))
651 ['tag', 'section', 'tag', 'tag']
652 >>> e("resultTypesTest()")
653 Traceback (most recent call last):
654 ...
655 XPathResultError: This is not a node: 'x'
656 >>> try:
657 ... e("resultTypesTest2()")
658 ... except etree.XPathResultError:
659 ... print("Got error")
660 Got error
661 """
662
663 if sys.version_info[0] >= 3:
664 xpath.__doc__ = xpath.__doc__.replace(" u'", " '")
665 xpath.__doc__ = xpath.__doc__.replace(" XPathResultError",
666 " lxml.etree.XPathResultError")
667 xpath.__doc__ = xpath.__doc__.replace(" exactly 2 arguments",
668 " exactly 2 positional arguments")
669
679
680 if __name__ == '__main__':
681 print('to test use test.py %s' % __file__)
682