From 76c280bf5b2263a435e38e0b3612c23eb633ed79 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 4 May 2008 12:31:48 +0200 Subject: [PATCH] improved sandbox, added proper striptags and updated documentation to latest sphinx changes --HG-- branch : trunk --- docs/conf.py | 2 +- docs/index.rst | 7 ------- docs/templates.rst | 5 +++++ jinja2/defaults.py | 1 + jinja2/filters.py | 3 +-- jinja2/sandbox.py | 15 ++++++++++++--- jinja2/utils.py | 27 ++++++++++++++++++++++++++- 7 files changed, 46 insertions(+), 14 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 54a1ad40c..8a4187432 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -145,4 +145,4 @@ #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 c548b91c6..d7b4d89e8 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 18bd422e5..8d582132d 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 848cb8f0e..e12493076 100644 --- a/jinja2/defaults.py +++ b/jinja2/defaults.py @@ -24,6 +24,7 @@ DEFAULT_NAMESPACE = { 'range': xrange, + 'dict': lambda **kw: kw, 'lipsum': generate_lorem_ipsum } diff --git a/jinja2/filters.py b/jinja2/filters.py index ec0186c14..94086a63c 100644 --- a/jinja2/filters.py +++ b/jinja2/filters.py @@ -19,7 +19,6 @@ 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 02a04383c..cd5b5796c 100644 --- a/jinja2/sandbox.py +++ b/jinja2/sandbox.py @@ -20,6 +20,13 @@ #: 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 @@ def is_safe_attribute(self, obj, attr, value): 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 1d579cc11..9437023c9 100644 --- a/jinja2/utils.py +++ b/jinja2/utils.py @@ -15,6 +15,7 @@ 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 @@ ) ) _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 @@ def splitlines(self, *args, **kwargs): 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):