From a7804ef05ca5c106e65eb3f1fdfc09b4427bedaf Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 15 Jun 2007 18:03:21 +0200 Subject: [PATCH] [svn] added "debugger of awesomeness" :D --HG-- branch : trunk --- CHANGES | 12 +++ docs/src/i18n.txt | 40 ++++++++- jinja/_debugger.c | 54 ++++++++++++ jinja/debugger.py | 167 ++++++++++++++++++++++++++++++++++++ jinja/environment.py | 44 +++++++++- jinja/loaders.py | 3 +- jinja/translators/python.py | 21 +++-- jinja/utils.py | 137 ++--------------------------- setup.py | 23 +++-- tests/runtime/exception.py | 14 ++- tests/test_i18n.py | 8 ++ 11 files changed, 368 insertions(+), 155 deletions(-) create mode 100644 jinja/_debugger.c create mode 100644 jinja/debugger.py diff --git a/CHANGES b/CHANGES index 814baf8..fca9a18 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,18 @@ Jinja Changelog =============== +Version 1.2 +----------- +(codename to be selected, release date unknown) + +- environments now have a `translator_factory` parameter that allows + to change the translator without subclassing the environment. + +- fixed bug in buffet plugin regarding the package loader + +- once again improved debugger. + + Version 1.1 ----------- (codename: sinka, released Jun 1, 2007) diff --git a/docs/src/i18n.txt b/docs/src/i18n.txt index cfad370..91af491 100644 --- a/docs/src/i18n.txt +++ b/docs/src/i18n.txt @@ -50,9 +50,41 @@ However. For many web applications this might be a way: tmpl = env.get_template('index.html') tmpl.render(LANGUAGE='de_DE') -This example assumes that you use gettext and have a gettext -`Translations` object which is returned by the `get_translator` -function. +This example assumes that you use gettext and have a gettext `Translations` +object which is returned by the `get_translator` function. But you don't +have to use gettext. The only thing Jinja requires is an object with to +functions/methods on it that return and accept unicode strings: +``gettext(string)`` that takes a string, translates and returns it, a +``ngettext(singular, plural, count)`` function that returns the correct plural +form for `count` items. Because some languages have no or multiple plural +forms this is necessary. + + +Translator Factory +================== + +With Jinja 1.2 onwards it's possible to use a translator factory +instead of an enviornment subclass to create a translator for a context. +A translator factory is passed a context and has to return a translator. +Because of the way classes work you can also assign a translator class +that takes a context object as only argument as factory. + +Example: + +.. sourcecode:: python + + from jinja import Environment + from myapplication import get_translator + + def translator_factory(context): + return get_translator(context['LANGUAGE']) + + env = ApplicationEnvironment(translator_factory=translator_factory) + tmpl = env.get_template('index.html') + tmpl.render(LANGUAGE='de_DE') + +This example assumes that the translator returned by `get_translator` +already has a gettext and ngettext function that returns unicode strings. Collecting Translations @@ -67,7 +99,7 @@ Example: .. sourcecode:: pycon >>> env.get_translations('index.html') - [(1, u'foo', None), (2, u'Foo', None), (3, u'%d Foo', u'%d Foos')] + [(1, u'foo', None), (2, u'Foo', None), (3, u'%(count)s Foo', u'%(count)s Foos')] The first item in the tuple is the linenumer, the second one is the singular form and the third is the plural form if given. diff --git a/jinja/_debugger.c b/jinja/_debugger.c new file mode 100644 index 0000000..203cd84 --- /dev/null +++ b/jinja/_debugger.c @@ -0,0 +1,54 @@ +/** + * Jinja Extended Debugger + * ~~~~~~~~~~~~~~~~~~~~~~~ + * + * this module allows the jinja debugger to set the tb_next flag + * on traceback objects. This is required to inject a traceback into + * another one. + * + * :copyright: 2007 by Armin Ronacher. + * :license: BSD, see LICENSE for more details. + */ + +#include + + +/** + * set the tb_next attribute of a traceback object + */ +PyObject* +tb_set_next(PyObject *self, PyObject *args) +{ + PyObject *tb, *next; + + if (!PyArg_ParseTuple(args, "OO", &tb, &next)) + return NULL; + if (!(PyTraceBack_Check(tb) && (PyTraceBack_Check(next) || next == Py_None))) { + PyErr_SetString(PyExc_TypeError, "traceback object required."); + return NULL; + } + + ((PyTracebackObject*)tb)->tb_next = next; + + Py_INCREF(Py_None); + return Py_None; +} + + +static PyMethodDef module_methods[] = { + {"tb_set_next", (PyCFunction)tb_set_next, METH_VARARGS, + "Set the tb_next member of a traceback object."}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + + +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ +#define PyMODINIT_FUNC void +#endif +PyMODINIT_FUNC +init_debugger(void) +{ + PyObject *module = Py_InitModule3("_debugger", module_methods, ""); + if (!module) + return; +} diff --git a/jinja/debugger.py b/jinja/debugger.py new file mode 100644 index 0000000..396c665 --- /dev/null +++ b/jinja/debugger.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +""" + jinja.debugger + ~~~~~~~~~~~~~~ + + The debugger module of awesomeness. + + :copyright: 2007 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + +import sys +from random import randrange +from opcode import opmap +from types import CodeType + +# if we have extended debugger support we should really use it +try: + from jinja._debugger import * + has_extended_debugger = True +except ImportError: + has_extended_debugger = False + +# we need the RUNTIME_EXCEPTION_OFFSET to skip the not used frames +from jinja.utils import RUNTIME_EXCEPTION_OFFSET + + +def fake_template_exception(exc_type, exc_value, traceback, filename, lineno, + source, context_or_env): + """ + Raise an exception "in a template". Return a traceback + object. This is used for runtime debugging, not compile time. + """ + # some traceback systems allow to skip frames + __traceback_hide__ = True + + # create the namespace which will be the local namespace + # of the new frame then. Some debuggers show local variables + # so we better inject the context and not the evaluation loop context. + from jinja.datastructure import Context + if isinstance(context_or_env, Context): + env = context_or_env.environment + namespace = context_or_env.to_dict() + else: + env = context_or_env + namespace = {} + + # no unicode for filenames + if isinstance(filename, unicode): + filename = filename.encode('utf-8') + + # generate an jinja unique filename used so that linecache + # gets data that doesn't interferes with other modules + if filename is None: + vfilename = 'jinja://~%d' % randrange(0, 10000) + filename = '' + else: + vfilename = 'jinja://%s' % filename + + # now create the used loaded and update the linecache + loader = TracebackLoader(env, source, filename) + loader.update_linecache(vfilename) + globals = { + '__name__': vfilename, + '__file__': vfilename, + '__loader__': loader + } + + # use the simple debugger to reraise the exception in the + # line where the error originally occoured + globals['__exception_to_raise__'] = (exc_type, exc_value) + offset = '\n' * (lineno - 1) + code = compile(offset + 'raise __exception_to_raise__[0], ' + '__exception_to_raise__[1]', + vfilename or '