--- /dev/null
+from jinja2 import Environment
+
+
+env = Environment(line_statement_prefix="#", variable_start_string="${", variable_end_string="}")
+
+
+print env.from_string("""\
+<ul>
+# for item in range(10)
+ <li class="${loop.cycle('odd', 'even')}">${item}</li>
+# endfor
+</ul>\
+""").render()
from jinja2 import Environment
print Environment().from_string("""\
+{{ foo.bar }}
{% trans %}Hello {{ user }}!{% endtrans %}
{% trans count=users|count %}{{ count }} user{% pluralize %}{{ count }} users{% endtrans %}
""").render(user="someone")
+++ /dev/null
-/**
- * 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.
- *
- * For better windows support (not everybody has a visual studio 2003
- * at home) it would be a good thing to have a ctypes implementation, but
- * because the struct is not exported there is currently no sane way.
- *
- * :copyright: 2007 by Armin Ronacher.
- * :license: BSD, see LICENSE for more details.
- */
-
-#include <Python.h>
-
-
-/**
- * set the tb_next attribute of a traceback object
- */
-static PyObject *
-tb_set_next(PyObject *self, PyObject *args)
-{
- PyTracebackObject *tb, *old;
- PyObject *next;
-
- if (!PyArg_ParseTuple(args, "O!O:tb_set_next", &PyTraceBack_Type, &tb, &next))
- return NULL;
- if (next == Py_None)
- next = NULL;
- else if (!PyTraceBack_Check(next)) {
- PyErr_SetString(PyExc_TypeError,
- "tb_set_next arg 2 must be traceback or None");
- return NULL;
- }
- else
- Py_INCREF(next);
-
- old = tb->tb_next;
- tb->tb_next = (PyTracebackObject*)next;
- Py_XDECREF(old);
-
- 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("jinja._debugger", module_methods, "");
- if (!module)
- return;
-}
+++ /dev/null
-# -*- coding: utf-8 -*-
-"""
- jinja._native
- ~~~~~~~~~~~~~
-
- This module implements the native base classes in case of not
- having a jinja with the _speedups module compiled.
-
- Note that if you change semantics here you have to edit the
- _speedups.c file to in order to support those changes for jinja
- setups with enabled speedup module.
-
- :copyright: 2007 by Armin Ronacher.
- :license: BSD, see LICENSE for more details.
-"""
-from jinja.datastructure import Deferred
-from jinja.utils import deque
-
-
-class BaseContext(object):
-
- def __init__(self, undefined_singleton, globals, initial):
- self._undefined_singleton = undefined_singleton
- self.current = current = {}
- self._stack = deque([current, initial, globals])
- self.globals = globals
- self.initial = initial
-
- self._push = self._stack.appendleft
- self._pop = self._stack.popleft
-
- def stack(self):
- return list(self._stack)[::-1]
- stack = property(stack)
-
- def pop(self):
- """Pop the last layer from the stack and return it."""
- rv = self._pop()
- self.current = self._stack[0]
- return rv
-
- def push(self, data=None):
- """
- Push one layer to the stack and return it. Layer must be
- a dict or omitted.
- """
- data = data or {}
- self._push(data)
- self.current = self._stack[0]
- return data
-
- def __getitem__(self, name):
- """
- Resolve one item. Restrict the access to internal variables
- such as ``'::cycle1'``. Resolve deferreds.
- """
- if not name.startswith('::'):
- for d in self._stack:
- if name in d:
- rv = d[name]
- if rv.__class__ is Deferred:
- rv = rv(self, name)
- # never touch the globals!
- if d is self.globals:
- self.initial[name] = rv
- else:
- d[name] = rv
- return rv
- return self._undefined_singleton
-
- def __setitem__(self, name, value):
- """Set a variable in the outermost layer."""
- self.current[name] = value
-
- def __delitem__(self, name):
- """Delete a variable in the outermost layer."""
- if name in self.current:
- del self.current[name]
-
- def __contains__(self, name):
- """ Check if the context contains a given variable."""
- for layer in self._stack:
- if name in layer:
- return True
- return False
+++ /dev/null
-/**
- * jinja._speedups
- * ~~~~~~~~~~~~~~~
- *
- * This module implements the BaseContext, a c implementation of the
- * Context baseclass. If this extension is not compiled the datastructure
- * module implements a class in python.
- *
- * Note that if you change semantics here you have to edit the _native.py
- * to in order to support those changes for jinja setups without the
- * speedup module too.
- *
- * :copyright: 2007 by Armin Ronacher.
- * :license: BSD, see LICENSE for more details.
- */
-
-#include <Python.h>
-#include <structmember.h>
-
-/* Set by init_constants to real values */
-static PyObject *Deferred;
-
-/**
- * Internal struct used by BaseContext to store the
- * stacked namespaces.
- */
-struct StackLayer {
- PyObject *dict; /* current value, a dict */
- struct StackLayer *prev; /* lower struct layer or NULL */
-};
-
-/**
- * BaseContext python class.
- */
-typedef struct {
- PyObject_HEAD
- struct StackLayer *globals; /* the dict for the globals */
- struct StackLayer *initial; /* initial values */
- struct StackLayer *current; /* current values */
- long stacksize; /* current size of the stack */
- PyObject *undefined_singleton; /* the singleton returned on missing values */
-} BaseContext;
-
-/**
- * Called by init_speedups in order to retrieve references
- * to some exceptions and classes defined in jinja python modules
- */
-static int
-init_constants(void)
-{
- PyObject *datastructure = PyImport_ImportModule("jinja.datastructure");
- if (!datastructure)
- return 0;
- Deferred = PyObject_GetAttrString(datastructure, "Deferred");
- Py_DECREF(datastructure);
- return 1;
-}
-
-/**
- * GC Helper
- */
-static int
-BaseContext_clear(BaseContext *self)
-{
- struct StackLayer *current = self->current, *tmp;
- while (current) {
- tmp = current;
- Py_XDECREF(current->dict);
- current->dict = NULL;
- current = tmp->prev;
- PyMem_Free(tmp);
- }
- self->current = NULL;
- return 0;
-}
-
-/**
- * Deallocator for BaseContext.
- *
- * Frees the memory for the stack layers before freeing the object.
- */
-static void
-BaseContext_dealloc(BaseContext *self)
-{
- BaseContext_clear(self);
- self->ob_type->tp_free((PyObject*)self);
-}
-
-/**
- * GC Helper
- */
-static int
-BaseContext_traverse(BaseContext *self, visitproc visit, void *args)
-{
- int vret;
- struct StackLayer *layer = self->current;
-
- while (layer) {
- vret = visit(layer->dict, args);
- if (vret != 0)
- return vret;
- layer = layer->prev;
- }
-
- return 0;
-}
-
-/**
- * Initializes the BaseContext.
- *
- * Like the native python class it takes a reference to the undefined
- * singleton which will be used for undefined values.
- * The other two arguments are the global namespace and the initial
- * namespace which usually contains the values passed to the render
- * function of the template. Both must be dicts.
- */
-static int
-BaseContext_init(BaseContext *self, PyObject *args, PyObject *kwds)
-{
- PyObject *undefined = NULL, *globals = NULL, *initial = NULL;
-
- if (!PyArg_ParseTuple(args, "OOO", &undefined, &globals, &initial))
- return -1;
- if (!PyDict_Check(globals) || !PyDict_Check(initial)) {
- PyErr_SetString(PyExc_TypeError, "stack layers must be dicts.");
- return -1;
- }
-
- self->current = PyMem_Malloc(sizeof(struct StackLayer));
- self->current->prev = NULL;
- self->current->dict = PyDict_New();
- if (!self->current->dict)
- return -1;
-
- self->initial = PyMem_Malloc(sizeof(struct StackLayer));
- self->initial->prev = NULL;
- self->initial->dict = initial;
- Py_INCREF(initial);
- self->current->prev = self->initial;
-
- self->globals = PyMem_Malloc(sizeof(struct StackLayer));
- self->globals->prev = NULL;
- self->globals->dict = globals;
- Py_INCREF(globals);
- self->initial->prev = self->globals;
-
- self->undefined_singleton = undefined;
- Py_INCREF(undefined);
-
- self->stacksize = 3;
- return 0;
-}
-
-/**
- * Pop the highest layer from the stack and return it
- */
-static PyObject*
-BaseContext_pop(BaseContext *self)
-{
- PyObject *result;
- struct StackLayer *tmp = self->current;
-
- if (self->stacksize <= 3) {
- PyErr_SetString(PyExc_IndexError, "stack too small.");
- return NULL;
- }
- result = self->current->dict;
- assert(result);
- self->current = tmp->prev;
- PyMem_Free(tmp);
- self->stacksize--;
- /* Took the reference to result from the struct. */
- return result;
-}
-
-/**
- * Push a new layer to the stack and return it. If no parameter
- * is provided an empty dict is created. Otherwise the dict passed
- * to it is used as new layer.
- */
-static PyObject*
-BaseContext_push(BaseContext *self, PyObject *args)
-{
- PyObject *value = NULL;
- struct StackLayer *new;
-
- if (!PyArg_ParseTuple(args, "|O:push", &value))
- return NULL;
- if (!value) {
- value = PyDict_New();
- if (!value)
- return NULL;
- }
- else if (!PyDict_Check(value)) {
- PyErr_SetString(PyExc_TypeError, "dict required.");
- return NULL;
- }
- else
- Py_INCREF(value);
- new = PyMem_Malloc(sizeof(struct StackLayer));
- if (!new) {
- Py_DECREF(value);
- return NULL;
- }
- new->dict = value;
- new->prev = self->current;
- self->current = new;
- self->stacksize++;
- Py_INCREF(value);
- return value;
-}
-
-/**
- * Getter that creates a list representation of the internal
- * stack. Used for compatibility with the native python implementation.
- */
-static PyObject*
-BaseContext_getstack(BaseContext *self, void *closure)
-{
- int idx = 0;
- struct StackLayer *current = self->current;
- PyObject *result = PyList_New(self->stacksize);
- if (!result)
- return NULL;
- while (current) {
- Py_INCREF(current->dict);
- PyList_SET_ITEM(result, idx++, current->dict);
- current = current->prev;
- }
- PyList_Reverse(result);
- return result;
-}
-
-/**
- * Getter that returns a reference to the current layer in the context.
- */
-static PyObject*
-BaseContext_getcurrent(BaseContext *self, void *closure)
-{
- Py_INCREF(self->current->dict);
- return self->current->dict;
-}
-
-/**
- * Getter that returns a reference to the initial layer in the context.
- */
-static PyObject*
-BaseContext_getinitial(BaseContext *self, void *closure)
-{
- Py_INCREF(self->initial->dict);
- return self->initial->dict;
-}
-
-/**
- * Getter that returns a reference to the global layer in the context.
- */
-static PyObject*
-BaseContext_getglobals(BaseContext *self, void *closure)
-{
- Py_INCREF(self->globals->dict);
- return self->globals->dict;
-}
-
-/**
- * Implements the context lookup.
- *
- * This works exactly like the native implementation but a lot
- * faster. It disallows access to internal names (names that start
- * with "::") and resolves Deferred values.
- */
-static PyObject*
-BaseContext_getitem(BaseContext *self, PyObject *item)
-{
- PyObject *result;
- char *name = NULL;
- int isdeferred;
- struct StackLayer *current = self->current;
-
- /* allow unicode keys as long as they are ascii keys */
- if (PyUnicode_CheckExact(item)) {
- item = PyUnicode_AsASCIIString(item);
- if (!item)
- goto missing;
- }
- else if (!PyString_Check(item))
- goto missing;
-
- /* disallow access to internal jinja values */
- name = PyString_AS_STRING(item);
- if (name[0] == ':' && name[1] == ':')
- goto missing;
-
- while (current) {
- /* GetItemString just builds a new string from "name" again... */
- result = PyDict_GetItem(current->dict, item);
- if (!result) {
- current = current->prev;
- continue;
- }
- isdeferred = PyObject_IsInstance(result, Deferred);
- if (isdeferred == -1)
- return NULL;
- else if (isdeferred) {
- PyObject *namespace;
- PyObject *resolved = PyObject_CallFunctionObjArgs(
- result, self, item, NULL);
- if (!resolved)
- return NULL;
-
- /* never touch the globals */
- if (current == self->globals)
- namespace = self->initial->dict;
- else
- namespace = current->dict;
- if (PyDict_SetItem(namespace, item, resolved) < 0)
- return NULL;
- Py_INCREF(resolved);
- return resolved;
- }
- Py_INCREF(result);
- return result;
- }
-
-missing:
- Py_INCREF(self->undefined_singleton);
- return self->undefined_singleton;
-}
-
-/**
- * Check if the context contains a given value.
- */
-static int
-BaseContext_contains(BaseContext *self, PyObject *item)
-{
- char *name;
- struct StackLayer *current = self->current;
-
- /* allow unicode objects as keys as long as they are ASCII */
- if (PyUnicode_CheckExact(item)) {
- item = PyUnicode_AsASCIIString(item);
- if (!item)
- return 0;
- }
- else if (!PyString_Check(item))
- return 0;
-
- name = PyString_AS_STRING(item);
- if (name[0] == ':' && name[1] == ':')
- return 0;
-
- while (current) {
- /* XXX: for 2.4 and newer, use PyDict_Contains */
- if (!PyMapping_HasKey(current->dict, item)) {
- current = current->prev;
- continue;
- }
- return 1;
- }
-
- return 0;
-}
-
-/**
- * Set an value in the highest layer or delete one.
- */
-static int
-BaseContext_setitem(BaseContext *self, PyObject *item, PyObject *value)
-{
- /* allow unicode objects as keys as long as they are ASCII */
- if (PyUnicode_CheckExact(item)) {
- item = PyUnicode_AsASCIIString(item);
- if (!item) {
- PyErr_Clear();
- goto error;
- }
- }
- else if (!PyString_Check(item))
- goto error;
- if (!value)
- return PyDict_DelItem(self->current->dict, item);
- return PyDict_SetItem(self->current->dict, item, value);
-
-error:
- PyErr_SetString(PyExc_TypeError, "expected string argument");
- return -1;
-}
-
-
-static PyGetSetDef BaseContext_getsetters[] = {
- {"stack", (getter)BaseContext_getstack, NULL,
- "a read only copy of the internal stack", NULL},
- {"current", (getter)BaseContext_getcurrent, NULL,
- "reference to the current layer on the stack", NULL},
- {"initial", (getter)BaseContext_getinitial, NULL,
- "reference to the initial layer on the stack", NULL},
- {"globals", (getter)BaseContext_getglobals, NULL,
- "reference to the global layer on the stack", NULL},
- {NULL} /* Sentinel */
-};
-
-static PyMethodDef BaseContext_methods[] = {
- {"pop", (PyCFunction)BaseContext_pop, METH_NOARGS,
- "ctx.pop() -> dict\n\n"
- "Pop the last layer from the stack and return it."},
- {"push", (PyCFunction)BaseContext_push, METH_VARARGS,
- "ctx.push([layer]) -> layer\n\n"
- "Push one layer to the stack. Layer must be a dict "
- "or omitted."},
- {NULL} /* Sentinel */
-};
-
-static PySequenceMethods BaseContext_as_sequence = {
- 0, /* sq_length */
- 0, /* sq_concat */
- 0, /* sq_repeat */
- 0, /* sq_item */
- 0, /* sq_slice */
- 0, /* sq_ass_item */
- 0, /* sq_ass_slice */
- (objobjproc)BaseContext_contains,/* sq_contains */
- 0, /* sq_inplace_concat */
- 0 /* sq_inplace_repeat */
-};
-
-static PyMappingMethods BaseContext_as_mapping = {
- NULL,
- (binaryfunc)BaseContext_getitem,
- (objobjargproc)BaseContext_setitem
-};
-
-static PyTypeObject BaseContextType = {
- PyObject_HEAD_INIT(NULL)
- 0, /* ob_size */
- "jinja._speedups.BaseContext", /* tp_name */
- sizeof(BaseContext), /* tp_basicsize */
- 0, /* tp_itemsize */
- (destructor)BaseContext_dealloc,/* tp_dealloc */
- 0, /* tp_print */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_compare */
- 0, /* tp_repr */
- 0, /* tp_as_number */
- &BaseContext_as_sequence, /* tp_as_sequence */
- &BaseContext_as_mapping, /* tp_as_mapping */
- 0, /* tp_hash */
- 0, /* tp_call */
- 0, /* tp_str */
- 0, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
- /*tp_flags*/
- "", /* tp_doc */
- (traverseproc)BaseContext_traverse, /* tp_traverse */
- (inquiry)BaseContext_clear, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- BaseContext_methods, /* tp_methods */
- 0, /* tp_members */
- BaseContext_getsetters, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- (initproc)BaseContext_init, /* tp_init */
- 0, /* tp_alloc */
- 0 /* tp_new */
-};
-
-static PyMethodDef module_methods[] = {
- {NULL, NULL, 0, NULL} /* Sentinel */
-};
-
-#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
-#define PyMODINIT_FUNC void
-#endif
-PyMODINIT_FUNC
-init_speedups(void)
-{
- PyObject *module;
-
- BaseContextType.tp_new = (newfunc)PyType_GenericNew;
- if (PyType_Ready(&BaseContextType) < 0)
- return;
-
- if (!init_constants())
- return;
-
- module = Py_InitModule3("_speedups", module_methods, "");
- if (!module)
- return;
-
- Py_INCREF(&BaseContextType);
- PyModule_AddObject(module, "BaseContext", (PyObject*)&BaseContextType);
-}
# more optimizations.
self.has_known_extends = False
+ # the current line number
+ self.lineno = 1
+
+ # the debug information
+ self.debug_info = []
+
# the number of new lines before the next write()
self._new_lines = 0
if self._new_lines:
if not self._first_write:
self.stream.write('\n' * self._new_lines)
+ self.lineno += self._new_lines
self._first_write = False
self.stream.write(' ' * self._indentation)
self._new_lines = 0
"""Add one or more newlines before the next write."""
self._new_lines = max(self._new_lines, 1 + extra)
if node is not None and node.lineno != self._last_line:
- self.write('# line: %s' % node.lineno)
- self._new_lines = 1
- self._last_line = node.lineno
+ self.debug_info.append((node.lineno, self.lineno))
def signature(self, node, frame, have_comma=True, extra_kwargs=None):
"""Writes a function call to the stream for the current node.
def visit_Template(self, node, frame=None):
assert frame is None, 'no root frame allowed'
self.writeline('from jinja2.runtime import *')
- self.writeline('filename = %r' % self.filename)
+ self.writeline('name = %r' % self.filename)
# do we have an extends tag at all? If not, we can save some
# overhead by just not processing any inheritance code.
self.blockvisit(block.body, block_frame)
self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
- for x in self.blocks), extra=1)
+ for x in self.blocks),
+ extra=1)
+
+ # add a function that returns the debug info
+ self.writeline('def get_debug_info():', extra=1)
+ self.indent()
+ self.writeline('return %r' % self.debug_info)
def visit_Block(self, node, frame):
"""Call a block and register it for the template."""
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+ jinja2.debug
+ ~~~~~~~~~~~~
+
+ Implements the debug interface for Jinja.
+
+ :copyright: Copyright 2008 by Armin Ronacher.
+ :license: BSD.
+"""
+import re
+import sys
+from jinja2.exceptions import TemplateNotFound
+
+
+_line_re = re.compile(r'^\s*# line: (\d+)\s*$')
+
+
+def fake_exc_info(exc_info, filename, lineno, tb_back=None):
+ exc_type, exc_value, tb = exc_info
+
+ # figure the real context out
+ real_locals = tb.tb_frame.f_locals.copy()
+ locals = dict(real_locals.get('context', {}))
+ for name, value in real_locals.iteritems():
+ if name.startswith('l_'):
+ locals[name[2:]] = value
+
+ # assamble fake globals we need
+ globals = {
+ '__name__': filename,
+ '__file__': filename,
+ '__jinja_exception__': exc_info[:2]
+ }
+
+ # and fake the exception
+ code = compile('\n' * (lineno - 1) + 'raise __jinja_exception__[0], ' +
+ '__jinja_exception__[1]', filename, 'exec')
+ try:
+ exec code in globals, locals
+ except:
+ exc_info = sys.exc_info()
+
+ # now we can patch the exc info accordingly
+ if tb_set_next is not None:
+ if tb_back is not None:
+ tb_set_next(tb_back, exc_info[2])
+ if tb is not None:
+ tb_set_next(exc_info[2].tb_next, tb.tb_next)
+ return exc_info
+
+
+def translate_exception(exc_info):
+ result_tb = prev_tb = None
+ initial_tb = tb = exc_info[2]
+
+ while tb is not None:
+ template = tb.tb_frame.f_globals.get('__jinja_template__')
+ if template is not None:
+ # TODO: inject faked exception with correct line number
+ lineno = template.get_corresponding_lineno(tb.tb_lineno)
+ tb = fake_exc_info(exc_info[:2] + (tb,), template.filename,
+ lineno, prev_tb)[2]
+ if result_tb is None:
+ result_tb = tb
+ prev_tb = tb
+ tb = tb.tb_next
+
+ return exc_info[:2] + (result_tb or initial_tb,)
+
+
+def _init_ugly_crap():
+ """This function implements a few ugly things so that we can patch the
+ traceback objects. The function returned allows resetting `tb_next` on
+ any python traceback object.
+ """
+ import ctypes
+ from types import TracebackType
+
+ # figure out side of _Py_ssize_t
+ if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'):
+ _Py_ssize_t = ctypes.c_int64
+ else:
+ _Py_ssize_t = ctypes.c_int
+
+ # regular python
+ class _PyObject(ctypes.Structure):
+ pass
+ _PyObject._fields_ = [
+ ('ob_refcnt', _Py_ssize_t),
+ ('ob_type', ctypes.POINTER(_PyObject))
+ ]
+
+ # python with trace
+ if object.__basicsize__ != ctypes.sizeof(_PyObject):
+ class _PyObject(ctypes.Structure):
+ pass
+ _PyObject._fields_ = [
+ ('_ob_next', ctypes.POINTER(_PyObject)),
+ ('_ob_prev', ctypes.POINTER(_PyObject)),
+ ('ob_refcnt', _Py_ssize_t),
+ ('ob_type', ctypes.POINTER(_PyObject))
+ ]
+
+ class _Traceback(_PyObject):
+ pass
+ _Traceback._fields_ = [
+ ('tb_next', ctypes.POINTER(_Traceback)),
+ ('tb_frame', ctypes.POINTER(_PyObject)),
+ ('tb_lasti', ctypes.c_int),
+ ('tb_lineno', ctypes.c_int)
+ ]
+
+ def tb_set_next(tb, next):
+ if not (isinstance(tb, TracebackType) and
+ isinstance(next, TracebackType)):
+ raise TypeError('tb_set_next arguments must be traceback objects')
+ obj = _Traceback.from_address(id(tb))
+ obj.tb_next = ctypes.pointer(_Traceback.from_address(id(next)))
+
+ return tb_set_next
+
+
+# no ctypes, no fun
+try:
+ tb_set_next = _init_ugly_crap()
+except:
+ tb_set_next = None
+del _init_ugly_crap
:copyright: 2007 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
+import sys
from jinja2.lexer import Lexer
from jinja2.parser import Parser
from jinja2.optimizer import optimize
from jinja2.compiler import generate
from jinja2.runtime import Undefined
+from jinja2.debug import translate_exception
from jinja2.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
except (TypeError, LookupError):
return self.undefined(obj, argument)
- def parse(self, source, filename=None):
+ def parse(self, source, name=None):
"""Parse the sourcecode and return the abstract syntax tree. This tree
of nodes is used by the compiler to convert the template into
executable source- or bytecode.
"""
- parser = Parser(self, source, filename)
+ parser = Parser(self, source, name)
return parser.parse()
- def lex(self, source, filename=None):
+ def lex(self, source, name=None):
"""Lex the given sourcecode and return a generator that yields tokens.
The stream returned is not usable for Jinja but can be used if
Jinja templates should be processed by other tools (for example
The tuples are returned in the form ``(lineno, token, value)``.
"""
- return self.lexer.tokeniter(source, filename)
+ return self.lexer.tokeniter(source, name)
def compile(self, source, filename=None, raw=False, globals=None):
"""Compile a node or source."""
if self.optimized:
node = optimize(source, self, globals or {})
source = generate(node, self, filename)
- print source
if raw:
return source
if filename is None:
namespace = {'environment': environment}
exec code in namespace
self.environment = environment
- self.name = namespace['filename']
+ self.name = namespace['name']
+ self.filename = code.co_filename
self.root_render_func = namespace['root']
self.blocks = namespace['blocks']
self.globals = globals
+ # debug helpers
+ self._get_debug_info = namespace['get_debug_info']
+ namespace['__jinja_template__'] = self
+
def render(self, *args, **kwargs):
return u''.join(self.generate(*args, **kwargs))
gen = self.root_render_func(dict(self.globals, **context))
# skip the first item which is a reference to the context
gen.next()
- return gen
+
+ try:
+ for event in gen:
+ yield event
+ except:
+ exc_info = translate_exception(sys.exc_info())
+ raise exc_info[0], exc_info[1], exc_info[2]
+
+ def get_corresponding_lineno(self, lineno):
+ """Return the source line number of a line number in the
+ generated bytecode as they are not in sync.
+ """
+ for template_line, code_line in reversed(self._get_debug_info()):
+ if code_line <= lineno:
+ return template_line
+ return 1
def __repr__(self):
return '<%s %r>' % (
def get_source(self, environment, template):
raise TemplateNotFound()
- def load(self, environment, template, globals=None):
- source, filename = self.get_source(environment, template)
+ def load(self, environment, name, globals=None):
+ source, filename = self.get_source(environment, name)
code = environment.compile(source, filename, globals=globals)
return Template(environment, code, globals or {})