Opened 14 years ago

Closed 14 years ago

Last modified 12 years ago

#12992 closed (fixed)

New template loaders lose debug information

Reported by: Karen Tracey Owned by: nobody
Component: Template system Version: dev
Severity: Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

For a TemplateSyntaxErorr, if you have the new-style template loaders in settings.py:

TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
#     'django.template.loaders.eggs.Loader',
)

The Template error section of the debug page doesn't report the name of the template file where the problem occurred. Instead it reports In template &lt;unknown source&gt;, error at line <whatever>. One problem is the angle brackets aren't displayed properly but the real problem is that the source file name is "unknown".

Problem is due to the new-style loader's returning already-compiled templates (from http://code.djangoproject.com/browser/django/trunk/django/template/loader.py#L36):

    def __call__(self, template_name, template_dirs=None):
        return self.load_template(template_name, template_dirs)

    def load_template(self, template_name, template_dirs=None):
        source, origin = self.load_template_source(template_name, template_dirs)
        template = get_template_from_string(source, name=template_name)
        return template, origin

The old loaders return the source, origin as originally returned by load_template_source, and the template source is compiled into a <django.template.Template> when get_template finds what it got doesn't have a render (from http://code.djangoproject.com/browser/django/trunk/django/template/loader.py#L139):

def get_template(template_name):
    """
    Returns a compiled Template object for the given template name,
    handling template inheritance recursively.
    """
    template, origin = find_template(template_name)
    if not hasattr(template, 'render'):
        # template needs to be compiled
        template = get_template_from_string(template, origin, template_name)
    return template

That call to get_template_from_string includes the origin, which is what is needed to report the file name on the debug page.

Note origin in this bit of code is different from the origin returned by load_template_source above. That part of the load_template_source return value is called display_name in other parts of the code and is one of four args passed to make_origin to produce the origin passed in to get_template_from_string in the working path. So we cannot simply change the new-style loader code to pass the thing it calls origin to get_template_from_string. Nor does attempting to call make_origin with likely-seeming args in the new-style loader code work, possibly because I don't really have a clue what those args are supposed to all be. What does work is:

    def __call__(self, template_name, template_dirs=None):
        return self.load_template_source(template_name, template_dirs)

But I gather based on the doc here: http://docs.djangoproject.com/en/dev/ref/templates/api/#using-an-alternative-template-language that loaders are supposed to have a load_template method that returns a pre-compiled template so it would probably be better for someone with a clue in this area to figure out how to get the origin information properly attached to the already-compiled template returned by the new-style loaders.

Change History (5)

comment:1 by Karen Tracey, 14 years ago

Changing the new loader's load_template to:

    def load_template(self, template_name, template_dirs=None):
        source, display_name = self.load_template_source(template_name, template_dirs)
        origin = make_origin(display_name, self.load_template_source, template_name, template_dirs)
        template = get_template_from_string(source, origin, template_name)
        return template, display_name

fixes the problem of the missing template file name on the debug page, but this is kind of ugly. The old loaders are returning a tuple of template source and its file name, this 2nd element of the tuple is turned into an origin in find_template (from http://code.djangoproject.com/browser/django/trunk/django/template/loader.py#L120):

    for loader in template_source_loaders:
        try:
            source, display_name = loader(name, dirs)
            return (source, make_origin(display_name, loader, name, dirs))

and that return value from make_origin is then passed when the template source is compiled via get_template_from_string, in get_template. For the new loader the return value from that make_origin call in find_template is never used, since get_template determines the returned tempalte value has already been compiled. This is good since that call to make_origin for the new loader case doesn't produce an origin object capable of retrieving the template source, since it expects to be given a loader that returns the template source, not an already-compiled template. But it's ugly to have that useless call in the new path producing something that could never be usefully used.

comment:2 by anonymous, 14 years ago

Version: 1.1SVN

comment:3 by Jacob, 14 years ago

Triage Stage: UnreviewedAccepted

comment:4 by Karen Tracey, 14 years ago

Resolution: fixed
Status: newclosed

(In [12643]) Fixed #12992: Adjusted the new template loader code so that the template
file name is correctly reported on the debug page when a template syntax
error is raised.

comment:5 by Jacob, 12 years ago

milestone: 1.2

Milestone 1.2 deleted

Note: See TracTickets for help on using tickets.
Back to Top