From: Armin Ronacher Date: Sun, 4 May 2008 10:31:48 +0000 (+0200) Subject: improved sandbox, added proper striptags and updated documentation to latest sphinx... X-Git-Tag: 2.0rc1~101 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=76c280bf5b2263a435e38e0b3612c23eb633ed79;p=jinja2.git improved sandbox, added proper striptags and updated documentation to latest sphinx changes --HG-- branch : trunk --- diff --git a/docs/conf.py b/docs/conf.py index 54a1ad4..8a41874 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -145,4 +145,4 @@ latex_preamble = ''' #latex_appendices = [] # If false, no module index is generated. -#latex_use_modindex = True +latex_use_modindex = False diff --git a/docs/index.rst b/docs/index.rst index c548b91..d7b4d89 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,10 +9,3 @@ Contents: intro api templates - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/docs/templates.rst b/docs/templates.rst index 18bd422..8d58213 100644 --- a/docs/templates.rst +++ b/docs/templates.rst @@ -838,3 +838,8 @@ The following functions are available in the global scope by default: with HTML are generated each paragraph between 20 and 100 words. If html is disabled regular text is returned. This is useful to generate simple contents for layout testing. + +.. function:: dict(**items) + + A convenient alternative to dict literals. ``{'foo': 'bar'}`` is the same + as ``dict(foo='bar')``. diff --git a/jinja2/defaults.py b/jinja2/defaults.py index 848cb8f..e124930 100644 --- a/jinja2/defaults.py +++ b/jinja2/defaults.py @@ -24,6 +24,7 @@ LINE_STATEMENT_PREFIX = None DEFAULT_NAMESPACE = { 'range': xrange, + 'dict': lambda **kw: kw, 'lipsum': generate_lorem_ipsum } diff --git a/jinja2/filters.py b/jinja2/filters.py index ec0186c..94086a6 100644 --- a/jinja2/filters.py +++ b/jinja2/filters.py @@ -19,7 +19,6 @@ from jinja2.runtime import Undefined from jinja2.exceptions import FilterArgumentError -_striptags_re = re.compile(r'(|<[^>]*>)') _word_re = re.compile(r'\w+') @@ -428,7 +427,7 @@ def do_striptags(value): """ if hasattr(value, '__html__'): value = value.__html__() - return u' '.join(_striptags_re.sub('', value).split()) + return Markup(unicode(value)).striptags() def do_slice(value, slices, fill_with=None): diff --git a/jinja2/sandbox.py b/jinja2/sandbox.py index 02a0438..cd5b579 100644 --- a/jinja2/sandbox.py +++ b/jinja2/sandbox.py @@ -20,6 +20,13 @@ from jinja2.environment import Environment #: 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']) + def safe_range(*args): """A range that can't generate ranges with a length of more than @@ -27,7 +34,8 @@ def safe_range(*args): """ rng = xrange(*args) if len(rng) > MAX_RANGE: - raise OverflowError('range too big') + raise OverflowError('range too big, maximum size for range is %d' % + MAX_RANGE) return rng @@ -54,9 +62,10 @@ class SandboxedEnvironment(Environment): if attr.startswith('_'): return False if isinstance(obj, FunctionType): - return not attr.startswith('func_') + return attr not in UNSAFE_FUNCTION_ATTRIBUTES if isinstance(obj, MethodType): - return not attr.startswith('im_') + return attr not in UNSAFE_FUNCTION_ATTRIBUTES and \ + attr not in UNSAFE_METHOD_ATTRIBUTES return True def is_safe_callable(self, obj): diff --git a/jinja2/utils.py b/jinja2/utils.py index 1d579cc..9437023 100644 --- a/jinja2/utils.py +++ b/jinja2/utils.py @@ -15,6 +15,7 @@ try: from thread import allocate_lock except ImportError: from dummy_thread import allocate_lock +from htmlentitydefs import name2codepoint from collections import deque from copy import deepcopy from itertools import imap @@ -28,7 +29,10 @@ _punctuation_re = re.compile( ) ) _simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$') - +_striptags_re = re.compile(r'(|<[^>]*>)') +_entity_re = re.compile(r'&([^;]+);') +_entities = name2codepoint.copy() +_entities['apos'] = 39 # special singleton representing missing values for the runtime missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})() @@ -278,6 +282,27 @@ class Markup(unicode): return map(self.__class__, unicode.splitlines(self, *args, **kwargs)) splitlines.__doc__ = unicode.splitlines.__doc__ + def unescape(self): + """Unescape markup.""" + def handle_match(m): + name = m.group(1) + if name in _entities: + return unichr(_entities[name]) + try: + if name[:2] in ('#x', '#X'): + return unichr(int(name[2:], 16)) + elif name.startswith('#'): + return unichr(int(name[1:])) + except ValueError: + pass + return u'' + return _entity_re.sub(handle_match, unicode(self)) + + def striptags(self): + """Strip tags and resolve enities.""" + stripped = u' '.join(_striptags_re.sub('', self).split()) + return Markup(stripped).unescape() + def make_wrapper(name): orig = getattr(unicode, name) def func(self, *args, **kwargs):