1. David Wolever
  2. djmako

Source

djmako / djmako / loader.py

"""
Mako templates for django >= 1.2.
"""

from django.conf import settings
from django.template import TemplateDoesNotExist
from django.template.context import Context
from django.template.loaders.filesystem import Loader as FSLoader
from django.template.loaders.app_directories import app_template_dirs

from mako.exceptions import MakoException, TopLevelLookupException
from mako.template import Template
from mako.lookup import TemplateLookup


def context_to_dict(ctxt):
    res = {}
    for d in reversed(ctxt.dicts):
        # sometimes contexts will be nested
        if isinstance(d, Context):
            res.update(context_to_dict(d))
        else:
            res.update(d)
    return res


def _get_start_and_end(source, lineno, pos):
    start = 0
    for n, line in enumerate(source.splitlines()):
        if n == lineno:
            start += pos
            break
        else:
            start += len(line) - 1
    return start, start


class MakoExceptionWrapper(Exception):
    def __init__(self, exc, origin):
        self._exc = exc
        self._origin = origin
        self.args = self._exc.args

    def __getattr__(self, name):
        return getattr(self._exc, name)

    @property
    def source(self):
        return (self._origin,
                _get_start_and_end(self._exc.source,
                                   self._exc.lineno,
                                   self._exc.pos))


class MakoTemplate(object):
    def __init__(self, template_obj, origin=None):
        self.template_obj = template_obj
        self.origin = origin

    def render(self, context):
        try:
            return self.template_obj.render_unicode(**context_to_dict(context))
        except MakoException, me:
            if hasattr(me, 'source'):
                raise MakoExceptionWrapper(me, self.origin)
            else:
                raise me

_lookup = None

def get_lookup():
    global _lookup
    if _lookup is None:
        # If values aren't explicitly specified, default to behaving more or
        # less like Django does by default: escape text by default, load
        # templates from app template directories.
        opts = getattr(settings, 'MAKO_TEMPLATE_OPTS', {
            'input_encoding': 'utf-8',
            'default_filters': ['h'],
        })
        directories = getattr(settings, 'MAKO_TEMPLATE_DIRS', None)
        if directories is None:
            directories = settings.TEMPLATE_DIRS + app_template_dirs
        _lookup = TemplateLookup(directories=directories, **opts)
    return _lookup


class MakoLoader(FSLoader):
    is_usable = True

    def load_template(self, template_name, template_dirs=None):
        if not template_name.endswith('.mako'):
            raise TemplateDoesNotExist(
                '%r is not a ".mako" file' % template_name)
        lookup = get_lookup()
        try:
            real_template = lookup.get_template(template_name)
            return MakoTemplate(real_template, template_name), template_name
        except TopLevelLookupException:
            raise TemplateDoesNotExist(
                'mako template not found for name %s' % template_name)
        except MakoException, me:
            if hasattr(me, 'source'):
                raise MakoExceptionWrapper(me, template_name)
            raise me