commit d886499651add63bbb944c25fc56a276cc2a7884
Author: Louis Sautier <sautier.louis@gmail.com>
Date:   Thu Sep 9 23:35:03 2021 +0200

    Stop using deprecated 2to3 option for setuptools

diff --git a/setup.py b/setup.py
index f575020..678b555 100644
--- a/setup.py
+++ b/setup.py
@@ -36,5 +36,4 @@ more to learn about it.
       test_suite='nose.collector',
       include_package_data=True,
       zip_safe=True,
-      use_2to3=True,
       )
diff --git a/tempita/__init__.py b/tempita/__init__.py
index acc2fd9..a3cb8a1 100755
--- a/tempita/__init__.py
+++ b/tempita/__init__.py
@@ -35,9 +35,9 @@ import os
 import re
 import sys
 import tokenize
-from cStringIO import StringIO
+from io import StringIO
 from html import escape
-from urllib import quote as url_quote
+from urllib.parse import quote as url_quote
 from tempita._looper import looper
 from tempita.compat3 import bytes, basestring_, next, is_unicode, coerce_text
 
@@ -103,7 +103,7 @@ class Template(object):
             delimiters = (self.default_namespace['start_braces'],
                           self.default_namespace['end_braces'])
         else:
-            assert len(delimiters) == 2 and all([isinstance(delimeter, basestring)
+            assert len(delimiters) == 2 and all([isinstance(delimeter, str)
                                                  for delimeter in delimiters])
             self.default_namespace = self.__class__.default_namespace.copy()
             self.default_namespace['start_braces'] = delimiters[0]
@@ -198,7 +198,7 @@ class Template(object):
                 position=None, name=self.name)
         templ = self.get_template(inherit_template, self)
         self_ = TemplateObject(self.name)
-        for name, value in defs.iteritems():
+        for name, value in defs.items():
             setattr(self_, name, value)
         self_.body = body
         ns = ns.copy()
@@ -294,7 +294,7 @@ class Template(object):
         try:
             try:
                 value = eval(code, self.default_namespace, ns)
-            except SyntaxError, e:
+            except SyntaxError as e:
                 raise SyntaxError(
                     'invalid syntax in expression: %s' % code)
             return value
@@ -306,12 +306,12 @@ class Template(object):
             else:
                 arg0 = coerce_text(e)
             e.args = (self._add_line_info(arg0, pos),)
-            raise exc_info[0], e, exc_info[2]
+            raise exc_info[0](e).with_traceback(exc_info[2])
 
     def _exec(self, code, ns, pos):
         __traceback_hide__ = True
         try:
-            exec code in self.default_namespace, ns
+            exec(code, self.default_namespace, ns)
         except:
             exc_info = sys.exc_info()
             e = exc_info[1]
@@ -319,7 +319,7 @@ class Template(object):
                 e.args = (self._add_line_info(e.args[0], pos),)
             else:
                 e.args = (self._add_line_info(None, pos),)
-            raise exc_info[0], e, exc_info[2]
+            raise exc_info[0](e).with_traceback(exc_info[2])
 
     def _repr(self, value, pos):
         __traceback_hide__ = True
@@ -328,7 +328,7 @@ class Template(object):
                 return ''
             if self._unicode:
                 try:
-                    value = unicode(value)
+                    value = str(value)
                 except UnicodeDecodeError:
                     value = bytes(value)
             else:
@@ -341,7 +341,7 @@ class Template(object):
             exc_info = sys.exc_info()
             e = exc_info[1]
             e.args = (self._add_line_info(e.args[0], pos),)
-            raise exc_info[0], e, exc_info[2]
+            raise exc_info[0](e).with_traceback(exc_info[2])
         else:
             if self._unicode and isinstance(value, bytes):
                 if not self.default_encoding:
@@ -350,7 +350,7 @@ class Template(object):
                         '(no default_encoding provided)' % value)
                 try:
                     value = value.decode(self.default_encoding)
-                except UnicodeDecodeError, e:
+                except UnicodeDecodeError as e:
                     raise UnicodeDecodeError(
                         e.encoding,
                         e.object,
@@ -387,7 +387,7 @@ def paste_script_template_renderer(content, vars, filename=None):
 class bunch(dict):
 
     def __init__(self, **kw):
-        for name, value in kw.iteritems():
+        for name, value in kw.items():
             setattr(self, name, value)
 
     def __setattr__(self, name, value):
@@ -410,7 +410,7 @@ class bunch(dict):
 
     def __repr__(self):
         items = [
-            (k, v) for k, v in self.iteritems()]
+            (k, v) for k, v in self.items()]
         items.sort()
         return '<%s %s>' % (
             self.__class__.__name__,
@@ -463,7 +463,7 @@ def url(v):
 
 
 def attr(**kw):
-    kw = list(kw.iteritems())
+    kw = list(kw.items())
     kw.sort()
     parts = []
     for name, value in kw:
@@ -545,7 +545,7 @@ class TemplateDef(object):
         values = {}
         sig_args, var_args, var_kw, defaults = self._func_signature
         extra_kw = {}
-        for name, value in kw.iteritems():
+        for name, value in kw.items():
             if not var_kw and name not in sig_args:
                 raise TypeError(
                     'Unexpected argument %s' % name)
@@ -568,7 +568,7 @@ class TemplateDef(object):
                 raise TypeError(
                     'Extra position arguments: %s'
                     % ', '.join(repr(v) for v in args))
-        for name, value_expr in defaults.iteritems():
+        for name, value_expr in defaults.items():
             if name not in values:
                 values[name] = self._template._eval(
                     value_expr, self._ns, self._pos)
@@ -614,7 +614,7 @@ class _Empty(object):
         return 'Empty'
 
     def __unicode__(self):
-        return u''
+        return ''
 
     def __iter__(self):
         return iter(())
@@ -1164,7 +1164,7 @@ def fill_command(args=None):
         vars.update(os.environ)
     for value in args:
         if '=' not in value:
-            print('Bad argument: %r' % value)
+            print(('Bad argument: %r' % value))
             sys.exit(2)
         name, value = value.split('=', 1)
         if name.startswith('py:'):
diff --git a/tempita/_looper.py b/tempita/_looper.py
index 6784c7c..70aded7 100644
--- a/tempita/_looper.py
+++ b/tempita/_looper.py
@@ -7,9 +7,9 @@ These can be awkward to manage in a normal Python loop, but using the
 looper you can get a better sense of the context.  Use like::
 
     >>> for loop, item in looper(['a', 'b', 'c']):
-    ...     print loop.number, item
+    ...     print(loop.number, item)
     ...     if not loop.last:
-    ...         print '---'
+    ...         print('---')
     1 a
     ---
     2 b
@@ -161,3 +161,4 @@ class loop_pos(object):
             return getter(item) != getter(other)
         else:
             return item[getter] != other[getter]
+
diff --git a/tempita/compat3.py b/tempita/compat3.py
index 5e18fa0..f17f588 100644
--- a/tempita/compat3.py
+++ b/tempita/compat3.py
@@ -4,7 +4,7 @@ __all__ = ['b', 'basestring_', 'bytes', 'next', 'is_unicode']
 
 if sys.version < "3":
     b = bytes = str
-    basestring_ = basestring
+    basestring_ = str
 else:
 
     def b(s):
@@ -18,14 +18,14 @@ text = str
 if sys.version < "3":
 
     def next(obj):
-        return obj.next()
+        return obj.__next__()
 else:
     next = next
 
 if sys.version < "3":
 
     def is_unicode(obj):
-        return isinstance(obj, unicode)
+        return isinstance(obj, str)
 else:
 
     def is_unicode(obj):
@@ -39,7 +39,7 @@ def coerce_text(v):
         else:
             attr = '__str__'
         if hasattr(v, attr):
-            return unicode(v)
+            return str(v)
         else:
             return bytes(v)
     return v
