aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPo-Chuan Hsieh <sunpoet@FreeBSD.org>2022-03-07 18:07:50 +0000
committerPo-Chuan Hsieh <sunpoet@FreeBSD.org>2022-03-07 18:13:21 +0000
commit86ae39175edc3d32e8aec35e374f9754cc607d31 (patch)
treee33f2b7a1fe475e5c05e11b93e8ba00ec5678ffa
parentd6ba389917805948fafc56d95e317219607e3d59 (diff)
downloadports-86ae39175edc3d32e8aec35e374f9754cc607d31.tar.gz
ports-86ae39175edc3d32e8aec35e374f9754cc607d31.zip
-rw-r--r--textproc/py-genshi/files/patch-2to35818
1 files changed, 5818 insertions, 0 deletions
diff --git a/textproc/py-genshi/files/patch-2to3 b/textproc/py-genshi/files/patch-2to3
new file mode 100644
index 000000000000..1764fbffbda4
--- /dev/null
+++ b/textproc/py-genshi/files/patch-2to3
@@ -0,0 +1,5818 @@
+--- 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:expression(alert())">'
+- u'XSS</div>')
+- self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
++ html = HTML('<div style="top:expression(alert())">'
++ 'XSS</div>')
++ self.assertEqual('<div>XSS</div>', str(html | StyleSanitizer()))
+ # Fullwidth capital letters
+- html = HTML(u'<div style="top:EXPRESSION(alert())">'
+- u'XSS</div>')
+- self.assertEqual('<div>XSS</div>', unicode(html | StyleSanitizer()))
++ html = HTML('<div style="top:EXPRESSION(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
+