summaryrefslogtreecommitdiff
path: root/repoze/bfg/xslt.py
blob: 7dae7ca8e3f7f021f71b412dcf28851687b676ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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