improved sandbox and updated setup.py
authorArmin Ronacher <armin.ronacher@active-4.com>
Mon, 26 May 2008 21:57:07 +0000 (23:57 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Mon, 26 May 2008 21:57:07 +0000 (23:57 +0200)
--HG--
branch : trunk

docs/sandbox.rst
jinja2/_speedups.c
jinja2/runtime.py
jinja2/sandbox.py
jinja2/utils.py
setup.py

index 2b65f9c49b5ecfcc3c7d6b7f73c1fc3af6bd03bb..f6ec78c5c4c23a62979f0605ad9716b6daf8296b 100644 (file)
@@ -28,4 +28,4 @@ SecurityError: access to attribute 'func_code' of 'function' object is unsafe.
 
 .. autofunction:: is_internal_attribute
 
-.. autofunction:: modifies_builtin_mutable
+.. autofunction:: modifies_known_mutable
index 112e6001c2c1c3b75137a7ea0d40e7254f4dbff9..f691c780290aa9f1ecb8a396d05d2d58ac4898a7 100644 (file)
@@ -157,7 +157,7 @@ soft_unicode(PyObject *self, PyObject *s)
 }
 
 
-static PyObject *
+static PyObject*
 tb_set_next(PyObject *self, PyObject *args)
 {
        PyTracebackObject *tb, *old;
index f5639f8d5f34296212653b236bd364a037c10def..babc3c93499bd1ae3750d9fb7cb5c103bd38a2f6 100644 (file)
@@ -117,10 +117,6 @@ class Context(object):
         return dict(self.parent, **self.vars)
 
     def call(__self, __obj, *args, **kwargs):
-        """Called by the template code to inject the current context
-        or environment as first arguments.  Then forwards the call to
-        the object with the arguments and keyword arguments.
-        """
         if __debug__:
             __traceback_hide__ = True
         if isinstance(__obj, _context_function_types):
index d44521ed604f6b0725d01e24348259b827d8f259..7135ff0987825854a8bb3f93e7e03a8e7b480ed5 100644 (file)
@@ -13,9 +13,6 @@
     :license: BSD.
 """
 import operator
-from UserDict import UserDict, DictMixin
-from UserList import UserList
-from sets import Set, ImmutableSet
 from types import FunctionType, MethodType, TracebackType, CodeType, \
      FrameType, GeneratorType
 from jinja2.runtime import Undefined
@@ -33,19 +30,40 @@ UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict',
 #: unsafe method attributes.  function attributes are unsafe for methods too
 UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
 
-SET_TYPES = (ImmutableSet, Set, set)
-MODIFYING_SET_ATTRIBUTES = set([
-    'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
-    'symmetric_difference_update', 'update'
-])
-
-DICT_TYPES = (UserDict, DictMixin, dict)
-MODIFYING_DICT_ATTRIBUTES = set(['clear', 'pop', 'popitem', 'setdefault',
-                                 'update'])
 
-LIST_TYPES = (UserList, list)
-MODIFYING_LIST_ATTRIBUTES = set(['append', 'reverse', 'insert', 'sort',
-                                 'extend', 'remove'])
+from collections import deque
+from sets import Set, ImmutableSet
+from UserDict import UserDict, DictMixin
+from UserList import UserList
+_mutable_set_types = (ImmutableSet, Set, set)
+_mutable_mapping_types = (UserDict, DictMixin, dict)
+_mutable_sequence_types = (UserList, list)
+
+#: 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):
@@ -86,45 +104,48 @@ def is_internal_attribute(obj, attr):
     False
     """
     if isinstance(obj, FunctionType):
-        return attr in UNSAFE_FUNCTION_ATTRIBUTES
-    if isinstance(obj, MethodType):
-        return attr in UNSAFE_FUNCTION_ATTRIBUTES or \
-               attr in UNSAFE_METHOD_ATTRIBUTES
-    if isinstance(obj, type):
-        return attr == 'mro'
-    if isinstance(obj, (CodeType, TracebackType, FrameType)):
+        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
-    if isinstance(obj, GeneratorType):
-        return attr == 'gi_frame'
+    elif isinstance(obj, GeneratorType):
+        if attr == 'gi_frame':
+            return True
     return attr.startswith('__')
 
 
-def modifies_builtin_mutable(obj, attr):
+def modifies_known_mutable(obj, attr):
     """This function checks if an attribute on a builtin mutable object
-    (list, dict or set) would modify it if called.  It also supports
-    the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.)
+    (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_builtin_mutable({}, "clear")
+    >>> modifies_known_mutable({}, "clear")
     True
-    >>> modifies_builtin_mutable({}, "keys")
+    >>> modifies_known_mutable({}, "keys")
     False
-    >>> modifies_builtin_mutable([], "append")
+    >>> modifies_known_mutable([], "append")
     True
-    >>> modifies_builtin_mutable([], "index")
+    >>> modifies_known_mutable([], "index")
     False
 
     If called with an unsupported object (such as unicode) `False` is
     returned.
 
-    >>> modifies_builtin_mutable("foo", "upper")
+    >>> modifies_known_mutable("foo", "upper")
     False
     """
-    if isinstance(obj, LIST_TYPES):
-        return attr in MODIFYING_LIST_ATTRIBUTES
-    elif isinstance(obj, DICT_TYPES):
-        return attr in MODIFYING_DICT_ATTRIBUTES
-    elif isinstance(obj, SET_TYPES):
-        return attr in MODIFYING_SET_ATTRIBUTES
+    for typespec, unsafe in _mutable_spec:
+        if isinstance(obj, typespec):
+            return attr in unsafe
     return False
 
 
@@ -199,10 +220,10 @@ class SandboxedEnvironment(Environment):
 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_builtin_mutable` function.
+    `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_builtin_mutable(obj, attr)
+        return not modifies_known_mutable(obj, attr)
index 258961f777bf37306cbc635ae3b93b17c5951bc4..e064a259bf379f09279f89497eaedde26f635236 100644 (file)
@@ -253,9 +253,9 @@ class Markup(unicode):
         return NotImplemented
 
     def __mul__(self, num):
-        if not isinstance(num, (int, long)):
-            return NotImplemented
-        return self.__class__(unicode.__mul__(self, num))
+        if isinstance(num, (int, long)):
+            return self.__class__(unicode.__mul__(self, num))
+        return NotImplemented
     __rmul__ = __mul__
 
     def __mod__(self, arg):
@@ -319,17 +319,13 @@ class Markup(unicode):
     def make_wrapper(name):
         orig = getattr(unicode, name)
         def func(self, *args, **kwargs):
-            args = list(args)
-            for idx, arg in enumerate(args):
-                if hasattr(arg, '__html__') or isinstance(arg, basestring):
-                    args[idx] = escape(arg)
-            for name, arg in kwargs.iteritems():
-                if hasattr(arg, '__html__') or isinstance(arg, basestring):
-                    kwargs[name] = escape(arg)
+            args = _escape_argspec(list(args), enumerate(args))
+            _escape_argspec(kwargs, kwargs.iteritems())
             return self.__class__(orig(self, *args, **kwargs))
         func.__name__ = orig.__name__
         func.__doc__ = orig.__doc__
         return func
+
     for method in '__getitem__', '__getslice__', 'capitalize', \
                   'title', 'lower', 'upper', 'replace', 'ljust', \
                   'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
@@ -338,13 +334,24 @@ class Markup(unicode):
 
     # new in python 2.5
     if hasattr(unicode, 'partition'):
-        locals().update(
-            partition=make_wrapper('partition'),
-            rpartition=make_wrapper('rpartition')
-        )
+        partition = make_wrapper('partition'),
+        rpartition = make_wrapper('rpartition')
+
+    # new in python 2.6
+    if hasattr(unicode, 'format'):
+        format = make_wrapper('format')
+
     del method, make_wrapper
 
 
+def _escape_argspec(obj, iterable):
+    """Helper for various string-wrapped functions."""
+    for key, value in iterable:
+        if hasattr(value, '__html__') or isinstance(value, basestring):
+            obj[key] = escape(value)
+    return obj
+
+
 class _MarkupEscapeHelper(object):
     """Helper for Markup.__mod__"""
 
index fd7d3f5bddc288b0dd07a7f9256c7076642c136d..8cc724cf878789d57f4dced011f815fd83f7348d 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -120,7 +120,7 @@ setup(
     ],
     packages=['jinja2'],
     data_files=[
-        ##('docs', list(list_files('docs/_build/html')))
+        ('docs', list(list_files('docs/_build/html')))
     ],
     features={
         'speedups': Feature("optional C speed-enhancements",