git: f8e7edd70ae1 - main - www/py-nevow: Fix build with setuptools 58.0.0+

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

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

commit f8e7edd70ae17a158315534377ddc4be9814a8d6
Author:     Po-Chuan Hsieh <sunpoet@FreeBSD.org>
AuthorDate: 2022-03-25 13:35:15 +0000
Commit:     Po-Chuan Hsieh <sunpoet@FreeBSD.org>
CommitDate: 2022-03-25 13:38:25 +0000

    www/py-nevow: Fix build with setuptools 58.0.0+
    
    With hat:       python
---
 www/py-nevow/files/patch-2to3 | 4233 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 4233 insertions(+)

diff --git a/www/py-nevow/files/patch-2to3 b/www/py-nevow/files/patch-2to3
new file mode 100644
index 000000000000..f669cb5b5eaa
--- /dev/null
+++ b/www/py-nevow/files/patch-2to3
@@ -0,0 +1,4233 @@
+--- formless/annotate.py.orig	2015-10-20 22:44:09 UTC
++++ formless/annotate.py
+@@ -22,11 +22,11 @@ from formless import iformless
+ class count(object):
+     def __init__(self):
+         self.id = 0
+-    def next(self):
++    def __next__(self):
+         self.id += 1
+         return self.id
+ 
+-nextId = count().next
++nextId = count().__next__
+ 
+ 
+ class InputError(Exception):
+@@ -102,7 +102,7 @@ class Typed(Attribute):
+     required = False
+     requiredFailMessage = 'Please enter a value'
+     null = None
+-    unicode = False
++    str = False
+ 
+     __name__ = ''
+ 
+@@ -114,7 +114,7 @@ class Typed(Attribute):
+         required=None,
+         requiredFailMessage=None,
+         null=None,
+-        unicode=None,
++        str=None,
+         **attributes):
+ 
+         self.id = nextId()
+@@ -130,15 +130,15 @@ class Typed(Attribute):
+             self.requiredFailMessage = requiredFailMessage
+         if null is not None:
+             self.null = null
+-        if unicode is not None:
+-            self.unicode = unicode
++        if str is not None:
++            self.str = str
+         self.attributes = attributes
+ 
+     def getAttribute(self, name, default=None):
+         return self.attributes.get(name, default)
+ 
+     def coerce(self, val, configurable):
+-        raise NotImplementedError, "Implement in %s" % util.qual(self.__class__)
++        raise NotImplementedError("Implement in %s" % util.qual(self.__class__))
+ 
+ 
+ #######################################
+@@ -209,7 +209,7 @@ class Integer(Typed):
+         except ValueError:
+             if sys.version_info < (2,3): # Long/Int aren't integrated
+                 try:
+-                    return long(val)
++                    return int(val)
+                 except ValueError:
+                     raise InputError("'%s' is not an integer." % val)
+             
+@@ -540,7 +540,7 @@ class Binding(object):
+         return self.original.__class__.__name__.lower()
+ 
+     def configure(self, boundTo, results):
+-        raise NotImplementedError, "Implement in %s" % util.qual(self.__class__)
++        raise NotImplementedError("Implement in %s" % util.qual(self.__class__))
+ 
+     def coerce(self, val, configurable):
+         if hasattr(self.original, 'coerce'):
+@@ -617,7 +617,7 @@ class GroupBinding(Binding):
+         self.complexType = typedValue.complexType
+ 
+     def configure(self, boundTo, group):
+-        print "CONFIGURING GROUP BINDING", boundTo, group
++        print("CONFIGURING GROUP BINDING", boundTo, group)
+ 
+ 
+ def _sorter(x, y):
+@@ -670,7 +670,7 @@ def nameToLabel(mname):
+ def labelAndDescriptionFromDocstring(docstring):
+     if docstring is None:
+         docstring = ''
+-    docs = filter(lambda x: x, [x.strip() for x in docstring.split('\n')])
++    docs = [x for x in [x.strip() for x in docstring.split('\n')] if x]
+     if len(docs) > 1:
+         return docs[0], '\n'.join(docs[1:])
+     else:
+@@ -723,7 +723,7 @@ class MetaTypedInterface(InterfaceClass):
+         cls.complexType = True
+         possibleActions = []
+         actionAttachers = []
+-        for key, value in dct.items():
++        for key, value in list(dct.items()):
+             if key[0] == '_': continue
+ 
+             if isinstance(value, MetaTypedInterface):
+--- formless/configurable.py.orig	2015-10-20 22:44:09 UTC
++++ formless/configurable.py
+@@ -66,7 +66,7 @@ class Configurable(object):
+             try:
+                 binding = self.bindingDict[name]
+             except KeyError:
+-                raise RuntimeError, "%s is not an exposed binding on object %s." % (name, self.boundTo)
++                raise RuntimeError("%s is not an exposed binding on object %s." % (name, self.boundTo))
+         binding.boundTo = self.boundTo
+         return binding
+ 
+@@ -125,7 +125,7 @@ class Configurable(object):
+ 
+ class NotFoundConfigurable(Configurable):
+     def getBinding(self, context, name):
+-        raise RuntimeError, self.original
++        raise RuntimeError(self.original)
+ 
+ 
+ class TypedInterfaceConfigurable(Configurable):
+--- formless/processors.py.orig	2015-10-20 22:44:09 UTC
++++ formless/processors.py
+@@ -21,7 +21,7 @@ faketag = tags.html()
+ def exceptblock(f, handler, exception, *a, **kw):
+     try:
+         result = f(*a, **kw)
+-    except exception, e:
++    except exception as e:
+         return handler(e)
+     if isinstance(result, Deferred):
+         def _(fail):
+@@ -91,7 +91,7 @@ class ProcessMethodBinding(components.Adapter):
+         typedValue = self.original.typedValue
+         results = {}
+         failures = {}
+-        if data.has_key('----'):
++        if '----' in data:
+             ## ---- is the "direct object", the one argument you can specify using the command line without saying what the argument name is
+             data[typedValue.arguments[0].name] = data['----']
+             del data['----']
+@@ -101,7 +101,7 @@ class ProcessMethodBinding(components.Adapter):
+                 context = WovenContext(context, faketag)
+                 context.remember(binding, iformless.IBinding)
+                 results[name] = iformless.IInputProcessor(binding.typedValue).process(context, boundTo, data.get(name, ['']))
+-            except formless.InputError, e:
++            except formless.InputError as e:
+                 results[name] = data.get(name, [''])[0]
+                 failures[name] = e.reason
+ 
+@@ -130,14 +130,14 @@ class ProcessPropertyBinding(components.Adapter):
+         result = {}
+         try:
+             result[binding.name] = iformless.IInputProcessor(binding.typedValue).process(context, boundTo, data.get(binding.name, ['']))
+-        except formless.InputError, e:
++        except formless.InputError as e:
+             result[binding.name] = data.get(binding.name, [''])
+             raise formless.ValidateError({binding.name: e.reason}, e.reason, result)
+ 
+         if autoConfigure:
+             try:
+                 return self.original.configure(boundTo, result)
+-            except formless.InputError, e:
++            except formless.InputError as e:
+                 result[binding.name] = data.get(binding.name, [''])
+                 raise formless.ValidateError({binding.name: e.reason}, e.reason, result)
+         return result
+@@ -150,7 +150,7 @@ class ProcessTyped(components.Adapter):
+         """
+         typed = self.original
+         val = data[0]
+-        if typed.unicode:
++        if typed.str:
+             try:
+                 val = val.decode(getPOSTCharset(context), 'replace')
+             except LookupError:
+@@ -164,7 +164,7 @@ class ProcessTyped(components.Adapter):
+                 return typed.null
+         try:
+             return typed.coerce(val, boundTo)
+-        except TypeError, e:
++        except TypeError as e:
+             warnings.warn('Typed.coerce takes two values now, the value to coerce and the configurable in whose context the coerce is taking place. %s %s' % (typed.__class__, typed))
+             return typed.coerce(val)
+ 
+@@ -190,7 +190,7 @@ class ProcessPassword(components.Adapter):
+             else:
+                 return typed.null
+         val = data[0]
+-        if typed.unicode:
++        if typed.str:
+             try:
+                 val = val.decode(getPOSTCharset(context), 'replace')
+             except LookupError:
+--- formless/webform.py.orig	2015-10-20 22:44:09 UTC
++++ formless/webform.py
+@@ -4,8 +4,8 @@
+ # See LICENSE for details.
+ 
+ 
+-from __future__ import generators
+ 
++
+ import warnings
+ from zope.interface import implements, Interface
+ 
+@@ -66,7 +66,7 @@ class BaseInputRenderer(components.Adapter):
+         return context.tag
+ 
+     def input(self, context, slot, data, name, value):
+-        raise NotImplementedError, "Implement in subclass"
++        raise NotImplementedError("Implement in subclass")
+ 
+ class PasswordRenderer(BaseInputRenderer):
+     def input(self, context, slot, data, name, value):
+@@ -437,7 +437,7 @@ def renderForms(configurableKey='', bindingNames=None,
+             if bindingDefaults is None:
+                 available = configurable.getBindingNames(context)
+             else:
+-                available = bindingDefaults.iterkeys()
++                available = iter(bindingDefaults.keys())
+ 
+             def _callback(binding):
+                 renderer = iformless.IBindingRenderer(binding, defaultBindingRenderer)
+--- nevow/_flat.py.orig	2016-05-08 19:28:50 UTC
++++ nevow/_flat.py
+@@ -61,7 +61,7 @@ class FlattenerError(Exception):
+         @return: A string representation of C{obj}.
+         @rtype: L{str}
+         """
+-        if isinstance(obj, (str, unicode)):
++        if isinstance(obj, str):
+             # It's somewhat unlikely that there will ever be a str in the roots
+             # list.  However, something like a MemoryError during a str.replace
+             # call (eg, replacing " with &quot;) could possibly cause this.
+@@ -175,7 +175,7 @@ def _ctxForRequest(request, slotData, renderFactory, i
+     ctx.remember(request, IRequest)
+     for slotGroup in slotData:
+         if slotGroup is not None:
+-            for k, v in slotGroup.items():
++            for k, v in list(slotGroup.items()):
+                 ctx.fillSlots(k, v)
+     if renderFactory is not None:
+         ctx.remember(_OldRendererFactory(renderFactory), IRendererFactory)
+@@ -224,7 +224,7 @@ def _flatten(request, write, root, slotData, renderFac
+     @return: An iterator which yields C{str}, L{Deferred}, and more iterators
+         of the same type.
+     """
+-    if isinstance(root, unicode):
++    if isinstance(root, str):
+         root = root.encode('utf-8')
+     elif isinstance(root, WovenContext):
+         # WovenContext is supported via the getFlattener case, but that is a
+@@ -268,13 +268,13 @@ def _flatten(request, write, root, slotData, renderFac
+                                    False, True)
+                 else:
+                     write('<')
+-                    if isinstance(root.tagName, unicode):
++                    if isinstance(root.tagName, str):
+                         tagName = root.tagName.encode('ascii')
+                     else:
+                         tagName = str(root.tagName)
+                     write(tagName)
+-                    for k, v in sorted(root.attributes.iteritems()):
+-                        if isinstance(k, unicode):
++                    for k, v in sorted(root.attributes.items()):
++                        if isinstance(k, str):
+                             k = k.encode('ascii')
+                         write(" " + k + "=\"")
+                         yield _flatten(request, write, v, slotData,
+@@ -310,7 +310,7 @@ def _flatten(request, write, root, slotData, renderFac
+         write(root.num)
+         write(';')
+     elif isinstance(root, xml):
+-        if isinstance(root.content, unicode):
++        if isinstance(root.content, str):
+             write(root.content.encode('utf-8'))
+         else:
+             write(root.content)
+@@ -409,10 +409,10 @@ def flatten(request, write, root, inAttribute, inXML):
+             # In Python 2.5, after an exception, a generator's gi_frame is
+             # None.
+             frame = stack[-1].gi_frame
+-            element = stack[-1].next()
++            element = next(stack[-1])
+         except StopIteration:
+             stack.pop()
+-        except Exception, e:
++        except Exception as e:
+             stack.pop()
+             roots = []
+             for generator in stack:
+@@ -423,7 +423,8 @@ def flatten(request, write, root, inAttribute, inXML):
+             if type(element) is str:
+                 write(element)
+             elif isinstance(element, Deferred):
+-                def cbx((original, toFlatten)):
++                def cbx(xxx_todo_changeme):
++                    (original, toFlatten) = xxx_todo_changeme
+                     stack.append(toFlatten)
+                     return original
+                 yield element.addCallback(cbx)
+@@ -456,7 +457,7 @@ def _flattensome(state, write, schedule, result):
+     """
+     while True:
+         try:
+-            element = state.next()
++            element = next(state)
+         except StopIteration:
+             result.callback(None)
+         except:
+--- nevow/accessors.py.orig	2015-10-20 22:44:09 UTC
++++ nevow/accessors.py
+@@ -48,7 +48,7 @@ class DirectiveAccessor(tpc.Adapter):
+         data = context.locate(IData)
+         container = IContainer(data, None)
+         if container is None:
+-            raise NoAccessor, "%r does not implement IContainer, and there is no registered adapter." % data
++            raise NoAccessor("%r does not implement IContainer, and there is no registered adapter." % data)
+         child = container.child(context, self.original.name)
+         return child
+ 
+--- nevow/athena.py.orig	2016-05-08 19:28:50 UTC
++++ nevow/athena.py
+@@ -1,6 +1,6 @@
+ # -*- test-case-name: nevow.test.test_athena -*-
+ 
+-import itertools, os, re, warnings, StringIO
++import itertools, os, re, warnings, io
+ 
+ from zope.interface import implements
+ 
+@@ -49,7 +49,7 @@ class LivePageError(Exception):
+     """
+     Base exception for LivePage errors.
+     """
+-    jsClass = u'Divmod.Error'
++    jsClass = 'Divmod.Error'
+ 
+ 
+ 
+@@ -58,7 +58,7 @@ class NoSuchMethod(LivePageError):
+     Raised when an attempt is made to invoke a method which is not defined or
+     exposed.
+     """
+-    jsClass = u'Nevow.Athena.NoSuchMethod'
++    jsClass = 'Nevow.Athena.NoSuchMethod'
+ 
+     def __init__(self, objectID, methodName):
+         self.objectID = objectID
+@@ -186,7 +186,7 @@ class AthenaModule(object):
+         Calculate our dependencies given the path to our source.
+         """
+         depgen = self._extractImports(file(jsFile, 'rU'))
+-        return self.packageDeps + dict.fromkeys(depgen).keys()
++        return self.packageDeps + list(dict.fromkeys(depgen).keys())
+ 
+ 
+     def dependencies(self):
+@@ -300,7 +300,7 @@ def _collectPackageBelow(baseDir, extension):
+             path = os.path.join(root, dir, '__init__.' + extension)
+             if not os.path.exists(path):
+                 path = EMPTY
+-            mapping[unicode(name, 'ascii')] = path
++            mapping[str(name, 'ascii')] = path
+             _revMap[os.path.join(root, dir)] = name + '.'
+ 
+         for fn in filenames:
+@@ -315,7 +315,7 @@ def _collectPackageBelow(baseDir, extension):
+ 
+             name = stem + fn[:-(len(extension) + 1)]
+             path = os.path.join(root, fn)
+-            mapping[unicode(name, 'ascii')] = path
++            mapping[str(name, 'ascii')] = path
+     return mapping
+ 
+ 
+@@ -540,11 +540,11 @@ def getJSFailure(exc, modules):
+     """
+     Convert a serialized client-side exception to a Failure.
+     """
+-    text = '%s: %s' % (exc[u'name'], exc[u'message'])
++    text = '%s: %s' % (exc['name'], exc['message'])
+ 
+     frames = []
+-    if u'stack' in exc:
+-        frames = parseStack(exc[u'stack'])
++    if 'stack' in exc:
++        frames = parseStack(exc['stack'])
+ 
+     return failure.Failure(JSException(text), exc_tb=buildTraceback(frames, modules))
+ 
+@@ -618,8 +618,8 @@ class ConnectionLost(Exception):
+     pass
+ 
+ 
+-CLOSE = u'close'
+-UNLOAD = u'unload'
++CLOSE = 'close'
++UNLOAD = 'unload'
+ 
+ class ReliableMessageDelivery(object):
+     """
+@@ -775,8 +775,8 @@ class ReliableMessageDelivery(object):
+ 
+ 
+     def _unregisterDeferredAsOutputChannel(self, deferred):
+-        for i in xrange(len(self.outputs)):
+-            if self.outputs[i][0].im_self is deferred:
++        for i in range(len(self.outputs)):
++            if self.outputs[i][0].__self__ is deferred:
+                 output, timeout = self.outputs.pop(i)
+                 timeout.cancel()
+                 break
+@@ -1007,7 +1007,7 @@ class LivePage(rend.Page, _HasJSClass, _HasCSSModule):
+     @ivar _localObjectIDCounter: A callable that will return a new
+         locally-unique object ID each time it is called.
+     """
+-    jsClass = u'Nevow.Athena.PageWidget'
++    jsClass = 'Nevow.Athena.PageWidget'
+     cssModule = None
+ 
+     factory = LivePageFactory()
+@@ -1140,7 +1140,7 @@ class LivePage(rend.Page, _HasJSClass, _HasCSSModule):
+         if self.cssModuleRoot is None:
+             self.cssModuleRoot = location.child(self.clientID).child('cssmodule')
+ 
+-        self._requestIDCounter = itertools.count().next
++        self._requestIDCounter = itertools.count().__next__
+ 
+         self._messageDeliverer = ReliableMessageDelivery(
+             self,
+@@ -1151,7 +1151,7 @@ class LivePage(rend.Page, _HasJSClass, _HasCSSModule):
+             connectionMade=self._connectionMade)
+         self._remoteCalls = {}
+         self._localObjects = {}
+-        self._localObjectIDCounter = itertools.count().next
++        self._localObjectIDCounter = itertools.count().__next__
+ 
+         self.addLocalObject(self)
+ 
+@@ -1252,7 +1252,7 @@ class LivePage(rend.Page, _HasJSClass, _HasCSSModule):
+         """
+         Invoke connectionMade on all attached widgets.
+         """
+-        for widget in self._localObjects.values():
++        for widget in list(self._localObjects.values()):
+             widget.connectionMade()
+         self._didConnect = True
+ 
+@@ -1274,10 +1274,10 @@ class LivePage(rend.Page, _HasJSClass, _HasCSSModule):
+                 d.errback(reason)
+             calls = self._remoteCalls
+             self._remoteCalls = {}
+-            for (reqID, resD) in calls.iteritems():
++            for (reqID, resD) in calls.items():
+                 resD.errback(reason)
+             if self._didConnect:
+-                for widget in self._localObjects.values():
++                for widget in list(self._localObjects.values()):
+                     widget.connectionLost(reason)
+             self.factory.removeClient(self.clientID)
+ 
+@@ -1316,8 +1316,8 @@ class LivePage(rend.Page, _HasJSClass, _HasCSSModule):
+ 
+ 
+     def callRemote(self, methodName, *args):
+-        requestID = u's2c%i' % (self._requestIDCounter(),)
+-        message = (u'call', (unicode(methodName, 'ascii'), requestID, args))
++        requestID = 's2c%i' % (self._requestIDCounter(),)
++        message = ('call', (str(methodName, 'ascii'), requestID, args))
+         resultD = defer.Deferred()
+         self._remoteCalls[requestID] = resultD
+         self.addMessage(message)
+@@ -1439,12 +1439,13 @@ class LivePage(rend.Page, _HasJSClass, _HasCSSModule):
+         raise AttributeError(methodName)
+ 
+ 
+-    def liveTransportMessageReceived(self, ctx, (action, args)):
++    def liveTransportMessageReceived(self, ctx, xxx_todo_changeme):
+         """
+         A message was received from the reliable transport layer.  Process it by
+         dispatching it first to myself, then later to application code if
+         applicable.
+         """
++        (action, args) = xxx_todo_changeme
+         method = getattr(self, 'action_' + action)
+         method(ctx, *args)
+ 
+@@ -1472,11 +1473,11 @@ class LivePage(rend.Page, _HasJSClass, _HasCSSModule):
+                         result.value.args)
+                 else:
+                     result = (
+-                        u'Divmod.Error',
+-                        [u'%s: %s' % (
++                        'Divmod.Error',
++                        ['%s: %s' % (
+                                 result.type.__name__.decode('ascii'),
+                                 result.getErrorMessage().decode('ascii'))])
+-            message = (u'respond', (unicode(requestId), success, result))
++            message = ('respond', (str(requestId), success, result))
+             self.addMessage(message)
+         result.addBoth(_cbCall)
+ 
+@@ -1517,7 +1518,7 @@ def _rewriteEventHandlerToAttribute(tag):
+     """
+     if isinstance(tag, stan.Tag):
+         extraAttributes = {}
+-        for i in xrange(len(tag.children) - 1, -1, -1):
++        for i in range(len(tag.children) - 1, -1, -1):
+             if isinstance(tag.children[i], stan.Tag) and tag.children[i].tagName == 'athena:handler':
+                 info = tag.children.pop(i)
+                 name = info.attributes['event'].encode('ascii')
+@@ -1565,7 +1566,7 @@ def _rewriteAthenaId(tag):
+             if headers is not None:
+                 ids = headers.split()
+                 headers = [_mangleId(headerId) for headerId in ids]
+-                for n in xrange(len(headers) - 1, 0, -1):
++                for n in range(len(headers) - 1, 0, -1):
+                     headers.insert(n, ' ')
+                 tag.attributes['headers'] = headers
+     return tag
+@@ -1580,7 +1581,7 @@ def rewriteAthenaIds(root):
+ 
+ 
+ class _LiveMixin(_HasJSClass, _HasCSSModule):
+-    jsClass = u'Nevow.Athena.Widget'
++    jsClass = 'Nevow.Athena.Widget'
+     cssModule = None
+ 
+     preprocessors = [rewriteEventHandlerNodes, rewriteAthenaIds]
+@@ -1633,7 +1634,7 @@ class _LiveMixin(_HasJSClass, _HasCSSModule):
+         C{self.page}, add this object to the page and fill the I{athena:id}
+         slot with this object's Athena identifier.
+         """
+-        assert isinstance(self.jsClass, unicode), "jsClass must be a unicode string"
++        assert isinstance(self.jsClass, str), "jsClass must be a unicode string"
+ 
+         if self.page is None:
+             raise OrphanedFragment(self)
+@@ -1681,7 +1682,7 @@ class _LiveMixin(_HasJSClass, _HasCSSModule):
+         # different module from whence nevow.athena and nevow.testutil could
+         # import it. -exarkun
+         from nevow.testutil import FakeRequest
+-        s = StringIO.StringIO()
++        s = io.StringIO()
+         for _ in _flat.flatten(FakeRequest(), s.write, what, False, False):
+             pass
+         return s.getvalue()
+@@ -1713,15 +1714,15 @@ class _LiveMixin(_HasJSClass, _HasCSSModule):
+         del children[0]
+ 
+         self._structuredCache = {
+-            u'requiredModules': [(name, flat.flatten(url).decode('utf-8'))
++            'requiredModules': [(name, flat.flatten(url).decode('utf-8'))
+                                  for (name, url) in requiredModules],
+-            u'requiredCSSModules': [flat.flatten(url).decode('utf-8')
++            'requiredCSSModules': [flat.flatten(url).decode('utf-8')
+                                     for url in requiredCSSModules],
+-            u'class': self.jsClass,
+-            u'id': self._athenaID,
+-            u'initArguments': tuple(self.getInitialArguments()),
+-            u'markup': markup,
+-            u'children': children}
++            'class': self.jsClass,
++            'id': self._athenaID,
++            'initArguments': tuple(self.getInitialArguments()),
++            'markup': markup,
++            'children': children}
+         return self._structuredCache
+ 
+ 
+@@ -1741,9 +1742,9 @@ class _LiveMixin(_HasJSClass, _HasCSSModule):
+         # This will only be set if _structured() is being run.
+         if context.get('children') is not None:
+             context.get('children').append({
+-                    u'class': self.jsClass,
+-                    u'id': self._athenaID,
+-                    u'initArguments': self.getInitialArguments()})
++                    'class': self.jsClass,
++                    'id': self._athenaID,
++                    'initArguments': self.getInitialArguments()})
+             context.get('requiredModules').extend(requiredModules)
+             context.get('requiredCSSModules').extend(requiredCSSModules)
+             return tag
+@@ -1792,7 +1793,7 @@ class _LiveMixin(_HasJSClass, _HasCSSModule):
+         return self.page.callRemote(
+             "Nevow.Athena.callByAthenaID",
+             self._athenaID,
+-            unicode(methodName, 'ascii'),
++            str(methodName, 'ascii'),
+             varargs)
+ 
+ 
+@@ -1998,7 +1999,7 @@ class IntrospectionFragment(LiveFragment):
+     the state of a live page.
+     """
+ 
+-    jsClass = u'Nevow.Athena.IntrospectionWidget'
++    jsClass = 'Nevow.Athena.IntrospectionWidget'
+ 
+     docFactory = loaders.stan(
+         tags.span(render=tags.directive('liveFragment'))[
+--- nevow/canvas.py.orig	2015-10-20 22:44:09 UTC
++++ nevow/canvas.py
+@@ -11,7 +11,7 @@ from nevow.flat import flatten
+ from nevow.stan import Proto, Tag
+ from itertools import count
+ 
+-cn = count().next
++cn = count().__next__
+ cookie = lambda: str(cn())
+ 
+ _hookup = {}
+@@ -193,7 +193,7 @@ class GroupBase(object):
+             l[[a(v=x) for x in colors]],
+             l[[a(v=x) for x in alphas]],
+             l[[a(v=x) for x in ratios]],
+-            d[[i(k=k, v=v) for (k, v) in matrix.items()]])
++            d[[i(k=k, v=v) for (k, v) in list(matrix.items())]])
+ 
+     def text(self, text, x, y, height, width):
+         """Place the given text on the canvas using the given x, y, height and width.
+@@ -212,7 +212,7 @@ class GroupBase(object):
+         cook = cookie()
+         I = Image(cook, self)
+         self.call('image', cook, where)
+-        print "IMAGE", where
++        print("IMAGE", where)
+         return I
+ 
+     def sound(self, where, stream=True):
+@@ -354,18 +354,18 @@ class CanvasSocket(GroupBase):
+ 
+     def handle_onMouseUp(self, info):
+         if self.delegate.onMouseUp:
+-            self.delegate.onMouseUp(self, *map(int, map(float, info.split())))
++            self.delegate.onMouseUp(self, *list(map(int, list(map(float, info.split())))))
+ 
+     def handle_onMouseDown(self, info):
+         if self.delegate.onMouseDown:
+-            self.delegate.onMouseDown(self, *map(int, map(float, info.split())))
++            self.delegate.onMouseDown(self, *list(map(int, list(map(float, info.split())))))
+ 
+     def handle_onMouseMove(self, info):
+         if self.delegate.onMouseMove:
+-            self.delegate.onMouseMove(self, *map(int, map(float, info.split())))
++            self.delegate.onMouseMove(self, *list(map(int, list(map(float, info.split())))))
+ 
+     def handle_diagnostic(self, info):
+-        print "Trace", info
++        print("Trace", info)
+ 
+ canvasServerMessage = loaders.stan(tags.html["This server dispatches for nevow canvas events."])
+ 
+--- nevow/context.py.orig	2015-10-20 22:44:09 UTC
++++ nevow/context.py
+@@ -2,8 +2,8 @@
+ # Copyright (c) 2004 Divmod.
+ # See LICENSE for details.
+ 
+-from __future__ import generators
+ 
++
+ import warnings
+ 
+ from nevow import stan
+@@ -109,7 +109,7 @@ class WebContext(object):
+ 
+             contextParent = currentContext.parent
+             if contextParent is None:
+-                raise KeyError, "Interface %s was not remembered." % key
++                raise KeyError("Interface %s was not remembered." % key)
+ 
+             currentContext = contextParent
+ 
+@@ -151,7 +151,7 @@ class WebContext(object):
+                 if data is not Unset:
+                     return data
+             if currentContext.parent is None:
+-                raise KeyError, "Slot named '%s' was not filled." % name
++                raise KeyError("Slot named '%s' was not filled." % name)
+             currentContext = currentContext.parent
+ 
+     def clone(self, deep=True, cloneTags=True):
+--- nevow/dirlist.py.orig	2015-10-20 22:44:09 UTC
++++ nevow/dirlist.py
+@@ -5,7 +5,7 @@
+ 
+ # system imports
+ import os
+-import urllib
++import urllib.request, urllib.parse, urllib.error
+ import stat
+ 
+ # twisted imports
+@@ -49,7 +49,7 @@ class DirectoryLister(rend.Page):
+         files = []; dirs = []
+ 
+         for path in directory:
+-            url = urllib.quote(path, '/')
++            url = urllib.parse.quote(path, '/')
+             if os.path.isdir(os.path.join(self.path, path)):
+                 url = url + '/'
+                 dirs.append({
+@@ -65,7 +65,7 @@ class DirectoryLister(rend.Page):
+                     self.contentTypes, self.contentEncodings, self.defaultType)
+                 try:
+                     filesize = os.stat(os.path.join(self.path, path))[stat.ST_SIZE]
+-                except OSError, x:
++                except OSError as x:
+                     if x.errno != 2 and x.errno != 13:
+                         raise x
+                 else:
+@@ -80,7 +80,7 @@ class DirectoryLister(rend.Page):
+ 
+     def data_header(self, context, data):
+         request = context.locate(inevow.IRequest)
+-        return "Directory listing for %s" % urllib.unquote(request.uri)
++        return "Directory listing for %s" % urllib.parse.unquote(request.uri)
+ 
+     def render_tableLink(self, context, data):
+         return tags.a(href=data['link'])[data['linktext']]
+--- nevow/entities.py.orig	2015-10-20 22:44:09 UTC
++++ nevow/entities.py
+@@ -10,7 +10,8 @@ import types
+ 
+ __by_number = {}
+ 
+-def makeEntity((name, num, description)):
++def makeEntity(xxx_todo_changeme):
++    (name, num, description) = xxx_todo_changeme
+     from nevow.stan import Entity
+     e = Entity(name, num, description)
+     __by_number[types.IntType(num)] = e
+--- nevow/events.py.orig	2015-10-20 22:44:09 UTC
++++ nevow/events.py
+@@ -16,7 +16,7 @@ class EventNotification:
+         Returns a token which should be passed to unsubscribe when done.
+         """
+         if DEBUG:
+-            print "SUBSCRIBE", self, identifier, subscriber
++            print("SUBSCRIBE", self, identifier, subscriber)
+         self._subscribers.setdefault(identifier, []).append(subscriber)
+         return identifier, subscriber
+ 
+@@ -24,7 +24,7 @@ class EventNotification:
+         """Unsubscribe the given token from events.
+         """
+         if DEBUG:
+-            print "UNSUBSCRIBE", token
++            print("UNSUBSCRIBE", token)
+         identifier, reference = token
+         self._subscribers[identifier].remove(reference)
+ 
+@@ -32,14 +32,14 @@ class EventNotification:
+         """Notify the listeners on a given identifier that an event has occurred.
+         """
+         if DEBUG:
+-            print "PUBLISH", self, identifier,
++            print("PUBLISH", self, identifier, end=' ')
+         subscribers = self._subscribers.get(identifier, [])
+         for sub in subscribers:
+             sub(*args)
+             if DEBUG:
+-                print "NOTIFY SUBSCRIBER", sub
++                print("NOTIFY SUBSCRIBER", sub)
+         if DEBUG:
+-            print "done"
++            print("done")
+ 
+     def nextId(self):
+         self._currentId += 1
+--- nevow/flat/flatstan.py.orig	2016-01-26 23:52:18 UTC
++++ nevow/flat/flatstan.py
+@@ -1,10 +1,10 @@
+ # Copyright (c) 2004 Divmod.
+ # See LICENSE for details.
+ 
+-from __future__ import generators
+ 
+-import urllib, warnings
+ 
++import urllib.request, urllib.parse, urllib.error, warnings
++
+ from twisted.python import log, failure
+ 
+ from nevow import util
+@@ -38,7 +38,7 @@ def TagSerializer(original, context, contextIsMine=Fal
+     visible = bool(original.tagName)
+     
+     if visible and context.isAttrib:
+-        raise RuntimeError, "Tried to render tag '%s' in an tag attribute context." % (original.tagName)
++        raise RuntimeError("Tried to render tag '%s' in an tag attribute context." % (original.tagName))
+ 
+     if context.precompile and original.macro:
+         toBeRenderedBy = original.macro
+@@ -53,7 +53,7 @@ def TagSerializer(original, context, contextIsMine=Fal
+     ## TODO: Do we really need to bypass precompiling for *all* specials?
+     ## Perhaps just render?
+     if context.precompile and (
+-        [x for x in original._specials.values() 
++        [x for x in list(original._specials.values()) 
+         if x is not None and x is not Unset]
+         or original.slotData):
+         ## The tags inside this one get a "fresh" parent chain, because
+@@ -111,7 +111,7 @@ def TagSerializer(original, context, contextIsMine=Fal
+     yield '<%s' % original.tagName
+     if original.attributes:
+         attribContext = WovenContext(parent=context, precompile=context.precompile, isAttrib=True)
+-        for (k, v) in sorted(original.attributes.iteritems()):
++        for (k, v) in sorted(original.attributes.items()):
+             if v is None:
+                 continue
+             yield ' %s="' % k
+@@ -155,7 +155,7 @@ def StringSerializer(original, context):
+     if context.inURL:
+         # The magic string "-_.!*'()" also appears in url.py.  Thinking about
+         # changing this?  Change that, too.
+-        return urllib.quote(original, safe="-_.!*'()")
++        return urllib.parse.quote(original, safe="-_.!*'()")
+     ## quote it
+     if context.inJS:
+         original = _jsSingleQuoteQuote(original)
+@@ -235,7 +235,7 @@ def FunctionSerializer(original, context, nocontextfun
+                 else:
+                     result = original(context, data)
+         except StopIteration:
+-            raise RuntimeError, "User function %r raised StopIteration." % original
++            raise RuntimeError("User function %r raised StopIteration." % original)
+         return serialize(result, context)
+ 
+ 
+--- nevow/guard.py.orig	2016-02-17 12:51:40 UTC
++++ nevow/guard.py
+@@ -16,7 +16,7 @@ try:
+     from hashlib import md5
+ except ImportError:
+     from md5 import md5
+-import StringIO
++import io
+ 
+ from zope.interface import implements
+ 
+@@ -68,7 +68,7 @@ class GuardSession(components.Componentized):
+         # XXX TODO: need to actually sort avatars by login order!
+         if len(self.portals) != 1:
+             raise RuntimeError("Ambiguous request for current avatar.")
+-        return self.portals.values()[0][0]
++        return list(self.portals.values())[0][0]
+ 
+     def resourceForPortal(self, port):
+         return self.portals.get(port)
+@@ -86,7 +86,7 @@ class GuardSession(components.Componentized):
+             raise RuntimeError("Ambiguous request for current avatar.")
+         self.setResourceForPortal(
+             rsrc,
+-            self.portals.keys()[0],
++            list(self.portals.keys())[0],
+             logout)
+ 
+     def setResourceForPortal(self, rsrc, port, logout):
+@@ -148,7 +148,7 @@ class GuardSession(components.Componentized):
+         del self.guard.sessions[self.uid]
+ 
+         # Logout of all portals
+-        for portal in self.portals.keys():
++        for portal in list(self.portals.keys()):
+             self.portalLogout(portal)
+ 
+         for c in self.expireCallbacks:
+@@ -170,7 +170,7 @@ class GuardSession(components.Componentized):
+         self.checkExpiredID = None
+         # If I haven't been touched in 15 minutes:
+         if time.time() - self.lastModified > self.lifetime / 2:
+-            if self.guard.sessions.has_key(self.uid):
++            if self.uid in self.guard.sessions:
+                 self.expire()
+             else:
+                 log.msg("no session to expire: %s" % str(self.uid))
+@@ -180,7 +180,7 @@ class GuardSession(components.Componentized):
+                                                     self.checkExpired)
+     def __getstate__(self):
+         d = self.__dict__.copy()
+-        if d.has_key('checkExpiredID'):
++        if 'checkExpiredID' in d:
+             del d['checkExpiredID']
+         return d
+ 
+@@ -196,7 +196,7 @@ def urlToChild(ctx, *ar, **kw):
+         u = u.child(stan.xml(segment))
+     if inevow.IRequest(ctx).method == 'POST':
+         u = u.clear()
+-    for k,v in kw.items():
++    for k,v in list(kw.items()):
+         u = u.replace(k, v)
+ 
+     return u
+@@ -272,7 +272,8 @@ class SessionWrapper:
+     def renderHTTP(self, ctx):
+         request = inevow.IRequest(ctx)
+         d = defer.maybeDeferred(self._delegate, ctx, [])
+-        def _cb((resource, segments), ctx):
++        def _cb(xxx_todo_changeme1, ctx):
++            (resource, segments) = xxx_todo_changeme1
+             assert not segments
+             res = inevow.IResource(resource)
+             return res.renderHTTP(ctx)
+@@ -425,7 +426,7 @@ class SessionWrapper:
+         if spoof and hasattr(session, 'args'):
+             request.args = session.args
+             request.fields = session.fields
+-            request.content = StringIO.StringIO()
++            request.content = io.StringIO()
+             request.content.close()
+             request.method = session.method
+             request.requestHeaders = session._requestHeaders
+@@ -450,9 +451,10 @@ class SessionWrapper:
+ 
+         if authCommand == LOGIN_AVATAR:
+             subSegments = segments[1:]
+-            def unmangleURL((res,segs)):
++            def unmangleURL(xxx_todo_changeme):
+                 # Tell the session that we just logged in so that it will
+                 # remember form values for us.
++                (res,segs) = xxx_todo_changeme
+                 session.justLoggedIn = True
+                 # Then, generate a redirect back to where we're supposed to be
+                 # by looking at the root of the site and calculating the path
+@@ -533,7 +535,8 @@ class SessionWrapper:
+             self._cbLoginSuccess, session, segments
+         )
+ 
+-    def _cbLoginSuccess(self, (iface, res, logout), session, segments):
++    def _cbLoginSuccess(self, xxx_todo_changeme2, session, segments):
++        (iface, res, logout) = xxx_todo_changeme2
+         session.setResourceForPortal(res, self.portal, logout)
+         return res, segments
+ 
+--- nevow/json.py.orig	2016-05-08 19:28:50 UTC
++++ nevow/json.py
+@@ -43,12 +43,12 @@ class StringTokenizer(object):
+         SLASH = "\\"
+ 
+         IT = iter(s)
+-        bits = [IT.next()]
++        bits = [next(IT)]
+         for char in IT:
+             bits.append(char)
+             if char == SLASH:
+                 try:
+-                    bits.append(IT.next())
++                    bits.append(next(IT))
+                 except StopIteration:
+                     return None
+             if char == '"':
+@@ -82,9 +82,9 @@ class WhitespaceToken(object):
+ 
+ def jsonlong(s):
+     if 'e' in s:
+-        m, e = map(long, s.split('e', 1))
++        m, e = list(map(int, s.split('e', 1)))
+     else:
+-        m, e = long(s), 0
++        m, e = int(s), 0
+     return m * 10 ** e
+ 
*** 3269 LINES SKIPPED ***