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, 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) # needed for Py3
     12
     13  from common_imports import etree, HelperTestCase, _bytes, BytesIO
     14  from common_imports import doctest, make_doctest
     15
     16 -class ETreeXPathTestCase(HelperTestCase):
     17      """XPath tests etree"""
     18
     19 -    def test_xpath_boolean(self):
     20          tree = self.parse('<a><b></b><b></b></a>')
     21          self.assert_(tree.xpath('boolean(/a/b)'))
     22          self.assert_(not tree.xpath('boolean(/a/c)'))
     23
     24 -    def test_xpath_number(self):
     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
     34 -    def test_xpath_string(self):
     35          tree = self.parse('<a>Foo</a>')
     36          self.assertEquals('Foo',
     37                            tree.xpath('string(/a/text())'))
     38
     39 -    def test_xpath_document_root(self):
     40          tree = self.parse('<a><b/></a>')
     41          self.assertEquals([],
     42                            tree.xpath('/'))
     43
     44 -    def test_xpath_namespace(self):
     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
     49 -    def test_xpath_namespace_empty(self):
     50          tree = self.parse('<a/>')
     51          self.assertEquals([('xml', 'http://www.w3.org/XML/1998/namespace')],
     52                            tree.xpath('namespace::*'))
     53
     54 -    def test_xpath_list_elements(self):
     55          tree = self.parse('<a><b>Foo</b><b>Bar</b></a>')
     56          root = tree.getroot()
     57          self.assertEquals([root[0], root[1]],
     58                            tree.xpath('/a/b'))
     59
     60 -    def test_xpath_list_nothing(self):
     61          tree = self.parse('<a><b/></a>')
     62          self.assertEquals([],
     63                            tree.xpath('/a/c'))
     64          # this seems to pass a different code path, also should return nothing
     65          self.assertEquals([],
     66                            tree.xpath('/a/c/text()'))
     67
     68 -    def test_xpath_list_text(self):
     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
     74 -    def test_xpath_list_text_parent(self):
     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
     82 -    def test_xpath_list_text_parent_no_smart_strings(self):
     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
     97 -    def test_xpath_list_unicode_text_parent(self):
     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
    107 -    def test_xpath_list_attribute(self):
    108          tree = self.parse('<a b="B" c="C"/>')
    109          self.assertEquals(['B'],
    110                            tree.xpath('/a/@b'))
    111
    112 -    def test_xpath_list_attribute_parent(self):
    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
    119 -    def test_xpath_list_attribute_parent_no_smart_strings(self):
    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
    132 -    def test_xpath_text_from_other_document(self):
    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
    150 -    def test_xpath_list_comment(self):
    151          tree = self.parse('<a><!-- Foo --></a>')
    152          self.assertEquals(['<!-- Foo -->'],
    153                            list(map(repr, tree.xpath('/a/node()'))))
    154
    155 -    def test_rel_xpath_boolean(self):
    156          root = etree.XML('<a><b><c/></b></a>')
    157          el = root[0]
    158          self.assert_(el.xpath('boolean(c)'))
    159          self.assert_(not el.xpath('boolean(d)'))
    160
    161 -    def test_rel_xpath_list_elements(self):
    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
    170 -    def test_xpath_ns(self):
    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
    183 -    def test_xpath_ns_none(self):
    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
    190 -    def test_xpath_ns_empty(self):
    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
    197 -    def test_xpath_error(self):
    198          tree = self.parse('<a/>')
    199          self.assertRaises(etree.XPathEvalError, tree.xpath, '\\fad')
    200
    201 -    def test_xpath_class_error(self):
    202          self.assertRaises(SyntaxError, etree.XPath, '\\fad')
    203          self.assertRaises(etree.XPathSyntaxError, etree.XPath, '\\fad')
    204
    205 -    def test_xpath_prefix_error(self):
    206          tree = self.parse('<a/>')
    207          self.assertRaises(etree.XPathEvalError, tree.xpath, '/fa:d')
    208
    209 -    def test_xpath_class_prefix_error(self):
    210          tree = self.parse('<a/>')
    211          xpath = etree.XPath("/fa:d")
    212          self.assertRaises(etree.XPathEvalError, xpath, tree)
    213
    214 -    def test_elementtree_getpath(self):
    215          a  = etree.Element("a")
    216          b  = etree.SubElement(a, "b")
    217          c  = etree.SubElement(a, "c")
    218          d1 = etree.SubElement(c, "d")
    219          d2 = etree.SubElement(c, "d")
    220
    221          tree = etree.ElementTree(a)
    222          self.assertEqual('/a/c/d',
    223                           tree.getpath(d2)[:6])
    224          self.assertEqual([d2],
    225                           tree.xpath(tree.getpath(d2)))
    226
    227 -    def test_elementtree_getpath_partial(self):
    228          a  = etree.Element("a")
    229          b  = etree.SubElement(a, "b")
    230          c  = etree.SubElement(a, "c")
    231          d1 = etree.SubElement(c, "d")
    232          d2 = etree.SubElement(c, "d")
    233
    234          tree = etree.ElementTree(c)
    235          self.assertEqual('/c/d',
    236                           tree.getpath(d2)[:4])
    237          self.assertEqual([d2],
    238                           tree.xpath(tree.getpath(d2)))
    239
    240 -    def test_xpath_evaluator(self):
    241          tree = self.parse('<a><b><c></c></b></a>')
    242          e = etree.XPathEvaluator(tree)
    243          root = tree.getroot()
    244          self.assertEquals(
    245              [root],
    246              e('//a'))
    247
    248 -    def test_xpath_evaluator_tree(self):
    249          tree = self.parse('<a><b><c></c></b></a>')
    250          child_tree = etree.ElementTree(tree.getroot()[0])
    251          e = etree.XPathEvaluator(child_tree)
    252          self.assertEquals(
    253              [],
    254              e('a'))
    255          root = child_tree.getroot()
    256          self.assertEquals(
    257              [root[0]],
    258              e('c'))
    259
    260 -    def test_xpath_evaluator_tree_absolute(self):
    261          tree = self.parse('<a><b><c></c></b></a>')
    262          child_tree = etree.ElementTree(tree.getroot()[0])
    263          e = etree.XPathEvaluator(child_tree)
    264          self.assertEquals(
    265              [],
    266              e('/a'))
    267          root = child_tree.getroot()
    268          self.assertEquals(
    269              [root],
    270              e('/b'))
    271          self.assertEquals(
    272              [],
    273              e('/c'))
    274
    275 -    def test_xpath_evaluator_element(self):
    276          tree = self.parse('<a><b><c></c></b></a>')
    277          root = tree.getroot()
    278          e = etree.XPathEvaluator(root[0])
    279          self.assertEquals(
    280              [root[0][0]],
    281              e('c'))
    282
    283 -    def test_xpath_extensions(self):
    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
    292 -    def test_xpath_extensions_wrong_args(self):
    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
    300 -    def test_xpath_extensions_error(self):
    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
    308 -    def test_xpath_extensions_nodes(self):
    309          def f(evaluator, arg):
    310              r = etree.Element('results')
    311              b = etree.SubElement(r, 'result')
    312              b.text = 'Hoi'
    313              b = etree.SubElement(r, 'result')
    314              b.text = 'Dag'
    315              return r
    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
    324 -    def test_xpath_extensions_nodes_append(self):
    325          def f(evaluator, nodes):
    326              r = etree.SubElement(nodes[0], 'results')
    327              b = etree.SubElement(r, 'result')
    328              b.text = 'Hoi'
    329              b = etree.SubElement(r, 'result')
    330              b.text = 'Dag'
    331              return r
    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
    340 -    def test_xpath_extensions_nodes_append2(self):
    341          def f(evaluator, nodes):
    342              r = etree.Element('results')
    343              b = etree.SubElement(r, 'result')
    344              b.text = 'Hoi'
    345              b = etree.SubElement(r, 'result')
    346              b.text = 'Dag'
    347              r.append(nodes[0])
    348              return r
    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
    358 -    def test_xpath_context_node(self):
    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
    376 -    def test_xpath_eval_context_propagation(self):
    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              # empty during the "b" call, a "b" during the "c" call
    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
    396 -    def test_xpath_eval_context_clear(self):
    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              # context must be empty for each new evaluation
    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
    424 -    def test_xpath_variables(self):
    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
    440 -    def test_xpath_variables_nodeset(self):
    441          x = self.parse('<a attr="true"/>')
    442          e = etree.XPathEvaluator(x)
    443
    444          element = etree.Element("test-el")
    445          etree.SubElement(element, "test-sub")
    446          expr = "$value"
    447          r = e(expr, value=element)
    448          self.assertEquals(1, len(r))
    449          self.assertEquals(element.tag, r[0].tag)
    450          self.assertEquals(element[0].tag, r[0][0].tag)
    451
    452 -    def test_xpath_extensions_mix(self):
    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
    515 -class ETreeXPathClassTestCase(HelperTestCase):
    516      "Tests for the XPath class"
    517 -    def test_xpath_compile_doc(self):
    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
    532 -    def test_xpath_compile_element(self):
    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
    545 -    def test_xpath_compile_vars(self):
    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
    555 -    def test_xpath_compile_error(self):
    556          self.assertRaises(SyntaxError, etree.XPath, '\\fad')
    557
    558 -    def test_xpath_elementtree_error(self):
    559          self.assertRaises(ValueError, etree.XPath('*'), etree.ElementTree())
    560
    561 -class ETreeETXPathClassTestCase(HelperTestCase):
    562      "Tests for the ETXPath class"
    563 -    def test_xpath_compile_ns(self):
    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      # disabled this test as non-ASCII characters in namespace URIs are
    577      # not acceptable
    578 -    def _test_xpath_compile_unicode(self):
    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
    603 -def tag(elem):
    604      return elem.tag
    605
    606 -def stringTest(ctxt, s1):
    607      return "Hello "+s1
    608
    609 -def floatTest(ctxt, f1):
    610      return f1+4
    611
    612 -def booleanTest(ctxt, b1):
    613      return not b1
    614
    615 -def setTest(ctxt, st1):
    616      return st1[0]
    617
    618 -def setTest2(ctxt, st1):
    619      return st1[0:2]
    620
    621 -def argsTest1(ctxt, s, f, b, st):
    622      return ", ".join(map(str, (s, f, b, list(map(tag, st)))))
    623
    624 -def argsTest2(ctxt, st1, st2):
    625      st1.extend(st2)
    626      return st1
    627
    628 -def resultTypesTest(ctxt):
    629      return ["x","y"]
    630
    631 -def resultTypesTest2(ctxt):
    632      return resultTypesTest
    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
    646 -def xpath():
    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
    690 -def test_suite():
    691      suite = unittest.TestSuite()
    692      suite.addTests([unittest.makeSuite(ETreeXPathTestCase)])
    693      suite.addTests([unittest.makeSuite(ETreeXPathClassTestCase)])
    694      suite.addTests([unittest.makeSuite(ETreeETXPathClassTestCase)])
    695      suite.addTests([doctest.DocTestSuite()])
    696      suite.addTests(
    697          [make_doctest('../../../doc/xpathxslt.txt')])
    698      return suite
    699
    700  if __name__ == '__main__':
    701      print('to test use test.py %s' % __file__)
    702