python 3 port: automated changes by a slightly modified python-modernize

(replacing unicode with six.text_type, but not replacing u"" by six.u(""))
diff --git a/jinja2/_markupsafe/__init__.py b/jinja2/_markupsafe/__init__.py
index ec7bd57..6aae11a 100644
--- a/jinja2/_markupsafe/__init__.py
+++ b/jinja2/_markupsafe/__init__.py
@@ -10,6 +10,9 @@
 """
 import re
 from itertools import imap
+import six
+from six.moves import map
+from six.moves import zip
 
 
 __all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent']
@@ -76,12 +79,12 @@
 
     def __add__(self, other):
         if hasattr(other, '__html__') or isinstance(other, basestring):
-            return self.__class__(unicode(self) + unicode(escape(other)))
+            return self.__class__(six.text_type(self) + six.text_type(escape(other)))
         return NotImplemented
 
     def __radd__(self, other):
         if hasattr(other, '__html__') or isinstance(other, basestring):
-            return self.__class__(unicode(escape(other)) + unicode(self))
+            return self.__class__(six.text_type(escape(other)) + six.text_type(self))
         return NotImplemented
 
     def __mul__(self, num):
@@ -139,7 +142,7 @@
             except ValueError:
                 pass
             return u''
-        return _entity_re.sub(handle_match, unicode(self))
+        return _entity_re.sub(handle_match, six.text_type(self))
 
     def striptags(self):
         r"""Unescape markup into an unicode string and strip all tags.  This
@@ -167,7 +170,7 @@
         orig = getattr(unicode, name)
         def func(self, *args, **kwargs):
             args = _escape_argspec(list(args), enumerate(args))
-            _escape_argspec(kwargs, kwargs.iteritems())
+            _escape_argspec(kwargs, six.iteritems(kwargs))
             return self.__class__(orig(self, *args, **kwargs))
         func.__name__ = orig.__name__
         func.__doc__ = orig.__doc__
@@ -211,7 +214,7 @@
 
     __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
     __str__ = lambda s: str(escape(s.obj))
-    __unicode__ = lambda s: unicode(escape(s.obj))
+    __unicode__ = lambda s: six.text_type(escape(s.obj))
     __repr__ = lambda s: str(escape(repr(s.obj)))
     __int__ = lambda s: int(s.obj)
     __float__ = lambda s: float(s.obj)
diff --git a/jinja2/_markupsafe/_bundle.py b/jinja2/_markupsafe/_bundle.py
index e694faf..d23730f 100644
--- a/jinja2/_markupsafe/_bundle.py
+++ b/jinja2/_markupsafe/_bundle.py
@@ -10,6 +10,7 @@
     :copyright: Copyright 2010 by the Jinja team, see AUTHORS.
     :license: BSD, see LICENSE for details.
 """
+from __future__ import print_function
 import sys
 import os
 import re
@@ -25,7 +26,7 @@
 
 def main():
     if len(sys.argv) != 2:
-        print 'error: only argument is path to markupsafe'
+        print('error: only argument is path to markupsafe')
         sys.exit(1)
     basedir = os.path.dirname(__file__)
     markupdir = sys.argv[1]
diff --git a/jinja2/_markupsafe/_native.py b/jinja2/_markupsafe/_native.py
index 7b95828..97065bb 100644
--- a/jinja2/_markupsafe/_native.py
+++ b/jinja2/_markupsafe/_native.py
@@ -9,6 +9,7 @@
     :license: BSD, see LICENSE for more details.
 """
 from jinja2._markupsafe import Markup
+import six
 
 
 def escape(s):
@@ -18,7 +19,7 @@
     """
     if hasattr(s, '__html__'):
         return s.__html__()
-    return Markup(unicode(s)
+    return Markup(six.text_type(s)
         .replace('&', '&')
         .replace('>', '>')
         .replace('<', '&lt;')
@@ -41,5 +42,5 @@
     string is not converted back to unicode.
     """
     if not isinstance(s, unicode):
-        s = unicode(s)
+        s = six.text_type(s)
     return s
diff --git a/jinja2/_markupsafe/tests.py b/jinja2/_markupsafe/tests.py
index c1ce394..449df49 100644
--- a/jinja2/_markupsafe/tests.py
+++ b/jinja2/_markupsafe/tests.py
@@ -1,6 +1,7 @@
 import gc
 import unittest
 from jinja2._markupsafe import Markup, escape, escape_silent
+import six
 
 
 class MarkupTestCase(unittest.TestCase):
@@ -9,7 +10,7 @@
         # adding two strings should escape the unsafe one
         unsafe = '<script type="application/x-some-script">alert("foo");</script>'
         safe = Markup('<em>username</em>')
-        assert unsafe + safe == unicode(escape(unsafe)) + unicode(safe)
+        assert unsafe + safe == six.text_type(escape(unsafe)) + six.text_type(safe)
 
         # string interpolations are safe to use too
         assert Markup('<em>%s</em>') % '<bad user>' == \
@@ -55,8 +56,8 @@
 
     def test_markup_leaks(self):
         counts = set()
-        for count in xrange(20):
-            for item in xrange(1000):
+        for count in range(20):
+            for item in range(1000):
                 escape("foo")
                 escape("<foo>")
                 escape(u"foo")
diff --git a/jinja2/compiler.py b/jinja2/compiler.py
index b21cb38..35f7d02 100644
--- a/jinja2/compiler.py
+++ b/jinja2/compiler.py
@@ -16,6 +16,9 @@
 from jinja2.visitor import NodeVisitor
 from jinja2.exceptions import TemplateAssertionError
 from jinja2.utils import Markup, concat, escape, is_python_keyword, next
+import six
+from six.moves import map
+from six.moves import zip
 
 
 operators = {
@@ -30,7 +33,7 @@
 }
 
 try:
-    exec '(0 if 0 else 0)'
+    exec('(0 if 0 else 0)')
 except SyntaxError:
     have_condexpr = False
 else:
@@ -51,7 +54,7 @@
     def f():
         if 0: dummy(x)
     return f
-unoptimize_before_dead_code = bool(unoptimize_before_dead_code().func_closure)
+unoptimize_before_dead_code = bool(unoptimize_before_dead_code().__closure__)
 
 
 def generate(node, environment, name, filename, stream=None,
@@ -78,7 +81,7 @@
                 return False
         return True
     elif isinstance(value, dict):
-        for key, value in value.iteritems():
+        for key, value in six.iteritems(value):
             if not has_safe_repr(key):
                 return False
             if not has_safe_repr(value):
@@ -542,7 +545,7 @@
                 self.write(', ')
                 self.visit(kwarg, frame)
             if extra_kwargs is not None:
-                for key, value in extra_kwargs.iteritems():
+                for key, value in six.iteritems(extra_kwargs):
                     self.write(', %s=%s' % (key, value))
         if node.dyn_args:
             self.write(', *')
@@ -558,7 +561,7 @@
                 self.visit(kwarg.value, frame)
                 self.write(', ')
             if extra_kwargs is not None:
-                for key, value in extra_kwargs.iteritems():
+                for key, value in six.iteritems(extra_kwargs):
                     self.write('%r: %s, ' % (key, value))
             if node.dyn_kwargs is not None:
                 self.write('}, **')
@@ -625,7 +628,7 @@
 
     def pop_scope(self, aliases, frame):
         """Restore all aliases and delete unused variables."""
-        for name, alias in aliases.iteritems():
+        for name, alias in six.iteritems(aliases):
             self.writeline('l_%s = %s' % (name, alias))
         to_delete = set()
         for name in frame.identifiers.declared_locally:
@@ -827,7 +830,7 @@
             self.outdent(2 + (not self.has_known_extends))
 
         # at this point we now have the blocks collected and can visit them too.
-        for name, block in self.blocks.iteritems():
+        for name, block in six.iteritems(self.blocks):
             block_frame = Frame(eval_ctx)
             block_frame.inspect(block.body)
             block_frame.block = name
@@ -1216,7 +1219,7 @@
             return
 
         if self.environment.finalize:
-            finalize = lambda x: unicode(self.environment.finalize(x))
+            finalize = lambda x: six.text_type(self.environment.finalize(x))
         else:
             finalize = unicode
 
diff --git a/jinja2/debug.py b/jinja2/debug.py
index 3ac4041..c653e36 100644
--- a/jinja2/debug.py
+++ b/jinja2/debug.py
@@ -15,6 +15,7 @@
 from types import TracebackType
 from jinja2.utils import CodeType, missing, internal_code
 from jinja2.exceptions import TemplateSyntaxError
+import six
 
 # on pypy we can take advantage of transparent proxies
 try:
@@ -25,7 +26,7 @@
 
 # how does the raise helper look like?
 try:
-    exec "raise TypeError, 'foo'"
+    exec("raise TypeError, 'foo'")
 except SyntaxError:
     raise_helper = 'raise __jinja_exception__[1]'
 except TypeError:
@@ -158,7 +159,7 @@
     frames = []
 
     # skip some internal frames if wanted
-    for x in xrange(initial_skip):
+    for x in range(initial_skip):
         if tb is not None:
             tb = tb.tb_next
     initial_tb = tb
@@ -206,7 +207,7 @@
             locals = ctx.get_all()
         else:
             locals = {}
-        for name, value in real_locals.iteritems():
+        for name, value in six.iteritems(real_locals):
             if name.startswith('l_') and value is not missing:
                 locals[name[2:]] = value
 
@@ -254,7 +255,7 @@
 
     # execute the code and catch the new traceback
     try:
-        exec code in globals, locals
+        exec(code, globals, locals)
     except:
         exc_info = sys.exc_info()
         new_tb = exc_info[2].tb_next
diff --git a/jinja2/environment.py b/jinja2/environment.py
index 1b5dc40..71f022f 100644
--- a/jinja2/environment.py
+++ b/jinja2/environment.py
@@ -21,6 +21,11 @@
      TemplatesNotFound
 from jinja2.utils import import_string, LRUCache, Markup, missing, \
      concat, consume, internalcode, _encode_filename
+import six
+from functools import reduce
+from six.moves import filter
+from six.moves import map
+from six.moves import zip
 
 
 # for direct template usage we have up to ten living environments
@@ -292,7 +297,7 @@
         yet.  This is used by :ref:`extensions <writing-extensions>` to register
         callbacks and configuration values without breaking inheritance.
         """
-        for key, value in attributes.iteritems():
+        for key, value in six.iteritems(attributes):
             if not hasattr(self, key):
                 setattr(self, key, value)
 
@@ -323,7 +328,7 @@
         rv.overlayed = True
         rv.linked_to = self
 
-        for key, value in args.iteritems():
+        for key, value in six.iteritems(args):
             if value is not missing:
                 setattr(rv, key, value)
 
@@ -333,7 +338,7 @@
             rv.cache = copy_cache(self.cache)
 
         rv.extensions = {}
-        for key, value in self.extensions.iteritems():
+        for key, value in six.iteritems(self.extensions):
             rv.extensions[key] = value.bind(rv)
         if extensions is not missing:
             rv.extensions.update(load_extensions(rv, extensions))
@@ -407,7 +412,7 @@
         of the extensions to be applied you have to filter source through
         the :meth:`preprocess` method.
         """
-        source = unicode(source)
+        source = six.text_type(source)
         try:
             return self.lexer.tokeniter(source, name, filename)
         except TemplateSyntaxError:
@@ -420,7 +425,7 @@
         because there you usually only want the actual source tokenized.
         """
         return reduce(lambda s, e: e.preprocess(s, name, filename),
-                      self.iter_extensions(), unicode(source))
+                      self.iter_extensions(), six.text_type(source))
 
     def _tokenize(self, source, name, filename=None, state=None):
         """Called by the parser to do the preprocessing and filtering
@@ -577,7 +582,7 @@
         def write_file(filename, data, mode):
             if zip:
                 info = ZipInfo(filename)
-                info.external_attr = 0755 << 16L
+                info.external_attr = 0o755 << 16
                 zip_file.writestr(info, data)
             else:
                 f = open(os.path.join(target, filename), mode)
@@ -601,7 +606,7 @@
                 source, filename, _ = self.loader.get_source(self, name)
                 try:
                     code = self.compile(source, name, filename, True, True)
-                except TemplateSyntaxError, e:
+                except TemplateSyntaxError as e:
                     if not ignore_errors:
                         raise
                     log_function('Could not compile "%s": %s' % (name, e))
@@ -843,7 +848,7 @@
             'environment':  environment,
             '__file__':     code.co_filename
         }
-        exec code in namespace
+        exec(code, namespace)
         rv = cls._from_namespace(environment, namespace, globals)
         rv._uptodate = uptodate
         return rv
@@ -1003,7 +1008,7 @@
         return Markup(concat(self._body_stream))
 
     def __str__(self):
-        return unicode(self).encode('utf-8')
+        return six.text_type(self).encode('utf-8')
 
     # unicode goes after __str__ because we configured 2to3 to rename
     # __unicode__ to __str__.  because the 2to3 tree is not designed to
diff --git a/jinja2/exceptions.py b/jinja2/exceptions.py
index 841aabb..9002521 100644
--- a/jinja2/exceptions.py
+++ b/jinja2/exceptions.py
@@ -8,6 +8,9 @@
     :copyright: (c) 2010 by the Jinja Team.
     :license: BSD, see LICENSE for more details.
 """
+import six
+from six.moves import map
+from six.moves import zip
 
 
 class TemplateError(Exception):
@@ -15,7 +18,7 @@
 
     def __init__(self, message=None):
         if message is not None:
-            message = unicode(message).encode('utf-8')
+            message = six.text_type(message).encode('utf-8')
         Exception.__init__(self, message)
 
     @property
@@ -83,7 +86,7 @@
         self.translated = False
 
     def __str__(self):
-        return unicode(self).encode('utf-8')
+        return six.text_type(self).encode('utf-8')
 
     # unicode goes after __str__ because we configured 2to3 to rename
     # __unicode__ to __str__.  because the 2to3 tree is not designed to
diff --git a/jinja2/ext.py b/jinja2/ext.py
index 206756f..984a4d9 100644
--- a/jinja2/ext.py
+++ b/jinja2/ext.py
@@ -17,6 +17,7 @@
 from jinja2.runtime import Undefined, concat
 from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError
 from jinja2.utils import contextfunction, import_string, Markup, next
+import six
 
 
 # the only real useful gettext functions for a Jinja template.  Note
@@ -354,7 +355,7 @@
         # enough to handle the variable expansion and autoescape
         # handling itself
         if self.environment.newstyle_gettext:
-            for key, value in variables.iteritems():
+            for key, value in six.iteritems(variables):
                 # the function adds that later anyways in case num was
                 # called num, so just skip it.
                 if num_called_num and key == 'num':
@@ -601,7 +602,7 @@
     try:
         node = environment.parse(source)
         tokens = list(environment.lex(environment.preprocess(source)))
-    except TemplateSyntaxError, e:
+    except TemplateSyntaxError as e:
         if not silent:
             raise
         # skip templates with syntax errors
diff --git a/jinja2/filters.py b/jinja2/filters.py
index 47f0768..43387ae 100644
--- a/jinja2/filters.py
+++ b/jinja2/filters.py
@@ -17,6 +17,9 @@
      unicode_urlencode
 from jinja2.runtime import Undefined
 from jinja2.exceptions import FilterArgumentError
+import six
+from six.moves import map
+from six.moves import zip
 
 
 _word_re = re.compile(r'\w+(?u)')
@@ -68,7 +71,7 @@
     """Enforce HTML escaping.  This will probably double escape variables."""
     if hasattr(value, '__html__'):
         value = value.__html__()
-    return escape(unicode(value))
+    return escape(six.text_type(value))
 
 
 def do_urlencode(value):
@@ -79,7 +82,7 @@
     """
     itemiter = None
     if isinstance(value, dict):
-        itemiter = value.iteritems()
+        itemiter = six.iteritems(value)
     elif not isinstance(value, basestring):
         try:
             itemiter = iter(value)
@@ -110,7 +113,7 @@
     if count is None:
         count = -1
     if not eval_ctx.autoescape:
-        return unicode(s).replace(unicode(old), unicode(new), count)
+        return six.text_type(s).replace(six.text_type(old), six.text_type(new), count)
     if hasattr(old, '__html__') or hasattr(new, '__html__') and \
        not hasattr(s, '__html__'):
         s = escape(s)
@@ -155,7 +158,7 @@
     """
     rv = u' '.join(
         u'%s="%s"' % (escape(key), escape(value))
-        for key, value in d.iteritems()
+        for key, value in six.iteritems(d)
         if value is not None and not isinstance(value, Undefined)
     )
     if autospace and rv:
@@ -309,7 +312,7 @@
 
     # no automatic escaping?  joining is a lot eaiser then
     if not eval_ctx.autoescape:
-        return unicode(d).join(imap(unicode, value))
+        return six.text_type(d).join(imap(unicode, value))
 
     # if the delimiter doesn't have an html representation we check
     # if any of the items has.  If yes we do a coercion to Markup
@@ -320,11 +323,11 @@
             if hasattr(item, '__html__'):
                 do_escape = True
             else:
-                value[idx] = unicode(item)
+                value[idx] = six.text_type(item)
         if do_escape:
             d = escape(d)
         else:
-            d = unicode(d)
+            d = six.text_type(d)
         return d.join(value)
 
     # no html involved, to normal joining
@@ -333,14 +336,14 @@
 
 def do_center(value, width=80):
     """Centers the value in a field of a given width."""
-    return unicode(value).center(width)
+    return six.text_type(value).center(width)
 
 
 @environmentfilter
 def do_first(environment, seq):
     """Return the first item of a sequence."""
     try:
-        return iter(seq).next()
+        return six.advance_iterator(iter(seq))
     except StopIteration:
         return environment.undefined('No first item, sequence was empty.')
 
@@ -349,7 +352,7 @@
 def do_last(environment, seq):
     """Return the last item of a sequence."""
     try:
-        return iter(reversed(seq)).next()
+        return six.advance_iterator(iter(reversed(seq)))
     except StopIteration:
         return environment.undefined('No last item, sequence was empty.')
 
@@ -539,7 +542,7 @@
     """
     if hasattr(value, '__html__'):
         value = value.__html__()
-    return Markup(unicode(value)).striptags()
+    return Markup(six.text_type(value)).striptags()
 
 
 def do_slice(value, slices, fill_with=None):
@@ -567,7 +570,7 @@
     items_per_slice = length // slices
     slices_with_extra = length % slices
     offset = 0
-    for slice_number in xrange(slices):
+    for slice_number in range(slices):
         start = offset + slice_number * items_per_slice
         if slice_number < slices_with_extra:
             offset += 1
@@ -692,7 +695,8 @@
     grouper = property(itemgetter(0))
     list = property(itemgetter(1))
 
-    def __new__(cls, (key, value)):
+    def __new__(cls, xxx_todo_changeme):
+        (key, value) = xxx_todo_changeme
         return tuple.__new__(cls, (key, list(value)))
 
 
@@ -733,7 +737,7 @@
 
 def do_mark_unsafe(value):
     """Mark a value as unsafe.  This is the reverse operation for :func:`safe`."""
-    return unicode(value)
+    return six.text_type(value)
 
 
 def do_reverse(value):
diff --git a/jinja2/lexer.py b/jinja2/lexer.py
index 69865d0..5d24718 100644
--- a/jinja2/lexer.py
+++ b/jinja2/lexer.py
@@ -19,6 +19,7 @@
 from collections import deque
 from jinja2.exceptions import TemplateSyntaxError
 from jinja2.utils import LRUCache, next
+import six
 
 
 # cache for the lexers. Exists in order to be able to have multiple
@@ -126,7 +127,7 @@
     ';':            TOKEN_SEMICOLON
 }
 
-reverse_operators = dict([(v, k) for k, v in operators.iteritems()])
+reverse_operators = dict([(v, k) for k, v in six.iteritems(operators)])
 assert len(operators) == len(reverse_operators), 'operators dropped'
 operator_re = re.compile('(%s)' % '|'.join(re.escape(x) for x in
                          sorted(operators, key=lambda x: -len(x))))
@@ -319,7 +320,7 @@
 
     def skip(self, n=1):
         """Got n tokens ahead."""
-        for x in xrange(n):
+        for x in range(n):
             next(self)
 
     def next_if(self, expr):
@@ -526,7 +527,7 @@
                     value = self._normalize_newlines(value[1:-1]) \
                         .encode('ascii', 'backslashreplace') \
                         .decode('unicode-escape')
-                except Exception, e:
+                except Exception as e:
                     msg = str(e).split(':')[-1].strip()
                     raise TemplateSyntaxError(msg, lineno, name, filename)
                 # if we can express it as bytestring (ascii only)
@@ -549,7 +550,7 @@
         """This method tokenizes the text and returns the tokens in a
         generator.  Use this method if you just want to tokenize a template.
         """
-        source = '\n'.join(unicode(source).splitlines())
+        source = '\n'.join(six.text_type(source).splitlines())
         pos = 0
         lineno = 1
         stack = ['root']
@@ -590,7 +591,7 @@
                         # yield for the current token the first named
                         # group that matched
                         elif token == '#bygroup':
-                            for key, value in m.groupdict().iteritems():
+                            for key, value in six.iteritems(m.groupdict()):
                                 if value is not None:
                                     yield lineno, key, value
                                     lineno += value.count('\n')
@@ -647,7 +648,7 @@
                         stack.pop()
                     # resolve the new state by group checking
                     elif new_state == '#bygroup':
-                        for key, value in m.groupdict().iteritems():
+                        for key, value in six.iteritems(m.groupdict()):
                             if value is not None:
                                 stack.append(key)
                                 break
diff --git a/jinja2/loaders.py b/jinja2/loaders.py
index c90bbe7..a274922 100644
--- a/jinja2/loaders.py
+++ b/jinja2/loaders.py
@@ -13,6 +13,7 @@
 import weakref
 from types import ModuleType
 from os import path
+import six
 try:
     from hashlib import sha1
 except ImportError:
@@ -359,7 +360,7 @@
 
     def list_templates(self):
         result = []
-        for prefix, loader in self.mapping.iteritems():
+        for prefix, loader in six.iteritems(self.mapping):
             for template in loader.list_templates():
                 result.append(prefix + self.delimiter + template)
         return result
diff --git a/jinja2/nodes.py b/jinja2/nodes.py
index f9da1da..5651a41 100644
--- a/jinja2/nodes.py
+++ b/jinja2/nodes.py
@@ -16,6 +16,7 @@
 from itertools import chain, izip
 from collections import deque
 from jinja2.utils import Markup, MethodType, FunctionType
+import six
 
 
 #: the types we support for context functions
@@ -142,7 +143,7 @@
             setattr(self, attr, attributes.pop(attr, None))
         if attributes:
             raise TypeError('unknown attribute %r' %
-                            iter(attributes).next())
+                            six.advance_iterator(iter(attributes)))
 
     def iter_fields(self, exclude=None, only=None):
         """This method iterates over all fields that are defined and yields
@@ -440,7 +441,7 @@
         constant value in the generated code, otherwise it will raise
         an `Impossible` exception.
         """
-        from compiler import has_safe_repr
+        from .compiler import has_safe_repr
         if not has_safe_repr(value):
             raise Impossible()
         return cls(value, lineno=lineno, environment=environment)
@@ -687,7 +688,7 @@
 
     def as_const(self, eval_ctx=None):
         eval_ctx = get_eval_context(self, eval_ctx)
-        return ''.join(unicode(x.as_const(eval_ctx)) for x in self.nodes)
+        return ''.join(six.text_type(x.as_const(eval_ctx)) for x in self.nodes)
 
 
 class Compare(Expr):
diff --git a/jinja2/parser.py b/jinja2/parser.py
index 2125338..b4b06fe 100644
--- a/jinja2/parser.py
+++ b/jinja2/parser.py
@@ -12,6 +12,8 @@
 from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError
 from jinja2.utils import next
 from jinja2.lexer import describe_token, describe_token_expr
+from six.moves import map
+from six.moves import zip
 
 
 #: statements that callinto 
diff --git a/jinja2/runtime.py b/jinja2/runtime.py
index 5c39984..750b630 100644
--- a/jinja2/runtime.py
+++ b/jinja2/runtime.py
@@ -14,6 +14,7 @@
      concat, internalcode, next, object_type_repr
 from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
      TemplateNotFound
+import six
 
 
 # these variables are exported to the template runtime
@@ -63,7 +64,7 @@
         # we don't want to modify the dict passed
         if shared:
             parent = dict(parent)
-        for key, value in locals.iteritems():
+        for key, value in six.iteritems(locals):
             if key[:2] == 'l_' and value is not missing:
                 parent[key[2:]] = value
     return Context(environment, parent, template_name, blocks)
@@ -119,7 +120,7 @@
         # create the initial mapping of blocks.  Whenever template inheritance
         # takes place the runtime will update this mapping with the new blocks
         # from the template.
-        self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
+        self.blocks = dict((k, [v]) for k, v in six.iteritems(blocks))
 
     def super(self, name, current):
         """Render a parent block."""
@@ -191,7 +192,7 @@
                               self.parent, True, None, locals)
         context.vars.update(self.vars)
         context.eval_ctx = self.eval_ctx
-        context.blocks.update((k, list(v)) for k, v in self.blocks.iteritems())
+        context.blocks.update((k, list(v)) for k, v in six.iteritems(self.blocks))
         return context
 
     def _all(meth):
@@ -483,7 +484,7 @@
         _fail_with_undefined_error
 
     def __str__(self):
-        return unicode(self).encode('utf-8')
+        return six.text_type(self).encode('utf-8')
 
     # unicode goes after __str__ because we configured 2to3 to rename
     # __unicode__ to __str__.  because the 2to3 tree is not designed to
diff --git a/jinja2/sandbox.py b/jinja2/sandbox.py
index a1cbb29..b9b1716 100644
--- a/jinja2/sandbox.py
+++ b/jinja2/sandbox.py
@@ -90,7 +90,7 @@
     """A range that can't generate ranges with a length of more than
     MAX_RANGE items.
     """
-    rng = xrange(*args)
+    rng = range(*args)
     if len(rng) > MAX_RANGE:
         raise OverflowError('range too big, maximum size for range is %d' %
                             MAX_RANGE)
diff --git a/jinja2/tests.py b/jinja2/tests.py
index 50510b0..74017e5 100644
--- a/jinja2/tests.py
+++ b/jinja2/tests.py
@@ -10,6 +10,7 @@
 """
 import re
 from jinja2.runtime import Undefined
+import six
 
 try:
     from collections import Mapping as MappingType
@@ -76,12 +77,12 @@
 
 def test_lower(value):
     """Return true if the variable is lowercased."""
-    return unicode(value).islower()
+    return six.text_type(value).islower()
 
 
 def test_upper(value):
     """Return true if the variable is uppercased."""
-    return unicode(value).isupper()
+    return six.text_type(value).isupper()
 
 
 def test_string(value):
diff --git a/jinja2/testsuite/__init__.py b/jinja2/testsuite/__init__.py
index 1f10ef6..e39e877 100644
--- a/jinja2/testsuite/__init__.py
+++ b/jinja2/testsuite/__init__.py
@@ -59,7 +59,7 @@
     def assert_traceback_matches(self, callback, expected_tb):
         try:
             callback()
-        except Exception, e:
+        except Exception as e:
             tb = format_exception(*sys.exc_info())
             if re.search(expected_tb.strip(), ''.join(tb)) is None:
                 raise self.fail('Traceback did not match:\n\n%s\nexpected:\n%s'
diff --git a/jinja2/testsuite/api.py b/jinja2/testsuite/api.py
index c8f9634..5385437 100644
--- a/jinja2/testsuite/api.py
+++ b/jinja2/testsuite/api.py
@@ -16,6 +16,7 @@
      StrictUndefined, UndefinedError, meta, \
      is_undefined, Template, DictLoader
 from jinja2.utils import Cycler
+import six
 
 env = Environment()
 
@@ -50,8 +51,8 @@
         c = Cycler(*items)
         for item in items + items:
             assert c.current == item
-            assert c.next() == item
-        c.next()
+            assert six.advance_iterator(c) == item
+        six.advance_iterator(c)
         assert c.current == 2
         c.reset()
         assert c.current == 1
@@ -107,8 +108,8 @@
     def test_find_refererenced_templates(self):
         ast = env.parse('{% extends "layout.html" %}{% include helper %}')
         i = meta.find_referenced_templates(ast)
-        assert i.next() == 'layout.html'
-        assert i.next() is None
+        assert six.advance_iterator(i) == 'layout.html'
+        assert six.advance_iterator(i) is None
         assert list(i) == []
 
         ast = env.parse('{% extends "layout.html" %}'
@@ -141,21 +142,21 @@
     def test_basic_streaming(self):
         tmpl = env.from_string("<ul>{% for item in seq %}<li>{{ loop.index "
                                "}} - {{ item }}</li>{%- endfor %}</ul>")
-        stream = tmpl.stream(seq=range(4))
-        self.assert_equal(stream.next(), '<ul>')
-        self.assert_equal(stream.next(), '<li>1 - 0</li>')
-        self.assert_equal(stream.next(), '<li>2 - 1</li>')
-        self.assert_equal(stream.next(), '<li>3 - 2</li>')
-        self.assert_equal(stream.next(), '<li>4 - 3</li>')
-        self.assert_equal(stream.next(), '</ul>')
+        stream = tmpl.stream(seq=list(range(4)))
+        self.assert_equal(six.advance_iterator(stream), '<ul>')
+        self.assert_equal(six.advance_iterator(stream), '<li>1 - 0</li>')
+        self.assert_equal(six.advance_iterator(stream), '<li>2 - 1</li>')
+        self.assert_equal(six.advance_iterator(stream), '<li>3 - 2</li>')
+        self.assert_equal(six.advance_iterator(stream), '<li>4 - 3</li>')
+        self.assert_equal(six.advance_iterator(stream), '</ul>')
 
     def test_buffered_streaming(self):
         tmpl = env.from_string("<ul>{% for item in seq %}<li>{{ loop.index "
                                "}} - {{ item }}</li>{%- endfor %}</ul>")
-        stream = tmpl.stream(seq=range(4))
+        stream = tmpl.stream(seq=list(range(4)))
         stream.enable_buffering(size=3)
-        self.assert_equal(stream.next(), u'<ul><li>1 - 0</li><li>2 - 1</li>')
-        self.assert_equal(stream.next(), u'<li>3 - 2</li><li>4 - 3</li></ul>')
+        self.assert_equal(six.advance_iterator(stream), u'<ul><li>1 - 0</li><li>2 - 1</li>')
+        self.assert_equal(six.advance_iterator(stream), u'<li>3 - 2</li><li>4 - 3</li></ul>')
 
     def test_streaming_behavior(self):
         tmpl = env.from_string("")
@@ -222,7 +223,7 @@
     def test_none_gives_proper_error(self):
         try:
             Environment().getattr(None, 'split')()
-        except UndefinedError, e:
+        except UndefinedError as e:
             assert e.message == "'None' has no attribute 'split'"
         else:
             assert False, 'expected exception'
@@ -230,7 +231,7 @@
     def test_object_repr(self):
         try:
             Undefined(obj=42, name='upper')()
-        except UndefinedError, e:
+        except UndefinedError as e:
             assert e.message == "'int object' has no attribute 'upper'"
         else:
             assert False, 'expected exception'
diff --git a/jinja2/testsuite/core_tags.py b/jinja2/testsuite/core_tags.py
index 2b5f580..409a579 100644
--- a/jinja2/testsuite/core_tags.py
+++ b/jinja2/testsuite/core_tags.py
@@ -22,7 +22,7 @@
 
     def test_simple(self):
         tmpl = env.from_string('{% for item in seq %}{{ item }}{% endfor %}')
-        assert tmpl.render(seq=range(10)) == '0123456789'
+        assert tmpl.render(seq=list(range(10))) == '0123456789'
 
     def test_else(self):
         tmpl = env.from_string('{% for item in seq %}XXX{% else %}...{% endfor %}')
@@ -55,12 +55,12 @@
         tmpl = env.from_string('''{% for item in seq %}{{
             loop.cycle('<1>', '<2>') }}{% endfor %}{%
             for item in seq %}{{ loop.cycle(*through) }}{% endfor %}''')
-        output = tmpl.render(seq=range(4), through=('<1>', '<2>'))
+        output = tmpl.render(seq=list(range(4)), through=('<1>', '<2>'))
         assert output == '<1><2>' * 4
 
     def test_scope(self):
         tmpl = env.from_string('{% for item in seq %}{% endfor %}{{ item }}')
-        output = tmpl.render(seq=range(10))
+        output = tmpl.render(seq=list(range(10)))
         assert not output
 
     def test_varlen(self):
diff --git a/jinja2/testsuite/ext.py b/jinja2/testsuite/ext.py
index 6ca6c22..65251a5 100644
--- a/jinja2/testsuite/ext.py
+++ b/jinja2/testsuite/ext.py
@@ -18,6 +18,7 @@
 from jinja2.ext import Extension
 from jinja2.lexer import Token, count_newlines
 from jinja2.utils import next
+import six
 
 # 2.x / 3.x
 try:
@@ -222,7 +223,7 @@
         original = Environment(extensions=[TestExtension])
         overlay = original.overlay()
         for env in original, overlay:
-            for ext in env.extensions.itervalues():
+            for ext in six.itervalues(env.extensions):
                 assert ext.environment is env
 
     def test_preprocessor_extension(self):
diff --git a/jinja2/testsuite/filters.py b/jinja2/testsuite/filters.py
index b037e24..cd95463 100644
--- a/jinja2/testsuite/filters.py
+++ b/jinja2/testsuite/filters.py
@@ -12,6 +12,9 @@
 from jinja2.testsuite import JinjaTestCase
 
 from jinja2 import Markup, Environment
+import six
+from six.moves import map
+from six.moves import zip
 
 env = Environment()
 
@@ -47,14 +50,14 @@
     def test_batch(self):
         tmpl = env.from_string("{{ foo|batch(3)|list }}|"
                                "{{ foo|batch(3, 'X')|list }}")
-        out = tmpl.render(foo=range(10))
+        out = tmpl.render(foo=list(range(10)))
         assert out == ("[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]|"
                        "[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 'X', 'X']]")
 
     def test_slice(self):
         tmpl = env.from_string('{{ foo|slice(3)|list }}|'
                                '{{ foo|slice(3, "X")|list }}')
-        out = tmpl.render(foo=range(10))
+        out = tmpl.render(foo=list(range(10)))
         assert out == ("[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|"
                        "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]")
 
@@ -109,7 +112,7 @@
 
     def test_first(self):
         tmpl = env.from_string('{{ foo|first }}')
-        out = tmpl.render(foo=range(10))
+        out = tmpl.render(foo=list(range(10)))
         assert out == '0'
 
     def test_float(self):
@@ -155,7 +158,7 @@
 
     def test_last(self):
         tmpl = env.from_string('''{{ foo|last }}''')
-        out = tmpl.render(foo=range(10))
+        out = tmpl.render(foo=list(range(10)))
         assert out == '9'
 
     def test_length(self):
@@ -171,12 +174,12 @@
     def test_pprint(self):
         from pprint import pformat
         tmpl = env.from_string('''{{ data|pprint }}''')
-        data = range(1000)
+        data = list(range(1000))
         assert tmpl.render(data=data) == pformat(data)
 
     def test_random(self):
         tmpl = env.from_string('''{{ seq|random }}''')
-        seq = range(100)
+        seq = list(range(100))
         for _ in range(10):
             assert int(tmpl.render(seq=seq)) in seq
 
@@ -188,7 +191,7 @@
     def test_string(self):
         x = [1, 2, 3, 4, 5]
         tmpl = env.from_string('''{{ obj|string }}''')
-        assert tmpl.render(obj=x) == unicode(x)
+        assert tmpl.render(obj=x) == six.text_type(x)
 
     def test_title(self):
         tmpl = env.from_string('''{{ "foo bar"|title }}''')
@@ -297,7 +300,7 @@
             def __init__(self, value):
                 self.value = value
             def __unicode__(self):
-                return unicode(self.value)
+                return six.text_type(self.value)
         tmpl = env.from_string('''{{ items|sort(attribute='value')|join }}''')
         assert tmpl.render(items=map(Magic, [3, 2, 4, 1])) == '1234'
 
diff --git a/jinja2/testsuite/imports.py b/jinja2/testsuite/imports.py
index 1cb12cb..3db9008 100644
--- a/jinja2/testsuite/imports.py
+++ b/jinja2/testsuite/imports.py
@@ -83,7 +83,7 @@
         self.assert_raises(TemplateNotFound, t.render)
         try:
             t.render()
-        except TemplatesNotFound, e:
+        except TemplatesNotFound as e:
             assert e.templates == ['missing', 'missing2']
             assert e.name == 'missing2'
         else:
diff --git a/jinja2/testsuite/inheritance.py b/jinja2/testsuite/inheritance.py
index 355aa0c..7909b03 100644
--- a/jinja2/testsuite/inheritance.py
+++ b/jinja2/testsuite/inheritance.py
@@ -148,7 +148,7 @@
         }))
         t = env.from_string('{% extends "master.html" %}{% block item %}'
                             '{{ item }}{% endblock %}')
-        assert t.render(seq=range(5)) == '[0][1][2][3][4]'
+        assert t.render(seq=list(range(5))) == '[0][1][2][3][4]'
 
     def test_super_in_scoped_block(self):
         env = Environment(loader=DictLoader({
@@ -157,7 +157,7 @@
         }))
         t = env.from_string('{% extends "master.html" %}{% block item %}'
                             '{{ super() }}|{{ item * 2 }}{% endblock %}')
-        assert t.render(seq=range(5)) == '[0|0][1|2][2|4][3|6][4|8]'
+        assert t.render(seq=list(range(5))) == '[0|0][1|2][2|4][3|6][4|8]'
 
     def test_scoped_block_after_inheritance(self):
         env = Environment(loader=DictLoader({
diff --git a/jinja2/testsuite/lexnparse.py b/jinja2/testsuite/lexnparse.py
index 77b76ec..e162463 100644
--- a/jinja2/testsuite/lexnparse.py
+++ b/jinja2/testsuite/lexnparse.py
@@ -15,6 +15,7 @@
 
 from jinja2 import Environment, Template, TemplateSyntaxError, \
      UndefinedError, nodes
+import six
 
 env = Environment()
 
@@ -42,7 +43,7 @@
         env = Environment('{%', '%}', '${', '}')
         tmpl = env.from_string('''{% for item in seq
             %}${{'foo': item}|upper}{% endfor %}''')
-        assert tmpl.render(seq=range(3)) == "{'FOO': 0}{'FOO': 1}{'FOO': 2}"
+        assert tmpl.render(seq=list(range(3))) == "{'FOO': 0}{'FOO': 1}{'FOO': 2}"
 
     def test_comments(self):
         env = Environment('<!--', '-->', '{', '}')
@@ -52,7 +53,7 @@
   <li>{item}</li>
 <!--- endfor -->
 </ul>''')
-        assert tmpl.render(seq=range(3)) == ("<ul>\n  <li>0</li>\n  "
+        assert tmpl.render(seq=list(range(3))) == ("<ul>\n  <li>0</li>\n  "
                                              "<li>1</li>\n  <li>2</li>\n</ul>")
 
     def test_string_escapes(self):
@@ -68,11 +69,11 @@
 
     def test_operators(self):
         from jinja2.lexer import operators
-        for test, expect in operators.iteritems():
+        for test, expect in six.iteritems(operators):
             if test in '([{}])':
                 continue
             stream = env.lexer.tokenize('{{ %s }}' % test)
-            stream.next()
+            six.advance_iterator(stream)
             assert stream.current.type == expect
 
     def test_normalizing(self):
@@ -92,7 +93,7 @@
 <? for item in seq -?>
     <?= item ?>
 <?- endfor ?>''')
-        assert tmpl.render(seq=range(5)) == '01234'
+        assert tmpl.render(seq=list(range(5))) == '01234'
 
     def test_erb_syntax(self):
         env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>')
@@ -101,7 +102,7 @@
 <% for item in seq -%>
     <%= item %>
 <%- endfor %>''')
-        assert tmpl.render(seq=range(5)) == '01234'
+        assert tmpl.render(seq=list(range(5))) == '01234'
 
     def test_comment_syntax(self):
         env = Environment('<!--', '-->', '${', '}', '<!--#', '-->')
@@ -110,7 +111,7 @@
 <!-- for item in seq --->
     ${item}
 <!--- endfor -->''')
-        assert tmpl.render(seq=range(5)) == '01234'
+        assert tmpl.render(seq=list(range(5))) == '01234'
 
     def test_balancing(self):
         tmpl = env.from_string('''{{{'foo':'bar'}.foo}}''')
@@ -130,8 +131,8 @@
 % for item in seq:
     ${item}
 % endfor''')
-        assert [int(x.strip()) for x in tmpl.render(seq=range(5)).split()] == \
-               range(5)
+        assert [int(x.strip()) for x in tmpl.render(seq=list(range(5))).split()] == \
+               list(range(5))
 
         env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##')
         tmpl = env.from_string('''\
@@ -139,8 +140,8 @@
 % for item in seq:
     ${item} ## the rest of the stuff
 % endfor''')
-        assert [int(x.strip()) for x in tmpl.render(seq=range(5)).split()] == \
-                range(5)
+        assert [int(x.strip()) for x in tmpl.render(seq=list(range(5))).split()] == \
+                list(range(5))
 
     def test_line_syntax_priority(self):
         # XXX: why is the whitespace there in front of the newline?
@@ -166,7 +167,7 @@
         def assert_error(code, expected):
             try:
                 Template(code)
-            except TemplateSyntaxError, e:
+            except TemplateSyntaxError as e:
                 assert str(e) == expected, 'unexpected error message'
             else:
                 assert False, 'that was supposed to be an error'
@@ -335,9 +336,9 @@
         assert tmpl.render() == 'foobarbaz'
 
     def test_notin(self):
-        bar = xrange(100)
+        bar = range(100)
         tmpl = env.from_string('''{{ not 42 in bar }}''')
-        assert tmpl.render(bar=bar) == unicode(not 42 in bar)
+        assert tmpl.render(bar=bar) == six.text_type(not 42 in bar)
 
     def test_implicit_subscribed_tuple(self):
         class Foo(object):
diff --git a/jinja2/testsuite/regression.py b/jinja2/testsuite/regression.py
index 4db9076..5c3633f 100644
--- a/jinja2/testsuite/regression.py
+++ b/jinja2/testsuite/regression.py
@@ -14,6 +14,8 @@
 
 from jinja2 import Template, Environment, DictLoader, TemplateSyntaxError, \
      TemplateNotFound, PrefixLoader
+from six.moves import map
+from six.moves import zip
 
 env = Environment()
 
@@ -118,7 +120,7 @@
 
         ''')
 
-        assert tmpl.render().split() == map(unicode, range(1, 11)) * 5
+        assert tmpl.render().split() == map(unicode, list(range(1, 11))) * 5
 
     def test_weird_inline_comment(self):
         env = Environment(line_statement_prefix='%')
@@ -242,7 +244,7 @@
         }))
         try:
             env.get_template('foo/bar.html')
-        except TemplateNotFound, e:
+        except TemplateNotFound as e:
             assert e.name == 'foo/bar.html'
         else:
             assert False, 'expected error here'
diff --git a/jinja2/testsuite/security.py b/jinja2/testsuite/security.py
index 4518eac..c892fed 100644
--- a/jinja2/testsuite/security.py
+++ b/jinja2/testsuite/security.py
@@ -18,6 +18,7 @@
 from jinja2 import Markup, escape
 from jinja2.exceptions import SecurityError, TemplateSyntaxError, \
      TemplateRuntimeError
+import six
 
 
 class PrivateStuff(object):
@@ -76,7 +77,7 @@
         # adding two strings should escape the unsafe one
         unsafe = '<script type="application/x-some-script">alert("foo");</script>'
         safe = Markup('<em>username</em>')
-        assert unsafe + safe == unicode(escape(unsafe)) + unicode(safe)
+        assert unsafe + safe == six.text_type(escape(unsafe)) + six.text_type(safe)
 
         # string interpolations are safe to use too
         assert Markup('<em>%s</em>') % '<bad user>' == \
@@ -114,7 +115,7 @@
                             '{{ say_hello("<blink>foo</blink>") }}')
         escaped_out = '<p>Hello &lt;blink&gt;foo&lt;/blink&gt;!</p>'
         assert t.render() == escaped_out
-        assert unicode(t.module) == escaped_out
+        assert six.text_type(t.module) == escaped_out
         assert escape(t.module) == escaped_out
         assert t.module.say_hello('<blink>foo</blink>') == escaped_out
         assert escape(t.module.say_hello('<blink>foo</blink>')) == escaped_out
@@ -136,7 +137,7 @@
             t = env.from_string('{{ %s }}' % expr)
             try:
                 t.render(ctx)
-            except TemplateRuntimeError, e:
+            except TemplateRuntimeError as e:
                 pass
             else:
                 self.fail('expected runtime error')
@@ -153,7 +154,7 @@
             t = env.from_string('{{ %s }}' % expr)
             try:
                 t.render(ctx)
-            except TemplateRuntimeError, e:
+            except TemplateRuntimeError as e:
                 pass
             else:
                 self.fail('expected runtime error')
diff --git a/jinja2/testsuite/utils.py b/jinja2/testsuite/utils.py
index be2e902..cab9b09 100644
--- a/jinja2/testsuite/utils.py
+++ b/jinja2/testsuite/utils.py
@@ -60,8 +60,8 @@
 
     def test_markup_leaks(self):
         counts = set()
-        for count in xrange(20):
-            for item in xrange(1000):
+        for count in range(20):
+            for item in range(1000):
                 escape("foo")
                 escape("<foo>")
                 escape(u"foo")
diff --git a/jinja2/utils.py b/jinja2/utils.py
index 401b579..121a008 100644
--- a/jinja2/utils.py
+++ b/jinja2/utils.py
@@ -11,6 +11,7 @@
 import re
 import sys
 import errno
+import six
 try:
     from urllib.parse import quote_from_bytes as url_quote
 except ImportError:
@@ -52,7 +53,7 @@
         raise TypeError(_test_gen_bug)
         yield None
     _concat(_test_gen_bug())
-except TypeError, _error:
+except TypeError as _error:
     if not _error.args or _error.args[0] is not _test_gen_bug:
         def concat(gen):
             try:
@@ -73,7 +74,7 @@
     next = next
 except NameError:
     def next(x):
-        return x.next()
+        return six.advance_iterator(x)
 
 
 # if this python version is unable to deal with unicode filenames
@@ -105,7 +106,7 @@
 FunctionType = type(_func)
 GeneratorType = type(_func())
 MethodType = type(_C.method)
-CodeType = type(_C.method.func_code)
+CodeType = type(_C.method.__code__)
 try:
     raise TypeError()
 except TypeError:
@@ -156,7 +157,7 @@
 
 def internalcode(f):
     """Marks the function as internally used"""
-    internal_code.add(f.func_code)
+    internal_code.add(f.__code__)
     return f
 
 
@@ -226,7 +227,7 @@
     """
     try:
         return open(filename, mode)
-    except IOError, e:
+    except IOError as e:
         if e.errno not in (errno.ENOENT, errno.EISDIR):
             raise
 
@@ -275,7 +276,7 @@
     trim_url = lambda x, limit=trim_url_limit: limit is not None \
                          and (x[:limit] + (len(x) >=limit and '...'
                          or '')) or x
-    words = _word_split_re.split(unicode(escape(text)))
+    words = _word_split_re.split(six.text_type(escape(text)))
     nofollow_attr = nofollow and ' rel="nofollow"' or ''
     for i, word in enumerate(words):
         match = _punctuation_re.match(word)
@@ -312,7 +313,7 @@
     words = LOREM_IPSUM_WORDS.split()
     result = []
 
-    for _ in xrange(n):
+    for _ in range(n):
         next_capitalized = True
         last_comma = last_fullstop = 0
         word = None
@@ -320,7 +321,7 @@
         p = []
 
         # each paragraph contains out of 20 to 100 words.
-        for idx, _ in enumerate(xrange(randrange(min, max))):
+        for idx, _ in enumerate(range(randrange(min, max))):
             while True:
                 word = choice(words)
                 if word != last:
@@ -363,10 +364,10 @@
     representation first.
     """
     if not isinstance(obj, basestring):
-        obj = unicode(obj)
+        obj = six.text_type(obj)
     if isinstance(obj, unicode):
         obj = obj.encode(charset)
-    return unicode(url_quote(obj))
+    return six.text_type(url_quote(obj))
 
 
 class LRUCache(object):