2 # Copyright (C) 2006, 2007, 2008 Michael Bayer mike_mp@zzzcomputing.com
4 # This module is part of Mako and is released under
5 # the MIT License: http://www.opensource.org/licenses/mit-license.php
7 """provides runtime services for templates, including Context, Namespace, and various helper functions."""
9 from mako import exceptions, util
10 import __builtin__, inspect, sys
12 class Context(object):
13 """provides runtime namespace, output buffer, and various callstacks for templates."""
14 def __init__(self, buffer, **data):
15 self._buffer_stack = [buffer]
16 self._orig = data # original data, minus the builtins
17 self._data = __builtin__.__dict__.copy() # the context data which includes builtins
18 self._data.update(data)
19 self._kwargs = data.copy()
20 self._with_template = None
23 # "capture" function which proxies to the generic "capture" function
24 self._data['capture'] = lambda x, *args, **kwargs: capture(self, x, *args, **kwargs)
26 # "caller" stack used by def calls with content
27 self.caller_stack = self._data['caller'] = CallerStack()
29 lookup = property(lambda self:self._with_template.lookup)
30 kwargs = property(lambda self:self._kwargs.copy())
32 def push_caller(self, caller):
33 self.caller_stack.append(caller)
36 del self.caller_stack[-1]
39 return self._data.keys()
41 def __getitem__(self, key):
42 return self._data[key]
44 def _push_writer(self):
45 """push a capturing buffer onto this Context and return the new Writer function."""
47 buf = util.FastEncodingBuffer()
48 self._buffer_stack.append(buf)
51 def _pop_buffer_and_writer(self):
52 """pop the most recent capturing buffer from this Context
53 and return the current writer after the pop.
57 buf = self._buffer_stack.pop()
58 return buf, self._buffer_stack[-1].write
60 def _push_buffer(self):
61 """push a capturing buffer onto this Context."""
65 def _pop_buffer(self):
66 """pop the most recent capturing buffer from this Context."""
68 return self._buffer_stack.pop()
70 def get(self, key, default=None):
71 return self._data.get(key, default)
73 def write(self, string):
74 """write a string to this Context's underlying output buffer."""
76 self._buffer_stack[-1].write(string)
79 """return the current writer function"""
81 return self._buffer_stack[-1].write
84 c = Context.__new__(Context)
85 c._buffer_stack = self._buffer_stack
86 c._data = self._data.copy()
88 c._kwargs = self._kwargs
89 c._with_template = self._with_template
90 c.namespaces = self.namespaces
91 c.caller_stack = self.caller_stack
94 """create a new Context with a copy of this Context's current state, updated with the given dictionary."""
100 def _clean_inheritance_tokens(self):
101 """create a new copy of this Context with tokens related to inheritance state removed."""
105 x.pop('parent', None)
109 class CallerStack(list):
111 self.nextcaller = None
112 def __nonzero__(self):
113 return self._get_caller() and True or False
114 def _get_caller(self):
116 def __getattr__(self, key):
117 return getattr(self._get_caller(), key)
118 def _push_frame(self):
119 self.append(self.nextcaller or None)
120 self.nextcaller = None
121 def _pop_frame(self):
122 self.nextcaller = self.pop()
125 class Undefined(object):
126 """represents an undefined value in a template."""
128 raise NameError("Undefined")
129 def __nonzero__(self):
132 UNDEFINED = Undefined()
134 class _NSAttr(object):
135 def __init__(self, parent):
136 self.__parent = parent
137 def __getattr__(self, key):
140 if hasattr(ns.module, key):
141 return getattr(ns.module, key)
144 raise AttributeError(key)
146 class Namespace(object):
147 """provides access to collections of rendering methods, which can be local, from other templates, or from imported modules"""
148 def __init__(self, name, context, module=None, template=None, templateuri=None, callables=None, inherits=None, populate_self=True, calling_uri=None):
150 if module is not None:
151 mod = __import__(module)
152 for token in module.split('.')[1:]:
153 mod = getattr(mod, token)
157 if templateuri is not None:
158 self.template = _lookup_template(context, templateuri, calling_uri)
159 self._templateuri = self.template.module._template_uri
161 self.template = template
162 if self.template is not None:
163 self._templateuri = self.template.module._template_uri
164 self.context = context
165 self.inherits = inherits
166 if callables is not None:
167 self.callables = dict([(c.func_name, c) for c in callables])
169 self.callables = None
170 if populate_self and self.template is not None:
171 (lclcallable, lclcontext) = _populate_self_namespace(context, self.template, self_ns=self)
173 module = property(lambda s:s._module or s.template.module)
174 filename = property(lambda s:s._module and s._module.__file__ or s.template.filename)
175 uri = property(lambda s:s.template.uri)
178 if not hasattr(self, '_attr'):
179 self._attr = _NSAttr(self)
181 attr = property(attr)
183 def get_namespace(self, uri):
184 """return a namespace corresponding to the given template uri.
186 if a relative uri, it is adjusted to that of the template of this namespace"""
188 if self.context.namespaces.has_key(key):
189 return self.context.namespaces[key]
191 ns = Namespace(uri, self.context._copy(), templateuri=uri, calling_uri=self._templateuri)
192 self.context.namespaces[key] = ns
195 def get_template(self, uri):
196 return _lookup_template(self.context, uri, self._templateuri)
198 def get_cached(self, key, **kwargs):
200 if not self.template.cache_enabled:
201 createfunc = kwargs.get('createfunc', None)
207 if self.template.cache_dir:
208 kwargs.setdefault('data_dir', self.template.cache_dir)
209 if self.template.cache_type:
210 kwargs.setdefault('type', self.template.cache_type)
211 if self.template.cache_url:
212 kwargs.setdefault('url', self.template.cache_url)
213 return self.cache.get(key, **kwargs)
216 return self.template.cache
217 cache = property(cache)
219 def include_file(self, uri, **kwargs):
220 """include a file at the given uri"""
221 _include_file(self.context, uri, self._templateuri, **kwargs)
223 def _populate(self, d, l):
226 for (k, v) in self._get_star():
229 d[ident] = getattr(self, ident)
233 for key in self.callables:
234 yield (key, self.callables[key])
237 callable_ = self.template.get_def(key).callable_
238 return lambda *args, **kwargs:callable_(self.context, *args, **kwargs)
239 for k in self.template.module._exports:
243 callable_ = getattr(self._module, key)
244 return lambda *args, **kwargs:callable_(self.context, *args, **kwargs)
245 for k in dir(self._module):
249 def __getattr__(self, key):
250 if self.callables and key in self.callables:
251 return self.callables[key]
253 if self.template and self.template.has_def(key):
254 callable_ = self.template.get_def(key).callable_
255 return lambda *args, **kwargs:callable_(self.context, *args, **kwargs)
257 if self._module and hasattr(self._module, key):
258 callable_ = getattr(self._module, key)
259 return lambda *args, **kwargs:callable_(self.context, *args, **kwargs)
261 if self.inherits is not None:
262 return getattr(self.inherits, key)
263 raise exceptions.RuntimeException("Namespace '%s' has no member '%s'" % (self.name, key))
265 def supports_caller(func):
266 """apply a caller_stack compatibility decorator to a plain Python function."""
267 def wrap_stackframe(context, *args, **kwargs):
268 context.caller_stack._push_frame()
270 return func(context, *args, **kwargs)
272 context.caller_stack._pop_frame()
273 return wrap_stackframe
275 def capture(context, callable_, *args, **kwargs):
276 """execute the given template def, capturing the output into a buffer."""
277 if not callable(callable_):
278 raise exceptions.RuntimeException("capture() function expects a callable as its argument (i.e. capture(func, *args, **kwargs))")
279 context._push_buffer()
281 callable_(*args, **kwargs)
283 buf = context._pop_buffer()
284 return buf.getvalue()
286 def _include_file(context, uri, calling_uri, **kwargs):
287 """locate the template from the given uri and include it in the current output."""
288 template = _lookup_template(context, uri, calling_uri)
289 (callable_, ctx) = _populate_self_namespace(context._clean_inheritance_tokens(), template)
290 callable_(ctx, **_kwargs_for_callable(callable_, context._orig, **kwargs))
292 def _inherit_from(context, uri, calling_uri):
293 """called by the _inherit method in template modules to set up the inheritance chain at the start
294 of a template's execution."""
297 template = _lookup_template(context, uri, calling_uri)
298 self_ns = context['self']
300 while ih.inherits is not None:
302 lclcontext = context.locals_({'next':ih})
303 ih.inherits = Namespace("self:%s" % template.uri, lclcontext, template = template, populate_self=False)
304 context._data['parent'] = lclcontext._data['local'] = ih.inherits
305 callable_ = getattr(template.module, '_mako_inherit', None)
306 if callable_ is not None:
307 ret = callable_(template, lclcontext)
311 gen_ns = getattr(template.module, '_mako_generate_namespaces', None)
312 if gen_ns is not None:
314 return (template.callable_, lclcontext)
316 def _lookup_template(context, uri, relativeto):
317 lookup = context._with_template.lookup
319 raise exceptions.TemplateLookupException("Template '%s' has no TemplateLookup associated" % context._with_template.uri)
320 uri = lookup.adjust_uri(uri, relativeto)
322 return lookup.get_template(uri)
323 except exceptions.TopLevelLookupException, e:
324 raise exceptions.TemplateLookupException(str(e))
326 def _populate_self_namespace(context, template, self_ns=None):
328 self_ns = Namespace('self:%s' % template.uri, context, template=template, populate_self=False)
329 context._data['self'] = context._data['local'] = self_ns
330 if hasattr(template.module, '_mako_inherit'):
331 ret = template.module._mako_inherit(template, context)
334 return (template.callable_, context)
336 def _render(template, callable_, args, data, as_unicode=False):
337 """create a Context and return the string output of the given template and template callable."""
340 buf = util.FastEncodingBuffer(unicode=True)
341 elif template.output_encoding:
342 buf = util.FastEncodingBuffer(unicode=as_unicode, encoding=template.output_encoding, errors=template.encoding_errors)
344 buf = util.StringIO()
345 context = Context(buf, **data)
346 context._with_template = template
347 _render_context(template, callable_, context, *args, **_kwargs_for_callable(callable_, data))
348 return context._pop_buffer().getvalue()
350 def _kwargs_for_callable(callable_, data, **kwargs):
351 argspec = inspect.getargspec(callable_)
352 namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None]
353 for arg in namedargs:
354 if arg != 'context' and arg in data and arg not in kwargs:
355 kwargs[arg] = data[arg]
358 def _render_context(tmpl, callable_, context, *args, **kwargs):
359 import mako.template as template
360 # create polymorphic 'self' namespace for this template with possibly updated context
361 if not isinstance(tmpl, template.DefTemplate):
362 # if main render method, call from the base of the inheritance stack
363 (inherit, lclcontext) = _populate_self_namespace(context, tmpl)
364 _exec_template(inherit, lclcontext, args=args, kwargs=kwargs)
366 # otherwise, call the actual rendering method specified
367 (inherit, lclcontext) = _populate_self_namespace(context, tmpl.parent)
368 _exec_template(callable_, context, args=args, kwargs=kwargs)
370 def _exec_template(callable_, context, args=None, kwargs=None):
371 """execute a rendering callable given the callable, a Context, and optional explicit arguments
373 the contextual Template will be located if it exists, and the error handling options specified
374 on that Template will be interpreted here.
376 template = context._with_template
377 if template is not None and (template.format_exceptions or template.error_handler):
380 callable_(context, *args, **kwargs)
384 e = sys.exc_info()[0]
387 if template.error_handler:
388 result = template.error_handler(context, error)
392 error_template = exceptions.html_error_template()
393 context._buffer_stack[:] = [util.FastEncodingBuffer(error_template.output_encoding, error_template.encoding_errors)]
394 context._with_template = error_template
395 error_template.render_context(context, error=error)
397 callable_(context, *args, **kwargs)