~ubuntu-branches/ubuntu/precise/z3c.pt/precise

« back to all changes in this revision

Viewing changes to src/z3c/pt/pagetemplate.py

  • Committer: Package Import Robot
  • Author(s): Gediminas Paulauskas
  • Date: 2012-02-03 16:03:32 UTC
  • Revision ID: package-import@ubuntu.com-20120203160332-y8iyshk0u8rn4w37
Tags: upstream-2.1.5
ImportĀ upstreamĀ versionĀ 2.1.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import os
 
2
import sys
 
3
 
 
4
from zope import i18n
 
5
from zope.security.proxy import ProxyFactory
 
6
 
 
7
from chameleon.i18n import fast_translate
 
8
from chameleon.zpt import template
 
9
from chameleon.tales import StringExpr
 
10
from chameleon.tales import NotExpr
 
11
from chameleon.compiler import ExpressionEvaluator
 
12
 
 
13
from z3c.pt import expressions
 
14
 
 
15
try:
 
16
    from Missing import MV
 
17
    MV  # pyflakes
 
18
except ImportError:
 
19
    MV = object()
 
20
 
 
21
_marker = object()
 
22
 
 
23
 
 
24
BOOLEAN_HTML_ATTRS = frozenset([
 
25
    # List of Boolean attributes in HTML that should be rendered in
 
26
    # minimized form (e.g. <img ismap> rather than <img ismap="">)
 
27
    # From http://www.w3.org/TR/xhtml1/#guidelines (C.10)
 
28
    # TODO: The problem with this is that this is not valid XML and
 
29
    # can't be parsed back!
 
30
    "compact", "nowrap", "ismap", "declare", "noshade", "checked",
 
31
    "disabled", "readonly", "multiple", "selected", "noresize",
 
32
    "defer"
 
33
])
 
34
 
 
35
 
 
36
class OpaqueDict(dict):
 
37
    def __new__(cls, dictionary):
 
38
        inst = dict.__new__(cls)
 
39
        inst.dictionary = dictionary
 
40
        return inst
 
41
 
 
42
    @property
 
43
    def __getitem__(self):
 
44
        return self.dictionary.__getitem__
 
45
 
 
46
    @property
 
47
    def __len__(self):
 
48
        return self.dictionary.__len__
 
49
 
 
50
    def __repr__(self):
 
51
        return "{...} (%d entries)" % len(self)
 
52
 
 
53
sys_modules = ProxyFactory(OpaqueDict(sys.modules))
 
54
 
 
55
 
 
56
class DummyRegistry(object):
 
57
    """This class is for B/W with Chameleon 1.x API."""
 
58
 
 
59
    @staticmethod
 
60
    def purge():
 
61
        pass
 
62
 
 
63
 
 
64
class BaseTemplate(template.PageTemplate):
 
65
    content_type = None
 
66
    version = 2
 
67
 
 
68
    registry = DummyRegistry()
 
69
 
 
70
    expression_types = {
 
71
        'python': expressions.PythonExpr,
 
72
        'string': StringExpr,
 
73
        'not': NotExpr,
 
74
        'exists': expressions.ExistsExpr,
 
75
        'path': expressions.PathExpr,
 
76
        'provider': expressions.ProviderExpr,
 
77
        'nocall': expressions.NocallExpr,
 
78
        }
 
79
 
 
80
    default_expression = "path"
 
81
 
 
82
    literal_false = True
 
83
 
 
84
    strict = False
 
85
 
 
86
    @property
 
87
    def boolean_attributes(self):
 
88
        if self.content_type == 'text/xml':
 
89
            return set()
 
90
 
 
91
        return BOOLEAN_HTML_ATTRS
 
92
 
 
93
    @property
 
94
    def builtins(self):
 
95
        builtins = {
 
96
            'nothing': None,
 
97
            'modules': sys_modules,
 
98
            }
 
99
 
 
100
        tales = ExpressionEvaluator(self.engine, builtins)
 
101
        builtins['tales'] = tales
 
102
 
 
103
        return builtins
 
104
 
 
105
    def bind(self, ob, request=None):
 
106
        def render(request=request, **kwargs):
 
107
            context = self._pt_get_context(ob, request, kwargs)
 
108
            return self.render(**context)
 
109
 
 
110
        return BoundPageTemplate(self, render)
 
111
 
 
112
    def render(self, target_language=None, **context):
 
113
        # We always include a ``request`` variable; it is (currently)
 
114
        # depended on in various expression types and must be defined
 
115
        request = context.setdefault('request', None)
 
116
 
 
117
        if target_language is None:
 
118
            if hasattr(request, "get"):
 
119
                target_language = request.get("LANGUAGE", None)
 
120
            if target_language is None:
 
121
                try:
 
122
                    target_language = i18n.negotiate(request)
 
123
                except:
 
124
                    target_language = None
 
125
 
 
126
        context['target_language'] = target_language
 
127
 
 
128
        # bind translation-method to request
 
129
        def translate(
 
130
            msgid, domain=None, mapping=None,
 
131
            target_language=None, default=None):
 
132
            if msgid is MV:
 
133
                # Special case handling of Zope2's Missing.MV
 
134
                # (Missing.Value) used by the ZCatalog but is
 
135
                # unhashable
 
136
                return
 
137
            return fast_translate(
 
138
                msgid, domain, mapping, request, target_language, default)
 
139
        context["translate"] = translate
 
140
 
 
141
        if request is not None and not isinstance(request, basestring):
 
142
            content_type = self.content_type or 'text/html'
 
143
            response = request.response
 
144
            if response and not response.getHeader("Content-Type"):
 
145
                response.setHeader(
 
146
                    "Content-Type", content_type)
 
147
 
 
148
        base_renderer = super(BaseTemplate, self).render
 
149
        return base_renderer(**context)
 
150
 
 
151
    def __call__(self, *args, **kwargs):
 
152
        bound_pt = self.bind(self)
 
153
        return bound_pt(*args, **kwargs)
 
154
 
 
155
    def _pt_get_context(self, instance, request, kwargs):
 
156
        return dict(
 
157
            context=instance,
 
158
            here=instance,
 
159
            options=kwargs,
 
160
            request=request,
 
161
            template=self,
 
162
            )
 
163
 
 
164
 
 
165
class BaseTemplateFile(BaseTemplate, template.PageTemplateFile):
 
166
    """If ``filename`` is a relative path, the module path of the
 
167
    class where the instance is used to get an absolute path."""
 
168
 
 
169
    cache = {}
 
170
 
 
171
    def __init__(self, filename, path=None, content_type=None, **kwargs):
 
172
        if path is not None:
 
173
            filename = os.path.join(path, filename)
 
174
 
 
175
        if not os.path.isabs(filename):
 
176
            for depth in (1, 2):
 
177
                frame = sys._getframe(depth)
 
178
                package_name = frame.f_globals.get('__name__', None)
 
179
                if package_name is not None and \
 
180
                       package_name != self.__module__:
 
181
                    module = sys.modules[package_name]
 
182
                    try:
 
183
                        path = module.__path__[0]
 
184
                    except AttributeError:
 
185
                        path = module.__file__
 
186
                        path = path[:path.rfind(os.sep)]
 
187
                    break
 
188
                else:
 
189
                    package_path = frame.f_globals.get('__file__', None)
 
190
                    if package_path is not None:
 
191
                        path = os.path.dirname(package_path)
 
192
                        break
 
193
 
 
194
            if path is not None:
 
195
                filename = os.path.join(path, filename)
 
196
 
 
197
        template.PageTemplateFile.__init__(
 
198
            self, filename, **kwargs)
 
199
 
 
200
        # Set content-type last, so that we can override whatever was
 
201
        # magically sniffed from the source template.
 
202
        self.content_type = content_type
 
203
 
 
204
 
 
205
class PageTemplate(BaseTemplate):
 
206
    """Page Templates using TAL, TALES, and METAL.
 
207
 
 
208
    This class is suitable for standalone use or class
 
209
    property. Keyword-arguments are passed into the template as-is.
 
210
 
 
211
    Initialize with a template string."""
 
212
 
 
213
    version = 1
 
214
 
 
215
    def __get__(self, instance, type):
 
216
        if instance is not None:
 
217
            return self.bind(instance)
 
218
        return self
 
219
 
 
220
 
 
221
class PageTemplateFile(BaseTemplateFile, PageTemplate):
 
222
    """Page Templates using TAL, TALES, and METAL.
 
223
 
 
224
    This class is suitable for standalone use or class
 
225
    property. Keyword-arguments are passed into the template as-is.
 
226
 
 
227
    Initialize with a filename."""
 
228
 
 
229
    cache = {}
 
230
 
 
231
 
 
232
class ViewPageTemplate(PageTemplate):
 
233
    """Template class suitable for use with a Zope browser view; the
 
234
    variables ``view``, ``context`` and ``request`` variables are
 
235
    brought in to the local scope of the template automatically, while
 
236
    keyword arguments are passed in through the ``options``
 
237
    dictionary. Note that the default expression type for this class
 
238
    is 'path' (standard Zope traversal)."""
 
239
 
 
240
    def _pt_get_context(self, view, request, kwargs):
 
241
        context = kwargs.get('context')
 
242
        if context is None:
 
243
            context = view.context
 
244
        request = request or kwargs.get('request') or view.request
 
245
        return dict(
 
246
            view=view,
 
247
            context=context,
 
248
            request=request,
 
249
            options=kwargs,
 
250
            template=self,
 
251
            )
 
252
 
 
253
    def __call__(self, _ob=None, context=None, request=None, **kwargs):
 
254
        kwargs.setdefault('context', context)
 
255
        kwargs.setdefault('request', request)
 
256
        bound_pt = self.bind(_ob)
 
257
        return bound_pt(**kwargs)
 
258
 
 
259
 
 
260
class ViewPageTemplateFile(ViewPageTemplate, PageTemplateFile):
 
261
    """If ``filename`` is a relative path, the module path of the
 
262
    class where the instance is used to get an absolute path."""
 
263
 
 
264
    cache = {}
 
265
 
 
266
 
 
267
class BoundPageTemplate(object):
 
268
    """When a page template class is used as a property, it's bound to
 
269
    the class instance on access, which is implemented using this
 
270
    helper class."""
 
271
 
 
272
    def __init__(self, pt, render):
 
273
        object.__setattr__(self, 'im_self', pt)
 
274
        object.__setattr__(self, 'im_func', render)
 
275
 
 
276
    macros = property(lambda self: self.im_self.macros)
 
277
    filename = property(lambda self: self.im_self.filename)
 
278
 
 
279
    def __call__(self, *args, **kw):
 
280
        kw.setdefault('args', args)
 
281
        return self.im_func(**kw)
 
282
 
 
283
    def __setattr__(self, name, v):
 
284
        raise AttributeError("Can't set attribute", name)
 
285
 
 
286
    def __repr__(self):
 
287
        return "<%s.Bound%s %r>" % (
 
288
            type(self.im_self).__module__,
 
289
            type(self.im_self).__name__, self.filename)