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
     45 -    def test_xpath_namespace_empty(self):
     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
     70 -    def test_xpath_list_attribute(self):
     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
     86 -    def test_rel_xpath_list_elements(self):
     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', namespaces={'foo': 'uri:a'}))
    101          self.assertEquals(
    102              [],
    103              tree.xpath('//foo:b', namespaces={'foo': 'uri:c'}))
    104          self.assertEquals(
    105              [root[0]],
    106              root.xpath('//baz:b', namespaces={'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', namespaces={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', namespaces={'': '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
    134 -    def test_xpath_class_prefix_error(self):
    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
    152 -    def test_elementtree_getpath_partial(self):
    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
    173 -    def test_xpath_evaluator_tree(self):
    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
    185 -    def test_xpath_evaluator_element(self):
    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, extensions=[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, extensions=[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, extensions=[{(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, extensions=[{(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, extensions=[{(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
    442 -    def test_xpath_compile_element(self):
    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
    468 -    def test_xpath_elementtree_error(self):
    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
    486 -    def test_xpath_compile_unicode(self):
    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, extensions=[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