Code

Opened 4 years ago

Closed 4 years ago

Last modified 3 years ago

#12992 closed (fixed)

New template loaders lose debug information

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

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.

Attachments (0)

Change History (5)

comment:1 Changed 4 years ago by kmtracey

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

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 Changed 4 years ago by anonymous

  • Version changed from 1.1 to SVN

comment:3 Changed 4 years ago by jacob

  • Triage Stage changed from Unreviewed to Accepted

comment:4 Changed 4 years ago by kmtracey

  • Resolution set to fixed
  • Status changed from new to closed

(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 Changed 3 years ago by jacob

  • milestone 1.2 deleted

Milestone 1.2 deleted

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.