From 7ceced5016676dc3d6c1b77db9d62a47a046ba4a Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 3 May 2008 10:15:31 +0200 Subject: [PATCH] moved concat to utils, fixed a few docstrings, fixed memory leak in _speedups.escape --HG-- branch : trunk --- docs/api.rst | 2 +- jinja2/_speedups.c | 20 +++++++++----------- jinja2/compiler.py | 3 +-- jinja2/environment.py | 13 ++++++++----- jinja2/filters.py | 6 +----- jinja2/nodes.py | 2 +- jinja2/optimizer.py | 3 ++- jinja2/runtime.py | 23 +---------------------- jinja2/sandbox.py | 8 +++++--- jinja2/utils.py | 23 +++++++++++++++++++++++ setup.py | 3 +-- tests/test_imports.py | 4 +++- 12 files changed, 56 insertions(+), 54 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index a8d488b..6cab983 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -85,7 +85,7 @@ High Level API .. autoclass:: jinja2.Template - :members: render, stream, generate, module + :members: render, stream, generate, make_module, module .. attribute:: globals diff --git a/jinja2/_speedups.c b/jinja2/_speedups.c index 10ae6f6..5a7e9aa 100644 --- a/jinja2/_speedups.c +++ b/jinja2/_speedups.c @@ -2,7 +2,10 @@ * jinja2._speedups * ~~~~~~~~~~~~~~~~ * - * This module implements a few functions in C for better performance. + * This module implements a few functions in C for better performance. It + * also defines a `tb_set_next` function that is used to patch the debug + * traceback. If the speedups module is not compiled a ctypes implementation + * is used. * * :copyright: 2008 by Armin Ronacher. * :license: BSD. @@ -129,15 +132,8 @@ escape(PyObject *self, PyObject *text) /* we don't have to escape integers, bools or floats */ if (PyInt_CheckExact(text) || PyLong_CheckExact(text) || PyFloat_CheckExact(text) || PyBool_Check(text) || - text == Py_None) { - PyObject *args = PyTuple_New(1); - if (!args) { - Py_DECREF(s); - return NULL; - } - PyTuple_SET_ITEM(args, 0, text); - return PyObject_CallObject(markup, args); - } + text == Py_None) + return PyObject_CallFunctionObjArgs(markup, text, NULL); /* if the object has an __html__ method that performs the escaping */ PyObject *html = PyObject_GetAttrString(text, "__html__"); @@ -160,7 +156,9 @@ escape(PyObject *self, PyObject *text) s = escape_unicode((PyUnicodeObject*)text); /* convert the unicode string into a markup object. */ - return PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL); + rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL); + Py_DECREF(s); + return rv; } diff --git a/jinja2/compiler.py b/jinja2/compiler.py index 3d4fcbe..e3b9e53 100644 --- a/jinja2/compiler.py +++ b/jinja2/compiler.py @@ -17,8 +17,7 @@ from itertools import chain from jinja2 import nodes from jinja2.visitor import NodeVisitor, NodeTransformer from jinja2.exceptions import TemplateAssertionError -from jinja2.runtime import concat -from jinja2.utils import Markup +from jinja2.utils import Markup, concat operators = { diff --git a/jinja2/environment.py b/jinja2/environment.py index 68571fd..a129c38 100644 --- a/jinja2/environment.py +++ b/jinja2/environment.py @@ -14,10 +14,10 @@ from jinja2.lexer import Lexer from jinja2.parser import Parser from jinja2.optimizer import optimize from jinja2.compiler import generate -from jinja2.runtime import Undefined, Context, concat +from jinja2.runtime import Undefined, Context from jinja2.debug import translate_exception, translate_syntax_error from jinja2.exceptions import TemplateSyntaxError -from jinja2.utils import import_string, LRUCache, Markup, missing +from jinja2.utils import import_string, LRUCache, Markup, missing, concat # for direct template usage we have up to ten living environments @@ -530,8 +530,11 @@ class Template(object): return Context(self.environment, parent, self.name, self.blocks) def make_module(self, vars=None, shared=False): - """Like the `module` property but always reevaluates the template - and it's possible to provide a context. + """This method works like the :attr:`module` attribute when called + without arguments but it will evaluate the template every call + rather then caching the template. It's also possible to provide + a dict which is then used as context. The arguments are the same + as fo the :meth:`new_context` method. """ return TemplateModule(self, self.new_context(vars, shared)) @@ -593,7 +596,7 @@ class TemplateModule(object): # compiler too. The Include without context passing directly # uses the mangled name. The reason why we use a mangled one # is to avoid name clashes with macros with those names. - self.__body_stream = tuple(template.root_render_func(context)) + self.__body_stream = list(template.root_render_func(context)) self.__dict__.update(context.get_exported()) self.__name__ = template.name diff --git a/jinja2/filters.py b/jinja2/filters.py index 850079d..09b1bab 100644 --- a/jinja2/filters.py +++ b/jinja2/filters.py @@ -11,11 +11,7 @@ import re import math from random import choice -try: - from operator import itemgetter -except ImportError: - itemgetter = lambda a: lambda b: b[a] -from urllib import urlencode, quote +from operator import itemgetter from itertools import imap, groupby from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode from jinja2.runtime import Undefined diff --git a/jinja2/nodes.py b/jinja2/nodes.py index d0372e8..83d7573 100644 --- a/jinja2/nodes.py +++ b/jinja2/nodes.py @@ -13,10 +13,10 @@ :license: BSD, see LICENSE for more details. """ import operator +from copy import copy from types import FunctionType from itertools import chain, izip from collections import deque -from copy import copy from jinja2.utils import Markup diff --git a/jinja2/optimizer.py b/jinja2/optimizer.py index 784c3a8..283d1fa 100644 --- a/jinja2/optimizer.py +++ b/jinja2/optimizer.py @@ -18,7 +18,8 @@ """ from jinja2 import nodes from jinja2.visitor import NodeVisitor, NodeTransformer -from jinja2.runtime import LoopContext, concat +from jinja2.runtime import LoopContext +from jinja2.utils import concat def optimize(node, environment, context_hint=None): diff --git a/jinja2/runtime.py b/jinja2/runtime.py index 417fa70..4b9ce6d 100644 --- a/jinja2/runtime.py +++ b/jinja2/runtime.py @@ -11,7 +11,7 @@ import sys from types import FunctionType from itertools import chain, imap -from jinja2.utils import Markup, partial, soft_unicode, escape, missing +from jinja2.utils import Markup, partial, soft_unicode, escape, missing, concat from jinja2.exceptions import UndefinedError, TemplateRuntimeError @@ -21,27 +21,6 @@ __all__ = ['LoopContext', 'Context', 'TemplateReference', 'Macro', 'Markup', 'markup_join', 'unicode_join'] -# concatenate a list of strings and convert them to unicode. -# unfortunately there is a bug in python 2.4 and lower that causes -# unicode.join trash the traceback. -try: - def _test_gen_bug(): - raise TypeError(_test_gen_bug) - yield None - u''.join(_test_gen_bug()) -except TypeError, _error: - if _error.args and _error.args[0] is _test_gen_bug: - concat = u''.join - else: - def concat(gen): - try: - return u''.join(list(gen)) - except: - exc_type, exc_value, tb = sys.exc_info() - raise exc_type, exc_value, tb.tb_next - del _test_gen_bug, _error - - def markup_join(*args): """Concatenation that escapes if necessary and converts to unicode.""" buf = [] diff --git a/jinja2/sandbox.py b/jinja2/sandbox.py index 71f0239..02a0438 100644 --- a/jinja2/sandbox.py +++ b/jinja2/sandbox.py @@ -23,7 +23,8 @@ MAX_RANGE = 100000 def safe_range(*args): """A range that can't generate ranges with a length of more than - MAX_RANGE items.""" + MAX_RANGE items. + """ rng = xrange(*args) if len(rng) > MAX_RANGE: raise OverflowError('range too big') @@ -61,10 +62,11 @@ class SandboxedEnvironment(Environment): 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 - truish. Override this method to alter the behavior, but this won't + 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) + return not (getattr(obj, 'unsafe_callable', False) or \ + getattr(obj, 'alters_data', False)) def subscribe(self, obj, argument): """Subscribe an object from sandboxed code.""" diff --git a/jinja2/utils.py b/jinja2/utils.py index 7bddcfe..d9a45a8 100644 --- a/jinja2/utils.py +++ b/jinja2/utils.py @@ -33,6 +33,29 @@ _simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$') missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})() +# concatenate a list of strings and convert them to unicode. +# unfortunately there is a bug in python 2.4 and lower that causes +# unicode.join trash the traceback. +try: + def _test_gen_bug(): + raise TypeError(_test_gen_bug) + yield None + u''.join(_test_gen_bug()) +except TypeError, _error: + if _error.args and _error.args[0] is _test_gen_bug: + concat = u''.join + else: + def concat(gen): + try: + return u''.join(list(gen)) + except: + # this hack is needed so that the current frame + # does not show up in the traceback. + exc_type, exc_value, tb = sys.exc_info() + raise exc_type, exc_value, tb.tb_next + del _test_gen_bug, _error + + def contextfunction(f): """This decorator can be used to mark a callable as context callable. A context callable is passed the active context as first argument if it diff --git a/setup.py b/setup.py index 0c4fb31..7fe263d 100644 --- a/setup.py +++ b/setup.py @@ -120,8 +120,7 @@ setup( ], packages=['jinja2'], data_files=[ - ('docs/html', list(list_files('docs/html'))), - ('docs/txt', list(list_files('docs/src'))) + ##('docs', list(list_files('docs/_build/html'))) ], features={ 'speedups': Feature("optional C speed-enhancements", diff --git a/tests/test_imports.py b/tests/test_imports.py index 7ce8bde..92495e5 100644 --- a/tests/test_imports.py +++ b/tests/test_imports.py @@ -13,7 +13,7 @@ test_env = Environment(loader=DictLoader(dict( module='{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}', header='[{{ foo }}|{{ 23 }}]' ))) -test_env.globals['bar'] = '23' +test_env.globals['bar'] = 23 def test_context_imports(): @@ -44,3 +44,5 @@ def test_trailing_comma(): test_env.from_string('{% from "foo" import bar, baz with context %}') test_env.from_string('{% from "foo" import bar, baz, with context %}') test_env.from_string('{% from "foo" import bar, with context %}') + test_env.from_string('{% from "foo" import bar, with, context %}') + test_env.from_string('{% from "foo" import bar, with with context %}') -- 2.26.2