Я использую следующий monkeypatch для включения чуть более полезной информации, когда возникает исключение во время рендеринга шаблона Jinja2:
# Enabling this monkeypatch can help track down hard to find errors that crop
# up during template rendering (since Jinja's own error reporting is so
# unhelpful on AppEngine).
real_handle_exception = environment.handle_exception
def handle_exception(self, *args, **kwargs):
import logging, traceback
logging.error('Template exception:\n%s', traceback.format_exc())
real_handle_exception(self, *args, **kwargs)
environment.handle_exception = handle_exception
Это приведет к чуть более точным отслеживаниям исключений в ваших журналах ошибок. Я не думаю, что это обычно показывает вам точно, что пошло не так (но если я правильно помню, иногда это происходит), но это, по крайней мере, сузит исключение до правильного шаблона.
Почему это работает, я не знаю (или не могу вспомнить).
В качестве примера, я просто добавил код, который вызовет исключение для одного из моих шаблонов. Под сервером разработки вот что мне показывает «обычный» обработчик исключений:
Traceback (most recent call last):
File "/Users/will/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/webapp/__init__.py", line 511, in __call__
handler.get(*groups)
File "/Users/will/workspace/keypremium/ki/shared/decorators.py", line 27, in inner
return func(self, *args, **kwargs)
File "/Users/will/workspace/keypremium/account/views.py", line 114, in get
self.render_jinja('accounts/edit_card.html', ctx)
File "/Users/will/workspace/keypremium/ki/webapp/handlers.py", line 186, in render_jinja
return self.response.out.write(jinja.render(template_path, new_context))
File "/Users/will/workspace/keypremium/ki/shared/jinja/__init__.py", line 21, in render
return template.render(context)
File "/Users/will/workspace/keypremium/ki/ext/jinja2/environment.py", line 705, in render
return self.environment.handle_exception(exc_info, True)
File "/Users/will/workspace/keypremium/ki/shared/jinja/environment.py", line 24, in handle_exception
real_handle_exception(self, *args, **kwargs)
File "/Users/will/workspace/keypremium/templates/accounts/edit_card.html", line 1, in top-level template code
{% extends 'accounts/base.html' %}
UndefinedError: 'sequence' is undefined
Но исключение не в шаблоне accounts/base.html
, а в accounts/edit_card.html
. Это самая неприятная часть отладки исключений шаблонов Jinja2 в App Engine: источник исключения почти всегда искажается. По моему опыту, источник обычно сообщается либо как родительский шаблон, либо как некоторый макрос шаблона.
При установленном monkeypatch для регистрации исключений, то же исключение генерирует эту трассировку в журналах:
Traceback (most recent call last):
File "/Users/will/workspace/keypremium/ki/ext/jinja2/environment.py", line 702, in render
return concat(self.root_render_func(self.new_context(vars)))
File "/Users/will/workspace/keypremium/templates/accounts/edit_card.html", line 11, in root
<div class="errors">
File "/Users/will/workspace/keypremium/templates/accounts/base.html", line 11, in root
</html>
File "/Users/will/workspace/keypremium/templates/accounts/edit_card.html", line 54, in block_content
<td>{{ form.cvv2|safe }}</td>
File "/Users/will/workspace/keypremium/ki/ext/jinja2/environment.py", line 352, in getattr
return getattr(obj, attribute)
File "/Users/will/workspace/keypremium/ki/ext/jinja2/runtime.py", line 445, in _fail_with_undefined_error
raise self._undefined_exception(hint)
UndefinedError: 'sequence' is undefined
Здесь все еще много посторонней информации, но эта обратная связь, по крайней мере, указывает мне верное направление. Он утверждает, что проблема в строке 54 accounts/edit_card.html
(правильный шаблон), но фактическое исключение происходит в строке 86.
Но, учитывая правильный шаблон и правильное исключение, я довольно легко могу обнаружить, что проблемный код - это
{% for x in sequence.sequence() %}
{{ x.y }}
{% endfor %}
, где в контексте шаблона нет переменной sequence
.
Это не идеальное решение, но я нашел его очень полезным.