lxml.tests.test_xslt

Package lxml :: Package tests :: Module test_xslt |
[hide private]
[frames] | no frames]

# Source Code for Module lxml.tests.test_xslt

       1  # -*- coding: utf-8 -*-
       2
       3  """
       4  Test cases related to XSLT processing
       5  """
       6
       7  from __future__ import absolute_import
       8
       9  import io
      10  import sys
      11  import copy
      12  import gzip
      13  import os.path
      14  import unittest
      15  import contextlib
      16  from textwrap import dedent
      17  from tempfile import NamedTemporaryFile, mkdtemp
      18
      19  is_python3 = sys.version_info[0] >= 3
      20
      21  try:
      22      unicode
      23  except NameError: # Python 3
      24      unicode = str
      25
      26  try:
      27      basestring
      28  except NameError: # Python 3
      29      basestring = str
      30
      31  from .common_imports import (
      32      etree, BytesIO, HelperTestCase, fileInTestDir, _bytes, make_doctest, skipif
      33  )
      34
      35
      36 -class ETreeXSLTTestCase(HelperTestCase):
      37      """XSLT tests etree"""
      38
      39 -    def test_xslt(self):
      40          tree = self.parse('<a><b>B</b><c>C</c></a>')
      41          style = self.parse('''\
      42  <xsl:stylesheet version="1.0"
      43      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      44    <xsl:template match="*" />
      45    <xsl:template match="/">
      46      <foo><xsl:value-of select="/a/b/text()" /></foo>
      47    </xsl:template>
      48  </xsl:stylesheet>''')
      49
      50          st = etree.XSLT(style)
      51          res = st(tree)
      52          self.assertEqual('''\
      53  <?xml version="1.0"?>
      54  <foo>B</foo>
      55  ''',
      56                            str(res))
      57
      58 -    def test_xslt_elementtree_error(self):
      59          self.assertRaises(ValueError, etree.XSLT, etree.ElementTree())
      60
      61 -    def test_xslt_input_none(self):
      62          self.assertRaises(TypeError, etree.XSLT, None)
      63
      64 -    def test_xslt_invalid_stylesheet(self):
      65          style = self.parse('''\
      66  <xsl:stylesheet version="1.0"
      67          xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      68      <xsl:stylesheet />
      69  </xsl:stylesheet>''')
      70
      71          self.assertRaises(
      72              etree.XSLTParseError, etree.XSLT, style)
      73
      74 -    def test_xslt_copy(self):
      75          tree = self.parse('<a><b>B</b><c>C</c></a>')
      76          style = self.parse('''\
      77  <xsl:stylesheet version="1.0"
      78      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      79    <xsl:template match="*" />
      80    <xsl:template match="/">
      81      <foo><xsl:value-of select="/a/b/text()" /></foo>
      82    </xsl:template>
      83  </xsl:stylesheet>''')
      84
      85          transform = etree.XSLT(style)
      86          res = transform(tree)
      87          self.assertEqual('''\
      88  <?xml version="1.0"?>
      89  <foo>B</foo>
      90  ''',
      91                            str(res))
      92
      93          transform_copy = copy.deepcopy(transform)
      94          res = transform_copy(tree)
      95          self.assertEqual('''\
      96  <?xml version="1.0"?>
      97  <foo>B</foo>
      98  ''',
      99                            str(res))
     100
     101          transform = etree.XSLT(style)
     102          res = transform(tree)
     103          self.assertEqual('''\
     104  <?xml version="1.0"?>
     105  <foo>B</foo>
     106  ''',
     107                            str(res))
     108
     109      @contextlib.contextmanager
     110 -    def _xslt_setup(
     111              self, encoding='UTF-16', expected_encoding=None,
     112              expected='<?xml version="1.0" encoding="%(ENCODING)s"?><foo>\\uF8D2</foo>'):
     113          tree = self.parse(_bytes('<a><b>\\uF8D2</b><c>\\uF8D2</c></a>'
     114                                   ).decode("unicode_escape"))
     115          style = self.parse('''\
     116  <xsl:stylesheet version="1.0"
     117      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     118    <xsl:output encoding="%(ENCODING)s"/>
     119    <xsl:template match="/">
     120      <foo><xsl:value-of select="/a/b/text()" /></foo>
     121    </xsl:template>
     122  </xsl:stylesheet>''' % {'ENCODING': encoding})
     123
     124          st = etree.XSLT(style)
     125          res = st(tree)
     126          expected = _bytes(dedent(expected).strip()).decode("unicode_escape").replace('\n', '') % {
     127              'ENCODING': expected_encoding or encoding,
     128          }
     129
     130          data = [res]
     131          yield data
     132          self.assertEqual(expected, data[0].replace('\n', ''))
     133
     134 -    def test_xslt_utf8(self):
     135          with self._xslt_setup(encoding='UTF-8') as res:
     136              res[0] = unicode(bytes(res[0]), 'UTF-8')
     137              assert 'UTF-8' in res[0]
     138
     139 -    def test_xslt_encoding(self):
     140          with self._xslt_setup() as res:
     141              res[0] = unicode(bytes(res[0]), 'UTF-16')
     142              assert 'UTF-16' in res[0]
     143
     144 -    def test_xslt_encoding_override(self):
     145          with self._xslt_setup(encoding='UTF-8', expected_encoding='UTF-16') as res:
     146              f = BytesIO()
     147              res[0].write(f, encoding='UTF-16')
     148              if is_python3:
     149                  output = str(f.getvalue(), 'UTF-16')
     150              else:
     151                  output = unicode(str(f.getvalue()), 'UTF-16')
     152              res[0] = output.replace("'", '"')
     153
     154 -    def test_xslt_write_output_bytesio(self):
     155          with self._xslt_setup() as res:
     156              f = BytesIO()
     157              res[0].write_output(f)
     158              res[0] = f.getvalue().decode('UTF-16')
     159
     160 -    def test_xslt_write_output_failure(self):
     161          class Writer(object):
     162              def write(self, data):
     163                  raise ValueError("FAILED!")
     164
     165          try:
     166              with self._xslt_setup() as res:
     167                  res[0].write_output(Writer())
     168          except ValueError as exc:
     169              self.assertTrue("FAILED!" in str(exc), exc)
     170          else:
     171              self.assertTrue(False, "exception not raised")
     172
     173 -    def test_xslt_write_output_file(self):
     174          with self._xslt_setup() as res:
     175              f = NamedTemporaryFile(delete=False)
     176              try:
     177                  try:
     178                      res[0].write_output(f)
     179                  finally:
     180                      f.close()
     181                  with io.open(f.name, encoding='UTF-16') as f:
     182                      res[0] = f.read()
     183              finally:
     184                  os.unlink(f.name)
     185
     186 -    def test_xslt_write_output_file_path(self):
     187          with self._xslt_setup() as res:
     188              f = NamedTemporaryFile(delete=False)
     189              try:
     190                  try:
     191                      res[0].write_output(f.name, compression=9)
     192                  finally:
     193                      f.close()
     194                  with gzip.GzipFile(f.name) as f:
     195                      res[0] = f.read().decode("UTF-16")
     196              finally:
     197                  os.unlink(f.name)
     198
     199 -    def test_xslt_write_output_file_path_urlescaped(self):
     200          # libxml2 should not unescape file paths.
     201          with self._xslt_setup() as res:
     202              f = NamedTemporaryFile(prefix='tmp%2e', suffix='.xml.gz', delete=False)
     203              try:
     204                  try:
     205                      res[0].write_output(f.name, compression=3)
     206                  finally:
     207                      f.close()
     208                  with gzip.GzipFile(f.name) as f:
     209                      res[0] = f.read().decode("UTF-16")
     210              finally:
     211                  os.unlink(f.name)
     212
     213 -    def test_xslt_write_output_file_path_urlescaped_plus(self):
     214          with self._xslt_setup() as res:
     215              f = NamedTemporaryFile(prefix='p+%2e', suffix='.xml.gz', delete=False)
     216              try:
     217                  try:
     218                      res[0].write_output(f.name, compression=1)
     219                  finally:
     220                      f.close()
     221                  with gzip.GzipFile(f.name) as f:
     222                      res[0] = f.read().decode("UTF-16")
     223              finally:
     224                  os.unlink(f.name)
     225
     226 -    def test_xslt_write_output_file_oserror(self):
     227          with self._xslt_setup(expected='') as res:
     228              tempdir = mkdtemp()
     229              try:
     230                  res[0].write_output(os.path.join(tempdir, 'missing_subdir', 'out.xml'))
     231              except IOError:
     232                  res[0] = ''
     233              else:
     234                  self.fail("IOError not raised")
     235              finally:
     236                  os.rmdir(tempdir)
     237
     238 -    def test_xslt_unicode(self):
     239          expected = '''
     240              <?xml version="1.0"?>
     241              <foo>\\uF8D2</foo>
     242          '''
     243          with self._xslt_setup(expected=expected) as res:
     244              res[0] = unicode(res[0])
     245
     246 -    def test_xslt_unicode_standalone(self):
     247          tree = self.parse(_bytes('<a><b>\\uF8D2</b><c>\\uF8D2</c></a>'
     248          ).decode("unicode_escape"))
     249          style = self.parse('''\
     250  <xsl:stylesheet version="1.0"
     251      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     252    <xsl:output encoding="UTF-16" standalone="no"/>
     253    <xsl:template match="/">
     254      <foo><xsl:value-of select="/a/b/text()" /></foo>
     255    </xsl:template>
     256  </xsl:stylesheet>''')
     257
     258          st = etree.XSLT(style)
     259          res = st(tree)
     260          expected = _bytes('''\
     261  <?xml version="1.0" standalone="no"?>
     262  <foo>\\uF8D2</foo>
     263  ''').decode("unicode_escape")
     264          self.assertEqual(expected,
     265                           unicode(res))
     266
     267 -    def test_xslt_input(self):
     268          style = self.parse('''\
     269  <xsl:stylesheet version="1.0"
     270      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     271    <xsl:template match="*" />
     272    <xsl:template match="/">
     273      <foo><xsl:value-of select="/a/b/text()" /></foo>
     274    </xsl:template>
     275  </xsl:stylesheet>''')
     276
     277          st = etree.XSLT(style)
     278          st = etree.XSLT(style.getroot())
     279
     280 -    def test_xslt_input_partial_doc(self):
     281          style = self.parse('''\
     282  <otherroot>
     283  <xsl:stylesheet version="1.0"
     284      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     285    <xsl:template match="*" />
     286    <xsl:template match="/">
     287      <foo><xsl:value-of select="/a/b/text()" /></foo>
     288    </xsl:template>
     289  </xsl:stylesheet>
     290  </otherroot>''')
     291
     292          self.assertRaises(etree.XSLTParseError, etree.XSLT, style)
     293          root_node = style.getroot()
     294          self.assertRaises(etree.XSLTParseError, etree.XSLT, root_node)
     295          st = etree.XSLT(root_node[0])
     296
     297 -    def test_xslt_broken(self):
     298          style = self.parse('''\
     299  <xsl:stylesheet version="1.0"
     300      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     301      <xsl:foo />
     302  </xsl:stylesheet>''')
     303          self.assertRaises(etree.XSLTParseError,
     304                            etree.XSLT, style)
     305
     306 -    def test_xslt_parsing_error_log(self):
     307          tree = self.parse('<a/>')
     308          style = self.parse('''\
     309  <xsl:stylesheet version="1.0"
     310      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     311      <xsl:foo />
     312  </xsl:stylesheet>''')
     313          self.assertRaises(etree.XSLTParseError,
     314                            etree.XSLT, style)
     315          exc = None
     316          try:
     317              etree.XSLT(style)
     318          except etree.XSLTParseError as e:
     319              exc = e
     320          else:
     321              self.assertFalse(True, "XSLT processing should have failed but didn't")
     322          self.assertTrue(exc is not None)
     323          self.assertTrue(len(exc.error_log))
     324          for error in exc.error_log:
     325              self.assertTrue(':ERROR:XSLT:' in str(error))
     326
     327 -    def test_xslt_apply_error_log(self):
     328          tree = self.parse('<a/>')
     329          style = self.parse('''\
     330  <xsl:stylesheet version="1.0"
     331      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     332      <xsl:template match="a">
     333          <xsl:copy>
     334              <xsl:message terminate="yes">FAIL</xsl:message>
     335          </xsl:copy>
     336      </xsl:template>
     337  </xsl:stylesheet>''')
     338          self.assertRaises(etree.XSLTApplyError,
     339                            etree.XSLT(style), tree)
     340
     341          transform = etree.XSLT(style)
     342          exc = None
     343          try:
     344              transform(tree)
     345          except etree.XSLTApplyError as e:
     346              exc = e
     347          else:
     348              self.assertFalse(True, "XSLT processing should have failed but didn't")
     349
     350          self.assertTrue(exc is not None)
     351          self.assertTrue(len(exc.error_log))
     352          self.assertEqual(len(transform.error_log), len(exc.error_log))
     353          for error in exc.error_log:
     354              self.assertTrue(':ERROR:XSLT:' in str(error))
     355          for error in transform.error_log:
     356              self.assertTrue(':ERROR:XSLT:' in str(error))
     357
     358 -    def test_xslt_parameters(self):
     359          tree = self.parse('<a><b>B</b><c>C</c></a>')
     360          style = self.parse('''\
     361  <xsl:stylesheet version="1.0"
     362      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     363    <xsl:template match="/">
     364      <foo><xsl:value-of select="$bar" /></foo>
     365    </xsl:template>
     366  </xsl:stylesheet>''')
     367
     368          st = etree.XSLT(style)
     369          res = st(tree, bar="'Bar'")
     370          self.assertEqual('''\
     371  <?xml version="1.0"?>
     372  <foo>Bar</foo>
     373  ''',
     374                            str(res))
     375
     376 -    def test_xslt_string_parameters(self):
     377          tree = self.parse('<a><b>B</b><c>C</c></a>')
     378          style = self.parse('''\
     379  <xsl:stylesheet version="1.0"
     380      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     381    <xsl:template match="/">
     382      <foo><xsl:value-of select="$bar" /></foo>
     383    </xsl:template>
     384  </xsl:stylesheet>''')
     385
     386          st = etree.XSLT(style)
     387          res = st(tree, bar=etree.XSLT.strparam('''it's me, "Bar"'''))
     388          self.assertEqual('''\
     389  <?xml version="1.0"?>
     390  <foo>it's me, "Bar"</foo>
     391  ''',
     392                            str(res))
     393
     394 -    def test_xslt_parameter_invalid(self):
     395          tree = self.parse('<a><b>B</b><c>C</c></a>')
     396          style = self.parse('''\
     397  <xsl:stylesheet version="1.0"
     398      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     399    <xsl:param name="bar"/>
     400    <xsl:template match="/">
     401      <foo><xsl:value-of select="$bar" /></foo>
     402    </xsl:template>
     403  </xsl:stylesheet>''')
     404
     405          st = etree.XSLT(style)
     406          res = self.assertRaises(etree.XSLTApplyError,
     407                                  st, tree, bar="<test/>")
     408          res = self.assertRaises(etree.XSLTApplyError,
     409                                  st, tree, bar="....")
     410
     411 -    def test_xslt_parameter_missing(self):
     412          # apply() without needed parameter will lead to XSLTApplyError
     413          tree = self.parse('<a><b>B</b><c>C</c></a>')
     414          style = self.parse('''\
     415  <xsl:stylesheet version="1.0"
     416      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     417    <xsl:template match="/">
     418      <foo><xsl:value-of select="$bar" /></foo>
     419    </xsl:template>
     420  </xsl:stylesheet>''')
     421
     422          st = etree.XSLT(style)
     423          # at least libxslt 1.1.28 produces this error, earlier ones (e.g. 1.1.18) might not ...
     424          self.assertRaises(etree.XSLTApplyError, st.apply, tree)
     425
     426 -    def test_xslt_multiple_parameters(self):
     427          tree = self.parse('<a><b>B</b><c>C</c></a>')
     428          style = self.parse('''\
     429  <xsl:stylesheet version="1.0"
     430      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     431    <xsl:template match="*" />
     432    <xsl:template match="/">
     433      <foo><xsl:value-of select="$bar" /></foo>
     434      <foo><xsl:value-of select="$baz" /></foo>
     435    </xsl:template>
     436  </xsl:stylesheet>''')
     437
     438          st = etree.XSLT(style)
     439          res = st(tree, bar="'Bar'", baz="'Baz'")
     440          self.assertEqual('''\
     441  <?xml version="1.0"?>
     442  <foo>Bar</foo><foo>Baz</foo>
     443  ''',
     444                            str(res))
     445
     446 -    def test_xslt_parameter_xpath(self):
     447          tree = self.parse('<a><b>B</b><c>C</c></a>')
     448          style = self.parse('''\
     449  <xsl:stylesheet version="1.0"
     450      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     451    <xsl:template match="*" />
     452    <xsl:template match="/">
     453      <foo><xsl:value-of select="$bar" /></foo>
     454    </xsl:template>
     455  </xsl:stylesheet>''')
     456
     457          st = etree.XSLT(style)
     458          res = st(tree, bar="/a/b/text()")
     459          self.assertEqual('''\
     460  <?xml version="1.0"?>
     461  <foo>B</foo>
     462  ''',
     463                            str(res))
     464
     465 -    def test_xslt_parameter_xpath_object(self):
     466          tree = self.parse('<a><b>B</b><c>C</c></a>')
     467          style = self.parse('''\
     468  <xsl:stylesheet version="1.0"
     469      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     470    <xsl:template match="*" />
     471    <xsl:template match="/">
     472      <foo><xsl:value-of select="$bar" /></foo>
     473    </xsl:template>
     474  </xsl:stylesheet>''')
     475
     476          st = etree.XSLT(style)
     477          res = st(tree, bar=etree.XPath("/a/b/text()"))
     478          self.assertEqual('''\
     479  <?xml version="1.0"?>
     480  <foo>B</foo>
     481  ''',
     482                            str(res))
     483
     484 -    def test_xslt_default_parameters(self):
     485          tree = self.parse('<a><b>B</b><c>C</c></a>')
     486          style = self.parse('''\
     487  <xsl:stylesheet version="1.0"
     488      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     489    <xsl:param name="bar" select="'Default'" />
     490    <xsl:template match="*" />
     491    <xsl:template match="/">
     492      <foo><xsl:value-of select="$bar" /></foo>
     493    </xsl:template>
     494  </xsl:stylesheet>''')
     495
     496          st = etree.XSLT(style)
     497          res = st(tree, bar="'Bar'")
     498          self.assertEqual('''\
     499  <?xml version="1.0"?>
     500  <foo>Bar</foo>
     501  ''',
     502                            str(res))
     503          res = st(tree)
     504          self.assertEqual('''\
     505  <?xml version="1.0"?>
     506  <foo>Default</foo>
     507  ''',
     508                            str(res))
     509
     510 -    def test_xslt_html_output(self):
     511          tree = self.parse('<a><b>B</b><c>C</c></a>')
     512          style = self.parse('''\
     513  <xsl:stylesheet version="1.0"
     514      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     515    <xsl:output method="html"/>
     516    <xsl:strip-space elements="*"/>
     517    <xsl:template match="/">
     518      <html><body><xsl:value-of select="/a/b/text()" /></body></html>
     519    </xsl:template>
     520  </xsl:stylesheet>''')
     521
     522          st = etree.XSLT(style)
     523          res = st(tree)
     524          self.assertEqual('<html><body>B</body></html>',
     525                            str(res).strip())
     526
     527 -    def test_xslt_include(self):
     528          tree = etree.parse(fileInTestDir('test1.xslt'))
     529          st = etree.XSLT(tree)
     530
     531 -    def test_xslt_include_from_filelike(self):
     532          f = open(fileInTestDir('test1.xslt'), 'rb')
     533          tree = etree.parse(f)
     534          f.close()
     535          st = etree.XSLT(tree)
     536
     537 -    def test_xslt_multiple_transforms(self):
     538          xml = '<a/>'
     539          xslt = '''\
     540  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
     541      <xsl:template match="/">
     542          <response>Some text</response>
     543      </xsl:template>
     544  </xsl:stylesheet>
     545  '''
     546          source = self.parse(xml)
     547          styledoc = self.parse(xslt)
     548          style = etree.XSLT(styledoc)
     549          result = style(source)
     550
     551          etree.tostring(result.getroot())
     552
     553          source = self.parse(xml)
     554          styledoc = self.parse(xslt)
     555          style = etree.XSLT(styledoc)
     556          result = style(source)
     557
     558          etree.tostring(result.getroot())
     559
     560 -    def test_xslt_repeat_transform(self):
     561          xml = '<a/>'
     562          xslt = '''\
     563  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
     564      <xsl:template match="/">
     565          <response>Some text</response>
     566      </xsl:template>
     567  </xsl:stylesheet>
     568  '''
     569          source = self.parse(xml)
     570          styledoc = self.parse(xslt)
     571          transform = etree.XSLT(styledoc)
     572          result = transform(source)
     573          result = transform(source)
     574          etree.tostring(result.getroot())
     575          result = transform(source)
     576          etree.tostring(result.getroot())
     577          str(result)
     578
     579          result1 = transform(source)
     580          result2 = transform(source)
     581          self.assertEqual(str(result1), str(result2))
     582          result = transform(source)
     583          str(result)
     584
     585 -    def test_xslt_empty(self):
     586          # could segfault if result contains "empty document"
     587          xml = '<blah/>'
     588          xslt = '''
     589          <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
     590            <xsl:template match="/" />
     591          </xsl:stylesheet>
     592          '''
     593
     594          source = self.parse(xml)
     595          styledoc = self.parse(xslt)
     596          style = etree.XSLT(styledoc)
     597          result = style(source)
     598          self.assertEqual('', str(result))
     599
     600 -    def test_xslt_message(self):
     601          xml = '<blah/>'
     602          xslt = '''
     603          <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
     604            <xsl:template match="/">
     605              <xsl:message>TEST TEST TEST</xsl:message>
     606            </xsl:template>
     607          </xsl:stylesheet>
     608          '''
     609
     610          source = self.parse(xml)
     611          styledoc = self.parse(xslt)
     612          style = etree.XSLT(styledoc)
     613          result = style(source)
     614          self.assertEqual('', str(result))
     615          self.assertTrue("TEST TEST TEST" in [entry.message
     616                                            for entry in style.error_log])
     617
     618 -    def test_xslt_message_terminate(self):
     619          xml = '<blah/>'
     620          xslt = '''
     621          <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
     622            <xsl:template match="/">
     623              <xsl:message terminate="yes">TEST TEST TEST</xsl:message>
     624            </xsl:template>
     625          </xsl:stylesheet>
     626          '''
     627
     628          source = self.parse(xml)
     629          styledoc = self.parse(xslt)
     630          style = etree.XSLT(styledoc)
     631
     632          self.assertRaises(etree.XSLTApplyError, style, source)
     633          self.assertTrue("TEST TEST TEST" in [entry.message
     634                                            for entry in style.error_log])
     635
     636 -    def test_xslt_shortcut(self):
     637          tree = self.parse('<a><b>B</b><c>C</c></a>')
     638          style = self.parse('''\
     639  <xsl:stylesheet version="1.0"
     640      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     641    <xsl:template match="*" />
     642    <xsl:template match="/">
     643      <doc>
     644      <foo><xsl:value-of select="$bar" /></foo>
     645      <foo><xsl:value-of select="$baz" /></foo>
     646      </doc>
     647    </xsl:template>
     648  </xsl:stylesheet>''')
     649
     650          result = tree.xslt(style, bar="'Bar'", baz="'Baz'")
     651          self.assertEqual(
     652              _bytes('<doc><foo>Bar</foo><foo>Baz</foo></doc>'),
     653              etree.tostring(result.getroot()))
     654
     655 -    def test_multiple_elementrees(self):
     656          tree = self.parse('<a><b>B</b><c>C</c></a>')
     657          style = self.parse('''\
     658  <xsl:stylesheet version="1.0"
     659      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     660    <xsl:template match="a"><A><xsl:apply-templates/></A></xsl:template>
     661    <xsl:template match="b"><B><xsl:apply-templates/></B></xsl:template>
     662    <xsl:template match="c"><C><xsl:apply-templates/></C></xsl:template>
     663  </xsl:stylesheet>''')
     664
     665          self.assertEqual(self._rootstring(tree),
     666                            _bytes('<a><b>B</b><c>C</c></a>'))
     667          result = tree.xslt(style)
     668          self.assertEqual(self._rootstring(tree),
     669                            _bytes('<a><b>B</b><c>C</c></a>'))
     670          self.assertEqual(self._rootstring(result),
     671                            _bytes('<A><B>B</B><C>C</C></A>'))
     672
     673          b_tree = etree.ElementTree(tree.getroot()[0])
     674          self.assertEqual(self._rootstring(b_tree),
     675                            _bytes('<b>B</b>'))
     676          result = b_tree.xslt(style)
     677          self.assertEqual(self._rootstring(tree),
     678                            _bytes('<a><b>B</b><c>C</c></a>'))
     679          self.assertEqual(self._rootstring(result),
     680                            _bytes('<B>B</B>'))
     681
     682          c_tree = etree.ElementTree(tree.getroot()[1])
     683          self.assertEqual(self._rootstring(c_tree),
     684                            _bytes('<c>C</c>'))
     685          result = c_tree.xslt(style)
     686          self.assertEqual(self._rootstring(tree),
     687                            _bytes('<a><b>B</b><c>C</c></a>'))
     688          self.assertEqual(self._rootstring(result),
     689                            _bytes('<C>C</C>'))
     690
     691 -    def test_xslt_document_XML(self):
     692          # make sure document('') works from parsed strings
     693          xslt = etree.XSLT(etree.XML("""\
     694  <xsl:stylesheet version="1.0"
     695     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     696    <xsl:template match="/">
     697      <test>TEXT<xsl:copy-of select="document('')//test"/></test>
     698    </xsl:template>
     699  </xsl:stylesheet>
     700  """))
     701          result = xslt(etree.XML('<a/>'))
     702          root = result.getroot()
     703          self.assertEqual(root.tag,
     704                            'test')
     705          self.assertEqual(root[0].tag,
     706                            'test')
     707          self.assertEqual(root[0].text,
     708                            'TEXT')
     709          self.assertEqual(root[0][0].tag,
     710                            '{http://www.w3.org/1999/XSL/Transform}copy-of')
     711
     712 -    def test_xslt_document_parse(self):
     713          # make sure document('') works from loaded files
     714          xslt = etree.XSLT(etree.parse(fileInTestDir("test-document.xslt")))
     715          result = xslt(etree.XML('<a/>'))
     716          root = result.getroot()
     717          self.assertEqual(root.tag,
     718                            'test')
     719          self.assertEqual(root[0].tag,
     720                            '{http://www.w3.org/1999/XSL/Transform}stylesheet')
     721
     722 -    def test_xslt_document_elementtree(self):
     723          # make sure document('') works from loaded files
     724          xslt = etree.XSLT(etree.ElementTree(file=fileInTestDir("test-document.xslt")))
     725          result = xslt(etree.XML('<a/>'))
     726          root = result.getroot()
     727          self.assertEqual(root.tag,
     728                            'test')
     729          self.assertEqual(root[0].tag,
     730                            '{http://www.w3.org/1999/XSL/Transform}stylesheet')
     731
     732 -    def test_xslt_document_error(self):
     733          xslt = etree.XSLT(etree.XML("""\
     734  <xsl:stylesheet version="1.0"
     735     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     736    <xsl:template match="/">
     737      <test>TEXT<xsl:copy-of select="document('uri:__junkfood__is__evil__')//test"/></test>
     738    </xsl:template>
     739  </xsl:stylesheet>
     740  """))
     741
     742          errors = None
     743          try:
     744              xslt(etree.XML('<a/>'))
     745          except etree.XSLTApplyError as exc:
     746              errors = exc.error_log
     747          else:
     748              self.assertFalse(True, "XSLT processing should have failed but didn't")
     749
     750          self.assertTrue(len(errors))
     751          for error in errors:
     752              if ':ERROR:XSLT:' in str(error):
     753                  break
     754          else:
     755              self.assertFalse(True, 'No XSLT errors found in error log:\n%s' % errors)
     756
     757 -    def test_xslt_document_XML_resolver(self):
     758          # make sure document('') works when custom resolvers are in use
     759          assertEqual = self.assertEqual
     760          called = {'count' : 0}
     761          class TestResolver(etree.Resolver):
     762              def resolve(self, url, id, context):
     763                  assertEqual(url, 'file://ANYTHING')
     764                  called['count'] += 1
     765                  return self.resolve_string('<CALLED/>', context)
     766
     767          parser = etree.XMLParser()
     768          parser.resolvers.add(TestResolver())
     769
     770          xslt = etree.XSLT(etree.XML(_bytes("""\
     771  <xsl:stylesheet version="1.0"
     772     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     773     xmlns:l="local">
     774    <xsl:template match="/">
     775      <test>
     776        <xsl:for-each select="document('')//l:data/l:entry">
     777          <xsl:copy-of select="document('file://ANYTHING')"/>
     778          <xsl:copy>
     779            <xsl:attribute name="value">
     780              <xsl:value-of select="."/>
     781            </xsl:attribute>
     782          </xsl:copy>
     783        </xsl:for-each>
     784      </test>
     785    </xsl:template>
     786    <l:data>
     787      <l:entry>A</l:entry>
     788      <l:entry>B</l:entry>
     789    </l:data>
     790  </xsl:stylesheet>
     791  """), parser))
     792
     793          self.assertEqual(called['count'], 0)
     794          result = xslt(etree.XML('<a/>'))
     795          self.assertEqual(called['count'], 1)
     796
     797          root = result.getroot()
     798          self.assertEqual(root.tag,
     799                            'test')
     800          self.assertEqual(len(root), 4)
     801
     802          self.assertEqual(root[0].tag,
     803                            'CALLED')
     804          self.assertEqual(root[1].tag,
     805                            '{local}entry')
     806          self.assertEqual(root[1].text,
     807                            None)
     808          self.assertEqual(root[1].get("value"),
     809                            'A')
     810          self.assertEqual(root[2].tag,
     811                            'CALLED')
     812          self.assertEqual(root[3].tag,
     813                            '{local}entry')
     814          self.assertEqual(root[3].text,
     815                            None)
     816          self.assertEqual(root[3].get("value"),
     817                            'B')
     818
     819 -    def test_xslt_resolver_url_building(self):
     820          assertEqual = self.assertEqual
     821          called = {'count' : 0}
     822          expected_url = None
     823          class TestResolver(etree.Resolver):
     824              def resolve(self, url, id, context):
     825                  assertEqual(url, expected_url)
     826                  called['count'] += 1
     827                  return self.resolve_string('<CALLED/>', context)
     828
     829          stylesheet_xml = _bytes("""\
     830  <xsl:stylesheet version="1.0"
     831     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     832     xmlns:l="local">
     833    <xsl:template match="/">
     834      <xsl:copy-of select="document('test.xml')"/>
     835    </xsl:template>
     836  </xsl:stylesheet>
     837  """)
     838
     839          parser = etree.XMLParser()
     840          parser.resolvers.add(TestResolver())
     841
     842          # test without base_url => relative path only
     843          expected_url = 'test.xml'
     844          xslt = etree.XSLT(etree.XML(stylesheet_xml, parser))
     845
     846          self.assertEqual(called['count'], 0)
     847          result = xslt(etree.XML('<a/>'))
     848          self.assertEqual(called['count'], 1)
     849
     850          # now the same thing with a stylesheet base URL on the filesystem
     851          called['count'] = 0
     852          expected_url = 'MY/BASE/test.xml'  # seems to be the same on Windows
     853          xslt = etree.XSLT(etree.XML(
     854              stylesheet_xml, parser,
     855              base_url=os.path.join('MY', 'BASE', 'FILE')))
     856
     857          self.assertEqual(called['count'], 0)
     858          result = xslt(etree.XML('<a/>'))
     859          self.assertEqual(called['count'], 1)
     860
     861          # now the same thing with a stylesheet base URL
     862          called['count'] = 0
     863          expected_url = 'http://server.com/BASE/DIR/test.xml'
     864          xslt = etree.XSLT(etree.XML(
     865              stylesheet_xml, parser,
     866              base_url='http://server.com/BASE/DIR/FILE'))
     867
     868          self.assertEqual(called['count'], 0)
     869          result = xslt(etree.XML('<a/>'))
     870          self.assertEqual(called['count'], 1)
     871
     872          # now the same thing with a stylesheet base file:// URL
     873          called['count'] = 0
     874          expected_url = 'file://BASE/DIR/test.xml'
     875          xslt = etree.XSLT(etree.XML(
     876              stylesheet_xml, parser,
     877              base_url='file://BASE/DIR/FILE'))
     878
     879          self.assertEqual(called['count'], 0)
     880          result = xslt(etree.XML('<a/>'))
     881          self.assertEqual(called['count'], 1)
     882
     883 -    def test_xslt_document_parse_allow(self):
     884          access_control = etree.XSLTAccessControl(read_file=True)
     885          xslt = etree.XSLT(etree.parse(fileInTestDir("test-document.xslt")),
     886                            access_control=access_control)
     887          result = xslt(etree.XML('<a/>'))
     888          root = result.getroot()
     889          self.assertEqual(root.tag,
     890                           'test')
     891          self.assertEqual(root[0].tag,
     892                           '{http://www.w3.org/1999/XSL/Transform}stylesheet')
     893
     894 -    def test_xslt_document_parse_deny(self):
     895          access_control = etree.XSLTAccessControl(read_file=False)
     896          xslt = etree.XSLT(etree.parse(fileInTestDir("test-document.xslt")),
     897                            access_control=access_control)
     898          self.assertRaises(etree.XSLTApplyError, xslt, etree.XML('<a/>'))
     899
     900 -    def test_xslt_document_parse_deny_all(self):
     901          access_control = etree.XSLTAccessControl.DENY_ALL
     902          xslt = etree.XSLT(etree.parse(fileInTestDir("test-document.xslt")),
     903                            access_control=access_control)
     904          self.assertRaises(etree.XSLTApplyError, xslt, etree.XML('<a/>'))
     905
     906 -    def test_xslt_access_control_repr(self):
     907          access_control = etree.XSLTAccessControl.DENY_ALL
     908          self.assertTrue(repr(access_control).startswith(type(access_control).__name__))
     909          self.assertEqual(repr(access_control), repr(access_control))
     910          self.assertNotEqual(repr(etree.XSLTAccessControl.DENY_ALL),
     911                              repr(etree.XSLTAccessControl.DENY_WRITE))
     912          self.assertNotEqual(repr(etree.XSLTAccessControl.DENY_ALL),
     913                              repr(etree.XSLTAccessControl()))
     914
     915 -    def test_xslt_move_result(self):
     916          root = etree.XML(_bytes('''\
     917          <transform>
     918            <widget displayType="fieldset"/>
     919          </transform>'''))
     920
     921          xslt = etree.XSLT(etree.XML(_bytes('''\
     922          <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     923            <xsl:output method="html" indent="no"/>
     924            <xsl:template match="/">
     925              <html>
     926                <xsl:apply-templates/>
     927              </html>
     928            </xsl:template>
     929
     930            <xsl:template match="widget">
     931              <xsl:element name="{@displayType}"/>
     932            </xsl:template>
     933
     934          </xsl:stylesheet>''')))
     935
     936          result = xslt(root[0])
     937          root[:] = result.getroot()[:]
     938          del root # segfaulted before
     939
     940 -    def test_xslt_pi(self):
     941          tree = self.parse('''\
     942  <?xml version="1.0"?>
     943  <?xml-stylesheet type="text/xsl" href="%s"?>
     944  <a>
     945    <b>B</b>
     946    <c>C</c>
     947  </a>''' % fileInTestDir("test1.xslt"))
     948
     949          style_root = tree.getroot().getprevious().parseXSL().getroot()
     950          self.assertEqual("{http://www.w3.org/1999/XSL/Transform}stylesheet",
     951                            style_root.tag)
     952
     953 -    def test_xslt_pi_embedded_xmlid(self):
     954          # test xml:id dictionary lookup mechanism
     955          tree = self.parse('''\
     956  <?xml version="1.0"?>
     957  <?xml-stylesheet type="text/xsl" href="#style"?>
     958  <a>
     959    <b>B</b>
     960    <c>C</c>
     961    <xsl:stylesheet version="1.0" xml:id="style"
     962        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     963      <xsl:template match="*" />
     964      <xsl:template match="/">
     965        <foo><xsl:value-of select="/a/b/text()" /></foo>
     966      </xsl:template>
     967    </xsl:stylesheet>
     968  </a>''')
     969
     970          style_root = tree.getroot().getprevious().parseXSL().getroot()
     971          self.assertEqual("{http://www.w3.org/1999/XSL/Transform}stylesheet",
     972                            style_root.tag)
     973
     974          st = etree.XSLT(style_root)
     975          res = st(tree)
     976          self.assertEqual('''\
     977  <?xml version="1.0"?>
     978  <foo>B</foo>
     979  ''',
     980                            str(res))
     981
     982 -    def test_xslt_pi_embedded_id(self):
     983          # test XPath lookup mechanism
     984          tree = self.parse('''\
     985  <?xml version="1.0"?>
     986  <?xml-stylesheet type="text/xsl" href="#style"?>
     987  <a>
     988    <b>B</b>
     989    <c>C</c>
     990  </a>''')
     991
     992          style = self.parse('''\
     993  <xsl:stylesheet version="1.0" xml:id="style"
     994      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     995    <xsl:template match="*" />
     996    <xsl:template match="/">
     997      <foo><xsl:value-of select="/a/b/text()" /></foo>
     998    </xsl:template>
     999  </xsl:stylesheet>
    1000  ''')
    1001
    1002          tree.getroot().append(style.getroot())
    1003
    1004          style_root = tree.getroot().getprevious().parseXSL().getroot()
    1005          self.assertEqual("{http://www.w3.org/1999/XSL/Transform}stylesheet",
    1006                            style_root.tag)
    1007
    1008          st = etree.XSLT(style_root)
    1009          res = st(tree)
    1010          self.assertEqual('''\
    1011  <?xml version="1.0"?>
    1012  <foo>B</foo>
    1013  ''',
    1014                            str(res))
    1015
    1016 -    def test_xslt_pi_get(self):
    1017          tree = self.parse('''\
    1018  <?xml version="1.0"?>
    1019  <?xml-stylesheet type="text/xsl" href="TEST"?>
    1020  <a>
    1021    <b>B</b>
    1022    <c>C</c>
    1023  </a>''')
    1024
    1025          pi = tree.getroot().getprevious()
    1026          self.assertEqual("TEST", pi.get("href"))
    1027
    1028 -    def test_xslt_pi_get_all(self):
    1029          tree = self.parse('''\
    1030  <?xml version="1.0"?>
    1031  <?xml-stylesheet type="text/xsl" href="TEST"?>
    1032  <a>
    1033    <b>B</b>
    1034    <c>C</c>
    1035  </a>''')
    1036
    1037          pi = tree.getroot().getprevious()
    1038          self.assertEqual("TEST", pi.get("href"))
    1039          self.assertEqual("text/xsl", pi.get("type"))
    1040          self.assertEqual(None, pi.get("motz"))
    1041
    1042 -    def test_xslt_pi_get_all_reversed(self):
    1043          tree = self.parse('''\
    1044  <?xml version="1.0"?>
    1045  <?xml-stylesheet href="TEST" type="text/xsl"?>
    1046  <a>
    1047    <b>B</b>
    1048    <c>C</c>
    1049  </a>''')
    1050
    1051          pi = tree.getroot().getprevious()
    1052          self.assertEqual("TEST", pi.get("href"))
    1053          self.assertEqual("text/xsl", pi.get("type"))
    1054          self.assertEqual(None, pi.get("motz"))
    1055
    1056 -    def test_xslt_pi_get_unknown(self):
    1057          tree = self.parse('''\
    1058  <?xml version="1.0"?>
    1059  <?xml-stylesheet type="text/xsl" href="TEST"?>
    1060  <a>
    1061    <b>B</b>
    1062    <c>C</c>
    1063  </a>''')
    1064
    1065          pi = tree.getroot().getprevious()
    1066          self.assertEqual(None, pi.get("unknownattribute"))
    1067
    1068 -    def test_xslt_pi_set_replace(self):
    1069          tree = self.parse('''\
    1070  <?xml version="1.0"?>
    1071  <?xml-stylesheet type="text/xsl" href="TEST"?>
    1072  <a>
    1073    <b>B</b>
    1074    <c>C</c>
    1075  </a>''')
    1076
    1077          pi = tree.getroot().getprevious()
    1078          self.assertEqual("TEST", pi.get("href"))
    1079
    1080          pi.set("href", "TEST123")
    1081          self.assertEqual("TEST123", pi.get("href"))
    1082
    1083 -    def test_xslt_pi_set_new(self):
    1084          tree = self.parse('''\
    1085  <?xml version="1.0"?>
    1086  <?xml-stylesheet type="text/xsl"?>
    1087  <a>
    1088    <b>B</b>
    1089    <c>C</c>
    1090  </a>''')
    1091
    1092          pi = tree.getroot().getprevious()
    1093          self.assertEqual(None, pi.get("href"))
    1094
    1095          pi.set("href", "TEST")
    1096          self.assertEqual("TEST", pi.get("href"))
    1097
    1098 -class ETreeEXSLTTestCase(HelperTestCase):
    1099      """EXSLT tests"""
    1100
    1101 -    def test_exslt_str(self):
    1102          tree = self.parse('<a><b>B</b><c>C</c></a>')
    1103          style = self.parse('''\
    1104  <xsl:stylesheet version="1.0"
    1105      xmlns:str="http://exslt.org/strings"
    1106      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1107      exclude-result-prefixes="str xsl">
    1108    <xsl:template match="text()">
    1109      <xsl:value-of select="str:align(string(.), '***', 'center')" />
    1110    </xsl:template>
    1111    <xsl:template match="*">
    1112      <xsl:copy>
    1113        <xsl:apply-templates/>
    1114      </xsl:copy>
    1115    </xsl:template>
    1116  </xsl:stylesheet>''')
    1117
    1118          st = etree.XSLT(style)
    1119          res = st(tree)
    1120          self.assertEqual('''\
    1121  <?xml version="1.0"?>
    1122  <a><b>*B*</b><c>*C*</c></a>
    1123  ''',
    1124                            str(res))
    1125
    1126 -    def test_exslt_str_attribute_replace(self):
    1127          tree = self.parse('<a><b>B</b><c>C</c></a>')
    1128          style = self.parse('''\
    1129            <xsl:stylesheet version = "1.0"
    1130                xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
    1131                xmlns:str="http://exslt.org/strings"
    1132                extension-element-prefixes="str">
    1133
    1134                <xsl:template match="/">
    1135                  <h1 class="{str:replace('abc', 'b', 'x')}">test</h1>
    1136                </xsl:template>
    1137
    1138            </xsl:stylesheet>''')
    1139
    1140          st = etree.XSLT(style)
    1141          res = st(tree)
    1142          self.assertEqual(str(res), '''\
    1143  <?xml version="1.0"?>
    1144  <h1 class="axc">test</h1>
    1145  ''')
    1146
    1147 -    def test_exslt_math(self):
    1148          tree = self.parse('<a><b>B</b><c>C</c></a>')
    1149          style = self.parse('''\
    1150  <xsl:stylesheet version="1.0"
    1151      xmlns:math="http://exslt.org/math"
    1152      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1153      exclude-result-prefixes="math xsl">
    1154    <xsl:template match="*">
    1155      <xsl:copy>
    1156        <xsl:attribute name="pi">
    1157          <xsl:value-of select="math:constant('PI', count(*)+2)"/>
    1158        </xsl:attribute>
    1159        <xsl:apply-templates/>
    1160      </xsl:copy>
    1161    </xsl:template>
    1162  </xsl:stylesheet>''')
    1163
    1164          st = etree.XSLT(style)
    1165          res = st(tree)
    1166          self.assertEqual('''\
    1167  <?xml version="1.0"?>
    1168  <a pi="3.14"><b pi="3">B</b><c pi="3">C</c></a>
    1169  ''',
    1170                            str(res))
    1171
    1172 -    def test_exslt_regexp_test(self):
    1173          xslt = etree.XSLT(etree.XML(_bytes("""\
    1174  <xsl:stylesheet version="1.0"
    1175     xmlns:regexp="http://exslt.org/regular-expressions"
    1176     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    1177    <xsl:template match="*">
    1178      <test><xsl:copy-of select="*[regexp:test(string(.), '8.')]"/></test>
    1179    </xsl:template>
    1180  </xsl:stylesheet>
    1181  """)))
    1182          result = xslt(etree.XML(_bytes('<a><b>123</b><b>098</b><b>987</b></a>')))
    1183          root = result.getroot()
    1184          self.assertEqual(root.tag,
    1185                            'test')
    1186          self.assertEqual(len(root), 1)
    1187          self.assertEqual(root[0].tag,
    1188                            'b')
    1189          self.assertEqual(root[0].text,
    1190                            '987')
    1191
    1192 -    def test_exslt_regexp_replace(self):
    1193          xslt = etree.XSLT(etree.XML("""\
    1194  <xsl:stylesheet version="1.0"
    1195     xmlns:regexp="http://exslt.org/regular-expressions"
    1196     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    1197    <xsl:template match="*">
    1198      <test>
    1199        <xsl:copy-of select="regexp:replace(string(.), 'd.', '',   'XX')"/>
    1200        <xsl:text>-</xsl:text>
    1201        <xsl:copy-of select="regexp:replace(string(.), 'd.', 'gi', 'XX')"/>
    1202      </test>
    1203    </xsl:template>
    1204  </xsl:stylesheet>
    1205  """))
    1206          result = xslt(etree.XML(_bytes('<a>abdCdEeDed</a>')))
    1207          root = result.getroot()
    1208          self.assertEqual(root.tag,
    1209                            'test')
    1210          self.assertEqual(len(root), 0)
    1211          self.assertEqual(root.text, 'abXXdEeDed-abXXXXeXXd')
    1212
    1213 -    def test_exslt_regexp_match(self):
    1214          xslt = etree.XSLT(etree.XML("""\
    1215  <xsl:stylesheet version="1.0"
    1216     xmlns:regexp="http://exslt.org/regular-expressions"
    1217     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    1218    <xsl:template match="*">
    1219      <test>
    1220        <test1><xsl:copy-of  select="regexp:match(string(.), 'd.')"/></test1>
    1221        <test2><xsl:copy-of  select="regexp:match(string(.), 'd.', 'g')"/></test2>
    1222        <test2i><xsl:copy-of select="regexp:match(string(.), 'd.', 'gi')"/></test2i>
    1223      </test>
    1224    </xsl:template>
    1225  </xsl:stylesheet>
    1226  """))
    1227          result = xslt(etree.XML(_bytes('<a>abdCdEeDed</a>')))
    1228          root = result.getroot()
    1229          self.assertEqual(root.tag,  'test')
    1230          self.assertEqual(len(root), 3)
    1231
    1232          self.assertEqual(len(root[0]), 1)
    1233          self.assertEqual(root[0][0].tag, 'match')
    1234          self.assertEqual(root[0][0].text, 'dC')
    1235
    1236          self.assertEqual(len(root[1]), 2)
    1237          self.assertEqual(root[1][0].tag, 'match')
    1238          self.assertEqual(root[1][0].text, 'dC')
    1239          self.assertEqual(root[1][1].tag, 'match')
    1240          self.assertEqual(root[1][1].text, 'dE')
    1241
    1242          self.assertEqual(len(root[2]), 3)
    1243          self.assertEqual(root[2][0].tag, 'match')
    1244          self.assertEqual(root[2][0].text, 'dC')
    1245          self.assertEqual(root[2][1].tag, 'match')
    1246          self.assertEqual(root[2][1].text, 'dE')
    1247          self.assertEqual(root[2][2].tag, 'match')
    1248          self.assertEqual(root[2][2].text, 'De')
    1249
    1250 -    def test_exslt_regexp_match_groups(self):
    1251          xslt = etree.XSLT(etree.XML(_bytes("""\
    1252  <xsl:stylesheet version="1.0"
    1253     xmlns:regexp="http://exslt.org/regular-expressions"
    1254     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    1255    <xsl:template match="/">
    1256      <test>
    1257        <xsl:for-each select="regexp:match(
    1258              '123abc567', '([0-9]+)([a-z]+)([0-9]+)' )">
    1259          <test1><xsl:value-of select="."/></test1>
    1260        </xsl:for-each>
    1261      </test>
    1262    </xsl:template>
    1263  </xsl:stylesheet>
    1264  """)))
    1265          result = xslt(etree.XML(_bytes('<a/>')))
    1266          root = result.getroot()
    1267          self.assertEqual(root.tag,  'test')
    1268          self.assertEqual(len(root), 4)
    1269
    1270          self.assertEqual(root[0].text, "123abc567")
    1271          self.assertEqual(root[1].text, "123")
    1272          self.assertEqual(root[2].text, "abc")
    1273          self.assertEqual(root[3].text, "567")
    1274
    1275 -    def test_exslt_regexp_match1(self):
    1276          # taken from http://www.exslt.org/regexp/functions/match/index.html
    1277          xslt = etree.XSLT(etree.XML(_bytes("""\
    1278  <xsl:stylesheet version="1.0"
    1279     xmlns:regexp="http://exslt.org/regular-expressions"
    1280     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    1281    <xsl:template match="/">
    1282      <test>
    1283        <xsl:for-each select="regexp:match(
    1284              'http://www.bayes.co.uk/xml/index.xml?/xml/utils/rechecker.xml',
    1285              '(\\w+):\\/\\/([^/:]+)(:\\d*)?([^# ]*)')">
    1286          <test1><xsl:value-of select="."/></test1>
    1287        </xsl:for-each>
    1288      </test>
    1289    </xsl:template>
    1290  </xsl:stylesheet>
    1291  """)))
    1292          result = xslt(etree.XML(_bytes('<a/>')))
    1293          root = result.getroot()
    1294          self.assertEqual(root.tag,  'test')
    1295          self.assertEqual(len(root), 5)
    1296
    1297          self.assertEqual(
    1298              root[0].text,
    1299              "http://www.bayes.co.uk/xml/index.xml?/xml/utils/rechecker.xml")
    1300          self.assertEqual(
    1301              root[1].text,
    1302              "http")
    1303          self.assertEqual(
    1304              root[2].text,
    1305              "www.bayes.co.uk")
    1306          self.assertFalse(root[3].text)
    1307          self.assertEqual(
    1308              root[4].text,
    1309              "/xml/index.xml?/xml/utils/rechecker.xml")
    1310
    1311 -    def test_exslt_regexp_match2(self):
    1312          # taken from http://www.exslt.org/regexp/functions/match/index.html
    1313          xslt = etree.XSLT(self.parse("""\
    1314  <xsl:stylesheet version="1.0"
    1315     xmlns:regexp="http://exslt.org/regular-expressions"
    1316     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    1317    <xsl:template match="/">
    1318      <test>
    1319        <xsl:for-each select="regexp:match(
    1320              'This is a test string', '(\\w+)', 'g')">
    1321          <test1><xsl:value-of select="."/></test1>
    1322        </xsl:for-each>
    1323      </test>
    1324    </xsl:template>
    1325  </xsl:stylesheet>
    1326  """))
    1327          result = xslt(etree.XML(_bytes('<a/>')))
    1328          root = result.getroot()
    1329          self.assertEqual(root.tag,  'test')
    1330          self.assertEqual(len(root), 5)
    1331
    1332          self.assertEqual(root[0].text, "This")
    1333          self.assertEqual(root[1].text, "is")
    1334          self.assertEqual(root[2].text, "a")
    1335          self.assertEqual(root[3].text, "test")
    1336          self.assertEqual(root[4].text, "string")
    1337
    1338 -    def _test_exslt_regexp_match3(self):
    1339          # taken from http://www.exslt.org/regexp/functions/match/index.html
    1340          # THIS IS NOT SUPPORTED!
    1341          xslt = etree.XSLT(etree.XML(_bytes("""\
    1342  <xsl:stylesheet version="1.0"
    1343     xmlns:regexp="http://exslt.org/regular-expressions"
    1344     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    1345    <xsl:template match="/">
    1346      <test>
    1347        <xsl:for-each select="regexp:match(
    1348              'This is a test string', '([a-z])+ ', 'g')">
    1349          <test1><xsl:value-of select="."/></test1>
    1350        </xsl:for-each>
    1351      </test>
    1352    </xsl:template>
    1353  </xsl:stylesheet>
    1354  """)))
    1355          result = xslt(etree.XML(_bytes('<a/>')))
    1356          root = result.getroot()
    1357          self.assertEqual(root.tag,  'test')
    1358          self.assertEqual(len(root), 4)
    1359
    1360          self.assertEqual(root[0].text, "his")
    1361          self.assertEqual(root[1].text, "is")
    1362          self.assertEqual(root[2].text, "a")
    1363          self.assertEqual(root[3].text, "test")
    1364
    1365 -    def _test_exslt_regexp_match4(self):
    1366          # taken from http://www.exslt.org/regexp/functions/match/index.html
    1367          # THIS IS NOT SUPPORTED!
    1368          xslt = etree.XSLT(etree.XML(_bytes("""\
    1369  <xsl:stylesheet version="1.0"
    1370     xmlns:regexp="http://exslt.org/regular-expressions"
    1371     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    1372    <xsl:template match="/">
    1373      <test>
    1374        <xsl:for-each select="regexp:match(
    1375              'This is a test string', '([a-z])+ ', 'gi')">
    1376          <test1><xsl:value-of select="."/></test1>
    1377        </xsl:for-each>
    1378      </test>
    1379    </xsl:template>
    1380  </xsl:stylesheet>
    1381  """)))
    1382          result = xslt(etree.XML(_bytes('<a/>')))
    1383          root = result.getroot()
    1384          self.assertEqual(root.tag,  'test')
    1385          self.assertEqual(len(root), 4)
    1386
    1387          self.assertEqual(root[0].text, "This")
    1388          self.assertEqual(root[1].text, "is")
    1389          self.assertEqual(root[2].text, "a")
    1390          self.assertEqual(root[3].text, "test")
    1391
    1392
    1393 -class ETreeXSLTExtFuncTestCase(HelperTestCase):
    1394      """Tests for XPath extension functions in XSLT."""
    1395
    1396 -    def test_extensions1(self):
    1397          tree = self.parse('<a><b>B</b></a>')
    1398          style = self.parse('''\
    1399  <xsl:stylesheet version="1.0"
    1400      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1401      xmlns:myns="testns"
    1402      exclude-result-prefixes="myns">
    1403    <xsl:template match="a"><A><xsl:value-of select="myns:mytext(b)"/></A></xsl:template>
    1404  </xsl:stylesheet>''')
    1405
    1406          def mytext(ctxt, values):
    1407              return 'X' * len(values)
    1408
    1409          result = tree.xslt(style, {('testns', 'mytext') : mytext})
    1410          self.assertEqual(self._rootstring(result),
    1411                            _bytes('<A>X</A>'))
    1412
    1413 -    def test_extensions2(self):
    1414          tree = self.parse('<a><b>B</b></a>')
    1415          style = self.parse('''\
    1416  <xsl:stylesheet version="1.0"
    1417      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1418      xmlns:myns="testns"
    1419      exclude-result-prefixes="myns">
    1420    <xsl:template match="a"><A><xsl:value-of select="myns:mytext(b)"/></A></xsl:template>
    1421  </xsl:stylesheet>''')
    1422
    1423          def mytext(ctxt, values):
    1424              return 'X' * len(values)
    1425
    1426          namespace = etree.FunctionNamespace('testns')
    1427          namespace['mytext'] = mytext
    1428
    1429          result = tree.xslt(style)
    1430          self.assertEqual(self._rootstring(result),
    1431                            _bytes('<A>X</A>'))
    1432
    1433 -    def test_variable_result_tree_fragment(self):
    1434          tree = self.parse('<a><b>B</b><b/></a>')
    1435          style = self.parse('''\
    1436  <xsl:stylesheet version="1.0"
    1437      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1438      xmlns:myns="testns"
    1439      exclude-result-prefixes="myns">
    1440    <xsl:template match="a">
    1441      <xsl:variable name="content">
    1442         <xsl:apply-templates/>
    1443      </xsl:variable>
    1444      <A><xsl:value-of select="myns:mytext($content)"/></A>
    1445    </xsl:template>
    1446    <xsl:template match="b"><xsl:copy>BBB</xsl:copy></xsl:template>
    1447  </xsl:stylesheet>''')
    1448
    1449          def mytext(ctxt, values):
    1450              for value in values:
    1451                  self.assertTrue(hasattr(value, 'tag'),
    1452                               "%s is not an Element" % type(value))
    1453                  self.assertEqual(value.tag, 'b')
    1454                  self.assertEqual(value.text, 'BBB')
    1455              return 'X'.join([el.tag for el in values])
    1456
    1457          namespace = etree.FunctionNamespace('testns')
    1458          namespace['mytext'] = mytext
    1459
    1460          result = tree.xslt(style)
    1461          self.assertEqual(self._rootstring(result),
    1462                           _bytes('<A>bXb</A>'))
    1463
    1464 -    def test_xpath_on_context_node(self):
    1465          tree = self.parse('<a><b>B<c/>C</b><b/></a>')
    1466          style = self.parse('''\
    1467  <xsl:stylesheet version="1.0"
    1468      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1469      xmlns:myns="testns"
    1470      exclude-result-prefixes="myns">
    1471    <xsl:template match="b">
    1472      <A><xsl:value-of select="myns:myext()"/></A>
    1473    </xsl:template>
    1474  </xsl:stylesheet>''')
    1475
    1476          def extfunc(ctxt):
    1477              text_content = ctxt.context_node.xpath('text()')
    1478              return 'x'.join(text_content)
    1479
    1480          namespace = etree.FunctionNamespace('testns')
    1481          namespace['myext'] = extfunc
    1482
    1483          result = tree.xslt(style)
    1484          self.assertEqual(self._rootstring(result),
    1485                           _bytes('<A>BxC</A>'))
    1486
    1487 -    def test_xpath_on_foreign_context_node(self):
    1488          # LP ticket 1354652
    1489          class Resolver(etree.Resolver):
    1490              def resolve(self, system_url, public_id, context):
    1491                  assert system_url == 'extdoc.xml'
    1492                  return self.resolve_string(b'<a><b>B<c/>C</b><b/></a>', context)
    1493
    1494          parser = etree.XMLParser()
    1495          parser.resolvers.add(Resolver())
    1496
    1497          tree = self.parse(b'<a><b/><b/></a>')
    1498          transform = etree.XSLT(self.parse(b'''\
    1499  <xsl:stylesheet version="1.0"
    1500      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1501      xmlns:mypre="testns"
    1502      exclude-result-prefixes="mypre">
    1503    <xsl:template match="b">
    1504      <B><xsl:value-of select="mypre:myext()"/></B>
    1505    </xsl:template>
    1506    <xsl:template match="a">
    1507      <A><xsl:apply-templates select="document('extdoc.xml')//b" /></A>
    1508    </xsl:template>
    1509  </xsl:stylesheet>''', parser=parser))
    1510
    1511          def extfunc(ctxt):
    1512              text_content = ctxt.context_node.xpath('text()')
    1513              return 'x'.join(text_content)
    1514
    1515          namespace = etree.FunctionNamespace('testns')
    1516          namespace['myext'] = extfunc
    1517
    1518          result = transform(tree)
    1519          self.assertEqual(self._rootstring(result),
    1520                           _bytes('<A><B>BxC</B><B/></A>'))
    1521
    1522
    1523 -class ETreeXSLTExtElementTestCase(HelperTestCase):
    1524      """Tests for extension elements in XSLT."""
    1525
    1526 -    def test_extension_element(self):
    1527          tree = self.parse('<a><b>B</b></a>')
    1528          style = self.parse('''\
    1529  <xsl:stylesheet version="1.0"
    1530      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1531      xmlns:myns="testns"
    1532      extension-element-prefixes="myns"
    1533      exclude-result-prefixes="myns">
    1534    <xsl:template match="a">
    1535      <A><myns:myext>b</myns:myext></A>
    1536    </xsl:template>
    1537  </xsl:stylesheet>''')
    1538
    1539          class MyExt(etree.XSLTExtension):
    1540              def execute(self, context, self_node, input_node, output_parent):
    1541                  child = etree.Element(self_node.text)
    1542                  child.text = 'X'
    1543                  output_parent.append(child)
    1544
    1545          extensions = { ('testns', 'myext') : MyExt() }
    1546
    1547          result = tree.xslt(style, extensions=extensions)
    1548          self.assertEqual(self._rootstring(result),
    1549                            _bytes('<A><b>X</b></A>'))
    1550
    1551 -    def test_extension_element_doc_context(self):
    1552          tree = self.parse('<a><b>B</b></a>')
    1553          style = self.parse('''\
    1554  <xsl:stylesheet version="1.0"
    1555      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1556      xmlns:myns="testns"
    1557      extension-element-prefixes="myns"
    1558      exclude-result-prefixes="myns">
    1559    <xsl:template match="/">
    1560      <A><myns:myext>b</myns:myext></A>
    1561    </xsl:template>
    1562  </xsl:stylesheet>''')
    1563
    1564          tags = []
    1565
    1566          class MyExt(etree.XSLTExtension):
    1567              def execute(self, context, self_node, input_node, output_parent):
    1568                  tags.append(input_node.tag)
    1569
    1570          extensions = { ('testns', 'myext') : MyExt() }
    1571
    1572          result = tree.xslt(style, extensions=extensions)
    1573          self.assertEqual(tags, ['a'])
    1574
    1575 -    def test_extension_element_comment_pi_context(self):
    1576          tree = self.parse('<?test toast?><a><!--a comment--><?another pi?></a>')
    1577          style = self.parse('''\
    1578  <xsl:stylesheet version="1.0"
    1579      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1580      xmlns:myns="testns"
    1581      extension-element-prefixes="myns"
    1582      exclude-result-prefixes="myns">
    1583    <xsl:template match="/">
    1584      <ROOT><xsl:apply-templates /></ROOT>
    1585    </xsl:template>
    1586    <xsl:template match="comment()">
    1587      <A><myns:myext>b</myns:myext></A>
    1588    </xsl:template>
    1589    <xsl:template match="processing-instruction()">
    1590      <A><myns:myext>b</myns:myext></A>
    1591    </xsl:template>
    1592  </xsl:stylesheet>''')
    1593
    1594          text = []
    1595
    1596          class MyExt(etree.XSLTExtension):
    1597              def execute(self, context, self_node, input_node, output_parent):
    1598                  text.append(input_node.text)
    1599
    1600          extensions = { ('testns', 'myext') : MyExt() }
    1601
    1602          result = tree.xslt(style, extensions=extensions)
    1603          self.assertEqual(text, ['toast', 'a comment', 'pi'])
    1604
    1605 -    def _test_extension_element_attribute_context(self):
    1606          # currently not supported
    1607          tree = self.parse('<a test="A"><b attr="B"/></a>')
    1608          style = self.parse('''\
    1609  <xsl:stylesheet version="1.0"
    1610      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1611      xmlns:myns="testns"
    1612      extension-element-prefixes="myns"
    1613      exclude-result-prefixes="myns">
    1614    <xsl:template match="@test">
    1615      <A><myns:myext>b</myns:myext></A>
    1616    </xsl:template>
    1617    <xsl:template match="@attr">
    1618      <A><myns:myext>b</myns:myext></A>
    1619    </xsl:template>
    1620  </xsl:stylesheet>''')
    1621
    1622          text = []
    1623
    1624          class MyExt(etree.XSLTExtension):
    1625              def execute(self, context, self_node, attr_value, output_parent):
    1626                  text.append(attr_value)
    1627
    1628          extensions = { ('testns', 'myext') : MyExt() }
    1629
    1630          result = tree.xslt(style, extensions=extensions)
    1631          self.assertEqual(text, ['A', 'B'])
    1632
    1633 -    def test_extension_element_content(self):
    1634          tree = self.parse('<a><b>B</b></a>')
    1635          style = self.parse('''\
    1636  <xsl:stylesheet version="1.0"
    1637      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1638      xmlns:myns="testns"
    1639      extension-element-prefixes="myns">
    1640    <xsl:template match="a">
    1641      <A><myns:myext><x>X</x><y>Y</y><z/></myns:myext></A>
    1642    </xsl:template>
    1643  </xsl:stylesheet>''')
    1644
    1645          class MyExt(etree.XSLTExtension):
    1646              def execute(self, context, self_node, input_node, output_parent):
    1647                  output_parent.extend(list(self_node)[1:])
    1648
    1649          extensions = { ('testns', 'myext') : MyExt() }
    1650
    1651          result = tree.xslt(style, extensions=extensions)
    1652          self.assertEqual(self._rootstring(result),
    1653                            _bytes('<A><y>Y</y><z/></A>'))
    1654
    1655 -    def test_extension_element_apply_templates(self):
    1656          tree = self.parse('<a><b>B</b></a>')
    1657          style = self.parse('''\
    1658  <xsl:stylesheet version="1.0"
    1659      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1660      xmlns:myns="testns"
    1661      extension-element-prefixes="myns">
    1662    <xsl:template match="a">
    1663      <A><myns:myext><x>X</x><y>Y</y><z/></myns:myext></A>
    1664    </xsl:template>
    1665    <xsl:template match="x" />
    1666    <xsl:template match="z">XYZ</xsl:template>
    1667  </xsl:stylesheet>''')
    1668
    1669          class MyExt(etree.XSLTExtension):
    1670              def execute(self, context, self_node, input_node, output_parent):
    1671                  for child in self_node:
    1672                      for result in self.apply_templates(context, child):
    1673                          if isinstance(result, basestring):
    1674                              el = etree.Element("T")
    1675                              el.text = result
    1676                          else:
    1677                              el = result
    1678                          output_parent.append(el)
    1679
    1680          extensions = { ('testns', 'myext') : MyExt() }
    1681
    1682          result = tree.xslt(style, extensions=extensions)
    1683          self.assertEqual(self._rootstring(result),
    1684                            _bytes('<A><T>Y</T><T>XYZ</T></A>'))
    1685
    1686 -    def test_extension_element_apply_templates_elements_only(self):
    1687          tree = self.parse('<a><b>B</b></a>')
    1688          style = self.parse('''\
    1689  <xsl:stylesheet version="1.0"
    1690      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1691      xmlns:myns="testns"
    1692      extension-element-prefixes="myns">
    1693    <xsl:template match="a">
    1694      <A><myns:myext><x>X</x><y>Y</y><z/></myns:myext></A>
    1695    </xsl:template>
    1696    <xsl:template match="x"><X/></xsl:template>
    1697    <xsl:template match="z">XYZ</xsl:template>
    1698  </xsl:stylesheet>''')
    1699
    1700          class MyExt(etree.XSLTExtension):
    1701              def execute(self, context, self_node, input_node, output_parent):
    1702                  for child in self_node:
    1703                      for result in self.apply_templates(context, child,
    1704                                                         elements_only=True):
    1705                          assert not isinstance(result, basestring)
    1706                          output_parent.append(result)
    1707
    1708          extensions = { ('testns', 'myext') : MyExt() }
    1709
    1710          result = tree.xslt(style, extensions=extensions)
    1711          self.assertEqual(self._rootstring(result),
    1712                            _bytes('<A><X/></A>'))
    1713
    1714 -    def test_extension_element_apply_templates_remove_blank_text(self):
    1715          tree = self.parse('<a><b>B</b></a>')
    1716          style = self.parse('''\
    1717  <xsl:stylesheet version="1.0"
    1718      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1719      xmlns:myns="testns"
    1720      extension-element-prefixes="myns">
    1721    <xsl:template match="a">
    1722      <A><myns:myext><x>X</x><y>Y</y><z/></myns:myext></A>
    1723    </xsl:template>
    1724    <xsl:template match="x"><X/></xsl:template>
    1725    <xsl:template match="y"><xsl:text>   </xsl:text></xsl:template>
    1726    <xsl:template match="z">XYZ</xsl:template>
    1727  </xsl:stylesheet>''')
    1728
    1729          class MyExt(etree.XSLTExtension):
    1730              def execute(self, context, self_node, input_node, output_parent):
    1731                  for child in self_node:
    1732                      for result in self.apply_templates(context, child,
    1733                                                         remove_blank_text=True):
    1734                          if isinstance(result, basestring):
    1735                              assert result.strip()
    1736                              el = etree.Element("T")
    1737                              el.text = result
    1738                          else:
    1739                              el = result
    1740                          output_parent.append(el)
    1741
    1742          extensions = { ('testns', 'myext') : MyExt() }
    1743
    1744          result = tree.xslt(style, extensions=extensions)
    1745          self.assertEqual(self._rootstring(result),
    1746                            _bytes('<A><X/><T>XYZ</T></A>'))
    1747
    1748 -    def test_extension_element_apply_templates_target_node(self):
    1749          tree = self.parse('<a><b>B</b></a>')
    1750          style = self.parse('''\
    1751  <xsl:stylesheet version="1.0"
    1752      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1753      xmlns:myns="testns"
    1754      extension-element-prefixes="myns">
    1755    <xsl:template match="a">
    1756      <A><myns:myext><x>X</x><y>Y</y><z/></myns:myext></A>
    1757    </xsl:template>
    1758    <xsl:template match="x" />
    1759    <xsl:template match="z">XYZ</xsl:template>
    1760  </xsl:stylesheet>''')
    1761
    1762          class MyExt(etree.XSLTExtension):
    1763              def execute(self, context, self_node, input_node, output_parent):
    1764                  for child in self_node:
    1765                      self.apply_templates(context, child, output_parent)
    1766
    1767          extensions = { ('testns', 'myext') : MyExt() }
    1768
    1769          result = tree.xslt(style, extensions=extensions)
    1770          self.assertEqual(self._rootstring(result),
    1771                            _bytes('<A>YXYZ</A>'))
    1772
    1773 -    def test_extension_element_apply_templates_target_node_doc(self):
    1774          tree = self.parse('<a><b>B</b></a>')
    1775          style = self.parse('''\
    1776  <xsl:stylesheet version="1.0"
    1777      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1778      xmlns:myns="testns"
    1779      extension-element-prefixes="myns">
    1780    <xsl:template match="a">
    1781      <myns:myext><x>X</x><y>Y</y><z/></myns:myext>
    1782    </xsl:template>
    1783    <xsl:template match="x"><xsl:processing-instruction name="test">TEST</xsl:processing-instruction></xsl:template>
    1784    <xsl:template match="y"><Y>XYZ</Y></xsl:template>
    1785    <xsl:template match="z"><xsl:comment>TEST</xsl:comment></xsl:template>
    1786  </xsl:stylesheet>''')
    1787
    1788          class MyExt(etree.XSLTExtension):
    1789              def execute(self, context, self_node, input_node, output_parent):
    1790                  for child in self_node:
    1791                      self.apply_templates(context, child, output_parent)
    1792
    1793          extensions = { ('testns', 'myext') : MyExt() }
    1794
    1795          result = tree.xslt(style, extensions=extensions)
    1796          self.assertEqual(etree.tostring(result),
    1797                            _bytes('<?test TEST?><Y>XYZ</Y><!--TEST-->'))
    1798
    1799 -    def test_extension_element_process_children(self):
    1800          tree = self.parse('<a><b>E</b></a>')
    1801          style = self.parse('''\
    1802  <xsl:stylesheet version="1.0"
    1803      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1804      xmlns:myns="testns"
    1805      extension-element-prefixes="myns">
    1806    <xsl:template match="a">
    1807      <xsl:variable name="testvar">yo</xsl:variable>
    1808      <A>
    1809        <myns:myext>
    1810          <xsl:attribute name="attr">
    1811            <xsl:value-of select="$testvar" />
    1812          </xsl:attribute>
    1813          <B>
    1814            <xsl:choose>
    1815              <xsl:when test="1 = 2"><C/></xsl:when>
    1816              <xsl:otherwise><D><xsl:value-of select="b/text()" /></D></xsl:otherwise>
    1817            </xsl:choose>
    1818          </B>
    1819        </myns:myext>
    1820      </A>
    1821    </xsl:template>
    1822  </xsl:stylesheet>''')
    1823
    1824          class MyExt(etree.XSLTExtension):
    1825              def execute(self, context, self_node, input_node, output_parent):
    1826                  el = etree.Element('MY')
    1827                  self.process_children(context, el)
    1828                  output_parent.append(el)
    1829
    1830          extensions = { ('testns', 'myext') : MyExt() }
    1831
    1832          result = tree.xslt(style, extensions=extensions)
    1833          self.assertEqual(self._rootstring(result),
    1834                            _bytes('<A><MYattr="yo"><B><D>E</D></B></MY></A>'))
    1835
    1836 -    def test_extension_element_process_children_to_append_only(self):
    1837          tree = self.parse('<a/>')
    1838          style = self.parse('''\
    1839  <xsl:stylesheet version="1.0"
    1840      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1841      xmlns:myns="testns"
    1842      extension-element-prefixes="myns">
    1843    <xsl:template match="a">
    1844      <myns:myext>
    1845        <A/>
    1846      </myns:myext>
    1847    </xsl:template>
    1848  </xsl:stylesheet>''')
    1849
    1850          class MyExt(etree.XSLTExtension):
    1851              def execute(self, context, self_node, input_node, output_parent):
    1852                  self.process_children(context, output_parent)
    1853
    1854          extensions = { ('testns', 'myext') : MyExt() }
    1855
    1856          result = tree.xslt(style, extensions=extensions)
    1857          self.assertEqual(self._rootstring(result),
    1858                            _bytes('<A/>'))
    1859
    1860 -    def test_extension_element_process_children_to_read_only_raise(self):
    1861          tree = self.parse('<a/>')
    1862          style = self.parse('''\
    1863  <xsl:stylesheet version="1.0"
    1864      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1865      xmlns:myns="testns"
    1866      extension-element-prefixes="myns">
    1867    <xsl:template match="a">
    1868      <myns:myext>
    1869        <A/>
    1870      </myns:myext>
    1871    </xsl:template>
    1872  </xsl:stylesheet>''')
    1873
    1874          class MyExt(etree.XSLTExtension):
    1875              def execute(self, context, self_node, input_node, output_parent):
    1876                  self.process_children(context, self_node)
    1877
    1878          extensions = { ('testns', 'myext') : MyExt() }
    1879
    1880          self.assertRaises(TypeError, tree.xslt, style, extensions=extensions)
    1881
    1882 -    def test_extension_element_process_children_with_subextension_element(self):
    1883          tree = self.parse('<a/>')
    1884          style = self.parse('''\
    1885  <xsl:stylesheet version="1.0"
    1886      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1887      xmlns:myns="testns"
    1888      extension-element-prefixes="myns">
    1889    <xsl:template match="a">
    1890      <myns:myext>
    1891        <A><myns:myext><B/></myns:myext></A>
    1892      </myns:myext>
    1893    </xsl:template>
    1894  </xsl:stylesheet>''')
    1895
    1896          class MyExt(etree.XSLTExtension):
    1897              callback_call_counter = 0
    1898              def execute(self, context, self_node, input_node, output_parent):
    1899                  self.callback_call_counter += 1
    1900                  el = etree.Element('MY', n=str(self.callback_call_counter))
    1901                  self.process_children(context, el)
    1902                  output_parent.append(el)
    1903
    1904          extensions = { ('testns', 'myext') : MyExt() }
    1905
    1906          result = tree.xslt(style, extensions=extensions)
    1907          self.assertEqual(self._rootstring(result),
    1908                            _bytes('<MYn="1"><A><MYn="2"><B/></MY></A></MY>'))
    1909
    1910 -    def test_extension_element_raise(self):
    1911          tree = self.parse('<a><b>B</b></a>')
    1912          style = self.parse('''\
    1913  <xsl:stylesheet version="1.0"
    1914      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    1915      xmlns:myns="testns"
    1916      extension-element-prefixes="myns"
    1917      exclude-result-prefixes="myns">
    1918    <xsl:template match="a">
    1919      <A><myns:myext>b</myns:myext></A>
    1920    </xsl:template>
    1921  </xsl:stylesheet>''')
    1922
    1923          class MyError(Exception):
    1924              pass
    1925
    1926          class MyExt(etree.XSLTExtension):
    1927              def execute(self, context, self_node, input_node, output_parent):
    1928                  raise MyError("expected!")
    1929
    1930          extensions = { ('testns', 'myext') : MyExt() }
    1931          self.assertRaises(MyError, tree.xslt, style, extensions=extensions)
    1932
    1933      # FIXME: DISABLED - implementation seems to be broken
    1934      # if someone cares enough about this feature, I take pull requests that fix it.
    1935 -    def _test_multiple_extension_elements_with_output_parent(self):
    1936          tree = self.parse("""\
    1937  <text>
    1938    <par>This is <format>arbitrary</format> text in a paragraph</par>
    1939  </text>""")
    1940          style = self.parse("""\
    1941  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my" extension-element-prefixes="my" version="1.0">
    1942    <xsl:template match="par">
    1943      <my:par><xsl:apply-templates /></my:par>
    1944    </xsl:template>
    1945    <xsl:template match="format">
    1946      <my:format><xsl:apply-templates /></my:format>
    1947    </xsl:template>
    1948  </xsl:stylesheet>
    1949  """)
    1950          test = self
    1951          calls = []
    1952
    1953          class ExtMyPar(etree.XSLTExtension):
    1954              def execute(self, context, self_node, input_node, output_parent):
    1955                  calls.append('par')
    1956                  p = etree.Element("p")
    1957                  p.attrib["style"] = "color:red"
    1958                  self.process_children(context, p)
    1959                  output_parent.append(p)
    1960
    1961          class ExtMyFormat(etree.XSLTExtension):
    1962              def execute(self, context, self_node, input_node, output_parent):
    1963                  calls.append('format')
    1964                  content = self.process_children(context)
    1965                  test.assertEqual(1, len(content))
    1966                  test.assertEqual('arbitrary', content[0])
    1967                  test.assertEqual('This is ', output_parent.text)
    1968                  output_parent.text += '*-%s-*' % content[0]
    1969
    1970          extensions = {("my", "par"): ExtMyPar(), ("my", "format"): ExtMyFormat()}
    1971          transform = etree.XSLT(style, extensions=extensions)
    1972          result = transform(tree)
    1973          self.assertEqual(['par', 'format'], calls)
    1974          self.assertEqual(
    1975              b'<p style="color:red">This is *-arbitrary-* text in a paragraph</p>\n',
    1976              etree.tostring(result))
    1977
    1978 -    def test_extensions_nsmap(self):
    1979          tree = self.parse("""\
    1980  <root>
    1981    <inner xmlns:sha256="http://www.w3.org/2001/04/xmlenc#sha256">
    1982      <data>test</data>
    1983    </inner>
    1984  </root>
    1985  """)
    1986          style = self.parse("""\
    1987  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="extns" extension-element-prefixes="my" version="1.0">
    1988    <xsl:template match="node()|@*">
    1989      <xsl:copy>
    1990        <xsl:apply-templates select="node()|@*"/>
    1991      </xsl:copy>
    1992    </xsl:template>
    1993
    1994    <xsl:template match="data">
    1995      <my:show-nsmap/>
    1996    </xsl:template>
    1997  </xsl:stylesheet>
    1998  """)
    1999          class MyExt(etree.XSLTExtension):
    2000              def execute(self, context, self_node, input_node, output_parent):
    2001                  output_parent.text = str(input_node.nsmap)
    2002
    2003          extensions = {('extns', 'show-nsmap'): MyExt()}
    2004
    2005          result = tree.xslt(style, extensions=extensions)
    2006          self.assertEqual(etree.tostring(result, pretty_print=True), b"""\
    2007  <root>
    2008    <inner xmlns:sha256="http://www.w3.org/2001/04/xmlenc#sha256">{'sha256': 'http://www.w3.org/2001/04/xmlenc#sha256'}
    2009    </inner>
    2010  </root>
    2011  """)
    2012
    2013
    2014
    2015 -class Py3XSLTTestCase(HelperTestCase):
    2016      """XSLT tests for etree under Python 3"""
    2017
    2018      pytestmark = skipif('sys.version_info < (3,0)')
    2019
    2020 -    def test_xslt_result_bytes(self):
    2021          tree = self.parse('<a><b>B</b><c>C</c></a>')
    2022          style = self.parse('''\
    2023  <xsl:stylesheet version="1.0"
    2024      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    2025    <xsl:template match="*" />
    2026    <xsl:template match="/">
    2027      <foo><xsl:value-of select="/a/b/text()" /></foo>
    2028    </xsl:template>
    2029  </xsl:stylesheet>''')
    2030
    2031          st = etree.XSLT(style)
    2032          res = st(tree)
    2033          self.assertEqual(_bytes('''\
    2034  <?xml version="1.0"?>
    2035  <foo>B</foo>
    2036  '''),
    2037                            bytes(res))
    2038
    2039 -    def test_xslt_result_bytearray(self):
    2040          tree = self.parse('<a><b>B</b><c>C</c></a>')
    2041          style = self.parse('''\
    2042  <xsl:stylesheet version="1.0"
    2043      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    2044    <xsl:template match="*" />
    2045    <xsl:template match="/">
    2046      <foo><xsl:value-of select="/a/b/text()" /></foo>
    2047    </xsl:template>
    2048  </xsl:stylesheet>''')
    2049
    2050          st = etree.XSLT(style)
    2051          res = st(tree)
    2052          self.assertEqual(_bytes('''\
    2053  <?xml version="1.0"?>
    2054  <foo>B</foo>
    2055  '''),
    2056                            bytearray(res))
    2057
    2058 -    def test_xslt_result_memoryview(self):
    2059          tree = self.parse('<a><b>B</b><c>C</c></a>')
    2060          style = self.parse('''\
    2061  <xsl:stylesheet version="1.0"
    2062      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    2063    <xsl:template match="*" />
    2064    <xsl:template match="/">
    2065      <foo><xsl:value-of select="/a/b/text()" /></foo>
    2066    </xsl:template>
    2067  </xsl:stylesheet>''')
    2068
    2069          st = etree.XSLT(style)
    2070          res = st(tree)
    2071          self.assertEqual(_bytes('''\
    2072  <?xml version="1.0"?>
    2073  <foo>B</foo>
    2074  '''),
    2075                            bytes(memoryview(res)))
    2076
    2077
    2078 -def test_suite():
    2079      suite = unittest.TestSuite()
    2080      suite.addTests([unittest.makeSuite(ETreeXSLTTestCase)])
    2081      suite.addTests([unittest.makeSuite(ETreeEXSLTTestCase)])
    2082      suite.addTests([unittest.makeSuite(ETreeXSLTExtFuncTestCase)])
    2083      suite.addTests([unittest.makeSuite(ETreeXSLTExtElementTestCase)])
    2084      if is_python3:
    2085          suite.addTests([unittest.makeSuite(Py3XSLTTestCase)])
    2086      suite.addTests(
    2087          [make_doctest('../../../doc/extensions.txt')])
    2088      suite.addTests(
    2089          [make_doctest('../../../doc/xpathxslt.txt')])
    2090      return suite
    2091
    2092  if __name__ == '__main__':
    2093      print('to test use test.py %s' % __file__)
    2094