from __future__ import nested_scopes
import StringIO
import os
import Pyana
import unittest
import sys
def sum(*args):
s = 0
for i in args:
s += i
return s
def recat(*args):
args = list(args)
args.reverse()
return ''.join(args)
def xor(x,y):
return Pyana.Boolean(int(x) ^ int(y))
class ExtensionException(Exception):
pass
def thrower():
raise ExtensionException
def badType():
return ["This doesn't seem easily convertable to an XObject"]
def passThrough(x):
return x
def depth(context):
if context is None:
return 0
else:
return depth(context.parentNode) + 1
xml = r'<a><b>Test</b></a>'
xsl = r'''
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:testNS="testNS" xmlns:xalan="http://xml.apache.org/xalan"
version="1.0">
<xsl:output method="text"/>
<xsl:variable name="rtfrag">5</xsl:variable>
<xsl:template match="b"><xsl:value-of select="%s"/></xsl:template>
</xsl:stylesheet>
'''
class XPathExtensionInstallationTestCase(unittest.TestCase):
def checkGlobalInstall(self):
self.assertRaises(
Pyana.XSLError,
Pyana.transform2String,
xml,
xsl % 'testNS:sum(1,2,3)'
)
Pyana.installGlobalExtension('testNS', sum, 'sum')
Pyana.transform2String(xml, xsl % 'testNS:sum(1,2,3)') == '3'
# Double install should be ok
Pyana.installGlobalExtension('testNS', sum, 'sum')
Pyana.transform2String(xml, xsl % 'testNS:sum(1,2,3)') == '3'
self.assertRaises(
TypeError,
Pyana.installGlobalExtension,
'testNS',
'notcallable',
'sum'
)
Pyana.installGlobalExtensionWithContext('testNS', depth, 'depth')
assert Pyana.transform2String(xml, xsl % 'testNS:depth()') == '3'
Pyana.removeGlobalExtension('testNS', 'sum')
Pyana.removeGlobalExtension('testNS', 'depth')
self.assertRaises(
Pyana.XSLError,
Pyana.transform2String,
xml,
xsl % 'testNS:sum(1,2,3)'
)
def checkTransformerInstall(self):
t = Pyana.Transformer()
self.assertRaises(
Pyana.XSLError,
t.transform2String,
xml,
xsl % 'testNS:sum(1,2,3)'
)
t.installExtension('testNS', sum, 'sum')
t.transform2String(xml, xsl % 'testNS:sum(1,2,3)') == '3'
self.assertRaises(
TypeError,
t.installExtension,
'testNS',
'notcallable',
'sum'
)
t.installExtensionWithContext('testNS', depth, 'depth')
assert t.transform2String(xml, xsl % 'testNS:depth()') == '3'
t.removeExtension('testNS', 'sum')
self.assertRaises(
Pyana.XSLError,
t.transform2String,
xml,
xsl % 'testNS:sum(1,2,3)'
)
t.removeExtension('testNS', 'sum')
# Check that Tranformer extensions beat global extensions
def negsum(*args): return -sum(*args)
Pyana.installGlobalExtension('testNS', sum, 'sum')
t.installExtension('testNS', negsum, 'sum')
Pyana.transform2String(xml, xsl % 'testNS:sum(1,2,3)') == '-3'
Pyana.removeGlobalExtension('testNS', 'sum')
class BooleanTestCase(unittest.TestCase):
def checkConversions(self):
import types
try:
types.BooleanType
except AttributeError:
# we can assume that repr works correctly for the core Python
# bool type
assert repr(Pyana.Boolean(0)) == "<Pyana.Boolean=false>"
assert repr(Pyana.Boolean(1)) == "<Pyana.Boolean=true>"
assert repr(Pyana.Boolean(2)) == "<Pyana.Boolean=true>"
else:
# it looks like Python defines a boolean type, lets make sure
# that we are using it
assert Pyana.Boolean(0) is False
assert Pyana.Boolean(1) is True
assert not Pyana.Boolean(0), "Pyana.Boolean(0) should be equivalent to 0"
assert Pyana.Boolean(1), "Pyana.Boolean(1) should be nonzero"
assert Pyana.Boolean(2), "Pyana.Boolean(2) should be nonzero"
assert int(Pyana.Boolean(0)) == 0
assert int(Pyana.Boolean(1)) == 1
assert int(Pyana.Boolean(2)) == 1
assert str(Pyana.Boolean(0)) == "False"
assert str(Pyana.Boolean(1)) == "True"
assert str(Pyana.Boolean(2)) == "True"
class XPathExtensionSimpleCallbacksTestCase(unittest.TestCase):
def setUp(self):
self.t = Pyana.Transformer()
self.t.installExtension('testNS', sum, 'sum')
self.t.installExtension('testNS', recat, 'recat')
self.t.installExtension('testNS', xor, 'xor')
self.t.installExtensionWithContext('testNS', depth, 'depth')
self.t.installExtension('testNS', passThrough, 'passThrough')
self.t.installExtension('testNS', thrower, 'thrower')
self.t.installExtension('testNS', badType, 'badType')
def checkNumericCallback(self):
assert self.t.transform2String(
xml,
xsl % 'testNS:sum(1,2,3,4,5.5)+10'
) == '25.5'
def checkStringCallback(self):
assert self.t.transform2String(
xml,
xsl % "testNS:recat('World!', ' ', 'Hello')"
) == 'Hello World!'
assert self.t.transform2String(
xml,
(xsl % u"testNS:recat('Quinlan', ' ', 'Andr\u00e9e')").encode('utf-8')
) == u'Andr\u00e9e Quinlan'.encode('utf-8')
def checkBooleanCallback(self):
assert self.t.transform2String(
xml,
xsl % 'testNS:xor(true(), false())'
) == 'true'
def checkContextCallback(self):
assert self.t.transform2String(
xml,
xsl % 'testNS:depth()'
) == '3'
def checkUnsupportedTypes(self):
self.assertRaises(
TypeError,
self.t.transform2String,
xml,
xsl % 'testNS:passThrough($rtfrag)'
)
self.assertRaises(
TypeError,
self.t.transform2String,
xml,
xsl % 'testNS:badType()'
)
def checkRaisingCallbacks(self):
self.assertRaises(
ExtensionException,
self.t.transform2String,
xml,
xsl % 'testNS:thrower()'
)
class XPathNodeSetTestCase(unittest.TestCase):
def setUp(self):
self.t = Pyana.Transformer()
self.t.installExtension('testNS', self.testNodeSet, 'testNodeSet')
def tearDown(self):
del self.t
def testNodeSet(self, nodeSet):
import re
assert len(nodeSet) == 8
assert str(nodeSet) == '1'
assert repr(nodeSet).startswith('<XNodeSet')
assert nodeSet[0].nodeName == 'c'
assert nodeSet[7].firstChild.nodeValue == '4'
try: nodeSet[8]
except IndexError: pass
else: assert 0, "expected IndexError"
return 'OK!'
def checkNodeSetCallback(self):
assert self.t.transform2String(
'<b><c>1</c><c/><c/><c>2</c><c>3</c><c/><c/><c>4</c></b>',
xsl % 'testNS:testNodeSet(//c)'
) == 'OK!'
# The next test ensures that nodes and nodesets are properly killed
# then the transformation is over
nodeSets = []
nodes = []
def retain(*args):
for ns in args:
nodeSets.append(ns)
for node in ns:
nodes.append(node)
self.t.installExtension('testNS', retain, 'retain')
self.t.transform2String(
xml,
xsl % 'testNS:retain(//*)'
)
self.t.removeExtension('testNS', 'retain')
for i in nodes:
repr(i)
try:
i.parentNode
except TypeError:
pass
else:
assert 0, "expected ReferenceError"
for i in nodeSets:
repr(i)
try:
i[0]
except TypeError:
pass
else:
assert 0, "expected ReferenceError"
for i in nodeSets:
try:
len(i)
except TypeError:
pass
else:
assert 0, "expected ReferenceError"
class XPathContextNodeTestCase(unittest.TestCase):
def setUp(self):
self.t = Pyana.Transformer()
self.t.installExtensionWithContext('testNS', self.callback, 'callback' )
def tearDown(self):
del self.t
def checkNodeMethods(self):
callbackTemplateXSL = r'''
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:test="testNS"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/fruits"><xsl:value-of select="%s"/></xsl:template>
</xsl:stylesheet>
'''
fruit = r'''
<fruits>
<fruit name="pineapple"/>
<fruit name="lemon"/>
<fruit name="lime"/>
<fruit name="orange"/>
</fruits>'''
self.retainedNodes = []
assert self.t.transform2String(fruit, callbackTemplateXSL % "test:callback()") == '4'
for i in self.retainedNodes:
assert repr(i).find('dead') != -1
try:
i.anyAttribute
except TypeError: pass # nodes
except AttributeError: pass # nodelists
else:
assert 0, "expected ReferenceError"
def callback(self, contextNode):
assert contextNode.nodeName == u'fruits'
self.retainedNodes.append(contextNode)
self.retainedNodes.append(contextNode.ownerDocument)
children = contextNode.childNodes
self.retainedNodes.append(children)
return len([node for node in children if node.nodeName == u'fruit'])
def getTestSuites(type):
return unittest.TestSuite([
unittest.makeSuite(XPathExtensionInstallationTestCase, type),
unittest.makeSuite(BooleanTestCase, type),
unittest.makeSuite(XPathExtensionSimpleCallbacksTestCase, type),
unittest.makeSuite(XPathContextNodeTestCase, type),
unittest.makeSuite(XPathNodeSetTestCase, type)
])
|