28

When I'm running Jinja2 in Google App Engine, I get useless debugging information. I gather this is because of this item in the FAQ:

My tracebacks look weird. What’s happening?

If the speedups module is not compiled and you are using a Python installation without ctypes (Python 2.4 without ctypes, Jython or Google’s AppEngine) Jinja2 is unable to provide correct debugging information and the traceback may be incomplete. There is currently no good workaround for Jython or the AppEngine as ctypes is unavailable there and it’s not possible to use the speedups extension.

While there is no 'good' workaround for this at the moment, is there any workaround so that the information printed when exceptions arise can be made more helpful?

Thank you for reading.

Brian

Brian M. Hunt
  • 81,008
  • 74
  • 230
  • 343
  • 4
    My solution is to simply debug locally. Although that can be hard since you can't always perfectly mimic the app engine environment and variables. – Wolph Jul 08 '10 at 19:10
  • i use jinja2 and debugging information given seems to be quite helpful. ( at least it point to file and line number. ) do you want more specific information ? – iamgopal Jul 10 '10 at 10:57
  • @iamgopal: Are you using Jinja2 in Google App Engine? – Brian M. Hunt Jul 10 '10 at 12:52
  • yes. but i am not using webapp, i am using it with werkzeug in kay framework. – iamgopal Jul 10 '10 at 14:18
  • can you show a sample when exeptions printed ? I think you may have dubug turn off or something ? – iamgopal Jul 12 '10 at 02:53
  • 1
    @Brian M. Hunt: I've added a bounty to this question since I also find it a very interesting question. Let's hope someone else knows (or wrote) a fitting solution for this. I've recently modified `werkzeug` a little to get the console working in the app engine but `jinja2` still seems less pretty. – Wolph Jul 21 '10 at 19:47
  • the author of jinja2 is already on so, i wonder how can we send message across http://stackoverflow.com/users/19990/armin-ronacher – iamgopal Jul 22 '10 at 04:50
  • I'll ask in the IRC channel, perhaps that can get some attention to this question :) – Wolph Jul 22 '10 at 11:54
  • 2
    I suspect the only way to get it to work would be to monkey patch the GAE dev_svr so it allows c modules to be loaded... I have suggested this feature to the gae team but who knows if they will ever get to it – gravitation Jul 22 '10 at 14:17
  • Glad to see interest in this question. I hope someone has time to turn their mind to a solution because I think Jinja2 really has wonderful debugging information -- when it works (ie you have c modules). – Brian M. Hunt Jul 24 '10 at 20:15
  • Agreed. Getting stacktraces into your html files is brilliant. Definately better debugging like that than any other template engine I've seen yet. – Wolph Jul 24 '10 at 22:58

6 Answers6

29

You can get around this by adding _ctypes and gestalt to the development server's C module whitelist with monkeypatching.

To do so, put the following snippet at the top of your main.py:

import os
if os.environ.get('SERVER_SOFTWARE', '').startswith('Dev'):
    # Enable ctypes for Jinja debugging
    from google.appengine.tools.dev_appserver import HardenedModulesHook
    HardenedModulesHook._WHITE_LIST_C_MODULES += ['_ctypes', 'gestalt']

You can also use this trick to enable other C modules, if you have similar local-only module needs. Do note that these modules still won't actually work once you deploy, so tread carefully.

On SDK 1.6.3 using python2.7 you need to change the above code to:

import os
if os.environ.get('SERVER_SOFTWARE', '').startswith('Dev'):
    # Enable ctypes for Jinja debugging
    import sys
    from google.appengine.tools.dev_appserver import HardenedModulesHook
    assert isinstance(sys.meta_path[0], HardenedModulesHook)
    sys.meta_path[0]._white_list_c_modules += ['_ctypes', 'gestalt']

On SDK 1.8.6 for python 2.7, try this:

PRODUCTION_MODE = not os.environ.get(
    'SERVER_SOFTWARE', 'Development').startswith('Development')
if not PRODUCTION_MODE:
    from google.appengine.tools.devappserver2.python import sandbox
    sandbox._WHITE_LIST_C_MODULES += ['_ctypes', 'gestalt']
rescdsk
  • 8,739
  • 4
  • 36
  • 32
T Johansson
  • 1,395
  • 10
  • 12
  • 2
    This doesn't seem to be working for me on the python27 runtime. Is it possible this has changed slightly for python2.7? – Alex Pretzlav Jan 29 '12 at 02:01
  • 2
    please star the issue on http://code.google.com/p/googleappengine/issues/detail?id=7164 – schettino72 Mar 17 '12 at 12:44
  • 1
    The second workaround for python 2.7 doesn't work for me. SDK 1.7.3 – Ali Nov 23 '12 at 02:10
  • 2
    From the line "import HardenedModulesHook" I get the following error: "ImportError: No module named simplejson". SDK: 1.8.1 Python: 2.7. It happens using both methods – Robert Dodd Jul 11 '13 at 21:37
2

Perhaps just use PyCharm's interactive debugger and step through the code:

http://www.jetbrains.com/pycharm/quickstart/#RunAndDebug

Freddie
  • 1,019
  • 10
  • 11
  • How would that help? Is PyCharm able to read the stacktraces from the logs and reconstruct the same scenario? If not than you have probably misread the question since this is about Jinja2 running on the Google App Engine. Not locally. – Wolph Aug 06 '10 at 00:39
  • I have been able to debug when a template throws an exception - but running locally only. Furthermore, I have yet to try the Google App Engine support built-in to the application. When I have a chance I'll see what I can observe that may be of assistance. – Freddie Aug 09 '10 at 23:47
2

I use the following monkeypatch to enable slightly more helpful information when an exception occurs during Jinja2 template rendering:

# 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

This will result in slightly more accurate exception tracebacks in your error logs. I don't think it usually shows you exactly what went wrong (but if I remember correctly it sometimes does), but it will at least narrow the exception down to the correct template.

Why this works, I do not know (or cannot remember).

As an example, I just added some code that will trigger an exception to one of my templates. Under the development server, this is what the "normal" exception handler shows me:

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

But the exception is not in the accounts/base.html template, it's in accounts/edit_card.html. This is the most frustrating part of debugging Jinja2 template exceptions on App Engine: The source of the exception is almost always misrepresented. In my experience, the source is usually reported as either the parent template or as some template macro.

With the exception logging monkeypatch installed, the same exception generates this traceback in the logs:

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

There's still a lot of extraneous information here, but this traceback at least points me in the right direction. It claims that the problem is on line 54 of accounts/edit_card.html (the correct template), but the actual exception occurs at line 86.

But given the correct template and the correct exception, I can pretty easily find that the troublesome code is this

{% for x in sequence.sequence() %}
    {{ x.y }}
{% endfor %}

where there is no sequence variable in the template context.

This isn't a perfect solution, but I've found it mighty helpful.

Will McCutchen
  • 13,047
  • 3
  • 44
  • 43
1

A way to avoid monkey-patching (which depends on the changing internals of the SDK) is to use the imp module, which is at least currently not disabled in the local development environment. Then just load _ctypes like this to enable better Jinja2 debugging:

import imp
file, pathname, description =  imp.find_module('_ctypes')
imp.load_module('_ctypes', file, pathname, description)
vbraun
  • 1,851
  • 17
  • 14
1

Not sure if that will be helpful, but it might be possible to at least add block templatetag like django's 'debug' which will at least help to localize the problem.

HoverHell
  • 4,739
  • 3
  • 21
  • 23
  • * I'm referring to this http://github.com/citylive/Django-Template-Tags/blob/master/templatetags/debug.py additional templatetag. – HoverHell Jul 23 '10 at 10:03
0

When I hit a problem like that I try to debug it on my local iPython shell. I wonder what the code that generates such a bug is. There should be a way to write a test for it.

Tudor
  • 4,137
  • 5
  • 38
  • 54