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
558
559
561 x = self.parse(_bytes('<a><b xmlns="http://nsa/\\uf8d2"/><b xmlns="http://nsb/\\uf8d1"/></a>'
562 ).decode("unicode_escape"))
563
564 expr = etree.ETXPath(_bytes("/a/{http://nsa/\\uf8d2}b").decode("unicode_escape"))
565 r = expr(x)
566 self.assertEquals(1, len(r))
567 self.assertEquals(_bytes('{http://nsa/\\uf8d2}b').decode("unicode_escape"), r[0].tag)
568
569 expr = etree.ETXPath(_bytes("/a/{http://nsb/\\uf8d1}b").decode("unicode_escape"))
570 r = expr(x)
571 self.assertEquals(1, len(r))
572 self.assertEquals(_bytes('{http://nsb/\\uf8d1}b').decode("unicode_escape"), r[0].tag)
573
574 SAMPLE_XML = etree.parse(BytesIO("""
575 <body>
576 <tag>text</tag>
577 <section>
578 <tag>subtext</tag>
579 </section>
580 <tag />
581 <tag />
582 </body>
583 """))
584
587
590
593
596
599
602
604 return ", ".join(map(str, (s, f, b, list(map(tag, st)))))
605
607 st1.extend(st2)
608 return st1
609
612
615
616 uri = "http://www.example.com/"
617
618 extension = {(None, 'stringTest'): stringTest,
619 (None, 'floatTest'): floatTest,
620 (None, 'booleanTest'): booleanTest,
621 (None, 'setTest'): setTest,
622 (None, 'setTest2'): setTest2,
623 (None, 'argsTest1'): argsTest1,
624 (None, 'argsTest2'): argsTest2,
625 (None, 'resultTypesTest'): resultTypesTest,
626 (None, 'resultTypesTest2'): resultTypesTest2,}
627
629 """
630 Test xpath extension functions.
631
632 >>> root = SAMPLE_XML
633 >>> e = etree.XPathEvaluator(root, extensions=[extension])
634 >>> e("stringTest('you')")
635 'Hello you'
636 >>> e(_bytes("stringTest('\\\\xe9lan')").decode("unicode_escape"))
637 u'Hello \\xe9lan'
638 >>> e("stringTest('you','there')")
639 Traceback (most recent call last):
640 ...
641 TypeError: stringTest() takes exactly 2 arguments (3 given)
642 >>> e("floatTest(2)")
643 6.0
644 >>> e("booleanTest(true())")
645 False
646 >>> list(map(tag, e("setTest(/body/tag)")))
647 ['tag']
648 >>> list(map(tag, e("setTest2(/body/*)")))
649 ['tag', 'section']
650 >>> e("argsTest1('a',1.5,true(),/body/tag)")
651 "a, 1.5, True, ['tag', 'tag', 'tag']"
652 >>> list(map(tag, e("argsTest2(/body/tag, /body/section)")))
653 ['tag', 'section', 'tag', 'tag']
654 >>> e("resultTypesTest()")
655 Traceback (most recent call last):
656 ...
657 XPathResultError: This is not a node: 'x'
658 >>> try:
659 ... e("resultTypesTest2()")
660 ... except etree.XPathResultError:
661 ... print("Got error")
662 Got error
663 """
664
665 if sys.version_info[0] >= 3:
666 xpath.__doc__ = xpath.__doc__.replace(" u'", " '")
667 xpath.__doc__ = xpath.__doc__.replace(" XPathResultError",
668 " lxml.etree.XPathResultError")
669 xpath.__doc__ = xpath.__doc__.replace(" exactly 2 arguments",
670 " exactly 2 positional arguments")
671
681
682 if __name__ == '__main__':
683 print('to test use test.py %s' % __file__)
684