git: 7730e6639858 - main - textproc/py-zope.tal: Fix build with setuptools 58.0.0+

From: Po-Chuan Hsieh <sunpoet_at_FreeBSD.org>
Date: Fri, 25 Mar 2022 13:51:10 UTC
The branch main has been updated by sunpoet:

URL: https://cgit.FreeBSD.org/ports/commit/?id=7730e66398589a02fe3fd201f153a816027ef6f4

commit 7730e66398589a02fe3fd201f153a816027ef6f4
Author:     Po-Chuan Hsieh <sunpoet@FreeBSD.org>
AuthorDate: 2022-03-25 13:34:59 +0000
Commit:     Po-Chuan Hsieh <sunpoet@FreeBSD.org>
CommitDate: 2022-03-25 13:38:23 +0000

    textproc/py-zope.tal: Fix build with setuptools 58.0.0+
    
    With hat:       python
---
 textproc/py-zope.tal/files/patch-2to3 | 1119 +++++++++++++++++++++++++++++++++
 1 file changed, 1119 insertions(+)

diff --git a/textproc/py-zope.tal/files/patch-2to3 b/textproc/py-zope.tal/files/patch-2to3
new file mode 100644
index 000000000000..406cba687986
--- /dev/null
+++ b/textproc/py-zope.tal/files/patch-2to3
@@ -0,0 +1,1119 @@
+--- src/zope/tal/driver.py.orig	2012-02-14 07:21:28 UTC
++++ src/zope/tal/driver.py
+@@ -43,7 +43,7 @@ import sys
+ import getopt
+ 
+ if __name__ == "__main__":
+-    import setpath                      # Local hack to tweak sys.path etc.
++    from . import setpath                      # Local hack to tweak sys.path etc.
+ 
+ # Import local classes
+ import zope.tal.taldefs
+@@ -104,9 +104,9 @@ ENGINES = {'test23.html': TestEngine,
+            }
+ 
+ def usage(code, msg=''):
+-    print >> sys.stderr, __doc__
++    print(__doc__, file=sys.stderr)
+     if msg:
+-        print >> sys.stderr, msg
++        print(msg, file=sys.stderr)
+     sys.exit(code)
+ 
+ def main():
+@@ -120,7 +120,7 @@ def main():
+     try:
+         opts, args = getopt.getopt(sys.argv[1:], "hHxlmstia",
+                                    ['help', 'html', 'xml'])
+-    except getopt.error, msg:
++    except getopt.error as msg:
+         usage(2, msg)
+     for opt, arg in opts:
+         if opt in ('-h', '--help'):
+--- src/zope/tal/dummyengine.py.orig	2012-02-14 07:21:28 UTC
++++ src/zope/tal/dummyengine.py
+@@ -100,12 +100,12 @@ class DummyEngine(object):
+         if type == "not":
+             return not self.evaluate(expr)
+         if type == "exists":
+-            return self.locals.has_key(expr) or self.globals.has_key(expr)
++            return expr in self.locals or expr in self.globals
+         if type == "python":
+             try:
+                 return eval(expr, self.globals, self.locals)
+             except:
+-                raise TALExpressionError("evaluation error in %s" % `expr`)
++                raise TALExpressionError("evaluation error in %s" % repr(expr))
+         if type == "position":
+             # Insert the current source file name, line number,
+             # and column offset.
+@@ -114,17 +114,17 @@ class DummyEngine(object):
+             else:
+                 lineno, offset = None, None
+             return '%s (%s,%s)' % (self.source_file, lineno, offset)
+-        raise TALExpressionError("unrecognized expression: " + `expression`)
++        raise TALExpressionError("unrecognized expression: " + repr(expression))
+ 
+     # implementation; can be overridden
+     def evaluatePathOrVar(self, expr):
+         expr = expr.strip()
+-        if self.locals.has_key(expr):
++        if expr in self.locals:
+             return self.locals[expr]
+-        elif self.globals.has_key(expr):
++        elif expr in self.globals:
+             return self.globals[expr]
+         else:
+-            raise TALExpressionError("unknown variable: %s" % `expr`)
++            raise TALExpressionError("unknown variable: %s" % repr(expr))
+ 
+     def evaluateValue(self, expr):
+         return self.evaluate(expr)
+@@ -134,7 +134,7 @@ class DummyEngine(object):
+ 
+     def evaluateText(self, expr):
+         text = self.evaluate(expr)
+-        if isinstance(text, (str, unicode, Message)):
++        if isinstance(text, (str, Message)):
+             return text
+         if text is not None and text is not Default:
+             text = str(text)
+@@ -159,7 +159,7 @@ class DummyEngine(object):
+             macro = self.macros[localName]
+         else:
+             # External macro
+-            import driver
++            from . import driver
+             program, macros = driver.compilefile(file)
+             macro = macros.get(localName)
+             if not macro:
+@@ -208,7 +208,7 @@ class DummyEngine(object):
+             locals = self.locals.copy()
+ 
+         assert lang == 'text/server-python'
+-        import sys, StringIO
++        import sys, io
+ 
+         # Removing probable comments
+         if code.strip().startswith('<!--') and code.strip().endswith('-->'):
+@@ -216,15 +216,15 @@ class DummyEngine(object):
+ 
+         # Prepare code.
+         lines = code.split('\n')
+-        lines = filter(lambda l: l.strip() != '', lines)
++        lines = [l for l in lines if l.strip() != '']
+         code = '\n'.join(lines)
+         # This saves us from all indentation issues :)
+         if code.startswith(' ') or code.startswith('\t'):
+             code = 'if 1 == 1:\n' + code + '\n'
+         tmp = sys.stdout
+-        sys.stdout = StringIO.StringIO()
++        sys.stdout = io.StringIO()
+         try:
+-            exec code in globals, locals
++            exec(code, globals, locals)
+         finally:
+             result = sys.stdout
+             sys.stdout = tmp
+@@ -246,7 +246,7 @@ class Iterator(object):
+         self.engine = engine
+         self.nextIndex = 0
+ 
+-    def next(self):
++    def __next__(self):
+         i = self.nextIndex
+         try:
+             item = self.seq[i]
+@@ -264,7 +264,7 @@ class DummyTranslationDomain(object):
+     msgids = {} 
+ 
+     def appendMsgid(self, domain, data):
+-        if not self.msgids.has_key(domain):
++        if domain not in self.msgids:
+             self.msgids[domain] = []
+         self.msgids[domain].append(data)    
+     
+@@ -308,7 +308,7 @@ class DummyTranslationDomain(object):
+         self.appendMsgid(domain, (msgid, mapping))
+         
+         def repl(m):
+-            return unicode(mapping[m.group(m.lastindex).lower()])
++            return str(mapping[m.group(m.lastindex).lower()])
+         cre = re.compile(r'\$(?:([_A-Za-z][-\w]*)|\{([_A-Za-z][-\w]*)\})')
+         return cre.sub(repl, text)
+ 
+--- src/zope/tal/htmltalparser.py.orig	2012-02-14 09:53:32 UTC
++++ src/zope/tal/htmltalparser.py
+@@ -14,7 +14,7 @@
+ """Parse HTML and compile to TALInterpreter intermediate code.
+ """
+ 
+-from HTMLParser import HTMLParser, HTMLParseError
++from html.parser import HTMLParser, HTMLParseError
+ 
+ from zope.tal.taldefs import (ZOPE_METAL_NS, ZOPE_TAL_NS, ZOPE_I18N_NS,
+                               METALError, TALError, I18NError)
+@@ -118,7 +118,7 @@ class HTMLTALParser(HTMLParser):
+         f.close()
+         try:
+             self.parseString(data)
+-        except TALError, e:
++        except TALError as e:
+             e.setFile(file)
+             raise
+ 
+@@ -141,7 +141,7 @@ class HTMLTALParser(HTMLParser):
+              = self.process_ns(tag, attrs)
+         if tag in EMPTY_HTML_TAGS and "content" in taldict:
+             raise TALError(
+-                "empty HTML tags cannot use tal:content: %s" % `tag`,
++                "empty HTML tags cannot use tal:content: %s" % repr(tag),
+                 self.getpos())
+         # Support for inline Python code.
+         if tag == 'script':
+@@ -163,7 +163,7 @@ class HTMLTALParser(HTMLParser):
+         if "content" in taldict:
+             if tag in EMPTY_HTML_TAGS:
+                 raise TALError(
+-                    "empty HTML tags cannot use tal:content: %s" % `tag`,
++                    "empty HTML tags cannot use tal:content: %s" % repr(tag),
+                     self.getpos())
+             self.gen.emitStartElement(tag, attrlist, taldict, metaldict,
+                                       i18ndict, self.getpos())
+--- src/zope/tal/interfaces.py.orig	2012-02-14 07:21:28 UTC
++++ src/zope/tal/interfaces.py
+@@ -61,13 +61,14 @@ class ITALExpressionEngine(Interface):
+         using the 'is' operator in Python.
+         """
+ 
+-    def setPosition((lineno, offset)):
++    def setPosition(xxx_todo_changeme):
+         """Inform the engine of the current position in the source file.
+ 
+         This is used to allow the evaluation engine to report
+         execution errors so that site developers can more easily
+         locate the offending expression.
+         """
++        (lineno, offset) = xxx_todo_changeme
+ 
+     def setSourceFile(filename):
+         """Inform the engine of the name of the current source file.
+@@ -128,12 +129,13 @@ class ITALExpressionEngine(Interface):
+         No constraints are imposed on the return value.
+         """
+ 
+-    def createErrorInfo(exception, (lineno, offset)):
++    def createErrorInfo(exception, xxx_todo_changeme1):
+         """Returns an ITALExpressionErrorInfo object.
+ 
+         The returned object is used to provide information about the
+         error condition for the on-error handler.
+         """
++        (lineno, offset) = xxx_todo_changeme1
+ 
+     def setGlobal(name, value):
+         """Set a global variable.
+--- src/zope/tal/ndiff.py.orig	2012-02-14 07:21:28 UTC
++++ src/zope/tal/ndiff.py
+@@ -114,6 +114,7 @@ TRACE = 0
+ 
+ # define what "junk" means
+ import re
++from functools import reduce
+ 
+ def IS_LINE_JUNK(line, pat=re.compile(r"\s*#?\s*$").match):
+     return pat(line) is not None
+@@ -209,7 +210,7 @@ class SequenceMatcher(object):
+         b = self.b
+         self.b2j = b2j = {}
+         self.b2jhas = b2jhas = b2j.has_key
+-        for i in xrange(len(b)):
++        for i in range(len(b)):
+             elt = b[i]
+             if b2jhas(elt):
+                 b2j[elt].append(i)
+@@ -222,7 +223,7 @@ class SequenceMatcher(object):
+         # saved.
+         isjunk, junkdict = self.isjunk, {}
+         if isjunk:
+-            for elt in b2j.keys():
++            for elt in list(b2j.keys()):
+                 if isjunk(elt):
+                     junkdict[elt] = 1   # value irrelevant; it's a set
+                     del b2j[elt]
+@@ -281,7 +282,7 @@ class SequenceMatcher(object):
+         # junk-free match ending with a[i-1] and b[j]
+         j2len = {}
+         nothing = []
+-        for i in xrange(alo, ahi):
++        for i in range(alo, ahi):
+             # look at all instances of a[i] in b; note that because
+             # b2j has no junk keys, the loop is skipped if a[i] is junk
+             j2lenget = j2len.get
+@@ -314,8 +315,8 @@ class SequenceMatcher(object):
+             bestsize = bestsize + 1
+ 
+         if TRACE:
+-            print "get_matching_blocks", alo, ahi, blo, bhi
+-            print "    returns", besti, bestj, bestsize
++            print("get_matching_blocks", alo, ahi, blo, bhi)
++            print("    returns", besti, bestj, bestsize)
+         return besti, bestj, bestsize
+ 
+     def get_matching_blocks(self):
+@@ -326,7 +327,7 @@ class SequenceMatcher(object):
+         self.__helper(0, la, 0, lb, self.matching_blocks)
+         self.matching_blocks.append((la, lb, 0))
+         if TRACE:
+-            print '*** matching blocks', self.matching_blocks
++            print('*** matching blocks', self.matching_blocks)
+         return self.matching_blocks
+ 
+     # builds list of matching blocks covering a[alo:ahi] and
+@@ -417,8 +418,8 @@ class SequenceMatcher(object):
+ 
+ # meant for dumping lines
+ def dump(tag, x, lo, hi):
+-    for i in xrange(lo, hi):
+-        print tag, x[i],
++    for i in range(lo, hi):
++        print(tag, x[i], end=' ')
+ 
+ def plain_replace(a, alo, ahi, b, blo, bhi):
+     assert alo < ahi and blo < bhi
+@@ -438,7 +439,7 @@ def plain_replace(a, alo, ahi, b, blo, bhi):
+ 
+ def fancy_replace(a, alo, ahi, b, blo, bhi):
+     if TRACE:
+-        print '*** fancy_replace', alo, ahi, blo, bhi
++        print('*** fancy_replace', alo, ahi, blo, bhi)
+         dump('>', a, alo, ahi)
+         dump('<', b, blo, bhi)
+ 
+@@ -451,10 +452,10 @@ def fancy_replace(a, alo, ahi, b, blo, bhi):
+     # search for the pair that matches best without being identical
+     # (identical lines must be junk lines, & we don't want to synch up
+     # on junk -- unless we have to)
+-    for j in xrange(blo, bhi):
++    for j in range(blo, bhi):
+         bj = b[j]
+         cruncher.set_seq2(bj)
+-        for i in xrange(alo, ahi):
++        for i in range(alo, ahi):
+             ai = a[i]
+             if ai == bj:
+                 if eqi is None:
+@@ -486,7 +487,7 @@ def fancy_replace(a, alo, ahi, b, blo, bhi):
+     # a[best_i] very similar to b[best_j]; eqi is None iff they're not
+     # identical
+     if TRACE:
+-        print '*** best_ratio', best_ratio, best_i, best_j
++        print('*** best_ratio', best_ratio, best_i, best_j)
+         dump('>', a, best_i, best_i+1)
+         dump('<', b, best_j, best_j+1)
+ 
+@@ -512,11 +513,11 @@ def fancy_replace(a, alo, ahi, b, blo, bhi):
+                 atags = atags + ' ' * la
+                 btags = btags + ' ' * lb
+             else:
+-                raise ValueError('unknown tag ' + `tag`)
++                raise ValueError('unknown tag ' + repr(tag))
+         printq(aelt, belt, atags, btags)
+     else:
+         # the synch pair is identical
+-        print ' ', aelt,
++        print(' ', aelt, end=' ')
+ 
+     # pump out diffs from after the synch point
+     fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
+@@ -537,12 +538,12 @@ def printq(aline, bline, atags, btags):
+     common = min(count_leading(aline, "\t"),
+                  count_leading(bline, "\t"))
+     common = min(common, count_leading(atags[:common], " "))
+-    print "-", aline,
++    print("-", aline, end=' ')
+     if count_leading(atags, " ") < len(atags):
+-        print "?", "\t" * common + atags[common:]
+-    print "+", bline,
++        print("?", "\t" * common + atags[common:])
++    print("+", bline, end=' ')
+     if count_leading(btags, " ") < len(btags):
+-        print "?", "\t" * common + btags[common:]
++        print("?", "\t" * common + btags[common:])
+ 
+ def count_leading(line, ch):
+     i, n = 0, len(line)
+@@ -562,7 +563,7 @@ def fail(msg):
+ def fopen(fname):
+     try:
+         return open(fname, 'r')
+-    except IOError, detail:
++    except IOError as detail:
+         return fail("couldn't open " + fname + ": " + str(detail))
+ 
+ # open two files & spray the diff to stdout; return false iff a problem
+@@ -586,7 +587,7 @@ def fcompare(f1name, f2name):
+         elif tag == 'equal':
+             dump(' ', a, alo, ahi)
+         else:
+-            raise ValueError('unknown tag ' + `tag`)
++            raise ValueError('unknown tag ' + repr(tag))
+ 
+     return 1
+ 
+@@ -597,7 +598,7 @@ def main(args):
+     import getopt
+     try:
+         opts, args = getopt.getopt(args, "qr:")
+-    except getopt.error, detail:
++    except getopt.error as detail:
+         return fail(str(detail))
+     noisy = 1
+     qseen = rseen = 0
+@@ -621,8 +622,8 @@ def main(args):
+         return fail("need 2 filename args")
+     f1name, f2name = args
+     if noisy:
+-        print '-:', f1name
+-        print '+:', f2name
++        print('-:', f1name)
++        print('+:', f2name)
+     return fcompare(f1name, f2name)
+ 
+ def restore(which):
+@@ -631,7 +632,7 @@ def restore(which):
+     prefixes = ("  ", tag)
+     for line in sys.stdin.readlines():
+         if line[:2] in prefixes:
+-            print line[2:],
++            print(line[2:], end=' ')
+ 
+ if __name__ == '__main__':
+     import sys
+--- src/zope/tal/runtest.py.orig	2012-02-14 07:21:28 UTC
++++ src/zope/tal/runtest.py
+@@ -19,24 +19,24 @@ import os
+ import sys
+ import traceback
+ 
+-from cStringIO import StringIO
++from io import StringIO
+ 
+ if __name__ == "__main__":
+-    import setpath                      # Local hack to tweak sys.path etc.
++    from . import setpath                      # Local hack to tweak sys.path etc.
+ 
+ import zope.tal.driver
+ import zope.tal.tests.utils
+ 
+ def showdiff(a, b):
+-    import ndiff
++    from . import ndiff
+     cruncher = ndiff.SequenceMatcher(ndiff.IS_LINE_JUNK, a, b)
+     for tag, alo, ahi, blo, bhi in cruncher.get_opcodes():
+         if tag == "equal":
+             continue
+-        print nicerange(alo, ahi) + tag[0] + nicerange(blo, bhi)
++        print(nicerange(alo, ahi) + tag[0] + nicerange(blo, bhi))
+         ndiff.dump('<', a, alo, ahi)
+         if a and b:
+-            print '---'
++            print('---')
+         ndiff.dump('>', b, blo, bhi)
+ 
+ def nicerange(lo, hi):
+@@ -80,10 +80,10 @@ def main():
+         if arg.find("_sa") >= 0 and "-a" not in opts:
+             locopts.append("-a")
+         if not unittesting:
+-            print arg,
++            print(arg, end=' ')
+             sys.stdout.flush()
+         if zope.tal.tests.utils.skipxml and arg.endswith(".xml"):
+-            print "SKIPPED (XML parser not available)"
++            print("SKIPPED (XML parser not available)")
+             continue
+         save = sys.stdout, sys.argv
+         try:
+@@ -98,13 +98,13 @@ def main():
+         except:
+             errors = 1
+             if quiet:
+-                print sys.exc_type
++                print(sys.exc_info()[0])
+                 sys.stdout.flush()
+             else:
+                 if unittesting:
+-                    print
++                    print()
+                 else:
+-                    print "Failed:"
++                    print("Failed:")
+                     sys.stdout.flush()
+                 traceback.print_exc()
+             continue
+@@ -116,7 +116,7 @@ def main():
+             f = open(outfile)
+         except IOError:
+             expected = None
+-            print "(missing file %s)" % outfile,
++            print("(missing file %s)" % outfile, end=' ')
+         else:
+             expected = f.readlines()
+             f.close()
+@@ -127,12 +127,12 @@ def main():
+             actual = readlines(stdout)
+         if actual == expected:
+             if not unittesting:
+-                print "OK"
++                print("OK")
+         else:
+             if unittesting:
+-                print
++                print()
+             else:
+-                print "not OK"
++                print("not OK")
+             errors = 1
+             if not quiet and expected is not None:
+                 showdiff(expected, actual)
+--- src/zope/tal/talgenerator.py.orig	2012-02-14 07:21:28 UTC
++++ src/zope/tal/talgenerator.py
+@@ -69,7 +69,7 @@ class TALGenerator(object):
+         output = []
+         collect = []
+         cursor = 0
+-        for cursor in xrange(len(program)+1):
++        for cursor in range(len(program)+1):
+             try:
+                 item = program[cursor]
+             except IndexError:
+@@ -197,8 +197,8 @@ class TALGenerator(object):
+     def compileExpression(self, expr):
+         try:
+             return self.expressionCompiler.compile(expr)
+-        except self.CompilerError, err:
+-            raise TALError('%s in expression %s' % (err.args[0], `expr`),
++        except self.CompilerError as err:
++            raise TALError('%s in expression %s' % (err.args[0], repr(expr)),
+                            self.position)
+ 
+     def pushProgram(self):
+@@ -259,7 +259,7 @@ class TALGenerator(object):
+             m = re.match(
+                 r"(?s)\s*(?:(global|local)\s+)?(%s)\s+(.*)\Z" % NAME_RE, part)
+             if not m:
+-                raise TALError("invalid define syntax: " + `part`,
++                raise TALError("invalid define syntax: " + repr(part),
+                                self.position)
+             scope, name, expr = m.group(1, 2, 3)
+             scope = scope or "local"
+@@ -293,7 +293,7 @@ class TALGenerator(object):
+     def emitRepeat(self, arg):
+         m = re.match("(?s)\s*(%s)\s+(.*)\Z" % NAME_RE, arg)
+         if not m:
+-            raise TALError("invalid repeat syntax: " + `arg`,
++            raise TALError("invalid repeat syntax: " + repr(arg),
+                            self.position)
+         name, expr = m.group(1, 2)
+         cexpr = self.compileExpression(expr)
+@@ -346,11 +346,11 @@ class TALGenerator(object):
+     def emitDefineMacro(self, macroName):
+         program = self.popProgram()
+         macroName = macroName.strip()
+-        if self.macros.has_key(macroName):
+-            raise METALError("duplicate macro definition: %s" % `macroName`,
++        if macroName in self.macros:
++            raise METALError("duplicate macro definition: %s" % repr(macroName),
+                              self.position)
+         if not re.match('%s$' % NAME_RE, macroName):
+-            raise METALError("invalid macro name: %s" % `macroName`,
++            raise METALError("invalid macro name: %s" % repr(macroName),
+                              self.position)
+         self.macros[macroName] = program
+         self.inMacroDef = self.inMacroDef - 1
+@@ -374,18 +374,18 @@ class TALGenerator(object):
+         program = self.popProgram()
+         slotName = slotName.strip()
+         if not re.match('%s$' % NAME_RE, slotName):
+-            raise METALError("invalid slot name: %s" % `slotName`,
++            raise METALError("invalid slot name: %s" % repr(slotName),
+                              self.position)
+         self.emit("defineSlot", slotName, program)
+ 
+     def emitFillSlot(self, slotName):
+         program = self.popProgram()
+         slotName = slotName.strip()
+-        if self.slots.has_key(slotName):
+-            raise METALError("duplicate fill-slot name: %s" % `slotName`,
++        if slotName in self.slots:
++            raise METALError("duplicate fill-slot name: %s" % repr(slotName),
+                              self.position)
+         if not re.match('%s$' % NAME_RE, slotName):
+-            raise METALError("invalid slot name: %s" % `slotName`,
++            raise METALError("invalid slot name: %s" % repr(slotName),
+                              self.position)
+         self.slots[slotName] = program
+         self.inMacroUse = 1
+@@ -449,13 +449,13 @@ class TALGenerator(object):
+         newlist = []
+         for item in attrlist:
+             key = item[0]
+-            if repldict.has_key(key):
++            if key in repldict:
+                 expr, xlat, msgid = repldict[key]
+                 item = item[:2] + ("replace", expr, xlat, msgid)
+                 del repldict[key]
+             newlist.append(item)
+         # Add dynamic-only attributes
+-        for key, (expr, xlat, msgid) in repldict.items():
++        for key, (expr, xlat, msgid) in list(repldict.items()):
+             newlist.append((key, None, "insert", expr, xlat, msgid))
+         return newlist
+ 
+@@ -482,25 +482,25 @@ class TALGenerator(object):
+             taldict["content"] = taldict.pop("replace")
+             replaced = True
+ 
+-        for key, value in taldict.items():
++        for key, value in list(taldict.items()):
+             if key not in taldefs.KNOWN_TAL_ATTRIBUTES:
+-                raise TALError("bad TAL attribute: " + `key`, position)
++                raise TALError("bad TAL attribute: " + repr(key), position)
+             if not (value or key == 'omit-tag'):
+                 raise TALError("missing value for TAL attribute: " +
+-                               `key`, position)
+-        for key, value in metaldict.items():
++                               repr(key), position)
++        for key, value in list(metaldict.items()):
+             if key not in taldefs.KNOWN_METAL_ATTRIBUTES:
+-                raise METALError("bad METAL attribute: " + `key`,
++                raise METALError("bad METAL attribute: " + repr(key),
+                                  position)
+             if not value:
+                 raise TALError("missing value for METAL attribute: " +
+-                               `key`, position)
+-        for key, value in i18ndict.items():
++                               repr(key), position)
++        for key, value in list(i18ndict.items()):
+             if key not in taldefs.KNOWN_I18N_ATTRIBUTES:
+-                raise I18NError("bad i18n attribute: " + `key`, position)
++                raise I18NError("bad i18n attribute: " + repr(key), position)
+             if not value and key in ("attributes", "data", "id"):
+                 raise I18NError("missing value for i18n attribute: " +
+-                                `key`, position)
++                                repr(key), position)
+ 
+         todo = {}
+         defineMacro = metaldict.get("define-macro")
+@@ -681,7 +681,7 @@ class TALGenerator(object):
+                 i18nattrs = {}
+             # Convert repldict's name-->expr mapping to a
+             # name-->(compiled_expr, translate) mapping
+-            for key, value in repldict.items():
++            for key, value in list(repldict.items()):
+                 if i18nattrs.get(key, None):
+                     raise I18NError(
+                         "attribute [%s] cannot both be part of tal:attributes"
+--- src/zope/tal/talgettext.py.orig	2012-02-14 07:21:28 UTC
++++ src/zope/tal/talgettext.py
+@@ -62,16 +62,16 @@ NLSTR = '"\n"'
+ 
+ def usage(code, msg=''):
+     # Python 2.1 required
+-    print >> sys.stderr, __doc__
++    print(__doc__, file=sys.stderr)
+     if msg:
+-        print >> sys.stderr, msg
++        print(msg, file=sys.stderr)
+     sys.exit(code)
+ 
+ 
+ class POTALInterpreter(TALInterpreter):
+     def translate(self, msgid, default=None, i18ndict=None, obj=None):
+         if default is None:
+-            default = getattr(msgid, 'default', unicode(msgid))
++            default = getattr(msgid, 'default', str(msgid))
+         # If no i18n dict exists yet, create one.
+         if i18ndict is None:
+             i18ndict = {}
+@@ -126,15 +126,15 @@ class POEngine(DummyEngine):
+         if msgid not in domain:
+             domain[msgid] = []
+         else:
+-            msgids = domain.keys()
++            msgids = list(domain.keys())
+             idx = msgids.index(msgid)
+             existing_msgid = msgids[idx]
+             if msgid.default != existing_msgid.default:
+                 references = '\n'.join([location[0]+':'+str(location[1]) for location in domain[msgid]])
+-                print >> sys.stderr, (u"Warning: msgid '%s' in %s already exists " \
++                print(("Warning: msgid '%s' in %s already exists " \
+                          "with a different default (bad: %s, should be: %s)\n" \
+                          "The references for the existent value are:\n%s\n" % \
+-                         (msgid, self.file+':'+str(position), msgid.default.encode('utf-8'), existing_msgid.default.encode('utf-8'), references)).encode('utf-8')
++                         (msgid, self.file+':'+str(position), msgid.default.encode('utf-8'), existing_msgid.default.encode('utf-8'), references)).encode('utf-8'), file=sys.stderr)
+         domain[msgid].append((self.file, position))
+         return 'x'
+ 
+@@ -170,8 +170,8 @@ class UpdatePOEngine(POEngine):
+ 
+         try:
+             lines = open(self._filename).readlines()
+-        except IOError, msg:
+-            print >> sys.stderr, msg
++        except IOError as msg:
++            print(msg, file=sys.stderr)
+             sys.exit(1)
+ 
+         section = None
+@@ -213,9 +213,9 @@ class UpdatePOEngine(POEngine):
+             elif section == STR:
+                 msgstr += '%s\n' % l
+             else:
+-                print >> sys.stderr, 'Syntax error on %s:%d' % (infile, lno), \
+-                      'before:'
+-                print >> sys.stderr, l
++                print('Syntax error on %s:%d' % (infile, lno), \
++                      'before:', file=sys.stderr)
++                print(l, file=sys.stderr)
+                 sys.exit(1)
+         # Add last entry
+         if section == STR:
+@@ -243,7 +243,7 @@ def main():
+             sys.argv[1:],
+             'ho:u:',
+             ['help', 'output=', 'update='])
+-    except getopt.error, msg:
++    except getopt.error as msg:
+         usage(1, msg)
+ 
+     outfile = None
+@@ -261,7 +261,7 @@ def main():
+             engine = UpdatePOEngine(filename=arg)
+ 
+     if not args:
+-        print 'nothing to do'
++        print('nothing to do')
+         return
+ 
+     # We don't care about the rendered output of the .pt file
+@@ -284,7 +284,7 @@ def main():
+             POTALInterpreter(program, macros, engine, stream=Devnull(),
+                              metal=False)()
+         except: # Hee hee, I love bare excepts!
+-            print 'There was an error processing', filename
++            print('There was an error processing', filename)
+             traceback.print_exc()
+ 
+     # Now output the keys in the engine.  Write them to a file if --output or
+@@ -295,7 +295,7 @@ def main():
+         outfile = file(outfile, update_mode and "a" or "w")
+ 
+     catalog = {}
+-    for domain in engine.catalog.keys():
++    for domain in list(engine.catalog.keys()):
+         catalog.update(engine.catalog[domain])
+ 
+     messages = catalog.copy()
+@@ -304,10 +304,10 @@ def main():
+     except AttributeError:
+         pass
+     if '' not in messages:
+-        print >> outfile, pot_header % {'time': time.ctime(),
+-                                        'version': __version__}
++        print(pot_header % {'time': time.ctime(),
++                                        'version': __version__}, file=outfile)
+ 
+-    msgids = catalog.keys()
++    msgids = list(catalog.keys())
+     # TODO: You should not sort by msgid, but by filename and position. (SR)
+     msgids.sort()
+     for msgid in msgids:
+--- src/zope/tal/talinterpreter.py.orig	2012-02-14 07:21:28 UTC
++++ src/zope/tal/talinterpreter.py
+@@ -29,7 +29,7 @@ from zope.tal.translationcontext import TranslationCon
+ # Avoid constructing this tuple over and over
+ I18nMessageTypes = (Message,)
+ 
+-TypesToTranslate = I18nMessageTypes + (str, unicode)
++TypesToTranslate = I18nMessageTypes + (str, str)
+ 
+ BOOLEAN_HTML_ATTRS = frozenset([
+     # List of Boolean attributes in HTML that should be rendered in
+@@ -251,7 +251,7 @@ class TALInterpreter(object):
+     def pushMacro(self, macroName, slots, definingName, extending):
+         if len(self.macroStack) >= self.stackLimit:
+             raise METALError("macro nesting limit (%d) exceeded "
+-                             "by %s" % (self.stackLimit, `macroName`))
++                             "by %s" % (self.stackLimit, repr(macroName)))
+         self.macroStack.append(
+             MacroStackItem((macroName, slots, definingName, extending,
+                             True, self.i18nContext)))
+@@ -371,12 +371,13 @@ class TALInterpreter(object):
+         self.do_startTag(stuff, self.endsep, self.endlen)
+     bytecode_handlers["startEndTag"] = do_startEndTag
+ 
+-    def do_startTag(self, (name, attrList),
++    def do_startTag(self, xxx_todo_changeme,
+                     end=">", endlen=1, _len=len):
+         # The bytecode generator does not cause calls to this method
+         # for start tags with no attributes; those are optimized down
+         # to rawtext events.  Hence, there is no special "fast path"
+         # for that case.
++        (name, attrList) = xxx_todo_changeme
+         self._currentTag = name
+         L = ["<", name]
+         append = L.append
+@@ -507,8 +508,9 @@ class TALInterpreter(object):
+         self.restoreOutputState(state)
+         self.interpret(program)
+ 
+-    def do_optTag(self, (name, cexpr, tag_ns, isend, start, program),
++    def do_optTag(self, xxx_todo_changeme1,
+                   omit=0):
++        (name, cexpr, tag_ns, isend, start, program) = xxx_todo_changeme1
+         if tag_ns and not self.showtal:
+             return self.no_tag(start, program)
+ 
+@@ -528,7 +530,8 @@ class TALInterpreter(object):
+             self.do_optTag(stuff)
+     bytecode_handlers["optTag"] = do_optTag
+ 
+-    def do_rawtextBeginScope(self, (s, col, position, closeprev, dict)):
++    def do_rawtextBeginScope(self, xxx_todo_changeme2):
++        (s, col, position, closeprev, dict) = xxx_todo_changeme2
+         self._stream_write(s)
+         self.col = col
+         self.do_setPosition(position)
+@@ -540,7 +543,8 @@ class TALInterpreter(object):
+             self.engine.beginScope()
+             self.scopeLevel = self.scopeLevel + 1
+ 
+-    def do_rawtextBeginScope_tal(self, (s, col, position, closeprev, dict)):
++    def do_rawtextBeginScope_tal(self, xxx_todo_changeme3):
++        (s, col, position, closeprev, dict) = xxx_todo_changeme3
+         self._stream_write(s)
+         self.col = col
+         engine = self.engine
+@@ -574,11 +578,13 @@ class TALInterpreter(object):
+     def do_setLocal(self, notused):
+         pass
+ 
+-    def do_setLocal_tal(self, (name, expr)):
++    def do_setLocal_tal(self, xxx_todo_changeme4):
++        (name, expr) = xxx_todo_changeme4
+         self.engine.setLocal(name, self.engine.evaluateValue(expr))
+     bytecode_handlers["setLocal"] = do_setLocal
+ 
+-    def do_setGlobal_tal(self, (name, expr)):
++    def do_setGlobal_tal(self, xxx_todo_changeme5):
++        (name, expr) = xxx_todo_changeme5
+         self.engine.setGlobal(name, self.engine.evaluateValue(expr))
+     bytecode_handlers["setGlobal"] = do_setLocal
+ 
+@@ -670,7 +676,7 @@ class TALInterpreter(object):
+                 value = self.translate(value)
+ 
+             if not structure:
+-                value = cgi.escape(unicode(value))
++                value = cgi.escape(str(value))
+ 
+         # Either the i18n:name tag is nested inside an i18n:translate in which
+         # case the last item on the stack has the i18n dictionary and string
+@@ -733,7 +739,8 @@ class TALInterpreter(object):
+     bytecode_handlers["insertStructure"] = do_insertStructure
+     bytecode_handlers["insertI18nStructure"] = do_insertStructure
+ 
+-    def do_insertStructure_tal(self, (expr, repldict, block)):
++    def do_insertStructure_tal(self, xxx_todo_changeme6):
++        (expr, repldict, block) = xxx_todo_changeme6
+         structure = self.engine.evaluateStructure(expr)
+         if structure is None:
+             return
+@@ -743,7 +750,7 @@ class TALInterpreter(object):
+         if isinstance(structure, I18nMessageTypes):
+             text = self.translate(structure)
+         else:
+-            text = unicode(structure)
++            text = str(structure)
+         if not (repldict or self.strictinsert):
+             # Take a shortcut, no error checking
+             self.stream_write(text)
+@@ -753,15 +760,16 @@ class TALInterpreter(object):
+         else:
+             self.insertXMLStructure(text, repldict)
+ 
+-    def do_insertI18nStructure_tal(self, (expr, repldict, block)):
++    def do_insertI18nStructure_tal(self, xxx_todo_changeme7):
+         # TODO: Code duplication is BAD, we need to fix it later
++        (expr, repldict, block) = xxx_todo_changeme7
+         structure = self.engine.evaluateStructure(expr)
+         if structure is not None:
+             if structure is self.Default:
+                 self.interpret(block)
+             else:
+                 if not isinstance(structure, TypesToTranslate):
+-                    structure = unicode(structure)
++                    structure = str(structure)
+                 text = self.translate(structure)
+                 if not (repldict or self.strictinsert):
+                     # Take a shortcut, no error checking
+@@ -807,19 +815,21 @@ class TALInterpreter(object):
+         self._stream_write(output)
+     bytecode_handlers["evaluateCode"] = do_evaluateCode
+ 
+-    def do_loop(self, (name, expr, block)):
++    def do_loop(self, xxx_todo_changeme8):
++        (name, expr, block) = xxx_todo_changeme8
+         self.interpret(block)
+ 
+-    def do_loop_tal(self, (name, expr, block)):
++    def do_loop_tal(self, xxx_todo_changeme9):
++        (name, expr, block) = xxx_todo_changeme9
+         iterator = self.engine.setRepeat(name, expr)
+-        while iterator.next():
++        while next(iterator):
+             self.interpret(block)
+     bytecode_handlers["loop"] = do_loop
+ 
+     def translate(self, msgid, default=None, i18ndict=None,
+                   obj=None, domain=None):
+         if default is None:
+-            default = getattr(msgid, 'default', unicode(msgid))
++            default = getattr(msgid, 'default', str(msgid))
+         if i18ndict is None:
+             i18ndict = {}
+         if domain is None:
+@@ -832,30 +842,35 @@ class TALInterpreter(object):
+         return self.engine.translate(msgid, self.i18nContext.domain,
+                                      i18ndict, default=default)
+ 
+-    def do_rawtextColumn(self, (s, col)):
++    def do_rawtextColumn(self, xxx_todo_changeme10):
++        (s, col) = xxx_todo_changeme10
+         self._stream_write(s)
+         self.col = col
+     bytecode_handlers["rawtextColumn"] = do_rawtextColumn
+ 
+-    def do_rawtextOffset(self, (s, offset)):
++    def do_rawtextOffset(self, xxx_todo_changeme11):
++        (s, offset) = xxx_todo_changeme11
+         self._stream_write(s)
+         self.col = self.col + offset
+     bytecode_handlers["rawtextOffset"] = do_rawtextOffset
+ 
+-    def do_condition(self, (condition, block)):
++    def do_condition(self, xxx_todo_changeme12):
++        (condition, block) = xxx_todo_changeme12
+         if not self.tal or self.engine.evaluateBoolean(condition):
+             self.interpret(block)
+     bytecode_handlers["condition"] = do_condition
+ 
+-    def do_defineMacro(self, (macroName, macro)):
++    def do_defineMacro(self, xxx_todo_changeme13):
++        (macroName, macro) = xxx_todo_changeme13
+         wasInUse = self.inUseDirective
+         self.inUseDirective = False
+         self.interpret(macro)
+         self.inUseDirective = wasInUse
+     bytecode_handlers["defineMacro"] = do_defineMacro
+ 
+-    def do_useMacro(self, (macroName, macroExpr, compiledSlots, block),
++    def do_useMacro(self, xxx_todo_changeme14,
+                     definingName=None, extending=False):
++        (macroName, macroExpr, compiledSlots, block) = xxx_todo_changeme14
+         if not self.metal:
+             self.interpret(block)
+             return
+@@ -865,12 +880,12 @@ class TALInterpreter(object):
+         else:
+             if not isCurrentVersion(macro):
+                 raise METALError("macro %s has incompatible version %s" %
+-                                 (`macroName`, `getProgramVersion(macro)`),
++                                 (repr(macroName), repr(getProgramVersion(macro))),
+                                  self.position)
+             mode = getProgramMode(macro)
+             if mode != (self.html and "html" or "xml"):
+                 raise METALError("macro %s has incompatible mode %s" %
+-                                 (`macroName`, `mode`), self.position)
++                                 (repr(macroName), repr(mode)), self.position)
+         self.pushMacro(macroName, compiledSlots, definingName, extending)
+ 
+         # We want 'macroname' name to be always available as a variable
+@@ -891,23 +906,26 @@ class TALInterpreter(object):
+         self.engine.setLocal('macroname', outer)
+     bytecode_handlers["useMacro"] = do_useMacro
+ 
+-    def do_extendMacro(self, (macroName, macroExpr, compiledSlots, block,
+-                              definingName)):
++    def do_extendMacro(self, xxx_todo_changeme15):
+         # extendMacro results from a combination of define-macro and
+         # use-macro.  definingName has the value of the
+         # metal:define-macro attribute.
++        (macroName, macroExpr, compiledSlots, block,
++                              definingName) = xxx_todo_changeme15
+         extending = self.metal and self.inUseDirective
+         self.do_useMacro((macroName, macroExpr, compiledSlots, block),
+                          definingName, extending)
+     bytecode_handlers["extendMacro"] = do_extendMacro
+ 
+-    def do_fillSlot(self, (slotName, block)):
++    def do_fillSlot(self, xxx_todo_changeme16):
+         # This is only executed if the enclosing 'use-macro' evaluates
+         # to 'default'.
*** 155 LINES SKIPPED ***