summaryrefslogtreecommitdiff
path: root/repoze/bfg/xslt.py
blob: 949bde6da49016e341832e62f3f042af4552c803 (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
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