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
133 xml_data = '''
134 <table>
135 <item xml:id="k1"><value>v1</value></item>
136 <item xml:id="k2"><value>v2</value></item>
137 </table>
138 '''
139
140 def lookup(dummy, id):
141 return etree.XML(xml_data).xpath('id(%r)' % id)
142 functions = {(None, 'lookup') : lookup}
143
144 root = etree.XML('<dummy/>')
145 values = root.xpath("lookup('k1')/value/text()",
146 extensions=functions)
147 self.assertEquals(['v1'], values)
148 self.assertEquals('value', values[0].getparent().tag)
149
154
160
162 tree = self.parse('<a><c><b>Foo</b><b>Bar</b></c><c><b>Hey</b></c></a>')
163 root = tree.getroot()
164 c = root[0]
165 self.assertEquals([c[0], c[1]],
166 c.xpath('b'))
167 self.assertEquals([c[0], c[1], root[1][0]],
168 c.xpath('//b'))
169
171 tree = self.parse('<a xmlns="uri:a"><b></b></a>')
172 root = tree.getroot()
173 self.assertEquals(
174 [root[0]],
175 tree.xpath('//foo:b', namespaces={'foo': 'uri:a'}))
176 self.assertEquals(
177 [],
178 tree.xpath('//foo:b', namespaces={'foo': 'uri:c'}))
179 self.assertEquals(
180 [root[0]],
181 root.xpath('//baz:b', namespaces={'baz': 'uri:a'}))
182
184 tree = self.parse('<a xmlns="uri:a"><b></b></a>')
185 root = tree.getroot()
186 self.assertRaises(
187 TypeError,
188 root.xpath, '//b', namespaces={None: 'uri:a'})
189
191 tree = self.parse('<a xmlns="uri:a"><b></b></a>')
192 root = tree.getroot()
193 self.assertRaises(
194 TypeError,
195 root.xpath, '//b', namespaces={'': 'uri:a'})
196
200
204
208
213
226
239
247
259
274
282
284 def foo(evaluator, a):
285 return 'hello %s' % a
286 extension = {(None, 'foo'): foo}
287 tree = self.parse('<a><b></b></a>')
288 e = etree.XPathEvaluator(tree, extensions=[extension])
289 self.assertEquals(
290 "hello you", e("foo('you')"))
291
293 def foo(evaluator, a, b):
294 return "hello %s and %s" % (a, b)
295 extension = {(None, 'foo'): foo}
296 tree = self.parse('<a><b></b></a>')
297 e = etree.XPathEvaluator(tree, extensions=[extension])
298 self.assertRaises(TypeError, e, "foo('you')")
299
301 def foo(evaluator, a):
302 return 1/0
303 extension = {(None, 'foo'): foo}
304 tree = self.parse('<a/>')
305 e = etree.XPathEvaluator(tree, extensions=[extension])
306 self.assertRaises(ZeroDivisionError, e, "foo('test')")
307
316
317 x = self.parse('<a/>')
318 e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}])
319 r = e("foo('World')/result")
320 self.assertEquals(2, len(r))
321 self.assertEquals('Hoi', r[0].text)
322 self.assertEquals('Dag', r[1].text)
323
332
333 x = self.parse('<a/>')
334 e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}])
335 r = e("foo(/*)/result")
336 self.assertEquals(2, len(r))
337 self.assertEquals('Hoi', r[0].text)
338 self.assertEquals('Dag', r[1].text)
339
349
350 x = self.parse('<result>Honk</result>')
351 e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}])
352 r = e("foo(/*)/result")
353 self.assertEquals(3, len(r))
354 self.assertEquals('Hoi', r[0].text)
355 self.assertEquals('Dag', r[1].text)
356 self.assertEquals('Honk', r[2].text)
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 check_call.append(nodes[0].tag)
365 self.assertEquals(ctxt.context_node, nodes[0])
366 return True
367
368 find = etree.XPath("//*[p:foo(.)]",
369 namespaces={'p' : 'ns'},
370 extensions=[{('ns', 'foo') : check_context}])
371 find(tree)
372
373 check_call.sort()
374 self.assertEquals(check_call, ["a", "b", "c", "root"])
375
377 tree = self.parse('<root><a/><b><c/></b></root>')
378
379 check_call = {}
380 def check_context(ctxt, nodes):
381 self.assertEquals(len(nodes), 1)
382 tag = nodes[0].tag
383
384 check_call[tag] = ctxt.eval_context.get("b")
385 ctxt.eval_context[tag] = tag
386 return True
387
388 find = etree.XPath("//b[p:foo(.)]/c[p:foo(.)]",
389 namespaces={'p' : 'ns'},
390 extensions=[{('ns', 'foo') : check_context}])
391 result = find(tree)
392
393 self.assertEquals(result, [tree.getroot()[1][0]])
394 self.assertEquals(check_call, {'b':None, 'c':'b'})
395
397 tree = self.parse('<root><a/><b><c/></b></root>')
398
399 check_call = {}
400 def check_context(ctxt):
401 check_call["done"] = True
402
403 self.assertEquals(len(ctxt.eval_context), 0)
404 ctxt.eval_context["test"] = True
405 return True
406
407 find = etree.XPath("//b[p:foo()]",
408 namespaces={'p' : 'ns'},
409 extensions=[{('ns', 'foo') : check_context}])
410 result = find(tree)
411
412 self.assertEquals(result, [tree.getroot()[1]])
413 self.assertEquals(check_call["done"], True)
414
415 check_call.clear()
416 find = etree.XPath("//b[p:foo()]",
417 namespaces={'p' : 'ns'},
418 extensions=[{('ns', 'foo') : check_context}])
419 result = find(tree)
420
421 self.assertEquals(result, [tree.getroot()[1]])
422 self.assertEquals(check_call["done"], True)
423
425 x = self.parse('<a attr="true"/>')
426 e = etree.XPathEvaluator(x)
427
428 expr = "/a[@attr=$aval]"
429 r = e(expr, aval=1)
430 self.assertEquals(0, len(r))
431
432 r = e(expr, aval="true")
433 self.assertEquals(1, len(r))
434 self.assertEquals("true", r[0].get('attr'))
435
436 r = e(expr, aval=True)
437 self.assertEquals(1, len(r))
438 self.assertEquals("true", r[0].get('attr'))
439
451
453 x = self.parse('<a attr="true"><test/></a>')
454
455 class LocalException(Exception):
456 pass
457
458 def foo(evaluator, a, varval):
459 etree.Element("DUMMY")
460 if varval == 0:
461 raise LocalException
462 elif varval == 1:
463 return ()
464 elif varval == 2:
465 return None
466 elif varval == 3:
467 return a[0][0]
468 a = a[0]
469 if a.get("attr") == str(varval):
470 return a
471 else:
472 return etree.Element("NODE")
473
474 extension = {(None, 'foo'): foo}
475 e = etree.XPathEvaluator(x, extensions=[extension])
476 del x
477
478 self.assertRaises(LocalException, e, "foo(., 0)")
479 self.assertRaises(LocalException, e, "foo(., $value)", value=0)
480
481 r = e("foo(., $value)", value=1)
482 self.assertEqual(len(r), 0)
483
484 r = e("foo(., 1)")
485 self.assertEqual(len(r), 0)
486
487 r = e("foo(., $value)", value=2)
488 self.assertEqual(len(r), 0)
489
490 r = e("foo(., $value)", value=3)
491 self.assertEqual(len(r), 1)
492 self.assertEqual(r[0].tag, "test")
493
494 r = e("foo(., $value)", value="false")
495 self.assertEqual(len(r), 1)
496 self.assertEqual(r[0].tag, "NODE")
497
498 r = e("foo(., 'false')")
499 self.assertEqual(len(r), 1)
500 self.assertEqual(r[0].tag, "NODE")
501
502 r = e("foo(., 'true')")
503 self.assertEqual(len(r), 1)
504 self.assertEqual(r[0].tag, "a")
505 self.assertEqual(r[0][0].tag, "test")
506
507 r = e("foo(., $value)", value="true")
508 self.assertEqual(len(r), 1)
509 self.assertEqual(r[0].tag, "a")
510
511 self.assertRaises(LocalException, e, "foo(., 0)")
512 self.assertRaises(LocalException, e, "foo(., $value)", value=0)
513
514
516 "Tests for the XPath class"
518 x = self.parse('<a attr="true"/>')
519
520 expr = etree.XPath("/a[@attr != 'true']")
521 r = expr(x)
522 self.assertEquals(0, len(r))
523
524 expr = etree.XPath("/a[@attr = 'true']")
525 r = expr(x)
526 self.assertEquals(1, len(r))
527
528 expr = etree.XPath( expr.path )
529 r = expr(x)
530 self.assertEquals(1, len(r))
531
533 x = self.parse('<a><b/><c/></a>')
534 root = x.getroot()
535
536 expr = etree.XPath("./b")
537 r = expr(root)
538 self.assertEquals(1, len(r))
539 self.assertEquals('b', r[0].tag)
540
541 expr = etree.XPath("./*")
542 r = expr(root)
543 self.assertEquals(2, len(r))
544
546 x = self.parse('<a attr="true"/>')
547
548 expr = etree.XPath("/a[@attr=$aval]")
549 r = expr(x, aval=False)
550 self.assertEquals(0, len(r))
551
552 r = expr(x, aval=True)
553 self.assertEquals(1, len(r))
554
556 self.assertRaises(SyntaxError, etree.XPath, '\\fad')
557
560
562 "Tests for the ETXPath class"
564 x = self.parse('<a><b xmlns="nsa"/><b xmlns="nsb"/></a>')
565
566 expr = etree.ETXPath("/a/{nsa}b")
567 r = expr(x)
568 self.assertEquals(1, len(r))
569 self.assertEquals('{nsa}b', r[0].tag)
570
571 expr = etree.ETXPath("/a/{nsb}b")
572 r = expr(x)
573 self.assertEquals(1, len(r))
574 self.assertEquals('{nsb}b', r[0].tag)
575
576
577
579 x = self.parse(_bytes('<a><b xmlns="http://nsa/\\uf8d2"/><b xmlns="http://nsb/\\uf8d1"/></a>'
580 ).decode("unicode_escape"))
581
582 expr = etree.ETXPath(_bytes("/a/{http://nsa/\\uf8d2}b").decode("unicode_escape"))
583 r = expr(x)
584 self.assertEquals(1, len(r))
585 self.assertEquals(_bytes('{http://nsa/\\uf8d2}b').decode("unicode_escape"), r[0].tag)
586
587 expr = etree.ETXPath(_bytes("/a/{http://nsb/\\uf8d1}b").decode("unicode_escape"))
588 r = expr(x)
589 self.assertEquals(1, len(r))
590 self.assertEquals(_bytes('{http://nsb/\\uf8d1}b').decode("unicode_escape"), r[0].tag)
591
592 SAMPLE_XML = etree.parse(BytesIO("""
593 <body>
594 <tag>text</tag>
595 <section>
596 <tag>subtext</tag>
597 </section>
598 <tag />
599 <tag />
600 </body>
601 """))
602
605
608
611
614
617
620
622 return ", ".join(map(str, (s, f, b, list(map(tag, st)))))
623
625 st1.extend(st2)
626 return st1
627
630
633
634 uri = "http://www.example.com/"
635
636 extension = {(None, 'stringTest'): stringTest,
637 (None, 'floatTest'): floatTest,
638 (None, 'booleanTest'): booleanTest,
639 (None, 'setTest'): setTest,
640 (None, 'setTest2'): setTest2,
641 (None, 'argsTest1'): argsTest1,
642 (None, 'argsTest2'): argsTest2,
643 (None, 'resultTypesTest'): resultTypesTest,
644 (None, 'resultTypesTest2'): resultTypesTest2,}
645
647 """
648 Test xpath extension functions.
649
650 >>> root = SAMPLE_XML
651 >>> e = etree.XPathEvaluator(root, extensions=[extension])
652 >>> e("stringTest('you')")
653 'Hello you'
654 >>> e(_bytes("stringTest('\\\\xe9lan')").decode("unicode_escape"))
655 u'Hello \\xe9lan'
656 >>> e("stringTest('you','there')")
657 Traceback (most recent call last):
658 ...
659 TypeError: stringTest() takes exactly 2 arguments (3 given)
660 >>> e("floatTest(2)")
661 6.0
662 >>> e("booleanTest(true())")
663 False
664 >>> list(map(tag, e("setTest(/body/tag)")))
665 ['tag']
666 >>> list(map(tag, e("setTest2(/body/*)")))
667 ['tag', 'section']
668 >>> e("argsTest1('a',1.5,true(),/body/tag)")
669 "a, 1.5, True, ['tag', 'tag', 'tag']"
670 >>> list(map(tag, e("argsTest2(/body/tag, /body/section)")))
671 ['tag', 'section', 'tag', 'tag']
672 >>> e("resultTypesTest()")
673 Traceback (most recent call last):
674 ...
675 XPathResultError: This is not a node: 'x'
676 >>> try:
677 ... e("resultTypesTest2()")
678 ... except etree.XPathResultError:
679 ... print("Got error")
680 Got error
681 """
682
683 if sys.version_info[0] >= 3:
684 xpath.__doc__ = xpath.__doc__.replace(" u'", " '")
685 xpath.__doc__ = xpath.__doc__.replace(" XPathResultError",
686 " lxml.etree.XPathResultError")
687 xpath.__doc__ = xpath.__doc__.replace(" exactly 2 arguments",
688 " exactly 2 positional arguments")
689
699
700 if __name__ == '__main__':
701 print('to test use test.py %s' % __file__)
702