moved concat to utils, fixed a few docstrings, fixed memory leak in _speedups.escape

--HG--
branch : trunk
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 @@
 
 
 .. 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 @@
 	/* 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 @@
 		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 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.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 @@
         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 @@
         # 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 @@
            '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 @@
 
 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 @@
     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 @@
 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 @@
     ],
     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 @@
     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 @@
     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 %}')