removed unused files from repo
This commit is contained in:
parent
57505b9af9
commit
9726346bc9
@ -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…
x
Reference in New Issue
Block a user