44

So far I've found it impossible to produce usable tracebacks when Mako templates aren't coded correctly.

Is there any way to debug templates besides iterating for every line of code?

Brandon Rhodes
  • 83,755
  • 16
  • 106
  • 147
Nikhil
  • 5,705
  • 1
  • 32
  • 30
  • I found this general problem with Mako. Perhaps you should try Jinja2, for which 1. tracebacks go into the templating code, and 2. there is a better separation of view and model, with sandboxing. – Ali Afshar Dec 24 '08 at 09:41
  • I don't really like Jinja's blocks (which are similar to Mako's defs) because they're echoed where they're called /and/ where they're defined. Mako decouples them - they're only echoed where they're called. That said, I'm getting really frustrated with Mako, so I'll try Jinja soon. Thanks. – Nikhil Dec 24 '08 at 22:20
  • Also maybe worth considering moving more complex logic out of the template layer and into one of the other layers of your application. – prairiedogg Dec 25 '08 at 00:13
  • @Nikhil: Jinja2 has the equivalent to Mako's defs too. They are called macros: http://jinja.pocoo.org/2/documentation/templates#macros. – nosklo Dec 26 '08 at 16:07
  • @Prairie Dogg: Perhaps it's just inexperience with Mako syntax, then, as I'm not coding logic into the templates. @nosklo: That's perfect. Thanks! – Nikhil Dec 27 '08 at 04:47

6 Answers6

48

Mako actually provides a VERY nice way to track down errors in a template:

from mako import exceptions

try:
    template = lookup.get_template(uri)
    print template.render()
except:
    print exceptions.html_error_template().render()
chris
  • 2,404
  • 3
  • 27
  • 33
Kenan Banks
  • 207,056
  • 34
  • 155
  • 173
  • 6
    There's also [exceptions.text_error_template()](http://www.makotemplates.org/docs/usage.html#mako.exceptions.text_error_template).render() for non-HTML environments. – Ian Mackinnon Jun 05 '11 at 10:47
  • 3
    Hum, better avoid catch-all excepts. – gb. Mar 16 '12 at 06:44
  • 1
    Can anyone please tell how to use this code? I mean what is uri? and where to place this code in my python script – Aakash Goyal Apr 16 '14 at 05:16
  • It seems necessary to add the second answer to this one in order to get sensible error messages, i.e. `app.config['MAKO_TRANSLATE_EXCEPTIONS'] = False` I wrapped the render_template function to include the exception handling, something like this: `from flask.ext.mako import render_template as render_template_1` `def render_template(*args, **kwargs): try: return render_template_1(*args, **kwargs2) except: ...` Doesn't work as a comment, I'll put it in an answer. – Sam Watkins Sep 20 '17 at 02:53
  • As for `pylons` which I am also using, unfortunately, `handle_mako_error` in `pylons/error.py` is bad, should finish with `raise` not `raise (exc, None, sys.exc_info()[2])`. Can copy that function out and fix it for the app. – Sam Watkins Nov 20 '17 at 01:18
4

Looking at the Flask-Mako source, I found an undocumented configuration parameter called MAKO_TRANSLATE_EXCEPTIONS.

Set this to False in your Flask app config and you'll get nice exceptions bubbling up from the template. This accomplishes the same thing as @Mariano suggested, without needing to edit the source. Apparently, this parameter was added after Mariano's answer.

ford
  • 10,687
  • 3
  • 47
  • 54
1

I break them down into pieces, and then reassemble the pieces when I've found the problem.

Not good, but it's really hard to tell what went wrong in a big, complex template.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
1

My main frustration with Mako was that it was hard to see what was happening in the template. As the template code is a runnable object that is in-memory, no debugger can look into it.

One solution is to write the template code to file, and re-run the template using this file as a standard python module. Then you can debug to your hearts content.

An example:

import sys
from mako import exceptions, template 
from mako.template import DefTemplate
from mako.runtime import _render

<Do Great Stuff>

try:
    template.render(**arguments))
except:
    # Try to re-create the error using a proper file template
    # This will give a clearer error message.
    with open('failed_template.py', 'w') as out:
        out.write(template._code)
    import failed_template
    data = dict(callable=failed_template.render_body, **arguments)
    try:
        _render(DefTemplate(template, failed_template.render_body),
                failed_template.render_body,
                [],
                data)
    except:
        msg = '<An error occurred when rendering template for %s>\n'%arguments
        msg += exceptions.text_error_template().render()
        print(msg, file=sys.stderr)
        raise
EvertW
  • 1,160
  • 9
  • 18
  • Very handy :) I just needed to add some imports: import sys from mako import exceptions, template from mako.template import DefTemplate from mako.runtime import _render – s6mike Nov 17 '20 at 19:32
  • 1
    Good one, forgot to add the imports. I'll edit the answer, thx. – EvertW Nov 18 '20 at 09:11
0

Using flask_mako, I find it's easier to skip over the TemplateError generation and just pass up the exception. I.e. in flask_mako.py, comment out the part that makes the TemplateError and just do a raise:

def _render(template, context, app):
 """Renders the template and fires the signal"""
app.update_template_context(context)
try:
    rv = template.render(**context)
    template_rendered.send(app, template=template, context=context)
    return rv
except:
    #translated = TemplateError(template)                                                                                                                 
    #raise translated                                                                                                                                     
    raise

}

Then you'll see a regular python exception that caused the problem along with line numbers in the template.

Mariano Alvira
  • 216
  • 1
  • 3
0

Combining the two top answers with my own special sauce:

from flask.ext.mako import render_template as render_template_1
from mako import exceptions

app.config['MAKO_TRANSLATE_EXCEPTIONS'] = False    # seems to be necessary

def render_template(*args, **kwargs):
    kwargs2 = dict(**kwargs)
    kwargs2['config'] = app.config     # this is irrelevant, but useful
    try:
        return render_template_1(*args, **kwargs2)
    except:
        if app.config.get('DEBUG'):
            return exceptions.html_error_template().render()
        raise

It wraps the stock "render_template" function:

  • catch exceptions, and
    • if debugging, render a backtrace
    • if not debugging, raise the exception again so it will be logged
  • make config accessible from the page (irrelevant)
Sam Watkins
  • 7,819
  • 3
  • 38
  • 38