parent
57505b9af9
commit
9726346bc9
10 changed files with 0 additions and 2040 deletions
@ -1,78 +0,0 @@ |
||||
/**
|
||||
* jinja2._debugsupport |
||||
* ~~~~~~~~~~~~~~~~~~~~ |
||||
* |
||||
* C implementation of `tb_set_next`. |
||||
* |
||||
* :copyright: (c) 2010 by the Jinja Team. |
||||
* :license: BSD. |
||||
*/ |
||||
|
||||
#include <Python.h> |
||||
|
||||
|
||||
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 */ |
||||
}; |
||||
|
||||
|
||||
#if PY_MAJOR_VERSION < 3 |
||||
|
||||
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ |
||||
#define PyMODINIT_FUNC void |
||||
#endif |
||||
PyMODINIT_FUNC |
||||
init_debugsupport(void) |
||||
{ |
||||
Py_InitModule3("jinja2._debugsupport", module_methods, ""); |
||||
} |
||||
|
||||
#else /* Python 3.x module initialization */ |
||||
|
||||
static struct PyModuleDef module_definition = { |
||||
PyModuleDef_HEAD_INIT, |
||||
"jinja2._debugsupport", |
||||
NULL, |
||||
-1, |
||||
module_methods, |
||||
NULL, |
||||
NULL, |
||||
NULL, |
||||
NULL |
||||
}; |
||||
|
||||
PyMODINIT_FUNC |
||||
PyInit__debugsupport(void) |
||||
{ |
||||
return PyModule_Create(&module_definition); |
||||
} |
||||
|
||||
#endif |
@ -1,267 +0,0 @@ |
||||
# -*- coding: utf-8 -*- |
||||
""" |
||||
markupsafe._constants |
||||
~~~~~~~~~~~~~~~~~~~~~ |
||||
|
||||
Highlevel implementation of the Markup string. |
||||
|
||||
:copyright: (c) 2010 by Armin Ronacher. |
||||
:license: BSD, see LICENSE for more details. |
||||
""" |
||||
|
||||
|
||||
HTML_ENTITIES = { |
||||
'AElig': 198, |
||||
'Aacute': 193, |
||||
'Acirc': 194, |
||||
'Agrave': 192, |
||||
'Alpha': 913, |
||||
'Aring': 197, |
||||
'Atilde': 195, |
||||
'Auml': 196, |
||||
'Beta': 914, |
||||
'Ccedil': 199, |
||||
'Chi': 935, |
||||
'Dagger': 8225, |
||||
'Delta': 916, |
||||
'ETH': 208, |
||||
'Eacute': 201, |
||||
'Ecirc': 202, |
||||
'Egrave': 200, |
||||
'Epsilon': 917, |
||||
'Eta': 919, |
||||
'Euml': 203, |
||||
'Gamma': 915, |
||||
'Iacute': 205, |
||||
'Icirc': 206, |
||||
'Igrave': 204, |
||||
'Iota': 921, |
||||
'Iuml': 207, |
||||
'Kappa': 922, |
||||
'Lambda': 923, |
||||
'Mu': 924, |
||||
'Ntilde': 209, |
||||
'Nu': 925, |
||||
'OElig': 338, |
||||
'Oacute': 211, |
||||
'Ocirc': 212, |
||||
'Ograve': 210, |
||||
'Omega': 937, |
||||
'Omicron': 927, |
||||
'Oslash': 216, |
||||
'Otilde': 213, |
||||
'Ouml': 214, |
||||
'Phi': 934, |
||||
'Pi': 928, |
||||
'Prime': 8243, |
||||
'Psi': 936, |
||||
'Rho': 929, |
||||
'Scaron': 352, |
||||
'Sigma': 931, |
||||
'THORN': 222, |
||||
'Tau': 932, |
||||
'Theta': 920, |
||||
'Uacute': 218, |
||||
'Ucirc': 219, |
||||
'Ugrave': 217, |
||||
'Upsilon': 933, |
||||
'Uuml': 220, |
||||
'Xi': 926, |
||||
'Yacute': 221, |
||||
'Yuml': 376, |
||||
'Zeta': 918, |
||||
'aacute': 225, |
||||
'acirc': 226, |
||||
'acute': 180, |
||||
'aelig': 230, |
||||
'agrave': 224, |
||||
'alefsym': 8501, |
||||
'alpha': 945, |
||||
'amp': 38, |
||||
'and': 8743, |
||||
'ang': 8736, |
||||
'apos': 39, |
||||
'aring': 229, |
||||
'asymp': 8776, |
||||
'atilde': 227, |
||||
'auml': 228, |
||||
'bdquo': 8222, |
||||
'beta': 946, |
||||
'brvbar': 166, |
||||
'bull': 8226, |
||||
'cap': 8745, |
||||
'ccedil': 231, |
||||
'cedil': 184, |
||||
'cent': 162, |
||||
'chi': 967, |
||||
'circ': 710, |
||||
'clubs': 9827, |
||||
'cong': 8773, |
||||
'copy': 169, |
||||
'crarr': 8629, |
||||
'cup': 8746, |
||||
'curren': 164, |
||||
'dArr': 8659, |
||||
'dagger': 8224, |
||||
'darr': 8595, |
||||
'deg': 176, |
||||
'delta': 948, |
||||
'diams': 9830, |
||||
'divide': 247, |
||||
'eacute': 233, |
||||
'ecirc': 234, |
||||
'egrave': 232, |
||||
'empty': 8709, |
||||
'emsp': 8195, |
||||
'ensp': 8194, |
||||
'epsilon': 949, |
||||
'equiv': 8801, |
||||
'eta': 951, |
||||
'eth': 240, |
||||
'euml': 235, |
||||
'euro': 8364, |
||||
'exist': 8707, |
||||
'fnof': 402, |
||||
'forall': 8704, |
||||
'frac12': 189, |
||||
'frac14': 188, |
||||
'frac34': 190, |
||||
'frasl': 8260, |
||||
'gamma': 947, |
||||
'ge': 8805, |
||||
'gt': 62, |
||||
'hArr': 8660, |
||||
'harr': 8596, |
||||
'hearts': 9829, |
||||
'hellip': 8230, |
||||
'iacute': 237, |
||||
'icirc': 238, |
||||
'iexcl': 161, |
||||
'igrave': 236, |
||||
'image': 8465, |
||||
'infin': 8734, |
||||
'int': 8747, |
||||
'iota': 953, |
||||
'iquest': 191, |
||||
'isin': 8712, |
||||
'iuml': 239, |
||||
'kappa': 954, |
||||
'lArr': 8656, |
||||
'lambda': 955, |
||||
'lang': 9001, |
||||
'laquo': 171, |
||||
'larr': 8592, |
||||
'lceil': 8968, |
||||
'ldquo': 8220, |
||||
'le': 8804, |
||||
'lfloor': 8970, |
||||
'lowast': 8727, |
||||
'loz': 9674, |
||||
'lrm': 8206, |
||||
'lsaquo': 8249, |
||||
'lsquo': 8216, |
||||
'lt': 60, |
||||
'macr': 175, |
||||
'mdash': 8212, |
||||
'micro': 181, |
||||
'middot': 183, |
||||
'minus': 8722, |
||||
'mu': 956, |
||||
'nabla': 8711, |
||||
'nbsp': 160, |
||||
'ndash': 8211, |
||||
'ne': 8800, |
||||
'ni': 8715, |
||||
'not': 172, |
||||
'notin': 8713, |
||||
'nsub': 8836, |
||||
'ntilde': 241, |
||||
'nu': 957, |
||||
'oacute': 243, |
||||
'ocirc': 244, |
||||
'oelig': 339, |
||||
'ograve': 242, |
||||
'oline': 8254, |
||||
'omega': 969, |
||||
'omicron': 959, |
||||
'oplus': 8853, |
||||
'or': 8744, |
||||
'ordf': 170, |
||||
'ordm': 186, |
||||
'oslash': 248, |
||||
'otilde': 245, |
||||
'otimes': 8855, |
||||
'ouml': 246, |
||||
'para': 182, |
||||
'part': 8706, |
||||
'permil': 8240, |
||||
'perp': 8869, |
||||
'phi': 966, |
||||
'pi': 960, |
||||
'piv': 982, |
||||
'plusmn': 177, |
||||
'pound': 163, |
||||
'prime': 8242, |
||||
'prod': 8719, |
||||
'prop': 8733, |
||||
'psi': 968, |
||||
'quot': 34, |
||||
'rArr': 8658, |
||||
'radic': 8730, |
||||
'rang': 9002, |
||||
'raquo': 187, |
||||
'rarr': 8594, |
||||
'rceil': 8969, |
||||
'rdquo': 8221, |
||||
'real': 8476, |
||||
'reg': 174, |
||||
'rfloor': 8971, |
||||
'rho': 961, |
||||
'rlm': 8207, |
||||
'rsaquo': 8250, |
||||
'rsquo': 8217, |
||||
'sbquo': 8218, |
||||
'scaron': 353, |
||||
'sdot': 8901, |
||||
'sect': 167, |
||||
'shy': 173, |
||||
'sigma': 963, |
||||
'sigmaf': 962, |
||||
'sim': 8764, |
||||
'spades': 9824, |
||||
'sub': 8834, |
||||
'sube': 8838, |
||||
'sum': 8721, |
||||
'sup': 8835, |
||||
'sup1': 185, |
||||
'sup2': 178, |
||||
'sup3': 179, |
||||
'supe': 8839, |
||||
'szlig': 223, |
||||
'tau': 964, |
||||
'there4': 8756, |
||||
'theta': 952, |
||||
'thetasym': 977, |
||||
'thinsp': 8201, |
||||
'thorn': 254, |
||||
'tilde': 732, |
||||
'times': 215, |
||||
'trade': 8482, |
||||
'uArr': 8657, |
||||
'uacute': 250, |
||||
'uarr': 8593, |
||||
'ucirc': 251, |
||||
'ugrave': 249, |
||||
'uml': 168, |
||||
'upsih': 978, |
||||
'upsilon': 965, |
||||
'uuml': 252, |
||||
'weierp': 8472, |
||||
'xi': 958, |
||||
'yacute': 253, |
||||
'yen': 165, |
||||
'yuml': 255, |
||||
'zeta': 950, |
||||
'zwj': 8205, |
||||
'zwnj': 8204 |
||||
} |
@ -1,80 +0,0 @@ |
||||
import gc |
||||
import unittest |
||||
from jinja2._markupsafe import Markup, escape, escape_silent |
||||
|
||||
|
||||
class MarkupTestCase(unittest.TestCase): |
||||
|
||||
def test_markup_operations(self): |
||||
# adding two strings should escape the unsafe one |
||||
unsafe = '<script type="application/x-some-script">alert("foo");</script>' |
||||
safe = Markup('<em>username</em>') |
||||
assert unsafe + safe == unicode(escape(unsafe)) + unicode(safe) |
||||
|
||||
# string interpolations are safe to use too |
||||
assert Markup('<em>%s</em>') % '<bad user>' == \ |
||||
'<em><bad user></em>' |
||||
assert Markup('<em>%(username)s</em>') % { |
||||
'username': '<bad user>' |
||||
} == '<em><bad user></em>' |
||||
|
||||
# an escaped object is markup too |
||||
assert type(Markup('foo') + 'bar') is Markup |
||||
|
||||
# and it implements __html__ by returning itself |
||||
x = Markup("foo") |
||||
assert x.__html__() is x |
||||
|
||||
# it also knows how to treat __html__ objects |
||||
class Foo(object): |
||||
def __html__(self): |
||||
return '<em>awesome</em>' |
||||
def __unicode__(self): |
||||
return 'awesome' |
||||
assert Markup(Foo()) == '<em>awesome</em>' |
||||
assert Markup('<strong>%s</strong>') % Foo() == \ |
||||
'<strong><em>awesome</em></strong>' |
||||
|
||||
# escaping and unescaping |
||||
assert escape('"<>&\'') == '"<>&'' |
||||
assert Markup("<em>Foo & Bar</em>").striptags() == "Foo & Bar" |
||||
assert Markup("<test>").unescape() == "<test>" |
||||
|
||||
def test_all_set(self): |
||||
import jinja2._markupsafe as markup |
||||
for item in markup.__all__: |
||||
getattr(markup, item) |
||||
|
||||
def test_escape_silent(self): |
||||
assert escape_silent(None) == Markup() |
||||
assert escape(None) == Markup(None) |
||||
assert escape_silent('<foo>') == Markup(u'<foo>') |
||||
|
||||
|
||||
class MarkupLeakTestCase(unittest.TestCase): |
||||
|
||||
def test_markup_leaks(self): |
||||
counts = set() |
||||
for count in xrange(20): |
||||
for item in xrange(1000): |
||||
escape("foo") |
||||
escape("<foo>") |
||||
escape(u"foo") |
||||
escape(u"<foo>") |
||||
counts.add(len(gc.get_objects())) |
||||
assert len(counts) == 1, 'ouch, c extension seems to leak objects' |
||||
|
||||
|
||||
def suite(): |
||||
suite = unittest.TestSuite() |
||||
suite.addTest(unittest.makeSuite(MarkupTestCase)) |
||||
|
||||
# this test only tests the c extension |
||||
if not hasattr(escape, 'func_code'): |
||||
suite.addTest(unittest.makeSuite(MarkupLeakTestCase)) |
||||
|
||||
return suite |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main(defaultTest='suite') |
File diff suppressed because one or more lines are too long
@ -1,32 +0,0 @@ |
||||
# -*- coding: utf-8 -*- |
||||
""" |
||||
jinja.constants |
||||
~~~~~~~~~~~~~~~ |
||||
|
||||
Various constants. |
||||
|
||||
:copyright: (c) 2010 by the Jinja Team. |
||||
:license: BSD, see LICENSE for more details. |
||||
""" |
||||
|
||||
|
||||
#: list of lorem ipsum words used by the lipsum() helper function |
||||
LOREM_IPSUM_WORDS = u'''\ |
||||
a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at |
||||
auctor augue bibendum blandit class commodo condimentum congue consectetuer |
||||
consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus |
||||
diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend |
||||
elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames |
||||
faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac |
||||
hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum |
||||
justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem |
||||
luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie |
||||
mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non |
||||
nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque |
||||
penatibus per pharetra phasellus placerat platea porta porttitor posuere |
||||
potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus |
||||
ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit |
||||
sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor |
||||
tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices |
||||
ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus |
||||
viverra volutpat vulputate''' |
@ -1,339 +0,0 @@ |
||||
# -*- coding: utf-8 -*- |
||||
""" |
||||
jinja2.debug |
||||
~~~~~~~~~~~~ |
||||
|
||||
Implements the debug interface for Jinja. This module does some pretty |
||||
ugly stuff with the Python traceback system in order to achieve tracebacks |
||||
with correct line numbers, locals and contents. |
||||
|
||||
:copyright: (c) 2010 by the Jinja Team. |
||||
:license: BSD, see LICENSE for more details. |
||||
""" |
||||
import sys |
||||
import traceback |
||||
from types import TracebackType |
||||
from jinja2.utils import CodeType, missing, internal_code |
||||
from jinja2.exceptions import TemplateSyntaxError |
||||
|
||||
# on pypy we can take advantage of transparent proxies |
||||
try: |
||||
from __pypy__ import tproxy |
||||
except ImportError: |
||||
tproxy = None |
||||
|
||||
|
||||
# how does the raise helper look like? |
||||
try: |
||||
exec "raise TypeError, 'foo'" |
||||
except SyntaxError: |
||||
raise_helper = 'raise __jinja_exception__[1]' |
||||
except TypeError: |
||||
raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]' |
||||
|
||||
|
||||
class TracebackFrameProxy(object): |
||||
"""Proxies a traceback frame.""" |
||||
|
||||
def __init__(self, tb): |
||||
self.tb = tb |
||||
self._tb_next = None |
||||
|
||||
@property |
||||
def tb_next(self): |
||||
return self._tb_next |
||||
|
||||
def set_next(self, next): |
||||
if tb_set_next is not None: |
||||
try: |
||||
tb_set_next(self.tb, next and next.tb or None) |
||||
except Exception: |
||||
# this function can fail due to all the hackery it does |
||||
# on various python implementations. We just catch errors |
||||
# down and ignore them if necessary. |
||||
pass |
||||
self._tb_next = next |
||||
|
||||
@property |
||||
def is_jinja_frame(self): |
||||
return '__jinja_template__' in self.tb.tb_frame.f_globals |
||||
|
||||
def __getattr__(self, name): |
||||
return getattr(self.tb, name) |
||||
|
||||
|
||||
def make_frame_proxy(frame): |
||||
proxy = TracebackFrameProxy(frame) |
||||
if tproxy is None: |
||||
return proxy |
||||
def operation_handler(operation, *args, **kwargs): |
||||
if operation in ('__getattribute__', '__getattr__'): |
||||
return getattr(proxy, args[0]) |
||||
elif operation == '__setattr__': |
||||
proxy.__setattr__(*args, **kwargs) |
||||
else: |
||||
return getattr(proxy, operation)(*args, **kwargs) |
||||
return tproxy(TracebackType, operation_handler) |
||||
|
||||
|
||||
class ProcessedTraceback(object): |
||||
"""Holds a Jinja preprocessed traceback for printing or reraising.""" |
||||
|
||||
def __init__(self, exc_type, exc_value, frames): |
||||
assert frames, 'no frames for this traceback?' |
||||
self.exc_type = exc_type |
||||
self.exc_value = exc_value |
||||
self.frames = frames |
||||
|
||||
# newly concatenate the frames (which are proxies) |
||||
prev_tb = None |
||||
for tb in self.frames: |
||||
if prev_tb is not None: |
||||
prev_tb.set_next(tb) |
||||
prev_tb = tb |
||||
prev_tb.set_next(None) |
||||
|
||||
def render_as_text(self, limit=None): |
||||
"""Return a string with the traceback.""" |
||||
lines = traceback.format_exception(self.exc_type, self.exc_value, |
||||
self.frames[0], limit=limit) |
||||
return ''.join(lines).rstrip() |
||||
|
||||
def render_as_html(self, full=False): |
||||
"""Return a unicode string with the traceback as rendered HTML.""" |
||||
from jinja2.debugrenderer import render_traceback |
||||
return u'%s\n\n<!--\n%s\n-->' % ( |
||||
render_traceback(self, full=full), |
||||
self.render_as_text().decode('utf-8', 'replace') |
||||
) |
||||
|
||||
@property |
||||
def is_template_syntax_error(self): |
||||
"""`True` if this is a template syntax error.""" |
||||
return isinstance(self.exc_value, TemplateSyntaxError) |
||||
|
||||
@property |
||||
def exc_info(self): |
||||
"""Exception info tuple with a proxy around the frame objects.""" |
||||
return self.exc_type, self.exc_value, self.frames[0] |
||||
|
||||
@property |
||||
def standard_exc_info(self): |
||||
"""Standard python exc_info for re-raising""" |
||||
tb = self.frames[0] |
||||
# the frame will be an actual traceback (or transparent proxy) if |
||||
# we are on pypy or a python implementation with support for tproxy |
||||
if type(tb) is not TracebackType: |
||||
tb = tb.tb |
||||
return self.exc_type, self.exc_value, tb |
||||
|
||||
|
||||
def make_traceback(exc_info, source_hint=None): |
||||
"""Creates a processed traceback object from the exc_info.""" |
||||
exc_type, exc_value, tb = exc_info |
||||
if isinstance(exc_value, TemplateSyntaxError): |
||||
exc_info = translate_syntax_error(exc_value, source_hint) |
||||
initial_skip = 0 |
||||
else: |
||||
initial_skip = 1 |
||||
return translate_exception(exc_info, initial_skip) |
||||
|
||||
|
||||
def translate_syntax_error(error, source=None): |
||||
"""Rewrites a syntax error to please traceback systems.""" |
||||
error.source = source |
||||
error.translated = True |
||||
exc_info = (error.__class__, error, None) |
||||
filename = error.filename |
||||
if filename is None: |
||||
filename = '<unknown>' |
||||
return fake_exc_info(exc_info, filename, error.lineno) |
||||
|
||||
|
||||
def translate_exception(exc_info, initial_skip=0): |
||||
"""If passed an exc_info it will automatically rewrite the exceptions |
||||
all the way down to the correct line numbers and frames. |
||||
""" |
||||
tb = exc_info[2] |
||||
frames = [] |
||||
|
||||
# skip some internal frames if wanted |
||||
for x in xrange(initial_skip): |
||||
if tb is not None: |
||||
tb = tb.tb_next |
||||
initial_tb = tb |
||||
|
||||
while tb is not None: |
||||
# skip frames decorated with @internalcode. These are internal |
||||
# calls we can't avoid and that are useless in template debugging |
||||
# output. |
||||
if tb.tb_frame.f_code in internal_code: |
||||
tb = tb.tb_next |
||||
continue |
||||
|
||||
# save a reference to the next frame if we override the current |
||||
# one with a faked one. |
||||
next = tb.tb_next |
||||
|
||||
# fake template exceptions |
||||
template = tb.tb_frame.f_globals.get('__jinja_template__') |
||||
if template is not None: |
||||
lineno = template.get_corresponding_lineno(tb.tb_lineno) |
||||
tb = fake_exc_info(exc_info[:2] + (tb,), template.filename, |
||||
lineno)[2] |
||||
|
||||
frames.append(make_frame_proxy(tb)) |
||||
tb = next |
||||
|
||||
# if we don't have any exceptions in the frames left, we have to |
||||
# reraise it unchanged. |
||||
# XXX: can we backup here? when could this happen? |
||||
if not frames: |
||||
raise exc_info[0], exc_info[1], exc_info[2] |
||||
|
||||
return ProcessedTraceback(exc_info[0], exc_info[1], frames) |
||||
|
||||
|
||||
def fake_exc_info(exc_info, filename, lineno): |
||||
"""Helper for `translate_exception`.""" |
||||
exc_type, exc_value, tb = exc_info |
||||
|
||||
# figure the real context out |
||||
if tb is not None: |
||||
real_locals = tb.tb_frame.f_locals.copy() |
||||
ctx = real_locals.get('context') |
||||
if ctx: |
||||
locals = ctx.get_all() |
||||
else: |
||||
locals = {} |
||||
for name, value in real_locals.iteritems(): |
||||
if name.startswith('l_') and value is not missing: |
||||
locals[name[2:]] = value |
||||
|
||||
# if there is a local called __jinja_exception__, we get |
||||
# rid of it to not break the debug functionality. |
||||
locals.pop('__jinja_exception__', None) |
||||
else: |
||||
locals = {} |
||||
|
||||
# assamble fake globals we need |
||||
globals = { |
||||
'__name__': filename, |
||||
'__file__': filename, |
||||
'__jinja_exception__': exc_info[:2], |
||||
|
||||
# we don't want to keep the reference to the template around |
||||
# to not cause circular dependencies, but we mark it as Jinja |
||||
# frame for the ProcessedTraceback |
||||
'__jinja_template__': None |
||||
} |
||||
|
||||
# and fake the exception |
||||
code = compile('\n' * (lineno - 1) + raise_helper, filename, 'exec') |
||||
|
||||
# if it's possible, change the name of the code. This won't work |
||||
# on some python environments such as google appengine |
||||
try: |
||||
if tb is None: |
||||
location = 'template' |
||||
else: |
||||
function = tb.tb_frame.f_code.co_name |
||||
if function == 'root': |
||||
location = 'top-level template code' |
||||
elif function.startswith('block_'): |
||||
location = 'block "%s"' % function[6:] |
||||
else: |
||||
location = 'template' |
||||
code = CodeType(0, code.co_nlocals, code.co_stacksize, |
||||
code.co_flags, code.co_code, code.co_consts, |
||||
code.co_names, code.co_varnames, filename, |
||||
location, code.co_firstlineno, |
||||
code.co_lnotab, (), ()) |
||||
except: |
||||
pass |
||||
|
||||
# execute the code and catch the new traceback |
||||
try: |
||||
exec code in globals, locals |
||||
except: |
||||
exc_info = sys.exc_info() |
||||
new_tb = exc_info[2].tb_next |
||||
|
||||
# return without this frame |
||||
return exc_info[:2] + (new_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. Do not attempt to use this on non cpython |
||||
interpreters |
||||
""" |
||||
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 hasattr(sys, 'getobjects'): |
||||
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): |
||||
"""Set the tb_next attribute of a traceback object.""" |
||||
if not (isinstance(tb, TracebackType) and |
||||
(next is None or isinstance(next, TracebackType))): |
||||
raise TypeError('tb_set_next arguments must be traceback objects') |
||||
obj = _Traceback.from_address(id(tb)) |
||||
if tb.tb_next is not None: |
||||
old = _Traceback.from_address(id(tb.tb_next)) |
||||
old.ob_refcnt -= 1 |
||||
if next is None: |
||||
obj.tb_next = ctypes.POINTER(_Traceback)() |
||||
else: |
||||
next = _Traceback.from_address(id(next)) |
||||
next.ob_refcnt += 1 |
||||
obj.tb_next = ctypes.pointer(next) |
||||
|
||||
return tb_set_next |
||||
|
||||
|
||||
# try to get a tb_set_next implementation if we don't have transparent |
||||
# proxies. |
||||
tb_set_next = None |
||||
if tproxy is None: |
||||
try: |
||||
from jinja2._debugsupport import tb_set_next |
||||
except ImportError: |
||||
try: |
||||
tb_set_next = _init_ugly_crap() |
||||
except: |
||||
pass |
||||
del _init_ugly_crap |
@ -1,620 +0,0 @@ |
||||
# -*- coding: utf-8 -*- |
||||
""" |
||||
jinja2.ext |
||||
~~~~~~~~~~ |
||||
|
||||
Jinja extensions allow to add custom tags similar to the way django custom |
||||
tags work. By default two example extensions exist: an i18n and a cache |
||||
extension. |
||||
|
||||
:copyright: (c) 2010 by the Jinja Team. |
||||
:license: BSD. |
||||
""" |
||||
from collections import deque |
||||
from jinja2 import nodes |
||||
from jinja2.defaults import * |
||||
from jinja2.environment import Environment |
||||
from jinja2.runtime import Undefined, concat |
||||
from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError |
||||
from jinja2.utils import contextfunction, import_string, Markup, next |
||||
|
||||
|
||||
# the only real useful gettext functions for a Jinja template. Note |
||||
# that ugettext must be assigned to gettext as Jinja doesn't support |
||||
# non unicode strings. |
||||
GETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext') |
||||
|
||||
|
||||
class ExtensionRegistry(type): |
||||
"""Gives the extension an unique identifier.""" |
||||
|
||||
def __new__(cls, name, bases, d): |
||||
rv = type.__new__(cls, name, bases, d) |
||||
rv.identifier = rv.__module__ + '.' + rv.__name__ |
||||
return rv |
||||
|
||||
|
||||
class Extension(object): |
||||
"""Extensions can be used to add extra functionality to the Jinja template |
||||
system at the parser level. Custom extensions are bound to an environment |
||||
but may not store environment specific data on `self`. The reason for |
||||
this is that an extension can be bound to another environment (for |
||||
overlays) by creating a copy and reassigning the `environment` attribute. |
||||
|
||||
As extensions are created by the environment they cannot accept any |
||||
arguments for configuration. One may want to work around that by using |
||||
a factory function, but that is not possible as extensions are identified |
||||
by their import name. The correct way to configure the extension is |
||||
storing the configuration values on the environment. Because this way the |
||||
environment ends up acting as central configuration storage the |
||||
attributes may clash which is why extensions have to ensure that the names |
||||
they choose for configuration are not too generic. ``prefix`` for example |
||||
is a terrible name, ``fragment_cache_prefix`` on the other hand is a good |
||||
name as includes the name of the extension (fragment cache). |
||||
""" |
||||
__metaclass__ = ExtensionRegistry |
||||
|
||||
#: if this extension parses this is the list of tags it's listening to. |
||||
tags = set() |
||||
|
||||
#: the priority of that extension. This is especially useful for |
||||
#: extensions that preprocess values. A lower value means higher |
||||
#: priority. |
||||
#: |
||||
#: .. versionadded:: 2.4 |
||||
priority = 100 |
||||
|
||||
def __init__(self, environment): |
||||
self.environment = environment |
||||
|
||||
def bind(self, environment): |
||||
"""Create a copy of this extension bound to another environment.""" |
||||
rv = object.__new__(self.__class__) |
||||
rv.__dict__.update(self.__dict__) |
||||
rv.environment = environment |
||||
return rv |
||||
|
||||
def preprocess(self, source, name, filename=None): |
||||
"""This method is called before the actual lexing and can be used to |
||||
preprocess the source. The `filename` is optional. The return value |
||||
must be the preprocessed source. |
||||
""" |
||||
return source |
||||
|
||||
def filter_stream(self, stream): |
||||
"""It's passed a :class:`~jinja2.lexer.TokenStream` that can be used |
||||
to filter tokens returned. This method has to return an iterable of |
||||
:class:`~jinja2.lexer.Token`\s, but it doesn't have to return a |
||||
:class:`~jinja2.lexer.TokenStream`. |
||||
|
||||
In the `ext` folder of the Jinja2 source distribution there is a file |
||||
called `inlinegettext.py` which implements a filter that utilizes this |
||||
method. |
||||
""" |
||||
return stream |
||||
|
||||
def parse(self, parser): |
||||
"""If any of the :attr:`tags` matched this method is called with the |
||||
parser as first argument. The token the parser stream is pointing at |
||||
is the name token that matched. This method has to return one or a |
||||
list of multiple nodes. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
def attr(self, name, lineno=None): |
||||
"""Return an attribute node for the current extension. This is useful |
||||
to pass constants on extensions to generated template code. |
||||
|
||||
:: |
||||
|
||||
self.attr('_my_attribute', lineno=lineno) |
||||
""" |
||||
return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno) |
||||
|
||||
def call_method(self, name, args=None, kwargs=None, dyn_args=None, |
||||
dyn_kwargs=None, lineno=None): |
||||
"""Call a method of the extension. This is a shortcut for |
||||
:meth:`attr` + :class:`jinja2.nodes.Call`. |
||||
""" |
||||
if args is None: |
||||
args = [] |
||||
if kwargs is None: |
||||
kwargs = [] |
||||
return nodes.Call(self.attr(name, lineno=lineno), args, kwargs, |
||||
dyn_args, dyn_kwargs, lineno=lineno) |
||||
|
||||
|
||||
@contextfunction |
||||
def _gettext_alias(__context, *args, **kwargs): |
||||
return __context.call(__context.resolve('gettext'), *args, **kwargs) |
||||
|
||||
|
||||
def _make_new_gettext(func): |
||||
@contextfunction |
||||
def gettext(__context, __string, **variables): |
||||
rv = __context.call(func, __string) |
||||
if __context.eval_ctx.autoescape: |
||||
rv = Markup(rv) |
||||
return rv % variables |
||||
return gettext |
||||
|
||||
|
||||
def _make_new_ngettext(func): |
||||
@contextfunction |
||||
def ngettext(__context, __singular, __plural, __num, **variables): |
||||
variables.setdefault('num', __num) |
||||
rv = __context.call(func, __singular, __plural, __num) |
||||
if __context.eval_ctx.autoescape: |
||||
rv = Markup(rv) |
||||
return rv % variables |
||||
return ngettext |
||||
|
||||
|
||||
class InternationalizationExtension(Extension): |
||||
"""This extension adds gettext support to Jinja2.""" |
||||
tags = set(['trans']) |
||||
|
||||
# TODO: the i18n extension is currently reevaluating values in a few |
||||
# situations. Take this example: |
||||
# {% trans count=something() %}{{ count }} foo{% pluralize |
||||
# %}{{ count }} fooss{% endtrans %} |
||||
# something is called twice here. One time for the gettext value and |
||||
# the other time for the n-parameter of the ngettext function. |
||||
|
||||
def __init__(self, environment): |
||||
Extension.__init__(self, environment) |
||||
environment.globals['_'] = _gettext_alias |
||||
environment.extend( |
||||
install_gettext_translations=self._install, |
||||
install_null_translations=self._install_null, |
||||
install_gettext_callables=self._install_callables, |
||||
uninstall_gettext_translations=self._uninstall, |
||||
extract_translations=self._extract, |
||||
newstyle_gettext=False |
||||
) |
||||
|
||||
def _install(self, translations, newstyle=None): |
||||
gettext = getattr(translations, 'ugettext', None) |
||||
if gettext is None: |
||||
gettext = translations.gettext |
||||
ngettext = getattr(translations, 'ungettext', None) |
||||
if ngettext is None: |
||||
ngettext = translations.ngettext |
||||
self._install_callables(gettext, ngettext, newstyle) |
||||
|
||||
def _install_null(self, newstyle=None): |
||||
self._install_callables( |
||||
lambda x: x, |
||||
lambda s, p, n: (n != 1 and (p,) or (s,))[0], |
||||
newstyle |
||||
) |
||||
|
||||
def _install_callables(self, gettext, ngettext, newstyle=None): |
||||
if newstyle is not None: |
||||
self.environment.newstyle_gettext = newstyle |
||||
if self.environment.newstyle_gettext: |
||||
gettext = _make_new_gettext(gettext) |
||||
ngettext = _make_new_ngettext(ngettext) |
||||
self.environment.globals.update( |
||||
gettext=gettext, |
||||
ngettext=ngettext |
||||
) |
||||
|
||||
def _uninstall(self, translations): |
||||
for key in 'gettext', 'ngettext': |
||||
self.environment.globals.pop(key, None) |
||||
|
||||
def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS): |
||||
if isinstance(source, basestring): |
||||
source = self.environment.parse(source) |
||||
return extract_from_ast(source, gettext_functions) |
||||
|
||||
def parse(self, parser): |
||||
"""Parse a translatable tag.""" |
||||
lineno = next(parser.stream).lineno |
||||
num_called_num = False |
||||
|
||||
# find all the variables referenced. Additionally a variable can be |
||||
# defined in the body of the trans block too, but this is checked at |
||||
# a later state. |
||||
plural_expr = None |
||||
variables = {} |
||||
while parser.stream.current.type != 'block_end': |
||||
if variables: |
||||
parser.stream.expect('comma') |
||||
|
||||
# skip colon for python compatibility |
||||
if parser.stream.skip_if('colon'): |
||||
break |
||||
|
||||
name = parser.stream.expect('name') |
||||
if name.value in variables: |
||||
parser.fail('translatable variable %r defined twice.' % |
||||
name.value, name.lineno, |
||||
exc=TemplateAssertionError) |
||||
|
||||
# expressions |
||||
if parser.stream.current.type == 'assign': |
||||
next(parser.stream) |
||||
variables[name.value] = var = parser.parse_expression() |
||||
else: |
||||
variables[name.value] = var = nodes.Name(name.value, 'load') |
||||
|
||||
if plural_expr is None: |
||||
plural_expr = var |
||||
num_called_num = name.value == 'num' |
||||
|
||||
parser.stream.expect('block_end') |
||||
|
||||
plural = plural_names = None |
||||
have_plural = False |
||||
referenced = set() |
||||
|
||||
# now parse until endtrans or pluralize |
||||
singular_names, singular = self._parse_block(parser, True) |
||||
if singular_names: |
||||
referenced.update(singular_names) |
||||
if plural_expr is None: |
||||
plural_expr = nodes.Name(singular_names[0], 'load') |
||||
num_called_num = singular_names[0] == 'num' |
||||
|
||||
# if we have a pluralize block, we parse that too |
||||
if parser.stream.current.test('name:pluralize'): |
||||
have_plural = True |
||||
next(parser.stream) |
||||
if parser.stream.current.type != 'block_end': |
||||
name = parser.stream.expect('name') |
||||
if name.value not in variables: |
||||
parser.fail('unknown variable %r for pluralization' % |
||||
name.value, name.lineno, |
||||
exc=TemplateAssertionError) |
||||
plural_expr = variables[name.value] |
||||
num_called_num = name.value == 'num' |
||||
parser.stream.expect('block_end') |
||||
plural_names, plural = self._parse_block(parser, False) |
||||
next(parser.stream) |
||||
referenced.update(plural_names) |
||||
else: |
||||
next(parser.stream) |
||||
|
||||
# register free names as simple name expressions |
||||
for var in referenced: |
||||
if var not in variables: |
||||
variables[var] = nodes.Name(var, 'load') |
||||
|
||||
if not have_plural: |
||||
plural_expr = None |
||||
elif plural_expr is None: |
||||
parser.fail('pluralize without variables', lineno) |
||||
|
||||
node = self._make_node(singular, plural, variables, plural_expr, |
||||
bool(referenced), |
||||
num_called_num and have_plural) |
||||
node.set_lineno(lineno) |
||||
return node |
||||
|
||||
def _parse_block(self, parser, allow_pluralize): |
||||
"""Parse until the next block tag with a given name.""" |
||||
referenced = [] |
||||
buf = [] |
||||
while 1: |
||||
if parser.stream.current.type == 'data': |
||||
buf.append(parser.stream.current.value.replace('%', '%%')) |
||||
next(parser.stream) |
||||
elif parser.stream.current.type == 'variable_begin': |
||||
next(parser.stream) |
||||
name = parser.stream.expect('name').value |
||||
referenced.append(name) |
||||
buf.append('%%(%s)s' % name) |
||||
parser.stream.expect('variable_end') |
||||
elif parser.stream.current.type == 'block_begin': |
||||
next(parser.stream) |
||||
if parser.stream.current.test('name:endtrans'): |
||||
break |
||||
elif parser.stream.current.test('name:pluralize'): |
||||
if allow_pluralize: |
||||
break |
||||
parser.fail('a translatable section can have only one ' |
||||
'pluralize section') |
||||
parser.fail('control structures in translatable sections are ' |
||||
'not allowed') |
||||
elif parser.stream.eos: |
||||
parser.fail('unclosed translation block') |
||||
else: |
||||
assert False, 'internal parser error' |
||||
|
||||
return referenced, concat(buf) |
||||
|
||||
def _make_node(self, singular, plural, variables, plural_expr, |
||||
vars_referenced, num_called_num): |
||||
"""Generates a useful node from the data provided.""" |
||||
# no variables referenced? no need to escape for old style |
||||
# gettext invocations only if there are vars. |
||||
if not vars_referenced and not self.environment.newstyle_gettext: |
||||
singular = singular.replace('%%', '%') |
||||
if plural: |
||||
plural = plural.replace('%%', '%') |
||||
|
||||
# singular only: |
||||
if plural_expr is None: |
||||
gettext = nodes.Name('gettext', 'load') |
||||
node = nodes.Call(gettext, [nodes.Const(singular)], |
||||
[], None, None) |
||||
|
||||
# singular and plural |
||||
else: |
||||
ngettext = nodes.Name('ngettext', 'load') |
||||
node = nodes.Call(ngettext, [ |
||||
nodes.Const(singular), |
||||
nodes.Const(plural), |
||||
plural_expr |
||||
], [], None, None) |
||||
|
||||
# in case newstyle gettext is used, the method is powerful |
||||
# enough to handle the variable expansion and autoescape |
||||
# handling itself |
||||
if self.environment.newstyle_gettext: |
||||
for key, value in variables.iteritems(): |
||||
# the function adds that later anyways in case num was |
||||
# called num, so just skip it. |
||||
if num_called_num and key == 'num': |
||||
continue |
||||
node.kwargs.append(nodes.Keyword(key, value)) |
||||
|
||||
# otherwise do that here |
||||
else: |
||||
# mark the return value as safe if we are in an |
||||
# environment with autoescaping turned on |
||||
node = nodes.MarkSafeIfAutoescape(node) |
||||
if variables: |
||||
node = nodes.Mod(node, nodes.Dict([ |
||||
nodes.Pair(nodes.Const(key), value) |
||||
for key, value in variables.items() |
||||
])) |
||||
return nodes.Output([node]) |
||||
|
||||
|
||||
class ExprStmtExtension(Extension): |
||||
"""Adds a `do` tag to Jinja2 that works like the print statement just |
||||
that it doesn't print the return value. |
||||
""" |
||||
tags = set(['do']) |
||||
|
||||
def parse(self, parser): |
||||
node = nodes.ExprStmt(lineno=next(parser.stream).lineno) |
||||
node.node = parser.parse_tuple() |
||||
return node |
||||
|
||||
|
||||
class LoopControlExtension(Extension): |
||||
"""Adds break and continue to the template engine.""" |
||||
tags = set(['break', 'continue']) |
||||
|
||||
def parse(self, parser): |
||||
token = next(parser.stream) |
||||
if token.value == 'break': |
||||
return nodes.Break(lineno=token.lineno) |
||||
return nodes.Continue(lineno=token.lineno) |
||||
|
||||
|
||||
class WithExtension(Extension): |
||||
"""Adds support for a django-like with block.""" |
||||
tags = set(['with']) |
||||
|
||||
def parse(self, parser): |
||||
node = nodes.Scope(lineno=next(parser.stream).lineno) |
||||
assignments = [] |
||||
while parser.stream.current.type != 'block_end': |
||||
lineno = parser.stream.current.lineno |
||||
if assignments: |
||||
parser.stream.expect('comma') |
||||
target = parser.parse_assign_target() |
||||
parser.stream.expect('assign') |
||||
expr = parser.parse_expression() |
||||
assignments.append(nodes.Assign(target, expr, lineno=lineno)) |
||||
node.body = assignments + \ |
||||
list(parser.parse_statements(('name:endwith',), |
||||
drop_needle=True)) |
||||
return node |
||||
|
||||
|
||||
class AutoEscapeExtension(Extension): |
||||
"""Changes auto escape rules for a scope.""" |
||||
tags = set(['autoescape']) |
||||
|
||||
def parse(self, parser): |
||||
node = nodes.ScopedEvalContextModifier(lineno=next(parser.stream).lineno) |
||||
node.options = [ |
||||
nodes.Keyword('autoescape', parser.parse_expression()) |
||||
] |
||||
node.body = parser.parse_statements(('name:endautoescape',), |
||||
drop_needle=True) |
||||
return nodes.Scope([node]) |
||||
|
||||
|
||||
def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS, |
||||
babel_style=True): |
||||
"""Extract localizable strings from the given template node. Per |
||||
default this function returns matches in babel style that means non string |
||||
parameters as well as keyword arguments are returned as `None`. This |
||||
allows Babel to figure out what you really meant if you are using |
||||
gettext functions that allow keyword arguments for placeholder expansion. |
||||
If you don't want that behavior set the `babel_style` parameter to `False` |
||||
which causes only strings to be returned and parameters are always stored |
||||
in tuples. As a consequence invalid gettext calls (calls without a single |
||||
string parameter or string parameters after non-string parameters) are |
||||
skipped. |
||||
|
||||
This example explains the behavior: |
||||
|
||||
>>> from jinja2 import Environment |
||||
>>> env = Environment() |
||||
>>> node = env.parse('{{ (_("foo"), _(), ngettext("foo", "bar", 42)) }}') |
||||
>>> list(extract_from_ast(node)) |
||||
[(1, '_', 'foo'), (1, '_', ()), (1, 'ngettext', ('foo', 'bar', None))] |
||||
>>> list(extract_from_ast(node, babel_style=False)) |
||||
[(1, '_', ('foo',)), (1, 'ngettext', ('foo', 'bar'))] |
||||
|
||||
For every string found this function yields a ``(lineno, function, |
||||
message)`` tuple, where: |
||||
|
||||
* ``lineno`` is the number of the line on which the string was found, |
||||
* ``function`` is the name of the ``gettext`` function used (if the |
||||
string was extracted from embedded Python code), and |
||||
* ``message`` is the string itself (a ``unicode`` object, or a tuple |
||||
of ``unicode`` objects for functions with multiple string arguments). |
||||
|
||||
This extraction function operates on the AST and is because of that unable |
||||
to extract any comments. For comment support you have to use the babel |
||||
extraction interface or extract comments yourself. |
||||
""" |
||||
for node in node.find_all(nodes.Call): |
||||
if not isinstance(node.node, nodes.Name) or \ |
||||
node.node.name not in gettext_functions: |
||||
continue |
||||
|
||||
strings = [] |
||||
for arg in node.args: |
||||
if isinstance(arg, nodes.Const) and \ |
||||
isinstance(arg.value, basestring): |
||||
strings.append(arg.value) |
||||
else: |
||||
strings.append(None) |
||||
|
||||
for arg in node.kwargs: |
||||
strings.append(None) |
||||
if node.dyn_args is not None: |
||||
strings.append(None) |
||||
if node.dyn_kwargs is not None: |
||||
strings.append(None) |
||||
|
||||
if not babel_style: |
||||
strings = tuple(x for x in strings if x is not None) |
||||
if not strings: |
||||
continue |
||||
else: |
||||
if len(strings) == 1: |
||||
strings = strings[0] |
||||
else: |
||||
strings = tuple(strings) |
||||
yield node.lineno, node.node.name, strings |
||||
|
||||
|
||||
class _CommentFinder(object): |
||||
"""Helper class to find comments in a token stream. Can only |
||||
find comments for gettext calls forwards. Once the comment |
||||
from line 4 is found, a comment for line 1 will not return a |
||||
usable value. |
||||
""" |
||||
|
||||
def __init__(self, tokens, comment_tags): |
||||
self.tokens = tokens |
||||
self.comment_tags = comment_tags |
||||
self.offset = 0 |
||||
self.last_lineno = 0 |
||||
|
||||
def find_backwards(self, offset): |
||||
try: |
||||
for _, token_type, token_value in \ |
||||
reversed(self.tokens[self.offset:offset]): |
||||
if token_type in ('comment', 'linecomment'): |
||||
try: |
||||
prefix, comment = token_value.split(None, 1) |
||||
except ValueError: |
||||
continue |
||||
if prefix in self.comment_tags: |
||||
return [comment.rstrip()] |
||||
return [] |
||||
finally: |
||||
self.offset = offset |
||||
|
||||
def find_comments(self, lineno): |
||||
if not self.comment_tags or self.last_lineno > lineno: |
||||
return [] |
||||
for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset:]): |
||||
if token_lineno > lineno: |
||||
return self.find_backwards(self.offset + idx) |
||||
return self.find_backwards(len(self.tokens)) |
||||
|
||||
|
||||
def babel_extract(fileobj, keywords, comment_tags, options): |
||||
"""Babel extraction method for Jinja templates. |
||||
|
||||
.. versionchanged:: 2.3 |
||||
Basic support for translation comments was added. If `comment_tags` |
||||
is now set to a list of keywords for extraction, the extractor will |
||||
try to find the best preceeding comment that begins with one of the |
||||
keywords. For best results, make sure to not have more than one |
||||
gettext call in one line of code and the matching comment in the |
||||
same line or the line before. |
||||
|
||||
.. versionchanged:: 2.5.1 |
||||
The `newstyle_gettext` flag can be set to `True` to enable newstyle |
||||
gettext calls. |
||||
|
||||
.. versionchanged:: 2.7 |
||||
A `silent` option can now be provided. If set to `False` template |
||||
syntax errors are propagated instead of being ignored. |
||||
|
||||
:param fileobj: the file-like object the messages should be extracted from |
||||
:param keywords: a list of keywords (i.e. function names) that should be |
||||
recognized as translation functions |
||||
:param comment_tags: a list of translator tags to search for and include |
||||
in the results. |
||||
:param options: a dictionary of additional options (optional) |
||||
:return: an iterator over ``(lineno, funcname, message, comments)`` tuples. |
||||
(comments will be empty currently) |
||||
""" |
||||
extensions = set() |
||||
for extension in options.get('extensions', '').split(','): |
||||
extension = extension.strip() |
||||
if not extension: |
||||
continue |
||||
extensions.add(import_string(extension)) |
||||
if InternationalizationExtension not in extensions: |
||||
extensions.add(InternationalizationExtension) |
||||
|
||||
def getbool(options, key, default=False): |
||||
return options.get(key, str(default)).lower() in \ |
||||
('1', 'on', 'yes', 'true') |
||||
|
||||
silent = getbool(options, 'silent', True) |
||||
environment = Environment( |
||||
options.get('block_start_string', BLOCK_START_STRING), |
||||
options.get('block_end_string', BLOCK_END_STRING), |
||||
options.get('variable_start_string', VARIABLE_START_STRING), |
||||
options.get('variable_end_string', VARIABLE_END_STRING), |
||||
options.get('comment_start_string', COMMENT_START_STRING), |
||||
options.get('comment_end_string', COMMENT_END_STRING), |
||||
options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX, |
||||
options.get('line_comment_prefix') or LINE_COMMENT_PREFIX, |
||||
getbool(options, 'trim_blocks', TRIM_BLOCKS), |
||||
NEWLINE_SEQUENCE, frozenset(extensions), |
||||
cache_size=0, |
||||
auto_reload=False |
||||
) |
||||
|
||||
if getbool(options, 'newstyle_gettext'): |
||||
environment.newstyle_gettext = True |
||||
|
||||
source = fileobj.read().decode(options.get('encoding', 'utf-8')) |
||||
try: |
||||
node = environment.parse(source) |
||||
tokens = list(environment.lex(environment.preprocess(source))) |
||||
except TemplateSyntaxError, e: |
||||
if not silent: |
||||
raise |
||||
# skip templates with syntax errors |
||||
return |
||||
|
||||
finder = _CommentFinder(tokens, comment_tags) |
||||
for lineno, func, message in extract_from_ast(node, keywords): |
||||
yield lineno, func, message, finder.find_comments(lineno) |
||||
|
||||
|
||||
#: nicer import names |
||||
i18n = InternationalizationExtension |
||||
do = ExprStmtExtension |
||||
loopcontrols = LoopControlExtension |
||||
with_ = WithExtension |
||||
autoescape = AutoEscapeExtension |
@ -1,102 +0,0 @@ |
||||
# -*- coding: utf-8 -*- |
||||
""" |
||||
jinja2.meta |
||||
~~~~~~~~~~~ |
||||
|
||||
This module implements various functions that exposes information about |
||||
templates that might be interesting for various kinds of applications. |
||||
|
||||
:copyright: (c) 2010 by the Jinja Team, see AUTHORS for more details. |
||||
:license: BSD, see LICENSE for more details. |
||||
""" |
||||
from jinja2 import nodes |
||||
from jinja2.compiler import CodeGenerator |
||||
|
||||
|
||||
class TrackingCodeGenerator(CodeGenerator): |
||||
"""We abuse the code generator for introspection.""" |
||||
|
||||
def __init__(self, environment): |
||||
CodeGenerator.__init__(self, environment, '<introspection>', |
||||
'<introspection>') |
||||
self.undeclared_identifiers = set() |
||||
|
||||
def write(self, x): |
||||
"""Don't write.""" |
||||
|
||||
def pull_locals(self, frame): |
||||
"""Remember all undeclared identifiers.""" |
||||
self.undeclared_identifiers.update(frame.identifiers.undeclared) |
||||
|
||||
|
||||
def find_undeclared_variables(ast): |
||||
"""Returns a set of all variables in the AST that will be looked up from |
||||
the context at runtime. Because at compile time it's not known which |
||||
variables will be used depending on the path the execution takes at |
||||
runtime, all variables are returned. |
||||
|
||||
>>> from jinja2 import Environment, meta |
||||
>>> env = Environment() |
||||
>>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') |
||||
>>> meta.find_undeclared_variables(ast) |
||||
set(['bar']) |
||||
|
||||
.. admonition:: Implementation |
||||
|
||||
Internally the code generator is used for finding undeclared variables. |
||||
This is good to know because the code generator might raise a |
||||
:exc:`TemplateAssertionError` during compilation and as a matter of |
||||
fact this function can currently raise that exception as well. |
||||
""" |
||||
codegen = TrackingCodeGenerator(ast.environment) |
||||
codegen.visit(ast) |
||||
return codegen.undeclared_identifiers |
||||
|
||||
|
||||
def find_referenced_templates(ast): |
||||
"""Finds all the referenced templates from the AST. This will return an |
||||
iterator over all the hardcoded template extensions, inclusions and |
||||
imports. If dynamic inheritance or inclusion is used, `None` will be |
||||
yielded. |
||||
|
||||
>>> from jinja2 import Environment, meta |
||||
>>> env = Environment() |
||||
>>> ast = env.parse('{% extends "layout.html" %}{% include helper %}') |
||||
>>> list(meta.find_referenced_templates(ast)) |
||||
['layout.html', None] |
||||
|
||||
This function is useful for dependency tracking. For example if you want |
||||
to rebuild parts of the website after a layout template has changed. |
||||
""" |
||||
for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import, |
||||
nodes.Include)): |
||||
if not isinstance(node.template, nodes.Const): |
||||
# a tuple with some non consts in there |
||||
if isinstance(node.template, (nodes.Tuple, nodes.List)): |
||||
for template_name in node.template.items: |
||||
# something const, only yield the strings and ignore |
||||
# non-string consts that really just make no sense |
||||
if isinstance(template_name, nodes.Const): |
||||
if isinstance(template_name.value, basestring): |
||||
yield template_name.value |
||||
# something dynamic in there |
||||
else: |
||||
yield None |
||||
# something dynamic we don't know about here |
||||
else: |
||||
yield None |
||||
continue |
||||
# constant is a basestring, direct template name |
||||
if isinstance(node.template.value, basestring): |
||||
yield node.template.value |
||||
# a tuple or list (latter *should* not happen) made of consts, |
||||
# yield the consts that are strings. We could warn here for |
||||
# non string values |
||||
elif isinstance(node, nodes.Include) and \ |
||||
isinstance(node.template.value, (tuple, list)): |
||||
for template_name in node.template.value: |
||||
if isinstance(template_name, basestring): |
||||
yield template_name |
||||
# something else we don't care about, we could warn here |
||||
else: |
||||
yield None |
@ -1,361 +0,0 @@ |
||||
# -*- coding: utf-8 -*- |
||||
""" |
||||
jinja2.sandbox |
||||
~~~~~~~~~~~~~~ |
||||
|
||||
Adds a sandbox layer to Jinja as it was the default behavior in the old |
||||
Jinja 1 releases. This sandbox is slightly different from Jinja 1 as the |
||||
default behavior is easier to use. |
||||
|
||||
The behavior can be changed by subclassing the environment. |
||||
|
||||
:copyright: (c) 2010 by the Jinja Team. |
||||
:license: BSD. |
||||
""" |
||||
import operator |
||||
from jinja2.environment import Environment |
||||
from jinja2.exceptions import SecurityError |
||||
from jinja2.utils import FunctionType, MethodType, TracebackType, CodeType, \ |
||||
FrameType, GeneratorType |
||||
|
||||
|
||||
#: maximum number of items a range may produce |
||||
MAX_RANGE = 100000 |
||||
|
||||
#: attributes of function objects that are considered unsafe. |
||||
UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict', |
||||
'func_defaults', 'func_globals']) |
||||
|
||||
#: unsafe method attributes. function attributes are unsafe for methods too |
||||
UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self']) |
||||
|
||||
|
||||
import warnings |
||||
|
||||
# make sure we don't warn in python 2.6 about stuff we don't care about |
||||
warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning, |
||||
module='jinja2.sandbox') |
||||
|
||||
from collections import deque |
||||
|
||||
_mutable_set_types = (set,) |
||||
_mutable_mapping_types = (dict,) |
||||
_mutable_sequence_types = (list,) |
||||
|
||||
|
||||
# on python 2.x we can register the user collection types |
||||
try: |
||||
from UserDict import UserDict, DictMixin |
||||
from UserList import UserList |
||||
_mutable_mapping_types += (UserDict, DictMixin) |
||||
_mutable_set_types += (UserList,) |
||||
except ImportError: |
||||
pass |
||||
|
||||
# if sets is still available, register the mutable set from there as well |
||||
try: |
||||
from sets import Set |
||||
_mutable_set_types += (Set,) |
||||
except ImportError: |
||||
pass |
||||
|
||||
#: register Python 2.6 abstract base classes |
||||
try: |
||||
from collections import MutableSet, MutableMapping, MutableSequence |
||||
_mutable_set_types += (MutableSet,) |
||||
_mutable_mapping_types += (MutableMapping,) |
||||
_mutable_sequence_types += (MutableSequence,) |
||||
except ImportError: |
||||
pass |
||||
|
||||
_mutable_spec = ( |
||||
(_mutable_set_types, frozenset([ |
||||
'add', 'clear', 'difference_update', 'discard', 'pop', 'remove', |
||||
'symmetric_difference_update', 'update' |
||||
])), |
||||
(_mutable_mapping_types, frozenset([ |
||||
'clear', 'pop', 'popitem', 'setdefault', 'update' |
||||
])), |
||||
(_mutable_sequence_types, frozenset([ |
||||
'append', 'reverse', 'insert', 'sort', 'extend', 'remove' |
||||
])), |
||||
(deque, frozenset([ |
||||
'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop', |
||||
'popleft', 'remove', 'rotate' |
||||
])) |
||||
) |
||||
|
||||
|
||||
def safe_range(*args): |
||||
"""A range that can't generate ranges with a length of more than |
||||
MAX_RANGE items. |
||||
""" |
||||
rng = xrange(*args) |
||||
if len(rng) > MAX_RANGE: |
||||
raise OverflowError('range too big, maximum size for range is %d' % |
||||
MAX_RANGE) |
||||
return rng |
||||
|
||||
|
||||
def unsafe(f): |
||||
"""Marks a function or method as unsafe. |
||||
|
||||
:: |
||||
|
||||
@unsafe |
||||
def delete(self): |
||||
pass |
||||
""" |
||||
f.unsafe_callable = True |
||||
return f |
||||
|
||||
|
||||
def is_internal_attribute(obj, attr): |
||||
"""Test if the attribute given is an internal python attribute. For |
||||
example this function returns `True` for the `func_code` attribute of |
||||
python objects. This is useful if the environment method |
||||
:meth:`~SandboxedEnvironment.is_safe_attribute` is overriden. |
||||
|
||||
>>> from jinja2.sandbox import is_internal_attribute |
||||
>>> is_internal_attribute(lambda: None, "func_code") |
||||
True |
||||
>>> is_internal_attribute((lambda x:x).func_code, 'co_code') |
||||
True |
||||
>>> is_internal_attribute(str, "upper") |
||||
False |
||||
""" |
||||
if isinstance(obj, FunctionType): |
||||
if attr in UNSAFE_FUNCTION_ATTRIBUTES: |
||||
return True |
||||
elif isinstance(obj, MethodType): |
||||
if attr in UNSAFE_FUNCTION_ATTRIBUTES or \ |
||||
attr in UNSAFE_METHOD_ATTRIBUTES: |
||||
return True |
||||
elif isinstance(obj, type): |
||||
if attr == 'mro': |
||||
return True |
||||
elif isinstance(obj, (CodeType, TracebackType, FrameType)): |
||||
return True |
||||
elif isinstance(obj, GeneratorType): |
||||
if attr == 'gi_frame': |
||||
return True |
||||
return attr.startswith('__') |
||||
|
||||
|
||||
def modifies_known_mutable(obj, attr): |
||||
"""This function checks if an attribute on a builtin mutable object |
||||
(list, dict, set or deque) would modify it if called. It also supports |
||||
the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and |
||||
with Python 2.6 onwards the abstract base classes `MutableSet`, |
||||
`MutableMapping`, and `MutableSequence`. |
||||
|
||||
>>> modifies_known_mutable({}, "clear") |
||||
True |
||||
>>> modifies_known_mutable({}, "keys") |
||||
False |
||||
>>> modifies_known_mutable([], "append") |
||||
True |
||||
>>> modifies_known_mutable([], "index") |
||||
False |
||||
|
||||
If called with an unsupported object (such as unicode) `False` is |
||||
returned. |
||||
|
||||
>>> modifies_known_mutable("foo", "upper") |
||||
False |
||||
""" |
||||
for typespec, unsafe in _mutable_spec: |
||||
if isinstance(obj, typespec): |
||||
return attr in unsafe |
||||
return False |
||||
|
||||
|
||||
class SandboxedEnvironment(Environment): |
||||
"""The sandboxed environment. It works like the regular environment but |
||||
tells the compiler to generate sandboxed code. Additionally subclasses of |
||||
this environment may override the methods that tell the runtime what |
||||
attributes or functions are safe to access. |
||||
|
||||
If the template tries to access insecure code a :exc:`SecurityError` is |
||||
raised. However also other exceptions may occour during the rendering so |
||||
the caller has to ensure that all exceptions are catched. |
||||
""" |
||||
sandboxed = True |
||||
|
||||
#: default callback table for the binary operators. A copy of this is |
||||
#: available on each instance of a sandboxed environment as |
||||
#: :attr:`binop_table` |
||||
default_binop_table = { |
||||
'+': operator.add, |
||||
'-': operator.sub, |
||||
'*': operator.mul, |
||||
'/': operator.truediv, |
||||
'//': operator.floordiv, |
||||
'**': operator.pow, |
||||
'%': operator.mod |
||||
} |
||||
|
||||
#: default callback table for the unary operators. A copy of this is |
||||
#: available on each instance of a sandboxed environment as |
||||
#: :attr:`unop_table` |
||||
default_unop_table = { |
||||
'+': operator.pos, |
||||
'-': operator.neg |
||||
} |
||||
|
||||
#: a set of binary operators that should be intercepted. Each operator |
||||
#: that is added to this set (empty by default) is delegated to the |
||||
#: :meth:`call_binop` method that will perform the operator. The default |
||||
#: operator callback is specified by :attr:`binop_table`. |
||||
#: |
||||
#: The following binary operators are interceptable: |
||||
#: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**`` |
||||
#: |
||||
#: The default operation form the operator table corresponds to the |
||||
#: builtin function. Intercepted calls are always slower than the native |
||||
#: operator call, so make sure only to intercept the ones you are |
||||
#: interested in. |
||||
#: |
||||
#: .. versionadded:: 2.6 |
||||
intercepted_binops = frozenset() |
||||
|
||||
#: a set of unary operators that should be intercepted. Each operator |
||||
#: that is added to this set (empty by default) is delegated to the |
||||
#: :meth:`call_unop` method that will perform the operator. The default |
||||
#: operator callback is specified by :attr:`unop_table`. |
||||
#: |
||||
#: The following unary operators are interceptable: ``+``, ``-`` |
||||
#: |
||||
#: The default operation form the operator table corresponds to the |
||||
#: builtin function. Intercepted calls are always slower than the native |
||||
#: operator call, so make sure only to intercept the ones you are |
||||
#: interested in. |
||||
#: |
||||
#: .. versionadded:: 2.6 |
||||
intercepted_unops = frozenset() |
||||
|
||||
def intercept_unop(self, operator): |
||||
"""Called during template compilation with the name of a unary |
||||
operator to check if it should be intercepted at runtime. If this |
||||
method returns `True`, :meth:`call_unop` is excuted for this unary |
||||
operator. The default implementation of :meth:`call_unop` will use |
||||
the :attr:`unop_table` dictionary to perform the operator with the |
||||
same logic as the builtin one. |
||||
|
||||
The following unary operators are interceptable: ``+`` and ``-`` |
||||
|
||||
Intercepted calls are always slower than the native operator call, |
||||
so make sure only to intercept the ones you are interested in. |
||||
|
||||
.. versionadded:: 2.6 |
||||
""" |
||||
return False |
||||
|
||||
|
||||
def __init__(self, *args, **kwargs): |
||||
Environment.__init__(self, *args, **kwargs) |
||||
self.globals['range'] = safe_range |
||||
self.binop_table = self.default_binop_table.copy() |
||||
self.unop_table = self.default_unop_table.copy() |
||||
|
||||
def is_safe_attribute(self, obj, attr, value): |
||||
"""The sandboxed environment will call this method to check if the |
||||
attribute of an object is safe to access. Per default all attributes |
||||
starting with an underscore are considered private as well as the |
||||
special attributes of internal python objects as returned by the |
||||
:func:`is_internal_attribute` function. |
||||
""" |
||||
return not (attr.startswith('_') or is_internal_attribute(obj, attr)) |
||||
|
||||
def is_safe_callable(self, obj): |
||||
"""Check if an object is safely callable. Per default a function is |
||||
considered safe unless the `unsafe_callable` attribute exists and is |
||||
True. Override this method to alter the behavior, but this won't |
||||
affect the `unsafe` decorator from this module. |
||||
""" |
||||
return not (getattr(obj, 'unsafe_callable', False) or |
||||
getattr(obj, 'alters_data', False)) |
||||
|
||||
def call_binop(self, context, operator, left, right): |
||||
"""For intercepted binary operator calls (:meth:`intercepted_binops`) |
||||
this function is executed instead of the builtin operator. This can |
||||
be used to fine tune the behavior of certain operators. |
||||
|
||||
.. versionadded:: 2.6 |
||||
""" |
||||
return self.binop_table[operator](left, right) |
||||
|
||||
def call_unop(self, context, operator, arg): |
||||
"""For intercepted unary operator calls (:meth:`intercepted_unops`) |
||||
this function is executed instead of the builtin operator. This can |
||||
be used to fine tune the behavior of certain operators. |
||||
|
||||
.. versionadded:: 2.6 |
||||
""" |
||||
return self.unop_table[operator](arg) |
||||
|
||||
def getitem(self, obj, argument): |
||||
"""Subscribe an object from sandboxed code.""" |
||||
try: |
||||
return obj[argument] |
||||
except (TypeError, LookupError): |
||||
if isinstance(argument, basestring): |
||||
try: |
||||
attr = str(argument) |
||||
except Exception: |
||||
pass |
||||
else: |
||||
try: |
||||
value = getattr(obj, attr) |
||||
except AttributeError: |
||||
pass |
||||
else: |
||||
if self.is_safe_attribute(obj, argument, value): |
||||
return value |
||||
return self.unsafe_undefined(obj, argument) |
||||
return self.undefined(obj=obj, name=argument) |
||||
|
||||
def getattr(self, obj, attribute): |
||||
"""Subscribe an object from sandboxed code and prefer the |
||||
attribute. The attribute passed *must* be a bytestring. |
||||
""" |
||||
try: |
||||
value = getattr(obj, attribute) |
||||
except AttributeError: |
||||
try: |
||||
return obj[attribute] |
||||
except (TypeError, LookupError): |
||||
pass |
||||
else: |
||||
if self.is_safe_attribute(obj, attribute, value): |
||||
return value |
||||
return self.unsafe_undefined(obj, attribute) |
||||
return self.undefined(obj=obj, name=attribute) |
||||
|
||||
def unsafe_undefined(self, obj, attribute): |
||||
"""Return an undefined object for unsafe attributes.""" |
||||
return self.undefined('access to attribute %r of %r ' |
||||
'object is unsafe.' % ( |
||||
attribute, |
||||
obj.__class__.__name__ |
||||
), name=attribute, obj=obj, exc=SecurityError) |
||||
|
||||
def call(__self, __context, __obj, *args, **kwargs): |
||||
"""Call an object from sandboxed code.""" |
||||
# the double prefixes are to avoid double keyword argument |
||||
# errors when proxying the call. |
||||
if not __self.is_safe_callable(__obj): |
||||
raise SecurityError('%r is not safely callable' % (__obj,)) |
||||
return __context.call(__obj, *args, **kwargs) |
||||
|
||||
|
||||
class ImmutableSandboxedEnvironment(SandboxedEnvironment): |
||||
"""Works exactly like the regular `SandboxedEnvironment` but does not |
||||
permit modifications on the builtin mutable objects `list`, `set`, and |
||||
`dict` by using the :func:`modifies_known_mutable` function. |
||||
""" |
||||
|
||||
def is_safe_attribute(self, obj, attr, value): |
||||
if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value): |
||||
return False |
||||
return not modifies_known_mutable(obj, attr) |
@ -1,31 +0,0 @@ |
||||
#include <unordered_map> |
||||
#include <string> |
||||
#include <bridge> |
||||
|
||||
typedef std::unordered_map Map; |
||||
|
||||
/*! @brief Hash from strings to OpenCV enums
|
||||
* |
||||
* This is a translation map for strings to OpenCV constants (enums). |
||||
* When an int is requested from the bridge, and the the mxArray storage
|
||||
* type is a string, this map is invoked. Thus functions can be called |
||||
* from Matlab as, e.g. |
||||
* cv.dft(x, xf, "DFT_FORWARD"); |
||||
* |
||||
* Note that an alternative Matlab class exists as well, so that functions |
||||
* can be called as, e.g. |
||||
* cv.dft(x, xf, cv.DFT_FORWARD); |
||||
* |
||||
* This string to int map tends to be faster than its Matlab companion, |
||||
* but there is no direct access to the value of the constants. It also |
||||
* enables different error reporting properties. |
||||
*/ |
||||
Map<std::string, int> constants = { |
||||
{% for key, val in constants.items() %} |
||||
{% if val|convertibleToInt %} |
||||
{ "{{key}}", {{val}} }, |
||||
{% else %} |
||||
{ "{{key}}", {{constants[val]}} }, |
||||
{% endif %} |
||||
{% endfor %} |
||||
}; |
Loading…
Reference in new issue