added new python only debug hack
--HG--
branch : trunk
diff --git a/examples/cycle.py b/examples/cycle.py
new file mode 100644
index 0000000..73dd632
--- /dev/null
+++ b/examples/cycle.py
@@ -0,0 +1,13 @@
+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()
diff --git a/examples/translate.py b/examples/translate.py
index d5f792f..9043a21 100644
--- a/examples/translate.py
+++ b/examples/translate.py
@@ -1,6 +1,7 @@
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")
diff --git a/jinja2/_debugger.c b/jinja2/_debugger.c
deleted file mode 100644
index 50462e1..0000000
--- a/jinja2/_debugger.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * 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;
-}
diff --git a/jinja2/_native.py b/jinja2/_native.py
deleted file mode 100644
index 1d9747a..0000000
--- a/jinja2/_native.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# -*- 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
diff --git a/jinja2/_speedups.c b/jinja2/_speedups.c
deleted file mode 100644
index 198b7a0..0000000
--- a/jinja2/_speedups.c
+++ /dev/null
@@ -1,499 +0,0 @@
-/**
- * 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);
-}
diff --git a/jinja2/compiler.py b/jinja2/compiler.py
index abaf861..06017f8 100644
--- a/jinja2/compiler.py
+++ b/jinja2/compiler.py
@@ -247,6 +247,12 @@
# 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
@@ -300,6 +306,7 @@
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
@@ -314,9 +321,7 @@
"""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.
@@ -439,7 +444,7 @@
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.
@@ -499,7 +504,13 @@
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."""
diff --git a/jinja2/debug.py b/jinja2/debug.py
new file mode 100644
index 0000000..909a852
--- /dev/null
+++ b/jinja2/debug.py
@@ -0,0 +1,129 @@
+# -*- 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
diff --git a/jinja2/environment.py b/jinja2/environment.py
index 60a7a2b..47d99e3 100644
--- a/jinja2/environment.py
+++ b/jinja2/environment.py
@@ -8,11 +8,13 @@
: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
@@ -114,15 +116,15 @@
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
@@ -130,7 +132,7 @@
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."""
@@ -139,7 +141,6 @@
if self.optimized:
node = optimize(source, self, globals or {})
source = generate(node, self, filename)
- print source
if raw:
return source
if filename is None:
@@ -183,11 +184,16 @@
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))
@@ -214,7 +220,22 @@
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>' % (
diff --git a/jinja2/loaders.py b/jinja2/loaders.py
index 4d1c335..6c03ad3 100644
--- a/jinja2/loaders.py
+++ b/jinja2/loaders.py
@@ -19,8 +19,8 @@
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 {})