aboutsummaryrefslogtreecommitdiffstats
path: root/mako/runtime.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2010-02-05 00:15:54 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2010-02-05 00:15:54 +0000
commit1575e1e4e117cf127ddeade810c3dccafe1f218b (patch)
tree4b66991b170127051dd3c250f1a0c03943477832 /mako/runtime.py
parent86411ebded5f62970eaa2b119a215a71212cb492 (diff)
downloadexternal_python_mako-1575e1e4e117cf127ddeade810c3dccafe1f218b.tar.gz
external_python_mako-1575e1e4e117cf127ddeade810c3dccafe1f218b.tar.bz2
external_python_mako-1575e1e4e117cf127ddeade810c3dccafe1f218b.zip
move lib/mako to mako
Diffstat (limited to 'mako/runtime.py')
-rw-r--r--mako/runtime.py419
1 files changed, 419 insertions, 0 deletions
diff --git a/mako/runtime.py b/mako/runtime.py
new file mode 100644
index 0000000..a475b71
--- /dev/null
+++ b/mako/runtime.py
@@ -0,0 +1,419 @@
+# runtime.py
+# Copyright (C) 2006, 2007, 2008, 2009, 2010 Michael Bayer mike_mp@zzzcomputing.com
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""provides runtime services for templates, including Context, Namespace, and various helper functions."""
+
+from mako import exceptions, util
+import __builtin__, inspect, sys
+
+class Context(object):
+ """provides runtime namespace, output buffer, and various callstacks for templates."""
+ def __init__(self, buffer, **data):
+ self._buffer_stack = [buffer]
+ self._orig = data # original data, minus the builtins
+ self._data = __builtin__.__dict__.copy() # the context data which includes builtins
+ self._data.update(data)
+ self._kwargs = data.copy()
+ self._with_template = None
+ self.namespaces = {}
+
+ # "capture" function which proxies to the generic "capture" function
+ self._data['capture'] = lambda x, *args, **kwargs: capture(self, x, *args, **kwargs)
+
+ # "caller" stack used by def calls with content
+ self.caller_stack = self._data['caller'] = CallerStack()
+
+ lookup = property(lambda self:self._with_template.lookup)
+ kwargs = property(lambda self:self._kwargs.copy())
+
+ def push_caller(self, caller):
+ self.caller_stack.append(caller)
+
+ def pop_caller(self):
+ del self.caller_stack[-1]
+
+ def keys(self):
+ return self._data.keys()
+
+ def __getitem__(self, key):
+ return self._data[key]
+
+ def _push_writer(self):
+ """push a capturing buffer onto this Context and return the new Writer function."""
+
+ buf = util.FastEncodingBuffer()
+ self._buffer_stack.append(buf)
+ return buf.write
+
+ def _pop_buffer_and_writer(self):
+ """pop the most recent capturing buffer from this Context
+ and return the current writer after the pop.
+
+ """
+
+ buf = self._buffer_stack.pop()
+ return buf, self._buffer_stack[-1].write
+
+ def _push_buffer(self):
+ """push a capturing buffer onto this Context."""
+
+ self._push_writer()
+
+ def _pop_buffer(self):
+ """pop the most recent capturing buffer from this Context."""
+
+ return self._buffer_stack.pop()
+
+ def get(self, key, default=None):
+ return self._data.get(key, default)
+
+ def write(self, string):
+ """write a string to this Context's underlying output buffer."""
+
+ self._buffer_stack[-1].write(string)
+
+ def writer(self):
+ """return the current writer function"""
+
+ return self._buffer_stack[-1].write
+
+ def _copy(self):
+ c = Context.__new__(Context)
+ c._buffer_stack = self._buffer_stack
+ c._data = self._data.copy()
+ c._orig = self._orig
+ c._kwargs = self._kwargs
+ c._with_template = self._with_template
+ c.namespaces = self.namespaces
+ c.caller_stack = self.caller_stack
+ return c
+ def locals_(self, d):
+ """create a new Context with a copy of this Context's current state, updated with the given dictionary."""
+ if len(d) == 0:
+ return self
+ c = self._copy()
+ c._data.update(d)
+ return c
+ def _clean_inheritance_tokens(self):
+ """create a new copy of this Context with tokens related to inheritance state removed."""
+ c = self._copy()
+ x = c._data
+ x.pop('self', None)
+ x.pop('parent', None)
+ x.pop('next', None)
+ return c
+
+class CallerStack(list):
+ def __init__(self):
+ self.nextcaller = None
+ def __nonzero__(self):
+ return self._get_caller() and True or False
+ def _get_caller(self):
+ return self[-1]
+ def __getattr__(self, key):
+ return getattr(self._get_caller(), key)
+ def _push_frame(self):
+ self.append(self.nextcaller or None)
+ self.nextcaller = None
+ def _pop_frame(self):
+ self.nextcaller = self.pop()
+
+
+class Undefined(object):
+ """represents an undefined value in a template."""
+ def __str__(self):
+ raise NameError("Undefined")
+ def __nonzero__(self):
+ return False
+
+UNDEFINED = Undefined()
+
+class _NSAttr(object):
+ def __init__(self, parent):
+ self.__parent = parent
+ def __getattr__(self, key):
+ ns = self.__parent
+ while ns:
+ if hasattr(ns.module, key):
+ return getattr(ns.module, key)
+ else:
+ ns = ns.inherits
+ raise AttributeError(key)
+
+class Namespace(object):
+ """provides access to collections of rendering methods, which can be local, from other templates, or from imported modules"""
+ def __init__(self, name, context, module=None, template=None, templateuri=None, callables=None, inherits=None, populate_self=True, calling_uri=None):
+ self.name = name
+ if module is not None:
+ mod = __import__(module)
+ for token in module.split('.')[1:]:
+ mod = getattr(mod, token)
+ self._module = mod
+ else:
+ self._module = None
+ if templateuri is not None:
+ self.template = _lookup_template(context, templateuri, calling_uri)
+ self._templateuri = self.template.module._template_uri
+ else:
+ self.template = template
+ if self.template is not None:
+ self._templateuri = self.template.module._template_uri
+ self.context = context
+ self.inherits = inherits
+ if callables is not None:
+ self.callables = dict([(c.func_name, c) for c in callables])
+ else:
+ self.callables = None
+ if populate_self and self.template is not None:
+ (lclcallable, lclcontext) = _populate_self_namespace(context, self.template, self_ns=self)
+
+ module = property(lambda s:s._module or s.template.module)
+ filename = property(lambda s:s._module and s._module.__file__ or s.template.filename)
+ uri = property(lambda s:s.template.uri)
+
+ def attr(self):
+ if not hasattr(self, '_attr'):
+ self._attr = _NSAttr(self)
+ return self._attr
+ attr = property(attr)
+
+ def get_namespace(self, uri):
+ """return a namespace corresponding to the given template uri.
+
+ if a relative uri, it is adjusted to that of the template of this namespace"""
+ key = (self, uri)
+ if self.context.namespaces.has_key(key):
+ return self.context.namespaces[key]
+ else:
+ ns = Namespace(uri, self.context._copy(), templateuri=uri, calling_uri=self._templateuri)
+ self.context.namespaces[key] = ns
+ return ns
+
+ def get_template(self, uri):
+ return _lookup_template(self.context, uri, self._templateuri)
+
+ def get_cached(self, key, **kwargs):
+ if self.template:
+ if not self.template.cache_enabled:
+ createfunc = kwargs.get('createfunc', None)
+ if createfunc:
+ return createfunc()
+ else:
+ return None
+
+ if self.template.cache_dir:
+ kwargs.setdefault('data_dir', self.template.cache_dir)
+ if self.template.cache_type:
+ kwargs.setdefault('type', self.template.cache_type)
+ if self.template.cache_url:
+ kwargs.setdefault('url', self.template.cache_url)
+ return self.cache.get(key, **kwargs)
+
+ def cache(self):
+ return self.template.cache
+ cache = property(cache)
+
+ def include_file(self, uri, **kwargs):
+ """include a file at the given uri"""
+ _include_file(self.context, uri, self._templateuri, **kwargs)
+
+ def _populate(self, d, l):
+ for ident in l:
+ if ident == '*':
+ for (k, v) in self._get_star():
+ d[k] = v
+ else:
+ d[ident] = getattr(self, ident)
+
+ def _get_star(self):
+ if self.callables:
+ for key in self.callables:
+ yield (key, self.callables[key])
+ if self.template:
+ def get(key):
+ callable_ = self.template._get_def_callable(key)
+ return lambda *args, **kwargs:callable_(self.context, *args, **kwargs)
+ for k in self.template.module._exports:
+ yield (k, get(k))
+ if self._module:
+ def get(key):
+ callable_ = getattr(self._module, key)
+ return lambda *args, **kwargs:callable_(self.context, *args, **kwargs)
+ for k in dir(self._module):
+ if k[0] != '_':
+ yield (k, get(k))
+
+ def __getattr__(self, key):
+ if self.callables and key in self.callables:
+ return self.callables[key]
+
+ if self.template and self.template.has_def(key):
+ callable_ = self.template._get_def_callable(key)
+ return lambda *args, **kwargs:callable_(self.context, *args, **kwargs)
+
+ if self._module and hasattr(self._module, key):
+ callable_ = getattr(self._module, key)
+ return lambda *args, **kwargs:callable_(self.context, *args, **kwargs)
+
+ if self.inherits is not None:
+ return getattr(self.inherits, key)
+ raise AttributeError("Namespace '%s' has no member '%s'" % (self.name, key))
+
+def supports_caller(func):
+ """apply a caller_stack compatibility decorator to a plain Python function."""
+ def wrap_stackframe(context, *args, **kwargs):
+ context.caller_stack._push_frame()
+ try:
+ return func(context, *args, **kwargs)
+ finally:
+ context.caller_stack._pop_frame()
+ return wrap_stackframe
+
+def capture(context, callable_, *args, **kwargs):
+ """execute the given template def, capturing the output into a buffer."""
+ if not callable(callable_):
+ raise exceptions.RuntimeException("capture() function expects a callable as its argument (i.e. capture(func, *args, **kwargs))")
+ context._push_buffer()
+ try:
+ callable_(*args, **kwargs)
+ finally:
+ buf = context._pop_buffer()
+ return buf.getvalue()
+
+def _decorate_toplevel(fn):
+ def decorate_render(render_fn):
+ def go(context, *args, **kw):
+ def y(*args, **kw):
+ return render_fn(context, *args, **kw)
+ try:
+ y.__name__ = render_fn.__name__[7:]
+ except TypeError:
+ # < Python 2.4
+ pass
+ return fn(y)(context, *args, **kw)
+ return go
+ return decorate_render
+
+def _decorate_inline(context, fn):
+ def decorate_render(render_fn):
+ dec = fn(render_fn)
+ def go(*args, **kw):
+ return dec(context, *args, **kw)
+ return go
+ return decorate_render
+
+def _include_file(context, uri, calling_uri, **kwargs):
+ """locate the template from the given uri and include it in the current output."""
+ template = _lookup_template(context, uri, calling_uri)
+ (callable_, ctx) = _populate_self_namespace(context._clean_inheritance_tokens(), template)
+ callable_(ctx, **_kwargs_for_callable(callable_, context._orig, **kwargs))
+
+def _inherit_from(context, uri, calling_uri):
+ """called by the _inherit method in template modules to set up the inheritance chain at the start
+ of a template's execution."""
+ if uri is None:
+ return None
+ template = _lookup_template(context, uri, calling_uri)
+ self_ns = context['self']
+ ih = self_ns
+ while ih.inherits is not None:
+ ih = ih.inherits
+ lclcontext = context.locals_({'next':ih})
+ ih.inherits = Namespace("self:%s" % template.uri, lclcontext, template = template, populate_self=False)
+ context._data['parent'] = lclcontext._data['local'] = ih.inherits
+ callable_ = getattr(template.module, '_mako_inherit', None)
+ if callable_ is not None:
+ ret = callable_(template, lclcontext)
+ if ret:
+ return ret
+
+ gen_ns = getattr(template.module, '_mako_generate_namespaces', None)
+ if gen_ns is not None:
+ gen_ns(context)
+ return (template.callable_, lclcontext)
+
+def _lookup_template(context, uri, relativeto):
+ lookup = context._with_template.lookup
+ if lookup is None:
+ raise exceptions.TemplateLookupException("Template '%s' has no TemplateLookup associated" % context._with_template.uri)
+ uri = lookup.adjust_uri(uri, relativeto)
+ try:
+ return lookup.get_template(uri)
+ except exceptions.TopLevelLookupException, e:
+ raise exceptions.TemplateLookupException(str(e))
+
+def _populate_self_namespace(context, template, self_ns=None):
+ if self_ns is None:
+ self_ns = Namespace('self:%s' % template.uri, context, template=template, populate_self=False)
+ context._data['self'] = context._data['local'] = self_ns
+ if hasattr(template.module, '_mako_inherit'):
+ ret = template.module._mako_inherit(template, context)
+ if ret:
+ return ret
+ return (template.callable_, context)
+
+def _render(template, callable_, args, data, as_unicode=False):
+ """create a Context and return the string output of the given template and template callable."""
+
+ if as_unicode:
+ buf = util.FastEncodingBuffer(unicode=True)
+ elif template.output_encoding:
+ buf = util.FastEncodingBuffer(unicode=as_unicode, encoding=template.output_encoding, errors=template.encoding_errors)
+ else:
+ buf = util.StringIO()
+ context = Context(buf, **data)
+ context._with_template = template
+ _render_context(template, callable_, context, *args, **_kwargs_for_callable(callable_, data))
+ return context._pop_buffer().getvalue()
+
+def _kwargs_for_callable(callable_, data, **kwargs):
+ argspec = inspect.getargspec(callable_)
+ namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None]
+ for arg in namedargs:
+ if arg != 'context' and arg in data and arg not in kwargs:
+ kwargs[arg] = data[arg]
+ return kwargs
+
+def _render_context(tmpl, callable_, context, *args, **kwargs):
+ import mako.template as template
+ # create polymorphic 'self' namespace for this template with possibly updated context
+ if not isinstance(tmpl, template.DefTemplate):
+ # if main render method, call from the base of the inheritance stack
+ (inherit, lclcontext) = _populate_self_namespace(context, tmpl)
+ _exec_template(inherit, lclcontext, args=args, kwargs=kwargs)
+ else:
+ # otherwise, call the actual rendering method specified
+ (inherit, lclcontext) = _populate_self_namespace(context, tmpl.parent)
+ _exec_template(callable_, context, args=args, kwargs=kwargs)
+
+def _exec_template(callable_, context, args=None, kwargs=None):
+ """execute a rendering callable given the callable, a Context, and optional explicit arguments
+
+ the contextual Template will be located if it exists, and the error handling options specified
+ on that Template will be interpreted here.
+ """
+ template = context._with_template
+ if template is not None and (template.format_exceptions or template.error_handler):
+ error = None
+ try:
+ callable_(context, *args, **kwargs)
+ except Exception, e:
+ error = e
+ except:
+ e = sys.exc_info()[0]
+ error = e
+ if error:
+ if template.error_handler:
+ result = template.error_handler(context, error)
+ if not result:
+ raise error
+ else:
+ error_template = exceptions.html_error_template()
+ context._buffer_stack[:] = [util.FastEncodingBuffer(error_template.output_encoding, error_template.encoding_errors)]
+ context._with_template = error_template
+ error_template.render_context(context, error=error)
+ else:
+ callable_(context, *args, **kwargs)