I'll be brief.
Because the Python "lxml" module doesn't support XSLT 2.0 functions, I was looking at support for EXSLT …
… but then stumbled on how to write my own functions and call them from stylesheets.
Freakin' cool.
I like calling it "pxslt" for "Python XSLT" and pronouncing it like "pixelate".

Example below of the "module" I made; the script that calls it, and the results.
Told you I'd be brief.
Module:
#pxslt.py
def underscore(context, word):
'''Replace whitespace with underscore.'''
out = word[0].replace(' ', '_')
return out
def multiply(context, int_val, int2_val):
'''Multiply two integers.'''
int_val, int2_val = int(int_val[0]), int(int2_val[0])
return int_val * int2_val
def libraryThing(context, isbn):
'''Get language for a work based on ISBN using LibraryThing API.'''
isbn = isbn[0]
import urllib
res = urllib.urlopen('http://www.librarything.com/api/thingLang.php?isbn=' + isbn)
res_r = res.read()
return res_r
##### DO NOT EDIT
##### makes it possible to call the above functions with XSLT
def pxslt():
myFunctions = []
gbs = globals()
from inspect import isfunction
for gb in gbs:
if isfunction(gbs[gb]) and gb != 'pxslt':
#print gb
myFunctions.append(gbs[gb])
from lxml import etree
#see: http://lxml.de/extensions.html
ns = etree.FunctionNamespace('file://libs/pxslt.py')
ns.prefix = 'pxsl'
for myFunction in myFunctions:
name = str(myFunction.func_name)
ns[name] = myFunction
return ns
Usage example:
from lxml import etree
#####
myXML = etree.XML('''\
<a>
<b>Hello. This will appear with whitespaces replaced by underscores.</b>
<c>3</c>
</a>''')
myXSL = etree.XSLT(etree.XML('''\
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:pxslt="file://libs/pxslt.py">
<xsl:output method="text" version="1.0" />
<xsl:template match="a">
<xsl:variable name="isbn">9955081260</xsl:variable>
<xsl:value-of select="pxslt:libraryThing($isbn)" />
<xsl:text>\n</xsl:text> <!-- Python will line break here -->
<xsl:value-of select="pxslt:underscore(b/text())" />
<xsl:text>\n</xsl:text> <!-- Python will line break here -->
<xsl:call-template name="mathFunc">
</xsl:call-template>
</xsl:template>
<xsl:template name="mathFunc">
<xsl:variable name="myNum">10</xsl:variable>
<xsl:value-of select="pxslt:multiply(c/text(), $myNum)" />
</xsl:template>
</xsl:stylesheet>'''))
import pxslt
pxslt.pxslt() #get all set up with namespaces and function stuff
print(myXSL(myXML))
#myXSL_file = etree.XSLT(etree.parse('foo.xsl')) #for testing with a real XSL file
#print(myXSL_file(myXML))
Output:
>>>
lit
Hello._This_will_appear_with_whitespaces_replaced_by_underscores.
30





