Merge remote-tracking branch 'ThomasWaldmann/sprint-branch' into sprint-branch
diff --git a/CHANGES b/CHANGES
index 7b204da..e4aa398 100644
--- a/CHANGES
+++ b/CHANGES
@@ -17,6 +17,9 @@
   to be consumed into a list.
 - Python requirement changed: 2.6, 2.7 or >= 3.3 are required now,
   supported by same source code, using the "six" compatibility library.
+- Allow `contextfunction` and other decorators to be applied to `__call__`.
+- Added support for changing from newline to different signs in the `wordwrap`
+  filter.
 
 Version 2.6
 -----------
diff --git a/docs/faq.rst b/docs/faq.rst
index d066bff..5c80d33 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -174,7 +174,7 @@
 Python 2.3 support you either have to use `Jinja 1`_ or other templating
 engines that still support 2.3.
 
-My Macros are overriden by something
+My Macros are overridden by something
 ------------------------------------
 
 In some situations the Jinja scoping appears arbitrary:
diff --git a/docs/templates.rst b/docs/templates.rst
index 790b95e..e121033 100644
--- a/docs/templates.rst
+++ b/docs/templates.rst
@@ -862,7 +862,7 @@
     </dl>
     <p>{{ textarea('comment') }}</p>
 
-Macros and variables starting with one ore more underscores are private and
+Macros and variables starting with one or more underscores are private and
 cannot be imported.
 
 .. versionchanged:: 2.4
diff --git a/docs/tricks.rst b/docs/tricks.rst
index 566575e..5427a3e 100644
--- a/docs/tricks.rst
+++ b/docs/tricks.rst
@@ -75,7 +75,7 @@
     <ul id="navigation">
     {% for href, id, caption in navigation_bar %}
       <li{% if id == active_page %} class="active"{% endif
-      %}><a href="{{ href|e }}">{{ caption|e }}</a>/li>
+      %}><a href="{{ href|e }}">{{ caption|e }}</a></li>
     {% endfor %}
     </ul>
     ...
diff --git a/ext/Vim/jinja.vim b/ext/Vim/jinja.vim
index 894dcc4..503aec6 100644
--- a/ext/Vim/jinja.vim
+++ b/ext/Vim/jinja.vim
@@ -49,14 +49,14 @@
 syn keyword jinjaStatement containedin=jinjaTagBlock contained block skipwhite nextgroup=jinjaBlockName
 
 " Variable Names
-syn match jinjaVariable containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained skipwhite /[a-zA-Z_][a-zA-Z0-9_]*/
+syn match jinjaVariable containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /[a-zA-Z_][a-zA-Z0-9_]*/
 syn keyword jinjaSpecial containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained false true none False True None loop super caller varargs kwargs
 
 " Filters
-syn match jinjaOperator "|" containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained nextgroup=jinjaFilter
-syn match jinjaFilter contained skipwhite /[a-zA-Z_][a-zA-Z0-9_]*/
-syn match jinjaFunction contained skipwhite /[a-zA-Z_][a-zA-Z0-9_]*/
-syn match jinjaBlockName contained skipwhite /[a-zA-Z_][a-zA-Z0-9_]*/
+syn match jinjaOperator "|" containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained skipwhite nextgroup=jinjaFilter
+syn match jinjaFilter contained /[a-zA-Z_][a-zA-Z0-9_]*/
+syn match jinjaFunction contained /[a-zA-Z_][a-zA-Z0-9_]*/
+syn match jinjaBlockName contained /[a-zA-Z_][a-zA-Z0-9_]*/
 
 " Jinja template constants
 syn region jinjaString containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained start=/"/ skip=/\\"/ end=/"/
@@ -73,7 +73,7 @@
 syn region jinjaNested matchgroup=jinjaOperator start="(" end=")" transparent display containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained
 syn region jinjaNested matchgroup=jinjaOperator start="\[" end="\]" transparent display containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained
 syn region jinjaNested matchgroup=jinjaOperator start="{" end="}" transparent display containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained
-syn region jinjaTagBlock matchgroup=jinjaTagDelim start=/{%-\?/ end=/-\?%}/ skipwhite containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaRaw,jinjaString,jinjaNested,jinjaComment
+syn region jinjaTagBlock matchgroup=jinjaTagDelim start=/{%-\?/ end=/-\?%}/ containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaRaw,jinjaString,jinjaNested,jinjaComment
 
 syn region jinjaVarBlock matchgroup=jinjaVarDelim start=/{{-\?/ end=/-\?}}/ containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaRaw,jinjaString,jinjaNested,jinjaComment
 
@@ -86,10 +86,10 @@
 " Block start keywords.  A bit tricker.  We only highlight at the start of a
 " tag block and only if the name is not followed by a comma or equals sign
 " which usually means that we have to deal with an assignment.
-syn match jinjaStatement containedin=jinjaTagBlock contained skipwhite /\({%-\?\s*\)\@<=\<[a-zA-Z_][a-zA-Z0-9_]*\>\(\s*[,=]\)\@!/
+syn match jinjaStatement containedin=jinjaTagBlock contained /\({%-\?\s*\)\@<=\<[a-zA-Z_][a-zA-Z0-9_]*\>\(\s*[,=]\)\@!/
 
 " and context modifiers
-syn match jinjaStatement containedin=jinjaTagBlock contained /\<with\(out\)\?\s\+context\>/ skipwhite
+syn match jinjaStatement containedin=jinjaTagBlock contained /\<with\(out\)\?\s\+context\>/
 
 
 " Define the default highlighting.
diff --git a/ext/django2jinja/django2jinja.py b/ext/django2jinja/django2jinja.py
index 6d9e76c..ad9627f 100644
--- a/ext/django2jinja/django2jinja.py
+++ b/ext/django2jinja/django2jinja.py
@@ -609,7 +609,7 @@
 @node(core_tags.WithNode)
 def with_block(writer, node):
     writer.warn('with block expanded into set statement.  This could cause '
-                'variables following that block to be overriden.', node)
+                'variables following that block to be overridden.', node)
     writer.start_block()
     writer.write('set %s = ' % node.name)
     writer.node(node.var)
diff --git a/jinja2/_compat.py b/jinja2/_compat.py
index a738757..6318f0b 100644
--- a/jinja2/_compat.py
+++ b/jinja2/_compat.py
@@ -17,3 +17,8 @@
     unichr = unichr  # py2
 except NameError:
     unichr = chr  # py3
+
+try:
+    range_type = xrange
+except NameError:
+    range_type = range
diff --git a/jinja2/_markupsafe/__init__.py b/jinja2/_markupsafe/__init__.py
index 49775f6..5ffb57d 100644
--- a/jinja2/_markupsafe/__init__.py
+++ b/jinja2/_markupsafe/__init__.py
@@ -11,7 +11,6 @@
 import re
 import six
 from six.moves import map
-from six.moves import zip
 from jinja2._compat import unichr
 
 __all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent']
diff --git a/jinja2/bccache.py b/jinja2/bccache.py
index 6638bbb..b7b637f 100644
--- a/jinja2/bccache.py
+++ b/jinja2/bccache.py
@@ -19,6 +19,7 @@
 import marshal
 import tempfile
 from six.moves import cPickle as pickle
+from six import BytesIO
 import fnmatch
 try:
     from hashlib import sha1
@@ -28,12 +29,10 @@
 
 
 # marshal works better on 3.x, one hack less required
-if sys.version_info[0] > 3:
-    from io import BytesIO
+if sys.version_info[0] >= 3:
     marshal_dump = marshal.dump
     marshal_load = marshal.load
 else:
-    from cStringIO import StringIO as BytesIO
 
     def marshal_dump(code, f):
         if isinstance(f, file):
diff --git a/jinja2/compiler.py b/jinja2/compiler.py
index 1b1b5a7..ec66908 100644
--- a/jinja2/compiler.py
+++ b/jinja2/compiler.py
@@ -15,10 +15,9 @@
 from jinja2.visitor import NodeVisitor
 from jinja2.exceptions import TemplateAssertionError
 from jinja2.utils import Markup, concat, escape, is_python_keyword
+from jinaj2._compat import range_type
 import six
-from six.moves import cStringIO as StringIO
-from six.moves import map, zip
-from six.moves import xrange
+from six.moves import cStringIO as StringIO, map
 
 
 operators = {
@@ -32,14 +31,6 @@
     'notin':    'not in'
 }
 
-try:
-    exec('(0 if 0 else 0)')
-except SyntaxError:
-    have_condexpr = False
-else:
-    have_condexpr = True
-
-
 # what method to iterate over items do we want to use for dict iteration
 # in generated code?  on 2.x let's go with iteritems, on 3.x with items
 if hasattr(dict, 'iteritems'):
@@ -76,7 +67,8 @@
     """Does the node have a safe representation?"""
     if value is None or value is NotImplemented or value is Ellipsis:
         return True
-    if isinstance(value, (bool, int, float, complex, xrange, Markup) + six.string_types):
+    if isinstance(value, (bool, int, float, complex, range_type,
+            Markup) + six.string_types):
         return True
     if isinstance(value, (tuple, list, set, frozenset)):
         for item in value:
@@ -669,16 +661,16 @@
         # it without aliasing all the variables.
         # this could be fixed in Python 3 where we have the nonlocal
         # keyword or if we switch to bytecode generation
-        overriden_closure_vars = (
+        overridden_closure_vars = (
             func_frame.identifiers.undeclared &
             func_frame.identifiers.declared &
             (func_frame.identifiers.declared_locally |
              func_frame.identifiers.declared_parameter)
         )
-        if overriden_closure_vars:
+        if overridden_closure_vars:
             self.fail('It\'s not possible to set and access variables '
                       'derived from an outer scope! (affects: %s)' %
-                      ', '.join(sorted(overriden_closure_vars)), node.lineno)
+                      ', '.join(sorted(overridden_closure_vars)), node.lineno)
 
         # remove variables from a closure from the frame's undeclared
         # identifiers.
@@ -1561,22 +1553,13 @@
                        'expression on %s evaluated to false and '
                        'no else section was defined.' % self.position(node)))
 
-        if not have_condexpr:
-            self.write('((')
-            self.visit(node.test, frame)
-            self.write(') and (')
-            self.visit(node.expr1, frame)
-            self.write(',) or (')
-            write_expr2()
-            self.write(',))[0]')
-        else:
-            self.write('(')
-            self.visit(node.expr1, frame)
-            self.write(' if ')
-            self.visit(node.test, frame)
-            self.write(' else ')
-            write_expr2()
-            self.write(')')
+        self.write('(')
+        self.visit(node.expr1, frame)
+        self.write(' if ')
+        self.visit(node.test, frame)
+        self.write(' else ')
+        write_expr2()
+        self.write(')')
 
     def visit_Call(self, node, frame, forward_caller=False):
         if self.environment.sandboxed:
diff --git a/jinja2/environment.py b/jinja2/environment.py
index d19ae15..58573a2 100644
--- a/jinja2/environment.py
+++ b/jinja2/environment.py
@@ -1069,7 +1069,7 @@
         """
         close = False
         if isinstance(fp, six.string_types):
-            fp = file(fp, 'w')
+            fp = open(fp, encoding is None and 'w' or 'wb')
             close = True
         try:
             if encoding is not None:
diff --git a/jinja2/exceptions.py b/jinja2/exceptions.py
index f38d347..813b7fb 100644
--- a/jinja2/exceptions.py
+++ b/jinja2/exceptions.py
@@ -9,7 +9,7 @@
     :license: BSD, see LICENSE for more details.
 """
 import six
-from six.moves import map, zip
+from six.moves import map
 
 
 class TemplateError(Exception):
diff --git a/jinja2/ext.py b/jinja2/ext.py
index 0652a34..495e643 100644
--- a/jinja2/ext.py
+++ b/jinja2/ext.py
@@ -10,11 +10,10 @@
     :copyright: (c) 2010 by the Jinja Team.
     :license: BSD.
 """
-from collections import deque
 from jinja2 import nodes
 from jinja2.defaults import *
 from jinja2.environment import Environment
-from jinja2.runtime import Undefined, concat
+from jinja2.runtime import concat
 from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError
 from jinja2.utils import contextfunction, import_string, Markup
 import six
diff --git a/jinja2/filters.py b/jinja2/filters.py
index d137fc5..9da1195 100644
--- a/jinja2/filters.py
+++ b/jinja2/filters.py
@@ -18,7 +18,7 @@
 from jinja2.runtime import Undefined
 from jinja2.exceptions import FilterArgumentError
 import six
-from six.moves import map, zip
+from six.moves import map
 
 
 _word_re = re.compile(r'\w+(?u)')
@@ -472,15 +472,23 @@
     return u' '.join(result)
 
 @environmentfilter
-def do_wordwrap(environment, s, width=79, break_long_words=True):
+def do_wordwrap(environment, s, width=79, break_long_words=True,
+                wrapstring=None):
     """
     Return a copy of the string passed to the filter wrapped after
     ``79`` characters.  You can override this default using the first
     parameter.  If you set the second parameter to `false` Jinja will not
-    split words apart if they are longer than `width`.
+    split words apart if they are longer than `width`. By default, the newlines
+    will be the default newlines for the environment, but this can be changed
+    using the wrapstring keyword argument.
+
+    .. versionadded:: 2.7
+       Added support for the `wrapstring` parameter.
     """
+    if not wrapstring:
+        wrapstring = environment.newline_sequence
     import textwrap
-    return environment.newline_sequence.join(textwrap.wrap(s, width=width, expand_tabs=False,
+    return wrapstring.join(textwrap.wrap(s, width=width, expand_tabs=False,
                                    replace_whitespace=False,
                                    break_long_words=break_long_words))
 
diff --git a/jinja2/loaders.py b/jinja2/loaders.py
index 809dad0..08fe872 100644
--- a/jinja2/loaders.py
+++ b/jinja2/loaders.py
@@ -19,7 +19,7 @@
 except ImportError:
     from sha import new as sha1
 from jinja2.exceptions import TemplateNotFound
-from jinja2.utils import LRUCache, open_if_exists, internalcode
+from jinja2.utils import open_if_exists, internalcode
 
 
 def split_template_path(template):
diff --git a/jinja2/parser.py b/jinja2/parser.py
index 520e88f..953027c 100644
--- a/jinja2/parser.py
+++ b/jinja2/parser.py
@@ -12,7 +12,7 @@
 from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError
 from jinja2.lexer import describe_token, describe_token_expr
 import six
-from six.moves import map, zip
+from six.moves import map
 
 
 #: statements that callinto 
diff --git a/jinja2/runtime.py b/jinja2/runtime.py
index a11095e..9742ece 100644
--- a/jinja2/runtime.py
+++ b/jinja2/runtime.py
@@ -10,8 +10,8 @@
 """
 from itertools import chain
 from jinja2.nodes import EvalContext, _context_function_types
-from jinja2.utils import Markup, partial, soft_unicode, escape, missing, \
-     concat, internalcode, object_type_repr
+from jinja2.utils import Markup, soft_unicode, escape, missing, concat, \
+     internalcode, object_type_repr
 from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
      TemplateNotFound
 import six
@@ -176,6 +176,17 @@
         """
         if __debug__:
             __traceback_hide__ = True
+
+        # Allow callable classes to take a context
+        if hasattr(__obj, '__call__'):
+            fn = __obj.__call__
+            for fn_type in ('contextfunction',
+                            'evalcontextfunction',
+                            'environmentfunction'):
+                if hasattr(fn, fn_type):
+                    __obj = fn
+                    break
+
         if isinstance(__obj, _context_function_types):
             if getattr(__obj, 'contextfunction', 0):
                 args = (__self,) + args
diff --git a/jinja2/sandbox.py b/jinja2/sandbox.py
index b880250..ed145d5 100644
--- a/jinja2/sandbox.py
+++ b/jinja2/sandbox.py
@@ -115,7 +115,7 @@
     """Test if the attribute given is an internal python attribute.  For
     example this function returns `True` for the `func_code` attribute of
     python objects.  This is useful if the environment method
-    :meth:`~SandboxedEnvironment.is_safe_attribute` is overriden.
+    :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden.
 
     >>> from jinja2.sandbox import is_internal_attribute
     >>> is_internal_attribute(lambda: None, "func_code")
diff --git a/jinja2/testsuite/debug.py b/jinja2/testsuite/debug.py
index 17ac991..2588a83 100644
--- a/jinja2/testsuite/debug.py
+++ b/jinja2/testsuite/debug.py
@@ -8,7 +8,6 @@
     :copyright: (c) 2010 by the Jinja Team.
     :license: BSD, see LICENSE for more details.
 """
-import sys
 import unittest
 
 from jinja2.testsuite import JinjaTestCase, filesystem_loader
diff --git a/jinja2/testsuite/filters.py b/jinja2/testsuite/filters.py
index e74881d..7f6b314 100644
--- a/jinja2/testsuite/filters.py
+++ b/jinja2/testsuite/filters.py
@@ -13,7 +13,7 @@
 
 from jinja2 import Markup, Environment
 import six
-from six.moves import map, zip
+from six.moves import map
 
 env = Environment()
 
diff --git a/jinja2/testsuite/regression.py b/jinja2/testsuite/regression.py
index bac4d60..4198259 100644
--- a/jinja2/testsuite/regression.py
+++ b/jinja2/testsuite/regression.py
@@ -15,8 +15,6 @@
 from jinja2 import Template, Environment, DictLoader, TemplateSyntaxError, \
      TemplateNotFound, PrefixLoader
 import six
-from six.moves import map
-from six.moves import zip
 
 env = Environment()
 
@@ -250,6 +248,19 @@
         else:
             assert False, 'expected error here'
 
+    def test_contextfunction_callable_classes(self):
+        from jinja2.utils import contextfunction
+        class CallableClass(object):
+            @contextfunction
+            def __call__(self, ctx):
+                return ctx.resolve('hello')
+
+        tpl = Template("""{{ callableclass() }}""")
+        output = tpl.render(callableclass = CallableClass(), hello = 'TEST')
+        expected = 'TEST'
+
+        self.assert_equal(output, expected)
+
 
 def suite():
     suite = unittest.TestSuite()