1   
   2   
   3  """ 
   4  Test cases related to XSLT processing 
   5  """ 
   6   
   7  import io 
   8  import sys 
   9  import copy 
  10  import gzip 
  11  import os.path 
  12  import unittest 
  13  import contextlib 
  14  from textwrap import dedent 
  15  from tempfile import NamedTemporaryFile 
  16   
  17  this_dir = os.path.dirname(__file__) 
  18  if this_dir not in sys.path: 
  19      sys.path.insert(0, this_dir)  
  20   
  21  is_python3 = sys.version_info[0] >= 3 
  22   
  23  try: 
  24      unicode 
  25  except NameError:  
  26      unicode = str 
  27   
  28  try: 
  29      basestring 
  30  except NameError:  
  31      basestring = str 
  32   
  33  from .common_imports import etree, BytesIO, HelperTestCase, fileInTestDir 
  34  from .common_imports import doctest, _bytes, _str, make_doctest, skipif 
  37      """XSLT tests etree""" 
  38           
  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   
  60   
  63   
  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   
  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   
 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   
 140          with self._xslt_setup() as res: 
 141              res[0] = unicode(bytes(res[0]), 'UTF-16') 
 142              assert 'UTF-16' in res[0] 
  143   
 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   
 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   
 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   
 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   
 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 contextlib.closing(gzip.GzipFile(f.name)) as f: 
 195                      res[0] = f.read().decode("UTF-16") 
 196              finally: 
 197                  os.unlink(f.name) 
  198   
 200          expected = ''' 
 201              <?xml version="1.0"?> 
 202              <foo>\\uF8D2</foo> 
 203          ''' 
 204          with self._xslt_setup(expected=expected) as res: 
 205              res[0] = unicode(res[0]) 
  206   
 208          tree = self.parse(_bytes('<a><b>\\uF8D2</b><c>\\uF8D2</c></a>' 
 209          ).decode("unicode_escape")) 
 210          style = self.parse('''\ 
 211  <xsl:stylesheet version="1.0" 
 212      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 213    <xsl:output encoding="UTF-16" standalone="no"/> 
 214    <xsl:template match="/"> 
 215      <foo><xsl:value-of select="/a/b/text()" /></foo> 
 216    </xsl:template> 
 217  </xsl:stylesheet>''') 
 218   
 219          st = etree.XSLT(style) 
 220          res = st(tree) 
 221          expected = _bytes('''\ 
 222  <?xml version="1.0" standalone="no"?> 
 223  <foo>\\uF8D2</foo> 
 224  ''').decode("unicode_escape") 
 225          self.assertEqual(expected, 
 226                           unicode(res)) 
  227   
 240   
 257   
 259          style = self.parse('''\ 
 260  <xsl:stylesheet version="1.0" 
 261      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 262      <xsl:foo /> 
 263  </xsl:stylesheet>''') 
 264          self.assertRaises(etree.XSLTParseError, 
 265                            etree.XSLT, style) 
  266   
 268          tree = self.parse('<a/>') 
 269          style = self.parse('''\ 
 270  <xsl:stylesheet version="1.0" 
 271      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 272      <xsl:foo /> 
 273  </xsl:stylesheet>''') 
 274          self.assertRaises(etree.XSLTParseError, 
 275                            etree.XSLT, style) 
 276          exc = None 
 277          try: 
 278              etree.XSLT(tree) 
 279          except etree.XSLTParseError as e: 
 280              exc = e 
 281          else: 
 282              self.assertFalse(True, "XSLT processing should have failed but didn't") 
 283          self.assertTrue(exc is not None) 
 284          self.assertTrue(len(exc.error_log)) 
 285          for error in exc.error_log: 
 286              self.assertTrue(':ERROR:XSLT:' in str(error)) 
  287   
 289          tree = self.parse('<a><b>B</b><c>C</c></a>') 
 290          style = self.parse('''\ 
 291  <xsl:stylesheet version="1.0" 
 292      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 293    <xsl:template match="/"> 
 294      <foo><xsl:value-of select="$bar" /></foo> 
 295    </xsl:template> 
 296  </xsl:stylesheet>''') 
 297   
 298          st = etree.XSLT(style) 
 299          res = st(tree, bar="'Bar'") 
 300          self.assertEqual('''\ 
 301  <?xml version="1.0"?> 
 302  <foo>Bar</foo> 
 303  ''', 
 304                            str(res)) 
  305   
 307          tree = self.parse('<a><b>B</b><c>C</c></a>') 
 308          style = self.parse('''\ 
 309  <xsl:stylesheet version="1.0" 
 310      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 311    <xsl:template match="/"> 
 312      <foo><xsl:value-of select="$bar" /></foo> 
 313    </xsl:template> 
 314  </xsl:stylesheet>''') 
 315   
 316          st = etree.XSLT(style) 
 317          res = st(tree, bar=etree.XSLT.strparam('''it's me, "Bar"''')) 
 318          self.assertEqual('''\ 
 319  <?xml version="1.0"?> 
 320  <foo>it's me, "Bar"</foo> 
 321  ''', 
 322                            str(res)) 
  323   
 325          tree = self.parse('<a><b>B</b><c>C</c></a>') 
 326          style = self.parse('''\ 
 327  <xsl:stylesheet version="1.0" 
 328      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 329    <xsl:param name="bar"/> 
 330    <xsl:template match="/"> 
 331      <foo><xsl:value-of select="$bar" /></foo> 
 332    </xsl:template> 
 333  </xsl:stylesheet>''') 
 334   
 335          st = etree.XSLT(style) 
 336          res = self.assertRaises(etree.XSLTApplyError, 
 337                                  st, tree, bar="<test/>") 
 338          res = self.assertRaises(etree.XSLTApplyError, 
 339                                  st, tree, bar="....") 
  340   
 342           
 343          tree = self.parse('<a><b>B</b><c>C</c></a>') 
 344          style = self.parse('''\ 
 345  <xsl:stylesheet version="1.0" 
 346      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 347    <xsl:template match="/"> 
 348      <foo><xsl:value-of select="$bar" /></foo> 
 349    </xsl:template> 
 350  </xsl:stylesheet>''') 
 351   
 352          st = etree.XSLT(style) 
 353           
 354          self.assertRaises(etree.XSLTApplyError, st.apply, tree) 
  355   
 357          tree = self.parse('<a><b>B</b><c>C</c></a>') 
 358          style = self.parse('''\ 
 359  <xsl:stylesheet version="1.0" 
 360      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 361    <xsl:template match="*" /> 
 362    <xsl:template match="/"> 
 363      <foo><xsl:value-of select="$bar" /></foo> 
 364      <foo><xsl:value-of select="$baz" /></foo> 
 365    </xsl:template> 
 366  </xsl:stylesheet>''') 
 367   
 368          st = etree.XSLT(style) 
 369          res = st(tree, bar="'Bar'", baz="'Baz'") 
 370          self.assertEqual('''\ 
 371  <?xml version="1.0"?> 
 372  <foo>Bar</foo><foo>Baz</foo> 
 373  ''', 
 374                            str(res)) 
  375           
 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    <xsl:template match="/"> 
 383      <foo><xsl:value-of select="$bar" /></foo> 
 384    </xsl:template> 
 385  </xsl:stylesheet>''') 
 386   
 387          st = etree.XSLT(style) 
 388          res = st(tree, bar="/a/b/text()") 
 389          self.assertEqual('''\ 
 390  <?xml version="1.0"?> 
 391  <foo>B</foo> 
 392  ''', 
 393                            str(res)) 
  394   
 396          tree = self.parse('<a><b>B</b><c>C</c></a>') 
 397          style = self.parse('''\ 
 398  <xsl:stylesheet version="1.0" 
 399      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 400    <xsl:template match="*" /> 
 401    <xsl:template match="/"> 
 402      <foo><xsl:value-of select="$bar" /></foo> 
 403    </xsl:template> 
 404  </xsl:stylesheet>''') 
 405   
 406          st = etree.XSLT(style) 
 407          res = st(tree, bar=etree.XPath("/a/b/text()")) 
 408          self.assertEqual('''\ 
 409  <?xml version="1.0"?> 
 410  <foo>B</foo> 
 411  ''', 
 412                            str(res)) 
  413           
 415          tree = self.parse('<a><b>B</b><c>C</c></a>') 
 416          style = self.parse('''\ 
 417  <xsl:stylesheet version="1.0" 
 418      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 419    <xsl:param name="bar" select="'Default'" /> 
 420    <xsl:template match="*" /> 
 421    <xsl:template match="/"> 
 422      <foo><xsl:value-of select="$bar" /></foo> 
 423    </xsl:template> 
 424  </xsl:stylesheet>''') 
 425   
 426          st = etree.XSLT(style) 
 427          res = st(tree, bar="'Bar'") 
 428          self.assertEqual('''\ 
 429  <?xml version="1.0"?> 
 430  <foo>Bar</foo> 
 431  ''', 
 432                            str(res)) 
 433          res = st(tree) 
 434          self.assertEqual('''\ 
 435  <?xml version="1.0"?> 
 436  <foo>Default</foo> 
 437  ''', 
 438                            str(res)) 
  439           
 441          tree = self.parse('<a><b>B</b><c>C</c></a>') 
 442          style = self.parse('''\ 
 443  <xsl:stylesheet version="1.0" 
 444      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 445    <xsl:output method="html"/> 
 446    <xsl:strip-space elements="*"/> 
 447    <xsl:template match="/"> 
 448      <html><body><xsl:value-of select="/a/b/text()" /></body></html> 
 449    </xsl:template> 
 450  </xsl:stylesheet>''') 
 451   
 452          st = etree.XSLT(style) 
 453          res = st(tree) 
 454          self.assertEqual('<html><body>B</body></html>', 
 455                            str(res).strip()) 
  456   
 460   
 466   
 489   
 514   
 516           
 517          xml = '<blah/>' 
 518          xslt = ''' 
 519          <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
 520            <xsl:template match="/" /> 
 521          </xsl:stylesheet> 
 522          ''' 
 523   
 524          source = self.parse(xml) 
 525          styledoc = self.parse(xslt) 
 526          style = etree.XSLT(styledoc) 
 527          result = style(source) 
 528          self.assertEqual('', str(result)) 
  529   
 531          xml = '<blah/>' 
 532          xslt = ''' 
 533          <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
 534            <xsl:template match="/"> 
 535              <xsl:message>TEST TEST TEST</xsl:message> 
 536            </xsl:template> 
 537          </xsl:stylesheet> 
 538          ''' 
 539   
 540          source = self.parse(xml) 
 541          styledoc = self.parse(xslt) 
 542          style = etree.XSLT(styledoc) 
 543          result = style(source) 
 544          self.assertEqual('', str(result)) 
 545          self.assertTrue("TEST TEST TEST" in [entry.message 
 546                                            for entry in style.error_log]) 
  547   
 549          xml = '<blah/>' 
 550          xslt = ''' 
 551          <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
 552            <xsl:template match="/"> 
 553              <xsl:message terminate="yes">TEST TEST TEST</xsl:message> 
 554            </xsl:template> 
 555          </xsl:stylesheet> 
 556          ''' 
 557   
 558          source = self.parse(xml) 
 559          styledoc = self.parse(xslt) 
 560          style = etree.XSLT(styledoc) 
 561   
 562          self.assertRaises(etree.XSLTApplyError, style, source) 
 563          self.assertTrue("TEST TEST TEST" in [entry.message 
 564                                            for entry in style.error_log]) 
  565   
 567          tree = self.parse('<a><b>B</b><c>C</c></a>') 
 568          style = self.parse('''\ 
 569  <xsl:stylesheet version="1.0" 
 570      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 571    <xsl:template match="*" /> 
 572    <xsl:template match="/"> 
 573      <doc> 
 574      <foo><xsl:value-of select="$bar" /></foo> 
 575      <foo><xsl:value-of select="$baz" /></foo> 
 576      </doc> 
 577    </xsl:template> 
 578  </xsl:stylesheet>''') 
 579   
 580          result = tree.xslt(style, bar="'Bar'", baz="'Baz'") 
 581          self.assertEqual( 
 582              _bytes('<doc><foo>Bar</foo><foo>Baz</foo></doc>'), 
 583              etree.tostring(result.getroot())) 
  584           
 586          tree = self.parse('<a><b>B</b><c>C</c></a>') 
 587          style = self.parse('''\ 
 588  <xsl:stylesheet version="1.0" 
 589      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 590    <xsl:template match="a"><A><xsl:apply-templates/></A></xsl:template> 
 591    <xsl:template match="b"><B><xsl:apply-templates/></B></xsl:template> 
 592    <xsl:template match="c"><C><xsl:apply-templates/></C></xsl:template> 
 593  </xsl:stylesheet>''') 
 594   
 595          self.assertEqual(self._rootstring(tree), 
 596                            _bytes('<a><b>B</b><c>C</c></a>')) 
 597          result = tree.xslt(style) 
 598          self.assertEqual(self._rootstring(tree), 
 599                            _bytes('<a><b>B</b><c>C</c></a>')) 
 600          self.assertEqual(self._rootstring(result), 
 601                            _bytes('<A><B>B</B><C>C</C></A>')) 
 602   
 603          b_tree = etree.ElementTree(tree.getroot()[0]) 
 604          self.assertEqual(self._rootstring(b_tree), 
 605                            _bytes('<b>B</b>')) 
 606          result = b_tree.xslt(style) 
 607          self.assertEqual(self._rootstring(tree), 
 608                            _bytes('<a><b>B</b><c>C</c></a>')) 
 609          self.assertEqual(self._rootstring(result), 
 610                            _bytes('<B>B</B>')) 
 611   
 612          c_tree = etree.ElementTree(tree.getroot()[1]) 
 613          self.assertEqual(self._rootstring(c_tree), 
 614                            _bytes('<c>C</c>')) 
 615          result = c_tree.xslt(style) 
 616          self.assertEqual(self._rootstring(tree), 
 617                            _bytes('<a><b>B</b><c>C</c></a>')) 
 618          self.assertEqual(self._rootstring(result), 
 619                            _bytes('<C>C</C>')) 
  620   
 622           
 623          xslt = etree.XSLT(etree.XML("""\ 
 624  <xsl:stylesheet version="1.0" 
 625     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 626    <xsl:template match="/"> 
 627      <test>TEXT<xsl:copy-of select="document('')//test"/></test> 
 628    </xsl:template> 
 629  </xsl:stylesheet> 
 630  """)) 
 631          result = xslt(etree.XML('<a/>')) 
 632          root = result.getroot() 
 633          self.assertEqual(root.tag, 
 634                            'test') 
 635          self.assertEqual(root[0].tag, 
 636                            'test') 
 637          self.assertEqual(root[0].text, 
 638                            'TEXT') 
 639          self.assertEqual(root[0][0].tag, 
 640                            '{http://www.w3.org/1999/XSL/Transform}copy-of') 
  641   
 651   
 661   
 663          xslt = etree.XSLT(etree.XML("""\ 
 664  <xsl:stylesheet version="1.0" 
 665     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 666    <xsl:template match="/"> 
 667      <test>TEXT<xsl:copy-of select="document('uri:__junkfood__is__evil__')//test"/></test> 
 668    </xsl:template> 
 669  </xsl:stylesheet> 
 670  """)) 
 671   
 672          errors = None 
 673          try: 
 674              xslt(etree.XML('<a/>')) 
 675          except etree.XSLTApplyError as exc: 
 676              errors = exc.error_log 
 677          else: 
 678              self.assertFalse(True, "XSLT processing should have failed but didn't") 
 679   
 680          self.assertTrue(len(errors)) 
 681          for error in errors: 
 682              if ':ERROR:XSLT:' in str(error): 
 683                  break 
 684          else: 
 685              self.assertFalse(True, 'No XSLT errors found in error log:\n%s' % errors) 
  686   
 688           
 689          assertEqual = self.assertEqual 
 690          called = {'count' : 0} 
 691          class TestResolver(etree.Resolver): 
 692              def resolve(self, url, id, context): 
 693                  assertEqual(url, 'file://ANYTHING') 
 694                  called['count'] += 1 
 695                  return self.resolve_string('<CALLED/>', context) 
  696   
 697          parser = etree.XMLParser() 
 698          parser.resolvers.add(TestResolver()) 
 699   
 700          xslt = etree.XSLT(etree.XML(_bytes("""\ 
 701  <xsl:stylesheet version="1.0" 
 702     xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
 703     xmlns:l="local"> 
 704    <xsl:template match="/"> 
 705      <test> 
 706        <xsl:for-each select="document('')//l:data/l:entry"> 
 707          <xsl:copy-of select="document('file://ANYTHING')"/> 
 708          <xsl:copy> 
 709            <xsl:attribute name="value"> 
 710              <xsl:value-of select="."/> 
 711            </xsl:attribute> 
 712          </xsl:copy> 
 713        </xsl:for-each> 
 714      </test> 
 715    </xsl:template> 
 716    <l:data> 
 717      <l:entry>A</l:entry> 
 718      <l:entry>B</l:entry> 
 719    </l:data> 
 720  </xsl:stylesheet> 
 721  """), parser)) 
 722   
 723          self.assertEqual(called['count'], 0) 
 724          result = xslt(etree.XML('<a/>')) 
 725          self.assertEqual(called['count'], 1) 
 726   
 727          root = result.getroot() 
 728          self.assertEqual(root.tag, 
 729                            'test') 
 730          self.assertEqual(len(root), 4) 
 731   
 732          self.assertEqual(root[0].tag, 
 733                            'CALLED') 
 734          self.assertEqual(root[1].tag, 
 735                            '{local}entry') 
 736          self.assertEqual(root[1].text, 
 737                            None) 
 738          self.assertEqual(root[1].get("value"), 
 739                            'A') 
 740          self.assertEqual(root[2].tag, 
 741                            'CALLED') 
 742          self.assertEqual(root[3].tag, 
 743                            '{local}entry') 
 744          self.assertEqual(root[3].text, 
 745                            None) 
 746          self.assertEqual(root[3].get("value"), 
 747                            'B') 
 748   
 750          assertEqual = self.assertEqual 
 751          called = {'count' : 0} 
 752          expected_url = None 
 753          class TestResolver(etree.Resolver): 
 754              def resolve(self, url, id, context): 
 755                  assertEqual(url, expected_url) 
 756                  called['count'] += 1 
 757                  return self.resolve_string('<CALLED/>', context) 
  758   
 759          stylesheet_xml = _bytes("""\ 
 760  <xsl:stylesheet version="1.0" 
 761     xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
 762     xmlns:l="local"> 
 763    <xsl:template match="/"> 
 764      <xsl:copy-of select="document('test.xml')"/> 
 765    </xsl:template> 
 766  </xsl:stylesheet> 
 767  """) 
 768   
 769          parser = etree.XMLParser() 
 770          parser.resolvers.add(TestResolver()) 
 771   
 772           
 773          expected_url = 'test.xml' 
 774          xslt = etree.XSLT(etree.XML(stylesheet_xml, parser)) 
 775   
 776          self.assertEqual(called['count'], 0) 
 777          result = xslt(etree.XML('<a/>')) 
 778          self.assertEqual(called['count'], 1) 
 779   
 780           
 781          called['count'] = 0 
 782          expected_url = 'MY/BASE/test.xml'   
 783          xslt = etree.XSLT(etree.XML( 
 784              stylesheet_xml, parser, 
 785              base_url=os.path.join('MY', 'BASE', 'FILE'))) 
 786   
 787          self.assertEqual(called['count'], 0) 
 788          result = xslt(etree.XML('<a/>')) 
 789          self.assertEqual(called['count'], 1) 
 790   
 791           
 792          called['count'] = 0 
 793          expected_url = 'http://server.com/BASE/DIR/test.xml' 
 794          xslt = etree.XSLT(etree.XML( 
 795              stylesheet_xml, parser, 
 796              base_url='http://server.com/BASE/DIR/FILE')) 
 797   
 798          self.assertEqual(called['count'], 0) 
 799          result = xslt(etree.XML('<a/>')) 
 800          self.assertEqual(called['count'], 1) 
 801   
 802           
 803          called['count'] = 0 
 804          expected_url = 'file://BASE/DIR/test.xml' 
 805          xslt = etree.XSLT(etree.XML( 
 806              stylesheet_xml, parser, 
 807              base_url='file://BASE/DIR/FILE')) 
 808   
 809          self.assertEqual(called['count'], 0) 
 810          result = xslt(etree.XML('<a/>')) 
 811          self.assertEqual(called['count'], 1) 
 812   
 823   
 829   
 835   
 844   
 846          root = etree.XML(_bytes('''\ 
 847          <transform> 
 848            <widget displayType="fieldset"/> 
 849          </transform>''')) 
 850   
 851          xslt = etree.XSLT(etree.XML(_bytes('''\ 
 852          <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 853            <xsl:output method="html" indent="no"/> 
 854            <xsl:template match="/"> 
 855              <html> 
 856                <xsl:apply-templates/> 
 857              </html> 
 858            </xsl:template> 
 859   
 860            <xsl:template match="widget"> 
 861              <xsl:element name="{@displayType}"/> 
 862            </xsl:template> 
 863   
 864          </xsl:stylesheet>'''))) 
 865   
 866          result = xslt(root[0]) 
 867          root[:] = result.getroot()[:] 
 868          del root  
  869           
 871          tree = self.parse('''\ 
 872  <?xml version="1.0"?> 
 873  <?xml-stylesheet type="text/xsl" href="%s"?> 
 874  <a> 
 875    <b>B</b> 
 876    <c>C</c> 
 877  </a>''' % fileInTestDir("test1.xslt")) 
 878   
 879          style_root = tree.getroot().getprevious().parseXSL().getroot() 
 880          self.assertEqual("{http://www.w3.org/1999/XSL/Transform}stylesheet", 
 881                            style_root.tag) 
  882   
 884           
 885          tree = self.parse('''\ 
 886  <?xml version="1.0"?> 
 887  <?xml-stylesheet type="text/xsl" href="#style"?> 
 888  <a> 
 889    <b>B</b> 
 890    <c>C</c> 
 891    <xsl:stylesheet version="1.0" xml:id="style" 
 892        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 893      <xsl:template match="*" /> 
 894      <xsl:template match="/"> 
 895        <foo><xsl:value-of select="/a/b/text()" /></foo> 
 896      </xsl:template> 
 897    </xsl:stylesheet> 
 898  </a>''') 
 899   
 900          style_root = tree.getroot().getprevious().parseXSL().getroot() 
 901          self.assertEqual("{http://www.w3.org/1999/XSL/Transform}stylesheet", 
 902                            style_root.tag) 
 903   
 904          st = etree.XSLT(style_root) 
 905          res = st(tree) 
 906          self.assertEqual('''\ 
 907  <?xml version="1.0"?> 
 908  <foo>B</foo> 
 909  ''', 
 910                            str(res)) 
  911   
 913           
 914          tree = self.parse('''\ 
 915  <?xml version="1.0"?> 
 916  <?xml-stylesheet type="text/xsl" href="#style"?> 
 917  <a> 
 918    <b>B</b> 
 919    <c>C</c> 
 920  </a>''') 
 921   
 922          style = self.parse('''\ 
 923  <xsl:stylesheet version="1.0" xml:id="style" 
 924      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 925    <xsl:template match="*" /> 
 926    <xsl:template match="/"> 
 927      <foo><xsl:value-of select="/a/b/text()" /></foo> 
 928    </xsl:template> 
 929  </xsl:stylesheet> 
 930  ''') 
 931   
 932          tree.getroot().append(style.getroot()) 
 933   
 934          style_root = tree.getroot().getprevious().parseXSL().getroot() 
 935          self.assertEqual("{http://www.w3.org/1999/XSL/Transform}stylesheet", 
 936                            style_root.tag) 
 937   
 938          st = etree.XSLT(style_root) 
 939          res = st(tree) 
 940          self.assertEqual('''\ 
 941  <?xml version="1.0"?> 
 942  <foo>B</foo> 
 943  ''', 
 944                            str(res)) 
  945   
 947          tree = self.parse('''\ 
 948  <?xml version="1.0"?> 
 949  <?xml-stylesheet type="text/xsl" href="TEST"?> 
 950  <a> 
 951    <b>B</b> 
 952    <c>C</c> 
 953  </a>''') 
 954   
 955          pi = tree.getroot().getprevious() 
 956          self.assertEqual("TEST", pi.get("href")) 
  957   
 959          tree = self.parse('''\ 
 960  <?xml version="1.0"?> 
 961  <?xml-stylesheet type="text/xsl" href="TEST"?> 
 962  <a> 
 963    <b>B</b> 
 964    <c>C</c> 
 965  </a>''') 
 966   
 967          pi = tree.getroot().getprevious() 
 968          self.assertEqual("TEST", pi.get("href")) 
 969          self.assertEqual("text/xsl", pi.get("type")) 
 970          self.assertEqual(None, pi.get("motz")) 
  971   
 973          tree = self.parse('''\ 
 974  <?xml version="1.0"?> 
 975  <?xml-stylesheet href="TEST" type="text/xsl"?> 
 976  <a> 
 977    <b>B</b> 
 978    <c>C</c> 
 979  </a>''') 
 980   
 981          pi = tree.getroot().getprevious() 
 982          self.assertEqual("TEST", pi.get("href")) 
 983          self.assertEqual("text/xsl", pi.get("type")) 
 984          self.assertEqual(None, pi.get("motz")) 
  985   
 987          tree = self.parse('''\ 
 988  <?xml version="1.0"?> 
 989  <?xml-stylesheet type="text/xsl" href="TEST"?> 
 990  <a> 
 991    <b>B</b> 
 992    <c>C</c> 
 993  </a>''') 
 994   
 995          pi = tree.getroot().getprevious() 
 996          self.assertEqual(None, pi.get("unknownattribute")) 
  997   
 999          tree = self.parse('''\ 
1000  <?xml version="1.0"?> 
1001  <?xml-stylesheet type="text/xsl" href="TEST"?> 
1002  <a> 
1003    <b>B</b> 
1004    <c>C</c> 
1005  </a>''') 
1006   
1007          pi = tree.getroot().getprevious() 
1008          self.assertEqual("TEST", pi.get("href")) 
1009   
1010          pi.set("href", "TEST123") 
1011          self.assertEqual("TEST123", pi.get("href")) 
 1012   
1014          tree = self.parse('''\ 
1015  <?xml version="1.0"?> 
1016  <?xml-stylesheet type="text/xsl"?> 
1017  <a> 
1018    <b>B</b> 
1019    <c>C</c> 
1020  </a>''') 
1021   
1022          pi = tree.getroot().getprevious() 
1023          self.assertEqual(None, pi.get("href")) 
1024   
1025          pi.set("href", "TEST") 
1026          self.assertEqual("TEST", pi.get("href")) 
 1027   
1029      """EXSLT tests""" 
1030   
1032          tree = self.parse('<a><b>B</b><c>C</c></a>') 
1033          style = self.parse('''\ 
1034  <xsl:stylesheet version="1.0" 
1035      xmlns:str="http://exslt.org/strings" 
1036      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1037      exclude-result-prefixes="str xsl"> 
1038    <xsl:template match="text()"> 
1039      <xsl:value-of select="str:align(string(.), '***', 'center')" /> 
1040    </xsl:template> 
1041    <xsl:template match="*"> 
1042      <xsl:copy> 
1043        <xsl:apply-templates/> 
1044      </xsl:copy> 
1045    </xsl:template> 
1046  </xsl:stylesheet>''') 
1047   
1048          st = etree.XSLT(style) 
1049          res = st(tree) 
1050          self.assertEqual('''\ 
1051  <?xml version="1.0"?> 
1052  <a><b>*B*</b><c>*C*</c></a> 
1053  ''', 
1054                            str(res)) 
 1055   
1057          tree = self.parse('<a><b>B</b><c>C</c></a>') 
1058          style = self.parse('''\ 
1059            <xsl:stylesheet version = "1.0" 
1060                xmlns:xsl='http://www.w3.org/1999/XSL/Transform' 
1061                xmlns:str="http://exslt.org/strings" 
1062                extension-element-prefixes="str"> 
1063   
1064                <xsl:template match="/"> 
1065                  <h1 class="{str:replace('abc', 'b', 'x')}">test</h1> 
1066                </xsl:template> 
1067   
1068            </xsl:stylesheet>''') 
1069   
1070          st = etree.XSLT(style) 
1071          res = st(tree) 
1072          self.assertEqual(str(res), '''\ 
1073  <?xml version="1.0"?> 
1074  <h1 class="axc">test</h1> 
1075  ''') 
 1076   
1078          tree = self.parse('<a><b>B</b><c>C</c></a>') 
1079          style = self.parse('''\ 
1080  <xsl:stylesheet version="1.0" 
1081      xmlns:math="http://exslt.org/math" 
1082      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1083      exclude-result-prefixes="math xsl"> 
1084    <xsl:template match="*"> 
1085      <xsl:copy> 
1086        <xsl:attribute name="pi"> 
1087          <xsl:value-of select="math:constant('PI', count(*)+2)"/> 
1088        </xsl:attribute> 
1089        <xsl:apply-templates/> 
1090      </xsl:copy> 
1091    </xsl:template> 
1092  </xsl:stylesheet>''') 
1093   
1094          st = etree.XSLT(style) 
1095          res = st(tree) 
1096          self.assertEqual('''\ 
1097  <?xml version="1.0"?> 
1098  <a pi="3.14"><b pi="3">B</b><c pi="3">C</c></a> 
1099  ''', 
1100                            str(res)) 
 1101   
1103          xslt = etree.XSLT(etree.XML(_bytes("""\ 
1104  <xsl:stylesheet version="1.0" 
1105     xmlns:regexp="http://exslt.org/regular-expressions" 
1106     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
1107    <xsl:template match="*"> 
1108      <test><xsl:copy-of select="*[regexp:test(string(.), '8.')]"/></test> 
1109    </xsl:template> 
1110  </xsl:stylesheet> 
1111  """))) 
1112          result = xslt(etree.XML(_bytes('<a><b>123</b><b>098</b><b>987</b></a>'))) 
1113          root = result.getroot() 
1114          self.assertEqual(root.tag, 
1115                            'test') 
1116          self.assertEqual(len(root), 1) 
1117          self.assertEqual(root[0].tag, 
1118                            'b') 
1119          self.assertEqual(root[0].text, 
1120                            '987') 
 1121   
1123          xslt = etree.XSLT(etree.XML("""\ 
1124  <xsl:stylesheet version="1.0" 
1125     xmlns:regexp="http://exslt.org/regular-expressions" 
1126     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
1127    <xsl:template match="*"> 
1128      <test> 
1129        <xsl:copy-of select="regexp:replace(string(.), 'd.', '',   'XX')"/> 
1130        <xsl:text>-</xsl:text> 
1131        <xsl:copy-of select="regexp:replace(string(.), 'd.', 'gi', 'XX')"/> 
1132      </test> 
1133    </xsl:template> 
1134  </xsl:stylesheet> 
1135  """)) 
1136          result = xslt(etree.XML(_bytes('<a>abdCdEeDed</a>'))) 
1137          root = result.getroot() 
1138          self.assertEqual(root.tag, 
1139                            'test') 
1140          self.assertEqual(len(root), 0) 
1141          self.assertEqual(root.text, 'abXXdEeDed-abXXXXeXXd') 
 1142   
1144          xslt = etree.XSLT(etree.XML("""\ 
1145  <xsl:stylesheet version="1.0" 
1146     xmlns:regexp="http://exslt.org/regular-expressions" 
1147     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
1148    <xsl:template match="*"> 
1149      <test> 
1150        <test1><xsl:copy-of  select="regexp:match(string(.), 'd.')"/></test1> 
1151        <test2><xsl:copy-of  select="regexp:match(string(.), 'd.', 'g')"/></test2> 
1152        <test2i><xsl:copy-of select="regexp:match(string(.), 'd.', 'gi')"/></test2i> 
1153      </test> 
1154    </xsl:template> 
1155  </xsl:stylesheet> 
1156  """)) 
1157          result = xslt(etree.XML(_bytes('<a>abdCdEeDed</a>'))) 
1158          root = result.getroot() 
1159          self.assertEqual(root.tag,  'test') 
1160          self.assertEqual(len(root), 3) 
1161   
1162          self.assertEqual(len(root[0]), 1) 
1163          self.assertEqual(root[0][0].tag, 'match') 
1164          self.assertEqual(root[0][0].text, 'dC') 
1165   
1166          self.assertEqual(len(root[1]), 2) 
1167          self.assertEqual(root[1][0].tag, 'match') 
1168          self.assertEqual(root[1][0].text, 'dC') 
1169          self.assertEqual(root[1][1].tag, 'match') 
1170          self.assertEqual(root[1][1].text, 'dE') 
1171   
1172          self.assertEqual(len(root[2]), 3) 
1173          self.assertEqual(root[2][0].tag, 'match') 
1174          self.assertEqual(root[2][0].text, 'dC') 
1175          self.assertEqual(root[2][1].tag, 'match') 
1176          self.assertEqual(root[2][1].text, 'dE') 
1177          self.assertEqual(root[2][2].tag, 'match') 
1178          self.assertEqual(root[2][2].text, 'De') 
 1179   
1181          xslt = etree.XSLT(etree.XML(_bytes("""\ 
1182  <xsl:stylesheet version="1.0" 
1183     xmlns:regexp="http://exslt.org/regular-expressions" 
1184     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
1185    <xsl:template match="/"> 
1186      <test> 
1187        <xsl:for-each select="regexp:match( 
1188              '123abc567', '([0-9]+)([a-z]+)([0-9]+)' )"> 
1189          <test1><xsl:value-of select="."/></test1> 
1190        </xsl:for-each> 
1191      </test> 
1192    </xsl:template> 
1193  </xsl:stylesheet> 
1194  """))) 
1195          result = xslt(etree.XML(_bytes('<a/>'))) 
1196          root = result.getroot() 
1197          self.assertEqual(root.tag,  'test') 
1198          self.assertEqual(len(root), 4) 
1199   
1200          self.assertEqual(root[0].text, "123abc567") 
1201          self.assertEqual(root[1].text, "123") 
1202          self.assertEqual(root[2].text, "abc") 
1203          self.assertEqual(root[3].text, "567") 
 1204   
1206           
1207          xslt = etree.XSLT(etree.XML(_bytes("""\ 
1208  <xsl:stylesheet version="1.0" 
1209     xmlns:regexp="http://exslt.org/regular-expressions" 
1210     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
1211    <xsl:template match="/"> 
1212      <test> 
1213        <xsl:for-each select="regexp:match( 
1214              'http://www.bayes.co.uk/xml/index.xml?/xml/utils/rechecker.xml', 
1215              '(\\w+):\\/\\/([^/:]+)(:\\d*)?([^# ]*)')"> 
1216          <test1><xsl:value-of select="."/></test1> 
1217        </xsl:for-each> 
1218      </test> 
1219    </xsl:template> 
1220  </xsl:stylesheet> 
1221  """))) 
1222          result = xslt(etree.XML(_bytes('<a/>'))) 
1223          root = result.getroot() 
1224          self.assertEqual(root.tag,  'test') 
1225          self.assertEqual(len(root), 5) 
1226   
1227          self.assertEqual( 
1228              root[0].text, 
1229              "http://www.bayes.co.uk/xml/index.xml?/xml/utils/rechecker.xml") 
1230          self.assertEqual( 
1231              root[1].text, 
1232              "http") 
1233          self.assertEqual( 
1234              root[2].text, 
1235              "www.bayes.co.uk") 
1236          self.assertFalse(root[3].text) 
1237          self.assertEqual( 
1238              root[4].text, 
1239              "/xml/index.xml?/xml/utils/rechecker.xml") 
 1240   
1242           
1243          xslt = etree.XSLT(self.parse("""\ 
1244  <xsl:stylesheet version="1.0" 
1245     xmlns:regexp="http://exslt.org/regular-expressions" 
1246     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
1247    <xsl:template match="/"> 
1248      <test> 
1249        <xsl:for-each select="regexp:match( 
1250              'This is a test string', '(\\w+)', 'g')"> 
1251          <test1><xsl:value-of select="."/></test1> 
1252        </xsl:for-each> 
1253      </test> 
1254    </xsl:template> 
1255  </xsl:stylesheet> 
1256  """)) 
1257          result = xslt(etree.XML(_bytes('<a/>'))) 
1258          root = result.getroot() 
1259          self.assertEqual(root.tag,  'test') 
1260          self.assertEqual(len(root), 5) 
1261   
1262          self.assertEqual(root[0].text, "This") 
1263          self.assertEqual(root[1].text, "is") 
1264          self.assertEqual(root[2].text, "a") 
1265          self.assertEqual(root[3].text, "test") 
1266          self.assertEqual(root[4].text, "string") 
 1267   
1269           
1270           
1271          xslt = etree.XSLT(etree.XML(_bytes("""\ 
1272  <xsl:stylesheet version="1.0" 
1273     xmlns:regexp="http://exslt.org/regular-expressions" 
1274     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
1275    <xsl:template match="/"> 
1276      <test> 
1277        <xsl:for-each select="regexp:match( 
1278              'This is a test string', '([a-z])+ ', 'g')"> 
1279          <test1><xsl:value-of select="."/></test1> 
1280        </xsl:for-each> 
1281      </test> 
1282    </xsl:template> 
1283  </xsl:stylesheet> 
1284  """))) 
1285          result = xslt(etree.XML(_bytes('<a/>'))) 
1286          root = result.getroot() 
1287          self.assertEqual(root.tag,  'test') 
1288          self.assertEqual(len(root), 4) 
1289   
1290          self.assertEqual(root[0].text, "his") 
1291          self.assertEqual(root[1].text, "is") 
1292          self.assertEqual(root[2].text, "a") 
1293          self.assertEqual(root[3].text, "test") 
 1294   
1296           
1297           
1298          xslt = etree.XSLT(etree.XML(_bytes("""\ 
1299  <xsl:stylesheet version="1.0" 
1300     xmlns:regexp="http://exslt.org/regular-expressions" 
1301     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
1302    <xsl:template match="/"> 
1303      <test> 
1304        <xsl:for-each select="regexp:match( 
1305              'This is a test string', '([a-z])+ ', 'gi')"> 
1306          <test1><xsl:value-of select="."/></test1> 
1307        </xsl:for-each> 
1308      </test> 
1309    </xsl:template> 
1310  </xsl:stylesheet> 
1311  """))) 
1312          result = xslt(etree.XML(_bytes('<a/>'))) 
1313          root = result.getroot() 
1314          self.assertEqual(root.tag,  'test') 
1315          self.assertEqual(len(root), 4) 
1316   
1317          self.assertEqual(root[0].text, "This") 
1318          self.assertEqual(root[1].text, "is") 
1319          self.assertEqual(root[2].text, "a") 
1320          self.assertEqual(root[3].text, "test") 
  1321   
1322   
1323 -class ETreeXSLTExtFuncTestCase(HelperTestCase): 
 1324      """Tests for XPath extension functions in XSLT.""" 
1325   
1326 -    def test_extensions1(self): 
 1327          tree = self.parse('<a><b>B</b></a>') 
1328          style = self.parse('''\ 
1329  <xsl:stylesheet version="1.0" 
1330      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1331      xmlns:myns="testns" 
1332      exclude-result-prefixes="myns"> 
1333    <xsl:template match="a"><A><xsl:value-of select="myns:mytext(b)"/></A></xsl:template> 
1334  </xsl:stylesheet>''') 
1335   
1336          def mytext(ctxt, values): 
1337              return 'X' * len(values) 
 1338   
1339          result = tree.xslt(style, {('testns', 'mytext') : mytext}) 
1340          self.assertEqual(self._rootstring(result), 
1341                            _bytes('<A>X</A>')) 
 1342   
1343 -    def test_extensions2(self): 
 1344          tree = self.parse('<a><b>B</b></a>') 
1345          style = self.parse('''\ 
1346  <xsl:stylesheet version="1.0" 
1347      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1348      xmlns:myns="testns" 
1349      exclude-result-prefixes="myns"> 
1350    <xsl:template match="a"><A><xsl:value-of select="myns:mytext(b)"/></A></xsl:template> 
1351  </xsl:stylesheet>''') 
1352   
1353          def mytext(ctxt, values): 
1354              return 'X' * len(values) 
 1355   
1356          namespace = etree.FunctionNamespace('testns') 
1357          namespace['mytext'] = mytext 
1358   
1359          result = tree.xslt(style) 
1360          self.assertEqual(self._rootstring(result), 
1361                            _bytes('<A>X</A>')) 
1362   
1364          tree = self.parse('<a><b>B</b><b/></a>') 
1365          style = self.parse('''\ 
1366  <xsl:stylesheet version="1.0" 
1367      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1368      xmlns:myns="testns" 
1369      exclude-result-prefixes="myns"> 
1370    <xsl:template match="a"> 
1371      <xsl:variable name="content"> 
1372         <xsl:apply-templates/> 
1373      </xsl:variable> 
1374      <A><xsl:value-of select="myns:mytext($content)"/></A> 
1375    </xsl:template> 
1376    <xsl:template match="b"><xsl:copy>BBB</xsl:copy></xsl:template> 
1377  </xsl:stylesheet>''') 
1378   
1379          def mytext(ctxt, values): 
1380              for value in values: 
1381                  self.assertTrue(hasattr(value, 'tag'), 
1382                               "%s is not an Element" % type(value)) 
1383                  self.assertEqual(value.tag, 'b') 
1384                  self.assertEqual(value.text, 'BBB') 
1385              return 'X'.join([el.tag for el in values]) 
 1386   
1387          namespace = etree.FunctionNamespace('testns') 
1388          namespace['mytext'] = mytext 
1389   
1390          result = tree.xslt(style) 
1391          self.assertEqual(self._rootstring(result), 
1392                           _bytes('<A>bXb</A>')) 
1393   
1395          tree = self.parse('<a><b>B<c/>C</b><b/></a>') 
1396          style = self.parse('''\ 
1397  <xsl:stylesheet version="1.0" 
1398      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1399      xmlns:myns="testns" 
1400      exclude-result-prefixes="myns"> 
1401    <xsl:template match="b"> 
1402      <A><xsl:value-of select="myns:myext()"/></A> 
1403    </xsl:template> 
1404  </xsl:stylesheet>''') 
1405   
1406          def extfunc(ctxt): 
1407              text_content = ctxt.context_node.xpath('text()') 
1408              return 'x'.join(text_content) 
 1409   
1410          namespace = etree.FunctionNamespace('testns') 
1411          namespace['myext'] = extfunc 
1412   
1413          result = tree.xslt(style) 
1414          self.assertEqual(self._rootstring(result), 
1415                           _bytes('<A>BxC</A>')) 
1416   
1418           
1419          class Resolver(etree.Resolver): 
1420              def resolve(self, system_url, public_id, context): 
1421                  assert system_url == 'extdoc.xml' 
1422                  return self.resolve_string(b'<a><b>B<c/>C</b><b/></a>', context) 
 1423   
1424          parser = etree.XMLParser() 
1425          parser.resolvers.add(Resolver()) 
1426   
1427          tree = self.parse(b'<a><b/><b/></a>') 
1428          transform = etree.XSLT(self.parse(b'''\ 
1429  <xsl:stylesheet version="1.0" 
1430      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1431      xmlns:mypre="testns" 
1432      exclude-result-prefixes="mypre"> 
1433    <xsl:template match="b"> 
1434      <B><xsl:value-of select="mypre:myext()"/></B> 
1435    </xsl:template> 
1436    <xsl:template match="a"> 
1437      <A><xsl:apply-templates select="document('extdoc.xml')//b" /></A> 
1438    </xsl:template> 
1439  </xsl:stylesheet>''', parser=parser)) 
1440   
1441          def extfunc(ctxt): 
1442              text_content = ctxt.context_node.xpath('text()') 
1443              return 'x'.join(text_content) 
1444   
1445          namespace = etree.FunctionNamespace('testns') 
1446          namespace['myext'] = extfunc 
1447   
1448          result = transform(tree) 
1449          self.assertEqual(self._rootstring(result), 
1450                           _bytes('<A><B>BxC</B><B/></A>')) 
1451   
1452   
1453 -class ETreeXSLTExtElementTestCase(HelperTestCase): 
 1454      """Tests for extension elements in XSLT.""" 
1455   
1457          tree = self.parse('<a><b>B</b></a>') 
1458          style = self.parse('''\ 
1459  <xsl:stylesheet version="1.0" 
1460      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1461      xmlns:myns="testns" 
1462      extension-element-prefixes="myns" 
1463      exclude-result-prefixes="myns"> 
1464    <xsl:template match="a"> 
1465      <A><myns:myext>b</myns:myext></A> 
1466    </xsl:template> 
1467  </xsl:stylesheet>''') 
1468   
1469          class MyExt(etree.XSLTExtension): 
1470              def execute(self, context, self_node, input_node, output_parent): 
1471                  child = etree.Element(self_node.text) 
1472                  child.text = 'X' 
1473                  output_parent.append(child) 
  1474   
1475          extensions = { ('testns', 'myext') : MyExt() } 
1476   
1477          result = tree.xslt(style, extensions=extensions) 
1478          self.assertEqual(self._rootstring(result), 
1479                            _bytes('<A><b>X</b></A>')) 
1480   
1482          tree = self.parse('<a><b>B</b></a>') 
1483          style = self.parse('''\ 
1484  <xsl:stylesheet version="1.0" 
1485      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1486      xmlns:myns="testns" 
1487      extension-element-prefixes="myns" 
1488      exclude-result-prefixes="myns"> 
1489    <xsl:template match="/"> 
1490      <A><myns:myext>b</myns:myext></A> 
1491    </xsl:template> 
1492  </xsl:stylesheet>''') 
1493   
1494          tags = [] 
1495   
1496          class MyExt(etree.XSLTExtension): 
1497              def execute(self, context, self_node, input_node, output_parent): 
1498                  tags.append(input_node.tag) 
 1499   
1500          extensions = { ('testns', 'myext') : MyExt() } 
1501   
1502          result = tree.xslt(style, extensions=extensions) 
1503          self.assertEqual(tags, ['a']) 
1504   
1506          tree = self.parse('<?test toast?><a><!--a comment--><?another pi?></a>') 
1507          style = self.parse('''\ 
1508  <xsl:stylesheet version="1.0" 
1509      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1510      xmlns:myns="testns" 
1511      extension-element-prefixes="myns" 
1512      exclude-result-prefixes="myns"> 
1513    <xsl:template match="/"> 
1514      <ROOT><xsl:apply-templates /></ROOT> 
1515    </xsl:template> 
1516    <xsl:template match="comment()"> 
1517      <A><myns:myext>b</myns:myext></A> 
1518    </xsl:template> 
1519    <xsl:template match="processing-instruction()"> 
1520      <A><myns:myext>b</myns:myext></A> 
1521    </xsl:template> 
1522  </xsl:stylesheet>''') 
1523   
1524          text = [] 
1525   
1526          class MyExt(etree.XSLTExtension): 
1527              def execute(self, context, self_node, input_node, output_parent): 
1528                  text.append(input_node.text) 
 1529   
1530          extensions = { ('testns', 'myext') : MyExt() } 
1531   
1532          result = tree.xslt(style, extensions=extensions) 
1533          self.assertEqual(text, ['toast', 'a comment', 'pi']) 
1534   
1536           
1537          tree = self.parse('<a test="A"><b attr="B"/></a>') 
1538          style = self.parse('''\ 
1539  <xsl:stylesheet version="1.0" 
1540      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1541      xmlns:myns="testns" 
1542      extension-element-prefixes="myns" 
1543      exclude-result-prefixes="myns"> 
1544    <xsl:template match="@test"> 
1545      <A><myns:myext>b</myns:myext></A> 
1546    </xsl:template> 
1547    <xsl:template match="@attr"> 
1548      <A><myns:myext>b</myns:myext></A> 
1549    </xsl:template> 
1550  </xsl:stylesheet>''') 
1551   
1552          text = [] 
1553   
1554          class MyExt(etree.XSLTExtension): 
1555              def execute(self, context, self_node, attr_value, output_parent): 
1556                  text.append(attr_value) 
 1557   
1558          extensions = { ('testns', 'myext') : MyExt() } 
1559   
1560          result = tree.xslt(style, extensions=extensions) 
1561          self.assertEqual(text, ['A', 'B']) 
1562   
1564          tree = self.parse('<a><b>B</b></a>') 
1565          style = self.parse('''\ 
1566  <xsl:stylesheet version="1.0" 
1567      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1568      xmlns:myns="testns" 
1569      extension-element-prefixes="myns"> 
1570    <xsl:template match="a"> 
1571      <A><myns:myext><x>X</x><y>Y</y><z/></myns:myext></A> 
1572    </xsl:template> 
1573  </xsl:stylesheet>''') 
1574   
1575          class MyExt(etree.XSLTExtension): 
1576              def execute(self, context, self_node, input_node, output_parent): 
1577                  output_parent.extend(list(self_node)[1:]) 
 1578   
1579          extensions = { ('testns', 'myext') : MyExt() } 
1580   
1581          result = tree.xslt(style, extensions=extensions) 
1582          self.assertEqual(self._rootstring(result), 
1583                            _bytes('<A><y>Y</y><z/></A>')) 
1584   
1586          tree = self.parse('<a><b>B</b></a>') 
1587          style = self.parse('''\ 
1588  <xsl:stylesheet version="1.0" 
1589      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1590      xmlns:myns="testns" 
1591      extension-element-prefixes="myns"> 
1592    <xsl:template match="a"> 
1593      <A><myns:myext><x>X</x><y>Y</y><z/></myns:myext></A> 
1594    </xsl:template> 
1595    <xsl:template match="x" /> 
1596    <xsl:template match="z">XYZ</xsl:template> 
1597  </xsl:stylesheet>''') 
1598   
1599          class MyExt(etree.XSLTExtension): 
1600              def execute(self, context, self_node, input_node, output_parent): 
1601                  for child in self_node: 
1602                      for result in self.apply_templates(context, child): 
1603                          if isinstance(result, basestring): 
1604                              el = etree.Element("T") 
1605                              el.text = result 
1606                          else: 
1607                              el = result 
1608                          output_parent.append(el) 
 1609   
1610          extensions = { ('testns', 'myext') : MyExt() } 
1611   
1612          result = tree.xslt(style, extensions=extensions) 
1613          self.assertEqual(self._rootstring(result), 
1614                            _bytes('<A><T>Y</T><T>XYZ</T></A>')) 
1615   
1617          tree = self.parse('<a><b>B</b></a>') 
1618          style = self.parse('''\ 
1619  <xsl:stylesheet version="1.0" 
1620      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1621      xmlns:myns="testns" 
1622      extension-element-prefixes="myns"> 
1623    <xsl:template match="a"> 
1624      <A><myns:myext><x>X</x><y>Y</y><z/></myns:myext></A> 
1625    </xsl:template> 
1626    <xsl:template match="x"><X/></xsl:template> 
1627    <xsl:template match="z">XYZ</xsl:template> 
1628  </xsl:stylesheet>''') 
1629   
1630          class MyExt(etree.XSLTExtension): 
1631              def execute(self, context, self_node, input_node, output_parent): 
1632                  for child in self_node: 
1633                      for result in self.apply_templates(context, child, 
1634                                                         elements_only=True): 
1635                          assert not isinstance(result, basestring) 
1636                          output_parent.append(result) 
 1637   
1638          extensions = { ('testns', 'myext') : MyExt() } 
1639   
1640          result = tree.xslt(style, extensions=extensions) 
1641          self.assertEqual(self._rootstring(result), 
1642                            _bytes('<A><X/></A>')) 
1643   
1645          tree = self.parse('<a><b>B</b></a>') 
1646          style = self.parse('''\ 
1647  <xsl:stylesheet version="1.0" 
1648      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1649      xmlns:myns="testns" 
1650      extension-element-prefixes="myns"> 
1651    <xsl:template match="a"> 
1652      <A><myns:myext><x>X</x><y>Y</y><z/></myns:myext></A> 
1653    </xsl:template> 
1654    <xsl:template match="x"><X/></xsl:template> 
1655    <xsl:template match="y"><xsl:text>   </xsl:text></xsl:template> 
1656    <xsl:template match="z">XYZ</xsl:template> 
1657  </xsl:stylesheet>''') 
1658   
1659          class MyExt(etree.XSLTExtension): 
1660              def execute(self, context, self_node, input_node, output_parent): 
1661                  for child in self_node: 
1662                      for result in self.apply_templates(context, child, 
1663                                                         remove_blank_text=True): 
1664                          if isinstance(result, basestring): 
1665                              assert result.strip() 
1666                              el = etree.Element("T") 
1667                              el.text = result 
1668                          else: 
1669                              el = result 
1670                          output_parent.append(el) 
 1671   
1672          extensions = { ('testns', 'myext') : MyExt() } 
1673   
1674          result = tree.xslt(style, extensions=extensions) 
1675          self.assertEqual(self._rootstring(result), 
1676                            _bytes('<A><X/><T>XYZ</T></A>')) 
1677   
1679          tree = self.parse('<a><b>B</b></a>') 
1680          style = self.parse('''\ 
1681  <xsl:stylesheet version="1.0" 
1682      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1683      xmlns:myns="testns" 
1684      extension-element-prefixes="myns"> 
1685    <xsl:template match="a"> 
1686      <A><myns:myext><x>X</x><y>Y</y><z/></myns:myext></A> 
1687    </xsl:template> 
1688    <xsl:template match="x" /> 
1689    <xsl:template match="z">XYZ</xsl:template> 
1690  </xsl:stylesheet>''') 
1691   
1692          class MyExt(etree.XSLTExtension): 
1693              def execute(self, context, self_node, input_node, output_parent): 
1694                  for child in self_node: 
1695                      self.apply_templates(context, child, output_parent) 
 1696   
1697          extensions = { ('testns', 'myext') : MyExt() } 
1698   
1699          result = tree.xslt(style, extensions=extensions) 
1700          self.assertEqual(self._rootstring(result), 
1701                            _bytes('<A>YXYZ</A>')) 
1702   
1704          tree = self.parse('<a><b>B</b></a>') 
1705          style = self.parse('''\ 
1706  <xsl:stylesheet version="1.0" 
1707      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1708      xmlns:myns="testns" 
1709      extension-element-prefixes="myns"> 
1710    <xsl:template match="a"> 
1711      <myns:myext><x>X</x><y>Y</y><z/></myns:myext> 
1712    </xsl:template> 
1713    <xsl:template match="x"><xsl:processing-instruction name="test">TEST</xsl:processing-instruction></xsl:template> 
1714    <xsl:template match="y"><Y>XYZ</Y></xsl:template> 
1715    <xsl:template match="z"><xsl:comment>TEST</xsl:comment></xsl:template> 
1716  </xsl:stylesheet>''') 
1717   
1718          class MyExt(etree.XSLTExtension): 
1719              def execute(self, context, self_node, input_node, output_parent): 
1720                  for child in self_node: 
1721                      self.apply_templates(context, child, output_parent) 
 1722   
1723          extensions = { ('testns', 'myext') : MyExt() } 
1724   
1725          result = tree.xslt(style, extensions=extensions) 
1726          self.assertEqual(etree.tostring(result), 
1727                            _bytes('<?test TEST?><Y>XYZ</Y><!--TEST-->')) 
1728   
1730          tree = self.parse('<a><b>E</b></a>') 
1731          style = self.parse('''\ 
1732  <xsl:stylesheet version="1.0" 
1733      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1734      xmlns:myns="testns" 
1735      extension-element-prefixes="myns"> 
1736    <xsl:template match="a"> 
1737      <xsl:variable name="testvar">yo</xsl:variable> 
1738      <A> 
1739        <myns:myext> 
1740          <xsl:attribute name="attr"> 
1741            <xsl:value-of select="$testvar" /> 
1742          </xsl:attribute> 
1743          <B> 
1744            <xsl:choose> 
1745              <xsl:when test="1 = 2"><C/></xsl:when> 
1746              <xsl:otherwise><D><xsl:value-of select="b/text()" /></D></xsl:otherwise> 
1747            </xsl:choose> 
1748          </B> 
1749        </myns:myext> 
1750      </A> 
1751    </xsl:template> 
1752  </xsl:stylesheet>''') 
1753   
1754          class MyExt(etree.XSLTExtension): 
1755              def execute(self, context, self_node, input_node, output_parent): 
1756                  el = etree.Element('MY') 
1757                  self.process_children(context, el) 
1758                  output_parent.append(el) 
 1759   
1760          extensions = { ('testns', 'myext') : MyExt() } 
1761   
1762          result = tree.xslt(style, extensions=extensions) 
1763          self.assertEqual(self._rootstring(result), 
1764                            _bytes('<A><MYattr="yo"><B><D>E</D></B></MY></A>')) 
1765   
1767          tree = self.parse('<a/>') 
1768          style = self.parse('''\ 
1769  <xsl:stylesheet version="1.0" 
1770      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1771      xmlns:myns="testns" 
1772      extension-element-prefixes="myns"> 
1773    <xsl:template match="a"> 
1774      <myns:myext> 
1775        <A/> 
1776      </myns:myext> 
1777    </xsl:template> 
1778  </xsl:stylesheet>''') 
1779   
1780          class MyExt(etree.XSLTExtension): 
1781              def execute(self, context, self_node, input_node, output_parent): 
1782                  self.process_children(context, output_parent) 
 1783   
1784          extensions = { ('testns', 'myext') : MyExt() } 
1785   
1786          result = tree.xslt(style, extensions=extensions) 
1787          self.assertEqual(self._rootstring(result), 
1788                            _bytes('<A/>')) 
1789   
1791          tree = self.parse('<a/>') 
1792          style = self.parse('''\ 
1793  <xsl:stylesheet version="1.0" 
1794      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1795      xmlns:myns="testns" 
1796      extension-element-prefixes="myns"> 
1797    <xsl:template match="a"> 
1798      <myns:myext> 
1799        <A/> 
1800      </myns:myext> 
1801    </xsl:template> 
1802  </xsl:stylesheet>''') 
1803   
1804          class MyExt(etree.XSLTExtension): 
1805              def execute(self, context, self_node, input_node, output_parent): 
1806                  self.process_children(context, self_node) 
 1807   
1808          extensions = { ('testns', 'myext') : MyExt() } 
1809   
1810          self.assertRaises(TypeError, tree.xslt, style, extensions=extensions) 
1811   
1813          tree = self.parse('<a/>') 
1814          style = self.parse('''\ 
1815  <xsl:stylesheet version="1.0" 
1816      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1817      xmlns:myns="testns" 
1818      extension-element-prefixes="myns"> 
1819    <xsl:template match="a"> 
1820      <myns:myext> 
1821        <A><myns:myext><B/></myns:myext></A> 
1822      </myns:myext> 
1823    </xsl:template> 
1824  </xsl:stylesheet>''') 
1825   
1826          class MyExt(etree.XSLTExtension): 
1827              callback_call_counter = 0 
1828              def execute(self, context, self_node, input_node, output_parent): 
1829                  self.callback_call_counter += 1 
1830                  el = etree.Element('MY', n=str(self.callback_call_counter)) 
1831                  self.process_children(context, el) 
1832                  output_parent.append(el) 
 1833   
1834          extensions = { ('testns', 'myext') : MyExt() } 
1835   
1836          result = tree.xslt(style, extensions=extensions) 
1837          self.assertEqual(self._rootstring(result), 
1838                            _bytes('<MYn="1"><A><MYn="2"><B/></MY></A></MY>')) 
1839   
1841          tree = self.parse('<a><b>B</b></a>') 
1842          style = self.parse('''\ 
1843  <xsl:stylesheet version="1.0" 
1844      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
1845      xmlns:myns="testns" 
1846      extension-element-prefixes="myns" 
1847      exclude-result-prefixes="myns"> 
1848    <xsl:template match="a"> 
1849      <A><myns:myext>b</myns:myext></A> 
1850    </xsl:template> 
1851  </xsl:stylesheet>''') 
1852   
1853          class MyError(Exception): 
1854              pass 
 1855   
1856          class MyExt(etree.XSLTExtension): 
1857              def execute(self, context, self_node, input_node, output_parent): 
1858                  raise MyError("expected!") 
1859   
1860          extensions = { ('testns', 'myext') : MyExt() } 
1861          self.assertRaises(MyError, tree.xslt, style, extensions=extensions) 
1862   
1863       
1864       
1866          tree = self.parse("""\ 
1867  <text> 
1868    <par>This is <format>arbitrary</format> text in a paragraph</par> 
1869  </text>""") 
1870          style = self.parse("""\ 
1871  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my" extension-element-prefixes="my" version="1.0"> 
1872    <xsl:template match="par"> 
1873      <my:par><xsl:apply-templates /></my:par> 
1874    </xsl:template> 
1875    <xsl:template match="format"> 
1876      <my:format><xsl:apply-templates /></my:format> 
1877    </xsl:template> 
1878  </xsl:stylesheet> 
1879  """) 
1880          test = self 
1881          calls = [] 
1882   
1883          class ExtMyPar(etree.XSLTExtension): 
1884              def execute(self, context, self_node, input_node, output_parent): 
1885                  calls.append('par') 
1886                  p = etree.Element("p") 
1887                  p.attrib["style"] = "color:red" 
1888                  self.process_children(context, p) 
1889                  output_parent.append(p) 
 1890   
1891          class ExtMyFormat(etree.XSLTExtension): 
1892              def execute(self, context, self_node, input_node, output_parent): 
1893                  calls.append('format') 
1894                  content = self.process_children(context) 
1895                  test.assertEqual(1, len(content)) 
1896                  test.assertEqual('arbitrary', content[0]) 
1897                  test.assertEqual('This is ', output_parent.text) 
1898                  output_parent.text += '*-%s-*' % content[0] 
1899   
1900          extensions = {("my", "par"): ExtMyPar(), ("my", "format"): ExtMyFormat()} 
1901          transform = etree.XSLT(style, extensions=extensions) 
1902          result = transform(tree) 
1903          self.assertEqual(['par', 'format'], calls) 
1904          self.assertEqual( 
1905              b'<p style="color:red">This is *-arbitrary-* text in a paragraph</p>\n', 
1906              etree.tostring(result)) 
1907   
1910      """XSLT tests for etree under Python 3""" 
1911   
1912      pytestmark = skipif('sys.version_info < (3,0)') 
1913   
1915          tree = self.parse('<a><b>B</b><c>C</c></a>') 
1916          style = self.parse('''\ 
1917  <xsl:stylesheet version="1.0" 
1918      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
1919    <xsl:template match="*" /> 
1920    <xsl:template match="/"> 
1921      <foo><xsl:value-of select="/a/b/text()" /></foo> 
1922    </xsl:template> 
1923  </xsl:stylesheet>''') 
1924   
1925          st = etree.XSLT(style) 
1926          res = st(tree) 
1927          self.assertEqual(_bytes('''\ 
1928  <?xml version="1.0"?> 
1929  <foo>B</foo> 
1930  '''), 
1931                            bytes(res)) 
 1932   
1934          tree = self.parse('<a><b>B</b><c>C</c></a>') 
1935          style = self.parse('''\ 
1936  <xsl:stylesheet version="1.0" 
1937      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
1938    <xsl:template match="*" /> 
1939    <xsl:template match="/"> 
1940      <foo><xsl:value-of select="/a/b/text()" /></foo> 
1941    </xsl:template> 
1942  </xsl:stylesheet>''') 
1943   
1944          st = etree.XSLT(style) 
1945          res = st(tree) 
1946          self.assertEqual(_bytes('''\ 
1947  <?xml version="1.0"?> 
1948  <foo>B</foo> 
1949  '''), 
1950                            bytearray(res)) 
 1951   
1953          tree = self.parse('<a><b>B</b><c>C</c></a>') 
1954          style = self.parse('''\ 
1955  <xsl:stylesheet version="1.0" 
1956      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
1957    <xsl:template match="*" /> 
1958    <xsl:template match="/"> 
1959      <foo><xsl:value-of select="/a/b/text()" /></foo> 
1960    </xsl:template> 
1961  </xsl:stylesheet>''') 
1962   
1963          st = etree.XSLT(style) 
1964          res = st(tree) 
1965          self.assertEqual(_bytes('''\ 
1966  <?xml version="1.0"?> 
1967  <foo>B</foo> 
1968  '''), 
1969                            bytes(memoryview(res))) 
  1970   
1985   
1986  if __name__ == '__main__': 
1987      print('to test use test.py %s' % __file__) 
1988