From 86ae39175edc3d32e8aec35e374f9754cc607d31 Mon Sep 17 00:00:00 2001 From: Po-Chuan Hsieh Date: Tue, 8 Mar 2022 02:07:50 +0800 Subject: textproc/py-genshi: Fix build with setuptools 58.0.0+ With hat: python --- textproc/py-genshi/files/patch-2to3 | 5818 +++++++++++++++++++++++++++++++++++ 1 file changed, 5818 insertions(+) create mode 100644 textproc/py-genshi/files/patch-2to3 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='', 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('"', '"') \ ++ return str(self).replace('"', '"') \ + .replace('>', '>') \ + .replace('<', '<') \ + .replace('&', '&') +@@ -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 +- _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 + # 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 or 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(""" + + """) +- 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(""" +@@ -237,10 +237,10 @@ class MsgDirectiveTestCase(unittest.TestCase): + Please see Help for details. +

+ """) +- 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""" ++ self.assertEqual(""" +

Für Details siehe bitte Hilfe.

+ """.encode('utf-8'), tmpl.generate().render(encoding='utf-8')) + +@@ -260,10 +260,10 @@ class MsgDirectiveTestCase(unittest.TestCase): + xmlns:i18n="http://genshi.edgewall.org/i18n"> +

Please see Help

+ """) +- 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""" ++ self.assertEqual(""" +

Für Details siehe bitte Hilfe

+ """, tmpl.generate().render()) + +@@ -283,10 +283,10 @@ class MsgDirectiveTestCase(unittest.TestCase): + xmlns:i18n="http://genshi.edgewall.org/i18n"> + Please see Help + """) +- 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""" ++ self.assertEqual(""" + Für Details siehe bitte Hilfe + """.encode('utf-8'), tmpl.generate().render(encoding='utf-8')) + +@@ -317,11 +317,11 @@ class MsgDirectiveTestCase(unittest.TestCase): + """) + 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""" ++ self.assertEqual(""" +

Siehe bitte Hilfe

+ """, tmpl.generate().render(encoding=None)) + +@@ -352,11 +352,11 @@ class MsgDirectiveTestCase(unittest.TestCase): + """) + 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""" ++ self.assertEqual(""" +

Siehe bitte Hilfe

+ """, tmpl.generate(_=translator.translate).render(encoding=None)) + +@@ -384,11 +384,11 @@ class MsgDirectiveTestCase(unittest.TestCase): + + """) + 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""" ++ self.assertEqual(""" + Siehe bitte Hilfe + """, tmpl.generate().render(encoding=None)) + +@@ -413,10 +413,10 @@ class MsgDirectiveTestCase(unittest.TestCase): + Please see Help page for details. +

+ """) +- 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""" ++ self.assertEqual(""" +

Für Details siehe bitte Hilfeseite.

+ """, tmpl.generate().render()) + +@@ -468,10 +468,10 @@ class MsgDirectiveTestCase(unittest.TestCase): + Show me entries per page. +

+ """) +- 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""" ++ self.assertEqual(""" +

Einträge pro Seite anzeigen.

+ """, tmpl.generate().render()) + +@@ -495,10 +495,10 @@ class MsgDirectiveTestCase(unittest.TestCase): + Please see Help for details. +

+ """) +- 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""" ++ self.assertEqual(""" +

Für Details siehe bitte Hilfe.

+ """, tmpl.generate().render()) + +@@ -523,10 +523,10 @@ class MsgDirectiveTestCase(unittest.TestCase): + Show me entries per page, starting at page . +

+ """, 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""" ++ self.assertEqual(""" +

Eintr\u00E4ge pro Seite, beginnend auf Seite .

+ """.encode('utf-8'), tmpl.generate().render(encoding='utf-8')) + +@@ -550,7 +550,7 @@ class MsgDirectiveTestCase(unittest.TestCase): + Hello, ${user.name}! +

+ """) +- gettext = lambda s: u"Hallo, %(name)s!" ++ gettext = lambda s: "Hallo, %(name)s!" + translator = Translator(gettext) + translator.setup(tmpl) + self.assertEqual(""" +@@ -564,10 +564,10 @@ class MsgDirectiveTestCase(unittest.TestCase): + Hello, ${user.name}! +

+ """) +- 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""" ++ self.assertEqual(""" +

Jim, sei gegrüßt!

+ """, tmpl.generate(user=dict(name='Jim')).render()) + +@@ -578,10 +578,10 @@ class MsgDirectiveTestCase(unittest.TestCase): + Hello, dude! +

+ """) +- 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""" ++ self.assertEqual(""" +

Sei gegrüßt, Alter!

+ """, tmpl.generate(anchor='42').render()) + +@@ -605,7 +605,7 @@ class MsgDirectiveTestCase(unittest.TestCase): + Written by ${entry.author} at ${entry.time.strftime('%H:%M')} +

+ """) +- 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 entries per page. +

+ """) +- 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""" ++ self.assertEqual(""" +

Einträge pro Seite anzeigen.

+ """, tmpl.generate().render()) + +@@ -668,7 +668,7 @@ class MsgDirectiveTestCase(unittest.TestCase): + xmlns:i18n="http://genshi.edgewall.org/i18n"> +

Foo

+ """) +- gettext = lambda s: u"Voh" ++ gettext = lambda s: "Voh" + translator = Translator(gettext) + translator.setup(tmpl) + self.assertEqual(""" +@@ -691,14 +691,14 @@ class MsgDirectiveTestCase(unittest.TestCase): + xmlns:i18n="http://genshi.edgewall.org/i18n"> +

Foo

+ """) +- 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""" ++ self.assertEqual(""" +

Voh

+ """, tmpl.generate().render()) + +@@ -738,11 +738,11 @@ class MsgDirectiveTestCase(unittest.TestCase): + + """) + 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""" ++ self.assertEqual(""" + Modificado à um dia por Pedro + """.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): +

Translation[ 0 ]: One coin

+ """) + 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""" ++ self.assertEqual(""" +

Trandução[ 0 ]: Uma moeda

+ """.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""" ++ self.assertEqual(""" +

Antes de o fazer, porém, + por favor tente procurar + por problemas semelhantes, 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""" ++ self.assertEqual(""" +

Nota: Este repositório está definido em + trac.ini + e não pode ser editado nesta página.

+@@ -933,7 +933,7 @@ class MsgDirectiveTestCase(unittest.TestCase): + xmlns:i18n="http://genshi.edgewall.org/i18n"> +

text $name

+ """) +- 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(""" +@@ -1342,12 +1342,12 @@ class ChooseDirectiveTestCase(unittest.TestCase): + }) + translator = Translator(translations) + translator.setup(tmpl) +- self.assertEqual(u""" ++ self.assertEqual(""" +

+ Da ist 1 Sache. +

+ """, tmpl.generate(link="/things", num=1).render(encoding=None)) +- self.assertEqual(u""" ++ self.assertEqual(""" +

+ Da sind 3 Sachen. +

+@@ -1399,10 +1399,10 @@ class ChooseDirectiveTestCase(unittest.TestCase): + }) + translator = Translator(translations) + translator.setup(tmpl) +- self.assertEqual(u""" ++ self.assertEqual(""" +

Da ist 1 Sache.

+ """, tmpl.generate(link="/things", num=1).render(encoding=None)) +- self.assertEqual(u""" ++ self.assertEqual(""" +

Da sind 3 Sachen.

+ """, 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"""

++ html = HTML("""

+ +

""") | HTMLFormFiller() +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", html.render()) + + def test_fill_input_text_single_value(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") | HTMLFormFiller(data={'foo': 'bar'}) +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", html.render()) + + def test_fill_input_text_multi_value(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") | HTMLFormFiller(data={'foo': ['bar']}) +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", html.render()) + + def test_fill_input_hidden_no_value(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") | HTMLFormFiller() +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", html.render()) + + def test_fill_input_hidden_single_value(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") | HTMLFormFiller(data={'foo': 'bar'}) +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", html.render()) + + def test_fill_input_hidden_multi_value(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") | HTMLFormFiller(data={'foo': ['bar']}) +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", html.render()) + + def test_fill_textarea_no_value(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") | HTMLFormFiller() +- self.assertEquals("""

++ self.assertEqual("""

+ +

""") | HTMLFormFiller(data={'foo': 'bar'}) +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", html.render()) + + def test_fill_textarea_multi_value(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") | HTMLFormFiller(data={'foo': ['bar']}) +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", html.render()) + + def test_fill_textarea_multiple(self): + # Ensure that the subsequent textarea doesn't get the data from the + # first +- html = HTML(u"""

++ html = HTML("""

+ + +

""") | HTMLFormFiller(data={'foo': 'Some text'}) +- self.assertEquals("""

++ self.assertEqual("""

+ + + +

""") | HTMLFormFiller(data={'foo': 'Some text'}) +- self.assertEquals("""

++ self.assertEqual("""

+ + +

""", html.render()) + + def test_fill_input_checkbox_single_value_auto_no_value(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") | HTMLFormFiller() +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", html.render()) + + def test_fill_input_checkbox_single_value_auto(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", (html | HTMLFormFiller(data={'foo': ''})).render()) +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", (html | HTMLFormFiller(data={'foo': 'on'})).render()) + +@@ -137,10 +137,10 @@ class HTMLFormFillerTestCase(unittest.TestCase): + html = HTML("""

+ +

""", encoding='ascii') +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", (html | HTMLFormFiller(data={'foo': '1'})).render()) +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", (html | HTMLFormFiller(data={'foo': '2'})).render()) + +@@ -148,79 +148,79 @@ class HTMLFormFillerTestCase(unittest.TestCase): + html = HTML("""

+ +

""", encoding='ascii') +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", (html | HTMLFormFiller(data={'foo': []})).render()) +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", (html | HTMLFormFiller(data={'foo': ['on']})).render()) + + def test_fill_input_checkbox_multi_value_defined(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", (html | HTMLFormFiller(data={'foo': ['1']})).render()) +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", (html | HTMLFormFiller(data={'foo': ['2']})).render()) + + def test_fill_input_radio_no_value(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") | HTMLFormFiller() +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", html.render()) + + def test_fill_input_radio_single_value(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", (html | HTMLFormFiller(data={'foo': '1'})).render()) +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", (html | HTMLFormFiller(data={'foo': '2'})).render()) + + def test_fill_input_radio_multi_value(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", (html | HTMLFormFiller(data={'foo': ['1']})).render()) +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", (html | HTMLFormFiller(data={'foo': ['2']})).render()) + + def test_fill_input_radio_empty_string(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", (html | HTMLFormFiller(data={'foo': ''})).render()) + + def test_fill_input_radio_multi_empty_string(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", (html | HTMLFormFiller(data={'foo': ['']})).render()) + + def test_fill_select_no_value_auto(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") | HTMLFormFiller() +- self.assertEquals("""

++ self.assertEqual("""

+ + + + + +

""") | HTMLFormFiller() +- self.assertEquals("""

++ self.assertEqual("""

+ + + + + +

""") | HTMLFormFiller(data={'foo': '1'}) +- self.assertEquals("""

++ self.assertEqual("""

+ + + + + +

""") | HTMLFormFiller(data={'foo': '1'}) +- self.assertEquals("""

++ self.assertEqual("""

+ + + + + +

""") | HTMLFormFiller(data={'foo': ['1', '3']}) +- self.assertEquals("""

++ self.assertEqual("""

+ + + + + +

""") | HTMLFormFiller(data={'foo': ['1', '3']}) +- self.assertEquals("""

++ self.assertEqual("""

+ + + +

""").generate(x=1) | HTMLFormFiller(data={'foo': '1'}) +- self.assertEquals(u"""
++ self.assertEqual(""" + +@@ -326,37 +326,37 @@ class HTMLFormFillerTestCase(unittest.TestCase): + + +
""").generate(x=1) | HTMLFormFiller(data={'foo': 'foo 1 bar'}) +- self.assertEquals("""
++ self.assertEqual(""" + +
""", html.render()) + + def test_fill_option_unicode_value(self): +- html = HTML(u"""
++ html = HTML(""" + +-
""") | HTMLFormFiller(data={'foo': u'ö'}) +- self.assertEquals(u"""
++
""") | HTMLFormFiller(data={'foo': 'ö'}) ++ self.assertEqual("""
+ +
""", html.render(encoding=None)) + + def test_fill_input_password_disabled(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") | HTMLFormFiller(data={'pass': 'bar'}) +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", html.render()) + + def test_fill_input_password_enabled(self): +- html = HTML(u"""

++ html = HTML("""

+ +

""") | HTMLFormFiller(data={'pass': '1234'}, passwords=True) +- self.assertEquals("""

++ self.assertEqual("""

+ +

""", 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'fo
o
') +- self.assertEquals('fo
o
', ++ html = HTML('fo
o
') ++ self.assertEqual('fo
o
', + (html | HTMLSanitizer()).render()) +- html = HTML(u'foo') +- self.assertEquals('foo', ++ html = HTML('foo') ++ self.assertEqual('foo', + (html | HTMLSanitizer()).render()) + + def test_sanitize_escape_text(self): +- html = HTML(u'fo&') +- self.assertEquals('fo&', ++ html = HTML('fo&') ++ self.assertEqual('fo&', + (html | HTMLSanitizer()).render()) +- html = HTML(u'<foo>') +- self.assertEquals('<foo>', ++ html = HTML('<foo>') ++ self.assertEqual('<foo>', + (html | HTMLSanitizer()).render()) + + def test_sanitize_entityref_text(self): +- html = HTML(u'foö') +- self.assertEquals(u'foö', ++ html = HTML('foö') ++ self.assertEqual('foö', + (html | HTMLSanitizer()).render(encoding=None)) + + def test_sanitize_escape_attr(self): +- html = HTML(u'
') +- self.assertEquals('
', ++ html = HTML('
') ++ self.assertEqual('
', + (html | HTMLSanitizer()).render()) + + def test_sanitize_close_empty_tag(self): +- html = HTML(u'fo
o
') +- self.assertEquals('fo
o
', ++ html = HTML('fo
o
') ++ self.assertEqual('fo
o
', + (html | HTMLSanitizer()).render()) + + def test_sanitize_invalid_entity(self): +- html = HTML(u'&junk;') +- self.assertEquals('&junk;', (html | HTMLSanitizer()).render()) ++ html = HTML('&junk;') ++ self.assertEqual('&junk;', (html | HTMLSanitizer()).render()) + + def test_sanitize_remove_script_elem(self): +- html = HTML(u'') +- self.assertEquals('', (html | HTMLSanitizer()).render()) +- html = HTML(u'') +- self.assertEquals('', (html | HTMLSanitizer()).render()) +- src = u'alert("foo")' ++ html = HTML('') ++ self.assertEqual('', (html | HTMLSanitizer()).render()) ++ html = HTML('') ++ self.assertEqual('', (html | HTMLSanitizer()).render()) ++ src = 'alert("foo")' + self.assert_parse_error_or_equal('<SCR\x00IPT>alert("foo")', src, + allow_strip=True) +- src = u'' ++ src = '' + self.assert_parse_error_or_equal('<SCRIPT&XYZ; ' + 'SRC="http://example.com/">', src, + allow_strip=True) + + def test_sanitize_remove_onclick_attr(self): +- html = HTML(u'
') +- self.assertEquals('
', (html | HTMLSanitizer()).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | HTMLSanitizer()).render()) + + def test_sanitize_remove_input_password(self): +- html = HTML(u'
') +- self.assertEquals('
', (html | HTMLSanitizer()).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | HTMLSanitizer()).render()) + + def test_sanitize_remove_comments(self): +- html = HTML(u'''
''') +- self.assertEquals('
', (html | HTMLSanitizer()).render()) ++ html = HTML('''
''') ++ self.assertEqual('
', (html | HTMLSanitizer()).render()) + + def test_sanitize_remove_style_scripts(self): + sanitizer = StyleSanitizer() + # Inline style with url() using javascript: scheme +- html = HTML(u'
') +- self.assertEquals('
', (html | sanitizer).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | sanitizer).render()) + # Inline style with url() using javascript: scheme, using control char +- html = HTML(u'
') +- self.assertEquals('
', (html | sanitizer).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | sanitizer).render()) + # Inline style with url() using javascript: scheme, in quotes +- html = HTML(u'
') +- self.assertEquals('
', (html | sanitizer).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | sanitizer).render()) + # IE expressions in CSS not allowed +- html = HTML(u'
') +- self.assertEquals('
', (html | sanitizer).render()) +- html = HTML(u'
') +- self.assertEquals('
', (html | sanitizer).render()) +- html = HTML(u'
') ++ self.assertEqual('
', (html | sanitizer).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | sanitizer).render()) ++ html = HTML('
') +- self.assertEquals('
', ++ self.assertEqual('
', + (html | sanitizer).render()) + # Inline style with url() using javascript: scheme, using unicode + # escapes +- html = HTML(u'
') +- self.assertEquals('
', (html | sanitizer).render()) +- html = HTML(u'
') +- self.assertEquals('
', (html | sanitizer).render()) +- html = HTML(u'
') +- self.assertEquals('
', (html | sanitizer).render()) +- html = HTML(u'
') +- self.assertEquals('
', (html | sanitizer).render()) +- html = HTML(u'
') +- self.assertEquals('
', (html | sanitizer).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | sanitizer).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | sanitizer).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | sanitizer).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | sanitizer).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | sanitizer).render()) + + def test_sanitize_remove_style_phishing(self): + sanitizer = StyleSanitizer() + # The position property is not allowed +- html = HTML(u'
') +- self.assertEquals('
', (html | sanitizer).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | sanitizer).render()) + # Normal margins get passed through +- html = HTML(u'
') +- self.assertEquals('
', ++ html = HTML('
') ++ self.assertEqual('
', + (html | sanitizer).render()) + # But not negative margins +- html = HTML(u'
') +- self.assertEquals('
', (html | sanitizer).render()) +- html = HTML(u'
') +- self.assertEquals('
', (html | sanitizer).render()) +- html = HTML(u'
') +- self.assertEquals('
', (html | sanitizer).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | sanitizer).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | sanitizer).render()) ++ html = HTML('
') ++ self.assertEqual('
', (html | sanitizer).render()) + + def test_sanitize_remove_src_javascript(self): +- html = HTML(u'') +- self.assertEquals('', (html | HTMLSanitizer()).render()) ++ html = HTML('') ++ self.assertEqual('', (html | HTMLSanitizer()).render()) + # Case-insensitive protocol matching +- html = HTML(u'') +- self.assertEquals('', (html | HTMLSanitizer()).render()) ++ html = HTML('') ++ self.assertEqual('', (html | HTMLSanitizer()).render()) + # Grave accents (not parsed) +- src = u'' ++ src = '' + self.assert_parse_error_or_equal('', src) + # Protocol encoded using UTF-8 numeric entities +- html = HTML(u'') +- self.assertEquals('', (html | HTMLSanitizer()).render()) ++ self.assertEqual('', (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'') +- self.assertEquals('', (html | HTMLSanitizer()).render()) ++ self.assertEqual('', (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'') +- self.assertEquals('', (html | HTMLSanitizer()).render()) ++ self.assertEqual('', (html | HTMLSanitizer()).render()) + # Embedded tab character in protocol +- html = HTML(u'') +- self.assertEquals('', (html | HTMLSanitizer()).render()) ++ html = HTML('') ++ self.assertEqual('', (html | HTMLSanitizer()).render()) + # Embedded tab character in protocol, but encoded this time +- html = HTML(u'') +- self.assertEquals('', (html | HTMLSanitizer()).render()) ++ html = HTML('') ++ self.assertEqual('', (html | HTMLSanitizer()).render()) + + def test_sanitize_expression(self): +- html = HTML(ur'
XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML(r'
XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + + def test_capital_expression(self): +- html = HTML(ur'
XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML(r'
XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + + def test_sanitize_url_with_javascript(self): +- html = HTML(u'
' +- u'XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML('
' ++ 'XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + + def test_sanitize_capital_url_with_javascript(self): +- html = HTML(u'
' +- u'XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML('
' ++ 'XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + + def test_sanitize_unicode_escapes(self): +- html = HTML(ur'
' +- ur'XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML(r'
' ++ r'XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + + def test_sanitize_backslash_without_hex(self): +- html = HTML(ur'
XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) +- html = HTML(ur'
XSS
') ++ html = HTML(r'
XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) ++ html = HTML(r'
XSS
') + self.assertEqual(r'
' + 'XSS
', +- unicode(html | StyleSanitizer())) ++ str(html | StyleSanitizer())) + + def test_sanitize_unsafe_props(self): +- html = HTML(u'
XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML('
XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + +- html = HTML(u'
XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML('
XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + +- html = HTML(u'
' +- u'XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML('
' ++ 'XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + +- html = HTML(u"""
XSS
""") +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML("""
XSS
""") ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + +- html = HTML(u"""
XSS
""") +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML("""
XSS
""") ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + + def test_sanitize_negative_margin(self): +- html = HTML(u'
XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) +- html = HTML(u'
XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML('
XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) ++ html = HTML('
XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + + def test_sanitize_css_hack(self): +- html = HTML(u'
XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML('
XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + +- html = HTML(u'
XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML('
XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + + def test_sanitize_property_name(self): +- html = HTML(u'
prop
') ++ html = HTML('
prop
') + self.assertEqual('
prop
', +- unicode(html | StyleSanitizer())) ++ str(html | StyleSanitizer())) + + def test_sanitize_unicode_expression(self): + # Fullwidth small letters +- html = HTML(u'
' +- u'XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML('
' ++ 'XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + # Fullwidth capital letters +- html = HTML(u'
' +- u'XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML('
' ++ 'XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + # IPA extensions +- html = HTML(u'
' +- u'XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML('
' ++ 'XSS
') ++ self.assertEqual('
XSS
', str(html | StyleSanitizer())) + + def test_sanitize_unicode_url(self): + # IPA extensions +- html = HTML(u'
' +- u'XSS
') +- self.assertEqual('
XSS
', unicode(html | StyleSanitizer())) ++ html = HTML('
' ++ 'XSS
') ++ self.assertEqual('
XSS
', 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( + 'x', 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 = ['' % (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 = '' + 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): + 3 + 4 + 5 +- """, tmpl.generate(items=range(1, 6)).render(encoding=None)) ++ """, tmpl.generate(items=list(range(1, 6))).render(encoding=None)) + + def test_as_element(self): + """ +@@ -443,7 +443,7 @@ class ForDirectiveTestCase(unittest.TestCase): + 3 + 4 + 5 +- """, tmpl.generate(items=range(1, 6)).render(encoding=None)) ++ """, 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): + + """, 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): + Foo + """, 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): + Foo + """, 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): +
""", tmpl.generate(foo={'bar': 42}).render(encoding=None)) + + def test_unicode_expr(self): +- tmpl = MarkupTemplate(u"""
++ tmpl = MarkupTemplate("""
+ + $weeks + +
""") +- self.assertEqual(u"""
++ self.assertEqual("""
+ + 一二三四五六日 + +--- 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(' has no member named "nil"', str(e)) + exc_type, exc_value, exc_traceback = sys.exc_info() + search_string = "" +@@ -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(' has no member named "nil"', str(e)) + exc_type, exc_value, exc_traceback = sys.exc_info() + search_string = '''''' +@@ -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'
\xf6
'.encode('iso-8859-1')) ++ f.write('
\xf6
'.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'
\xf6
'.encode('iso-8859-1')) ++ f.write('
\xf6
'.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 = '

' + 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): +

""", str(tmpl.generate())) + + def test_latin1_encoded_with_xmldecl(self): +- tmpl = MarkupTemplate(u""" ++ tmpl = MarkupTemplate(""" +
+ \xf6 +
""".encode('iso-8859-1'), encoding='iso-8859-1') +- self.assertEqual(u"""\n
++ self.assertEqual("""\n
+ \xf6 +-
""", unicode(tmpl.generate())) ++
""", str(tmpl.generate())) + + def test_latin1_encoded_explicit_encoding(self): +- tmpl = MarkupTemplate(u"""
++ tmpl = MarkupTemplate("""
+ \xf6 +
""".encode('iso-8859-1'), encoding='iso-8859-1') +- self.assertEqual(u"""
++ self.assertEqual("""
+ \xf6 +-
""", unicode(tmpl.generate())) ++
""", 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'Foo'}, template=tmpl) ++ output = plugin.render({'snippet': 'Foo'}, template=tmpl) + self.assertEqual("""
+ 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('
  • Über uns
  • ') +- self.assertEqual(u'
  • Über uns
  • '.encode('utf-8'), xml.render(encoding='utf-8')) ++ self.assertEqual('
  • Über uns
  • '.encode('utf-8'), xml.render(encoding='utf-8')) + + def test_render_unicode(self): + xml = XML('
  • Über uns
  • ') +- self.assertEqual(u'
  • Über uns
  • ', xml.render()) +- self.assertEqual(u'
  • Über uns
  • ', xml.render(encoding=None)) ++ self.assertEqual('
  • Über uns
  • ', xml.render()) ++ self.assertEqual('
  • Über uns
  • ', xml.render(encoding=None)) + + def test_render_ascii(self): + xml = XML('
  • Über uns
  • ') +- self.assertEqual(u'
  • Über uns
  • '.encode('ascii'), xml.render(encoding='ascii')) ++ self.assertEqual('
  • Über uns
  • '.encode('ascii'), xml.render(encoding='ascii')) + + def test_render_output_stream_utf8(self): + xml = XML('
  • Über uns
  • ') + strio = BytesIO() + self.assertEqual(None, xml.render(encoding='utf-8', out=strio)) +- self.assertEqual(u'
  • Über uns
  • '.encode('utf-8'), strio.getvalue()) ++ self.assertEqual('
  • Über uns
  • '.encode('utf-8'), strio.getvalue()) + + def test_render_output_stream_unicode(self): + xml = XML('
  • Über uns
  • ') + strio = StringIO() + self.assertEqual(None, xml.render(encoding=None, out=strio)) +- self.assertEqual(u'
  • Über uns
  • ', strio.getvalue()) ++ self.assertEqual('
  • Über uns
  • ', strio.getvalue()) + + def test_pickle(self): + xml = XML('
  • Foo
  • ') +@@ -54,122 +54,122 @@ class StreamTestCase(unittest.TestCase): + pickle.dump(xml, buf, 2) + buf.seek(0) + xml = pickle.load(buf) +- self.assertEquals('
  • Foo
  • ', xml.render(encoding=None)) ++ self.assertEqual('
  • Foo
  • ', 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("" % u'D\u00f6ner', repr(markup)) ++ self.assertEqual("" % 'D\u00f6ner', repr(markup)) + + def test_repr(self): + markup = Markup('foo') +- self.assertEquals("", repr(markup)) ++ self.assertEqual("", repr(markup)) + + def test_escape(self): + markup = escape('"&"') + assert type(markup) is Markup +- self.assertEquals('<b>"&"</b>', markup) ++ self.assertEqual('<b>"&"</b>', markup) + + def test_escape_noquotes(self): + markup = escape('"&"', quotes=False) + assert type(markup) is Markup +- self.assertEquals('<b>"&"</b>', markup) ++ self.assertEqual('<b>"&"</b>', markup) + + def test_unescape_markup(self): + string = '"&"' + 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('foo') + '
    ' + assert type(markup) is Markup +- self.assertEquals('foo<br/>', markup) ++ self.assertEqual('foo<br/>', markup) + + def test_add_markup(self): + markup = Markup('foo') + Markup('
    ') + assert type(markup) is Markup +- self.assertEquals('foo
    ', markup) ++ self.assertEqual('foo
    ', markup) + + def test_add_reverse(self): + markup = '
    ' + Markup('bar') + assert type(markup) is Markup +- self.assertEquals('<br/>bar', markup) ++ self.assertEqual('<br/>bar', markup) + + def test_mod(self): + markup = Markup('%s') % '&' + assert type(markup) is Markup +- self.assertEquals('&', markup) ++ self.assertEqual('&', markup) + + def test_mod_multi(self): + markup = Markup('%s %s') % ('&', 'boo') + assert type(markup) is Markup +- self.assertEquals('& boo', markup) ++ self.assertEqual('& boo', markup) + + def test_mod_mapping(self): + markup = Markup('%(foo)s') % {'foo': '&'} + assert type(markup) is Markup +- self.assertEquals('&', markup) ++ self.assertEqual('&', markup) + + def test_mod_noescape(self): + markup = Markup('%(amp)s') % {'amp': Markup('&')} + assert type(markup) is Markup +- self.assertEquals('&', markup) ++ self.assertEqual('&', markup) + + def test_mul(self): + markup = Markup('foo') * 2 + assert type(markup) is Markup +- self.assertEquals('foofoo', markup) ++ self.assertEqual('foofoo', markup) + + def test_mul_reverse(self): + markup = 2 * Markup('foo') + assert type(markup) is Markup +- self.assertEquals('foofoo', markup) ++ self.assertEqual('foofoo', markup) + + def test_join(self): + markup = Markup('
    ').join(['foo', '', Markup('')]) + assert type(markup) is Markup +- self.assertEquals('foo
    <bar />
    ', markup) ++ self.assertEqual('foo
    <bar />
    ', markup) + + def test_join_over_iter(self): + items = ['foo', '', Markup('')] + markup = Markup('
    ').join(i for i in items) +- self.assertEquals('foo
    <bar />
    ', markup) ++ self.assertEqual('foo
    <bar />
    ', markup) + + def test_stripentities_all(self): + markup = Markup('& j').stripentities() + assert type(markup) is Markup +- self.assertEquals('& j', markup) ++ self.assertEqual('& j', markup) + + def test_stripentities_keepxml(self): + markup = Markup('& j').stripentities(keepxmlentities=True) + assert type(markup) is Markup +- self.assertEquals('& j', markup) ++ self.assertEqual('& j', markup) + + def test_striptags_empty(self): + markup = Markup('
    ').striptags() + assert type(markup) is Markup +- self.assertEquals('', markup) ++ self.assertEqual('', markup) + + def test_striptags_mid(self): + markup = Markup('fo
    o
    ').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("", repr(pickle.load(buf))) ++ self.assertEqual("", 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''' + self.assertEqual(('class', 'bar'), attrib[2]) + + def test_unicode_input(self): +- text = u'
    \u2013
    ' ++ text = '
    \u2013
    ' + 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'
    \xf6
    '.encode('iso-8859-1') ++ text = '
    \xf6
    '.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""" ++ text = """ +
    \xf6
    + """.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 = """''' + 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 = ' ' + 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 = '

    ' + 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''' + class HTMLParserTestCase(unittest.TestCase): + + def test_text_node_pos_single_line(self): +- text = u'foo bar' ++ text = 'foo bar' + 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'''foo ++ text = '''foo + bar''' + events = list(HTMLParser(StringIO(text))) + kind, data, pos = events[1] +@@ -133,44 +133,44 @@ bar''' + self.assertEqual((None, 1, 6), pos) + + def test_input_encoding_text(self): +- text = u'

    \xf6
    '.encode('iso-8859-1') ++ text = '
    \xf6
    '.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'
    '.encode('iso-8859-1') ++ text = '
    '.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'
    \u2013
    ' ++ text = '
    \u2013
    ' + 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'

    ' ++ text = '

    ' + 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'

     

    ' ++ text = '

     

    ' + 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'' ++ text = '' + events = list(HTMLParser(StringIO(text))) + kind, (target, data), pos = events[0] + self.assertEqual(Stream.PI, kind) +@@ -178,7 +178,7 @@ bar''' + self.assertEqual('echo "Foobar"', data) + + def test_processing_instruction_no_data_1(self): +- text = u'' ++ text = '' + events = list(HTMLParser(StringIO(text))) + kind, (target, data), pos = events[0] + self.assertEqual(Stream.PI, kind) +@@ -186,7 +186,7 @@ bar''' + self.assertEqual('', data) + + def test_processing_instruction_no_data_2(self): +- text = u'...' ++ text = '...' + events = list(HTMLParser(StringIO(text))) + kind, (target, data), pos = events[0] + self.assertEqual(Stream.PI, kind) +@@ -224,7 +224,7 @@ bar''' + self.assertEqual(1, standalone) + + def test_processing_instruction_trailing_qmark(self): +- text = u'' ++ text = '' + events = list(HTMLParser(StringIO(text))) + kind, (target, data), pos = events[0] + self.assertEqual(Stream.PI, kind) +@@ -232,7 +232,7 @@ bar''' + self.assertEqual('echo "Foobar" ?', data) + + def test_out_of_order_tags1(self): +- text = u'Foobar' ++ text = 'Foobar' + events = list(HTMLParser(StringIO(text))) + self.assertEqual(5, len(events)) + self.assertEqual((Stream.START, ('span', ())), events[0][:2]) +@@ -242,7 +242,7 @@ bar''' + self.assertEqual((Stream.END, 'span'), events[4][:2]) + + def test_out_of_order_tags2(self): +- text = u'Foobar'.encode('utf-8') ++ text = 'Foobar'.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''' + self.assertEqual((Stream.END, 'span'), events[6][:2]) + + def test_out_of_order_tags3(self): +- text = u'Foobar'.encode('utf-8') ++ text = 'Foobar'.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''' + self.assertEqual((Stream.END, 'span'), events[4][:2]) + + def test_hex_charref(self): +- text = u''' ++ text = ''' + events = list(HTMLParser(StringIO(text))) + self.assertEqual(3, len(events)) + self.assertEqual((Stream.START, ('span', ())), events[0][:2]) +@@ -273,7 +273,7 @@ bar''' + 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): +
    """, output) + + def test_html5_doctype(self): +- stream = HTML(u'') ++ stream = HTML('') + output = stream.render(XHTMLSerializer, doctype=DocType.HTML5, + encoding=None) + self.assertEqual('\n', 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'…', loc), ++ (Stream.TEXT, '…', loc), + (Stream.END, QName('foo'), loc), + (Stream.START, (QName('bar'), Attrs()), loc), + (Stream.TEXT, Markup('…'), loc), +@@ -444,7 +444,7 @@ class HTMLSerializerTestCase(unittest.TestCase): + """, output) + + def test_html5_doctype(self): +- stream = HTML(u'') ++ stream = HTML('') + output = stream.render(HTMLSerializer, doctype=DocType.HTML5, + encoding=None) + self.assertEqual('\n', 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 < 2') + u'1 < 2' + >>> stripentities('more …') +- u'more \u2026' ++ u'more \\u2026' + >>> stripentities('…') +- u'\u2026' ++ u'\\u2026' + >>> stripentities('…') +- u'\u2026' ++ u'\\u2026' + + If the `keepxmlentities` parameter is provided and is a truth value, the + core XML entities (&, ', >, < and ") are left intact. + + >>> stripentities('1 < 2 …', keepxmlentities=True) +- u'1 < 2 \u2026' ++ u'1 < 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 '&%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 + -- cgit v1.2.3