| # mako/runtime.py |
| # Copyright (C) 2006-2016 the Mako authors and contributors <see AUTHORS file> |
| # |
| # 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, compat |
| from mako.compat import compat_builtins |
| import sys |
| |
| |
| class Context(object): |
| |
| """Provides runtime namespace, output buffer, and various |
| callstacks for templates. |
| |
| See :ref:`runtime_toplevel` for detail on the usage of |
| :class:`.Context`. |
| |
| """ |
| |
| def __init__(self, buffer, **data): |
| self._buffer_stack = [buffer] |
| |
| self._data = data |
| |
| self._kwargs = data.copy() |
| self._with_template = None |
| self._outputting_as_unicode = None |
| self.namespaces = {} |
| |
| # "capture" function which proxies to the |
| # generic "capture" function |
| self._data['capture'] = compat.partial(capture, self) |
| |
| # "caller" stack used by def calls with content |
| self.caller_stack = self._data['caller'] = CallerStack() |
| |
| def _set_with_template(self, t): |
| self._with_template = t |
| illegal_names = t.reserved_names.intersection(self._data) |
| if illegal_names: |
| raise exceptions.NameConflictError( |
| "Reserved words passed to render(): %s" % |
| ", ".join(illegal_names)) |
| |
| @property |
| def lookup(self): |
| """Return the :class:`.TemplateLookup` associated |
| with this :class:`.Context`. |
| |
| """ |
| return self._with_template.lookup |
| |
| @property |
| def kwargs(self): |
| """Return the dictionary of top level keyword arguments associated |
| with this :class:`.Context`. |
| |
| This dictionary only includes the top-level arguments passed to |
| :meth:`.Template.render`. It does not include names produced within |
| the template execution such as local variable names or special names |
| such as ``self``, ``next``, etc. |
| |
| The purpose of this dictionary is primarily for the case that |
| a :class:`.Template` accepts arguments via its ``<%page>`` tag, |
| which are normally expected to be passed via :meth:`.Template.render`, |
| except the template is being called in an inheritance context, |
| using the ``body()`` method. :attr:`.Context.kwargs` can then be |
| used to propagate these arguments to the inheriting template:: |
| |
| ${next.body(**context.kwargs)} |
| |
| """ |
| return self._kwargs.copy() |
| |
| def push_caller(self, caller): |
| """Push a ``caller`` callable onto the callstack for |
| this :class:`.Context`.""" |
| |
| self.caller_stack.append(caller) |
| |
| def pop_caller(self): |
| """Pop a ``caller`` callable onto the callstack for this |
| :class:`.Context`.""" |
| |
| del self.caller_stack[-1] |
| |
| def keys(self): |
| """Return a list of all names established in this :class:`.Context`.""" |
| |
| return list(self._data.keys()) |
| |
| def __getitem__(self, key): |
| if key in self._data: |
| return self._data[key] |
| else: |
| return compat_builtins.__dict__[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 a value from this :class:`.Context`.""" |
| |
| return self._data.get(key, compat_builtins.__dict__.get(key, default)) |
| |
| def write(self, string): |
| """Write a string to this :class:`.Context` object'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._kwargs = self._kwargs |
| c._with_template = self._with_template |
| c._outputting_as_unicode = self._outputting_as_unicode |
| c.namespaces = self.namespaces |
| c.caller_stack = self.caller_stack |
| return c |
| |
| def _locals(self, d): |
| """Create a new :class:`.Context` with a copy of this |
| :class:`.Context`'s current state, |
| updated with the given dictionary. |
| |
| The :attr:`.Context.kwargs` collection remains |
| unaffected. |
| |
| |
| """ |
| |
| if not d: |
| return self |
| c = self._copy() |
| c._data.update(d) |
| return c |
| |
| def _clean_inheritance_tokens(self): |
| """create a new copy of this :class:`.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.__bool__() |
| |
| def __bool__(self): |
| return len(self) and self._get_caller() and True or False |
| |
| def _get_caller(self): |
| # this method can be removed once |
| # codegen MAGIC_NUMBER moves past 7 |
| return self[-1] |
| |
| def __getattr__(self, key): |
| return getattr(self._get_caller(), key) |
| |
| def _push_frame(self): |
| frame = self.nextcaller or None |
| self.append(frame) |
| self.nextcaller = None |
| return frame |
| |
| def _pop_frame(self): |
| self.nextcaller = self.pop() |
| |
| |
| class Undefined(object): |
| |
| """Represents an undefined value in a template. |
| |
| All template modules have a constant value |
| ``UNDEFINED`` present which is an instance of this |
| object. |
| |
| """ |
| |
| def __str__(self): |
| raise NameError("Undefined") |
| |
| def __nonzero__(self): |
| return self.__bool__() |
| |
| def __bool__(self): |
| return False |
| |
| UNDEFINED = Undefined() |
| STOP_RENDERING = "" |
| |
| |
| class LoopStack(object): |
| |
| """a stack for LoopContexts that implements the context manager protocol |
| to automatically pop off the top of the stack on context exit |
| """ |
| |
| def __init__(self): |
| self.stack = [] |
| |
| def _enter(self, iterable): |
| self._push(iterable) |
| return self._top |
| |
| def _exit(self): |
| self._pop() |
| return self._top |
| |
| @property |
| def _top(self): |
| if self.stack: |
| return self.stack[-1] |
| else: |
| return self |
| |
| def _pop(self): |
| return self.stack.pop() |
| |
| def _push(self, iterable): |
| new = LoopContext(iterable) |
| if self.stack: |
| new.parent = self.stack[-1] |
| return self.stack.append(new) |
| |
| def __getattr__(self, key): |
| raise exceptions.RuntimeException("No loop context is established") |
| |
| def __iter__(self): |
| return iter(self._top) |
| |
| |
| class LoopContext(object): |
| |
| """A magic loop variable. |
| Automatically accessible in any ``% for`` block. |
| |
| See the section :ref:`loop_context` for usage |
| notes. |
| |
| :attr:`parent` -> :class:`.LoopContext` or ``None`` |
| The parent loop, if one exists. |
| :attr:`index` -> `int` |
| The 0-based iteration count. |
| :attr:`reverse_index` -> `int` |
| The number of iterations remaining. |
| :attr:`first` -> `bool` |
| ``True`` on the first iteration, ``False`` otherwise. |
| :attr:`last` -> `bool` |
| ``True`` on the last iteration, ``False`` otherwise. |
| :attr:`even` -> `bool` |
| ``True`` when ``index`` is even. |
| :attr:`odd` -> `bool` |
| ``True`` when ``index`` is odd. |
| """ |
| |
| def __init__(self, iterable): |
| self._iterable = iterable |
| self.index = 0 |
| self.parent = None |
| |
| def __iter__(self): |
| for i in self._iterable: |
| yield i |
| self.index += 1 |
| |
| @util.memoized_instancemethod |
| def __len__(self): |
| return len(self._iterable) |
| |
| @property |
| def reverse_index(self): |
| return len(self) - self.index - 1 |
| |
| @property |
| def first(self): |
| return self.index == 0 |
| |
| @property |
| def last(self): |
| return self.index == len(self) - 1 |
| |
| @property |
| def even(self): |
| return not self.odd |
| |
| @property |
| def odd(self): |
| return bool(self.index % 2) |
| |
| def cycle(self, *values): |
| """Cycle through values as the loop progresses. |
| """ |
| if not values: |
| raise ValueError("You must provide values to cycle through") |
| return values[self.index % len(values)] |
| |
| |
| 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. |
| |
| To access a particular rendering method referenced by a |
| :class:`.Namespace`, use plain attribute access: |
| |
| .. sourcecode:: mako |
| |
| ${some_namespace.foo(x, y, z)} |
| |
| :class:`.Namespace` also contains several built-in attributes |
| described here. |
| |
| """ |
| |
| def __init__(self, name, context, |
| callables=None, inherits=None, |
| populate_self=True, calling_uri=None): |
| self.name = name |
| self.context = context |
| self.inherits = inherits |
| if callables is not None: |
| self.callables = dict([(c.__name__, c) for c in callables]) |
| |
| callables = () |
| |
| module = None |
| """The Python module referenced by this :class:`.Namespace`. |
| |
| If the namespace references a :class:`.Template`, then |
| this module is the equivalent of ``template.module``, |
| i.e. the generated module for the template. |
| |
| """ |
| |
| template = None |
| """The :class:`.Template` object referenced by this |
| :class:`.Namespace`, if any. |
| |
| """ |
| |
| context = None |
| """The :class:`.Context` object for this :class:`.Namespace`. |
| |
| Namespaces are often created with copies of contexts that |
| contain slightly different data, particularly in inheritance |
| scenarios. Using the :class:`.Context` off of a :class:`.Namespace` one |
| can traverse an entire chain of templates that inherit from |
| one-another. |
| |
| """ |
| |
| filename = None |
| """The path of the filesystem file used for this |
| :class:`.Namespace`'s module or template. |
| |
| If this is a pure module-based |
| :class:`.Namespace`, this evaluates to ``module.__file__``. If a |
| template-based namespace, it evaluates to the original |
| template file location. |
| |
| """ |
| |
| uri = None |
| """The URI for this :class:`.Namespace`'s template. |
| |
| I.e. whatever was sent to :meth:`.TemplateLookup.get_template()`. |
| |
| This is the equivalent of :attr:`.Template.uri`. |
| |
| """ |
| |
| _templateuri = None |
| |
| @util.memoized_property |
| def attr(self): |
| """Access module level attributes by name. |
| |
| This accessor allows templates to supply "scalar" |
| attributes which are particularly handy in inheritance |
| relationships. |
| |
| .. seealso:: |
| |
| :ref:`inheritance_attr` |
| |
| :ref:`namespace_attr_for_includes` |
| |
| """ |
| return _NSAttr(self) |
| |
| def get_namespace(self, uri): |
| """Return a :class:`.Namespace` corresponding to the given ``uri``. |
| |
| If the given ``uri`` is a relative URI (i.e. it does not |
| contain a leading slash ``/``), the ``uri`` is adjusted to |
| be relative to the ``uri`` of the namespace itself. This |
| method is therefore mostly useful off of the built-in |
| ``local`` namespace, described in :ref:`namespace_local`. |
| |
| In |
| most cases, a template wouldn't need this function, and |
| should instead use the ``<%namespace>`` tag to load |
| namespaces. However, since all ``<%namespace>`` tags are |
| evaluated before the body of a template ever runs, |
| this method can be used to locate namespaces using |
| expressions that were generated within the body code of |
| the template, or to conditionally use a particular |
| namespace. |
| |
| """ |
| key = (self, uri) |
| if key in self.context.namespaces: |
| return self.context.namespaces[key] |
| else: |
| ns = TemplateNamespace(uri, self.context._copy(), |
| templateuri=uri, |
| calling_uri=self._templateuri) |
| self.context.namespaces[key] = ns |
| return ns |
| |
| def get_template(self, uri): |
| """Return a :class:`.Template` from the given ``uri``. |
| |
| The ``uri`` resolution is relative to the ``uri`` of this |
| :class:`.Namespace` object's :class:`.Template`. |
| |
| """ |
| return _lookup_template(self.context, uri, self._templateuri) |
| |
| def get_cached(self, key, **kwargs): |
| """Return a value from the :class:`.Cache` referenced by this |
| :class:`.Namespace` object's :class:`.Template`. |
| |
| The advantage to this method versus direct access to the |
| :class:`.Cache` is that the configuration parameters |
| declared in ``<%page>`` take effect here, thereby calling |
| up the same configured backend as that configured |
| by ``<%page>``. |
| |
| """ |
| |
| return self.cache.get(key, **kwargs) |
| |
| @property |
| def cache(self): |
| """Return the :class:`.Cache` object referenced |
| by this :class:`.Namespace` object's |
| :class:`.Template`. |
| |
| """ |
| return self.template.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]) |
| |
| def __getattr__(self, key): |
| if key in self.callables: |
| val = self.callables[key] |
| elif self.inherits: |
| val = getattr(self.inherits, key) |
| else: |
| raise AttributeError( |
| "Namespace '%s' has no member '%s'" % |
| (self.name, key)) |
| setattr(self, key, val) |
| return val |
| |
| |
| class TemplateNamespace(Namespace): |
| |
| """A :class:`.Namespace` specific to a :class:`.Template` instance.""" |
| |
| def __init__(self, name, context, template=None, templateuri=None, |
| callables=None, inherits=None, |
| populate_self=True, calling_uri=None): |
| self.name = name |
| self.context = context |
| self.inherits = inherits |
| if callables is not None: |
| self.callables = dict([(c.__name__, c) for c in callables]) |
| |
| if templateuri is not None: |
| self.template = _lookup_template(context, templateuri, |
| calling_uri) |
| self._templateuri = self.template.module._template_uri |
| elif template is not None: |
| self.template = template |
| self._templateuri = template.module._template_uri |
| else: |
| raise TypeError("'template' argument is required.") |
| |
| if populate_self: |
| lclcallable, lclcontext = \ |
| _populate_self_namespace(context, self.template, |
| self_ns=self) |
| |
| @property |
| def module(self): |
| """The Python module referenced by this :class:`.Namespace`. |
| |
| If the namespace references a :class:`.Template`, then |
| this module is the equivalent of ``template.module``, |
| i.e. the generated module for the template. |
| |
| """ |
| return self.template.module |
| |
| @property |
| def filename(self): |
| """The path of the filesystem file used for this |
| :class:`.Namespace`'s module or template. |
| """ |
| return self.template.filename |
| |
| @property |
| def uri(self): |
| """The URI for this :class:`.Namespace`'s template. |
| |
| I.e. whatever was sent to :meth:`.TemplateLookup.get_template()`. |
| |
| This is the equivalent of :attr:`.Template.uri`. |
| |
| """ |
| return self.template.uri |
| |
| def _get_star(self): |
| if self.callables: |
| for key in self.callables: |
| yield (key, self.callables[key]) |
| |
| def get(key): |
| callable_ = self.template._get_def_callable(key) |
| return compat.partial(callable_, self.context) |
| for k in self.template.module._exports: |
| yield (k, get(k)) |
| |
| def __getattr__(self, key): |
| if key in self.callables: |
| val = self.callables[key] |
| elif self.template.has_def(key): |
| callable_ = self.template._get_def_callable(key) |
| val = compat.partial(callable_, self.context) |
| elif self.inherits: |
| val = getattr(self.inherits, key) |
| |
| else: |
| raise AttributeError( |
| "Namespace '%s' has no member '%s'" % |
| (self.name, key)) |
| setattr(self, key, val) |
| return val |
| |
| |
| class ModuleNamespace(Namespace): |
| |
| """A :class:`.Namespace` specific to a Python module instance.""" |
| |
| def __init__(self, name, context, module, |
| callables=None, inherits=None, |
| populate_self=True, calling_uri=None): |
| self.name = name |
| self.context = context |
| self.inherits = inherits |
| if callables is not None: |
| self.callables = dict([(c.__name__, c) for c in callables]) |
| |
| mod = __import__(module) |
| for token in module.split('.')[1:]: |
| mod = getattr(mod, token) |
| self.module = mod |
| |
| @property |
| def filename(self): |
| """The path of the filesystem file used for this |
| :class:`.Namespace`'s module or template. |
| """ |
| return self.module.__file__ |
| |
| def _get_star(self): |
| if self.callables: |
| for key in self.callables: |
| yield (key, self.callables[key]) |
| for key in dir(self.module): |
| if key[0] != '_': |
| callable_ = getattr(self.module, key) |
| if compat.callable(callable_): |
| yield key, compat.partial(callable_, self.context) |
| |
| def __getattr__(self, key): |
| if key in self.callables: |
| val = self.callables[key] |
| elif hasattr(self.module, key): |
| callable_ = getattr(self.module, key) |
| val = compat.partial(callable_, self.context) |
| elif self.inherits: |
| val = getattr(self.inherits, key) |
| else: |
| raise AttributeError( |
| "Namespace '%s' has no member '%s'" % |
| (self.name, key)) |
| setattr(self, key, val) |
| return val |
| |
| |
| def supports_caller(func): |
| """Apply a caller_stack compatibility decorator to a plain |
| Python function. |
| |
| See the example in :ref:`namespaces_python_modules`. |
| |
| """ |
| |
| 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. |
| |
| See the example in :ref:`namespaces_python_modules`. |
| |
| """ |
| |
| if not compat.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_include(callable_, context._data, **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 = TemplateNamespace("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: |
| raise exceptions.TemplateLookupException(str(compat.exception_as())) |
| |
| |
| def _populate_self_namespace(context, template, self_ns=None): |
| if self_ns is None: |
| self_ns = TemplateNamespace('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(as_unicode=True) |
| elif template.bytestring_passthrough: |
| buf = compat.StringIO() |
| else: |
| buf = util.FastEncodingBuffer( |
| as_unicode=as_unicode, |
| encoding=template.output_encoding, |
| errors=template.encoding_errors) |
| context = Context(buf, **data) |
| context._outputting_as_unicode = as_unicode |
| context._set_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): |
| argspec = compat.inspect_func_args(callable_) |
| # for normal pages, **pageargs is usually present |
| if argspec[2]: |
| return data |
| |
| # for rendering defs from the top level, figure out the args |
| namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None] |
| kwargs = {} |
| for arg in namedargs: |
| if arg != 'context' and arg in data and arg not in kwargs: |
| kwargs[arg] = data[arg] |
| return kwargs |
| |
| |
| def _kwargs_for_include(callable_, data, **kwargs): |
| argspec = compat.inspect_func_args(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): |
| try: |
| callable_(context, *args, **kwargs) |
| except Exception: |
| _render_error(template, context, compat.exception_as()) |
| except: |
| e = sys.exc_info()[0] |
| _render_error(template, context, e) |
| else: |
| callable_(context, *args, **kwargs) |
| |
| |
| def _render_error(template, context, error): |
| if template.error_handler: |
| result = template.error_handler(context, error) |
| if not result: |
| compat.reraise(*sys.exc_info()) |
| else: |
| error_template = exceptions.html_error_template() |
| if context._outputting_as_unicode: |
| context._buffer_stack[:] = [ |
| util.FastEncodingBuffer(as_unicode=True)] |
| else: |
| context._buffer_stack[:] = [util.FastEncodingBuffer( |
| error_template.output_encoding, |
| error_template.encoding_errors)] |
| |
| context._set_with_template(error_template) |
| error_template.render_context(context, error=error) |