--- examples/basic/kidrun.py.orig	2019-05-27 21:03:08 UTC
+++ examples/basic/kidrun.py
@@ -17,11 +17,11 @@ def test():
 
     start = time.clock()
     template = kid.Template(file='test.kid', **ctxt)
-    print ' --> parse stage: %.4f ms' % ((time.clock() - start) * 1000)
+    print(' --> parse stage: %.4f ms' % ((time.clock() - start) * 1000))
 
     for output in template.generate():
         sys.stdout.write(output)
-    print
+    print()
 
     times = []
     for i in range(1000):
@@ -30,10 +30,10 @@ def test():
         times.append(time.clock() - start)
         sys.stdout.write('.')
         sys.stdout.flush()
-    print
+    print()
 
-    print ' --> render stage: %s ms (average)' % (
-          (sum(times) / len(times) * 1000))
+    print(' --> render stage: %s ms (average)' % (
+          (sum(times) / len(times) * 1000)))
 
 if __name__ == '__main__':
     if '-p' in sys.argv:
--- examples/basic/run.py.orig	2019-05-27 21:03:08 UTC
+++ examples/basic/run.py
@@ -13,13 +13,13 @@ def test():
 
     start = time.clock()
     tmpl = loader.load('test.html')
-    print ' --> parse stage: %.4f ms' % ((time.clock() - start) * 1000)
+    print(' --> parse stage: %.4f ms' % ((time.clock() - start) * 1000))
 
     data = dict(hello='<world>', skin='default', hey='ZYX', bozz=None,
                 items=['Number %d' % num for num in range(1, 15)],
                 prefix='#')
 
-    print tmpl.generate(**data).render(method='html')
+    print(tmpl.generate(**data).render(method='html'))
 
     times = []
     for i in range(1000):
@@ -28,10 +28,10 @@ def test():
         times.append(time.clock() - start)
         sys.stdout.write('.')
         sys.stdout.flush()
-    print
+    print()
 
-    print ' --> render stage: %s ms (average)' % (
-          (sum(times) / len(times) * 1000))
+    print(' --> render stage: %s ms (average)' % (
+          (sum(times) / len(times) * 1000)))
 
 if __name__ == '__main__':
     if '-p' in sys.argv:
--- examples/bench/basic.py.orig	2019-05-27 21:03:08 UTC
+++ examples/bench/basic.py
@@ -5,7 +5,7 @@
 
 from cgi import escape
 import os
-from StringIO import StringIO
+from io import StringIO
 import sys
 import timeit
 
@@ -22,7 +22,7 @@ def genshi(dirname, verbose=False):
         return template.generate(**data).render('xhtml')
 
     if verbose:
-        print render()
+        print(render())
     return render
 
 def genshi_text(dirname, verbose=False):
@@ -36,14 +36,14 @@ def genshi_text(dirname, verbose=False):
         return template.generate(**data).render('text')
 
     if verbose:
-        print render()
+        print(render())
     return render
 
 def mako(dirname, verbose=False):
     try:
         from mako.lookup import TemplateLookup
     except ImportError:
-        print>>sys.stderr, 'Mako not installed, skipping'
+        print('Mako not installed, skipping', file=sys.stderr)
         return lambda: None
     lookup = TemplateLookup(directories=[dirname], filesystem_checks=False)
     template = lookup.get_template('template.html')
@@ -52,7 +52,7 @@ def mako(dirname, verbose=False):
                     list_items=['Number %d' % num for num in range(1, 15)])
         return template.render(**data)
     if verbose:
-        print render()
+        print(render())
     return render
 
 def cheetah(dirname, verbose=False):
@@ -60,7 +60,7 @@ def cheetah(dirname, verbose=False):
     try:
         from Cheetah.Template import Template
     except ImportError:
-        print>>sys.stderr, 'Cheetah not installed, skipping'
+        print('Cheetah not installed, skipping', file=sys.stderr)
         return lambda: None
     class MyTemplate(Template):
         def serverSidePath(self, path): return os.path.join(dirname, path)
@@ -70,18 +70,18 @@ def cheetah(dirname, verbose=False):
     def render():
         template = MyTemplate(file=filename,
                               searchList=[{'title': 'Just a test', 'user': 'joe',
-                                           'items': [u'Number %d' % num for num in range(1, 15)]}])
+                                           'items': ['Number %d' % num for num in range(1, 15)]}])
         return template.respond()
 
     if verbose:
-        print render()
+        print(render())
     return render
 
 def clearsilver(dirname, verbose=False):
     try:
         import neo_cgi
     except ImportError:
-        print>>sys.stderr, 'ClearSilver not installed, skipping'
+        print('ClearSilver not installed, skipping', file=sys.stderr)
         return lambda: None
     neo_cgi.update()
     import neo_util
@@ -98,7 +98,7 @@ def clearsilver(dirname, verbose=False):
         return cs.render()
 
     if verbose:
-        print render()
+        print(render())
     return render
 
 def django(dirname, verbose=False):
@@ -106,7 +106,7 @@ def django(dirname, verbose=False):
         from django.conf import settings
         settings.configure(TEMPLATE_DIRS=[os.path.join(dirname, 'templates')])
     except ImportError:
-        print>>sys.stderr, 'Django not installed, skipping'
+        print('Django not installed, skipping', file=sys.stderr)
         return lambda: None
     from django import template, templatetags
     from django.template import loader
@@ -119,14 +119,14 @@ def django(dirname, verbose=False):
         return tmpl.render(template.Context(data))
 
     if verbose:
-        print render()
+        print(render())
     return render
 
 def kid(dirname, verbose=False):
     try:
         import kid
     except ImportError:
-        print>>sys.stderr, "Kid not installed, skipping"
+        print("Kid not installed, skipping", file=sys.stderr)
         return lambda: None
     kid.path = kid.TemplatePath([dirname])
     template = kid.load_template('template.kid').Template
@@ -137,14 +137,14 @@ def kid(dirname, verbose=False):
         ).serialize(output='xhtml')
 
     if verbose:
-        print render()
+        print(render())
     return render
 
 def simpletal(dirname, verbose=False):
     try:
         from simpletal import simpleTAL, simpleTALES
     except ImportError:
-        print>>sys.stderr, "SimpleTAL not installed, skipping"
+        print("SimpleTAL not installed, skipping", file=sys.stderr)
         return lambda: None
     fileobj = open(os.path.join(dirname, 'base.html'))
     base = simpleTAL.compileHTMLTemplate(fileobj)
@@ -163,7 +163,7 @@ def simpletal(dirname, verbose=False):
         return buf.getvalue()
 
     if verbose:
-        print render()
+        print(render())
     return render
 
 def run(engines, number=2000, verbose=False):
@@ -171,19 +171,19 @@ def run(engines, number=2000, verbose=False):
     for engine in engines:
         dirname = os.path.join(basepath, engine)
         if verbose:
-            print '%s:' % engine.capitalize()
-            print '--------------------------------------------------------'
+            print('%s:' % engine.capitalize())
+            print('--------------------------------------------------------')
         else:
-            print '%s:' % engine.capitalize(),
+            print('%s:' % engine.capitalize(), end=' ')
         t = timeit.Timer(setup='from __main__ import %s; render = %s(r"%s", %s)'
                                % (engine, engine, dirname, verbose),
                          stmt='render()')
         time = t.timeit(number=number) / number
         if verbose:
-            print '--------------------------------------------------------'
-        print '%.2f ms' % (1000 * time)
+            print('--------------------------------------------------------')
+        print('%.2f ms' % (1000 * time))
         if verbose:
-            print '--------------------------------------------------------'
+            print('--------------------------------------------------------')
 
 
 if __name__ == '__main__':
--- examples/bench/bigtable.py.orig	2019-05-27 21:03:08 UTC
+++ examples/bench/bigtable.py
@@ -8,7 +8,7 @@
 import cgi
 import sys
 import timeit
-from StringIO import StringIO
+from io import StringIO
 from genshi.builder import tag
 from genshi.template import MarkupTemplate, NewTextTemplate
 
@@ -111,7 +111,7 @@ def test_genshi_text():
 def test_genshi_builder():
     """Genshi template + tag builder"""
     stream = tag.TABLE([
-        tag.tr([tag.td(c) for c in row.values()])
+        tag.tr([tag.td(c) for c in list(row.values())])
         for row in table
     ]).generate()
     stream = genshi_tmpl2.generate(table=stream)
@@ -121,7 +121,7 @@ def test_builder():
     """Genshi tag builder"""
     stream = tag.TABLE([
         tag.tr([
-            tag.td(c) for c in row.values()
+            tag.td(c) for c in list(row.values())
         ])
         for row in table
     ]).generate()
@@ -151,7 +151,7 @@ if kid:
             _table = cet.Element('table')
             for row in table:
                 td = cet.SubElement(_table, 'tr')
-                for c in row.values():
+                for c in list(row.values()):
                     cet.SubElement(td, 'td').text=str(c)
             kid_tmpl2.table = _table
             kid_tmpl2.serialize(output='html')
@@ -162,7 +162,7 @@ if et:
         _table = et.Element('table')
         for row in table:
             tr = et.SubElement(_table, 'tr')
-            for c in row.values():
+            for c in list(row.values()):
                 et.SubElement(tr, 'td').text=str(c)
         et.tostring(_table)
 
@@ -172,7 +172,7 @@ if cet:
         _table = cet.Element('table')
         for row in table:
             tr = cet.SubElement(_table, 'tr')
-            for c in row.values():
+            for c in list(row.values()):
                 cet.SubElement(tr, 'td').text=str(c)
         cet.tostring(_table)
 
@@ -201,7 +201,7 @@ def run(which=None, number=10):
              'test_et', 'test_cet', 'test_clearsilver', 'test_django']
 
     if which:
-        tests = filter(lambda n: n[5:] in which, tests)
+        tests = [n for n in tests if n[5:] in which]
 
     for test in [t for t in tests if hasattr(sys.modules[__name__], t)]:
         t = timeit.Timer(setup='from __main__ import %s;' % test,
@@ -212,7 +212,7 @@ def run(which=None, number=10):
             result = '   (not installed?)'
         else:
             result = '%16.2f ms' % (1000 * time)
-        print '%-35s %s' % (getattr(sys.modules[__name__], test).__doc__, result)
+        print('%-35s %s' % (getattr(sys.modules[__name__], test).__doc__, result))
 
 
 if __name__ == '__main__':
--- examples/bench/xpath.py.orig	2019-05-27 21:03:08 UTC
+++ examples/bench/xpath.py
@@ -32,7 +32,7 @@ def benchmark(f, acurate_time=1):
     runs = 1
     while True:
         start_time = time_func()
-        for _ in xrange(runs):
+        for _ in range(runs):
             f()
         dt = time_func() - start_time
         if dt >= acurate_time:
@@ -61,23 +61,23 @@ def spell(t):
 
 def test_paths_in_streams(exprs, streams, test_strategies=False):
     for expr in exprs:
-        print "Testing path %r" % expr
+        print("Testing path %r" % expr)
         for stream, sname in streams:
-            print '\tRunning on "%s" example:' % sname
+            print('\tRunning on "%s" example:' % sname)
 
             path = Path(expr)
             def f():
                 for e in path.select(stream):
                     pass
             t = spell(benchmark(f))
-            print "\t\tselect:\t\t%s" % t
+            print("\t\tselect:\t\t%s" % t)
 
             def f():
                 path = Path(expr)
                 for e in path.select(stream):
                     pass
             t = spell(benchmark(f))
-            print "\t\tinit + select:\t%s" % t
+            print("\t\tinit + select:\t%s" % t)
 
             if test_strategies and len(path.paths) == 1:
                 from genshi.path import GenericStrategy, SingleStepStrategy, \
@@ -88,13 +88,13 @@ def test_paths_in_streams(exprs, streams, test_strateg
                 for strategy in strategies:
                     if not strategy.supports(path.paths[0]):
                         continue
-                    print "\t\t%s Strategy"%strategy.__name__
+                    print("\t\t%s Strategy"%strategy.__name__)
                     fp = FakePath(strategy(path.paths[0]))
                     def f():
                         for e in fp.select(stream):
                             pass
                     t = spell(benchmark(f))
-                    print "\t\t\tselect:\t\t%s"%t
+                    print("\t\t\tselect:\t\t%s"%t)
 
 
 def test_documents(test_strategies=False):
--- examples/tutorial/geddit/controller.py.orig	2019-05-27 21:03:08 UTC
+++ examples/tutorial/geddit/controller.py
@@ -20,7 +20,7 @@ class Root(object):
     @cherrypy.expose
     @template.output('index.html')
     def index(self):
-        links = sorted(self.data.values(), key=operator.attrgetter('time'))
+        links = sorted(list(self.data.values()), key=operator.attrgetter('time'))
         return template.render(links=links)
 
     @cherrypy.expose
@@ -35,7 +35,7 @@ class Root(object):
                 link = Link(**data)
                 self.data[link.id] = link
                 raise cherrypy.HTTPRedirect('/')
-            except Invalid, e:
+            except Invalid as e:
                 errors = e.unpack_errors()
         else:
             errors = {}
@@ -69,7 +69,7 @@ class Root(object):
                     raise cherrypy.HTTPRedirect('/info/%s' % link.id)
                 return template.render('_comment.html', comment=comment,
                                        num=len(link.comments))
-            except Invalid, e:
+            except Invalid as e:
                 errors = e.unpack_errors()
         else:
             errors = {}
@@ -89,7 +89,7 @@ class Root(object):
                 raise cherrypy.NotFound()
             return template.render('info.xml', link=link)
         else:
-            links = sorted(self.data.values(), key=operator.attrgetter('time'))
+            links = sorted(list(self.data.values()), key=operator.attrgetter('time'))
             return template.render(links=links)
 
 
--- genshi/builder.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/builder.py
@@ -107,7 +107,7 @@ class Fragment(object):
         return str(self.generate())
 
     def __unicode__(self):
-        return unicode(self.generate())
+        return str(self.generate())
 
     def __html__(self):
         return Markup(self.generate())
@@ -118,7 +118,7 @@ class Fragment(object):
         :param node: the node to append; can be an `Element`, `Fragment`, or a
                      `Stream`, or a Python string or number
         """
-        if isinstance(node, (Stream, Element, basestring, int, float, long)):
+        if isinstance(node, (Stream, Element, str, int, float)):
             # For objects of a known/primitive type, we avoid the check for
             # whether it is iterable for better performance
             self.children.append(node)
@@ -140,8 +140,8 @@ class Fragment(object):
                 for event in child:
                     yield event
             else:
-                if not isinstance(child, basestring):
-                    child = unicode(child)
+                if not isinstance(child, str):
+                    child = str(child)
                 yield TEXT, child, (None, -1, -1)
 
     def generate(self):
@@ -155,10 +155,10 @@ class Fragment(object):
 def _kwargs_to_attrs(kwargs):
     attrs = []
     names = set()
-    for name, value in kwargs.items():
+    for name, value in list(kwargs.items()):
         name = name.rstrip('_').replace('_', '-')
         if value is not None and name not in names:
-            attrs.append((QName(name), unicode(value)))
+            attrs.append((QName(name), str(value)))
             names.add(name)
     return Attrs(attrs)
 
--- genshi/compat.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/compat.py
@@ -40,7 +40,7 @@ else:
 
 if IS_PYTHON2:
     def isstring(obj):
-        return isinstance(obj, basestring)
+        return isinstance(obj, str)
 else:
     def isstring(obj):
         return isinstance(obj, str)
@@ -48,9 +48,9 @@ else:
 # We need to differentiate between StringIO and BytesIO in places
 
 if IS_PYTHON2:
-    from StringIO import StringIO
+    from io import StringIO
     try:
-        from cStringIO import StringIO as BytesIO
+        from io import StringIO as BytesIO
     except ImportError:
         BytesIO = StringIO
 else:
@@ -124,7 +124,7 @@ try:
     next = next
 except NameError:
     def next(iterator):
-        return iterator.next()
+        return iterator.__next__()
 
 # Compatibility fallback implementations for Python < 2.5
 
--- genshi/core.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/core.py
@@ -271,7 +271,7 @@ def _ensure(stream):
     """Ensure that every item on the stream is actually a markup event."""
     stream = iter(stream)
     try:
-        event = stream.next()
+        event = next(stream)
     except StopIteration:
         return
 
@@ -282,7 +282,7 @@ def _ensure(stream):
             if hasattr(event, 'totuple'):
                 event = event.totuple()
             else:
-                event = TEXT, unicode(event), (None, -1, -1)
+                event = TEXT, str(event), (None, -1, -1)
             yield event
         return
 
@@ -411,7 +411,7 @@ class Attrs(tuple):
         :return: a new instance with the attribute removed
         :rtype: `Attrs`
         """
-        if isinstance(names, basestring):
+        if isinstance(names, str):
             names = (names,)
         return Attrs([(name, val) for name, val in self if name not in names])
 
@@ -445,33 +445,33 @@ class Attrs(tuple):
         return TEXT, ''.join([x[1] for x in self]), (None, -1, -1)
 
 
-class Markup(unicode):
+class Markup(str):
     """Marks a string as being safe for inclusion in HTML/XML output without
     needing to be escaped.
     """
     __slots__ = []
 
     def __add__(self, other):
-        return Markup(unicode.__add__(self, escape(other)))
+        return Markup(str.__add__(self, escape(other)))
 
     def __radd__(self, other):
-        return Markup(unicode.__add__(escape(other), self))
+        return Markup(str.__add__(escape(other), self))
 
     def __mod__(self, args):
         if isinstance(args, dict):
-            args = dict(zip(args.keys(), map(escape, args.values())))
+            args = dict(list(zip(list(args.keys()), list(map(escape, list(args.values()))))))
         elif isinstance(args, (list, tuple)):
             args = tuple(map(escape, args))
         else:
             args = escape(args)
-        return Markup(unicode.__mod__(self, args))
+        return Markup(str.__mod__(self, args))
 
     def __mul__(self, num):
-        return Markup(unicode.__mul__(self, num))
+        return Markup(str.__mul__(self, num))
     __rmul__ = __mul__
 
     def __repr__(self):
-        return "<%s %s>" % (type(self).__name__, unicode.__repr__(self))
+        return "<%s %s>" % (type(self).__name__, str.__repr__(self))
 
     def join(self, seq, escape_quotes=True):
         """Return a `Markup` object which is the concatenation of the strings
@@ -488,7 +488,7 @@ class Markup(unicode):
         :rtype: `Markup`
         :see: `escape`
         """
-        return Markup(unicode.join(self, [escape(item, quotes=escape_quotes)
+        return Markup(str.join(self, [escape(item, quotes=escape_quotes)
                                           for item in seq]))
 
     @classmethod
@@ -538,7 +538,7 @@ class Markup(unicode):
         """
         if not self:
             return ''
-        return unicode(self).replace('&#34;', '"') \
+        return str(self).replace('&#34;', '"') \
                             .replace('&gt;', '>') \
                             .replace('&lt;', '<') \
                             .replace('&amp;', '&')
@@ -652,7 +652,7 @@ class Namespace(object):
         self.uri = uri
 
     def __init__(self, uri):
-        self.uri = unicode(uri)
+        self.uri = str(uri)
 
     def __contains__(self, qname):
         return qname.namespace == self.uri
@@ -691,7 +691,7 @@ class Namespace(object):
 XML_NAMESPACE = Namespace('http://www.w3.org/XML/1998/namespace')
 
 
-class QName(unicode):
+class QName(str):
     """A qualified element or attribute name.
     
     The unicode value of instances of this class contains the qualified name of
@@ -729,11 +729,11 @@ class QName(unicode):
         qname = qname.lstrip('{')
         parts = qname.split('}', 1)
         if len(parts) > 1:
-            self = unicode.__new__(cls, '{%s' % qname)
-            self.namespace, self.localname = map(unicode, parts)
+            self = str.__new__(cls, '{%s' % qname)
+            self.namespace, self.localname = list(map(str, parts))
         else:
-            self = unicode.__new__(cls, qname)
-            self.namespace, self.localname = None, unicode(qname)
+            self = str.__new__(cls, qname)
+            self.namespace, self.localname = None, str(qname)
         return self
 
     def __getnewargs__(self):
--- genshi/filters/html.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/filters/html.py
@@ -101,13 +101,13 @@ class HTMLFormFiller(object):
                                 checked = False
                                 if isinstance(value, (list, tuple)):
                                     if declval is not None:
-                                        checked = declval in [unicode(v) for v
+                                        checked = declval in [str(v) for v
                                                               in value]
                                     else:
                                         checked = any(value)
                                 else:
                                     if declval is not None:
-                                        checked = declval == unicode(value)
+                                        checked = declval == str(value)
                                     elif type == 'checkbox':
                                         checked = bool(value)
                                 if checked:
@@ -123,7 +123,7 @@ class HTMLFormFiller(object):
                                     value = value[0]
                                 if value is not None:
                                     attrs |= [
-                                        (QName('value'), unicode(value))
+                                        (QName('value'), str(value))
                                     ]
                     elif tagname == 'select':
                         name = attrs.get('name')
@@ -166,10 +166,10 @@ class HTMLFormFiller(object):
                     select_value = None
                 elif in_select and tagname == 'option':
                     if isinstance(select_value, (tuple, list)):
-                        selected = option_value in [unicode(v) for v
+                        selected = option_value in [str(v) for v
                                                     in select_value]
                     else:
-                        selected = option_value == unicode(select_value)
+                        selected = option_value == str(select_value)
                     okind, (tag, attrs), opos = option_start
                     if selected:
                         attrs |= [(QName('selected'), 'selected')]
@@ -185,7 +185,7 @@ class HTMLFormFiller(object):
                     option_text = []
                 elif in_textarea and tagname == 'textarea':
                     if textarea_value:
-                        yield TEXT, unicode(textarea_value), pos
+                        yield TEXT, str(textarea_value), pos
                         textarea_value = None
                     in_textarea = False
                 yield kind, data, pos
@@ -311,7 +311,7 @@ class HTMLSanitizer(object):
         # The set of URI schemes that are considered safe.
 
     # IE6 <http://heideri.ch/jso/#80>
-    _EXPRESSION_SEARCH = re.compile(u"""
+    _EXPRESSION_SEARCH = re.compile("""
         [eE
          \uFF25 # FULLWIDTH LATIN CAPITAL LETTER E
          \uFF45 # FULLWIDTH LATIN SMALL LETTER E
@@ -356,7 +356,7 @@ class HTMLSanitizer(object):
     # IE6 <http://openmya.hacker.jp/hasegawa/security/expression.txt>
     #     7) Particular bit of Unicode characters
     _URL_FINDITER = re.compile(
-        u'[Uu][Rr\u0280][Ll\u029F]\s*\(([^)]+)').finditer
+        '[Uu][Rr\u0280][Ll\u029F]\s*\(([^)]+)').finditer
 
     def __call__(self, stream):
         """Apply the filter to the given stream.
@@ -528,7 +528,7 @@ class HTMLSanitizer(object):
         def _repl(match):
             t = match.group(1)
             if t:
-                return unichr(int(t, 16))
+                return chr(int(t, 16))
             t = match.group(2)
             if t == '\\':
                 return r'\\'
--- genshi/filters/i18n.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/filters/i18n.py
@@ -163,12 +163,12 @@ class MsgDirective(ExtractableI18NDirective):
 
         def _generate():
             msgbuf = MessageBuffer(self)
-            previous = stream.next()
+            previous = next(stream)
             if previous[0] is START:
                 yield previous
             else:
                 msgbuf.append(*previous)
-            previous = stream.next()
+            previous = next(stream)
             for kind, data, pos in stream:
                 msgbuf.append(*previous)
                 previous = kind, data, pos
@@ -188,13 +188,13 @@ class MsgDirective(ExtractableI18NDirective):
         strip = False
 
         stream = iter(stream)
-        previous = stream.next()
+        previous = next(stream)
         if previous[0] is START:
             for message in translator._extract_attrs(previous,
                                                      gettext_functions,
                                                      search_text=search_text):
                 yield message
-            previous = stream.next()
+            previous = next(stream)
             strip = True
         for event in stream:
             if event[0] is START:
@@ -218,14 +218,14 @@ class ChooseBranchDirective(I18NDirective):
         msgbuf = MessageBuffer(self)
         stream = _apply_directives(stream, directives, ctxt, vars)
 
-        previous = stream.next()
+        previous = next(stream)
         if previous[0] is START:
             yield previous
         else:
             msgbuf.append(*previous)
 
         try:
-            previous = stream.next()
+            previous = next(stream)
         except StopIteration:
             # For example <i18n:singular> or <i18n:plural> directives
             yield MSGBUF, (), -1 # the place holder for msgbuf output
@@ -246,7 +246,7 @@ class ChooseBranchDirective(I18NDirective):
     def extract(self, translator, stream, gettext_functions=GETTEXT_FUNCTIONS,
                 search_text=True, comment_stack=None, msgbuf=None):
         stream = iter(stream)
-        previous = stream.next()
+        previous = next(stream)
 
         if previous[0] is START:
             # skip the enclosing element
@@ -254,7 +254,7 @@ class ChooseBranchDirective(I18NDirective):
                                                      gettext_functions,
                                                      search_text=search_text):
                 yield message
-            previous = stream.next()
+            previous = next(stream)
 
         for event in stream:
             if previous[0] is START:
@@ -427,7 +427,7 @@ class ChooseDirective(ExtractableI18NDirective):
                 search_text=True, comment_stack=None):
         strip = False
         stream = iter(stream)
-        previous = stream.next()
+        previous = next(stream)
 
         if previous[0] is START:
             # skip the enclosing element
@@ -435,7 +435,7 @@ class ChooseDirective(ExtractableI18NDirective):
                                                      gettext_functions,
                                                      search_text=search_text):
                 yield message
-            previous = stream.next()
+            previous = next(stream)
             strip = True
 
         singular_msgbuf = MessageBuffer(self)
@@ -480,8 +480,8 @@ class ChooseDirective(ExtractableI18NDirective):
         # XXX: should we test which form was chosen like this!?!?!?
         # There should be no match in any catalogue for these singular and
         # plural test strings
-        singular = u'O\x85\xbe\xa9\xa8az\xc3?\xe6\xa1\x02n\x84\x93'
-        plural = u'\xcc\xfb+\xd3Pn\x9d\tT\xec\x1d\xda\x1a\x88\x00'
+        singular = 'O\x85\xbe\xa9\xa8az\xc3?\xe6\xa1\x02n\x84\x93'
+        plural = '\xcc\xfb+\xd3Pn\x9d\tT\xec\x1d\xda\x1a\x88\x00'
         return ngettext(singular, plural, numeral) == plural
 
 
@@ -703,7 +703,7 @@ class Translator(DirectiveFactory):
             if kind is START:
                 tag, attrs = data
                 if tag in self.ignore_tags or \
-                        isinstance(attrs.get(xml_lang), basestring):
+                        isinstance(attrs.get(xml_lang), str):
                     skip += 1
                     yield kind, data, pos
                     continue
@@ -713,7 +713,7 @@ class Translator(DirectiveFactory):
 
                 for name, value in attrs:
                     newval = value
-                    if isinstance(value, basestring):
+                    if isinstance(value, str):
                         if translate_attrs and name in include_attrs:
                             newval = gettext(value)
                     else:
@@ -732,7 +732,7 @@ class Translator(DirectiveFactory):
             elif translate_text and kind is TEXT:
                 text = data.strip()
                 if text:
-                    data = data.replace(text, unicode(gettext(text)))
+                    data = data.replace(text, str(gettext(text)))
                 yield kind, data, pos
 
             elif kind is SUB:
@@ -830,7 +830,7 @@ class Translator(DirectiveFactory):
             if kind is START and not skip:
                 tag, attrs = data
                 if tag in self.ignore_tags or \
-                        isinstance(attrs.get(xml_lang), basestring):
+                        isinstance(attrs.get(xml_lang), str):
                     skip += 1
                     continue
 
@@ -917,7 +917,7 @@ class Translator(DirectiveFactory):
 
     def _extract_attrs(self, event, gettext_functions, search_text):
         for name, value in event[1][1]:
-            if search_text and isinstance(value, basestring):
+            if search_text and isinstance(value, str):
                 if name in self.include_attrs:
                     text = value.strip()
                     if text:
@@ -1188,10 +1188,10 @@ def extract_from_code(code, gettext_functions):
             strings = []
             def _add(arg):
                 if isinstance(arg, _ast_Str) \
-                        and isinstance(_ast_Str_value(arg), unicode):
+                        and isinstance(_ast_Str_value(arg), str):
                     strings.append(_ast_Str_value(arg))
                 elif isinstance(arg, _ast_Str):
-                    strings.append(unicode(_ast_Str_value(arg), 'utf-8'))
+                    strings.append(str(_ast_Str_value(arg), 'utf-8'))
                 elif arg:
                     strings.append(None)
             [_add(arg) for arg in node.args]
@@ -1232,22 +1232,22 @@ def extract(fileobj, keywords, comment_tags, options):
     :rtype: ``iterator``
     """
     template_class = options.get('template_class', MarkupTemplate)
-    if isinstance(template_class, basestring):
+    if isinstance(template_class, str):
         module, clsname = template_class.split(':', 1)
         template_class = getattr(__import__(module, {}, {}, [clsname]), clsname)
     encoding = options.get('encoding', None)
 
     extract_text = options.get('extract_text', True)
-    if isinstance(extract_text, basestring):
+    if isinstance(extract_text, str):
         extract_text = extract_text.lower() in ('1', 'on', 'yes', 'true')
 
     ignore_tags = options.get('ignore_tags', Translator.IGNORE_TAGS)
-    if isinstance(ignore_tags, basestring):
+    if isinstance(ignore_tags, str):
         ignore_tags = ignore_tags.split()
     ignore_tags = [QName(tag) for tag in ignore_tags]
 
     include_attrs = options.get('include_attrs', Translator.INCLUDE_ATTRS)
-    if isinstance(include_attrs, basestring):
+    if isinstance(include_attrs, str):
         include_attrs = include_attrs.split()
     include_attrs = [QName(attr) for attr in include_attrs]
 
--- genshi/filters/tests/i18n.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/filters/tests/i18n.py
@@ -46,7 +46,7 @@ class DummyTranslations(NullTranslations):
             if tmsg is missing:
                 if self._fallback:
                     return self._fallback.ugettext(message)
-                return unicode(message)
+                return str(message)
             return tmsg
     else:
         def gettext(self, message):
@@ -55,7 +55,7 @@ class DummyTranslations(NullTranslations):
             if tmsg is missing:
                 if self._fallback:
                     return self._fallback.gettext(message)
-                return unicode(message)
+                return str(message)
             return tmsg
 
     if IS_PYTHON2:
@@ -94,10 +94,10 @@ class TranslatorTestCase(unittest.TestCase):
         """
         Verify that translated attributes end up in a proper `Attrs` instance.
         """
-        html = HTML(u"""<html>
+        html = HTML("""<html>
           <span title="Foo"></span>
         </html>""")
-        translator = Translator(lambda s: u"Voh")
+        translator = Translator(lambda s: "Voh")
         stream = list(html.filter(translator))
         kind, data, pos = stream[2]
         assert isinstance(data[1], Attrs)
@@ -139,7 +139,7 @@ class TranslatorTestCase(unittest.TestCase):
         translator = Translator()
         messages = list(translator.extract(tmpl.stream))
         self.assertEqual(1, len(messages))
-        self.assertEqual((2, 'gettext', u'Gr\xfc\xdfe', []), messages[0])
+        self.assertEqual((2, 'gettext', 'Gr\xfc\xdfe', []), messages[0])
 
     def test_extract_included_attribute_text(self):
         tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
@@ -237,10 +237,10 @@ class MsgDirectiveTestCase(unittest.TestCase):
             Please see <a href="help.html">Help</a> for details.
           </p>
         </html>""")
-        gettext = lambda s: u"Für Details siehe bitte [1:Hilfe]."
+        gettext = lambda s: "Für Details siehe bitte [1:Hilfe]."
         translator = Translator(gettext)
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p>Für Details siehe bitte <a href="help.html">Hilfe</a>.</p>
         </html>""".encode('utf-8'), tmpl.generate().render(encoding='utf-8'))
 
@@ -260,10 +260,10 @@ class MsgDirectiveTestCase(unittest.TestCase):
             xmlns:i18n="http://genshi.edgewall.org/i18n">
           <p i18n:msg="">Please see <a href="help.html">Help</a></p>
         </html>""")
-        gettext = lambda s: u"Für Details siehe bitte [1:Hilfe]"
+        gettext = lambda s: "Für Details siehe bitte [1:Hilfe]"
         translator = Translator(gettext)
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p>Für Details siehe bitte <a href="help.html">Hilfe</a></p>
         </html>""", tmpl.generate().render())
 
@@ -283,10 +283,10 @@ class MsgDirectiveTestCase(unittest.TestCase):
             xmlns:i18n="http://genshi.edgewall.org/i18n">
           <i18n:msg>Please see <a href="help.html">Help</a></i18n:msg>
         </html>""")
-        gettext = lambda s: u"Für Details siehe bitte [1:Hilfe]"
+        gettext = lambda s: "Für Details siehe bitte [1:Hilfe]"
         translator = Translator(gettext)
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           Für Details siehe bitte <a href="help.html">Hilfe</a>
         </html>""".encode('utf-8'), tmpl.generate().render(encoding='utf-8'))
 
@@ -317,11 +317,11 @@ class MsgDirectiveTestCase(unittest.TestCase):
         </html>""")
         translator = Translator(lambda msgid: {
             'A helpful paragraph': 'Ein hilfreicher Absatz',
-            'Click for help': u'Klicken für Hilfe',
-            'Please see [1:Help]': u'Siehe bitte [1:Hilfe]'
+            'Click for help': 'Klicken für Hilfe',
+            'Please see [1:Help]': 'Siehe bitte [1:Hilfe]'
         }[msgid])
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p title="Ein hilfreicher Absatz">Siehe bitte <a href="help.html" title="Klicken für Hilfe">Hilfe</a></p>
         </html>""", tmpl.generate().render(encoding=None))
 
@@ -352,11 +352,11 @@ class MsgDirectiveTestCase(unittest.TestCase):
         </html>""")
         translator = Translator(lambda msgid: {
             'A helpful paragraph': 'Ein hilfreicher Absatz',
-            'Click for help': u'Klicken für Hilfe',
-            'Please see [1:Help]': u'Siehe bitte [1:Hilfe]'
+            'Click for help': 'Klicken für Hilfe',
+            'Please see [1:Help]': 'Siehe bitte [1:Hilfe]'
         }[msgid])
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p title="Ein hilfreicher Absatz">Siehe bitte <a href="help.html" title="Klicken für Hilfe">Hilfe</a></p>
         </html>""", tmpl.generate(_=translator.translate).render(encoding=None))
 
@@ -384,11 +384,11 @@ class MsgDirectiveTestCase(unittest.TestCase):
           </i18n:msg>
         </html>""")
         translator = Translator(lambda msgid: {
-            'Click for help': u'Klicken für Hilfe',
-            'Please see [1:Help]': u'Siehe bitte [1:Hilfe]'
+            'Click for help': 'Klicken für Hilfe',
+            'Please see [1:Help]': 'Siehe bitte [1:Hilfe]'
         }[msgid])
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           Siehe bitte <a href="help.html" title="Klicken für Hilfe">Hilfe</a>
         </html>""", tmpl.generate().render(encoding=None))
 
@@ -413,10 +413,10 @@ class MsgDirectiveTestCase(unittest.TestCase):
             Please see <a href="help.html"><em>Help</em> page</a> for details.
           </p>
         </html>""")
-        gettext = lambda s: u"Für Details siehe bitte [1:[2:Hilfeseite]]."
+        gettext = lambda s: "Für Details siehe bitte [1:[2:Hilfeseite]]."
         translator = Translator(gettext)
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p>Für Details siehe bitte <a href="help.html"><em>Hilfeseite</em></a>.</p>
         </html>""", tmpl.generate().render())
 
@@ -468,10 +468,10 @@ class MsgDirectiveTestCase(unittest.TestCase):
             Show me <input type="text" name="num" /> entries per page.
           </p>
         </html>""")
-        gettext = lambda s: u"[1:] Einträge pro Seite anzeigen."
+        gettext = lambda s: "[1:] Einträge pro Seite anzeigen."
         translator = Translator(gettext)
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p><input type="text" name="num"/> Einträge pro Seite anzeigen.</p>
         </html>""", tmpl.generate().render())
 
@@ -495,10 +495,10 @@ class MsgDirectiveTestCase(unittest.TestCase):
             Please see <a href="help.html">Help</a> for <em>details</em>.
           </p>
         </html>""")
-        gettext = lambda s: u"Für [2:Details] siehe bitte [1:Hilfe]."
+        gettext = lambda s: "Für [2:Details] siehe bitte [1:Hilfe]."
         translator = Translator(gettext)
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p>Für <em>Details</em> siehe bitte <a href="help.html">Hilfe</a>.</p>
         </html>""", tmpl.generate().render())
 
@@ -523,10 +523,10 @@ class MsgDirectiveTestCase(unittest.TestCase):
             Show me <input type="text" name="num" /> entries per page, starting at page <input type="text" name="num" />.
           </p>
         </html>""", encoding='utf-8')
-        gettext = lambda s: u"[1:] Einträge pro Seite, beginnend auf Seite [2:]."
+        gettext = lambda s: "[1:] Einträge pro Seite, beginnend auf Seite [2:]."
         translator = Translator(gettext)
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p><input type="text" name="num"/> Eintr\u00E4ge pro Seite, beginnend auf Seite <input type="text" name="num"/>.</p>
         </html>""".encode('utf-8'), tmpl.generate().render(encoding='utf-8'))
 
@@ -550,7 +550,7 @@ class MsgDirectiveTestCase(unittest.TestCase):
             Hello, ${user.name}!
           </p>
         </html>""")
-        gettext = lambda s: u"Hallo, %(name)s!"
+        gettext = lambda s: "Hallo, %(name)s!"
         translator = Translator(gettext)
         translator.setup(tmpl)
         self.assertEqual("""<html>
@@ -564,10 +564,10 @@ class MsgDirectiveTestCase(unittest.TestCase):
             Hello, ${user.name}!
           </p>
         </html>""")
-        gettext = lambda s: u"%(name)s, sei gegrüßt!"
+        gettext = lambda s: "%(name)s, sei gegrüßt!"
         translator = Translator(gettext)
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p>Jim, sei gegrüßt!</p>
         </html>""", tmpl.generate(user=dict(name='Jim')).render())
 
@@ -578,10 +578,10 @@ class MsgDirectiveTestCase(unittest.TestCase):
             Hello, <a href="#${anchor}">dude</a>!
           </p>
         </html>""")
-        gettext = lambda s: u"Sei gegrüßt, [1:Alter]!"
+        gettext = lambda s: "Sei gegrüßt, [1:Alter]!"
         translator = Translator(gettext)
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p>Sei gegrüßt, <a href="#42">Alter</a>!</p>
         </html>""", tmpl.generate(anchor='42').render())
 
@@ -605,7 +605,7 @@ class MsgDirectiveTestCase(unittest.TestCase):
             Written by ${entry.author} at ${entry.time.strftime('%H:%M')}
           </p>
         </html>""")
-        gettext = lambda s: u"%(name)s schrieb dies um %(time)s"
+        gettext = lambda s: "%(name)s schrieb dies um %(time)s"
         translator = Translator(gettext)
         translator.setup(tmpl)
         entry = {
@@ -636,10 +636,10 @@ class MsgDirectiveTestCase(unittest.TestCase):
             Show me <input type="text" name="num" py:attrs="{'value': 'x'}" /> entries per page.
           </p>
         </html>""")
-        gettext = lambda s: u"[1:] Einträge pro Seite anzeigen."
+        gettext = lambda s: "[1:] Einträge pro Seite anzeigen."
         translator = Translator(gettext)
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p><input type="text" name="num" value="x"/> Einträge pro Seite anzeigen.</p>
         </html>""", tmpl.generate().render())
 
@@ -668,7 +668,7 @@ class MsgDirectiveTestCase(unittest.TestCase):
             xmlns:i18n="http://genshi.edgewall.org/i18n">
           <p i18n:msg="" i18n:comment="As in foo bar">Foo</p>
         </html>""")
-        gettext = lambda s: u"Voh"
+        gettext = lambda s: "Voh"
         translator = Translator(gettext)
         translator.setup(tmpl)
         self.assertEqual("""<html>
@@ -691,14 +691,14 @@ class MsgDirectiveTestCase(unittest.TestCase):
             xmlns:i18n="http://genshi.edgewall.org/i18n">
           <p i18n:msg="" title="Foo bar">Foo</p>
         </html>""")
-        gettext = lambda s: u"Voh"
+        gettext = lambda s: "Voh"
         translator = Translator(DummyTranslations({
             'Foo': 'Voh',
-            'Foo bar': u'Voh bär'
+            'Foo bar': 'Voh bär'
         }))
         tmpl.filters.insert(0, translator)
         tmpl.add_directives(Translator.NAMESPACE, translator)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p title="Voh bär">Voh</p>
         </html>""", tmpl.generate().render())
 
@@ -738,11 +738,11 @@ class MsgDirectiveTestCase(unittest.TestCase):
           </i18n:msg>
         </html>""")
         translations = DummyTranslations({
-            'Changed %(date)s ago by %(author)s': u'Modificado à %(date)s por %(author)s'
+            'Changed %(date)s ago by %(author)s': 'Modificado à %(date)s por %(author)s'
         })
         translator = Translator(translations)
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           Modificado à um dia por Pedro
         </html>""".encode('utf-8'), tmpl.generate(date='um dia', author="Pedro").render(encoding='utf-8'))
 
@@ -757,7 +757,7 @@ class MsgDirectiveTestCase(unittest.TestCase):
         messages = list(translator.extract(tmpl.stream))
         self.assertEqual(1, len(messages))
         self.assertEqual(
-            (3, None, u'[1:[2:Translation\\[\xa00\xa0\\]]: [3:One coin]]', []), messages[0]
+            (3, None, '[1:[2:Translation\\[\xa00\xa0\\]]: [3:One coin]]', []), messages[0]
         )
 
     def test_i18n_msg_ticket_251_translate(self):
@@ -766,12 +766,12 @@ class MsgDirectiveTestCase(unittest.TestCase):
           <p i18n:msg=""><tt><b>Translation[&nbsp;0&nbsp;]</b>: <em>One coin</em></tt></p>
         </html>""")
         translations = DummyTranslations({
-            u'[1:[2:Translation\\[\xa00\xa0\\]]: [3:One coin]]':
-                u'[1:[2:Trandução\\[\xa00\xa0\\]]: [3:Uma moeda]]'
+            '[1:[2:Translation\\[\xa00\xa0\\]]: [3:One coin]]':
+                '[1:[2:Trandução\\[\xa00\xa0\\]]: [3:Uma moeda]]'
         })
         translator = Translator(translations)
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p><tt><b>Trandução[ 0 ]</b>: <em>Uma moeda</em></tt></p>
         </html>""".encode('utf-8'), tmpl.generate().render(encoding='utf-8'))
 
@@ -820,12 +820,12 @@ class MsgDirectiveTestCase(unittest.TestCase):
             'before. For questions about installation\n            and '
             'configuration of Trac, please try the\n            '
             '[3:mailing list]\n            instead of filing a ticket.':
-                u'Antes de o fazer, porém,\n            '
-                u'[1:por favor tente [2:procurar]\n            por problemas semelhantes], uma vez que '
-                u'é muito provável que este problema\n            já tenha sido reportado '
-                u'anteriormente. Para questões relativas à instalação\n            e '
-                u'configuração do Trac, por favor tente a\n            '
-                u'[3:mailing list]\n            em vez de criar um assunto.'
+                'Antes de o fazer, porém,\n            '
+                '[1:por favor tente [2:procurar]\n            por problemas semelhantes], uma vez que '
+                'é muito provável que este problema\n            já tenha sido reportado '
+                'anteriormente. Para questões relativas à instalação\n            e '
+                'configuração do Trac, por favor tente a\n            '
+                '[3:mailing list]\n            em vez de criar um assunto.'
         })
         translator = Translator(translations)
         translator.setup(tmpl)
@@ -833,7 +833,7 @@ class MsgDirectiveTestCase(unittest.TestCase):
         self.assertEqual(1, len(messages))
         ctx = Context()
         ctx.push({'trac': {'homepage': 'http://trac.edgewall.org/'}})
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p>Antes de o fazer, porém,
             <strong>por favor tente <a href="http://trac.edgewall.org/search?ticket=yes&amp;noquickjump=1&amp;q=q">procurar</a>
             por problemas semelhantes</strong>, uma vez que é muito provável que este problema
@@ -856,8 +856,8 @@ class MsgDirectiveTestCase(unittest.TestCase):
         translations = DummyTranslations({
             '[1:Note:] This repository is defined in\n            '
             '[2:[3:trac.ini]]\n            and cannot be edited on this page.':
-                u'[1:Nota:] Este repositório está definido em \n           '
-                u'[2:[3:trac.ini]]\n            e não pode ser editado nesta página.',
+                '[1:Nota:] Este repositório está definido em \n           '
+                '[2:[3:trac.ini]]\n            e não pode ser editado nesta página.',
         })
         translator = Translator(translations)
         translator.setup(tmpl)
@@ -868,7 +868,7 @@ class MsgDirectiveTestCase(unittest.TestCase):
             '[2:[3:trac.ini]]\n            and cannot be edited on this page.',
             messages[0][2]
         )
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p class="hint"><strong>Nota:</strong> Este repositório está definido em
            <code><a href="href.wiki(TracIni)">trac.ini</a></code>
             e não pode ser editado nesta página.</p>
@@ -933,7 +933,7 @@ class MsgDirectiveTestCase(unittest.TestCase):
             xmlns:i18n="http://genshi.edgewall.org/i18n">
           <h1 i18n:msg="name">text <a>$name</a></h1>
         </html>""")
-        gettext = lambda s: u'head [1:%(name)s] tail'
+        gettext = lambda s: 'head [1:%(name)s] tail'
         translator = Translator(gettext)
         translator.setup(tmpl)
         self.assertEqual("""<html>
@@ -1342,12 +1342,12 @@ class ChooseDirectiveTestCase(unittest.TestCase):
         })
         translator = Translator(translations)
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p title="Sachen">
             Da ist <a href="/things" title="Sache betrachten">1 Sache</a>.
           </p>
         </html>""", tmpl.generate(link="/things", num=1).render(encoding=None))
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
           <p title="Sachen">
             Da sind <a href="/things" title="Sachen betrachten">3 Sachen</a>.
           </p>
@@ -1399,10 +1399,10 @@ class ChooseDirectiveTestCase(unittest.TestCase):
         })
         translator = Translator(translations)
         translator.setup(tmpl)
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
             <p title="Sachen">Da ist <a href="/things" title="Sache betrachten">1 Sache</a>.</p>
         </html>""", tmpl.generate(link="/things", num=1).render(encoding=None))
-        self.assertEqual(u"""<html>
+        self.assertEqual("""<html>
             <p title="Sachen">Da sind <a href="/things" title="Sachen betrachten">3 Sachen</a>.</p>
         </html>""", tmpl.generate(link="/things", num=3).render(encoding=None))
 
--- genshi/filters/tests/test_html.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/filters/tests/test_html.py
@@ -21,115 +21,115 @@ from genshi.template import MarkupTemplate
 class HTMLFormFillerTestCase(unittest.TestCase):
 
     def test_fill_input_text_no_value(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="text" name="foo" />
         </p></form>""") | HTMLFormFiller()
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="text" name="foo"/>
         </p></form>""", html.render())
 
     def test_fill_input_text_single_value(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="text" name="foo" />
         </p></form>""") | HTMLFormFiller(data={'foo': 'bar'})
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="text" name="foo" value="bar"/>
         </p></form>""", html.render())
 
     def test_fill_input_text_multi_value(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="text" name="foo" />
         </p></form>""") | HTMLFormFiller(data={'foo': ['bar']})
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="text" name="foo" value="bar"/>
         </p></form>""", html.render())
 
     def test_fill_input_hidden_no_value(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="hidden" name="foo" />
         </p></form>""") | HTMLFormFiller()
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="hidden" name="foo"/>
         </p></form>""", html.render())
 
     def test_fill_input_hidden_single_value(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="hidden" name="foo" />
         </p></form>""") | HTMLFormFiller(data={'foo': 'bar'})
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="hidden" name="foo" value="bar"/>
         </p></form>""", html.render())
 
     def test_fill_input_hidden_multi_value(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="hidden" name="foo" />
         </p></form>""") | HTMLFormFiller(data={'foo': ['bar']})
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="hidden" name="foo" value="bar"/>
         </p></form>""", html.render())
 
     def test_fill_textarea_no_value(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <textarea name="foo"></textarea>
         </p></form>""") | HTMLFormFiller()
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <textarea name="foo"/>
         </p></form>""", html.render())
 
     def test_fill_textarea_single_value(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <textarea name="foo"></textarea>
         </p></form>""") | HTMLFormFiller(data={'foo': 'bar'})
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <textarea name="foo">bar</textarea>
         </p></form>""", html.render())
 
     def test_fill_textarea_multi_value(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <textarea name="foo"></textarea>
         </p></form>""") | HTMLFormFiller(data={'foo': ['bar']})
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <textarea name="foo">bar</textarea>
         </p></form>""", html.render())
 
     def test_fill_textarea_multiple(self):
         # Ensure that the subsequent textarea doesn't get the data from the
         # first
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <textarea name="foo"></textarea>
           <textarea name="bar"></textarea>
         </p></form>""") | HTMLFormFiller(data={'foo': 'Some text'})
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <textarea name="foo">Some text</textarea>
           <textarea name="bar"/>
         </p></form>""", html.render())
 
     def test_fill_textarea_preserve_original(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <textarea name="foo"></textarea>
           <textarea name="bar">Original value</textarea>
         </p></form>""") | HTMLFormFiller(data={'foo': 'Some text'})
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <textarea name="foo">Some text</textarea>
           <textarea name="bar">Original value</textarea>
         </p></form>""", html.render())
 
     def test_fill_input_checkbox_single_value_auto_no_value(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="checkbox" name="foo" />
         </p></form>""") | HTMLFormFiller()
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="checkbox" name="foo"/>
         </p></form>""", html.render())
 
     def test_fill_input_checkbox_single_value_auto(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="checkbox" name="foo" />
         </p></form>""")
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="checkbox" name="foo"/>
         </p></form>""", (html | HTMLFormFiller(data={'foo': ''})).render())
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="checkbox" name="foo" checked="checked"/>
         </p></form>""", (html | HTMLFormFiller(data={'foo': 'on'})).render())
 
@@ -137,10 +137,10 @@ class HTMLFormFillerTestCase(unittest.TestCase):
         html = HTML("""<form><p>
           <input type="checkbox" name="foo" value="1" />
         </p></form>""", encoding='ascii')
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="checkbox" name="foo" value="1" checked="checked"/>
         </p></form>""", (html | HTMLFormFiller(data={'foo': '1'})).render())
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="checkbox" name="foo" value="1"/>
         </p></form>""", (html | HTMLFormFiller(data={'foo': '2'})).render())
 
@@ -148,79 +148,79 @@ class HTMLFormFillerTestCase(unittest.TestCase):
         html = HTML("""<form><p>
           <input type="checkbox" name="foo" />
         </p></form>""", encoding='ascii')
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="checkbox" name="foo"/>
         </p></form>""", (html | HTMLFormFiller(data={'foo': []})).render())
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="checkbox" name="foo" checked="checked"/>
         </p></form>""", (html | HTMLFormFiller(data={'foo': ['on']})).render())
 
     def test_fill_input_checkbox_multi_value_defined(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="checkbox" name="foo" value="1" />
         </p></form>""")
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="checkbox" name="foo" value="1" checked="checked"/>
         </p></form>""", (html | HTMLFormFiller(data={'foo': ['1']})).render())
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="checkbox" name="foo" value="1"/>
         </p></form>""", (html | HTMLFormFiller(data={'foo': ['2']})).render())
 
     def test_fill_input_radio_no_value(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="radio" name="foo" />
         </p></form>""") | HTMLFormFiller()
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="radio" name="foo"/>
         </p></form>""", html.render())
 
     def test_fill_input_radio_single_value(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="radio" name="foo" value="1" />
         </p></form>""")
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="radio" name="foo" value="1" checked="checked"/>
         </p></form>""", (html | HTMLFormFiller(data={'foo': '1'})).render())
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="radio" name="foo" value="1"/>
         </p></form>""", (html | HTMLFormFiller(data={'foo': '2'})).render())
 
     def test_fill_input_radio_multi_value(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="radio" name="foo" value="1" />
         </p></form>""")
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="radio" name="foo" value="1" checked="checked"/>
         </p></form>""", (html | HTMLFormFiller(data={'foo': ['1']})).render())
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="radio" name="foo" value="1"/>
         </p></form>""", (html | HTMLFormFiller(data={'foo': ['2']})).render())
 
     def test_fill_input_radio_empty_string(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="radio" name="foo" value="" />
         </p></form>""")
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="radio" name="foo" value="" checked="checked"/>
         </p></form>""", (html | HTMLFormFiller(data={'foo': ''})).render())
 
     def test_fill_input_radio_multi_empty_string(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="radio" name="foo" value="" />
         </p></form>""")
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="radio" name="foo" value="" checked="checked"/>
         </p></form>""", (html | HTMLFormFiller(data={'foo': ['']})).render())
 
     def test_fill_select_no_value_auto(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <select name="foo">
             <option>1</option>
             <option>2</option>
             <option>3</option>
           </select>
         </p></form>""") | HTMLFormFiller()
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <select name="foo">
             <option>1</option>
             <option>2</option>
@@ -229,14 +229,14 @@ class HTMLFormFillerTestCase(unittest.TestCase):
         </p></form>""", html.render())
 
     def test_fill_select_no_value_defined(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <select name="foo">
             <option value="1">1</option>
             <option value="2">2</option>
             <option value="3">3</option>
           </select>
         </p></form>""") | HTMLFormFiller()
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <select name="foo">
             <option value="1">1</option>
             <option value="2">2</option>
@@ -245,14 +245,14 @@ class HTMLFormFillerTestCase(unittest.TestCase):
         </p></form>""", html.render())
 
     def test_fill_select_single_value_auto(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <select name="foo">
             <option>1</option>
             <option>2</option>
             <option>3</option>
           </select>
         </p></form>""") | HTMLFormFiller(data={'foo': '1'})
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <select name="foo">
             <option selected="selected">1</option>
             <option>2</option>
@@ -261,14 +261,14 @@ class HTMLFormFillerTestCase(unittest.TestCase):
         </p></form>""", html.render())
 
     def test_fill_select_single_value_defined(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <select name="foo">
             <option value="1">1</option>
             <option value="2">2</option>
             <option value="3">3</option>
           </select>
         </p></form>""") | HTMLFormFiller(data={'foo': '1'})
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <select name="foo">
             <option value="1" selected="selected">1</option>
             <option value="2">2</option>
@@ -277,14 +277,14 @@ class HTMLFormFillerTestCase(unittest.TestCase):
         </p></form>""", html.render())
 
     def test_fill_select_multi_value_auto(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <select name="foo" multiple>
             <option>1</option>
             <option>2</option>
             <option>3</option>
           </select>
         </p></form>""") | HTMLFormFiller(data={'foo': ['1', '3']})
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <select name="foo" multiple="multiple">
             <option selected="selected">1</option>
             <option>2</option>
@@ -293,14 +293,14 @@ class HTMLFormFillerTestCase(unittest.TestCase):
         </p></form>""", html.render())
 
     def test_fill_select_multi_value_defined(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <select name="foo" multiple>
             <option value="1">1</option>
             <option value="2">2</option>
             <option value="3">3</option>
           </select>
         </p></form>""") | HTMLFormFiller(data={'foo': ['1', '3']})
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <select name="foo" multiple="multiple">
             <option value="1" selected="selected">1</option>
             <option value="2">2</option>
@@ -309,12 +309,12 @@ class HTMLFormFillerTestCase(unittest.TestCase):
         </p></form>""", html.render())
 
     def test_fill_option_segmented_text(self):
-        html = MarkupTemplate(u"""<form>
+        html = MarkupTemplate("""<form>
           <select name="foo">
             <option value="1">foo $x</option>
           </select>
         </form>""").generate(x=1) | HTMLFormFiller(data={'foo': '1'})
-        self.assertEquals(u"""<form>
+        self.assertEqual("""<form>
           <select name="foo">
             <option value="1" selected="selected">foo 1</option>
           </select>
@@ -326,37 +326,37 @@ class HTMLFormFillerTestCase(unittest.TestCase):
             <option>foo $x bar</option>
           </select>
         </form>""").generate(x=1) | HTMLFormFiller(data={'foo': 'foo 1 bar'})
-        self.assertEquals("""<form>
+        self.assertEqual("""<form>
           <select name="foo">
             <option selected="selected">foo 1 bar</option>
           </select>
         </form>""", html.render())
 
     def test_fill_option_unicode_value(self):
-        html = HTML(u"""<form>
+        html = HTML("""<form>
           <select name="foo">
             <option value="&ouml;">foo</option>
           </select>
-        </form>""") | HTMLFormFiller(data={'foo': u'ö'})
-        self.assertEquals(u"""<form>
+        </form>""") | HTMLFormFiller(data={'foo': 'ö'})
+        self.assertEqual("""<form>
           <select name="foo">
             <option value="ö" selected="selected">foo</option>
           </select>
         </form>""", html.render(encoding=None))
 
     def test_fill_input_password_disabled(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="password" name="pass" />
         </p></form>""") | HTMLFormFiller(data={'pass': 'bar'})
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="password" name="pass"/>
         </p></form>""", html.render())
 
     def test_fill_input_password_enabled(self):
-        html = HTML(u"""<form><p>
+        html = HTML("""<form><p>
           <input type="password" name="pass" />
         </p></form>""") | HTMLFormFiller(data={'pass': '1234'}, passwords=True)
-        self.assertEquals("""<form><p>
+        self.assertEqual("""<form><p>
           <input type="password" name="pass" value="1234"/>
         </p></form>""", html.render())
 
@@ -377,237 +377,237 @@ class HTMLSanitizerTestCase(unittest.TestCase):
         sanitized_html = (html | HTMLSanitizer()).render()
         if not sanitized_html and allow_strip:
             return
-        self.assertEquals(expected, sanitized_html)
+        self.assertEqual(expected, sanitized_html)
 
     def test_sanitize_unchanged(self):
-        html = HTML(u'<a href="#">fo<br />o</a>')
-        self.assertEquals('<a href="#">fo<br/>o</a>',
+        html = HTML('<a href="#">fo<br />o</a>')
+        self.assertEqual('<a href="#">fo<br/>o</a>',
                           (html | HTMLSanitizer()).render())
-        html = HTML(u'<a href="#with:colon">foo</a>')
-        self.assertEquals('<a href="#with:colon">foo</a>',
+        html = HTML('<a href="#with:colon">foo</a>')
+        self.assertEqual('<a href="#with:colon">foo</a>',
                           (html | HTMLSanitizer()).render())
 
     def test_sanitize_escape_text(self):
-        html = HTML(u'<a href="#">fo&amp;</a>')
-        self.assertEquals('<a href="#">fo&amp;</a>',
+        html = HTML('<a href="#">fo&amp;</a>')
+        self.assertEqual('<a href="#">fo&amp;</a>',
                           (html | HTMLSanitizer()).render())
-        html = HTML(u'<a href="#">&lt;foo&gt;</a>')
-        self.assertEquals('<a href="#">&lt;foo&gt;</a>',
+        html = HTML('<a href="#">&lt;foo&gt;</a>')
+        self.assertEqual('<a href="#">&lt;foo&gt;</a>',
                           (html | HTMLSanitizer()).render())
 
     def test_sanitize_entityref_text(self):
-        html = HTML(u'<a href="#">fo&ouml;</a>')
-        self.assertEquals(u'<a href="#">foö</a>',
+        html = HTML('<a href="#">fo&ouml;</a>')
+        self.assertEqual('<a href="#">foö</a>',
                           (html | HTMLSanitizer()).render(encoding=None))
 
     def test_sanitize_escape_attr(self):
-        html = HTML(u'<div title="&lt;foo&gt;"></div>')
-        self.assertEquals('<div title="&lt;foo&gt;"/>',
+        html = HTML('<div title="&lt;foo&gt;"></div>')
+        self.assertEqual('<div title="&lt;foo&gt;"/>',
                           (html | HTMLSanitizer()).render())
 
     def test_sanitize_close_empty_tag(self):
-        html = HTML(u'<a href="#">fo<br>o</a>')
-        self.assertEquals('<a href="#">fo<br/>o</a>',
+        html = HTML('<a href="#">fo<br>o</a>')
+        self.assertEqual('<a href="#">fo<br/>o</a>',
                           (html | HTMLSanitizer()).render())
 
     def test_sanitize_invalid_entity(self):
-        html = HTML(u'&junk;')
-        self.assertEquals('&amp;junk;', (html | HTMLSanitizer()).render())
+        html = HTML('&junk;')
+        self.assertEqual('&amp;junk;', (html | HTMLSanitizer()).render())
 
     def test_sanitize_remove_script_elem(self):
-        html = HTML(u'<script>alert("Foo")</script>')
-        self.assertEquals('', (html | HTMLSanitizer()).render())
-        html = HTML(u'<SCRIPT SRC="http://example.com/"></SCRIPT>')
-        self.assertEquals('', (html | HTMLSanitizer()).render())
-        src = u'<SCR\0IPT>alert("foo")</SCR\0IPT>'
+        html = HTML('<script>alert("Foo")</script>')
+        self.assertEqual('', (html | HTMLSanitizer()).render())
+        html = HTML('<SCRIPT SRC="http://example.com/"></SCRIPT>')
+        self.assertEqual('', (html | HTMLSanitizer()).render())
+        src = '<SCR\0IPT>alert("foo")</SCR\0IPT>'
         self.assert_parse_error_or_equal('&lt;SCR\x00IPT&gt;alert("foo")', src,
                                          allow_strip=True)
-        src = u'<SCRIPT&XYZ SRC="http://example.com/"></SCRIPT>'
+        src = '<SCRIPT&XYZ SRC="http://example.com/"></SCRIPT>'
         self.assert_parse_error_or_equal('&lt;SCRIPT&amp;XYZ; '
                                          'SRC="http://example.com/"&gt;', src,
                                          allow_strip=True)
 
     def test_sanitize_remove_onclick_attr(self):
-        html = HTML(u'<div onclick=\'alert("foo")\' />')
-        self.assertEquals('<div/>', (html | HTMLSanitizer()).render())
+        html = HTML('<div onclick=\'alert("foo")\' />')
+        self.assertEqual('<div/>', (html | HTMLSanitizer()).render())
 
     def test_sanitize_remove_input_password(self):
-        html = HTML(u'<form><input type="password" /></form>')
-        self.assertEquals('<form/>', (html | HTMLSanitizer()).render())
+        html = HTML('<form><input type="password" /></form>')
+        self.assertEqual('<form/>', (html | HTMLSanitizer()).render())
 
     def test_sanitize_remove_comments(self):
-        html = HTML(u'''<div><!-- conditional comment crap --></div>''')
-        self.assertEquals('<div/>', (html | HTMLSanitizer()).render())
+        html = HTML('''<div><!-- conditional comment crap --></div>''')
+        self.assertEqual('<div/>', (html | HTMLSanitizer()).render())
 
     def test_sanitize_remove_style_scripts(self):
         sanitizer = StyleSanitizer()
         # Inline style with url() using javascript: scheme
-        html = HTML(u'<DIV STYLE=\'background: url(javascript:alert("foo"))\'>')
-        self.assertEquals('<div/>', (html | sanitizer).render())
+        html = HTML('<DIV STYLE=\'background: url(javascript:alert("foo"))\'>')
+        self.assertEqual('<div/>', (html | sanitizer).render())
         # Inline style with url() using javascript: scheme, using control char
-        html = HTML(u'<DIV STYLE=\'background: url(&#1;javascript:alert("foo"))\'>')
-        self.assertEquals('<div/>', (html | sanitizer).render())
+        html = HTML('<DIV STYLE=\'background: url(&#1;javascript:alert("foo"))\'>')
+        self.assertEqual('<div/>', (html | sanitizer).render())
         # Inline style with url() using javascript: scheme, in quotes
-        html = HTML(u'<DIV STYLE=\'background: url("javascript:alert(foo)")\'>')
-        self.assertEquals('<div/>', (html | sanitizer).render())
+        html = HTML('<DIV STYLE=\'background: url("javascript:alert(foo)")\'>')
+        self.assertEqual('<div/>', (html | sanitizer).render())
         # IE expressions in CSS not allowed
-        html = HTML(u'<DIV STYLE=\'width: expression(alert("foo"));\'>')
-        self.assertEquals('<div/>', (html | sanitizer).render())
-        html = HTML(u'<DIV STYLE=\'width: e/**/xpression(alert("foo"));\'>')
-        self.assertEquals('<div/>', (html | sanitizer).render())
-        html = HTML(u'<DIV STYLE=\'background: url(javascript:alert("foo"));'
+        html = HTML('<DIV STYLE=\'width: expression(alert("foo"));\'>')
+        self.assertEqual('<div/>', (html | sanitizer).render())
+        html = HTML('<DIV STYLE=\'width: e/**/xpression(alert("foo"));\'>')
+        self.assertEqual('<div/>', (html | sanitizer).render())
+        html = HTML('<DIV STYLE=\'background: url(javascript:alert("foo"));'
                                  'color: #fff\'>')
-        self.assertEquals('<div style="color: #fff"/>',
+        self.assertEqual('<div style="color: #fff"/>',
                           (html | sanitizer).render())
         # Inline style with url() using javascript: scheme, using unicode
         # escapes
-        html = HTML(u'<DIV STYLE=\'background: \\75rl(javascript:alert("foo"))\'>')
-        self.assertEquals('<div/>', (html | sanitizer).render())
-        html = HTML(u'<DIV STYLE=\'background: \\000075rl(javascript:alert("foo"))\'>')
-        self.assertEquals('<div/>', (html | sanitizer).render())
-        html = HTML(u'<DIV STYLE=\'background: \\75 rl(javascript:alert("foo"))\'>')
-        self.assertEquals('<div/>', (html | sanitizer).render())
-        html = HTML(u'<DIV STYLE=\'background: \\000075 rl(javascript:alert("foo"))\'>')
-        self.assertEquals('<div/>', (html | sanitizer).render())
-        html = HTML(u'<DIV STYLE=\'background: \\000075\r\nrl(javascript:alert("foo"))\'>')
-        self.assertEquals('<div/>', (html | sanitizer).render())
+        html = HTML('<DIV STYLE=\'background: \\75rl(javascript:alert("foo"))\'>')
+        self.assertEqual('<div/>', (html | sanitizer).render())
+        html = HTML('<DIV STYLE=\'background: \\000075rl(javascript:alert("foo"))\'>')
+        self.assertEqual('<div/>', (html | sanitizer).render())
+        html = HTML('<DIV STYLE=\'background: \\75 rl(javascript:alert("foo"))\'>')
+        self.assertEqual('<div/>', (html | sanitizer).render())
+        html = HTML('<DIV STYLE=\'background: \\000075 rl(javascript:alert("foo"))\'>')
+        self.assertEqual('<div/>', (html | sanitizer).render())
+        html = HTML('<DIV STYLE=\'background: \\000075\r\nrl(javascript:alert("foo"))\'>')
+        self.assertEqual('<div/>', (html | sanitizer).render())
 
     def test_sanitize_remove_style_phishing(self):
         sanitizer = StyleSanitizer()
         # The position property is not allowed
-        html = HTML(u'<div style="position:absolute;top:0"></div>')
-        self.assertEquals('<div style="top:0"/>', (html | sanitizer).render())
+        html = HTML('<div style="position:absolute;top:0"></div>')
+        self.assertEqual('<div style="top:0"/>', (html | sanitizer).render())
         # Normal margins get passed through
-        html = HTML(u'<div style="margin:10px 20px"></div>')
-        self.assertEquals('<div style="margin:10px 20px"/>',
+        html = HTML('<div style="margin:10px 20px"></div>')
+        self.assertEqual('<div style="margin:10px 20px"/>',
                           (html | sanitizer).render())
         # But not negative margins
-        html = HTML(u'<div style="margin:-1000px 0 0"></div>')
-        self.assertEquals('<div/>', (html | sanitizer).render())
-        html = HTML(u'<div style="margin-left:-2000px 0 0"></div>')
-        self.assertEquals('<div/>', (html | sanitizer).render())
-        html = HTML(u'<div style="margin-left:1em 1em 1em -4000px"></div>')
-        self.assertEquals('<div/>', (html | sanitizer).render())
+        html = HTML('<div style="margin:-1000px 0 0"></div>')
+        self.assertEqual('<div/>', (html | sanitizer).render())
+        html = HTML('<div style="margin-left:-2000px 0 0"></div>')
+        self.assertEqual('<div/>', (html | sanitizer).render())
+        html = HTML('<div style="margin-left:1em 1em 1em -4000px"></div>')
+        self.assertEqual('<div/>', (html | sanitizer).render())
 
     def test_sanitize_remove_src_javascript(self):
-        html = HTML(u'<img src=\'javascript:alert("foo")\'>')
-        self.assertEquals('<img/>', (html | HTMLSanitizer()).render())
+        html = HTML('<img src=\'javascript:alert("foo")\'>')
+        self.assertEqual('<img/>', (html | HTMLSanitizer()).render())
         # Case-insensitive protocol matching
-        html = HTML(u'<IMG SRC=\'JaVaScRiPt:alert("foo")\'>')
-        self.assertEquals('<img/>', (html | HTMLSanitizer()).render())
+        html = HTML('<IMG SRC=\'JaVaScRiPt:alert("foo")\'>')
+        self.assertEqual('<img/>', (html | HTMLSanitizer()).render())
         # Grave accents (not parsed)
-        src = u'<IMG SRC=`javascript:alert("RSnake says, \'foo\'")`>'
+        src = '<IMG SRC=`javascript:alert("RSnake says, \'foo\'")`>'
         self.assert_parse_error_or_equal('<img/>', src)
         # Protocol encoded using UTF-8 numeric entities
-        html = HTML(u'<IMG SRC=\'&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;'
+        html = HTML('<IMG SRC=\'&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;'
                     '&#112;&#116;&#58;alert("foo")\'>')
-        self.assertEquals('<img/>', (html | HTMLSanitizer()).render())
+        self.assertEqual('<img/>', (html | HTMLSanitizer()).render())
         # Protocol encoded using UTF-8 numeric entities without a semicolon
         # (which is allowed because the max number of digits is used)
-        html = HTML(u'<IMG SRC=\'&#0000106&#0000097&#0000118&#0000097'
+        html = HTML('<IMG SRC=\'&#0000106&#0000097&#0000118&#0000097'
                     '&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116'
                     '&#0000058alert("foo")\'>')
-        self.assertEquals('<img/>', (html | HTMLSanitizer()).render())
+        self.assertEqual('<img/>', (html | HTMLSanitizer()).render())
         # Protocol encoded using UTF-8 numeric hex entities without a semicolon
         # (which is allowed because the max number of digits is used)
-        html = HTML(u'<IMG SRC=\'&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69'
+        html = HTML('<IMG SRC=\'&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69'
                     '&#x70&#x74&#x3A;alert("foo")\'>')
-        self.assertEquals('<img/>', (html | HTMLSanitizer()).render())
+        self.assertEqual('<img/>', (html | HTMLSanitizer()).render())
         # Embedded tab character in protocol
-        html = HTML(u'<IMG SRC=\'jav\tascript:alert("foo");\'>')
-        self.assertEquals('<img/>', (html | HTMLSanitizer()).render())
+        html = HTML('<IMG SRC=\'jav\tascript:alert("foo");\'>')
+        self.assertEqual('<img/>', (html | HTMLSanitizer()).render())
         # Embedded tab character in protocol, but encoded this time
-        html = HTML(u'<IMG SRC=\'jav&#x09;ascript:alert("foo");\'>')
-        self.assertEquals('<img/>', (html | HTMLSanitizer()).render())
+        html = HTML('<IMG SRC=\'jav&#x09;ascript:alert("foo");\'>')
+        self.assertEqual('<img/>', (html | HTMLSanitizer()).render())
 
     def test_sanitize_expression(self):
-        html = HTML(ur'<div style="top:expression(alert())">XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML(r'<div style="top:expression(alert())">XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
     def test_capital_expression(self):
-        html = HTML(ur'<div style="top:EXPRESSION(alert())">XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML(r'<div style="top:EXPRESSION(alert())">XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
     def test_sanitize_url_with_javascript(self):
-        html = HTML(u'<div style="background-image:url(javascript:alert())">'
-                    u'XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML('<div style="background-image:url(javascript:alert())">'
+                    'XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
     def test_sanitize_capital_url_with_javascript(self):
-        html = HTML(u'<div style="background-image:URL(javascript:alert())">'
-                    u'XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML('<div style="background-image:URL(javascript:alert())">'
+                    'XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
     def test_sanitize_unicode_escapes(self):
-        html = HTML(ur'<div style="top:exp\72 ess\000069 on(alert())">'
-                    ur'XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML(r'<div style="top:exp\72 ess\000069 on(alert())">'
+                    r'XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
     def test_sanitize_backslash_without_hex(self):
-        html = HTML(ur'<div style="top:e\xp\ression(alert())">XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
-        html = HTML(ur'<div style="top:e\\xp\\ression(alert())">XSS</div>')
+        html = HTML(r'<div style="top:e\xp\ression(alert())">XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
+        html = HTML(r'<div style="top:e\\xp\\ression(alert())">XSS</div>')
         self.assertEqual(r'<div style="top:e\\xp\\ression(alert())">'
                          'XSS</div>',
-                         unicode(html | StyleSanitizer()))
+                         str(html | StyleSanitizer()))
 
     def test_sanitize_unsafe_props(self):
-        html = HTML(u'<div style="POSITION:RELATIVE">XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML('<div style="POSITION:RELATIVE">XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
-        html = HTML(u'<div style="behavior:url(test.htc)">XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML('<div style="behavior:url(test.htc)">XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
-        html = HTML(u'<div style="-ms-behavior:url(test.htc) url(#obj)">'
-                    u'XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML('<div style="-ms-behavior:url(test.htc) url(#obj)">'
+                    'XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
-        html = HTML(u"""<div style="-o-link:'javascript:alert(1)';"""
-                    u"""-o-link-source:current">XSS</div>""")
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML("""<div style="-o-link:'javascript:alert(1)';"""
+                    """-o-link-source:current">XSS</div>""")
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
-        html = HTML(u"""<div style="-moz-binding:url(xss.xbl)">XSS</div>""")
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML("""<div style="-moz-binding:url(xss.xbl)">XSS</div>""")
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
     def test_sanitize_negative_margin(self):
-        html = HTML(u'<div style="margin-top:-9999px">XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
-        html = HTML(u'<div style="margin:0 -9999px">XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML('<div style="margin-top:-9999px">XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
+        html = HTML('<div style="margin:0 -9999px">XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
     def test_sanitize_css_hack(self):
-        html = HTML(u'<div style="*position:static">XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML('<div style="*position:static">XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
-        html = HTML(u'<div style="_margin:-10px">XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML('<div style="_margin:-10px">XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
     def test_sanitize_property_name(self):
-        html = HTML(u'<div style="display:none;border-left-color:red;'
-                    u'user_defined:1;-moz-user-selct:-moz-all">prop</div>')
+        html = HTML('<div style="display:none;border-left-color:red;'
+                    'user_defined:1;-moz-user-selct:-moz-all">prop</div>')
         self.assertEqual('<div style="display:none; border-left-color:red'
                          '">prop</div>',
-                         unicode(html | StyleSanitizer()))
+                         str(html | StyleSanitizer()))
 
     def test_sanitize_unicode_expression(self):
         # Fullwidth small letters
-        html = HTML(u'<div style="top:ｅｘｐｒｅｓｓｉｏｎ(alert())">'
-                    u'XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML('<div style="top:ｅｘｐｒｅｓｓｉｏｎ(alert())">'
+                    'XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
         # Fullwidth capital letters
-        html = HTML(u'<div style="top:ＥＸＰＲＥＳＳＩＯＮ(alert())">'
-                    u'XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML('<div style="top:ＥＸＰＲＥＳＳＩＯＮ(alert())">'
+                    'XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
         # IPA extensions
-        html = HTML(u'<div style="top:expʀessɪoɴ(alert())">'
-                    u'XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML('<div style="top:expʀessɪoɴ(alert())">'
+                    'XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
     def test_sanitize_unicode_url(self):
         # IPA extensions
-        html = HTML(u'<div style="background-image:uʀʟ(javascript:alert())">'
-                    u'XSS</div>')
-        self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
+        html = HTML('<div style="background-image:uʀʟ(javascript:alert())">'
+                    'XSS</div>')
+        self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
 
 
 def suite():
--- genshi/filters/tests/transform.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/filters/tests/transform.py
@@ -33,24 +33,24 @@ def _simplify(stream, with_attrs=False):
         for mark, (kind, data, pos) in stream:
             if kind is START:
                 if with_attrs:
-                    data = (unicode(data[0]), dict((unicode(k), v)
+                    data = (str(data[0]), dict((str(k), v)
                                                    for k, v in data[1]))
                 else:
-                    data = unicode(data[0])
+                    data = str(data[0])
             elif kind is END:
-                data = unicode(data)
+                data = str(data)
             elif kind is ATTR:
                 kind = ATTR
-                data = dict((unicode(k), v) for k, v in data[1])
+                data = dict((str(k), v) for k, v in data[1])
             yield mark, kind, data
     return list(_generate())
 
 
 def _transform(html, transformer, with_attrs=False):
     """Apply transformation returning simplified marked stream."""
-    if isinstance(html, basestring) and not isinstance(html, unicode):
+    if isinstance(html, str) and not isinstance(html, str):
         html = HTML(html, encoding='utf-8')
-    elif isinstance(html, unicode):
+    elif isinstance(html, str):
         html = HTML(html, encoding='utf-8')
     stream = transformer(html, keep_marks=True)
     return _simplify(stream, with_attrs)
@@ -60,7 +60,7 @@ class SelectTest(unittest.TestCase):
     """Test .select()"""
     def _select(self, select):
         html = HTML(FOOBAR, encoding='utf-8')
-        if isinstance(select, basestring):
+        if isinstance(select, str):
             select = [select]
         transformer = Transformer(select[0])
         for sel in select[1:]:
@@ -70,78 +70,78 @@ class SelectTest(unittest.TestCase):
     def test_select_single_element(self):
         self.assertEqual(
             self._select('foo'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')],
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')],
             )
 
     def test_select_context(self):
         self.assertEqual(
             self._select('.'),
-            [(ENTER, START, u'root'),
-             (INSIDE, TEXT, u'ROOT'),
-             (INSIDE, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (INSIDE, END, u'foo'),
-             (INSIDE, START, u'bar'),
-             (INSIDE, TEXT, u'BAR'),
-             (INSIDE, END, u'bar'),
-             (EXIT, END, u'root')]
+            [(ENTER, START, 'root'),
+             (INSIDE, TEXT, 'ROOT'),
+             (INSIDE, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (INSIDE, END, 'foo'),
+             (INSIDE, START, 'bar'),
+             (INSIDE, TEXT, 'BAR'),
+             (INSIDE, END, 'bar'),
+             (EXIT, END, 'root')]
             )
 
     def test_select_inside_select(self):
         self.assertEqual(
             self._select(['.', 'foo']),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')],
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')],
             )
 
     def test_select_text(self):
         self.assertEqual(
             self._select('*/text()'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, START, u'foo'),
-             (OUTSIDE, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (None, START, u'bar'),
-             (OUTSIDE, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')],
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, START, 'foo'),
+             (OUTSIDE, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (None, START, 'bar'),
+             (OUTSIDE, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')],
             )
 
     def test_select_attr(self):
         self.assertEqual(
             self._select('foo/@name'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ATTR, ATTR, {'name': u'foo'}),
-             (None, START, u'foo'),
-             (None, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ATTR, ATTR, {'name': 'foo'}),
+             (None, START, 'foo'),
+             (None, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_select_text_context(self):
         self.assertEqual(
-            list(Transformer('.')(HTML(u'foo'), keep_marks=True)),
-            [('OUTSIDE', ('TEXT', u'foo', (None, 1, 0)))],
+            list(Transformer('.')(HTML('foo'), keep_marks=True)),
+            [('OUTSIDE', ('TEXT', 'foo', (None, 1, 0)))],
             )
 
 
@@ -152,63 +152,63 @@ class InvertTest(unittest.TestCase):
     def test_invert_element(self):
         self.assertEqual(
             self._invert('foo'),
-            [(OUTSIDE, START, u'root'),
-             (OUTSIDE, TEXT, u'ROOT'),
-             (None, START, u'foo'),
-             (None, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (OUTSIDE, END, u'root')]
+            [(OUTSIDE, START, 'root'),
+             (OUTSIDE, TEXT, 'ROOT'),
+             (None, START, 'foo'),
+             (None, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (OUTSIDE, END, 'root')]
             )
 
     def test_invert_inverted_element(self):
         self.assertEqual(
             _transform(FOO, Transformer('foo').invert().invert()),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (OUTSIDE, START, u'foo'),
-             (OUTSIDE, TEXT, u'FOO'),
-             (OUTSIDE, END, u'foo'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (OUTSIDE, START, 'foo'),
+             (OUTSIDE, TEXT, 'FOO'),
+             (OUTSIDE, END, 'foo'),
+             (None, END, 'root')]
             )
 
     def test_invert_text(self):
         self.assertEqual(
             self._invert('foo/text()'),
-            [(OUTSIDE, START, u'root'),
-             (OUTSIDE, TEXT, u'ROOT'),
-             (OUTSIDE, START, u'foo'),
-             (None, TEXT, u'FOO'),
-             (OUTSIDE, END, u'foo'),
-             (OUTSIDE, END, u'root')]
+            [(OUTSIDE, START, 'root'),
+             (OUTSIDE, TEXT, 'ROOT'),
+             (OUTSIDE, START, 'foo'),
+             (None, TEXT, 'FOO'),
+             (OUTSIDE, END, 'foo'),
+             (OUTSIDE, END, 'root')]
             )
 
     def test_invert_attribute(self):
         self.assertEqual(
             self._invert('foo/@name'),
-            [(OUTSIDE, START, u'root'),
-             (OUTSIDE, TEXT, u'ROOT'),
-             (None, ATTR, {'name': u'foo'}),
-             (OUTSIDE, START, u'foo'),
-             (OUTSIDE, TEXT, u'FOO'),
-             (OUTSIDE, END, u'foo'),
-             (OUTSIDE, END, u'root')]
+            [(OUTSIDE, START, 'root'),
+             (OUTSIDE, TEXT, 'ROOT'),
+             (None, ATTR, {'name': 'foo'}),
+             (OUTSIDE, START, 'foo'),
+             (OUTSIDE, TEXT, 'FOO'),
+             (OUTSIDE, END, 'foo'),
+             (OUTSIDE, END, 'root')]
             )
 
     def test_invert_context(self):
         self.assertEqual(
             self._invert('.'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, START, u'foo'),
-             (None, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, START, 'foo'),
+             (None, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (None, END, 'root')]
             )
 
     def test_invert_text_context(self):
         self.assertEqual(
-            _simplify(Transformer('.').invert()(HTML(u'foo'), keep_marks=True)),
-            [(None, 'TEXT', u'foo')],
+            _simplify(Transformer('.').invert()(HTML('foo'), keep_marks=True)),
+            [(None, 'TEXT', 'foo')],
             )
 
 
@@ -218,12 +218,12 @@ class EndTest(unittest.TestCase):
         stream = _transform(FOO, Transformer('foo').end())
         self.assertEqual(
             stream,
-            [(OUTSIDE, START, u'root'),
-             (OUTSIDE, TEXT, u'ROOT'),
-             (OUTSIDE, START, u'foo'),
-             (OUTSIDE, TEXT, u'FOO'),
-             (OUTSIDE, END, u'foo'),
-             (OUTSIDE, END, u'root')]
+            [(OUTSIDE, START, 'root'),
+             (OUTSIDE, TEXT, 'ROOT'),
+             (OUTSIDE, START, 'foo'),
+             (OUTSIDE, TEXT, 'FOO'),
+             (OUTSIDE, END, 'foo'),
+             (OUTSIDE, END, 'root')]
             )
 
 
@@ -234,47 +234,47 @@ class EmptyTest(unittest.TestCase):
     def test_empty_element(self):
         self.assertEqual(
             self._empty('foo'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, u'foo'),
-             (EXIT, END, u'foo'),
-             (None, END, u'root')],
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, 'foo'),
+             (EXIT, END, 'foo'),
+             (None, END, 'root')],
             )
 
     def test_empty_text(self):
         self.assertEqual(
             self._empty('foo/text()'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, START, u'foo'),
-             (OUTSIDE, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, START, 'foo'),
+             (OUTSIDE, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (None, END, 'root')]
             )
 
     def test_empty_attr(self):
         self.assertEqual(
             self._empty('foo/@name'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ATTR, ATTR, {'name': u'foo'}),
-             (None, START, u'foo'),
-             (None, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ATTR, ATTR, {'name': 'foo'}),
+             (None, START, 'foo'),
+             (None, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (None, END, 'root')]
             )
 
     def test_empty_context(self):
         self.assertEqual(
             self._empty('.'),
-            [(ENTER, START, u'root'),
-             (EXIT, END, u'root')]
+            [(ENTER, START, 'root'),
+             (EXIT, END, 'root')]
             )
 
     def test_empty_text_context(self):
         self.assertEqual(
-            _simplify(Transformer('.')(HTML(u'foo'), keep_marks=True)),
-            [(OUTSIDE, TEXT, u'foo')],
+            _simplify(Transformer('.')(HTML('foo'), keep_marks=True)),
+            [(OUTSIDE, TEXT, 'foo')],
             )
 
 
@@ -285,29 +285,29 @@ class RemoveTest(unittest.TestCase):
     def test_remove_element(self):
         self.assertEqual(
             self._remove('foo|bar'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, END, 'root')]
             )
 
     def test_remove_text(self):
         self.assertEqual(
             self._remove('//text()'),
-            [(None, START, u'root'),
-             (None, START, u'foo'),
-             (None, END, u'foo'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, START, 'foo'),
+             (None, END, 'foo'),
+             (None, END, 'root')]
             )
 
     def test_remove_attr(self):
         self.assertEqual(
             self._remove('foo/@name'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, START, u'foo'),
-             (None, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, START, 'foo'),
+             (None, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (None, END, 'root')]
             )
 
     def test_remove_context(self):
@@ -330,52 +330,52 @@ class UnwrapText(unittest.TestCase):
     def test_unwrap_element(self):
         self.assertEqual(
             self._unwrap('foo'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (INSIDE, TEXT, u'FOO'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (INSIDE, TEXT, 'FOO'),
+             (None, END, 'root')]
             )
 
     def test_unwrap_text(self):
         self.assertEqual(
             self._unwrap('foo/text()'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, START, u'foo'),
-             (OUTSIDE, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, START, 'foo'),
+             (OUTSIDE, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (None, END, 'root')]
             )
 
     def test_unwrap_attr(self):
         self.assertEqual(
             self._unwrap('foo/@name'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ATTR, ATTR, {'name': u'foo'}),
-             (None, START, u'foo'),
-             (None, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ATTR, ATTR, {'name': 'foo'}),
+             (None, START, 'foo'),
+             (None, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (None, END, 'root')]
             )
 
     def test_unwrap_adjacent(self):
         self.assertEqual(
             _transform(FOOBAR, Transformer('foo|bar').unwrap()),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (INSIDE, TEXT, u'FOO'),
-             (INSIDE, TEXT, u'BAR'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (INSIDE, TEXT, 'FOO'),
+             (INSIDE, TEXT, 'BAR'),
+             (None, END, 'root')]
             )
 
     def test_unwrap_root(self):
         self.assertEqual(
             self._unwrap('.'),
-            [(INSIDE, TEXT, u'ROOT'),
-             (INSIDE, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (INSIDE, END, u'foo')]
+            [(INSIDE, TEXT, 'ROOT'),
+             (INSIDE, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (INSIDE, END, 'foo')]
             )
 
     def test_unwrap_text_root(self):
@@ -392,75 +392,75 @@ class WrapTest(unittest.TestCase):
     def test_wrap_element(self):
         self.assertEqual(
             self._wrap('foo'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, START, u'wrap'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, END, u'wrap'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, START, 'wrap'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, END, 'wrap'),
+             (None, END, 'root')]
             )
 
     def test_wrap_adjacent_elements(self):
         self.assertEqual(
             _transform(FOOBAR, Transformer('foo|bar').wrap('wrap')),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, START, u'wrap'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, END, u'wrap'),
-             (None, START, u'wrap'),
-             (ENTER, START, u'bar'),
-             (INSIDE, TEXT, u'BAR'),
-             (EXIT, END, u'bar'),
-             (None, END, u'wrap'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, START, 'wrap'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, END, 'wrap'),
+             (None, START, 'wrap'),
+             (ENTER, START, 'bar'),
+             (INSIDE, TEXT, 'BAR'),
+             (EXIT, END, 'bar'),
+             (None, END, 'wrap'),
+             (None, END, 'root')]
             )
 
     def test_wrap_text(self):
         self.assertEqual(
             self._wrap('foo/text()'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, START, u'foo'),
-             (None, START, u'wrap'),
-             (OUTSIDE, TEXT, u'FOO'),
-             (None, END, u'wrap'),
-             (None, END, u'foo'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, START, 'foo'),
+             (None, START, 'wrap'),
+             (OUTSIDE, TEXT, 'FOO'),
+             (None, END, 'wrap'),
+             (None, END, 'foo'),
+             (None, END, 'root')]
             )
 
     def test_wrap_root(self):
         self.assertEqual(
             self._wrap('.'),
-            [(None, START, u'wrap'),
-             (ENTER, START, u'root'),
-             (INSIDE, TEXT, u'ROOT'),
-             (INSIDE, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (INSIDE, END, u'foo'),
-             (EXIT, END, u'root'),
-             (None, END, u'wrap')]
+            [(None, START, 'wrap'),
+             (ENTER, START, 'root'),
+             (INSIDE, TEXT, 'ROOT'),
+             (INSIDE, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (INSIDE, END, 'foo'),
+             (EXIT, END, 'root'),
+             (None, END, 'wrap')]
             )
 
     def test_wrap_text_root(self):
         self.assertEqual(
             _transform('foo', Transformer('.').wrap('wrap')),
-            [(None, START, u'wrap'),
-             (OUTSIDE, TEXT, u'foo'),
-             (None, END, u'wrap')],
+            [(None, START, 'wrap'),
+             (OUTSIDE, TEXT, 'foo'),
+             (None, END, 'wrap')],
             )
 
     def test_wrap_with_element(self):
         element = Element('a', href='http://localhost')
         self.assertEqual(
             _transform('foo', Transformer('.').wrap(element), with_attrs=True),
-            [(None, START, (u'a', {u'href': u'http://localhost'})),
-             (OUTSIDE, TEXT, u'foo'),
-             (None, END, u'a')]
+            [(None, START, ('a', {'href': 'http://localhost'})),
+             (OUTSIDE, TEXT, 'foo'),
+             (None, END, 'a')]
             )
 
 
@@ -483,55 +483,55 @@ class FilterTest(unittest.TestCase):
     def test_filter_element(self):
         self.assertEqual(
             self._filter('foo'),
-            [[(None, START, u'foo'),
-              (None, TEXT, u'FOO'),
-              (None, END, u'foo')]]
+            [[(None, START, 'foo'),
+              (None, TEXT, 'FOO'),
+              (None, END, 'foo')]]
             )
 
     def test_filter_adjacent_elements(self):
         self.assertEqual(
             self._filter('foo|bar'),
-            [[(None, START, u'foo'),
-              (None, TEXT, u'FOO'),
-              (None, END, u'foo')],
-             [(None, START, u'bar'),
-              (None, TEXT, u'BAR'),
-              (None, END, u'bar')]]
+            [[(None, START, 'foo'),
+              (None, TEXT, 'FOO'),
+              (None, END, 'foo')],
+             [(None, START, 'bar'),
+              (None, TEXT, 'BAR'),
+              (None, END, 'bar')]]
             )
 
     def test_filter_text(self):
         self.assertEqual(
             self._filter('*/text()'),
-            [[(None, TEXT, u'FOO')],
-             [(None, TEXT, u'BAR')]]
+            [[(None, TEXT, 'FOO')],
+             [(None, TEXT, 'BAR')]]
             )
     def test_filter_root(self):
         self.assertEqual(
             self._filter('.'),
-            [[(None, START, u'root'),
-              (None, TEXT, u'ROOT'),
-              (None, START, u'foo'),
-              (None, TEXT, u'FOO'),
-              (None, END, u'foo'),
-              (None, START, u'bar'),
-              (None, TEXT, u'BAR'),
-              (None, END, u'bar'),
-              (None, END, u'root')]]
+            [[(None, START, 'root'),
+              (None, TEXT, 'ROOT'),
+              (None, START, 'foo'),
+              (None, TEXT, 'FOO'),
+              (None, END, 'foo'),
+              (None, START, 'bar'),
+              (None, TEXT, 'BAR'),
+              (None, END, 'bar'),
+              (None, END, 'root')]]
             )
 
     def test_filter_text_root(self):
         self.assertEqual(
             self._filter('.', 'foo'),
-            [[(None, TEXT, u'foo')]])
+            [[(None, TEXT, 'foo')]])
 
     def test_filter_after_outside(self):
         stream = _transform(
             '<root>x</root>', Transformer('//root/text()').filter(lambda x: x))
         self.assertEqual(
             list(stream),
-            [(None, START, u'root'),
-             (OUTSIDE, TEXT, u'x'),
-             (None, END, u'root')])
+            [(None, START, 'root'),
+             (OUTSIDE, TEXT, 'x'),
+             (None, END, 'root')])
 
 
 class MapTest(unittest.TestCase):
@@ -546,16 +546,16 @@ class MapTest(unittest.TestCase):
     def test_map_element(self):
         self.assertEqual(
             self._map('foo'),
-            [(QName('foo'), Attrs([(QName('name'), u'foo'),
-                                   (QName('size'), u'100')])),
-             u'FOO',
+            [(QName('foo'), Attrs([(QName('name'), 'foo'),
+                                   (QName('size'), '100')])),
+             'FOO',
              QName('foo')]
         )
 
     def test_map_with_text_kind(self):
         self.assertEqual(
             self._map('.', TEXT),
-            [u'ROOT', u'FOO', u'BAR']
+            ['ROOT', 'FOO', 'BAR']
         )
 
     def test_map_with_root_and_end_kind(self):
@@ -567,7 +567,7 @@ class MapTest(unittest.TestCase):
     def test_map_with_attribute(self):
         self.assertEqual(
             self._map('foo/@name'),
-            [(QName('foo@*'), Attrs([('name', u'foo')]))]
+            [(QName('foo@*'), Attrs([('name', 'foo')]))]
         )
 
 
@@ -578,29 +578,29 @@ class SubstituteTest(unittest.TestCase):
     def test_substitute_foo(self):
         self.assertEqual(
             self._substitute('foo', 'FOO|BAR', 'FOOOOO'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'FOOOOO'),
-             (EXIT, END, u'foo'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, 'FOOOOO'),
+             (EXIT, END, 'foo'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_substitute_foobar_with_group(self):
         self.assertEqual(
             self._substitute('foo|bar', '(FOO|BAR)', r'(\1)'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'(FOO)'),
-             (EXIT, END, u'foo'),
-             (ENTER, START, u'bar'),
-             (INSIDE, TEXT, u'(BAR)'),
-             (EXIT, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, '(FOO)'),
+             (EXIT, END, 'foo'),
+             (ENTER, START, 'bar'),
+             (INSIDE, TEXT, '(BAR)'),
+             (EXIT, END, 'bar'),
+             (None, END, 'root')]
             )
 
 
@@ -611,43 +611,43 @@ class RenameTest(unittest.TestCase):
     def test_rename_root(self):
         self.assertEqual(
             self._rename('.'),
-            [(ENTER, START, u'foobar'),
-             (INSIDE, TEXT, u'ROOT'),
-             (INSIDE, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (INSIDE, END, u'foo'),
-             (INSIDE, START, u'bar'),
-             (INSIDE, TEXT, u'BAR'),
-             (INSIDE, END, u'bar'),
-             (EXIT, END, u'foobar')]
+            [(ENTER, START, 'foobar'),
+             (INSIDE, TEXT, 'ROOT'),
+             (INSIDE, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (INSIDE, END, 'foo'),
+             (INSIDE, START, 'bar'),
+             (INSIDE, TEXT, 'BAR'),
+             (INSIDE, END, 'bar'),
+             (EXIT, END, 'foobar')]
             )
 
     def test_rename_element(self):
         self.assertEqual(
             self._rename('foo|bar'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, u'foobar'),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foobar'),
-             (ENTER, START, u'foobar'),
-             (INSIDE, TEXT, u'BAR'),
-             (EXIT, END, u'foobar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, 'foobar'),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foobar'),
+             (ENTER, START, 'foobar'),
+             (INSIDE, TEXT, 'BAR'),
+             (EXIT, END, 'foobar'),
+             (None, END, 'root')]
             )
 
     def test_rename_text(self):
         self.assertEqual(
             self._rename('foo/text()'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, START, u'foo'),
-             (OUTSIDE, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, START, 'foo'),
+             (OUTSIDE, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
 
@@ -658,15 +658,15 @@ class ContentTestMixin(object):
 
             def __iter__(self):
                 self.count += 1
-                return iter(HTML(u'CONTENT %i' % self.count))
+                return iter(HTML('CONTENT %i' % self.count))
 
-        if isinstance(html, basestring) and not isinstance(html, unicode):
+        if isinstance(html, str) and not isinstance(html, str):
             html = HTML(html, encoding='utf-8')
         else:
             html = HTML(html)
         if content is None:
             content = Injector()
-        elif isinstance(content, basestring):
+        elif isinstance(content, str):
             content = HTML(content)
         return _transform(html, getattr(Transformer(select), self.operation)
                                 (content))
@@ -678,59 +678,59 @@ class ReplaceTest(unittest.TestCase, ContentTestMixin)
     def test_replace_element(self):
         self.assertEqual(
             self._apply('foo'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, TEXT, u'CONTENT 1'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, TEXT, 'CONTENT 1'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_replace_text(self):
         self.assertEqual(
             self._apply('text()'),
-            [(None, START, u'root'),
-             (None, TEXT, u'CONTENT 1'),
-             (None, START, u'foo'),
-             (None, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'CONTENT 1'),
+             (None, START, 'foo'),
+             (None, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_replace_context(self):
         self.assertEqual(
             self._apply('.'),
-            [(None, TEXT, u'CONTENT 1')],
+            [(None, TEXT, 'CONTENT 1')],
             )
 
     def test_replace_text_context(self):
         self.assertEqual(
             self._apply('.', html='foo'),
-            [(None, TEXT, u'CONTENT 1')],
+            [(None, TEXT, 'CONTENT 1')],
             )
 
     def test_replace_adjacent_elements(self):
         self.assertEqual(
             self._apply('*'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, TEXT, u'CONTENT 1'),
-             (None, TEXT, u'CONTENT 2'),
-             (None, END, u'root')],
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, TEXT, 'CONTENT 1'),
+             (None, TEXT, 'CONTENT 2'),
+             (None, END, 'root')],
             )
 
     def test_replace_all(self):
         self.assertEqual(
             self._apply('*|text()'),
-            [(None, START, u'root'),
-             (None, TEXT, u'CONTENT 1'),
-             (None, TEXT, u'CONTENT 2'),
-             (None, TEXT, u'CONTENT 3'),
-             (None, END, u'root')],
+            [(None, START, 'root'),
+             (None, TEXT, 'CONTENT 1'),
+             (None, TEXT, 'CONTENT 2'),
+             (None, TEXT, 'CONTENT 3'),
+             (None, END, 'root')],
             )
 
     def test_replace_with_callback(self):
@@ -740,11 +740,11 @@ class ReplaceTest(unittest.TestCase, ContentTestMixin)
             yield '%2i.' % count[0]
         self.assertEqual(
             self._apply('*', content),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, TEXT, u' 1.'),
-             (None, TEXT, u' 2.'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, TEXT, ' 1.'),
+             (None, TEXT, ' 2.'),
+             (None, END, 'root')]
             )
 
 
@@ -754,87 +754,87 @@ class BeforeTest(unittest.TestCase, ContentTestMixin):
     def test_before_element(self):
         self.assertEqual(
             self._apply('foo'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, TEXT, u'CONTENT 1'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, TEXT, 'CONTENT 1'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_before_text(self):
         self.assertEqual(
             self._apply('text()'),
-            [(None, START, u'root'),
-             (None, TEXT, u'CONTENT 1'),
-             (OUTSIDE, TEXT, u'ROOT'),
-             (None, START, u'foo'),
-             (None, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'CONTENT 1'),
+             (OUTSIDE, TEXT, 'ROOT'),
+             (None, START, 'foo'),
+             (None, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_before_context(self):
         self.assertEqual(
             self._apply('.'),
-            [(None, TEXT, u'CONTENT 1'),
-             (ENTER, START, u'root'),
-             (INSIDE, TEXT, u'ROOT'),
-             (INSIDE, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (INSIDE, END, u'foo'),
-             (INSIDE, START, u'bar'),
-             (INSIDE, TEXT, u'BAR'),
-             (INSIDE, END, u'bar'),
-             (EXIT, END, u'root')]
+            [(None, TEXT, 'CONTENT 1'),
+             (ENTER, START, 'root'),
+             (INSIDE, TEXT, 'ROOT'),
+             (INSIDE, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (INSIDE, END, 'foo'),
+             (INSIDE, START, 'bar'),
+             (INSIDE, TEXT, 'BAR'),
+             (INSIDE, END, 'bar'),
+             (EXIT, END, 'root')]
             )
 
     def test_before_text_context(self):
         self.assertEqual(
             self._apply('.', html='foo'),
-            [(None, TEXT, u'CONTENT 1'),
-             (OUTSIDE, TEXT, u'foo')]
+            [(None, TEXT, 'CONTENT 1'),
+             (OUTSIDE, TEXT, 'foo')]
             )
 
     def test_before_adjacent_elements(self):
         self.assertEqual(
             self._apply('*'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (None, TEXT, u'CONTENT 1'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, TEXT, u'CONTENT 2'),
-             (ENTER, START, u'bar'),
-             (INSIDE, TEXT, u'BAR'),
-             (EXIT, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (None, TEXT, 'CONTENT 1'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, TEXT, 'CONTENT 2'),
+             (ENTER, START, 'bar'),
+             (INSIDE, TEXT, 'BAR'),
+             (EXIT, END, 'bar'),
+             (None, END, 'root')]
 
             )
 
     def test_before_all(self):
         self.assertEqual(
             self._apply('*|text()'),
-            [(None, START, u'root'),
-             (None, TEXT, u'CONTENT 1'),
-             (OUTSIDE, TEXT, u'ROOT'),
-             (None, TEXT, u'CONTENT 2'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, TEXT, u'CONTENT 3'),
-             (ENTER, START, u'bar'),
-             (INSIDE, TEXT, u'BAR'),
-             (EXIT, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'CONTENT 1'),
+             (OUTSIDE, TEXT, 'ROOT'),
+             (None, TEXT, 'CONTENT 2'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, TEXT, 'CONTENT 3'),
+             (ENTER, START, 'bar'),
+             (INSIDE, TEXT, 'BAR'),
+             (EXIT, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_before_with_callback(self):
@@ -844,16 +844,16 @@ class BeforeTest(unittest.TestCase, ContentTestMixin):
             yield '%2i.' % count[0]
         self.assertEqual(
             self._apply('foo/text()', content),
-            [(None, 'START', u'root'),
-             (None, 'TEXT', u'ROOT'),
-             (None, 'START', u'foo'),
-             (None, 'TEXT', u' 1.'),
-             ('OUTSIDE', 'TEXT', u'FOO'),
-             (None, 'END', u'foo'),
-             (None, 'START', u'bar'),
-             (None, 'TEXT', u'BAR'),
-             (None, 'END', u'bar'),
-             (None, 'END', u'root')]
+            [(None, 'START', 'root'),
+             (None, 'TEXT', 'ROOT'),
+             (None, 'START', 'foo'),
+             (None, 'TEXT', ' 1.'),
+             ('OUTSIDE', 'TEXT', 'FOO'),
+             (None, 'END', 'foo'),
+             (None, 'START', 'bar'),
+             (None, 'TEXT', 'BAR'),
+             (None, 'END', 'bar'),
+             (None, 'END', 'root')]
             )
 
 
@@ -863,87 +863,87 @@ class AfterTest(unittest.TestCase, ContentTestMixin):
     def test_after_element(self):
         self.assertEqual(
             self._apply('foo'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, TEXT, u'CONTENT 1'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, TEXT, 'CONTENT 1'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_after_text(self):
         self.assertEqual(
             self._apply('text()'),
-            [(None, START, u'root'),
-             (OUTSIDE, TEXT, u'ROOT'),
-             (None, TEXT, u'CONTENT 1'),
-             (None, START, u'foo'),
-             (None, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (OUTSIDE, TEXT, 'ROOT'),
+             (None, TEXT, 'CONTENT 1'),
+             (None, START, 'foo'),
+             (None, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_after_context(self):
         self.assertEqual(
             self._apply('.'),
-            [(ENTER, START, u'root'),
-             (INSIDE, TEXT, u'ROOT'),
-             (INSIDE, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (INSIDE, END, u'foo'),
-             (INSIDE, START, u'bar'),
-             (INSIDE, TEXT, u'BAR'),
-             (INSIDE, END, u'bar'),
-             (EXIT, END, u'root'),
-             (None, TEXT, u'CONTENT 1')]
+            [(ENTER, START, 'root'),
+             (INSIDE, TEXT, 'ROOT'),
+             (INSIDE, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (INSIDE, END, 'foo'),
+             (INSIDE, START, 'bar'),
+             (INSIDE, TEXT, 'BAR'),
+             (INSIDE, END, 'bar'),
+             (EXIT, END, 'root'),
+             (None, TEXT, 'CONTENT 1')]
             )
 
     def test_after_text_context(self):
         self.assertEqual(
             self._apply('.', html='foo'),
-            [(OUTSIDE, TEXT, u'foo'),
-             (None, TEXT, u'CONTENT 1')]
+            [(OUTSIDE, TEXT, 'foo'),
+             (None, TEXT, 'CONTENT 1')]
             )
 
     def test_after_adjacent_elements(self):
         self.assertEqual(
             self._apply('*'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, TEXT, u'CONTENT 1'),
-             (ENTER, START, u'bar'),
-             (INSIDE, TEXT, u'BAR'),
-             (EXIT, END, u'bar'),
-             (None, TEXT, u'CONTENT 2'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, TEXT, 'CONTENT 1'),
+             (ENTER, START, 'bar'),
+             (INSIDE, TEXT, 'BAR'),
+             (EXIT, END, 'bar'),
+             (None, TEXT, 'CONTENT 2'),
+             (None, END, 'root')]
 
             )
 
     def test_after_all(self):
         self.assertEqual(
             self._apply('*|text()'),
-            [(None, START, u'root'),
-             (OUTSIDE, TEXT, u'ROOT'),
-             (None, TEXT, u'CONTENT 1'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, TEXT, u'CONTENT 2'),
-             (ENTER, START, u'bar'),
-             (INSIDE, TEXT, u'BAR'),
-             (EXIT, END, u'bar'),
-             (None, TEXT, u'CONTENT 3'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (OUTSIDE, TEXT, 'ROOT'),
+             (None, TEXT, 'CONTENT 1'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, TEXT, 'CONTENT 2'),
+             (ENTER, START, 'bar'),
+             (INSIDE, TEXT, 'BAR'),
+             (EXIT, END, 'bar'),
+             (None, TEXT, 'CONTENT 3'),
+             (None, END, 'root')]
             )
 
     def test_after_with_callback(self):
@@ -953,16 +953,16 @@ class AfterTest(unittest.TestCase, ContentTestMixin):
             yield '%2i.' % count[0]
         self.assertEqual(
             self._apply('foo/text()', content),
-            [(None, 'START', u'root'),
-             (None, 'TEXT', u'ROOT'),
-             (None, 'START', u'foo'),
-             ('OUTSIDE', 'TEXT', u'FOO'),
-             (None, 'TEXT', u' 1.'),
-             (None, 'END', u'foo'),
-             (None, 'START', u'bar'),
-             (None, 'TEXT', u'BAR'),
-             (None, 'END', u'bar'),
-             (None, 'END', u'root')]
+            [(None, 'START', 'root'),
+             (None, 'TEXT', 'ROOT'),
+             (None, 'START', 'foo'),
+             ('OUTSIDE', 'TEXT', 'FOO'),
+             (None, 'TEXT', ' 1.'),
+             (None, 'END', 'foo'),
+             (None, 'START', 'bar'),
+             (None, 'TEXT', 'BAR'),
+             (None, 'END', 'bar'),
+             (None, 'END', 'root')]
             )
 
 
@@ -972,84 +972,84 @@ class PrependTest(unittest.TestCase, ContentTestMixin)
     def test_prepend_element(self):
         self.assertEqual(
             self._apply('foo'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, u'foo'),
-             (None, TEXT, u'CONTENT 1'),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, 'foo'),
+             (None, TEXT, 'CONTENT 1'),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_prepend_text(self):
         self.assertEqual(
             self._apply('text()'),
-            [(None, START, u'root'),
-             (OUTSIDE, TEXT, u'ROOT'),
-             (None, START, u'foo'),
-             (None, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (OUTSIDE, TEXT, 'ROOT'),
+             (None, START, 'foo'),
+             (None, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_prepend_context(self):
         self.assertEqual(
             self._apply('.'),
-            [(ENTER, START, u'root'),
-             (None, TEXT, u'CONTENT 1'),
-             (INSIDE, TEXT, u'ROOT'),
-             (INSIDE, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (INSIDE, END, u'foo'),
-             (INSIDE, START, u'bar'),
-             (INSIDE, TEXT, u'BAR'),
-             (INSIDE, END, u'bar'),
-             (EXIT, END, u'root')],
+            [(ENTER, START, 'root'),
+             (None, TEXT, 'CONTENT 1'),
+             (INSIDE, TEXT, 'ROOT'),
+             (INSIDE, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (INSIDE, END, 'foo'),
+             (INSIDE, START, 'bar'),
+             (INSIDE, TEXT, 'BAR'),
+             (INSIDE, END, 'bar'),
+             (EXIT, END, 'root')],
             )
 
     def test_prepend_text_context(self):
         self.assertEqual(
             self._apply('.', html='foo'),
-            [(OUTSIDE, TEXT, u'foo')]
+            [(OUTSIDE, TEXT, 'foo')]
             )
 
     def test_prepend_adjacent_elements(self):
         self.assertEqual(
             self._apply('*'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, u'foo'),
-             (None, TEXT, u'CONTENT 1'),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (ENTER, START, u'bar'),
-             (None, TEXT, u'CONTENT 2'),
-             (INSIDE, TEXT, u'BAR'),
-             (EXIT, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, 'foo'),
+             (None, TEXT, 'CONTENT 1'),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (ENTER, START, 'bar'),
+             (None, TEXT, 'CONTENT 2'),
+             (INSIDE, TEXT, 'BAR'),
+             (EXIT, END, 'bar'),
+             (None, END, 'root')]
 
             )
 
     def test_prepend_all(self):
         self.assertEqual(
             self._apply('*|text()'),
-            [(None, START, u'root'),
-             (OUTSIDE, TEXT, u'ROOT'),
-             (ENTER, START, u'foo'),
-             (None, TEXT, u'CONTENT 1'),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (ENTER, START, u'bar'),
-             (None, TEXT, u'CONTENT 2'),
-             (INSIDE, TEXT, u'BAR'),
-             (EXIT, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (OUTSIDE, TEXT, 'ROOT'),
+             (ENTER, START, 'foo'),
+             (None, TEXT, 'CONTENT 1'),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (ENTER, START, 'bar'),
+             (None, TEXT, 'CONTENT 2'),
+             (INSIDE, TEXT, 'BAR'),
+             (EXIT, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_prepend_with_callback(self):
@@ -1059,16 +1059,16 @@ class PrependTest(unittest.TestCase, ContentTestMixin)
             yield '%2i.' % count[0]
         self.assertEqual(
             self._apply('foo', content),
-            [(None, 'START', u'root'),
-             (None, 'TEXT', u'ROOT'),
-             (ENTER, 'START', u'foo'),
-             (None, 'TEXT', u' 1.'),
-             (INSIDE, 'TEXT', u'FOO'),
-             (EXIT, 'END', u'foo'),
-             (None, 'START', u'bar'),
-             (None, 'TEXT', u'BAR'),
-             (None, 'END', u'bar'),
-             (None, 'END', u'root')]
+            [(None, 'START', 'root'),
+             (None, 'TEXT', 'ROOT'),
+             (ENTER, 'START', 'foo'),
+             (None, 'TEXT', ' 1.'),
+             (INSIDE, 'TEXT', 'FOO'),
+             (EXIT, 'END', 'foo'),
+             (None, 'START', 'bar'),
+             (None, 'TEXT', 'BAR'),
+             (None, 'END', 'bar'),
+             (None, 'END', 'root')]
             )
 
 
@@ -1078,84 +1078,84 @@ class AppendTest(unittest.TestCase, ContentTestMixin):
     def test_append_element(self):
         self.assertEqual(
             self._apply('foo'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (None, TEXT, u'CONTENT 1'),
-             (EXIT, END, u'foo'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (None, TEXT, 'CONTENT 1'),
+             (EXIT, END, 'foo'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_append_text(self):
         self.assertEqual(
             self._apply('text()'),
-            [(None, START, u'root'),
-             (OUTSIDE, TEXT, u'ROOT'),
-             (None, START, u'foo'),
-             (None, TEXT, u'FOO'),
-             (None, END, u'foo'),
-             (None, START, u'bar'),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (OUTSIDE, TEXT, 'ROOT'),
+             (None, START, 'foo'),
+             (None, TEXT, 'FOO'),
+             (None, END, 'foo'),
+             (None, START, 'bar'),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_append_context(self):
         self.assertEqual(
             self._apply('.'),
-            [(ENTER, START, u'root'),
-             (INSIDE, TEXT, u'ROOT'),
-             (INSIDE, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (INSIDE, END, u'foo'),
-             (INSIDE, START, u'bar'),
-             (INSIDE, TEXT, u'BAR'),
-             (INSIDE, END, u'bar'),
-             (None, TEXT, u'CONTENT 1'),
-             (EXIT, END, u'root')],
+            [(ENTER, START, 'root'),
+             (INSIDE, TEXT, 'ROOT'),
+             (INSIDE, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (INSIDE, END, 'foo'),
+             (INSIDE, START, 'bar'),
+             (INSIDE, TEXT, 'BAR'),
+             (INSIDE, END, 'bar'),
+             (None, TEXT, 'CONTENT 1'),
+             (EXIT, END, 'root')],
             )
 
     def test_append_text_context(self):
         self.assertEqual(
             self._apply('.', html='foo'),
-            [(OUTSIDE, TEXT, u'foo')]
+            [(OUTSIDE, TEXT, 'foo')]
             )
 
     def test_append_adjacent_elements(self):
         self.assertEqual(
             self._apply('*'),
-            [(None, START, u'root'),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (None, TEXT, u'CONTENT 1'),
-             (EXIT, END, u'foo'),
-             (ENTER, START, u'bar'),
-             (INSIDE, TEXT, u'BAR'),
-             (None, TEXT, u'CONTENT 2'),
-             (EXIT, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (None, TEXT, 'CONTENT 1'),
+             (EXIT, END, 'foo'),
+             (ENTER, START, 'bar'),
+             (INSIDE, TEXT, 'BAR'),
+             (None, TEXT, 'CONTENT 2'),
+             (EXIT, END, 'bar'),
+             (None, END, 'root')]
 
             )
 
     def test_append_all(self):
         self.assertEqual(
             self._apply('*|text()'),
-            [(None, START, u'root'),
-             (OUTSIDE, TEXT, u'ROOT'),
-             (ENTER, START, u'foo'),
-             (INSIDE, TEXT, u'FOO'),
-             (None, TEXT, u'CONTENT 1'),
-             (EXIT, END, u'foo'),
-             (ENTER, START, u'bar'),
-             (INSIDE, TEXT, u'BAR'),
-             (None, TEXT, u'CONTENT 2'),
-             (EXIT, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, 'root'),
+             (OUTSIDE, TEXT, 'ROOT'),
+             (ENTER, START, 'foo'),
+             (INSIDE, TEXT, 'FOO'),
+             (None, TEXT, 'CONTENT 1'),
+             (EXIT, END, 'foo'),
+             (ENTER, START, 'bar'),
+             (INSIDE, TEXT, 'BAR'),
+             (None, TEXT, 'CONTENT 2'),
+             (EXIT, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_append_with_callback(self):
@@ -1165,16 +1165,16 @@ class AppendTest(unittest.TestCase, ContentTestMixin):
             yield '%2i.' % count[0]
         self.assertEqual(
             self._apply('foo', content),
-            [(None, 'START', u'root'),
-             (None, 'TEXT', u'ROOT'),
-             (ENTER, 'START', u'foo'),
-             (INSIDE, 'TEXT', u'FOO'),
-             (None, 'TEXT', u' 1.'),
-             (EXIT, 'END', u'foo'),
-             (None, 'START', u'bar'),
-             (None, 'TEXT', u'BAR'),
-             (None, 'END', u'bar'),
-             (None, 'END', u'root')]
+            [(None, 'START', 'root'),
+             (None, 'TEXT', 'ROOT'),
+             (ENTER, 'START', 'foo'),
+             (INSIDE, 'TEXT', 'FOO'),
+             (None, 'TEXT', ' 1.'),
+             (EXIT, 'END', 'foo'),
+             (None, 'START', 'bar'),
+             (None, 'TEXT', 'BAR'),
+             (None, 'END', 'bar'),
+             (None, 'END', 'root')]
             )
 
 
@@ -1187,29 +1187,29 @@ class AttrTest(unittest.TestCase):
     def test_set_existing_attr(self):
         self.assertEqual(
             self._attr('foo', 'name', 'FOO'),
-            [(None, START, (u'root', {})),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, (u'foo', {u'name': 'FOO', u'size': '100'})),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, START, (u'bar', {u'name': u'bar'})),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, ('root', {})),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, ('foo', {'name': 'FOO', 'size': '100'})),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, START, ('bar', {'name': 'bar'})),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_set_new_attr(self):
         self.assertEqual(
             self._attr('foo', 'title', 'FOO'),
-            [(None, START, (u'root', {})),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, (u'foo', {u'name': u'foo', u'title': 'FOO', u'size': '100'})),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, START, (u'bar', {u'name': u'bar'})),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, ('root', {})),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, ('foo', {'name': 'foo', 'title': 'FOO', 'size': '100'})),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, START, ('bar', {'name': 'bar'})),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_attr_from_function(self):
@@ -1219,29 +1219,29 @@ class AttrTest(unittest.TestCase):
 
         self.assertEqual(
             self._attr('foo|bar', 'name', set),
-            [(None, START, (u'root', {})),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, (u'foo', {u'name': 'FOO', u'size': '100'})),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (ENTER, START, (u'bar', {u'name': 'BAR'})),
-             (INSIDE, TEXT, u'BAR'),
-             (EXIT, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, ('root', {})),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, ('foo', {'name': 'FOO', 'size': '100'})),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (ENTER, START, ('bar', {'name': 'BAR'})),
+             (INSIDE, TEXT, 'BAR'),
+             (EXIT, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_remove_attr(self):
         self.assertEqual(
             self._attr('foo', 'name', None),
-            [(None, START, (u'root', {})),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, (u'foo', {u'size': '100'})),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, START, (u'bar', {u'name': u'bar'})),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, ('root', {})),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, ('foo', {'size': '100'})),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, START, ('bar', {'name': 'bar'})),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
     def test_remove_attr_with_function(self):
@@ -1250,15 +1250,15 @@ class AttrTest(unittest.TestCase):
 
         self.assertEqual(
             self._attr('foo', 'name', set),
-            [(None, START, (u'root', {})),
-             (None, TEXT, u'ROOT'),
-             (ENTER, START, (u'foo', {u'size': '100'})),
-             (INSIDE, TEXT, u'FOO'),
-             (EXIT, END, u'foo'),
-             (None, START, (u'bar', {u'name': u'bar'})),
-             (None, TEXT, u'BAR'),
-             (None, END, u'bar'),
-             (None, END, u'root')]
+            [(None, START, ('root', {})),
+             (None, TEXT, 'ROOT'),
+             (ENTER, START, ('foo', {'size': '100'})),
+             (INSIDE, TEXT, 'FOO'),
+             (EXIT, END, 'foo'),
+             (None, START, ('bar', {'name': 'bar'})),
+             (None, TEXT, 'BAR'),
+             (None, END, 'bar'),
+             (None, END, 'root')]
             )
 
 
@@ -1294,65 +1294,65 @@ class CopyTest(unittest.TestCase, BufferTestMixin):
     def test_copy_element(self):
         self.assertEqual(
             self._apply('foo')[1],
-            [[(None, START, u'foo'),
-              (None, TEXT, u'FOO'),
-              (None, END, u'foo')]]
+            [[(None, START, 'foo'),
+              (None, TEXT, 'FOO'),
+              (None, END, 'foo')]]
             )
 
     def test_copy_adjacent_elements(self):
         self.assertEqual(
             self._apply('foo|bar')[1],
-            [[(None, START, u'foo'),
-              (None, TEXT, u'FOO'),
-              (None, END, u'foo')],
-             [(None, START, u'bar'),
-              (None, TEXT, u'BAR'),
-              (None, END, u'bar')]]
+            [[(None, START, 'foo'),
+              (None, TEXT, 'FOO'),
+              (None, END, 'foo')],
+             [(None, START, 'bar'),
+              (None, TEXT, 'BAR'),
+              (None, END, 'bar')]]
             )
 
     def test_copy_all(self):
         self.assertEqual(
             self._apply('*|text()')[1],
-            [[(None, TEXT, u'ROOT')],
-             [(None, START, u'foo'),
-              (None, TEXT, u'FOO'),
-              (None, END, u'foo')],
-             [(None, START, u'bar'),
-              (None, TEXT, u'BAR'),
-              (None, END, u'bar')]]
+            [[(None, TEXT, 'ROOT')],
+             [(None, START, 'foo'),
+              (None, TEXT, 'FOO'),
+              (None, END, 'foo')],
+             [(None, START, 'bar'),
+              (None, TEXT, 'BAR'),
+              (None, END, 'bar')]]
             )
 
     def test_copy_text(self):
         self.assertEqual(
             self._apply('*/text()')[1],
-            [[(None, TEXT, u'FOO')],
-             [(None, TEXT, u'BAR')]]
+            [[(None, TEXT, 'FOO')],
+             [(None, TEXT, 'BAR')]]
             )
 
     def test_copy_context(self):
         self.assertEqual(
             self._apply('.')[1],
-            [[(None, START, u'root'),
-              (None, TEXT, u'ROOT'),
-              (None, START, u'foo'),
-              (None, TEXT, u'FOO'),
-              (None, END, u'foo'),
-              (None, START, u'bar'),
-              (None, TEXT, u'BAR'),
-              (None, END, u'bar'),
-              (None, END, u'root')]]
+            [[(None, START, 'root'),
+              (None, TEXT, 'ROOT'),
+              (None, START, 'foo'),
+              (None, TEXT, 'FOO'),
+              (None, END, 'foo'),
+              (None, START, 'bar'),
+              (None, TEXT, 'BAR'),
+              (None, END, 'bar'),
+              (None, END, 'root')]]
             )
 
     def test_copy_attribute(self):
         self.assertEqual(
             self._apply('foo/@name', with_attrs=True)[1],
-            [[(None, ATTR, {'name': u'foo'})]]
+            [[(None, ATTR, {'name': 'foo'})]]
             )
 
     def test_copy_attributes(self):
         self.assertEqual(
             self._apply('foo/@*', with_attrs=True)[1],
-            [[(None, ATTR, {u'name': u'foo', u'size': u'100'})]]
+            [[(None, ATTR, {'name': 'foo', 'size': '100'})]]
             )
 
 
@@ -1362,104 +1362,104 @@ class CutTest(unittest.TestCase, BufferTestMixin):
     def test_cut_element(self):
         self.assertEqual(
             self._apply('foo'),
-            ([(None, START, u'root'),
-              (None, TEXT, u'ROOT'),
-              (None, START, u'bar'),
-              (None, TEXT, u'BAR'),
-              (None, END, u'bar'),
-              (None, END, u'root')],
-             [[(None, START, u'foo'),
-               (None, TEXT, u'FOO'),
-               (None, END, u'foo')]])
+            ([(None, START, 'root'),
+              (None, TEXT, 'ROOT'),
+              (None, START, 'bar'),
+              (None, TEXT, 'BAR'),
+              (None, END, 'bar'),
+              (None, END, 'root')],
+             [[(None, START, 'foo'),
+               (None, TEXT, 'FOO'),
+               (None, END, 'foo')]])
             )
 
     def test_cut_adjacent_elements(self):
         self.assertEqual(
             self._apply('foo|bar'),
-            ([(None, START, u'root'), 
-              (None, TEXT, u'ROOT'),
+            ([(None, START, 'root'), 
+              (None, TEXT, 'ROOT'),
               (BREAK, BREAK, None),
-              (None, END, u'root')],
-             [[(None, START, u'foo'),
-               (None, TEXT, u'FOO'),
-               (None, END, u'foo')],
-              [(None, START, u'bar'),
-               (None, TEXT, u'BAR'),
-               (None, END, u'bar')]])
+              (None, END, 'root')],
+             [[(None, START, 'foo'),
+               (None, TEXT, 'FOO'),
+               (None, END, 'foo')],
+              [(None, START, 'bar'),
+               (None, TEXT, 'BAR'),
+               (None, END, 'bar')]])
             )
 
     def test_cut_all(self):
         self.assertEqual(
             self._apply('*|text()'),
-            ([(None, 'START', u'root'),
+            ([(None, 'START', 'root'),
               ('BREAK', 'BREAK', None),
               ('BREAK', 'BREAK', None),
-              (None, 'END', u'root')],
-             [[(None, 'TEXT', u'ROOT')],
-              [(None, 'START', u'foo'),
-               (None, 'TEXT', u'FOO'),
-               (None, 'END', u'foo')],
-              [(None, 'START', u'bar'),
-               (None, 'TEXT', u'BAR'),
-               (None, 'END', u'bar')]])
+              (None, 'END', 'root')],
+             [[(None, 'TEXT', 'ROOT')],
+              [(None, 'START', 'foo'),
+               (None, 'TEXT', 'FOO'),
+               (None, 'END', 'foo')],
+              [(None, 'START', 'bar'),
+               (None, 'TEXT', 'BAR'),
+               (None, 'END', 'bar')]])
             )
 
     def test_cut_text(self):
         self.assertEqual(
             self._apply('*/text()'),
-            ([(None, 'START', u'root'),
-              (None, 'TEXT', u'ROOT'),
-              (None, 'START', u'foo'),
-              (None, 'END', u'foo'),
-              (None, 'START', u'bar'),
-              (None, 'END', u'bar'),
-              (None, 'END', u'root')],
-             [[(None, 'TEXT', u'FOO')],
-              [(None, 'TEXT', u'BAR')]])
+            ([(None, 'START', 'root'),
+              (None, 'TEXT', 'ROOT'),
+              (None, 'START', 'foo'),
+              (None, 'END', 'foo'),
+              (None, 'START', 'bar'),
+              (None, 'END', 'bar'),
+              (None, 'END', 'root')],
+             [[(None, 'TEXT', 'FOO')],
+              [(None, 'TEXT', 'BAR')]])
             )
 
     def test_cut_context(self):
         self.assertEqual(
             self._apply('.')[1],
-            [[(None, 'START', u'root'),
-              (None, 'TEXT', u'ROOT'),
-              (None, 'START', u'foo'),
-              (None, 'TEXT', u'FOO'),
-              (None, 'END', u'foo'),
-              (None, 'START', u'bar'),
-              (None, 'TEXT', u'BAR'),
-              (None, 'END', u'bar'),
-              (None, 'END', u'root')]]
+            [[(None, 'START', 'root'),
+              (None, 'TEXT', 'ROOT'),
+              (None, 'START', 'foo'),
+              (None, 'TEXT', 'FOO'),
+              (None, 'END', 'foo'),
+              (None, 'START', 'bar'),
+              (None, 'TEXT', 'BAR'),
+              (None, 'END', 'bar'),
+              (None, 'END', 'root')]]
             )
 
     def test_cut_attribute(self):
         self.assertEqual(
             self._apply('foo/@name', with_attrs=True),
-            ([(None, START, (u'root', {})),
-              (None, TEXT, u'ROOT'),
-              (None, START, (u'foo', {u'size': u'100'})),
-              (None, TEXT, u'FOO'),
-              (None, END, u'foo'),
-              (None, START, (u'bar', {u'name': u'bar'})),
-              (None, TEXT, u'BAR'),
-              (None, END, u'bar'),
-              (None, END, u'root')],
-             [[(None, ATTR, {u'name': u'foo'})]])
+            ([(None, START, ('root', {})),
+              (None, TEXT, 'ROOT'),
+              (None, START, ('foo', {'size': '100'})),
+              (None, TEXT, 'FOO'),
+              (None, END, 'foo'),
+              (None, START, ('bar', {'name': 'bar'})),
+              (None, TEXT, 'BAR'),
+              (None, END, 'bar'),
+              (None, END, 'root')],
+             [[(None, ATTR, {'name': 'foo'})]])
             )
 
     def test_cut_attributes(self):
         self.assertEqual(
             self._apply('foo/@*', with_attrs=True),
-            ([(None, START, (u'root', {})),
-              (None, TEXT, u'ROOT'),
-              (None, START, (u'foo', {})),
-              (None, TEXT, u'FOO'),
-              (None, END, u'foo'),
-              (None, START, (u'bar', {u'name': u'bar'})),
-              (None, TEXT, u'BAR'),
-              (None, END, u'bar'),
-              (None, END, u'root')],
-             [[(None, ATTR, {u'name': u'foo', u'size': u'100'})]])
+            ([(None, START, ('root', {})),
+              (None, TEXT, 'ROOT'),
+              (None, START, ('foo', {})),
+              (None, TEXT, 'FOO'),
+              (None, END, 'foo'),
+              (None, START, ('bar', {'name': 'bar'})),
+              (None, TEXT, 'BAR'),
+              (None, END, 'bar'),
+              (None, END, 'root')],
+             [[(None, ATTR, {'name': 'foo', 'size': '100'})]])
             )
 
 # XXX Test this when the XPath implementation is fixed (#233).
--- genshi/filters/transform.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/filters/transform.py
@@ -115,7 +115,7 @@ class PushBackStream(object):
                 yield peek
             else:
                 try:
-                    event = self.stream.next()
+                    event = next(self.stream)
                     yield event
                 except StopIteration:
                     if self.peek is None:
@@ -730,7 +730,7 @@ class SelectTransformation(object):
         variables = {}
         test = self.path.test()
         stream = iter(stream)
-        next = stream.next
+        next = stream.__next__
         for mark, event in stream:
             if mark is None:
                 yield mark, event
@@ -764,7 +764,7 @@ class SelectTransformation(object):
                 yield OUTSIDE, result
             elif result:
                 # XXX Assume everything else is "text"?
-                yield None, (TEXT, unicode(result), (None, -1, -1))
+                yield None, (TEXT, str(result), (None, -1, -1))
             else:
                 yield None, event
 
@@ -990,7 +990,7 @@ class SubstituteTransformation(object):
         :param replace: Replacement pattern.
         :param count: Number of replacements to make in each text fragment.
         """
-        if isinstance(pattern, basestring):
+        if isinstance(pattern, str):
             self.pattern = re.compile(pattern)
         else:
             self.pattern = pattern
--- genshi/input.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/input.py
@@ -17,8 +17,8 @@ sources.
 
 from itertools import chain
 import codecs
-import htmlentitydefs as entities
-import HTMLParser as html
+import html.entities as entities
+import html.parser as html
 from xml.parsers import expat
 
 from genshi.core import Attrs, QName, Stream, stripentities
@@ -39,7 +39,7 @@ def ET(element):
     """
     tag_name = QName(element.tag.lstrip('{'))
     attrs = Attrs([(QName(attr.lstrip('{')), value)
-                   for attr, value in element.items()])
+                   for attr, value in list(element.items())])
 
     yield START, (tag_name, attrs), (None, -1, -1)
     if element.text:
@@ -91,8 +91,8 @@ class XMLParser(object):
     """
 
     _entitydefs = ['<!ENTITY %s "&#%d;">' % (name, value) for name, value in
-                   entities.name2codepoint.items()]
-    _external_dtd = u'\n'.join(_entitydefs).encode('utf-8')
+                   list(entities.name2codepoint.items())]
+    _external_dtd = '\n'.join(_entitydefs).encode('utf-8')
 
     def __init__(self, source, filename=None, encoding=None):
         """Initialize the parser for the given XML input.
@@ -156,7 +156,7 @@ class XMLParser(object):
                                 del self.expat # get rid of circular references
                             done = True
                         else:
-                            if isinstance(data, unicode):
+                            if isinstance(data, str):
                                 data = data.encode('utf-8')
                             self.expat.Parse(data, False)
                     for event in self._queue:
@@ -164,7 +164,7 @@ class XMLParser(object):
                     self._queue = []
                     if done:
                         break
-            except expat.ExpatError, e:
+            except expat.ExpatError as e:
                 msg = str(e)
                 raise ParseError(msg, self.filename, e.lineno, e.offset)
         return Stream(_generate()).filter(_coalesce)
@@ -242,7 +242,7 @@ class XMLParser(object):
         if text.startswith('&'):
             # deal with undefined entities
             try:
-                text = unichr(entities.name2codepoint[text[1:-1]])
+                text = chr(entities.name2codepoint[text[1:-1]])
                 self._enqueue(TEXT, text)
             except KeyError:
                 filename, lineno, offset = self._getpos()
@@ -333,7 +333,7 @@ class HTMLParser(html.HTMLParser, object):
                             self.close()
                             done = True
                         else:
-                            if not isinstance(data, unicode):
+                            if not isinstance(data, str):
                                 raise UnicodeError("source returned bytes, but no encoding specified")
                             self.feed(data)
                     for kind, data, pos in self._queue:
@@ -345,7 +345,7 @@ class HTMLParser(html.HTMLParser, object):
                         for tag in open_tags:
                             yield END, QName(tag), pos
                         break
-            except html.HTMLParseError, e:
+            except html.HTMLParseError as e:
                 msg = '%s: line %d, column %d' % (e.msg, e.lineno, e.offset)
                 raise ParseError(msg, self.filename, e.lineno, e.offset)
         return Stream(_generate()).filter(_coalesce)
@@ -388,14 +388,14 @@ class HTMLParser(html.HTMLParser, object):
 
     def handle_charref(self, name):
         if name.lower().startswith('x'):
-            text = unichr(int(name[1:], 16))
+            text = chr(int(name[1:], 16))
         else:
-            text = unichr(int(name))
+            text = chr(int(name))
         self._enqueue(TEXT, text)
 
     def handle_entityref(self, name):
         try:
-            text = unichr(entities.name2codepoint[name])
+            text = chr(entities.name2codepoint[name])
         except KeyError:
             text = '&%s;' % name
         self._enqueue(TEXT, text)
@@ -434,7 +434,7 @@ def HTML(text, encoding=None):
     :raises ParseError: if the HTML text is not well-formed, and error recovery
                         fails
     """
-    if isinstance(text, unicode):
+    if isinstance(text, str):
         # If it's unicode text the encoding should be set to None.
         # The option to pass in an incorrect encoding is for ease
         # of writing doctests that work in both Python 2.x and 3.x.
--- genshi/output.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/output.py
@@ -71,7 +71,7 @@ def get_serializer(method='xml', **kwargs):
     :see: `XMLSerializer`, `XHTMLSerializer`, `HTMLSerializer`, `TextSerializer`
     :since: version 0.4.1
     """
-    if isinstance(method, basestring):
+    if isinstance(method, str):
         method = {'xml':   XMLSerializer,
                   'xhtml': XHTMLSerializer,
                   'html':  HTMLSerializer,
@@ -581,7 +581,7 @@ class TextSerializer(object):
                 data = event[1]
                 if strip_markup and type(data) is Markup:
                     data = data.striptags().stripentities()
-                yield unicode(data)
+                yield str(data)
 
 
 class EmptyTagFilter(object):
@@ -636,7 +636,7 @@ class NamespaceFlattener(object):
         self.cache = cache
 
     def __call__(self, stream):
-        prefixes = dict([(v, [k]) for k, v in self.prefixes.items()])
+        prefixes = dict([(v, [k]) for k, v in list(self.prefixes.items())])
         namespaces = {XML_NAMESPACE.uri: ['xml']}
         _emit, _get, cache = _prepare_cache(self.cache)
         def _push_ns(prefix, uri):
@@ -666,7 +666,7 @@ class NamespaceFlattener(object):
             while 1:
                 val += 1
                 yield 'ns%d' % val
-        _gen_prefix = _gen_prefix().next
+        _gen_prefix = _gen_prefix().__next__
 
         for kind, data, pos in stream:
             if kind is TEXT and isinstance(data, Markup):
@@ -822,7 +822,7 @@ class DocTypeInserter(object):
 
         :param doctype: DOCTYPE as a string or DocType object.
         """
-        if isinstance(doctype, basestring):
+        if isinstance(doctype, str):
             doctype = DocType.get(doctype)
         self.doctype_event = (DOCTYPE, doctype, (None, -1, -1))
 
--- genshi/path.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/path.py
@@ -576,7 +576,7 @@ class Path(object):
             variables = {}
         stream = iter(stream)
         def _generate(stream=stream, ns=namespaces, vs=variables):
-            next = stream.next
+            next = stream.__next__
             test = self.test()
             for event in stream:
                 result = test(event, ns, vs)
@@ -932,13 +932,13 @@ def as_float(value):
     return float(as_scalar(value))
 
 def as_long(value):
-    return long(as_scalar(value))
+    return int(as_scalar(value))
 
 def as_string(value):
     value = as_scalar(value)
     if value is False:
         return ''
-    return unicode(value)
+    return str(value)
 
 def as_bool(value):
     return bool(as_scalar(value))
@@ -1346,8 +1346,8 @@ class TranslateFunction(Function):
         string = as_string(self.string(kind, data, pos, namespaces, variables))
         fromchars = as_string(self.fromchars(kind, data, pos, namespaces, variables))
         tochars = as_string(self.tochars(kind, data, pos, namespaces, variables))
-        table = dict(zip([ord(c) for c in fromchars],
-                         [ord(c) for c in tochars]))
+        table = dict(list(zip([ord(c) for c in fromchars],
+                         [ord(c) for c in tochars])))
         return string.translate(table)
     def __repr__(self):
         return 'translate(%r, %r, %r)' % (self.string, self.fromchars,
--- genshi/template/ast24.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/ast24.py
@@ -378,7 +378,7 @@ class ASTUpgrader(object):
     def visit_Const(self, node):
         if node.value is None: # appears in slices
             return None
-        elif isinstance(node.value, basestring):
+        elif isinstance(node.value, str):
             return self._new(_ast.Str, node.value)
         else:
             return self._new(_ast.Num, node.value)
--- genshi/template/base.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/base.py
@@ -179,7 +179,7 @@ class Context(object):
         
         :return: the number of variables in the context
         """
-        return len(self.items())
+        return len(list(self.items()))
 
     def __setitem__(self, key, value):
         """Set a variable in the current scope.
@@ -232,7 +232,7 @@ class Context(object):
         
         :return: a list of variables
         """
-        return [(key, self.get(key)) for key in self.keys()]
+        return [(key, self.get(key)) for key in list(self.keys())]
 
     def update(self, mapping):
         """Update the context from the mapping provided."""
@@ -321,12 +321,11 @@ class DirectiveFactoryMeta(type):
         return type.__new__(cls, name, bases, d)
 
 
-class DirectiveFactory(object):
+class DirectiveFactory(object, metaclass=DirectiveFactoryMeta):
     """Base for classes that provide a set of template directives.
     
     :since: version 0.6
     """
-    __metaclass__ = DirectiveFactoryMeta
 
     directives = []
     """A list of ``(name, cls)`` tuples that define the set of directives
@@ -379,7 +378,7 @@ class Template(DirectiveFactory):
     """
 
     serializer = None
-    _number_conv = unicode # function used to convert numbers to event data
+    _number_conv = str # function used to convert numbers to event data
 
     def __init__(self, source, filepath=None, filename=None, loader=None,
                  encoding=None, lookup='strict', allow_exec=True):
@@ -411,13 +410,13 @@ class Template(DirectiveFactory):
         self._prepared = False
 
         if not isinstance(source, Stream) and not hasattr(source, 'read'):
-            if isinstance(source, unicode):
+            if isinstance(source, str):
                 source = StringIO(source)
             else:
                 source = BytesIO(source)
         try:
             self._stream = self._parse(source, encoding)
-        except ParseError, e:
+        except ParseError as e:
             raise TemplateSyntaxError(e.msg, self.filepath, e.lineno, e.offset)
 
     def __getstate__(self):
@@ -502,7 +501,7 @@ class Template(DirectiveFactory):
                 if kind is INCLUDE:
                     href, cls, fallback = data
                     tmpl_inlined = False
-                    if (isinstance(href, basestring) and
+                    if (isinstance(href, str) and
                             not getattr(self.loader, 'auto_reload', True)):
                         # If the path to the included template is static, and
                         # auto-reloading is disabled on the template loader,
@@ -601,16 +600,16 @@ class Template(DirectiveFactory):
                         # First check for a string, otherwise the iterable test
                         # below succeeds, and the string will be chopped up into
                         # individual characters
-                        if isinstance(result, basestring):
+                        if isinstance(result, str):
                             yield TEXT, result, pos
-                        elif isinstance(result, (int, float, long)):
+                        elif isinstance(result, (int, float)):
                             yield TEXT, number_conv(result), pos
                         elif hasattr(result, '__iter__'):
                             push(stream)
                             stream = _ensure(result)
                             break
                         else:
-                            yield TEXT, unicode(result), pos
+                            yield TEXT, str(result), pos
 
                 elif kind is SUB:
                     # This event is a list of directives and a list of nested
@@ -639,7 +638,7 @@ class Template(DirectiveFactory):
         for event in stream:
             if event[0] is INCLUDE:
                 href, cls, fallback = event[1]
-                if not isinstance(href, basestring):
+                if not isinstance(href, str):
                     parts = []
                     for subkind, subdata, subpos in self._flatten(href, ctxt,
                                                                   **vars):
--- genshi/template/directives.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/directives.py
@@ -35,7 +35,7 @@ class DirectiveMeta(type):
         return type.__new__(cls, name, bases, d)
 
 
-class Directive(object):
+class Directive(object, metaclass=DirectiveMeta):
     """Abstract base class for template directives.
     
     A directive is basically a callable that takes three positional arguments:
@@ -53,7 +53,6 @@ class Directive(object):
     described above, and can only be applied programmatically (for example by
     template filters).
     """
-    __metaclass__ = DirectiveMeta
     __slots__ = ['expr']
 
     def __init__(self, value, template=None, namespaces=None, lineno=-1,
@@ -108,7 +107,7 @@ class Directive(object):
         try:
             return expr and Expression(expr, template.filepath, lineno,
                                        lookup=template.lookup) or None
-        except SyntaxError, err:
+        except SyntaxError as err:
             err.msg += ' in expression "%s" of "%s" directive' % (expr,
                                                                   cls.tagname)
             raise TemplateSyntaxError(err, template.filepath, lineno,
@@ -165,18 +164,18 @@ class AttrsDirective(Directive):
 
     def __call__(self, stream, directives, ctxt, **vars):
         def _generate():
-            kind, (tag, attrib), pos  = stream.next()
+            kind, (tag, attrib), pos  = next(stream)
             attrs = _eval_expr(self.expr, ctxt, vars)
             if attrs:
                 if isinstance(attrs, Stream):
                     try:
-                        attrs = iter(attrs).next()
+                        attrs = next(iter(attrs))
                     except StopIteration:
                         attrs = []
                 elif not isinstance(attrs, list): # assume it's a dict
-                    attrs = attrs.items()
+                    attrs = list(attrs.items())
                 attrib |= [
-                    (QName(n), v is not None and unicode(v).strip() or None)
+                    (QName(n), v is not None and str(v).strip() or None)
                     for n, v in attrs
                 ]
             yield kind, (tag, attrib), pos
@@ -537,8 +536,8 @@ class StripDirective(Directive):
     def __call__(self, stream, directives, ctxt, **vars):
         def _generate():
             if not self.expr or _eval_expr(self.expr, ctxt, vars):
-                stream.next() # skip start tag
-                previous = stream.next()
+                next(stream) # skip start tag
+                previous = next(stream)
                 for event in stream:
                     yield previous
                     previous = event
@@ -630,13 +629,13 @@ class WhenDirective(Directive):
         if not info:
             raise TemplateRuntimeError('"when" directives can only be used '
                                        'inside a "choose" directive',
-                                       self.filename, *(stream.next())[2][1:])
+                                       self.filename, *(next(stream))[2][1:])
         if info[0]:
             return []
         if not self.expr and not info[1]:
             raise TemplateRuntimeError('either "choose" or "when" directive '
                                        'must have a test expression',
-                                       self.filename, *(stream.next())[2][1:])
+                                       self.filename, *(next(stream))[2][1:])
         if info[1]:
             value = info[2]
             if self.expr:
@@ -669,7 +668,7 @@ class OtherwiseDirective(Directive):
         if not info:
             raise TemplateRuntimeError('an "otherwise" directive can only be '
                                        'used inside a "choose" directive',
-                                       self.filename, *(stream.next())[2][1:])
+                                       self.filename, *(next(stream))[2][1:])
         if info[0]:
             return []
         info[0] = True
@@ -706,7 +705,7 @@ class WithDirective(Directive):
                 self.vars.append(([_assignment(n) for n in node.targets],
                                   Expression(node.value, template.filepath,
                                              lineno, lookup=template.lookup)))
-        except SyntaxError, err:
+        except SyntaxError as err:
             err.msg += ' in expression "%s" of "%s" directive' % (value,
                                                                   self.tagname)
             raise TemplateSyntaxError(err, template.filepath, lineno,
--- genshi/template/eval.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/eval.py
@@ -13,7 +13,7 @@
 
 """Support for "safe" evaluation of Python expressions."""
 
-import __builtin__
+import builtins
 
 from textwrap import dedent
 from types import CodeType
@@ -37,7 +37,7 @@ has_star_import_bug = False
 try:
     class _FakeMapping(object):
         __getitem__ = __setitem__ = lambda *a: None
-    exec 'from sys import *' in {}, _FakeMapping()
+    exec('from sys import *', {}, _FakeMapping())
 except SystemError:
     has_star_import_bug = True
 del _FakeMapping
@@ -75,7 +75,7 @@ class Code(object):
                       if `None`, the appropriate transformation is chosen
                       depending on the mode
         """
-        if isinstance(source, basestring):
+        if isinstance(source, str):
             self.source = source
             node = _parse(source, mode=self.mode)
         else:
@@ -94,13 +94,13 @@ class Code(object):
                              filename=filename, lineno=lineno, xform=xform)
         if lookup is None:
             lookup = LenientLookup
-        elif isinstance(lookup, basestring):
+        elif isinstance(lookup, str):
             lookup = {'lenient': LenientLookup, 'strict': StrictLookup}[lookup]
         self._globals = lookup.globals
 
     def __getstate__(self):
         state = {'source': self.source, 'ast': self.ast,
-                 'lookup': self._globals.im_self}
+                 'lookup': self._globals.__self__}
         state['code'] = get_code_params(self.code)
         return state
 
@@ -196,7 +196,7 @@ class Suite(Code):
         """
         __traceback_hide__ = 'before_and_this'
         _globals = self._globals(data)
-        exec self.code in _globals, data
+        exec(self.code, _globals, data)
 
 
 UNDEFINED = object()
@@ -264,7 +264,7 @@ class Undefined(object):
     def __iter__(self):
         return iter([])
 
-    def __nonzero__(self):
+    def __bool__(self):
         return False
 
     def __repr__(self):
@@ -333,8 +333,8 @@ class LookupBase(object):
             key = key[0]
         try:
             return obj[key]
-        except (AttributeError, KeyError, IndexError, TypeError), e:
-            if isinstance(key, basestring):
+        except (AttributeError, KeyError, IndexError, TypeError) as e:
+            if isinstance(key, str):
                 val = getattr(obj, key, UNDEFINED)
                 if val is UNDEFINED:
                     val = cls.undefined(key, owner=obj)
@@ -424,8 +424,8 @@ def _parse(source, mode='eval'):
             if first.rstrip().endswith(':') and not rest[0].isspace():
                 rest = '\n'.join(['    %s' % line for line in rest.splitlines()])
             source = '\n'.join([first, rest])
-    if isinstance(source, unicode):
-        source = (u'\ufeff' + source).encode('utf-8')
+    if isinstance(source, str):
+        source = ('\ufeff' + source).encode('utf-8')
     return parse(source, mode)
 
 
@@ -435,11 +435,11 @@ def _compile(node, source=None, mode='eval', filename=
         filename = '<string>'
     if IS_PYTHON2:
         # Python 2 requires non-unicode filenames
-        if isinstance(filename, unicode):
+        if isinstance(filename, str):
             filename = filename.encode('utf-8', 'replace')
     else:
         # Python 3 requires unicode filenames
-        if not isinstance(filename, unicode):
+        if not isinstance(filename, str):
             filename = filename.decode('utf-8', 'replace')
     if lineno <= 0:
         lineno = 1
@@ -483,7 +483,7 @@ def _new(class_, *args, **kwargs):
     return ret
 
 
-BUILTINS = __builtin__.__dict__.copy()
+BUILTINS = builtins.__dict__.copy()
 BUILTINS.update({'Markup': Markup, 'Undefined': Undefined})
 CONSTANTS = frozenset(['False', 'True', 'None', 'NotImplemented', 'Ellipsis'])
 
@@ -527,7 +527,7 @@ class TemplateASTTransformer(ASTTransformer):
         return names
 
     def visit_Str(self, node):
-        if not isinstance(node.s, unicode):
+        if not isinstance(node.s, str):
             try: # If the string is ASCII, return a `str` object
                 node.s.decode('ascii')
             except ValueError: # Otherwise return a `unicode` object
--- genshi/template/interpolation.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/interpolation.py
@@ -77,7 +77,7 @@ def interpolate(text, filepath=None, lineno=-1, offset
                     expr = Expression(chunk.strip(), pos[0], pos[1],
                                       lookup=lookup)
                     yield EXPR, expr, tuple(pos)
-                except SyntaxError, err:
+                except SyntaxError as err:
                     raise TemplateSyntaxError(err, filepath, pos[1],
                                               pos[2] + (err.offset or 0))
         else:
--- genshi/template/loader.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/loader.py
@@ -217,7 +217,7 @@ class TemplateLoader(object):
                 raise TemplateError('Search path for templates not configured')
 
             for loadfunc in search_path:
-                if isinstance(loadfunc, basestring):
+                if isinstance(loadfunc, str):
                     loadfunc = directory(loadfunc)
                 try:
                     filepath, filename, fileobj, uptodate = loadfunc(filename)
@@ -327,9 +327,9 @@ class TemplateLoader(object):
         :rtype: ``function``
         """
         def _dispatch_by_prefix(filename):
-            for prefix, delegate in delegates.items():
+            for prefix, delegate in list(delegates.items()):
                 if filename.startswith(prefix):
-                    if isinstance(delegate, basestring):
+                    if isinstance(delegate, str):
                         delegate = directory(delegate)
                     filepath, _, fileobj, uptodate = delegate(
                         filename[len(prefix):].lstrip('/\\')
--- genshi/template/markup.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/markup.py
@@ -93,7 +93,7 @@ class MarkupTemplate(Template):
                 try:
                     suite = Suite(data[1], self.filepath, pos[1],
                                   lookup=self.lookup)
-                except SyntaxError, err:
+                except SyntaxError as err:
                     raise TemplateSyntaxError(err, self.filepath,
                                               pos[1] + (err.lineno or 1) - 1,
                                               pos[2] + (err.offset or 0))
@@ -311,7 +311,7 @@ class MarkupTemplate(Template):
 
         def _strip(stream, append):
             depth = 1
-            next = stream.next
+            next = stream.__next__
             while 1:
                 event = next()
                 if event[0] is START:
--- genshi/template/plugin.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/plugin.py
@@ -46,7 +46,7 @@ class AbstractTemplateEnginePlugin(object):
 
         self.default_encoding = options.get('genshi.default_encoding', None)
         auto_reload = options.get('genshi.auto_reload', '1')
-        if isinstance(auto_reload, basestring):
+        if isinstance(auto_reload, str):
             auto_reload = auto_reload.lower() in ('1', 'on', 'yes', 'true')
         search_path = [p for p in
                        options.get('genshi.search_path', '').split(':') if p]
@@ -168,7 +168,7 @@ class TextTemplateEnginePlugin(AbstractTemplateEngineP
             options = {}
 
         new_syntax = options.get('genshi.new_text_syntax')
-        if isinstance(new_syntax, basestring):
+        if isinstance(new_syntax, str):
             new_syntax = new_syntax.lower() in ('1', 'on', 'yes', 'true')
         if new_syntax:
             self.template_class = NewTextTemplate
--- genshi/template/tests/directives.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/tests/directives.py
@@ -426,7 +426,7 @@ class ForDirectiveTestCase(unittest.TestCase):
             <b>3</b>
             <b>4</b>
             <b>5</b>
-        </doc>""", tmpl.generate(items=range(1, 6)).render(encoding=None))
+        </doc>""", tmpl.generate(items=list(range(1, 6))).render(encoding=None))
 
     def test_as_element(self):
         """
@@ -443,7 +443,7 @@ class ForDirectiveTestCase(unittest.TestCase):
             <b>3</b>
             <b>4</b>
             <b>5</b>
-        </doc>""", tmpl.generate(items=range(1, 6)).render(encoding=None))
+        </doc>""", tmpl.generate(items=list(range(1, 6))).render(encoding=None))
 
     def test_multi_assignment(self):
         """
@@ -487,7 +487,7 @@ class ForDirectiveTestCase(unittest.TestCase):
         try:
             list(tmpl.generate(foo=12))
             self.fail('Expected TemplateRuntimeError')
-        except TypeError, e:
+        except TypeError as e:
             assert (str(e) == "iteration over non-sequence" or
                     str(e) == "'int' object is not iterable")
             exc_type, exc_value, exc_traceback = sys.exc_info()
@@ -513,7 +513,7 @@ class ForDirectiveTestCase(unittest.TestCase):
               </py:for>
             </doc>""", filename='test.html').generate()
             self.fail('ExpectedTemplateSyntaxError')
-        except TemplateSyntaxError, e:
+        except TemplateSyntaxError as e:
             self.assertEqual('test.html', e.filename)
             if sys.version_info[:2] > (2,4):
                 self.assertEqual(2, e.lineno)
@@ -1050,7 +1050,7 @@ class ContentDirectiveTestCase(unittest.TestCase):
               <py:content foo="">Foo</py:content>
             </doc>""", filename='test.html').generate()
             self.fail('Expected TemplateSyntaxError')
-        except TemplateSyntaxError, e:
+        except TemplateSyntaxError as e:
             self.assertEqual('test.html', e.filename)
             self.assertEqual(2, e.lineno)
 
@@ -1068,7 +1068,7 @@ class ReplaceDirectiveTestCase(unittest.TestCase):
               <elem py:replace="">Foo</elem>
             </doc>""", filename='test.html').generate()
             self.fail('Expected TemplateSyntaxError')
-        except TemplateSyntaxError, e:
+        except TemplateSyntaxError as e:
             self.assertEqual('test.html', e.filename)
             self.assertEqual(2, e.lineno)
 
@@ -1190,12 +1190,12 @@ class WithDirectiveTestCase(unittest.TestCase):
         </div>""", tmpl.generate(foo={'bar': 42}).render(encoding=None))
 
     def test_unicode_expr(self):
-        tmpl = MarkupTemplate(u"""<div xmlns:py="http://genshi.edgewall.org/">
+        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
           <span py:with="weeks=(u'一', u'二', u'三', u'四', u'五', u'六', u'日')">
             $weeks
           </span>
         </div>""")
-        self.assertEqual(u"""<div>
+        self.assertEqual("""<div>
           <span>
             一二三四五六日
           </span>
--- genshi/template/tests/eval.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/tests/eval.py
@@ -58,34 +58,34 @@ class ExpressionTestCase(unittest.TestCase):
     def test_str_literal(self):
         self.assertEqual('foo', Expression('"foo"').evaluate({}))
         self.assertEqual('foo', Expression('"""foo"""').evaluate({}))
-        self.assertEqual(u'foo'.encode('utf-8'),
+        self.assertEqual('foo'.encode('utf-8'),
                          Expression(wrapped_bytes("b'foo'")).evaluate({}))
         self.assertEqual('foo', Expression("'''foo'''").evaluate({}))
         self.assertEqual('foo', Expression("u'foo'").evaluate({}))
         self.assertEqual('foo', Expression("r'foo'").evaluate({}))
 
     def test_str_literal_non_ascii(self):
-        expr = Expression(u"u'\xfe'")
-        self.assertEqual(u'þ', expr.evaluate({}))
         expr = Expression("u'\xfe'")
-        self.assertEqual(u'þ', expr.evaluate({}))
+        self.assertEqual('þ', expr.evaluate({}))
+        expr = Expression("u'\xfe'")
+        self.assertEqual('þ', expr.evaluate({}))
         # On Python2 strings are converted to unicode if they contained
         # non-ASCII characters.
         # On Py3k, we have no need to do this as non-prefixed strings aren't
         # raw.
         expr = Expression(wrapped_bytes(r"b'\xc3\xbe'"))
         if IS_PYTHON2:
-            self.assertEqual(u'þ', expr.evaluate({}))
+            self.assertEqual('þ', expr.evaluate({}))
         else:
-            self.assertEqual(u'þ'.encode('utf-8'), expr.evaluate({}))
+            self.assertEqual('þ'.encode('utf-8'), expr.evaluate({}))
 
     def test_num_literal(self):
         self.assertEqual(42, Expression("42").evaluate({}))
         if IS_PYTHON2:
-            self.assertEqual(42L, Expression("42L").evaluate({}))
+            self.assertEqual(42, Expression("42L").evaluate({}))
         self.assertEqual(.42, Expression(".42").evaluate({}))
         if IS_PYTHON2:
-            self.assertEqual(07, Expression("07").evaluate({}))
+            self.assertEqual(0o7, Expression("07").evaluate({}))
         self.assertEqual(0xF2, Expression("0xF2").evaluate({}))
         self.assertEqual(0XF2, Expression("0XF2").evaluate({}))
 
@@ -236,7 +236,7 @@ class ExpressionTestCase(unittest.TestCase):
         self.assertEqual(42, Expression("foo()").evaluate({'foo': lambda: 42}))
         data = {'foo': 'bar'}
         self.assertEqual('BAR', Expression("foo.upper()").evaluate(data))
-        data = {'foo': {'bar': range(42)}}
+        data = {'foo': {'bar': list(range(42))}}
         self.assertEqual(42, Expression("len(foo.bar)").evaluate(data))
 
     def test_call_keywords(self):
@@ -254,7 +254,7 @@ class ExpressionTestCase(unittest.TestCase):
         self.assertEqual(42, expr.evaluate({'foo': foo, 'bar': {"x": 42}}))
 
     def test_lambda(self):
-        data = {'items': range(5)}
+        data = {'items': list(range(5))}
         expr = Expression("filter(lambda x: x > 2, items)")
         self.assertEqual([3, 4], list(expr.evaluate(data)))
 
@@ -268,19 +268,19 @@ class ExpressionTestCase(unittest.TestCase):
 
     def test_list_comprehension(self):
         expr = Expression("[n for n in numbers if n < 2]")
-        self.assertEqual([0, 1], expr.evaluate({'numbers': range(5)}))
+        self.assertEqual([0, 1], expr.evaluate({'numbers': list(range(5))}))
 
         expr = Expression("[(i, n + 1) for i, n in enumerate(numbers)]")
         self.assertEqual([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)],
-                         expr.evaluate({'numbers': range(5)}))
+                         expr.evaluate({'numbers': list(range(5))}))
 
         expr = Expression("[offset + n for n in numbers]")
         self.assertEqual([2, 3, 4, 5, 6],
-                         expr.evaluate({'numbers': range(5), 'offset': 2}))
+                         expr.evaluate({'numbers': list(range(5)), 'offset': 2}))
 
         expr = Expression("[n for group in groups for n in group]")
         self.assertEqual([0, 1, 0, 1, 2],
-                         expr.evaluate({'groups': [range(2), range(3)]}))
+                         expr.evaluate({'groups': [list(range(2)), list(range(3))]}))
 
         expr = Expression("[(a, b) for a in x for b in y]")
         self.assertEqual([('x0', 'y0'), ('x0', 'y1'), ('x1', 'y0'), ('x1', 'y1')],
@@ -298,19 +298,19 @@ class ExpressionTestCase(unittest.TestCase):
 
     def test_generator_expression(self):
         expr = Expression("list(n for n in numbers if n < 2)")
-        self.assertEqual([0, 1], expr.evaluate({'numbers': range(5)}))
+        self.assertEqual([0, 1], expr.evaluate({'numbers': list(range(5))}))
 
         expr = Expression("list((i, n + 1) for i, n in enumerate(numbers))")
         self.assertEqual([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)],
-                         expr.evaluate({'numbers': range(5)}))
+                         expr.evaluate({'numbers': list(range(5))}))
 
         expr = Expression("list(offset + n for n in numbers)")
         self.assertEqual([2, 3, 4, 5, 6],
-                         expr.evaluate({'numbers': range(5), 'offset': 2}))
+                         expr.evaluate({'numbers': list(range(5)), 'offset': 2}))
 
         expr = Expression("list(n for group in groups for n in group)")
         self.assertEqual([0, 1, 0, 1, 2],
-                         expr.evaluate({'groups': [range(2), range(3)]}))
+                         expr.evaluate({'groups': [list(range(2)), list(range(3))]}))
 
         expr = Expression("list((a, b) for a in x for b in y)")
         self.assertEqual([('x0', 'y0'), ('x0', 'y1'), ('x1', 'y0'), ('x1', 'y1')],
@@ -334,29 +334,29 @@ class ExpressionTestCase(unittest.TestCase):
 
     def test_slice(self):
         expr = Expression("numbers[0:2]")
-        self.assertEqual([0, 1], expr.evaluate({'numbers': range(5)}))
+        self.assertEqual([0, 1], expr.evaluate({'numbers': list(range(5))}))
 
     def test_slice_with_vars(self):
         expr = Expression("numbers[start:end]")
-        self.assertEqual([0, 1], expr.evaluate({'numbers': range(5), 'start': 0,
+        self.assertEqual([0, 1], expr.evaluate({'numbers': list(range(5)), 'start': 0,
                                                 'end': 2}))
 
     def test_slice_copy(self):
         expr = Expression("numbers[:]")
-        self.assertEqual([0, 1, 2, 3, 4], expr.evaluate({'numbers': range(5)}))
+        self.assertEqual([0, 1, 2, 3, 4], expr.evaluate({'numbers': list(range(5))}))
 
     def test_slice_stride(self):
         expr = Expression("numbers[::stride]")
-        self.assertEqual([0, 2, 4], expr.evaluate({'numbers': range(5),
+        self.assertEqual([0, 2, 4], expr.evaluate({'numbers': list(range(5)),
                                                    'stride': 2}))
 
     def test_slice_negative_start(self):
         expr = Expression("numbers[-1:]")
-        self.assertEqual([4], expr.evaluate({'numbers': range(5)}))
+        self.assertEqual([4], expr.evaluate({'numbers': list(range(5))}))
 
     def test_slice_negative_end(self):
         expr = Expression("numbers[:-1]")
-        self.assertEqual([0, 1, 2, 3], expr.evaluate({'numbers': range(5)}))
+        self.assertEqual([0, 1, 2, 3], expr.evaluate({'numbers': list(range(5))}))
 
     def test_access_undefined(self):
         expr = Expression("nothing", filename='index.html', lineno=50,
@@ -416,7 +416,7 @@ class ExpressionTestCase(unittest.TestCase):
         try:
             expr.evaluate({})
             self.fail('Expected UndefinedError')
-        except UndefinedError, e:
+        except UndefinedError as e:
             exc_type, exc_value, exc_traceback = sys.exc_info()
             frame = exc_traceback.tb_next
             frames = []
@@ -439,7 +439,7 @@ class ExpressionTestCase(unittest.TestCase):
         try:
             expr.evaluate({'something': Something()})
             self.fail('Expected UndefinedError')
-        except UndefinedError, e:
+        except UndefinedError as e:
             self.assertEqual('<Something> has no member named "nil"', str(e))
             exc_type, exc_value, exc_traceback = sys.exc_info()
             search_string = "<Expression 'something.nil'>"
@@ -463,7 +463,7 @@ class ExpressionTestCase(unittest.TestCase):
         try:
             expr.evaluate({'something': Something()})
             self.fail('Expected UndefinedError')
-        except UndefinedError, e:
+        except UndefinedError as e:
             self.assertEqual('<Something> has no member named "nil"', str(e))
             exc_type, exc_value, exc_traceback = sys.exc_info()
             search_string = '''<Expression 'something["nil"]'>'''
@@ -834,12 +834,12 @@ assert f() == 42
                 self.attr = 'foo'
         obj = Something()
         Suite("del obj.attr").execute({'obj': obj})
-        self.failIf(hasattr(obj, 'attr'))
+        self.assertFalse(hasattr(obj, 'attr'))
 
     def test_delitem(self):
         d = {'k': 'foo'}
         Suite("del d['k']").execute({'d': d})
-        self.failIf('k' in d, repr(d))
+        self.assertFalse('k' in d, repr(d))
 
     if sys.version_info >= (2, 5):
         def test_with_statement(self):
--- genshi/template/tests/interpolation.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/tests/interpolation.py
@@ -131,7 +131,7 @@ class InterpolateTestCase(unittest.TestCase):
     def test_interpolate_full_mismatched_brackets(self):
         try:
             list(interpolate('${{1:2}'))
-        except TemplateSyntaxError, e:
+        except TemplateSyntaxError as e:
             pass
         else:
             self.fail('Expected TemplateSyntaxError')
--- genshi/template/tests/loader.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/tests/loader.py
@@ -372,7 +372,7 @@ class TemplateLoaderTestCase(unittest.TestCase):
     def test_load_with_default_encoding(self):
         f = open(os.path.join(self.dirname, 'tmpl.html'), 'wb')
         try:
-            f.write(u'<div>\xf6</div>'.encode('iso-8859-1'))
+            f.write('<div>\xf6</div>'.encode('iso-8859-1'))
         finally:
             f.close()
         loader = TemplateLoader([self.dirname], default_encoding='iso-8859-1')
@@ -381,7 +381,7 @@ class TemplateLoaderTestCase(unittest.TestCase):
     def test_load_with_explicit_encoding(self):
         f = open(os.path.join(self.dirname, 'tmpl.html'), 'wb')
         try:
-            f.write(u'<div>\xf6</div>'.encode('iso-8859-1'))
+            f.write('<div>\xf6</div>'.encode('iso-8859-1'))
         finally:
             f.close()
         loader = TemplateLoader([self.dirname], default_encoding='utf-8')
--- genshi/template/tests/markup.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/tests/markup.py
@@ -83,7 +83,7 @@ class MarkupTemplateTestCase(unittest.TestCase):
         xml = '<p xmlns:py="http://genshi.edgewall.org/" py:do="nothing" />'
         try:
             tmpl = MarkupTemplate(xml, filename='test.html')
-        except BadDirectiveError, e:
+        except BadDirectiveError as e:
             self.assertEqual('test.html', e.filename)
             self.assertEqual(1, e.lineno)
 
@@ -92,7 +92,7 @@ class MarkupTemplateTestCase(unittest.TestCase):
         try:
             tmpl = MarkupTemplate(xml, filename='test.html').generate()
             self.fail('Expected TemplateSyntaxError')
-        except TemplateSyntaxError, e:
+        except TemplateSyntaxError as e:
             self.assertEqual('test.html', e.filename)
             self.assertEqual(1, e.lineno)
 
@@ -103,7 +103,7 @@ class MarkupTemplateTestCase(unittest.TestCase):
         try:
             tmpl = MarkupTemplate(xml, filename='test.html')
             self.fail('Expected TemplateSyntaxError')
-        except TemplateSyntaxError, e:
+        except TemplateSyntaxError as e:
             self.assertEqual('test.html', e.filename)
             self.assertEqual(2, e.lineno)
 
@@ -116,7 +116,7 @@ class MarkupTemplateTestCase(unittest.TestCase):
         try:
             tmpl = MarkupTemplate(xml, filename='test.html')
             self.fail('Expected TemplateSyntaxError')
-        except TemplateSyntaxError, e:
+        except TemplateSyntaxError as e:
             self.assertEqual('test.html', e.filename)
             self.assertEqual(3, e.lineno)
 
@@ -190,21 +190,21 @@ class MarkupTemplateTestCase(unittest.TestCase):
         </div>""", str(tmpl.generate()))
 
     def test_latin1_encoded_with_xmldecl(self):
-        tmpl = MarkupTemplate(u"""<?xml version="1.0" encoding="iso-8859-1" ?>
+        tmpl = MarkupTemplate("""<?xml version="1.0" encoding="iso-8859-1" ?>
         <div xmlns:py="http://genshi.edgewall.org/">
           \xf6
         </div>""".encode('iso-8859-1'), encoding='iso-8859-1')
-        self.assertEqual(u"""<?xml version="1.0" encoding="iso-8859-1"?>\n<div>
+        self.assertEqual("""<?xml version="1.0" encoding="iso-8859-1"?>\n<div>
           \xf6
-        </div>""", unicode(tmpl.generate()))
+        </div>""", str(tmpl.generate()))
 
     def test_latin1_encoded_explicit_encoding(self):
-        tmpl = MarkupTemplate(u"""<div xmlns:py="http://genshi.edgewall.org/">
+        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
           \xf6
         </div>""".encode('iso-8859-1'), encoding='iso-8859-1')
-        self.assertEqual(u"""<div>
+        self.assertEqual("""<div>
           \xf6
-        </div>""", unicode(tmpl.generate()))
+        </div>""", str(tmpl.generate()))
 
     def test_exec_with_trailing_space(self):
         """
@@ -619,7 +619,7 @@ class MarkupTemplateTestCase(unittest.TestCase):
             tmpl = MarkupTemplate(xml, filename='test.html',
                                   allow_exec=False)
             self.fail('Expected SyntaxError')
-        except TemplateSyntaxError, e:
+        except TemplateSyntaxError as e:
             pass
 
     def test_allow_exec_true(self): 
--- genshi/template/tests/plugin.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/tests/plugin.py
@@ -165,7 +165,7 @@ class MarkupTemplateEnginePluginTestCase(unittest.Test
     def test_helper_functions(self):
         plugin = MarkupTemplateEnginePlugin()
         tmpl = plugin.load_template(PACKAGE + '.templates.functions')
-        output = plugin.render({'snippet': u'<b>Foo</b>'}, template=tmpl)
+        output = plugin.render({'snippet': '<b>Foo</b>'}, template=tmpl)
         self.assertEqual("""<div>
 False
 bar
--- genshi/template/tests/text.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/tests/text.py
@@ -52,15 +52,15 @@ class OldTextTemplateTestCase(unittest.TestCase):
         self.assertEqual('\n', tmpl.generate(foo=False).render(encoding=None))
 
     def test_latin1_encoded(self):
-        text = u'$foo\xf6$bar'.encode('iso-8859-1')
+        text = '$foo\xf6$bar'.encode('iso-8859-1')
         tmpl = OldTextTemplate(text, encoding='iso-8859-1')
-        self.assertEqual(u'x\xf6y',
+        self.assertEqual('x\xf6y',
                          tmpl.generate(foo='x', bar='y').render(encoding=None))
 
     def test_unicode_input(self):
-        text = u'$foo\xf6$bar'
+        text = '$foo\xf6$bar'
         tmpl = OldTextTemplate(text)
-        self.assertEqual(u'x\xf6y',
+        self.assertEqual('x\xf6y',
                          tmpl.generate(foo='x', bar='y').render(encoding=None))
 
     def test_empty_lines1(self):
@@ -74,7 +74,7 @@ class OldTextTemplateTestCase(unittest.TestCase):
           * 0
           * 1
           * 2
-""", tmpl.generate(items=range(3)).render(encoding=None))
+""", tmpl.generate(items=list(range(3))).render(encoding=None))
 
     def test_empty_lines2(self):
         tmpl = OldTextTemplate("""Your items:
@@ -91,18 +91,18 @@ class OldTextTemplateTestCase(unittest.TestCase):
 
           * 2
 
-""", tmpl.generate(items=range(3)).render(encoding=None))
+""", tmpl.generate(items=list(range(3))).render(encoding=None))
 
     def test_include(self):
         file1 = open(os.path.join(self.dirname, 'tmpl1.txt'), 'wb')
         try:
-            file1.write(u"Included\n".encode("utf-8"))
+            file1.write("Included\n".encode("utf-8"))
         finally:
             file1.close()
 
         file2 = open(os.path.join(self.dirname, 'tmpl2.txt'), 'wb')
         try:
-            file2.write(u"""----- Included data below this line -----
+            file2.write("""----- Included data below this line -----
             #include tmpl1.txt
             ----- Included data above this line -----""".encode("utf-8"))
         finally:
@@ -147,15 +147,15 @@ class NewTextTemplateTestCase(unittest.TestCase):
         self.assertEqual('\n', tmpl.generate(foo=False).render(encoding=None))
 
     def test_latin1_encoded(self):
-        text = u'$foo\xf6$bar'.encode('iso-8859-1')
+        text = '$foo\xf6$bar'.encode('iso-8859-1')
         tmpl = NewTextTemplate(text, encoding='iso-8859-1')
-        self.assertEqual(u'x\xf6y',
+        self.assertEqual('x\xf6y',
                          tmpl.generate(foo='x', bar='y').render(encoding=None))
 
     def test_unicode_input(self):
-        text = u'$foo\xf6$bar'
+        text = '$foo\xf6$bar'
         tmpl = NewTextTemplate(text)
-        self.assertEqual(u'x\xf6y',
+        self.assertEqual('x\xf6y',
                          tmpl.generate(foo='x', bar='y').render(encoding=None))
 
     def test_empty_lines1(self):
@@ -169,7 +169,7 @@ class NewTextTemplateTestCase(unittest.TestCase):
   * 0
   * 1
   * 2
-""", tmpl.generate(items=range(3)).render(encoding=None))
+""", tmpl.generate(items=list(range(3))).render(encoding=None))
 
     def test_empty_lines1_with_crlf(self):
         tmpl = NewTextTemplate('Your items:\r\n'
@@ -182,7 +182,7 @@ class NewTextTemplateTestCase(unittest.TestCase):
 '\r\n'
 '  * 0\r\n'
 '  * 1\r\n'
-'  * 2\r\n', tmpl.generate(items=range(3)).render(encoding=None))
+'  * 2\r\n', tmpl.generate(items=list(range(3))).render(encoding=None))
 
     def test_empty_lines2(self):
         tmpl = NewTextTemplate("""Your items:
@@ -199,7 +199,7 @@ class NewTextTemplateTestCase(unittest.TestCase):
 
   * 2
 
-""", tmpl.generate(items=range(3)).render(encoding=None))
+""", tmpl.generate(items=list(range(3))).render(encoding=None))
 
     def test_empty_lines2_with_crlf(self):
         tmpl = NewTextTemplate('Your items:\r\n'
@@ -215,7 +215,7 @@ class NewTextTemplateTestCase(unittest.TestCase):
 '  * 1\r\n'
 '\r\n'
 '  * 2\r\n'
-'\r\n', tmpl.generate(items=range(3)).render(encoding=None))
+'\r\n', tmpl.generate(items=list(range(3))).render(encoding=None))
 
     def test_exec_with_trailing_space(self):
         """
@@ -250,13 +250,13 @@ class NewTextTemplateTestCase(unittest.TestCase):
     def test_include(self):
         file1 = open(os.path.join(self.dirname, 'tmpl1.txt'), 'wb')
         try:
-            file1.write(u"Included".encode("utf-8"))
+            file1.write("Included".encode("utf-8"))
         finally:
             file1.close()
 
         file2 = open(os.path.join(self.dirname, 'tmpl2.txt'), 'wb')
         try:
-            file2.write(u"""----- Included data below this line -----
+            file2.write("""----- Included data below this line -----
 {% include tmpl1.txt %}
 ----- Included data above this line -----""".encode("utf-8"))
         finally:
@@ -272,13 +272,13 @@ Included
     def test_include_expr(self):
          file1 = open(os.path.join(self.dirname, 'tmpl1.txt'), 'wb')
          try:
-             file1.write(u"Included".encode("utf-8"))
+             file1.write("Included".encode("utf-8"))
          finally:
              file1.close()
  
          file2 = open(os.path.join(self.dirname, 'tmpl2.txt'), 'wb')
          try:
-             file2.write(u"""----- Included data below this line -----
+             file2.write("""----- Included data below this line -----
     {% include ${'%s.txt' % ('tmpl1',)} %}
     ----- Included data above this line -----""".encode("utf-8"))
          finally:
--- genshi/template/text.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/template/text.py
@@ -162,7 +162,7 @@ class NewTextTemplate(Template):
         depth = 0
 
         source = source.read()
-        if not isinstance(source, unicode):
+        if not isinstance(source, str):
             source = source.decode(encoding or 'utf-8', 'replace')
         offset = 0
         lineno = 1
@@ -201,7 +201,7 @@ class NewTextTemplate(Template):
                 try:
                     suite = Suite(value, self.filepath, lineno,
                                   lookup=self.lookup)
-                except SyntaxError, err:
+                except SyntaxError as err:
                     raise TemplateSyntaxError(err, self.filepath,
                                               lineno + (err.lineno or 1) - 1)
                 pos = (self.filename, lineno, 0)
@@ -279,7 +279,7 @@ class OldTextTemplate(Template):
         depth = 0
 
         source = source.read()
-        if not isinstance(source, unicode):
+        if not isinstance(source, str):
             source = source.decode(encoding or 'utf-8', 'replace')
         offset = 0
         lineno = 1
--- genshi/tests/core.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/tests/core.py
@@ -25,28 +25,28 @@ class StreamTestCase(unittest.TestCase):
 
     def test_render_utf8(self):
         xml = XML('<li>Über uns</li>')
-        self.assertEqual(u'<li>Über uns</li>'.encode('utf-8'), xml.render(encoding='utf-8'))
+        self.assertEqual('<li>Über uns</li>'.encode('utf-8'), xml.render(encoding='utf-8'))
 
     def test_render_unicode(self):
         xml = XML('<li>Über uns</li>')
-        self.assertEqual(u'<li>Über uns</li>', xml.render())
-        self.assertEqual(u'<li>Über uns</li>', xml.render(encoding=None))
+        self.assertEqual('<li>Über uns</li>', xml.render())
+        self.assertEqual('<li>Über uns</li>', xml.render(encoding=None))
 
     def test_render_ascii(self):
         xml = XML('<li>Über uns</li>')
-        self.assertEqual(u'<li>&#220;ber uns</li>'.encode('ascii'), xml.render(encoding='ascii'))
+        self.assertEqual('<li>&#220;ber uns</li>'.encode('ascii'), xml.render(encoding='ascii'))
 
     def test_render_output_stream_utf8(self):
         xml = XML('<li>Über uns</li>')
         strio = BytesIO()
         self.assertEqual(None, xml.render(encoding='utf-8', out=strio))
-        self.assertEqual(u'<li>Über uns</li>'.encode('utf-8'), strio.getvalue())
+        self.assertEqual('<li>Über uns</li>'.encode('utf-8'), strio.getvalue())
 
     def test_render_output_stream_unicode(self):
         xml = XML('<li>Über uns</li>')
         strio = StringIO()
         self.assertEqual(None, xml.render(encoding=None, out=strio))
-        self.assertEqual(u'<li>Über uns</li>', strio.getvalue())
+        self.assertEqual('<li>Über uns</li>', strio.getvalue())
 
     def test_pickle(self):
         xml = XML('<li>Foo</li>')
@@ -54,122 +54,122 @@ class StreamTestCase(unittest.TestCase):
         pickle.dump(xml, buf, 2)
         buf.seek(0)
         xml = pickle.load(buf)
-        self.assertEquals('<li>Foo</li>', xml.render(encoding=None))
+        self.assertEqual('<li>Foo</li>', xml.render(encoding=None))
 
 
 class MarkupTestCase(unittest.TestCase):
 
     def test_new_with_encoding(self):
-        markup = Markup(u'Döner'.encode('utf-8'), encoding='utf-8')
+        markup = Markup('Döner'.encode('utf-8'), encoding='utf-8')
         # mimic Markup.__repr__ when constructing output for Python 2/3 compatibility
-        self.assertEquals("<Markup %r>" % u'D\u00f6ner', repr(markup))
+        self.assertEqual("<Markup %r>" % 'D\u00f6ner', repr(markup))
 
     def test_repr(self):
         markup = Markup('foo')
-        self.assertEquals("<Markup u'foo'>", repr(markup))
+        self.assertEqual("<Markup u'foo'>", repr(markup))
 
     def test_escape(self):
         markup = escape('<b>"&"</b>')
         assert type(markup) is Markup
-        self.assertEquals('&lt;b&gt;&#34;&amp;&#34;&lt;/b&gt;', markup)
+        self.assertEqual('&lt;b&gt;&#34;&amp;&#34;&lt;/b&gt;', markup)
 
     def test_escape_noquotes(self):
         markup = escape('<b>"&"</b>', quotes=False)
         assert type(markup) is Markup
-        self.assertEquals('&lt;b&gt;"&amp;"&lt;/b&gt;', markup)
+        self.assertEqual('&lt;b&gt;"&amp;"&lt;/b&gt;', markup)
 
     def test_unescape_markup(self):
         string = '<b>"&"</b>'
         markup = Markup.escape(string)
         assert type(markup) is Markup
-        self.assertEquals(string, unescape(markup))
+        self.assertEqual(string, unescape(markup))
 
     def test_Markup_escape_None_noquotes(self):
         markup = Markup.escape(None, False)
         assert type(markup) is Markup
-        self.assertEquals('', markup)
+        self.assertEqual('', markup)
 
     def test_add_str(self):
         markup = Markup('<b>foo</b>') + '<br/>'
         assert type(markup) is Markup
-        self.assertEquals('<b>foo</b>&lt;br/&gt;', markup)
+        self.assertEqual('<b>foo</b>&lt;br/&gt;', markup)
 
     def test_add_markup(self):
         markup = Markup('<b>foo</b>') + Markup('<br/>')
         assert type(markup) is Markup
-        self.assertEquals('<b>foo</b><br/>', markup)
+        self.assertEqual('<b>foo</b><br/>', markup)
 
     def test_add_reverse(self):
         markup = '<br/>' + Markup('<b>bar</b>')
         assert type(markup) is Markup
-        self.assertEquals('&lt;br/&gt;<b>bar</b>', markup)
+        self.assertEqual('&lt;br/&gt;<b>bar</b>', markup)
 
     def test_mod(self):
         markup = Markup('<b>%s</b>') % '&'
         assert type(markup) is Markup
-        self.assertEquals('<b>&amp;</b>', markup)
+        self.assertEqual('<b>&amp;</b>', markup)
 
     def test_mod_multi(self):
         markup = Markup('<b>%s</b> %s') % ('&', 'boo')
         assert type(markup) is Markup
-        self.assertEquals('<b>&amp;</b> boo', markup)
+        self.assertEqual('<b>&amp;</b> boo', markup)
 
     def test_mod_mapping(self):
         markup = Markup('<b>%(foo)s</b>') % {'foo': '&'}
         assert type(markup) is Markup
-        self.assertEquals('<b>&amp;</b>', markup)
+        self.assertEqual('<b>&amp;</b>', markup)
 
     def test_mod_noescape(self):
         markup = Markup('<b>%(amp)s</b>') % {'amp': Markup('&amp;')}
         assert type(markup) is Markup
-        self.assertEquals('<b>&amp;</b>', markup)
+        self.assertEqual('<b>&amp;</b>', markup)
 
     def test_mul(self):
         markup = Markup('<b>foo</b>') * 2
         assert type(markup) is Markup
-        self.assertEquals('<b>foo</b><b>foo</b>', markup)
+        self.assertEqual('<b>foo</b><b>foo</b>', markup)
 
     def test_mul_reverse(self):
         markup = 2 * Markup('<b>foo</b>')
         assert type(markup) is Markup
-        self.assertEquals('<b>foo</b><b>foo</b>', markup)
+        self.assertEqual('<b>foo</b><b>foo</b>', markup)
 
     def test_join(self):
         markup = Markup('<br />').join(['foo', '<bar />', Markup('<baz />')])
         assert type(markup) is Markup
-        self.assertEquals('foo<br />&lt;bar /&gt;<br /><baz />', markup)
+        self.assertEqual('foo<br />&lt;bar /&gt;<br /><baz />', markup)
 
     def test_join_over_iter(self):
         items = ['foo', '<bar />', Markup('<baz />')]
         markup = Markup('<br />').join(i for i in items)
-        self.assertEquals('foo<br />&lt;bar /&gt;<br /><baz />', markup)
+        self.assertEqual('foo<br />&lt;bar /&gt;<br /><baz />', markup)
 
     def test_stripentities_all(self):
         markup = Markup('&amp; &#106;').stripentities()
         assert type(markup) is Markup
-        self.assertEquals('& j', markup)
+        self.assertEqual('& j', markup)
 
     def test_stripentities_keepxml(self):
         markup = Markup('&amp; &#106;').stripentities(keepxmlentities=True)
         assert type(markup) is Markup
-        self.assertEquals('&amp; j', markup)
+        self.assertEqual('&amp; j', markup)
 
     def test_striptags_empty(self):
         markup = Markup('<br />').striptags()
         assert type(markup) is Markup
-        self.assertEquals('', markup)
+        self.assertEqual('', markup)
 
     def test_striptags_mid(self):
         markup = Markup('<a href="#">fo<br />o</a>').striptags()
         assert type(markup) is Markup
-        self.assertEquals('foo', markup)
+        self.assertEqual('foo', markup)
 
     def test_pickle(self):
         markup = Markup('foo')
         buf = BytesIO()
         pickle.dump(markup, buf, 2)
         buf.seek(0)
-        self.assertEquals("<Markup u'foo'>", repr(pickle.load(buf)))
+        self.assertEqual("<Markup u'foo'>", repr(pickle.load(buf)))
 
 
 class AttrsTestCase(unittest.TestCase):
@@ -180,12 +180,12 @@ class AttrsTestCase(unittest.TestCase):
         pickle.dump(attrs, buf, 2)
         buf.seek(0)
         unpickled = pickle.load(buf)
-        self.assertEquals("Attrs([('attr1', 'foo'), ('attr2', 'bar')])",
+        self.assertEqual("Attrs([('attr1', 'foo'), ('attr2', 'bar')])",
                           repr(unpickled))
 
     def test_non_ascii(self):
-        attrs_tuple = Attrs([("attr1", u"föö"), ("attr2", u"bär")]).totuple()
-        self.assertEqual(u'fööbär', attrs_tuple[1])
+        attrs_tuple = Attrs([("attr1", "föö"), ("attr2", "bär")]).totuple()
+        self.assertEqual('fööbär', attrs_tuple[1])
 
 
 class NamespaceTestCase(unittest.TestCase):
@@ -199,7 +199,7 @@ class NamespaceTestCase(unittest.TestCase):
         self.assertEqual(eval(repr(ns)), ns)
 
     def test_repr_eval_non_ascii(self):
-        ns = Namespace(u'http://www.example.org/nämespäcé')
+        ns = Namespace('http://www.example.org/nämespäcé')
         self.assertEqual(eval(repr(ns)), ns)
 
     def test_pickle(self):
@@ -208,9 +208,9 @@ class NamespaceTestCase(unittest.TestCase):
         pickle.dump(ns, buf, 2)
         buf.seek(0)
         unpickled = pickle.load(buf)
-        self.assertEquals("Namespace('http://www.example.org/namespace')",
+        self.assertEqual("Namespace('http://www.example.org/namespace')",
                           repr(unpickled))
-        self.assertEquals('http://www.example.org/namespace', unpickled.uri)
+        self.assertEqual('http://www.example.org/namespace', unpickled.uri)
 
 
 class QNameTestCase(unittest.TestCase):
@@ -221,10 +221,10 @@ class QNameTestCase(unittest.TestCase):
         pickle.dump(qname, buf, 2)
         buf.seek(0)
         unpickled = pickle.load(buf)
-        self.assertEquals('{http://www.example.org/namespace}elem', unpickled)
-        self.assertEquals('http://www.example.org/namespace',
+        self.assertEqual('{http://www.example.org/namespace}elem', unpickled)
+        self.assertEqual('http://www.example.org/namespace',
                           unpickled.namespace)
-        self.assertEquals('elem', unpickled.localname)
+        self.assertEqual('elem', unpickled.localname)
 
     def test_repr(self):
         self.assertEqual("QName('elem')", repr(QName('elem')))
@@ -236,13 +236,13 @@ class QNameTestCase(unittest.TestCase):
         self.assertEqual(eval(repr(qn)), qn)
 
     def test_repr_eval_non_ascii(self):
-        qn = QName(u'élem')
+        qn = QName('élem')
         self.assertEqual(eval(repr(qn)), qn)
 
     def test_leading_curly_brace(self):
         qname = QName('{http://www.example.org/namespace}elem')
-        self.assertEquals('http://www.example.org/namespace', qname.namespace)
-        self.assertEquals('elem', qname.localname)
+        self.assertEqual('http://www.example.org/namespace', qname.namespace)
+        self.assertEqual('elem', qname.localname)
 
     def test_curly_brace_equality(self):
         qname1 = QName('{http://www.example.org/namespace}elem')
--- genshi/tests/input.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/tests/input.py
@@ -51,27 +51,27 @@ bar</elem>'''
         self.assertEqual(('class', 'bar'), attrib[2])
 
     def test_unicode_input(self):
-        text = u'<div>\u2013</div>'
+        text = '<div>\u2013</div>'
         events = list(XMLParser(StringIO(text)))
         kind, data, pos = events[1]
         self.assertEqual(Stream.TEXT, kind)
-        self.assertEqual(u'\u2013', data)
+        self.assertEqual('\u2013', data)
 
     def test_latin1_encoded(self):
-        text = u'<div>\xf6</div>'.encode('iso-8859-1')
+        text = '<div>\xf6</div>'.encode('iso-8859-1')
         events = list(XMLParser(BytesIO(text), encoding='iso-8859-1'))
         kind, data, pos = events[1]
         self.assertEqual(Stream.TEXT, kind)
-        self.assertEqual(u'\xf6', data)
+        self.assertEqual('\xf6', data)
 
     def test_latin1_encoded_xmldecl(self):
-        text = u"""<?xml version="1.0" encoding="iso-8859-1" ?>
+        text = """<?xml version="1.0" encoding="iso-8859-1" ?>
         <div>\xf6</div>
         """.encode('iso-8859-1')
         events = list(XMLParser(BytesIO(text)))
         kind, data, pos = events[2]
         self.assertEqual(Stream.TEXT, kind)
-        self.assertEqual(u'\xf6', data)
+        self.assertEqual('\xf6', data)
 
     def test_html_entity_with_dtd(self):
         text = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
@@ -81,21 +81,21 @@ bar</elem>'''
         events = list(XMLParser(StringIO(text)))
         kind, data, pos = events[2]
         self.assertEqual(Stream.TEXT, kind)
-        self.assertEqual(u'\xa0', data)
+        self.assertEqual('\xa0', data)
 
     def test_html_entity_without_dtd(self):
         text = '<html>&nbsp;</html>'
         events = list(XMLParser(StringIO(text)))
         kind, data, pos = events[1]
         self.assertEqual(Stream.TEXT, kind)
-        self.assertEqual(u'\xa0', data)
+        self.assertEqual('\xa0', data)
 
     def test_html_entity_in_attribute(self):
         text = '<p title="&nbsp;"/>'
         events = list(XMLParser(StringIO(text)))
         kind, data, pos = events[0]
         self.assertEqual(Stream.START, kind)
-        self.assertEqual(u'\xa0', data[1].get('title'))
+        self.assertEqual('\xa0', data[1].get('title'))
         kind, data, pos = events[1]
         self.assertEqual(Stream.END, kind)
 
@@ -116,7 +116,7 @@ bar</elem>'''
 class HTMLParserTestCase(unittest.TestCase):
 
     def test_text_node_pos_single_line(self):
-        text = u'<elem>foo bar</elem>'
+        text = '<elem>foo bar</elem>'
         events = list(HTMLParser(StringIO(text)))
         kind, data, pos = events[1]
         self.assertEqual(Stream.TEXT, kind)
@@ -124,7 +124,7 @@ class HTMLParserTestCase(unittest.TestCase):
         self.assertEqual((None, 1, 6), pos)
 
     def test_text_node_pos_multi_line(self):
-        text = u'''<elem>foo
+        text = '''<elem>foo
 bar</elem>'''
         events = list(HTMLParser(StringIO(text)))
         kind, data, pos = events[1]
@@ -133,44 +133,44 @@ bar</elem>'''
         self.assertEqual((None, 1, 6), pos)
 
     def test_input_encoding_text(self):
-        text = u'<div>\xf6</div>'.encode('iso-8859-1')
+        text = '<div>\xf6</div>'.encode('iso-8859-1')
         events = list(HTMLParser(BytesIO(text), encoding='iso-8859-1'))
         kind, data, pos = events[1]
         self.assertEqual(Stream.TEXT, kind)
-        self.assertEqual(u'\xf6', data)
+        self.assertEqual('\xf6', data)
 
     def test_input_encoding_attribute(self):
-        text = u'<div title="\xf6"></div>'.encode('iso-8859-1')
+        text = '<div title="\xf6"></div>'.encode('iso-8859-1')
         events = list(HTMLParser(BytesIO(text), encoding='iso-8859-1'))
         kind, (tag, attrib), pos = events[0]
         self.assertEqual(Stream.START, kind)
-        self.assertEqual(u'\xf6', attrib.get('title'))
+        self.assertEqual('\xf6', attrib.get('title'))
 
     def test_unicode_input(self):
-        text = u'<div>\u2013</div>'
+        text = '<div>\u2013</div>'
         events = list(HTMLParser(StringIO(text)))
         kind, data, pos = events[1]
         self.assertEqual(Stream.TEXT, kind)
-        self.assertEqual(u'\u2013', data)
+        self.assertEqual('\u2013', data)
 
     def test_html_entity_in_attribute(self):
-        text = u'<p title="&nbsp;"></p>'
+        text = '<p title="&nbsp;"></p>'
         events = list(HTMLParser(StringIO(text)))
         kind, data, pos = events[0]
         self.assertEqual(Stream.START, kind)
-        self.assertEqual(u'\xa0', data[1].get('title'))
+        self.assertEqual('\xa0', data[1].get('title'))
         kind, data, pos = events[1]
         self.assertEqual(Stream.END, kind)
 
     def test_html_entity_in_text(self):
-        text = u'<p>&nbsp;</p>'
+        text = '<p>&nbsp;</p>'
         events = list(HTMLParser(StringIO(text)))
         kind, data, pos = events[1]
         self.assertEqual(Stream.TEXT, kind)
-        self.assertEqual(u'\xa0', data)
+        self.assertEqual('\xa0', data)
 
     def test_processing_instruction(self):
-        text = u'<?php echo "Foobar" ?>'
+        text = '<?php echo "Foobar" ?>'
         events = list(HTMLParser(StringIO(text)))
         kind, (target, data), pos = events[0]
         self.assertEqual(Stream.PI, kind)
@@ -178,7 +178,7 @@ bar</elem>'''
         self.assertEqual('echo "Foobar"', data)
 
     def test_processing_instruction_no_data_1(self):
-        text = u'<?foo ?>'
+        text = '<?foo ?>'
         events = list(HTMLParser(StringIO(text)))
         kind, (target, data), pos = events[0]
         self.assertEqual(Stream.PI, kind)
@@ -186,7 +186,7 @@ bar</elem>'''
         self.assertEqual('', data)
 
     def test_processing_instruction_no_data_2(self):
-        text = u'<?experiment>...<?/experiment>'
+        text = '<?experiment>...<?/experiment>'
         events = list(HTMLParser(StringIO(text)))
         kind, (target, data), pos = events[0]
         self.assertEqual(Stream.PI, kind)
@@ -224,7 +224,7 @@ bar</elem>'''
         self.assertEqual(1, standalone)
 
     def test_processing_instruction_trailing_qmark(self):
-        text = u'<?php echo "Foobar" ??>'
+        text = '<?php echo "Foobar" ??>'
         events = list(HTMLParser(StringIO(text)))
         kind, (target, data), pos = events[0]
         self.assertEqual(Stream.PI, kind)
@@ -232,7 +232,7 @@ bar</elem>'''
         self.assertEqual('echo "Foobar" ?', data)
 
     def test_out_of_order_tags1(self):
-        text = u'<span><b>Foobar</span></b>'
+        text = '<span><b>Foobar</span></b>'
         events = list(HTMLParser(StringIO(text)))
         self.assertEqual(5, len(events))
         self.assertEqual((Stream.START, ('span', ())), events[0][:2])
@@ -242,7 +242,7 @@ bar</elem>'''
         self.assertEqual((Stream.END, 'span'), events[4][:2])
 
     def test_out_of_order_tags2(self):
-        text = u'<span class="baz"><b><i>Foobar</span></b></i>'.encode('utf-8')
+        text = '<span class="baz"><b><i>Foobar</span></b></i>'.encode('utf-8')
         events = list(HTMLParser(BytesIO(text), encoding='utf-8'))
         self.assertEqual(7, len(events))
         self.assertEqual((Stream.START, ('span', Attrs([('class', 'baz')]))),
@@ -255,7 +255,7 @@ bar</elem>'''
         self.assertEqual((Stream.END, 'span'), events[6][:2])
 
     def test_out_of_order_tags3(self):
-        text = u'<span><b>Foobar</i>'.encode('utf-8')
+        text = '<span><b>Foobar</i>'.encode('utf-8')
         events = list(HTMLParser(BytesIO(text), encoding='utf-8'))
         self.assertEqual(5, len(events))
         self.assertEqual((Stream.START, ('span', ())), events[0][:2])
@@ -265,7 +265,7 @@ bar</elem>'''
         self.assertEqual((Stream.END, 'span'), events[4][:2])
 
     def test_hex_charref(self):
-        text = u'<span>&#x27;</span>'
+        text = '<span>&#x27;</span>'
         events = list(HTMLParser(StringIO(text)))
         self.assertEqual(3, len(events))
         self.assertEqual((Stream.START, ('span', ())), events[0][:2])
@@ -273,7 +273,7 @@ bar</elem>'''
         self.assertEqual((Stream.END, 'span'), events[2][:2])
 
     def test_multibyte_character_on_chunk_boundary(self):
-        text = u'a' * ((4 * 1024) - 1) + u'\xe6'
+        text = 'a' * ((4 * 1024) - 1) + '\xe6'
         events = list(HTMLParser(BytesIO(text.encode('utf-8')),
                                  encoding='utf-8'))
         self.assertEqual(1, len(events))
--- genshi/tests/output.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/tests/output.py
@@ -356,7 +356,7 @@ class XHTMLSerializerTestCase(unittest.TestCase):
         </div>""", output)
 
     def test_html5_doctype(self):
-        stream = HTML(u'<html></html>')
+        stream = HTML('<html></html>')
         output = stream.render(XHTMLSerializer, doctype=DocType.HTML5,
                                encoding=None)
         self.assertEqual('<!DOCTYPE html>\n<html></html>', output)
@@ -369,7 +369,7 @@ class XHTMLSerializerTestCase(unittest.TestCase):
     def test_cache_markup(self):
         loc = (None, -1, -1)
         stream = Stream([(Stream.START, (QName('foo'), Attrs()), loc),
-                         (Stream.TEXT, u'&hellip;', loc),
+                         (Stream.TEXT, '&hellip;', loc),
                          (Stream.END, QName('foo'), loc),
                          (Stream.START, (QName('bar'), Attrs()), loc),
                          (Stream.TEXT, Markup('&hellip;'), loc),
@@ -444,7 +444,7 @@ class HTMLSerializerTestCase(unittest.TestCase):
         </style>""", output)
 
     def test_html5_doctype(self):
-        stream = HTML(u'<html></html>')
+        stream = HTML('<html></html>')
         output = stream.render(HTMLSerializer, doctype=DocType.HTML5,
                                encoding=None)
         self.assertEqual('<!DOCTYPE html>\n<html></html>', output)
--- genshi/tests/path.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/tests/path.py
@@ -636,7 +636,7 @@ class PathTestCase(unittest.TestCase):
         path = Path('foo/@bar')
         result = path.select(xml)
         self.assertEqual(list(result), [
-            Attrs([(QName('bar'), u'abc')])
+            Attrs([(QName('bar'), 'abc')])
         ])
 
     def test_attr_selection_with_namespace(self):
@@ -647,7 +647,7 @@ class PathTestCase(unittest.TestCase):
         path = Path('foo/@ns1:bar')
         result = path.select(xml, namespaces={'ns1': 'http://example.com'})
         self.assertEqual(list(result), [
-            Attrs([(QName('http://example.com}bar'), u'abc')])
+            Attrs([(QName('http://example.com}bar'), 'abc')])
         ])
 
     def _test_support(self, strategy_class, text):
@@ -655,21 +655,21 @@ class PathTestCase(unittest.TestCase):
         return strategy_class.supports(path)
 
     def test_simple_strategy_support(self):
-        self.assert_(self._test_support(SimplePathStrategy, 'a/b'))
-        self.assert_(self._test_support(SimplePathStrategy, 'self::a/b'))
-        self.assert_(self._test_support(SimplePathStrategy, 'descendant::a/b'))
-        self.assert_(self._test_support(SimplePathStrategy,
+        self.assertTrue(self._test_support(SimplePathStrategy, 'a/b'))
+        self.assertTrue(self._test_support(SimplePathStrategy, 'self::a/b'))
+        self.assertTrue(self._test_support(SimplePathStrategy, 'descendant::a/b'))
+        self.assertTrue(self._test_support(SimplePathStrategy,
                          'descendant-or-self::a/b'))
-        self.assert_(self._test_support(SimplePathStrategy, '//a/b'))
-        self.assert_(self._test_support(SimplePathStrategy, 'a/@b'))
-        self.assert_(self._test_support(SimplePathStrategy, 'a/text()'))
+        self.assertTrue(self._test_support(SimplePathStrategy, '//a/b'))
+        self.assertTrue(self._test_support(SimplePathStrategy, 'a/@b'))
+        self.assertTrue(self._test_support(SimplePathStrategy, 'a/text()'))
 
         # a//b is a/descendant-or-self::node()/b
-        self.assert_(not self._test_support(SimplePathStrategy, 'a//b'))
-        self.assert_(not self._test_support(SimplePathStrategy, 'node()/@a'))
-        self.assert_(not self._test_support(SimplePathStrategy, '@a'))
-        self.assert_(not self._test_support(SimplePathStrategy, 'foo:bar'))
-        self.assert_(not self._test_support(SimplePathStrategy, 'a/@foo:bar'))
+        self.assertTrue(not self._test_support(SimplePathStrategy, 'a//b'))
+        self.assertTrue(not self._test_support(SimplePathStrategy, 'node()/@a'))
+        self.assertTrue(not self._test_support(SimplePathStrategy, '@a'))
+        self.assertTrue(not self._test_support(SimplePathStrategy, 'foo:bar'))
+        self.assertTrue(not self._test_support(SimplePathStrategy, 'a/@foo:bar'))
 
     def _test_strategies(self, input, path, output,
                          namespaces=None, variables=None):
--- genshi/util.py.orig	2019-05-27 21:03:08 UTC
+++ genshi/util.py
@@ -13,10 +13,10 @@
 
 """Various utility classes and functions."""
 
-import htmlentitydefs as entities
+import html.entities as entities
 import re
 
-from compat import any, all, stringrepr
+from .compat import any, all, stringrepr
 
 __docformat__ = 'restructuredtext en'
 
@@ -193,17 +193,17 @@ def stripentities(text, keepxmlentities=False):
     >>> stripentities('1 &lt; 2')
     u'1 < 2'
     >>> stripentities('more &hellip;')
-    u'more \u2026'
+    u'more \\u2026'
     >>> stripentities('&#8230;')
-    u'\u2026'
+    u'\\u2026'
     >>> stripentities('&#x2026;')
-    u'\u2026'
+    u'\\u2026'
     
     If the `keepxmlentities` parameter is provided and is a truth value, the
     core XML entities (&amp;, &apos;, &gt;, &lt; and &quot;) are left intact.
     
     >>> stripentities('1 &lt; 2 &hellip;', keepxmlentities=True)
-    u'1 &lt; 2 \u2026'
+    u'1 &lt; 2 \\u2026'
     """
     def _replace_entity(match):
         if match.group(1): # numeric entity
@@ -212,13 +212,13 @@ def stripentities(text, keepxmlentities=False):
                 ref = int(ref[1:], 16)
             else:
                 ref = int(ref, 10)
-            return unichr(ref)
+            return chr(ref)
         else: # character entity
             ref = match.group(2)
             if keepxmlentities and ref in ('amp', 'apos', 'gt', 'lt', 'quot'):
                 return '&%s;' % ref
             try:
-                return unichr(entities.name2codepoint[ref])
+                return chr(entities.name2codepoint[ref])
             except KeyError:
                 if keepxmlentities:
                     return '&amp;%s;' % ref
--- scripts/ast_generator.py.orig	2019-05-27 21:03:08 UTC
+++ scripts/ast_generator.py
@@ -23,12 +23,12 @@ def print_class(cls):
             bnames.append("%s" % base.__name__)
         else:
             bnames.append("%s.%s" % (base.__module__,base.__name__))
-    print("class %s(%s):" % (cls.__name__, ", ".join(bnames)))
+    print(("class %s(%s):" % (cls.__name__, ", ".join(bnames))))
     written = False
     for attr in cls.__dict__:
         if attr not in IGNORE_ATTRS:
             written = True
-            print("\t%s = %s" % (attr, repr(cls.__dict__[attr]),))
+            print(("\t%s = %s" % (attr, repr(cls.__dict__[attr]),)))
     if not written:
         print("\tpass")
     done.add(cls)
@@ -36,11 +36,11 @@ def print_class(cls):
 print('# Generated automatically, please do not edit')
 print('# Generator can be found in Genshi SVN, scripts/ast_generator.py')
 print('')
-print('__version__ = %s' % _ast.__version__)
+print(('__version__ = %s' % _ast.__version__))
 print('')
 
 for name in dir(_ast):
     cls = getattr(_ast, name)
     if cls.__class__ is type:
         print_class(cls)
-        print
+        print()
--- setup.py.orig	2019-05-27 21:03:08 UTC
+++ setup.py
@@ -93,12 +93,8 @@ if bdist_egg:
     cmdclass['bdist_egg'] = my_bdist_egg
 
 
-# Use 2to3 if we're running under Python 3 (with Distribute)
 extra = {}
 if sys.version_info >= (3,):
-    extra['use_2to3'] = True
-    extra['convert_2to3_doctests'] = []
-    extra['use_2to3_fixers'] = ['fixes']
     # Install genshi template tests
     extra['include_package_data'] = True
 
