lxml.tests.test_xslt
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 ( style )
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/>' )
290 style = self . parse ( '''\
291 <xsl:stylesheet version="1.0"
292 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
293 <xsl:template match="a">
294 <xsl:copy>
295 <xsl:message terminate="yes">FAIL</xsl:message>
296 </xsl:copy>
297 </xsl:template>
298 </xsl:stylesheet>''' )
299 self . assertRaises ( etree . XSLTApplyError ,
300 etree . XSLT ( style ) , tree )
301
302 transform = etree . XSLT ( style )
303 exc = None
304 try :
305 transform ( tree )
306 except etree . XSLTApplyError as e :
307 exc = e
308 else :
309 self . assertFalse ( True , "XSLT processing should have failed but didn't" )
310
311 self . assertTrue ( exc is not None )
312 self . assertTrue ( len ( exc . error_log ) )
313 self . assertEqual ( len ( transform . error_log ) , len ( exc . error_log ) )
314 for error in exc . error_log :
315 self . assertTrue ( ':ERROR:XSLT:' in str ( error ) )
316 for error in transform . error_log :
317 self . assertTrue ( ':ERROR:XSLT:' in str ( error ) )
318
320 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
321 style = self . parse ( '''\
322 <xsl:stylesheet version="1.0"
323 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
324 <xsl:template match="/">
325 <foo><xsl:value-of select="$bar" /></foo>
326 </xsl:template>
327 </xsl:stylesheet>''' )
328
329 st = etree . XSLT ( style )
330 res = st ( tree , bar = "'Bar'" )
331 self . assertEqual ( '''\
332 <?xml version="1.0"?>
333 <foo>Bar</foo>
334 ''' ,
335 str ( res ) )
336
338 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
339 style = self . parse ( '''\
340 <xsl:stylesheet version="1.0"
341 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
342 <xsl:template match="/">
343 <foo><xsl:value-of select="$bar" /></foo>
344 </xsl:template>
345 </xsl:stylesheet>''' )
346
347 st = etree . XSLT ( style )
348 res = st ( tree , bar = etree . XSLT . strparam ( '''it's me, "Bar"''' ) )
349 self . assertEqual ( '''\
350 <?xml version="1.0"?>
351 <foo>it's me, "Bar"</foo>
352 ''' ,
353 str ( res ) )
354
356 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
357 style = self . parse ( '''\
358 <xsl:stylesheet version="1.0"
359 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
360 <xsl:param name="bar"/>
361 <xsl:template match="/">
362 <foo><xsl:value-of select="$bar" /></foo>
363 </xsl:template>
364 </xsl:stylesheet>''' )
365
366 st = etree . XSLT ( style )
367 res = self . assertRaises ( etree . XSLTApplyError ,
368 st , tree , bar = "<test/>" )
369 res = self . assertRaises ( etree . XSLTApplyError ,
370 st , tree , bar = "...." )
371
373
374 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
375 style = self . parse ( '''\
376 <xsl:stylesheet version="1.0"
377 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
378 <xsl:template match="/">
379 <foo><xsl:value-of select="$bar" /></foo>
380 </xsl:template>
381 </xsl:stylesheet>''' )
382
383 st = etree . XSLT ( style )
384
385 self . assertRaises ( etree . XSLTApplyError , st . apply , tree )
386
388 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
389 style = self . parse ( '''\
390 <xsl:stylesheet version="1.0"
391 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
392 <xsl:template match="*" />
393 <xsl:template match="/">
394 <foo><xsl:value-of select="$bar" /></foo>
395 <foo><xsl:value-of select="$baz" /></foo>
396 </xsl:template>
397 </xsl:stylesheet>''' )
398
399 st = etree . XSLT ( style )
400 res = st ( tree , bar = "'Bar'" , baz = "'Baz'" )
401 self . assertEqual ( '''\
402 <?xml version="1.0"?>
403 <foo>Bar</foo><foo>Baz</foo>
404 ''' ,
405 str ( res ) )
406
408 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
409 style = self . parse ( '''\
410 <xsl:stylesheet version="1.0"
411 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
412 <xsl:template match="*" />
413 <xsl:template match="/">
414 <foo><xsl:value-of select="$bar" /></foo>
415 </xsl:template>
416 </xsl:stylesheet>''' )
417
418 st = etree . XSLT ( style )
419 res = st ( tree , bar = "/a/b/text()" )
420 self . assertEqual ( '''\
421 <?xml version="1.0"?>
422 <foo>B</foo>
423 ''' ,
424 str ( res ) )
425
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 </xsl:template>
435 </xsl:stylesheet>''' )
436
437 st = etree . XSLT ( style )
438 res = st ( tree , bar = etree . XPath ( "/a/b/text()" ) )
439 self . assertEqual ( '''\
440 <?xml version="1.0"?>
441 <foo>B</foo>
442 ''' ,
443 str ( res ) )
444
446 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
447 style = self . parse ( '''\
448 <xsl:stylesheet version="1.0"
449 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
450 <xsl:param name="bar" select="'Default'" />
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 = "'Bar'" )
459 self . assertEqual ( '''\
460 <?xml version="1.0"?>
461 <foo>Bar</foo>
462 ''' ,
463 str ( res ) )
464 res = st ( tree )
465 self . assertEqual ( '''\
466 <?xml version="1.0"?>
467 <foo>Default</foo>
468 ''' ,
469 str ( res ) )
470
472 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
473 style = self . parse ( '''\
474 <xsl:stylesheet version="1.0"
475 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
476 <xsl:output method="html"/>
477 <xsl:strip-space elements="*"/>
478 <xsl:template match="/">
479 <html><body><xsl:value-of select="/a/b/text()" /></body></html>
480 </xsl:template>
481 </xsl:stylesheet>''' )
482
483 st = etree . XSLT ( style )
484 res = st ( tree )
485 self . assertEqual ( '<html><body>B</body></html>' ,
486 str ( res ) . strip ( ) )
487
491
497
520
545
547
548 xml = '<blah/>'
549 xslt = '''
550 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
551 <xsl:template match="/" />
552 </xsl:stylesheet>
553 '''
554
555 source = self . parse ( xml )
556 styledoc = self . parse ( xslt )
557 style = etree . XSLT ( styledoc )
558 result = style ( source )
559 self . assertEqual ( '' , str ( result ) )
560
562 xml = '<blah/>'
563 xslt = '''
564 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
565 <xsl:template match="/">
566 <xsl:message>TEST TEST TEST</xsl:message>
567 </xsl:template>
568 </xsl:stylesheet>
569 '''
570
571 source = self . parse ( xml )
572 styledoc = self . parse ( xslt )
573 style = etree . XSLT ( styledoc )
574 result = style ( source )
575 self . assertEqual ( '' , str ( result ) )
576 self . assertTrue ( "TEST TEST TEST" in [ entry . message
577 for entry in style . error_log ] )
578
580 xml = '<blah/>'
581 xslt = '''
582 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
583 <xsl:template match="/">
584 <xsl:message terminate="yes">TEST TEST TEST</xsl:message>
585 </xsl:template>
586 </xsl:stylesheet>
587 '''
588
589 source = self . parse ( xml )
590 styledoc = self . parse ( xslt )
591 style = etree . XSLT ( styledoc )
592
593 self . assertRaises ( etree . XSLTApplyError , style , source )
594 self . assertTrue ( "TEST TEST TEST" in [ entry . message
595 for entry in style . error_log ] )
596
598 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
599 style = self . parse ( '''\
600 <xsl:stylesheet version="1.0"
601 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
602 <xsl:template match="*" />
603 <xsl:template match="/">
604 <doc>
605 <foo><xsl:value-of select="$bar" /></foo>
606 <foo><xsl:value-of select="$baz" /></foo>
607 </doc>
608 </xsl:template>
609 </xsl:stylesheet>''' )
610
611 result = tree . xslt ( style , bar = "'Bar'" , baz = "'Baz'" )
612 self . assertEqual (
613 _bytes ( '<doc><foo>Bar</foo><foo>Baz</foo></doc>' ) ,
614 etree . tostring ( result . getroot ( ) ) )
615
617 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
618 style = self . parse ( '''\
619 <xsl:stylesheet version="1.0"
620 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
621 <xsl:template match="a"><A><xsl:apply-templates/></A></xsl:template>
622 <xsl:template match="b"><B><xsl:apply-templates/></B></xsl:template>
623 <xsl:template match="c"><C><xsl:apply-templates/></C></xsl:template>
624 </xsl:stylesheet>''' )
625
626 self . assertEqual ( self . _rootstring ( tree ) ,
627 _bytes ( '<a><b>B</b><c>C</c></a>' ) )
628 result = tree . xslt ( style )
629 self . assertEqual ( self . _rootstring ( tree ) ,
630 _bytes ( '<a><b>B</b><c>C</c></a>' ) )
631 self . assertEqual ( self . _rootstring ( result ) ,
632 _bytes ( '<A><B>B</B><C>C</C></A>' ) )
633
634 b_tree = etree . ElementTree ( tree . getroot ( ) [ 0 ] )
635 self . assertEqual ( self . _rootstring ( b_tree ) ,
636 _bytes ( '<b>B</b>' ) )
637 result = b_tree . xslt ( style )
638 self . assertEqual ( self . _rootstring ( tree ) ,
639 _bytes ( '<a><b>B</b><c>C</c></a>' ) )
640 self . assertEqual ( self . _rootstring ( result ) ,
641 _bytes ( '<B>B</B>' ) )
642
643 c_tree = etree . ElementTree ( tree . getroot ( ) [ 1 ] )
644 self . assertEqual ( self . _rootstring ( c_tree ) ,
645 _bytes ( '<c>C</c>' ) )
646 result = c_tree . xslt ( style )
647 self . assertEqual ( self . _rootstring ( tree ) ,
648 _bytes ( '<a><b>B</b><c>C</c></a>' ) )
649 self . assertEqual ( self . _rootstring ( result ) ,
650 _bytes ( '<C>C</C>' ) )
651
653
654 xslt = etree . XSLT ( etree . XML ( """\
655 <xsl:stylesheet version="1.0"
656 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
657 <xsl:template match="/">
658 <test>TEXT<xsl:copy-of select="document('')//test"/></test>
659 </xsl:template>
660 </xsl:stylesheet>
661 """ ) )
662 result = xslt ( etree . XML ( '<a/>' ) )
663 root = result . getroot ( )
664 self . assertEqual ( root . tag ,
665 'test' )
666 self . assertEqual ( root [ 0 ] . tag ,
667 'test' )
668 self . assertEqual ( root [ 0 ] . text ,
669 'TEXT' )
670 self . assertEqual ( root [ 0 ] [ 0 ] . tag ,
671 '{http://www.w3.org/1999/XSL/Transform}copy-of' )
672
682
692
694 xslt = etree . XSLT ( etree . XML ( """\
695 <xsl:stylesheet version="1.0"
696 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
697 <xsl:template match="/">
698 <test>TEXT<xsl:copy-of select="document('uri:__junkfood__is__evil__')//test"/></test>
699 </xsl:template>
700 </xsl:stylesheet>
701 """ ) )
702
703 errors = None
704 try :
705 xslt ( etree . XML ( '<a/>' ) )
706 except etree . XSLTApplyError as exc :
707 errors = exc . error_log
708 else :
709 self . assertFalse ( True , "XSLT processing should have failed but didn't" )
710
711 self . assertTrue ( len ( errors ) )
712 for error in errors :
713 if ':ERROR:XSLT:' in str ( error ) :
714 break
715 else :
716 self . assertFalse ( True , 'No XSLT errors found in error log:\n%s' % errors )
717
719
720 assertEqual = self . assertEqual
721 called = { 'count' : 0 }
722 class TestResolver ( etree . Resolver ) :
723 def resolve ( self , url , id , context ) :
724 assertEqual ( url , 'file://ANYTHING' )
725 called [ 'count' ] += 1
726 return self . resolve_string ( '<CALLED/>' , context )
727
728 parser = etree . XMLParser ( )
729 parser . resolvers . add ( TestResolver ( ) )
730
731 xslt = etree . XSLT ( etree . XML ( _bytes ( """\
732 <xsl:stylesheet version="1.0"
733 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
734 xmlns:l="local">
735 <xsl:template match="/">
736 <test>
737 <xsl:for-each select="document('')//l:data/l:entry">
738 <xsl:copy-of select="document('file://ANYTHING')"/>
739 <xsl:copy>
740 <xsl:attribute name="value">
741 <xsl:value-of select="."/>
742 </xsl:attribute>
743 </xsl:copy>
744 </xsl:for-each>
745 </test>
746 </xsl:template>
747 <l:data>
748 <l:entry>A</l:entry>
749 <l:entry>B</l:entry>
750 </l:data>
751 </xsl:stylesheet>
752 """ ) , parser ) )
753
754 self . assertEqual ( called [ 'count' ] , 0 )
755 result = xslt ( etree . XML ( '<a/>' ) )
756 self . assertEqual ( called [ 'count' ] , 1 )
757
758 root = result . getroot ( )
759 self . assertEqual ( root . tag ,
760 'test' )
761 self . assertEqual ( len ( root ) , 4 )
762
763 self . assertEqual ( root [ 0 ] . tag ,
764 'CALLED' )
765 self . assertEqual ( root [ 1 ] . tag ,
766 '{local}entry' )
767 self . assertEqual ( root [ 1 ] . text ,
768 None )
769 self . assertEqual ( root [ 1 ] . get ( "value" ) ,
770 'A' )
771 self . assertEqual ( root [ 2 ] . tag ,
772 'CALLED' )
773 self . assertEqual ( root [ 3 ] . tag ,
774 '{local}entry' )
775 self . assertEqual ( root [ 3 ] . text ,
776 None )
777 self . assertEqual ( root [ 3 ] . get ( "value" ) ,
778 'B' )
779
781 assertEqual = self . assertEqual
782 called = { 'count' : 0 }
783 expected_url = None
784 class TestResolver ( etree . Resolver ) :
785 def resolve ( self , url , id , context ) :
786 assertEqual ( url , expected_url )
787 called [ 'count' ] += 1
788 return self . resolve_string ( '<CALLED/>' , context )
789
790 stylesheet_xml = _bytes ( """\
791 <xsl:stylesheet version="1.0"
792 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
793 xmlns:l="local">
794 <xsl:template match="/">
795 <xsl:copy-of select="document('test.xml')"/>
796 </xsl:template>
797 </xsl:stylesheet>
798 """ )
799
800 parser = etree . XMLParser ( )
801 parser . resolvers . add ( TestResolver ( ) )
802
803
804 expected_url = 'test.xml'
805 xslt = etree . XSLT ( etree . XML ( stylesheet_xml , parser ) )
806
807 self . assertEqual ( called [ 'count' ] , 0 )
808 result = xslt ( etree . XML ( '<a/>' ) )
809 self . assertEqual ( called [ 'count' ] , 1 )
810
811
812 called [ 'count' ] = 0
813 expected_url = 'MY/BASE/test.xml'
814 xslt = etree . XSLT ( etree . XML (
815 stylesheet_xml , parser ,
816 base_url = os . path . join ( 'MY' , 'BASE' , 'FILE' ) ) )
817
818 self . assertEqual ( called [ 'count' ] , 0 )
819 result = xslt ( etree . XML ( '<a/>' ) )
820 self . assertEqual ( called [ 'count' ] , 1 )
821
822
823 called [ 'count' ] = 0
824 expected_url = 'http://server.com/BASE/DIR/test.xml'
825 xslt = etree . XSLT ( etree . XML (
826 stylesheet_xml , parser ,
827 base_url = 'http://server.com/BASE/DIR/FILE' ) )
828
829 self . assertEqual ( called [ 'count' ] , 0 )
830 result = xslt ( etree . XML ( '<a/>' ) )
831 self . assertEqual ( called [ 'count' ] , 1 )
832
833
834 called [ 'count' ] = 0
835 expected_url = 'file://BASE/DIR/test.xml'
836 xslt = etree . XSLT ( etree . XML (
837 stylesheet_xml , parser ,
838 base_url = 'file://BASE/DIR/FILE' ) )
839
840 self . assertEqual ( called [ 'count' ] , 0 )
841 result = xslt ( etree . XML ( '<a/>' ) )
842 self . assertEqual ( called [ 'count' ] , 1 )
843
854
860
866
875
877 root = etree . XML ( _bytes ( '''\
878 <transform>
879 <widget displayType="fieldset"/>
880 </transform>''' ) )
881
882 xslt = etree . XSLT ( etree . XML ( _bytes ( '''\
883 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
884 <xsl:output method="html" indent="no"/>
885 <xsl:template match="/">
886 <html>
887 <xsl:apply-templates/>
888 </html>
889 </xsl:template>
890
891 <xsl:template match="widget">
892 <xsl:element name="{@displayType}"/>
893 </xsl:template>
894
895 </xsl:stylesheet>''' ) ) )
896
897 result = xslt ( root [ 0 ] )
898 root [ : ] = result . getroot ( ) [ : ]
899 del root
900
902 tree = self . parse ( '''\
903 <?xml version="1.0"?>
904 <?xml-stylesheet type="text/xsl" href="%s"?>
905 <a>
906 <b>B</b>
907 <c>C</c>
908 </a>''' % fileInTestDir ( "test1.xslt" ) )
909
910 style_root = tree . getroot ( ) . getprevious ( ) . parseXSL ( ) . getroot ( )
911 self . assertEqual ( "{http://www.w3.org/1999/XSL/Transform}stylesheet" ,
912 style_root . tag )
913
915
916 tree = self . parse ( '''\
917 <?xml version="1.0"?>
918 <?xml-stylesheet type="text/xsl" href="#style"?>
919 <a>
920 <b>B</b>
921 <c>C</c>
922 <xsl:stylesheet version="1.0" xml:id="style"
923 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
924 <xsl:template match="*" />
925 <xsl:template match="/">
926 <foo><xsl:value-of select="/a/b/text()" /></foo>
927 </xsl:template>
928 </xsl:stylesheet>
929 </a>''' )
930
931 style_root = tree . getroot ( ) . getprevious ( ) . parseXSL ( ) . getroot ( )
932 self . assertEqual ( "{http://www.w3.org/1999/XSL/Transform}stylesheet" ,
933 style_root . tag )
934
935 st = etree . XSLT ( style_root )
936 res = st ( tree )
937 self . assertEqual ( '''\
938 <?xml version="1.0"?>
939 <foo>B</foo>
940 ''' ,
941 str ( res ) )
942
944
945 tree = self . parse ( '''\
946 <?xml version="1.0"?>
947 <?xml-stylesheet type="text/xsl" href="#style"?>
948 <a>
949 <b>B</b>
950 <c>C</c>
951 </a>''' )
952
953 style = self . parse ( '''\
954 <xsl:stylesheet version="1.0" xml:id="style"
955 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
956 <xsl:template match="*" />
957 <xsl:template match="/">
958 <foo><xsl:value-of select="/a/b/text()" /></foo>
959 </xsl:template>
960 </xsl:stylesheet>
961 ''' )
962
963 tree . getroot ( ) . append ( style . getroot ( ) )
964
965 style_root = tree . getroot ( ) . getprevious ( ) . parseXSL ( ) . getroot ( )
966 self . assertEqual ( "{http://www.w3.org/1999/XSL/Transform}stylesheet" ,
967 style_root . tag )
968
969 st = etree . XSLT ( style_root )
970 res = st ( tree )
971 self . assertEqual ( '''\
972 <?xml version="1.0"?>
973 <foo>B</foo>
974 ''' ,
975 str ( res ) )
976
978 tree = self . parse ( '''\
979 <?xml version="1.0"?>
980 <?xml-stylesheet type="text/xsl" href="TEST"?>
981 <a>
982 <b>B</b>
983 <c>C</c>
984 </a>''' )
985
986 pi = tree . getroot ( ) . getprevious ( )
987 self . assertEqual ( "TEST" , pi . get ( "href" ) )
988
990 tree = self . parse ( '''\
991 <?xml version="1.0"?>
992 <?xml-stylesheet type="text/xsl" href="TEST"?>
993 <a>
994 <b>B</b>
995 <c>C</c>
996 </a>''' )
997
998 pi = tree . getroot ( ) . getprevious ( )
999 self . assertEqual ( "TEST" , pi . get ( "href" ) )
1000 self . assertEqual ( "text/xsl" , pi . get ( "type" ) )
1001 self . assertEqual ( None , pi . get ( "motz" ) )
1002
1004 tree = self . parse ( '''\
1005 <?xml version="1.0"?>
1006 <?xml-stylesheet href="TEST" type="text/xsl"?>
1007 <a>
1008 <b>B</b>
1009 <c>C</c>
1010 </a>''' )
1011
1012 pi = tree . getroot ( ) . getprevious ( )
1013 self . assertEqual ( "TEST" , pi . get ( "href" ) )
1014 self . assertEqual ( "text/xsl" , pi . get ( "type" ) )
1015 self . assertEqual ( None , pi . get ( "motz" ) )
1016
1018 tree = self . parse ( '''\
1019 <?xml version="1.0"?>
1020 <?xml-stylesheet type="text/xsl" href="TEST"?>
1021 <a>
1022 <b>B</b>
1023 <c>C</c>
1024 </a>''' )
1025
1026 pi = tree . getroot ( ) . getprevious ( )
1027 self . assertEqual ( None , pi . get ( "unknownattribute" ) )
1028
1030 tree = self . parse ( '''\
1031 <?xml version="1.0"?>
1032 <?xml-stylesheet type="text/xsl" href="TEST"?>
1033 <a>
1034 <b>B</b>
1035 <c>C</c>
1036 </a>''' )
1037
1038 pi = tree . getroot ( ) . getprevious ( )
1039 self . assertEqual ( "TEST" , pi . get ( "href" ) )
1040
1041 pi . set ( "href" , "TEST123" )
1042 self . assertEqual ( "TEST123" , pi . get ( "href" ) )
1043
1045 tree = self . parse ( '''\
1046 <?xml version="1.0"?>
1047 <?xml-stylesheet type="text/xsl"?>
1048 <a>
1049 <b>B</b>
1050 <c>C</c>
1051 </a>''' )
1052
1053 pi = tree . getroot ( ) . getprevious ( )
1054 self . assertEqual ( None , pi . get ( "href" ) )
1055
1056 pi . set ( "href" , "TEST" )
1057 self . assertEqual ( "TEST" , pi . get ( "href" ) )
1058
1060 """EXSLT tests"""
1061
1063 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
1064 style = self . parse ( '''\
1065 <xsl:stylesheet version="1.0"
1066 xmlns:str="http://exslt.org/strings"
1067 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1068 exclude-result-prefixes="str xsl">
1069 <xsl:template match="text()">
1070 <xsl:value-of select="str:align(string(.), '***', 'center')" />
1071 </xsl:template>
1072 <xsl:template match="*">
1073 <xsl:copy>
1074 <xsl:apply-templates/>
1075 </xsl:copy>
1076 </xsl:template>
1077 </xsl:stylesheet>''' )
1078
1079 st = etree . XSLT ( style )
1080 res = st ( tree )
1081 self . assertEqual ( '''\
1082 <?xml version="1.0"?>
1083 <a><b>*B*</b><c>*C*</c></a>
1084 ''' ,
1085 str ( res ) )
1086
1088 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
1089 style = self . parse ( '''\
1090 <xsl:stylesheet version = "1.0"
1091 xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
1092 xmlns:str="http://exslt.org/strings"
1093 extension-element-prefixes="str">
1094
1095 <xsl:template match="/">
1096 <h1 class="{str:replace('abc', 'b', 'x')}">test</h1>
1097 </xsl:template>
1098
1099 </xsl:stylesheet>''' )
1100
1101 st = etree . XSLT ( style )
1102 res = st ( tree )
1103 self . assertEqual ( str ( res ) , '''\
1104 <?xml version="1.0"?>
1105 <h1 class="axc">test</h1>
1106 ''' )
1107
1109 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
1110 style = self . parse ( '''\
1111 <xsl:stylesheet version="1.0"
1112 xmlns:math="http://exslt.org/math"
1113 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1114 exclude-result-prefixes="math xsl">
1115 <xsl:template match="*">
1116 <xsl:copy>
1117 <xsl:attribute name="pi">
1118 <xsl:value-of select="math:constant('PI', count(*)+2)"/>
1119 </xsl:attribute>
1120 <xsl:apply-templates/>
1121 </xsl:copy>
1122 </xsl:template>
1123 </xsl:stylesheet>''' )
1124
1125 st = etree . XSLT ( style )
1126 res = st ( tree )
1127 self . assertEqual ( '''\
1128 <?xml version="1.0"?>
1129 <a pi="3.14"><b pi="3">B</b><c pi="3">C</c></a>
1130 ''' ,
1131 str ( res ) )
1132
1134 xslt = etree . XSLT ( etree . XML ( _bytes ( """\
1135 <xsl:stylesheet version="1.0"
1136 xmlns:regexp="http://exslt.org/regular-expressions"
1137 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
1138 <xsl:template match="*">
1139 <test><xsl:copy-of select="*[regexp:test(string(.), '8.')]"/></test>
1140 </xsl:template>
1141 </xsl:stylesheet>
1142 """ ) ) )
1143 result = xslt ( etree . XML ( _bytes ( '<a><b>123</b><b>098</b><b>987</b></a>' ) ) )
1144 root = result . getroot ( )
1145 self . assertEqual ( root . tag ,
1146 'test' )
1147 self . assertEqual ( len ( root ) , 1 )
1148 self . assertEqual ( root [ 0 ] . tag ,
1149 'b' )
1150 self . assertEqual ( root [ 0 ] . text ,
1151 '987' )
1152
1154 xslt = etree . XSLT ( etree . XML ( """\
1155 <xsl:stylesheet version="1.0"
1156 xmlns:regexp="http://exslt.org/regular-expressions"
1157 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
1158 <xsl:template match="*">
1159 <test>
1160 <xsl:copy-of select="regexp:replace(string(.), 'd.', '', 'XX')"/>
1161 <xsl:text>-</xsl:text>
1162 <xsl:copy-of select="regexp:replace(string(.), 'd.', 'gi', 'XX')"/>
1163 </test>
1164 </xsl:template>
1165 </xsl:stylesheet>
1166 """ ) )
1167 result = xslt ( etree . XML ( _bytes ( '<a>abdCdEeDed</a>' ) ) )
1168 root = result . getroot ( )
1169 self . assertEqual ( root . tag ,
1170 'test' )
1171 self . assertEqual ( len ( root ) , 0 )
1172 self . assertEqual ( root . text , 'abXXdEeDed-abXXXXeXXd' )
1173
1175 xslt = etree . XSLT ( etree . XML ( """\
1176 <xsl:stylesheet version="1.0"
1177 xmlns:regexp="http://exslt.org/regular-expressions"
1178 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
1179 <xsl:template match="*">
1180 <test>
1181 <test1><xsl:copy-of select="regexp:match(string(.), 'd.')"/></test1>
1182 <test2><xsl:copy-of select="regexp:match(string(.), 'd.', 'g')"/></test2>
1183 <test2i><xsl:copy-of select="regexp:match(string(.), 'd.', 'gi')"/></test2i>
1184 </test>
1185 </xsl:template>
1186 </xsl:stylesheet>
1187 """ ) )
1188 result = xslt ( etree . XML ( _bytes ( '<a>abdCdEeDed</a>' ) ) )
1189 root = result . getroot ( )
1190 self . assertEqual ( root . tag , 'test' )
1191 self . assertEqual ( len ( root ) , 3 )
1192
1193 self . assertEqual ( len ( root [ 0 ] ) , 1 )
1194 self . assertEqual ( root [ 0 ] [ 0 ] . tag , 'match' )
1195 self . assertEqual ( root [ 0 ] [ 0 ] . text , 'dC' )
1196
1197 self . assertEqual ( len ( root [ 1 ] ) , 2 )
1198 self . assertEqual ( root [ 1 ] [ 0 ] . tag , 'match' )
1199 self . assertEqual ( root [ 1 ] [ 0 ] . text , 'dC' )
1200 self . assertEqual ( root [ 1 ] [ 1 ] . tag , 'match' )
1201 self . assertEqual ( root [ 1 ] [ 1 ] . text , 'dE' )
1202
1203 self . assertEqual ( len ( root [ 2 ] ) , 3 )
1204 self . assertEqual ( root [ 2 ] [ 0 ] . tag , 'match' )
1205 self . assertEqual ( root [ 2 ] [ 0 ] . text , 'dC' )
1206 self . assertEqual ( root [ 2 ] [ 1 ] . tag , 'match' )
1207 self . assertEqual ( root [ 2 ] [ 1 ] . text , 'dE' )
1208 self . assertEqual ( root [ 2 ] [ 2 ] . tag , 'match' )
1209 self . assertEqual ( root [ 2 ] [ 2 ] . text , 'De' )
1210
1212 xslt = etree . XSLT ( etree . XML ( _bytes ( """\
1213 <xsl:stylesheet version="1.0"
1214 xmlns:regexp="http://exslt.org/regular-expressions"
1215 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
1216 <xsl:template match="/">
1217 <test>
1218 <xsl:for-each select="regexp:match(
1219 '123abc567', '([0-9]+)([a-z]+)([0-9]+)' )">
1220 <test1><xsl:value-of select="."/></test1>
1221 </xsl:for-each>
1222 </test>
1223 </xsl:template>
1224 </xsl:stylesheet>
1225 """ ) ) )
1226 result = xslt ( etree . XML ( _bytes ( '<a/>' ) ) )
1227 root = result . getroot ( )
1228 self . assertEqual ( root . tag , 'test' )
1229 self . assertEqual ( len ( root ) , 4 )
1230
1231 self . assertEqual ( root [ 0 ] . text , "123abc567" )
1232 self . assertEqual ( root [ 1 ] . text , "123" )
1233 self . assertEqual ( root [ 2 ] . text , "abc" )
1234 self . assertEqual ( root [ 3 ] . text , "567" )
1235
1237
1238 xslt = etree . XSLT ( etree . XML ( _bytes ( """\
1239 <xsl:stylesheet version="1.0"
1240 xmlns:regexp="http://exslt.org/regular-expressions"
1241 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
1242 <xsl:template match="/">
1243 <test>
1244 <xsl:for-each select="regexp:match(
1245 'http://www.bayes.co.uk/xml/index.xml?/xml/utils/rechecker.xml',
1246 '(\\w+):\\/\\/([^/:]+)(:\\d*)?([^# ]*)')">
1247 <test1><xsl:value-of select="."/></test1>
1248 </xsl:for-each>
1249 </test>
1250 </xsl:template>
1251 </xsl:stylesheet>
1252 """ ) ) )
1253 result = xslt ( etree . XML ( _bytes ( '<a/>' ) ) )
1254 root = result . getroot ( )
1255 self . assertEqual ( root . tag , 'test' )
1256 self . assertEqual ( len ( root ) , 5 )
1257
1258 self . assertEqual (
1259 root [ 0 ] . text ,
1260 "http://www.bayes.co.uk/xml/index.xml?/xml/utils/rechecker.xml" )
1261 self . assertEqual (
1262 root [ 1 ] . text ,
1263 "http" )
1264 self . assertEqual (
1265 root [ 2 ] . text ,
1266 "www.bayes.co.uk" )
1267 self . assertFalse ( root [ 3 ] . text )
1268 self . assertEqual (
1269 root [ 4 ] . text ,
1270 "/xml/index.xml?/xml/utils/rechecker.xml" )
1271
1273
1274 xslt = etree . XSLT ( self . parse ( """\
1275 <xsl:stylesheet version="1.0"
1276 xmlns:regexp="http://exslt.org/regular-expressions"
1277 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
1278 <xsl:template match="/">
1279 <test>
1280 <xsl:for-each select="regexp:match(
1281 'This is a test string', '(\\w+)', 'g')">
1282 <test1><xsl:value-of select="."/></test1>
1283 </xsl:for-each>
1284 </test>
1285 </xsl:template>
1286 </xsl:stylesheet>
1287 """ ) )
1288 result = xslt ( etree . XML ( _bytes ( '<a/>' ) ) )
1289 root = result . getroot ( )
1290 self . assertEqual ( root . tag , 'test' )
1291 self . assertEqual ( len ( root ) , 5 )
1292
1293 self . assertEqual ( root [ 0 ] . text , "This" )
1294 self . assertEqual ( root [ 1 ] . text , "is" )
1295 self . assertEqual ( root [ 2 ] . text , "a" )
1296 self . assertEqual ( root [ 3 ] . text , "test" )
1297 self . assertEqual ( root [ 4 ] . text , "string" )
1298
1300
1301
1302 xslt = etree . XSLT ( etree . XML ( _bytes ( """\
1303 <xsl:stylesheet version="1.0"
1304 xmlns:regexp="http://exslt.org/regular-expressions"
1305 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
1306 <xsl:template match="/">
1307 <test>
1308 <xsl:for-each select="regexp:match(
1309 'This is a test string', '([a-z])+ ', 'g')">
1310 <test1><xsl:value-of select="."/></test1>
1311 </xsl:for-each>
1312 </test>
1313 </xsl:template>
1314 </xsl:stylesheet>
1315 """ ) ) )
1316 result = xslt ( etree . XML ( _bytes ( '<a/>' ) ) )
1317 root = result . getroot ( )
1318 self . assertEqual ( root . tag , 'test' )
1319 self . assertEqual ( len ( root ) , 4 )
1320
1321 self . assertEqual ( root [ 0 ] . text , "his" )
1322 self . assertEqual ( root [ 1 ] . text , "is" )
1323 self . assertEqual ( root [ 2 ] . text , "a" )
1324 self . assertEqual ( root [ 3 ] . text , "test" )
1325
1327
1328
1329 xslt = etree . XSLT ( etree . XML ( _bytes ( """\
1330 <xsl:stylesheet version="1.0"
1331 xmlns:regexp="http://exslt.org/regular-expressions"
1332 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
1333 <xsl:template match="/">
1334 <test>
1335 <xsl:for-each select="regexp:match(
1336 'This is a test string', '([a-z])+ ', 'gi')">
1337 <test1><xsl:value-of select="."/></test1>
1338 </xsl:for-each>
1339 </test>
1340 </xsl:template>
1341 </xsl:stylesheet>
1342 """ ) ) )
1343 result = xslt ( etree . XML ( _bytes ( '<a/>' ) ) )
1344 root = result . getroot ( )
1345 self . assertEqual ( root . tag , 'test' )
1346 self . assertEqual ( len ( root ) , 4 )
1347
1348 self . assertEqual ( root [ 0 ] . text , "This" )
1349 self . assertEqual ( root [ 1 ] . text , "is" )
1350 self . assertEqual ( root [ 2 ] . text , "a" )
1351 self . assertEqual ( root [ 3 ] . text , "test" )
1352
1353
1354 - class ETreeXSLTExtFuncTestCase ( HelperTestCase ) :
1355 """Tests for XPath extension functions in XSLT."""
1356
1357 - def test_extensions1 ( self ) :
1358 tree = self . parse ( '<a><b>B</b></a>' )
1359 style = self . parse ( '''\
1360 <xsl:stylesheet version="1.0"
1361 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1362 xmlns:myns="testns"
1363 exclude-result-prefixes="myns">
1364 <xsl:template match="a"><A><xsl:value-of select="myns:mytext(b)"/></A></xsl:template>
1365 </xsl:stylesheet>''' )
1366
1367 def mytext ( ctxt , values ) :
1368 return 'X' * len ( values )
1369
1370 result = tree . xslt ( style , { ( 'testns' , 'mytext' ) : mytext } )
1371 self . assertEqual ( self . _rootstring ( result ) ,
1372 _bytes ( '<A>X</A>' ) )
1373
1374 - def test_extensions2 ( self ) :
1375 tree = self . parse ( '<a><b>B</b></a>' )
1376 style = self . parse ( '''\
1377 <xsl:stylesheet version="1.0"
1378 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1379 xmlns:myns="testns"
1380 exclude-result-prefixes="myns">
1381 <xsl:template match="a"><A><xsl:value-of select="myns:mytext(b)"/></A></xsl:template>
1382 </xsl:stylesheet>''' )
1383
1384 def mytext ( ctxt , values ) :
1385 return 'X' * len ( 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>X</A>' ) )
1393
1395 tree = self . parse ( '<a><b>B</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="a">
1402 <xsl:variable name="content">
1403 <xsl:apply-templates/>
1404 </xsl:variable>
1405 <A><xsl:value-of select="myns:mytext($content)"/></A>
1406 </xsl:template>
1407 <xsl:template match="b"><xsl:copy>BBB</xsl:copy></xsl:template>
1408 </xsl:stylesheet>''' )
1409
1410 def mytext ( ctxt , values ) :
1411 for value in values :
1412 self . assertTrue ( hasattr ( value , 'tag' ) ,
1413 "%s is not an Element" % type ( value ) )
1414 self . assertEqual ( value . tag , 'b' )
1415 self . assertEqual ( value . text , 'BBB' )
1416 return 'X' . join ( [ el . tag for el in values ] )
1417
1418 namespace = etree . FunctionNamespace ( 'testns' )
1419 namespace [ 'mytext' ] = mytext
1420
1421 result = tree . xslt ( style )
1422 self . assertEqual ( self . _rootstring ( result ) ,
1423 _bytes ( '<A>bXb</A>' ) )
1424
1426 tree = self . parse ( '<a><b>B<c/>C</b><b/></a>' )
1427 style = self . parse ( '''\
1428 <xsl:stylesheet version="1.0"
1429 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1430 xmlns:myns="testns"
1431 exclude-result-prefixes="myns">
1432 <xsl:template match="b">
1433 <A><xsl:value-of select="myns:myext()"/></A>
1434 </xsl:template>
1435 </xsl:stylesheet>''' )
1436
1437 def extfunc ( ctxt ) :
1438 text_content = ctxt . context_node . xpath ( 'text()' )
1439 return 'x' . join ( text_content )
1440
1441 namespace = etree . FunctionNamespace ( 'testns' )
1442 namespace [ 'myext' ] = extfunc
1443
1444 result = tree . xslt ( style )
1445 self . assertEqual ( self . _rootstring ( result ) ,
1446 _bytes ( '<A>BxC</A>' ) )
1447
1449
1450 class Resolver ( etree . Resolver ) :
1451 def resolve ( self , system_url , public_id , context ) :
1452 assert system_url == 'extdoc.xml'
1453 return self . resolve_string ( b'<a><b>B<c/>C</b><b/></a>' , context )
1454
1455 parser = etree . XMLParser ( )
1456 parser . resolvers . add ( Resolver ( ) )
1457
1458 tree = self . parse ( b'<a><b/><b/></a>' )
1459 transform = etree . XSLT ( self . parse ( b'''\
1460 <xsl:stylesheet version="1.0"
1461 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1462 xmlns:mypre="testns"
1463 exclude-result-prefixes="mypre">
1464 <xsl:template match="b">
1465 <B><xsl:value-of select="mypre:myext()"/></B>
1466 </xsl:template>
1467 <xsl:template match="a">
1468 <A><xsl:apply-templates select="document('extdoc.xml')//b" /></A>
1469 </xsl:template>
1470 </xsl:stylesheet>''' , parser = parser ) )
1471
1472 def extfunc ( ctxt ) :
1473 text_content = ctxt . context_node . xpath ( 'text()' )
1474 return 'x' . join ( text_content )
1475
1476 namespace = etree . FunctionNamespace ( 'testns' )
1477 namespace [ 'myext' ] = extfunc
1478
1479 result = transform ( tree )
1480 self . assertEqual ( self . _rootstring ( result ) ,
1481 _bytes ( '<A><B>BxC</B><B/></A>' ) )
1482
1483
1484 - class ETreeXSLTExtElementTestCase ( HelperTestCase ) :
1485 """Tests for extension elements in XSLT."""
1486
1488 tree = self . parse ( '<a><b>B</b></a>' )
1489 style = self . parse ( '''\
1490 <xsl:stylesheet version="1.0"
1491 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1492 xmlns:myns="testns"
1493 extension-element-prefixes="myns"
1494 exclude-result-prefixes="myns">
1495 <xsl:template match="a">
1496 <A><myns:myext>b</myns:myext></A>
1497 </xsl:template>
1498 </xsl:stylesheet>''' )
1499
1500 class MyExt ( etree . XSLTExtension ) :
1501 def execute ( self , context , self_node , input_node , output_parent ) :
1502 child = etree . Element ( self_node . text )
1503 child . text = 'X'
1504 output_parent . append ( child )
1505
1506 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1507
1508 result = tree . xslt ( style , extensions = extensions )
1509 self . assertEqual ( self . _rootstring ( result ) ,
1510 _bytes ( '<A><b>X</b></A>' ) )
1511
1513 tree = self . parse ( '<a><b>B</b></a>' )
1514 style = self . parse ( '''\
1515 <xsl:stylesheet version="1.0"
1516 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1517 xmlns:myns="testns"
1518 extension-element-prefixes="myns"
1519 exclude-result-prefixes="myns">
1520 <xsl:template match="/">
1521 <A><myns:myext>b</myns:myext></A>
1522 </xsl:template>
1523 </xsl:stylesheet>''' )
1524
1525 tags = [ ]
1526
1527 class MyExt ( etree . XSLTExtension ) :
1528 def execute ( self , context , self_node , input_node , output_parent ) :
1529 tags . append ( input_node . tag )
1530
1531 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1532
1533 result = tree . xslt ( style , extensions = extensions )
1534 self . assertEqual ( tags , [ 'a' ] )
1535
1537 tree = self . parse ( '<?test toast?><a><!--a comment--><?another pi?></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="/">
1545 <ROOT><xsl:apply-templates /></ROOT>
1546 </xsl:template>
1547 <xsl:template match="comment()">
1548 <A><myns:myext>b</myns:myext></A>
1549 </xsl:template>
1550 <xsl:template match="processing-instruction()">
1551 <A><myns:myext>b</myns:myext></A>
1552 </xsl:template>
1553 </xsl:stylesheet>''' )
1554
1555 text = [ ]
1556
1557 class MyExt ( etree . XSLTExtension ) :
1558 def execute ( self , context , self_node , input_node , output_parent ) :
1559 text . append ( input_node . text )
1560
1561 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1562
1563 result = tree . xslt ( style , extensions = extensions )
1564 self . assertEqual ( text , [ 'toast' , 'a comment' , 'pi' ] )
1565
1567
1568 tree = self . parse ( '<a test="A"><b attr="B"/></a>' )
1569 style = self . parse ( '''\
1570 <xsl:stylesheet version="1.0"
1571 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1572 xmlns:myns="testns"
1573 extension-element-prefixes="myns"
1574 exclude-result-prefixes="myns">
1575 <xsl:template match="@test">
1576 <A><myns:myext>b</myns:myext></A>
1577 </xsl:template>
1578 <xsl:template match="@attr">
1579 <A><myns:myext>b</myns:myext></A>
1580 </xsl:template>
1581 </xsl:stylesheet>''' )
1582
1583 text = [ ]
1584
1585 class MyExt ( etree . XSLTExtension ) :
1586 def execute ( self , context , self_node , attr_value , output_parent ) :
1587 text . append ( attr_value )
1588
1589 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1590
1591 result = tree . xslt ( style , extensions = extensions )
1592 self . assertEqual ( text , [ 'A' , 'B' ] )
1593
1595 tree = self . parse ( '<a><b>B</b></a>' )
1596 style = self . parse ( '''\
1597 <xsl:stylesheet version="1.0"
1598 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1599 xmlns:myns="testns"
1600 extension-element-prefixes="myns">
1601 <xsl:template match="a">
1602 <A><myns:myext><x>X</x><y>Y</y><z/></myns:myext></A>
1603 </xsl:template>
1604 </xsl:stylesheet>''' )
1605
1606 class MyExt ( etree . XSLTExtension ) :
1607 def execute ( self , context , self_node , input_node , output_parent ) :
1608 output_parent . extend ( list ( self_node ) [ 1 : ] )
1609
1610 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1611
1612 result = tree . xslt ( style , extensions = extensions )
1613 self . assertEqual ( self . _rootstring ( result ) ,
1614 _bytes ( '<A><y>Y</y><z/></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" />
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 if isinstance ( result , basestring ) :
1635 el = etree . Element ( "T" )
1636 el . text = result
1637 else :
1638 el = result
1639 output_parent . append ( el )
1640
1641 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1642
1643 result = tree . xslt ( style , extensions = extensions )
1644 self . assertEqual ( self . _rootstring ( result ) ,
1645 _bytes ( '<A><T>Y</T><T>XYZ</T></A>' ) )
1646
1648 tree = self . parse ( '<a><b>B</b></a>' )
1649 style = self . parse ( '''\
1650 <xsl:stylesheet version="1.0"
1651 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1652 xmlns:myns="testns"
1653 extension-element-prefixes="myns">
1654 <xsl:template match="a">
1655 <A><myns:myext><x>X</x><y>Y</y><z/></myns:myext></A>
1656 </xsl:template>
1657 <xsl:template match="x"><X/></xsl:template>
1658 <xsl:template match="z">XYZ</xsl:template>
1659 </xsl:stylesheet>''' )
1660
1661 class MyExt ( etree . XSLTExtension ) :
1662 def execute ( self , context , self_node , input_node , output_parent ) :
1663 for child in self_node :
1664 for result in self . apply_templates ( context , child ,
1665 elements_only = True ) :
1666 assert not isinstance ( result , basestring )
1667 output_parent . append ( result )
1668
1669 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1670
1671 result = tree . xslt ( style , extensions = extensions )
1672 self . assertEqual ( self . _rootstring ( result ) ,
1673 _bytes ( '<A><X/></A>' ) )
1674
1676 tree = self . parse ( '<a><b>B</b></a>' )
1677 style = self . parse ( '''\
1678 <xsl:stylesheet version="1.0"
1679 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1680 xmlns:myns="testns"
1681 extension-element-prefixes="myns">
1682 <xsl:template match="a">
1683 <A><myns:myext><x>X</x><y>Y</y><z/></myns:myext></A>
1684 </xsl:template>
1685 <xsl:template match="x"><X/></xsl:template>
1686 <xsl:template match="y"><xsl:text> </xsl:text></xsl:template>
1687 <xsl:template match="z">XYZ</xsl:template>
1688 </xsl:stylesheet>''' )
1689
1690 class MyExt ( etree . XSLTExtension ) :
1691 def execute ( self , context , self_node , input_node , output_parent ) :
1692 for child in self_node :
1693 for result in self . apply_templates ( context , child ,
1694 remove_blank_text = True ) :
1695 if isinstance ( result , basestring ) :
1696 assert result . strip ( )
1697 el = etree . Element ( "T" )
1698 el . text = result
1699 else :
1700 el = result
1701 output_parent . append ( el )
1702
1703 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1704
1705 result = tree . xslt ( style , extensions = extensions )
1706 self . assertEqual ( self . _rootstring ( result ) ,
1707 _bytes ( '<A><X/><T>XYZ</T></A>' ) )
1708
1710 tree = self . parse ( '<a><b>B</b></a>' )
1711 style = self . parse ( '''\
1712 <xsl:stylesheet version="1.0"
1713 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1714 xmlns:myns="testns"
1715 extension-element-prefixes="myns">
1716 <xsl:template match="a">
1717 <A><myns:myext><x>X</x><y>Y</y><z/></myns:myext></A>
1718 </xsl:template>
1719 <xsl:template match="x" />
1720 <xsl:template match="z">XYZ</xsl:template>
1721 </xsl:stylesheet>''' )
1722
1723 class MyExt ( etree . XSLTExtension ) :
1724 def execute ( self , context , self_node , input_node , output_parent ) :
1725 for child in self_node :
1726 self . apply_templates ( context , child , output_parent )
1727
1728 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1729
1730 result = tree . xslt ( style , extensions = extensions )
1731 self . assertEqual ( self . _rootstring ( result ) ,
1732 _bytes ( '<A>YXYZ</A>' ) )
1733
1735 tree = self . parse ( '<a><b>B</b></a>' )
1736 style = self . parse ( '''\
1737 <xsl:stylesheet version="1.0"
1738 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1739 xmlns:myns="testns"
1740 extension-element-prefixes="myns">
1741 <xsl:template match="a">
1742 <myns:myext><x>X</x><y>Y</y><z/></myns:myext>
1743 </xsl:template>
1744 <xsl:template match="x"><xsl:processing-instruction name="test">TEST</xsl:processing-instruction></xsl:template>
1745 <xsl:template match="y"><Y>XYZ</Y></xsl:template>
1746 <xsl:template match="z"><xsl:comment>TEST</xsl:comment></xsl:template>
1747 </xsl:stylesheet>''' )
1748
1749 class MyExt ( etree . XSLTExtension ) :
1750 def execute ( self , context , self_node , input_node , output_parent ) :
1751 for child in self_node :
1752 self . apply_templates ( context , child , output_parent )
1753
1754 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1755
1756 result = tree . xslt ( style , extensions = extensions )
1757 self . assertEqual ( etree . tostring ( result ) ,
1758 _bytes ( '<?test TEST?><Y>XYZ</Y><!--TEST-->' ) )
1759
1761 tree = self . parse ( '<a><b>E</b></a>' )
1762 style = self . parse ( '''\
1763 <xsl:stylesheet version="1.0"
1764 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1765 xmlns:myns="testns"
1766 extension-element-prefixes="myns">
1767 <xsl:template match="a">
1768 <xsl:variable name="testvar">yo</xsl:variable>
1769 <A>
1770 <myns:myext>
1771 <xsl:attribute name="attr">
1772 <xsl:value-of select="$testvar" />
1773 </xsl:attribute>
1774 <B>
1775 <xsl:choose>
1776 <xsl:when test="1 = 2"><C/></xsl:when>
1777 <xsl:otherwise><D><xsl:value-of select="b/text()" /></D></xsl:otherwise>
1778 </xsl:choose>
1779 </B>
1780 </myns:myext>
1781 </A>
1782 </xsl:template>
1783 </xsl:stylesheet>''' )
1784
1785 class MyExt ( etree . XSLTExtension ) :
1786 def execute ( self , context , self_node , input_node , output_parent ) :
1787 el = etree . Element ( 'MY' )
1788 self . process_children ( context , el )
1789 output_parent . append ( el )
1790
1791 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1792
1793 result = tree . xslt ( style , extensions = extensions )
1794 self . assertEqual ( self . _rootstring ( result ) ,
1795 _bytes ( '<A><MYattr="yo"><B><D>E</D></B></MY></A>' ) )
1796
1798 tree = self . parse ( '<a/>' )
1799 style = self . parse ( '''\
1800 <xsl:stylesheet version="1.0"
1801 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1802 xmlns:myns="testns"
1803 extension-element-prefixes="myns">
1804 <xsl:template match="a">
1805 <myns:myext>
1806 <A/>
1807 </myns:myext>
1808 </xsl:template>
1809 </xsl:stylesheet>''' )
1810
1811 class MyExt ( etree . XSLTExtension ) :
1812 def execute ( self , context , self_node , input_node , output_parent ) :
1813 self . process_children ( context , output_parent )
1814
1815 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1816
1817 result = tree . xslt ( style , extensions = extensions )
1818 self . assertEqual ( self . _rootstring ( result ) ,
1819 _bytes ( '<A/>' ) )
1820
1822 tree = self . parse ( '<a/>' )
1823 style = self . parse ( '''\
1824 <xsl:stylesheet version="1.0"
1825 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1826 xmlns:myns="testns"
1827 extension-element-prefixes="myns">
1828 <xsl:template match="a">
1829 <myns:myext>
1830 <A/>
1831 </myns:myext>
1832 </xsl:template>
1833 </xsl:stylesheet>''' )
1834
1835 class MyExt ( etree . XSLTExtension ) :
1836 def execute ( self , context , self_node , input_node , output_parent ) :
1837 self . process_children ( context , self_node )
1838
1839 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1840
1841 self . assertRaises ( TypeError , tree . xslt , style , extensions = extensions )
1842
1844 tree = self . parse ( '<a/>' )
1845 style = self . parse ( '''\
1846 <xsl:stylesheet version="1.0"
1847 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1848 xmlns:myns="testns"
1849 extension-element-prefixes="myns">
1850 <xsl:template match="a">
1851 <myns:myext>
1852 <A><myns:myext><B/></myns:myext></A>
1853 </myns:myext>
1854 </xsl:template>
1855 </xsl:stylesheet>''' )
1856
1857 class MyExt ( etree . XSLTExtension ) :
1858 callback_call_counter = 0
1859 def execute ( self , context , self_node , input_node , output_parent ) :
1860 self . callback_call_counter += 1
1861 el = etree . Element ( 'MY' , n = str ( self . callback_call_counter ) )
1862 self . process_children ( context , el )
1863 output_parent . append ( el )
1864
1865 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1866
1867 result = tree . xslt ( style , extensions = extensions )
1868 self . assertEqual ( self . _rootstring ( result ) ,
1869 _bytes ( '<MYn="1"><A><MYn="2"><B/></MY></A></MY>' ) )
1870
1872 tree = self . parse ( '<a><b>B</b></a>' )
1873 style = self . parse ( '''\
1874 <xsl:stylesheet version="1.0"
1875 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1876 xmlns:myns="testns"
1877 extension-element-prefixes="myns"
1878 exclude-result-prefixes="myns">
1879 <xsl:template match="a">
1880 <A><myns:myext>b</myns:myext></A>
1881 </xsl:template>
1882 </xsl:stylesheet>''' )
1883
1884 class MyError ( Exception ) :
1885 pass
1886
1887 class MyExt ( etree . XSLTExtension ) :
1888 def execute ( self , context , self_node , input_node , output_parent ) :
1889 raise MyError ( "expected!" )
1890
1891 extensions = { ( 'testns' , 'myext' ) : MyExt ( ) }
1892 self . assertRaises ( MyError , tree . xslt , style , extensions = extensions )
1893
1894
1895
1897 tree = self . parse ( """\
1898 <text>
1899 <par>This is <format>arbitrary</format> text in a paragraph</par>
1900 </text>""" )
1901 style = self . parse ( """\
1902 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my" extension-element-prefixes="my" version="1.0">
1903 <xsl:template match="par">
1904 <my:par><xsl:apply-templates /></my:par>
1905 </xsl:template>
1906 <xsl:template match="format">
1907 <my:format><xsl:apply-templates /></my:format>
1908 </xsl:template>
1909 </xsl:stylesheet>
1910 """ )
1911 test = self
1912 calls = [ ]
1913
1914 class ExtMyPar ( etree . XSLTExtension ) :
1915 def execute ( self , context , self_node , input_node , output_parent ) :
1916 calls . append ( 'par' )
1917 p = etree . Element ( "p" )
1918 p . attrib [ "style" ] = "color:red"
1919 self . process_children ( context , p )
1920 output_parent . append ( p )
1921
1922 class ExtMyFormat ( etree . XSLTExtension ) :
1923 def execute ( self , context , self_node , input_node , output_parent ) :
1924 calls . append ( 'format' )
1925 content = self . process_children ( context )
1926 test . assertEqual ( 1 , len ( content ) )
1927 test . assertEqual ( 'arbitrary' , content [ 0 ] )
1928 test . assertEqual ( 'This is ' , output_parent . text )
1929 output_parent . text += '*-%s-*' % content [ 0 ]
1930
1931 extensions = { ( "my" , "par" ) : ExtMyPar ( ) , ( "my" , "format" ) : ExtMyFormat ( ) }
1932 transform = etree . XSLT ( style , extensions = extensions )
1933 result = transform ( tree )
1934 self . assertEqual ( [ 'par' , 'format' ] , calls )
1935 self . assertEqual (
1936 b'<p style="color:red">This is *-arbitrary-* text in a paragraph</p>\n' ,
1937 etree . tostring ( result ) )
1938
1941 """XSLT tests for etree under Python 3"""
1942
1943 pytestmark = skipif ( 'sys.version_info < (3,0)' )
1944
1946 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
1947 style = self . parse ( '''\
1948 <xsl:stylesheet version="1.0"
1949 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
1950 <xsl:template match="*" />
1951 <xsl:template match="/">
1952 <foo><xsl:value-of select="/a/b/text()" /></foo>
1953 </xsl:template>
1954 </xsl:stylesheet>''' )
1955
1956 st = etree . XSLT ( style )
1957 res = st ( tree )
1958 self . assertEqual ( _bytes ( '''\
1959 <?xml version="1.0"?>
1960 <foo>B</foo>
1961 ''' ) ,
1962 bytes ( res ) )
1963
1965 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
1966 style = self . parse ( '''\
1967 <xsl:stylesheet version="1.0"
1968 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
1969 <xsl:template match="*" />
1970 <xsl:template match="/">
1971 <foo><xsl:value-of select="/a/b/text()" /></foo>
1972 </xsl:template>
1973 </xsl:stylesheet>''' )
1974
1975 st = etree . XSLT ( style )
1976 res = st ( tree )
1977 self . assertEqual ( _bytes ( '''\
1978 <?xml version="1.0"?>
1979 <foo>B</foo>
1980 ''' ) ,
1981 bytearray ( res ) )
1982
1984 tree = self . parse ( '<a><b>B</b><c>C</c></a>' )
1985 style = self . parse ( '''\
1986 <xsl:stylesheet version="1.0"
1987 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
1988 <xsl:template match="*" />
1989 <xsl:template match="/">
1990 <foo><xsl:value-of select="/a/b/text()" /></foo>
1991 </xsl:template>
1992 </xsl:stylesheet>''' )
1993
1994 st = etree . XSLT ( style )
1995 res = st ( tree )
1996 self . assertEqual ( _bytes ( '''\
1997 <?xml version="1.0"?>
1998 <foo>B</foo>
1999 ''' ) ,
2000 bytes ( memoryview ( res ) ) )
2001
2016
2017 if __name__ == '__main__' :
2018 print ( 'to test use test.py %s' % __file__ )
2019