import os from webob import Response from zope.component import queryUtility from zope.component import getSiteManager from zope.component.interfaces import ComponentLookupError from zope.interface import classProvides from zope.interface import implements from repoze.bfg.path import caller_path from repoze.bfg.interfaces import INodeTemplate from repoze.bfg.interfaces import ITemplateFactory def get_transform(path, node): """ Return a callable transform object. When called, the transform will return a string. The ``path`` argument should be a package-relative path (also may be absolute) to an XSLT file. When called, the transform will use the kwargs in ``*kw`` as top level names and the lxml node at ``node``.""" # Render using XSLT path = caller_path(path) template = queryUtility(INodeTemplate, path) if template is None: if not os.path.exists(path): raise ValueError('Missing template file: %s' % path) template = XSLTemplateFactory(path) try: sm = getSiteManager() except ComponentLookupError: pass else: sm.registerUtility(template, INodeTemplate, name=path) return template def render_transform(path, node, **kw): """ Render a XSL template at the package-relative path (may also be absolute) using the kwargs in ``*kw`` as top-level names and the lxml node at ``node`` and return a string.""" path = caller_path(path) template = get_transform(path, node) return template(node, **kw) def render_transform_to_response(path, node, **kw): """ Render a XSL template at the package-relative path (may also be absolute) using the kwargs in ``*kw`` as top-level names and the lxml node at ``node`` and return a Response object.""" path = caller_path(path) result = render_transform(path, node, **kw) return Response(result) class XSLTemplateFactory(object): classProvides(ITemplateFactory) implements(INodeTemplate) def __init__(self, path, auto_reload=False): self.path = path self.auto_reload = auto_reload def __call__(self, node, **kw): processor = get_processor(self.path, self.auto_reload) result = str(processor(node, **kw)) return result # Manage XSLT processors on a per-thread basis import threading from lxml import etree xslt_pool = threading.local() def get_processor(xslt_fn, auto_reload=False): if not auto_reload: try: return xslt_pool.processors[xslt_fn] except AttributeError: xslt_pool.processors = {} except KeyError: pass # Make a processor and add it to the pool source = etree.ElementTree(file=xslt_fn) proc = etree.XSLT(source) xslt_pool.processors[xslt_fn] = proc return proc