import os from webob import Response from zope.component import queryUtility from zope.component import getSiteManager from zope.deprecation import deprecated from zope.interface import classProvides from zope.interface import implements from repoze.bfg.path import caller_path from repoze.bfg.interfaces import INodeTemplateRenderer from repoze.bfg.interfaces import IResponseFactory from repoze.bfg.interfaces import ITemplateRendererFactory 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) renderer = queryUtility(INodeTemplateRenderer, path) if renderer is None: if not os.path.exists(path): raise ValueError('Missing template file: %s' % path) renderer = XSLTemplateRenderer(path) sm = getSiteManager() sm.registerUtility(renderer, INodeTemplateRenderer, name=path) return renderer 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) response_factory = queryUtility(IResponseFactory, default=Response) return response_factory(result) class XSLTemplateRenderer(object): classProvides(ITemplateRendererFactory) implements(INodeTemplateRenderer) 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 XSLTemplateFactory = XSLTemplateRenderer deprecated('ZPTTemplateFactory', ('repoze.bfg.xslt.XSLTemplateFactory should now be ' 'imported as repoze.bfg.xslt.XSLTTemplateRenderer')) # 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