# -*- coding: utf-8 -*-
"""
Test cases related to XSLT processing
"""
from __future__ import absolute_import
import io
import sys
import copy
import gzip
import os.path
import unittest
import contextlib
from textwrap import dedent
from tempfile import NamedTemporaryFile, mkdtemp
is_python3 = sys.version_info[0] >= 3
try:
unicode
except NameError: # Python 3
unicode = str
try:
basestring
except NameError: # Python 3
basestring = str
from .common_imports import (
etree, BytesIO, HelperTestCase, fileInTestDir, _bytes, make_doctest, skipif, SimpleFSPath
)
class ETreeXSLTTestCase(HelperTestCase):
"""XSLT tests etree"""
def test_xslt(self):
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
res = st(tree)
self.assertEqual('''\
B
''',
str(res))
def test_xslt_elementtree_error(self):
self.assertRaises(ValueError, etree.XSLT, etree.ElementTree())
def test_xslt_input_none(self):
self.assertRaises(TypeError, etree.XSLT, None)
def test_xslt_invalid_stylesheet(self):
style = self.parse('''\
''')
self.assertRaises(
etree.XSLTParseError, etree.XSLT, style)
def test_xslt_copy(self):
tree = self.parse('BC')
style = self.parse('''\
''')
transform = etree.XSLT(style)
res = transform(tree)
self.assertEqual('''\
B
''',
str(res))
transform_copy = copy.deepcopy(transform)
res = transform_copy(tree)
self.assertEqual('''\
B
''',
str(res))
transform = etree.XSLT(style)
res = transform(tree)
self.assertEqual('''\
B
''',
str(res))
@contextlib.contextmanager
def _xslt_setup(
self, encoding='UTF-16', expected_encoding=None,
expected='\\uF8D2'):
tree = self.parse(_bytes('\\uF8D2\\uF8D2'
).decode("unicode_escape"))
style = self.parse('''\
''' % {'ENCODING': encoding})
st = etree.XSLT(style)
res = st(tree)
expected = _bytes(dedent(expected).strip()).decode("unicode_escape").replace('\n', '') % {
'ENCODING': expected_encoding or encoding,
}
data = [res]
yield data
self.assertEqual(expected, data[0].replace('\n', ''))
def test_xslt_utf8(self):
with self._xslt_setup(encoding='UTF-8') as res:
res[0] = unicode(bytes(res[0]), 'UTF-8')
assert 'UTF-8' in res[0]
def test_xslt_encoding(self):
with self._xslt_setup() as res:
res[0] = unicode(bytes(res[0]), 'UTF-16')
assert 'UTF-16' in res[0]
def test_xslt_encoding_override(self):
with self._xslt_setup(encoding='UTF-8', expected_encoding='UTF-16') as res:
f = BytesIO()
res[0].write(f, encoding='UTF-16')
if is_python3:
output = str(f.getvalue(), 'UTF-16')
else:
output = unicode(str(f.getvalue()), 'UTF-16')
res[0] = output.replace("'", '"')
def test_xslt_write_output_bytesio(self):
with self._xslt_setup() as res:
f = BytesIO()
res[0].write_output(f)
res[0] = f.getvalue().decode('UTF-16')
def test_xslt_write_output_failure(self):
class Writer(object):
def write(self, data):
raise ValueError("FAILED!")
try:
with self._xslt_setup() as res:
res[0].write_output(Writer())
except ValueError as exc:
self.assertTrue("FAILED!" in str(exc), exc)
else:
self.assertTrue(False, "exception not raised")
def test_xslt_write_output_file(self):
with self._xslt_setup() as res:
f = NamedTemporaryFile(delete=False)
try:
try:
res[0].write_output(f)
finally:
f.close()
with io.open(f.name, encoding='UTF-16') as f:
res[0] = f.read()
finally:
os.unlink(f.name)
def test_xslt_write_output_file_path(self):
with self._xslt_setup() as res:
f = NamedTemporaryFile(delete=False)
try:
try:
res[0].write_output(f.name, compression=9)
finally:
f.close()
with gzip.GzipFile(f.name) as f:
res[0] = f.read().decode("UTF-16")
finally:
os.unlink(f.name)
def test_xslt_write_output_file_pathlike(self):
with self._xslt_setup() as res:
f = NamedTemporaryFile(delete=False)
try:
try:
res[0].write_output(SimpleFSPath(f.name), compression=9)
finally:
f.close()
with gzip.GzipFile(f.name) as f:
res[0] = f.read().decode("UTF-16")
finally:
os.unlink(f.name)
def test_xslt_write_output_file_path_urlescaped(self):
# libxml2 should not unescape file paths.
with self._xslt_setup() as res:
f = NamedTemporaryFile(prefix='tmp%2e', suffix='.xml.gz', delete=False)
try:
try:
res[0].write_output(f.name, compression=3)
finally:
f.close()
with gzip.GzipFile(f.name) as f:
res[0] = f.read().decode("UTF-16")
finally:
os.unlink(f.name)
def test_xslt_write_output_file_path_urlescaped_plus(self):
with self._xslt_setup() as res:
f = NamedTemporaryFile(prefix='p+%2e', suffix='.xml.gz', delete=False)
try:
try:
res[0].write_output(f.name, compression=1)
finally:
f.close()
with gzip.GzipFile(f.name) as f:
res[0] = f.read().decode("UTF-16")
finally:
os.unlink(f.name)
def test_xslt_write_output_file_oserror(self):
with self._xslt_setup(expected='') as res:
tempdir = mkdtemp()
try:
res[0].write_output(os.path.join(tempdir, 'missing_subdir', 'out.xml'))
except IOError:
res[0] = ''
else:
self.fail("IOError not raised")
finally:
os.rmdir(tempdir)
def test_xslt_unicode(self):
expected = '''
\\uF8D2
'''
with self._xslt_setup(expected=expected) as res:
res[0] = unicode(res[0])
def test_xslt_unicode_standalone(self):
tree = self.parse(_bytes('\\uF8D2\\uF8D2'
).decode("unicode_escape"))
style = self.parse('''\
''')
st = etree.XSLT(style)
res = st(tree)
expected = _bytes('''\
\\uF8D2
''').decode("unicode_escape")
self.assertEqual(expected,
unicode(res))
def test_xslt_input(self):
style = self.parse('''\
''')
st = etree.XSLT(style)
st = etree.XSLT(style.getroot())
def test_xslt_input_partial_doc(self):
style = self.parse('''\
''')
self.assertRaises(etree.XSLTParseError, etree.XSLT, style)
root_node = style.getroot()
self.assertRaises(etree.XSLTParseError, etree.XSLT, root_node)
st = etree.XSLT(root_node[0])
def test_xslt_broken(self):
style = self.parse('''\
''')
self.assertRaises(etree.XSLTParseError,
etree.XSLT, style)
def test_xslt_parsing_error_log(self):
tree = self.parse('')
style = self.parse('''\
''')
self.assertRaises(etree.XSLTParseError,
etree.XSLT, style)
exc = None
try:
etree.XSLT(style)
except etree.XSLTParseError as e:
exc = e
else:
self.assertFalse(True, "XSLT processing should have failed but didn't")
self.assertTrue(exc is not None)
self.assertTrue(len(exc.error_log))
for error in exc.error_log:
self.assertTrue(':ERROR:XSLT:' in str(error))
def test_xslt_apply_error_log(self):
tree = self.parse('')
style = self.parse('''\
FAIL
''')
self.assertRaises(etree.XSLTApplyError,
etree.XSLT(style), tree)
transform = etree.XSLT(style)
exc = None
try:
transform(tree)
except etree.XSLTApplyError as e:
exc = e
else:
self.assertFalse(True, "XSLT processing should have failed but didn't")
self.assertTrue(exc is not None)
self.assertTrue(len(exc.error_log))
self.assertEqual(len(transform.error_log), len(exc.error_log))
for error in exc.error_log:
self.assertTrue(':ERROR:XSLT:' in str(error))
for error in transform.error_log:
self.assertTrue(':ERROR:XSLT:' in str(error))
def test_xslt_parameters(self):
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
res = st(tree, bar="'Bar'")
self.assertEqual('''\
Bar
''',
str(res))
def test_xslt_string_parameters(self):
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
res = st(tree, bar=etree.XSLT.strparam('''it's me, "Bar"'''))
self.assertEqual('''\
it's me, "Bar"
''',
str(res))
def test_xslt_parameter_invalid(self):
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
res = self.assertRaises(etree.XSLTApplyError,
st, tree, bar="")
res = self.assertRaises(etree.XSLTApplyError,
st, tree, bar="....")
def test_xslt_parameter_missing(self):
# apply() without needed parameter will lead to XSLTApplyError
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
# at least libxslt 1.1.28 produces this error, earlier ones (e.g. 1.1.18) might not ...
self.assertRaises(etree.XSLTApplyError, st.apply, tree)
def test_xslt_multiple_parameters(self):
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
res = st(tree, bar="'Bar'", baz="'Baz'")
self.assertEqual('''\
BarBaz
''',
str(res))
def test_xslt_parameter_xpath(self):
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
res = st(tree, bar="/a/b/text()")
self.assertEqual('''\
B
''',
str(res))
def test_xslt_parameter_xpath_object(self):
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
res = st(tree, bar=etree.XPath("/a/b/text()"))
self.assertEqual('''\
B
''',
str(res))
def test_xslt_default_parameters(self):
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
res = st(tree, bar="'Bar'")
self.assertEqual('''\
Bar
''',
str(res))
res = st(tree)
self.assertEqual('''\
Default
''',
str(res))
def test_xslt_html_output(self):
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
res = st(tree)
self.assertEqual('
B',
str(res).strip())
def test_xslt_include(self):
tree = etree.parse(fileInTestDir('test1.xslt'))
st = etree.XSLT(tree)
def test_xslt_include_from_filelike(self):
f = open(fileInTestDir('test1.xslt'), 'rb')
tree = etree.parse(f)
f.close()
st = etree.XSLT(tree)
def test_xslt_multiple_transforms(self):
xml = ''
xslt = '''\
Some text
'''
source = self.parse(xml)
styledoc = self.parse(xslt)
style = etree.XSLT(styledoc)
result = style(source)
etree.tostring(result.getroot())
source = self.parse(xml)
styledoc = self.parse(xslt)
style = etree.XSLT(styledoc)
result = style(source)
etree.tostring(result.getroot())
def test_xslt_repeat_transform(self):
xml = ''
xslt = '''\
Some text
'''
source = self.parse(xml)
styledoc = self.parse(xslt)
transform = etree.XSLT(styledoc)
result = transform(source)
result = transform(source)
etree.tostring(result.getroot())
result = transform(source)
etree.tostring(result.getroot())
str(result)
result1 = transform(source)
result2 = transform(source)
self.assertEqual(str(result1), str(result2))
result = transform(source)
str(result)
def test_xslt_empty(self):
# could segfault if result contains "empty document"
xml = ''
xslt = '''
'''
source = self.parse(xml)
styledoc = self.parse(xslt)
style = etree.XSLT(styledoc)
result = style(source)
self.assertEqual('', str(result))
def test_xslt_message(self):
xml = ''
xslt = '''
TEST TEST TEST
'''
source = self.parse(xml)
styledoc = self.parse(xslt)
style = etree.XSLT(styledoc)
result = style(source)
self.assertEqual('', str(result))
self.assertTrue("TEST TEST TEST" in [entry.message
for entry in style.error_log])
def test_xslt_message_terminate(self):
xml = ''
xslt = '''
TEST TEST TEST
'''
source = self.parse(xml)
styledoc = self.parse(xslt)
style = etree.XSLT(styledoc)
self.assertRaises(etree.XSLTApplyError, style, source)
self.assertTrue("TEST TEST TEST" in [entry.message
for entry in style.error_log])
def test_xslt_shortcut(self):
tree = self.parse('BC')
style = self.parse('''\
''')
result = tree.xslt(style, bar="'Bar'", baz="'Baz'")
self.assertEqual(
_bytes('BarBaz'),
etree.tostring(result.getroot()))
def test_multiple_elementrees(self):
tree = self.parse('BC')
style = self.parse('''\
''')
self.assertEqual(self._rootstring(tree),
_bytes('BC'))
result = tree.xslt(style)
self.assertEqual(self._rootstring(tree),
_bytes('BC'))
self.assertEqual(self._rootstring(result),
_bytes('BC'))
b_tree = etree.ElementTree(tree.getroot()[0])
self.assertEqual(self._rootstring(b_tree),
_bytes('B'))
result = b_tree.xslt(style)
self.assertEqual(self._rootstring(tree),
_bytes('BC'))
self.assertEqual(self._rootstring(result),
_bytes('B'))
c_tree = etree.ElementTree(tree.getroot()[1])
self.assertEqual(self._rootstring(c_tree),
_bytes('C'))
result = c_tree.xslt(style)
self.assertEqual(self._rootstring(tree),
_bytes('BC'))
self.assertEqual(self._rootstring(result),
_bytes('C'))
def test_xslt_document_XML(self):
# make sure document('') works from parsed strings
xslt = etree.XSLT(etree.XML("""\
TEXT
"""))
result = xslt(etree.XML(''))
root = result.getroot()
self.assertEqual(root.tag,
'test')
self.assertEqual(root[0].tag,
'test')
self.assertEqual(root[0].text,
'TEXT')
self.assertEqual(root[0][0].tag,
'{http://www.w3.org/1999/XSL/Transform}copy-of')
def test_xslt_document_parse(self):
# make sure document('') works from loaded files
xslt = etree.XSLT(etree.parse(fileInTestDir("test-document.xslt")))
result = xslt(etree.XML(''))
root = result.getroot()
self.assertEqual(root.tag,
'test')
self.assertEqual(root[0].tag,
'{http://www.w3.org/1999/XSL/Transform}stylesheet')
def test_xslt_document_elementtree(self):
# make sure document('') works from loaded files
xslt = etree.XSLT(etree.ElementTree(file=fileInTestDir("test-document.xslt")))
result = xslt(etree.XML(''))
root = result.getroot()
self.assertEqual(root.tag,
'test')
self.assertEqual(root[0].tag,
'{http://www.w3.org/1999/XSL/Transform}stylesheet')
def test_xslt_document_error(self):
xslt = etree.XSLT(etree.XML("""\
TEXT
"""))
errors = None
try:
xslt(etree.XML(''))
except etree.XSLTApplyError as exc:
errors = exc.error_log
else:
self.assertFalse(True, "XSLT processing should have failed but didn't")
self.assertTrue(len(errors))
for error in errors:
if ':ERROR:XSLT:' in str(error):
break
else:
self.assertFalse(True, 'No XSLT errors found in error log:\n%s' % errors)
def test_xslt_document_XML_resolver(self):
# make sure document('') works when custom resolvers are in use
assertEqual = self.assertEqual
called = {'count' : 0}
class TestResolver(etree.Resolver):
def resolve(self, url, id, context):
assertEqual(url, 'file://ANYTHING')
called['count'] += 1
return self.resolve_string('', context)
parser = etree.XMLParser()
parser.resolvers.add(TestResolver())
xslt = etree.XSLT(etree.XML(_bytes("""\
A
B
"""), parser))
self.assertEqual(called['count'], 0)
result = xslt(etree.XML(''))
self.assertEqual(called['count'], 1)
root = result.getroot()
self.assertEqual(root.tag,
'test')
self.assertEqual(len(root), 4)
self.assertEqual(root[0].tag,
'CALLED')
self.assertEqual(root[1].tag,
'{local}entry')
self.assertEqual(root[1].text,
None)
self.assertEqual(root[1].get("value"),
'A')
self.assertEqual(root[2].tag,
'CALLED')
self.assertEqual(root[3].tag,
'{local}entry')
self.assertEqual(root[3].text,
None)
self.assertEqual(root[3].get("value"),
'B')
def test_xslt_resolver_url_building(self):
assertEqual = self.assertEqual
called = {'count' : 0}
expected_url = None
class TestResolver(etree.Resolver):
def resolve(self, url, id, context):
assertEqual(url, expected_url)
called['count'] += 1
return self.resolve_string('', context)
stylesheet_xml = _bytes("""\
""")
parser = etree.XMLParser()
parser.resolvers.add(TestResolver())
# test without base_url => relative path only
expected_url = 'test.xml'
xslt = etree.XSLT(etree.XML(stylesheet_xml, parser))
self.assertEqual(called['count'], 0)
result = xslt(etree.XML(''))
self.assertEqual(called['count'], 1)
# now the same thing with a stylesheet base URL on the filesystem
called['count'] = 0
expected_url = 'MY/BASE/test.xml' # seems to be the same on Windows
xslt = etree.XSLT(etree.XML(
stylesheet_xml, parser,
base_url=os.path.join('MY', 'BASE', 'FILE')))
self.assertEqual(called['count'], 0)
result = xslt(etree.XML(''))
self.assertEqual(called['count'], 1)
# now the same thing with a stylesheet base URL
called['count'] = 0
expected_url = 'http://server.com/BASE/DIR/test.xml'
xslt = etree.XSLT(etree.XML(
stylesheet_xml, parser,
base_url='http://server.com/BASE/DIR/FILE'))
self.assertEqual(called['count'], 0)
result = xslt(etree.XML(''))
self.assertEqual(called['count'], 1)
# now the same thing with a stylesheet base file:// URL
called['count'] = 0
expected_url = 'file://BASE/DIR/test.xml'
xslt = etree.XSLT(etree.XML(
stylesheet_xml, parser,
base_url='file://BASE/DIR/FILE'))
self.assertEqual(called['count'], 0)
result = xslt(etree.XML(''))
self.assertEqual(called['count'], 1)
def test_xslt_document_parse_allow(self):
access_control = etree.XSLTAccessControl(read_file=True)
xslt = etree.XSLT(etree.parse(fileInTestDir("test-document.xslt")),
access_control=access_control)
result = xslt(etree.XML(''))
root = result.getroot()
self.assertEqual(root.tag,
'test')
self.assertEqual(root[0].tag,
'{http://www.w3.org/1999/XSL/Transform}stylesheet')
def test_xslt_document_parse_deny(self):
access_control = etree.XSLTAccessControl(read_file=False)
xslt = etree.XSLT(etree.parse(fileInTestDir("test-document.xslt")),
access_control=access_control)
self.assertRaises(etree.XSLTApplyError, xslt, etree.XML(''))
def test_xslt_document_parse_deny_all(self):
access_control = etree.XSLTAccessControl.DENY_ALL
xslt = etree.XSLT(etree.parse(fileInTestDir("test-document.xslt")),
access_control=access_control)
self.assertRaises(etree.XSLTApplyError, xslt, etree.XML(''))
def test_xslt_access_control_repr(self):
access_control = etree.XSLTAccessControl.DENY_ALL
self.assertTrue(repr(access_control).startswith(type(access_control).__name__))
self.assertEqual(repr(access_control), repr(access_control))
self.assertNotEqual(repr(etree.XSLTAccessControl.DENY_ALL),
repr(etree.XSLTAccessControl.DENY_WRITE))
self.assertNotEqual(repr(etree.XSLTAccessControl.DENY_ALL),
repr(etree.XSLTAccessControl()))
def test_xslt_move_result(self):
root = etree.XML(_bytes('''\
'''))
xslt = etree.XSLT(etree.XML(_bytes('''\
''')))
result = xslt(root[0])
root[:] = result.getroot()[:]
del root # segfaulted before
def test_xslt_pi(self):
tree = self.parse('''\
B
C
''' % fileInTestDir("test1.xslt"))
style_root = tree.getroot().getprevious().parseXSL().getroot()
self.assertEqual("{http://www.w3.org/1999/XSL/Transform}stylesheet",
style_root.tag)
def test_xslt_pi_embedded_xmlid(self):
# test xml:id dictionary lookup mechanism
tree = self.parse('''\
B
C
''')
style_root = tree.getroot().getprevious().parseXSL().getroot()
self.assertEqual("{http://www.w3.org/1999/XSL/Transform}stylesheet",
style_root.tag)
st = etree.XSLT(style_root)
res = st(tree)
self.assertEqual('''\
B
''',
str(res))
def test_xslt_pi_embedded_id(self):
# test XPath lookup mechanism
tree = self.parse('''\
B
C
''')
style = self.parse('''\
''')
tree.getroot().append(style.getroot())
style_root = tree.getroot().getprevious().parseXSL().getroot()
self.assertEqual("{http://www.w3.org/1999/XSL/Transform}stylesheet",
style_root.tag)
st = etree.XSLT(style_root)
res = st(tree)
self.assertEqual('''\
B
''',
str(res))
def test_xslt_pi_get(self):
tree = self.parse('''\
B
C
''')
pi = tree.getroot().getprevious()
self.assertEqual("TEST", pi.get("href"))
def test_xslt_pi_get_all(self):
tree = self.parse('''\
B
C
''')
pi = tree.getroot().getprevious()
self.assertEqual("TEST", pi.get("href"))
self.assertEqual("text/xsl", pi.get("type"))
self.assertEqual(None, pi.get("motz"))
def test_xslt_pi_get_all_reversed(self):
tree = self.parse('''\
B
C
''')
pi = tree.getroot().getprevious()
self.assertEqual("TEST", pi.get("href"))
self.assertEqual("text/xsl", pi.get("type"))
self.assertEqual(None, pi.get("motz"))
def test_xslt_pi_get_unknown(self):
tree = self.parse('''\
B
C
''')
pi = tree.getroot().getprevious()
self.assertEqual(None, pi.get("unknownattribute"))
def test_xslt_pi_set_replace(self):
tree = self.parse('''\
B
C
''')
pi = tree.getroot().getprevious()
self.assertEqual("TEST", pi.get("href"))
pi.set("href", "TEST123")
self.assertEqual("TEST123", pi.get("href"))
def test_xslt_pi_set_new(self):
tree = self.parse('''\
B
C
''')
pi = tree.getroot().getprevious()
self.assertEqual(None, pi.get("href"))
pi.set("href", "TEST")
self.assertEqual("TEST", pi.get("href"))
class ETreeEXSLTTestCase(HelperTestCase):
"""EXSLT tests"""
def test_exslt_str(self):
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
res = st(tree)
self.assertEqual('''\
*B**C*
''',
str(res))
def test_exslt_str_attribute_replace(self):
tree = self.parse('BC')
style = self.parse('''\
test
''')
st = etree.XSLT(style)
res = st(tree)
self.assertEqual(str(res), '''\
test
''')
def test_exslt_math(self):
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
res = st(tree)
self.assertEqual('''\
BC
''',
str(res))
def test_exslt_regexp_test(self):
xslt = etree.XSLT(etree.XML(_bytes("""\
""")))
result = xslt(etree.XML(_bytes('123098987')))
root = result.getroot()
self.assertEqual(root.tag,
'test')
self.assertEqual(len(root), 1)
self.assertEqual(root[0].tag,
'b')
self.assertEqual(root[0].text,
'987')
def test_exslt_regexp_replace(self):
xslt = etree.XSLT(etree.XML("""\
-
"""))
result = xslt(etree.XML(_bytes('abdCdEeDed')))
root = result.getroot()
self.assertEqual(root.tag,
'test')
self.assertEqual(len(root), 0)
self.assertEqual(root.text, 'abXXdEeDed-abXXXXeXXd')
def test_exslt_regexp_match(self):
xslt = etree.XSLT(etree.XML("""\
"""))
result = xslt(etree.XML(_bytes('abdCdEeDed')))
root = result.getroot()
self.assertEqual(root.tag, 'test')
self.assertEqual(len(root), 3)
self.assertEqual(len(root[0]), 1)
self.assertEqual(root[0][0].tag, 'match')
self.assertEqual(root[0][0].text, 'dC')
self.assertEqual(len(root[1]), 2)
self.assertEqual(root[1][0].tag, 'match')
self.assertEqual(root[1][0].text, 'dC')
self.assertEqual(root[1][1].tag, 'match')
self.assertEqual(root[1][1].text, 'dE')
self.assertEqual(len(root[2]), 3)
self.assertEqual(root[2][0].tag, 'match')
self.assertEqual(root[2][0].text, 'dC')
self.assertEqual(root[2][1].tag, 'match')
self.assertEqual(root[2][1].text, 'dE')
self.assertEqual(root[2][2].tag, 'match')
self.assertEqual(root[2][2].text, 'De')
def test_exslt_regexp_match_groups(self):
xslt = etree.XSLT(etree.XML(_bytes("""\
""")))
result = xslt(etree.XML(_bytes('')))
root = result.getroot()
self.assertEqual(root.tag, 'test')
self.assertEqual(len(root), 4)
self.assertEqual(root[0].text, "123abc567")
self.assertEqual(root[1].text, "123")
self.assertEqual(root[2].text, "abc")
self.assertEqual(root[3].text, "567")
def test_exslt_regexp_match1(self):
# taken from http://www.exslt.org/regexp/functions/match/index.html
xslt = etree.XSLT(etree.XML(_bytes("""\
""")))
result = xslt(etree.XML(_bytes('')))
root = result.getroot()
self.assertEqual(root.tag, 'test')
self.assertEqual(len(root), 5)
self.assertEqual(
root[0].text,
"http://www.bayes.co.uk/xml/index.xml?/xml/utils/rechecker.xml")
self.assertEqual(
root[1].text,
"http")
self.assertEqual(
root[2].text,
"www.bayes.co.uk")
self.assertFalse(root[3].text)
self.assertEqual(
root[4].text,
"/xml/index.xml?/xml/utils/rechecker.xml")
def test_exslt_regexp_match2(self):
# taken from http://www.exslt.org/regexp/functions/match/index.html
xslt = etree.XSLT(self.parse("""\
"""))
result = xslt(etree.XML(_bytes('')))
root = result.getroot()
self.assertEqual(root.tag, 'test')
self.assertEqual(len(root), 5)
self.assertEqual(root[0].text, "This")
self.assertEqual(root[1].text, "is")
self.assertEqual(root[2].text, "a")
self.assertEqual(root[3].text, "test")
self.assertEqual(root[4].text, "string")
def _test_exslt_regexp_match3(self):
# taken from http://www.exslt.org/regexp/functions/match/index.html
# THIS IS NOT SUPPORTED!
xslt = etree.XSLT(etree.XML(_bytes("""\
""")))
result = xslt(etree.XML(_bytes('')))
root = result.getroot()
self.assertEqual(root.tag, 'test')
self.assertEqual(len(root), 4)
self.assertEqual(root[0].text, "his")
self.assertEqual(root[1].text, "is")
self.assertEqual(root[2].text, "a")
self.assertEqual(root[3].text, "test")
def _test_exslt_regexp_match4(self):
# taken from http://www.exslt.org/regexp/functions/match/index.html
# THIS IS NOT SUPPORTED!
xslt = etree.XSLT(etree.XML(_bytes("""\
""")))
result = xslt(etree.XML(_bytes('')))
root = result.getroot()
self.assertEqual(root.tag, 'test')
self.assertEqual(len(root), 4)
self.assertEqual(root[0].text, "This")
self.assertEqual(root[1].text, "is")
self.assertEqual(root[2].text, "a")
self.assertEqual(root[3].text, "test")
class ETreeXSLTExtFuncTestCase(HelperTestCase):
"""Tests for XPath extension functions in XSLT."""
def test_extensions1(self):
tree = self.parse('B')
style = self.parse('''\
''')
def mytext(ctxt, values):
return 'X' * len(values)
result = tree.xslt(style, {('testns', 'mytext') : mytext})
self.assertEqual(self._rootstring(result),
_bytes('X'))
def test_extensions2(self):
tree = self.parse('B')
style = self.parse('''\
''')
def mytext(ctxt, values):
return 'X' * len(values)
namespace = etree.FunctionNamespace('testns')
namespace['mytext'] = mytext
result = tree.xslt(style)
self.assertEqual(self._rootstring(result),
_bytes('X'))
def test_variable_result_tree_fragment(self):
tree = self.parse('B')
style = self.parse('''\
BBB
''')
def mytext(ctxt, values):
for value in values:
self.assertTrue(hasattr(value, 'tag'),
"%s is not an Element" % type(value))
self.assertEqual(value.tag, 'b')
self.assertEqual(value.text, 'BBB')
return 'X'.join([el.tag for el in values])
namespace = etree.FunctionNamespace('testns')
namespace['mytext'] = mytext
result = tree.xslt(style)
self.assertEqual(self._rootstring(result),
_bytes('bXb'))
def test_xpath_on_context_node(self):
tree = self.parse('BC')
style = self.parse('''\
''')
def extfunc(ctxt):
text_content = ctxt.context_node.xpath('text()')
return 'x'.join(text_content)
namespace = etree.FunctionNamespace('testns')
namespace['myext'] = extfunc
result = tree.xslt(style)
self.assertEqual(self._rootstring(result),
_bytes('BxC'))
def test_xpath_on_foreign_context_node(self):
# LP ticket 1354652
class Resolver(etree.Resolver):
def resolve(self, system_url, public_id, context):
assert system_url == 'extdoc.xml'
return self.resolve_string(b'BC', context)
parser = etree.XMLParser()
parser.resolvers.add(Resolver())
tree = self.parse(b'')
transform = etree.XSLT(self.parse(b'''\
''', parser=parser))
def extfunc(ctxt):
text_content = ctxt.context_node.xpath('text()')
return 'x'.join(text_content)
namespace = etree.FunctionNamespace('testns')
namespace['myext'] = extfunc
result = transform(tree)
self.assertEqual(self._rootstring(result),
_bytes('BxC'))
class ETreeXSLTExtElementTestCase(HelperTestCase):
"""Tests for extension elements in XSLT."""
def test_extension_element(self):
tree = self.parse('B')
style = self.parse('''\
b
''')
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
child = etree.Element(self_node.text)
child.text = 'X'
output_parent.append(child)
extensions = { ('testns', 'myext') : MyExt() }
result = tree.xslt(style, extensions=extensions)
self.assertEqual(self._rootstring(result),
_bytes('X'))
def test_extension_element_doc_context(self):
tree = self.parse('B')
style = self.parse('''\
b
''')
tags = []
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
tags.append(input_node.tag)
extensions = { ('testns', 'myext') : MyExt() }
result = tree.xslt(style, extensions=extensions)
self.assertEqual(tags, ['a'])
def test_extension_element_comment_pi_context(self):
tree = self.parse('')
style = self.parse('''\
b
b
''')
text = []
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
text.append(input_node.text)
extensions = { ('testns', 'myext') : MyExt() }
result = tree.xslt(style, extensions=extensions)
self.assertEqual(text, ['toast', 'a comment', 'pi'])
def _test_extension_element_attribute_context(self):
# currently not supported
tree = self.parse('')
style = self.parse('''\
b
b
''')
text = []
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, attr_value, output_parent):
text.append(attr_value)
extensions = { ('testns', 'myext') : MyExt() }
result = tree.xslt(style, extensions=extensions)
self.assertEqual(text, ['A', 'B'])
def test_extension_element_content(self):
tree = self.parse('B')
style = self.parse('''\
XY
''')
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
output_parent.extend(list(self_node)[1:])
extensions = { ('testns', 'myext') : MyExt() }
result = tree.xslt(style, extensions=extensions)
self.assertEqual(self._rootstring(result),
_bytes('Y'))
def test_extension_element_apply_templates(self):
tree = self.parse('B')
style = self.parse('''\
XY
XYZ
''')
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
for child in self_node:
for result in self.apply_templates(context, child):
if isinstance(result, basestring):
el = etree.Element("T")
el.text = result
else:
el = result
output_parent.append(el)
extensions = { ('testns', 'myext') : MyExt() }
result = tree.xslt(style, extensions=extensions)
self.assertEqual(self._rootstring(result),
_bytes('YXYZ'))
def test_extension_element_apply_templates_elements_only(self):
tree = self.parse('B')
style = self.parse('''\
XY
XYZ
''')
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
for child in self_node:
for result in self.apply_templates(context, child,
elements_only=True):
assert not isinstance(result, basestring)
output_parent.append(result)
extensions = { ('testns', 'myext') : MyExt() }
result = tree.xslt(style, extensions=extensions)
self.assertEqual(self._rootstring(result),
_bytes(''))
def test_extension_element_apply_templates_remove_blank_text(self):
tree = self.parse('B')
style = self.parse('''\
XY
XYZ
''')
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
for child in self_node:
for result in self.apply_templates(context, child,
remove_blank_text=True):
if isinstance(result, basestring):
assert result.strip()
el = etree.Element("T")
el.text = result
else:
el = result
output_parent.append(el)
extensions = { ('testns', 'myext') : MyExt() }
result = tree.xslt(style, extensions=extensions)
self.assertEqual(self._rootstring(result),
_bytes('XYZ'))
def test_extension_element_apply_templates_target_node(self):
tree = self.parse('B')
style = self.parse('''\
XY
XYZ
''')
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
for child in self_node:
self.apply_templates(context, child, output_parent)
extensions = { ('testns', 'myext') : MyExt() }
result = tree.xslt(style, extensions=extensions)
self.assertEqual(self._rootstring(result),
_bytes('YXYZ'))
def test_extension_element_apply_templates_target_node_doc(self):
tree = self.parse('B')
style = self.parse('''\
XY
TEST
XYZ
TEST
''')
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
for child in self_node:
self.apply_templates(context, child, output_parent)
extensions = { ('testns', 'myext') : MyExt() }
result = tree.xslt(style, extensions=extensions)
self.assertEqual(etree.tostring(result),
_bytes('XYZ'))
def test_extension_element_process_children(self):
tree = self.parse('E')
style = self.parse('''\
yo
''')
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
el = etree.Element('MY')
self.process_children(context, el)
output_parent.append(el)
extensions = { ('testns', 'myext') : MyExt() }
result = tree.xslt(style, extensions=extensions)
self.assertEqual(self._rootstring(result),
_bytes('E'))
def test_extension_element_process_children_to_append_only(self):
tree = self.parse('')
style = self.parse('''\
''')
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
self.process_children(context, output_parent)
extensions = { ('testns', 'myext') : MyExt() }
result = tree.xslt(style, extensions=extensions)
self.assertEqual(self._rootstring(result),
_bytes(''))
def test_extension_element_process_children_to_read_only_raise(self):
tree = self.parse('')
style = self.parse('''\
''')
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
self.process_children(context, self_node)
extensions = { ('testns', 'myext') : MyExt() }
self.assertRaises(TypeError, tree.xslt, style, extensions=extensions)
def test_extension_element_process_children_with_subextension_element(self):
tree = self.parse('')
style = self.parse('''\
''')
class MyExt(etree.XSLTExtension):
callback_call_counter = 0
def execute(self, context, self_node, input_node, output_parent):
self.callback_call_counter += 1
el = etree.Element('MY', n=str(self.callback_call_counter))
self.process_children(context, el)
output_parent.append(el)
extensions = { ('testns', 'myext') : MyExt() }
result = tree.xslt(style, extensions=extensions)
self.assertEqual(self._rootstring(result),
_bytes(''))
def test_extension_element_raise(self):
tree = self.parse('B')
style = self.parse('''\
b
''')
class MyError(Exception):
pass
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
raise MyError("expected!")
extensions = { ('testns', 'myext') : MyExt() }
self.assertRaises(MyError, tree.xslt, style, extensions=extensions)
# FIXME: DISABLED - implementation seems to be broken
# if someone cares enough about this feature, I take pull requests that fix it.
def _test_multiple_extension_elements_with_output_parent(self):
tree = self.parse("""\
This is arbitrary text in a paragraph
""")
style = self.parse("""\
""")
test = self
calls = []
class ExtMyPar(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
calls.append('par')
p = etree.Element("p")
p.attrib["style"] = "color:red"
self.process_children(context, p)
output_parent.append(p)
class ExtMyFormat(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
calls.append('format')
content = self.process_children(context)
test.assertEqual(1, len(content))
test.assertEqual('arbitrary', content[0])
test.assertEqual('This is ', output_parent.text)
output_parent.text += '*-%s-*' % content[0]
extensions = {("my", "par"): ExtMyPar(), ("my", "format"): ExtMyFormat()}
transform = etree.XSLT(style, extensions=extensions)
result = transform(tree)
self.assertEqual(['par', 'format'], calls)
self.assertEqual(
b'This is *-arbitrary-* text in a paragraph
\n',
etree.tostring(result))
def test_extensions_nsmap(self):
tree = self.parse("""\
test
""")
style = self.parse("""\
""")
class MyExt(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
output_parent.text = str(input_node.nsmap)
extensions = {('extns', 'show-nsmap'): MyExt()}
result = tree.xslt(style, extensions=extensions)
self.assertEqual(etree.tostring(result, pretty_print=True), b"""\
{'sha256': 'http://www.w3.org/2001/04/xmlenc#sha256'}
""")
class Py3XSLTTestCase(HelperTestCase):
"""XSLT tests for etree under Python 3"""
pytestmark = skipif('sys.version_info < (3,0)')
def test_xslt_result_bytes(self):
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
res = st(tree)
self.assertEqual(_bytes('''\
B
'''),
bytes(res))
def test_xslt_result_bytearray(self):
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
res = st(tree)
self.assertEqual(_bytes('''\
B
'''),
bytearray(res))
def test_xslt_result_memoryview(self):
tree = self.parse('BC')
style = self.parse('''\
''')
st = etree.XSLT(style)
res = st(tree)
self.assertEqual(_bytes('''\
B
'''),
bytes(memoryview(res)))
def test_suite():
suite = unittest.TestSuite()
suite.addTests([unittest.makeSuite(ETreeXSLTTestCase)])
suite.addTests([unittest.makeSuite(ETreeEXSLTTestCase)])
suite.addTests([unittest.makeSuite(ETreeXSLTExtFuncTestCase)])
suite.addTests([unittest.makeSuite(ETreeXSLTExtElementTestCase)])
if is_python3:
suite.addTests([unittest.makeSuite(Py3XSLTTestCase)])
suite.addTests(
[make_doctest('../../../doc/extensions.txt')])
suite.addTests(
[make_doctest('../../../doc/xpathxslt.txt')])
return suite
if __name__ == '__main__':
print('to test use test.py %s' % __file__)