Merge branch 'master' into python3
authorFredrik Tolf <fredrik@dolda2000.com>
Sun, 15 Sep 2013 22:41:53 +0000 (00:41 +0200)
committerFredrik Tolf <fredrik@dolda2000.com>
Sun, 15 Sep 2013 22:41:53 +0000 (00:41 +0200)
Conflicts:
wrw/sp/cons.py
wrw/sp/xhtml.py

15 files changed:
setup.py
wrw/__init__.py
wrw/cookie.py
wrw/dispatch.py
wrw/filesys.py
wrw/form.py
wrw/proto.py
wrw/req.py
wrw/resp.py
wrw/session.py
wrw/sp/cons.py
wrw/sp/util.py
wrw/sp/xhtml.py
wrw/util.py
wrw/wmako.py

index 3775c63..d8eb073 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 
 from distutils.core import setup, Extension
 
index 95ff4cd..eb4a0a0 100644 (file)
@@ -1,7 +1,7 @@
 __all__ = ["wsgiwrap", "restart", "cookie", "formdata"]
 
-import proto
-from util import wsgiwrap, formparams, funplex, persession, sessiondata, autodirty, manudirty, specdirty
-from dispatch import restart
-import cookie
-from form import formdata
+from . import proto
+from .util import wsgiwrap, stringwrap, formparams, funplex, persession, sessiondata, autodirty, manudirty, specdirty
+from .dispatch import restart
+from . import cookie
+from .form import formdata
index 140ba8f..c6e848f 100644 (file)
@@ -1,5 +1,5 @@
-import Cookie, time
-import proto
+import http.cookies, time
+from . import proto
 
 __all__ = ["cookies", "get", "add"]
 
@@ -11,10 +11,10 @@ def addcookies(req):
 class cookiedict(object):
     def __init__(self, req):
         try:
-            self.bk = Cookie.SimpleCookie(req.ihead.get("Cookie"))
-        except Cookie.CookieError:
-            self.bk = Cookie.SimpleCookie()
-        self.codec = Cookie.SimpleCookie()
+            self.bk = http.cookies.SimpleCookie(req.ihead.get("Cookie"))
+        except http.cookies.CookieError:
+            self.bk = http.cookies.SimpleCookie()
+        self.codec = http.cookies.SimpleCookie()
         req.oncommit(addcookies)
 
     def __getitem__(self, name):
@@ -30,7 +30,7 @@ class cookiedict(object):
 
     def add(self, name, value, **kw):
         self.codec[name] = value
-        for key, value in kw.iteritems():
+        for key, value in kw.items():
             self.codec[name][key] = value
 
     def __setitem__(self, name, value):
index 1dedbe6..5276892 100644 (file)
@@ -1,5 +1,5 @@
 import sys, traceback
-import env, req, proto
+from . import env, req, proto
 
 __all__ = ["restart"]
 
@@ -17,7 +17,7 @@ def mangle(result):
     return [str(result)]
 
 def defaulterror(req, excinfo):
-    import resp
+    from . import resp
     traceback.print_exception(*excinfo)
     raise resp.httperror(500)
 
@@ -37,9 +37,9 @@ def handle(req, startreq, handler):
             try:
                 resp = handler(req)
                 break
-            except restart, i:
+            except restart as i:
                 handler = i.handle
-            except Exception, i:
+            except Exception as i:
                 if eh is None:
                     raise
                 handler = wraphandler(eh, sys.exc_info())
index dcdc802..56d58aa 100644 (file)
@@ -1,5 +1,5 @@
 import os
-import resp
+from . import resp
 pj = os.path.join
 
 __all__ = ["filehandler"]
@@ -29,7 +29,7 @@ class filehandler(object):
             elif ext == "html":
                 ctype = "text/html"
         req.ohead["Content-Type"] = ctype
-        return open(path, "r")
+        return open(path, "rb")
 
     def resolvefile(self, req, curpath, el):
         if os.path.isfile(pj(curpath, el)):
index 0363b0b..c97b0f9 100644 (file)
@@ -1,5 +1,5 @@
 import cgi
-import proto
+from . import proto
 
 __all__ = ["formdata"]
 
@@ -32,7 +32,7 @@ class formwrap(object):
         return list(iter())
 
     def keys(self):
-        return self.cf.keys()
+        return list(self.cf.keys())
 
     def values(self):
         return [val for key, val in self.items()]
@@ -43,7 +43,7 @@ class badmultipart(Exception):
 class formpart(object):
     def __init__(self, form):
         self.form = form
-        self.buf = ""
+        self.buf = b""
         self.eof = False
         self.head = {}
 
@@ -52,8 +52,8 @@ class formpart(object):
 
     def fillbuf(self, sz):
         req = self.form.req
-        mboundary = "\r\n--" + self.form.boundary + "\r\n"
-        lboundary = "\r\n--" + self.form.boundary + "--\r\n"
+        mboundary = b"\r\n--" + self.form.boundary + b"\r\n"
+        lboundary = b"\r\n--" + self.form.boundary + b"--\r\n"
         while not self.eof:
             p = self.form.buf.find(mboundary)
             if p >= 0:
@@ -91,7 +91,7 @@ class formpart(object):
     def readline(self, limit=-1):
         last = 0
         while True:
-            p = self.buf.find('\n', last)
+            p = self.buf.find(b'\n', last)
             if p < 0:
                 if self.eof:
                     ret = self.buf
@@ -114,12 +114,15 @@ class formpart(object):
         self.close()
         return False
 
-    def parsehead(self):
+    def parsehead(self, charset):
         def headline():
             ln = self.readline(256)
-            if ln[-1] != '\n':
+            if ln[-1] != ord(b'\n'):
                 raise badmultipart("Too long header line in part")
-            return ln.rstrip()
+            try:
+                return ln.decode(charset).rstrip()
+            except UnicodeError:
+                raise badmultipart("Form part header is not in assumed charset")
 
         ln = headline()
         while True:
@@ -151,29 +154,33 @@ class formpart(object):
             raise badmultipart("Form part uses unexpected transfer encoding: %r" % encoding)
 
 class multipart(object):
-    def __init__(self, req):
+    def __init__(self, req, charset):
         val, par = proto.pmimehead(req.ihead.get("Content-Type", ""))
         if req.method != "POST" or val != "multipart/form-data":
             raise badmultipart("Request is not a multipart form")
         if "boundary" not in par:
             raise badmultipart("Multipart form lacks boundary")
-        self.boundary = par["boundary"]
+        try:
+            self.boundary = par["boundary"].encode("us-ascii")
+        except UnicodeError:
+            raise badmultipart("Multipart boundary must be ASCII string")
         self.req = req
-        self.buf = "\r\n"
+        self.buf = b"\r\n"
         self.eof = False
+        self.headcs = charset
         self.lastpart = formpart(self)
         self.lastpart.close()
 
     def __iter__(self):
         return self
 
-    def next(self):
+    def __next__(self):
         if not self.lastpart.eof:
             raise RuntimeError("All form parts must be read entirely")
         if self.eof:
             raise StopIteration()
         self.lastpart = formpart(self)
-        self.lastpart.parsehead()
+        self.lastpart.parsehead(self.headcs)
         return self.lastpart
 
 def formdata(req):
index 8c8dcee..96a8878 100644 (file)
@@ -98,6 +98,7 @@ def simpleerror(env, startreq, code, title, msg):
 </body>
 </html>
 """ % (title, title, htmlq(msg))
+    buf = buf.encode("us-ascii")
     startreq("%i %s" % (code, title), [("Content-Type", "text/html"), ("Content-Length", str(len(buf)))])
     return [buf]
 
index 41fd330..016d2d8 100644 (file)
@@ -19,7 +19,7 @@ class headdict(object):
         del self.dict[key.lower()]
 
     def __iter__(self):
-        return iter((list[0] for list in self.dict.itervalues()))
+        return iter((list[0] for list in self.dict.values()))
     
     def get(self, key, default=""):
         if key.lower() in self.dict:
@@ -67,29 +67,29 @@ class limitreader(object):
             ra = min(ra, size)
         while len(self.buf) < ra:
             ret = self.bk.read(ra - len(self.buf))
-            if ret == "":
+            if ret == b"":
                 raise IOError("Unexpected EOF")
             self.buf.extend(ret)
             self.rb += len(ret)
-        ret = str(self.buf[:ra])
+        ret = bytes(self.buf[:ra])
         self.buf = self.buf[ra:]
         return ret
 
     def readline(self, size=-1):
         off = 0
         while True:
-            p = self.buf.find('\n', off)
+            p = self.buf.find(b'\n', off)
             if p >= 0:
-                ret = str(self.buf[:p + 1])
+                ret = bytes(self.buf[:p + 1])
                 self.buf = self.buf[p + 1:]
                 return ret
             off = len(self.buf)
             if size >= 0 and len(self.buf) >= size:
-                ret = str(self.buf[:size])
+                ret = bytes(self.buf[:size])
                 self.buf = self.buf[size:]
                 return ret
             if self.rb == self.limit:
-                ret = str(self.buf)
+                ret = bytes(self.buf)
                 self.buf = bytearray()
                 return ret
             ra = self.limit - self.rb
@@ -97,7 +97,7 @@ class limitreader(object):
                 ra = min(ra, size)
             ra = min(ra, 1024)
             ret = self.bk.read(ra)
-            if ret == "":
+            if ret == b"":
                 raise IOError("Unpexpected EOF")
             self.buf.extend(ret)
             self.rb += len(ret)
@@ -109,9 +109,9 @@ class limitreader(object):
         class lineiter(object):
             def __iter__(self):
                 return self
-            def next(self):
+            def __next__(self):
                 ret = rd.readline()
-                if ret == "":
+                if ret == b"":
                     raise StopIteration()
                 return ret
         return lineiter()
@@ -148,7 +148,7 @@ class origrequest(request):
                     self.input = limitreader(env["wsgi.input"], int(clen))
                 else:
                     # XXX: What to do?
-                    self.input = io.BytesIO("")
+                    self.input = io.BytesIO(b"")
             else:
                 # Assume input is chunked and read until ordinary EOF.
                 self.input = env["wsgi.input"]
index 7fae787..965df6c 100644 (file)
@@ -1,5 +1,5 @@
-import dispatch, proto, env
-from sp import xhtml
+from . import dispatch, proto, env
+from .sp import xhtml
 h = xhtml.cons()
 
 __all__ = ["skeleton", "skelfor", "setskel", "usererror"]
@@ -28,7 +28,7 @@ def setskel(req, skel):
 
 class usererror(dispatch.restart):
     def __init__(self, message, *detail):
-        super(usererror, self).__init__()
+        super().__init__()
         self.message = message
         self.detail = detail
 
@@ -36,9 +36,9 @@ class usererror(dispatch.restart):
         return skelfor(req).error(req, self.message, *self.detail)
 
 class message(dispatch.restart):
-    def __init__(self, msg, *detail):
-        super(message, self).__init__()
-        self.message = msg
+    def __init__(self, message, *detail):
+        super().__init__()
+        self.message = message
         self.detail = detail
 
     def handle(self, req):
@@ -50,16 +50,16 @@ class httperror(usererror):
             message = proto.statusinfo[status][0]
         if detail is None:
             detail = (proto.statusinfo[status][1],)
-        super(httperror, self).__init__(message, *detail)
+        super().__init__(message, *detail)
         self.status = status
 
     def handle(self, req):
         req.status(self.status, self.message)
-        return super(httperror, self).handle(req)
+        return super().handle(req)
 
 class notfound(httperror):
     def __init__(self):
-        return super(notfound, self).__init__(404)
+        return super().__init__(404)
 
 class redirect(dispatch.restart):
     bases = {"url": proto.requrl,
@@ -67,7 +67,7 @@ class redirect(dispatch.restart):
              "site": proto.siteurl}
 
     def __init__(self, url, status=303, base="url"):
-        super(redirect, self).__init__()
+        super().__init__()
         self.url = url
         self.status = status
         self.bases[base]
index 1e615e3..e7d8581 100644 (file)
@@ -1,5 +1,5 @@
 import threading, time, pickle, random, os
-import cookie, env
+from . import cookie, env
 
 __all__ = ["db", "get"]
 
@@ -11,7 +11,7 @@ def hexencode(str):
 
 def gennonce(length):
     nonce = ""
-    for i in xrange(length):
+    for i in range(length):
         nonce += chr(random.randint(0, 255))
     return nonce
 
@@ -86,7 +86,7 @@ class db(object):
     def clean(self):
         now = int(time.time())
         with self.lock:
-            clist = self.live.keys()
+            clist = list(self.live.keys())
         for sessid in clist:
             with self.lock:
                 try:
@@ -204,7 +204,7 @@ class db(object):
         data = self.backdb[sessid]
         try:
             return pickle.loads(data)
-        except Exception, e:
+        except:
             raise KeyError()
 
     def freeze(self, sess):
index 4052dce..205de03 100644 (file)
@@ -3,18 +3,18 @@ import xml.dom.minidom
 class node(object):
     pass
 
-class text(node, unicode):
+class text(node, str):
     def __todom__(self, doc):
         return doc.createTextNode(self)
 
-class raw(node, unicode):
+class raw(node, str):
     def __todom__(self, doc):
         raise Exception("Cannot convert raw code to DOM objects")
 
 class element(node):
     def __init__(self, ns, name, ctx):
         self.ns = ns
-        self.name = unicode(name)
+        self.name = str(name)
         self.ctx = ctx
         self.attrs = {}
         self.children = []
@@ -22,13 +22,13 @@ class element(node):
     def __call__(self, *children, **attrs):
         for child in children:
             self.ctx.addchild(self, child)
-        for k, v in attrs.iteritems():
+        for k, v in attrs.items():
             self.ctx.addattr(self, k, v)
         return self
 
     def __todom__(self, doc):
         el = doc.createElementNS(self.ns, self.name)
-        for k, v in self.attrs.iteritems():
+        for k, v in self.attrs.items():
             el.setAttribute(k, v)
         for child in self.children:
             el.appendChild(child.__todom__(doc))
@@ -41,10 +41,9 @@ class element(node):
 class context(object):
     def __init__(self):
         self.nodeconv = {}
-        self.nodeconv[str] = lambda ob: text(ob, "utf-8")
-        self.nodeconv[unicode] = text
+        self.nodeconv[bytes] = lambda ob: text(ob, "utf-8")
+        self.nodeconv[str] = text
         self.nodeconv[int] = text
-        self.nodeconv[long] = text
         self.nodeconv[float] = text
 
     def nodefrom(self, ob):
@@ -61,7 +60,7 @@ class context(object):
 
     def addattr(self, node, k, v):
         if v is not None:
-            node.attrs[unicode(k)] = unicode(v)
+            node.attrs[str(k)] = str(v)
 
 class constructor(object):
     def __init__(self, ns, elcls=element, ctx=None):
index 3ea7a8a..ad99e8b 100644 (file)
@@ -1,6 +1,6 @@
-import StringIO
-from wrw import dispatch
-import cons
+import io
+from .. import dispatch
+from . import cons
 
 def findnsnames(el):
     names = {}
@@ -8,7 +8,7 @@ def findnsnames(el):
     def proc(el):
         if isinstance(el, cons.element):
             if el.ns not in names:
-                names[el.ns] = u"n" + unicode(nid[0])
+                names[el.ns] = "n" + str(nid[0])
                 nid[:] = [nid[0] + 1]
             for ch in el.children:
                 proc(ch)
@@ -34,12 +34,12 @@ class formatter(object):
 
     def quotewrite(self, buf):
         for ch in buf:
-            if ch == u'&':
-                self.write(u"&amp;")
-            elif ch == u'<':
-                self.write(u"&lt;")
-            elif ch == u'>':
-                self.write(u"&gt;")
+            if ch == '&':
+                self.write("&amp;")
+            elif ch == '<':
+                self.write("&lt;")
+            elif ch == '>':
+                self.write("&gt;")
             else:
                 self.write(ch)
 
@@ -50,15 +50,15 @@ class formatter(object):
         self.write(el)
 
     def attrval(self, buf):
-        qc, qt = (u"'", u"&apos;") if u'"' in buf else (u'"', u"&quot;")
+        qc, qt = ("'", "&apos;") if '"' in buf else ('"', "&quot;")
         self.write(qc)
         for ch in buf:
-            if ch == u'&':
-                self.write(u"&amp;")
-            elif ch == u'<':
-                self.write(u"&lt;")
-            elif ch == u'>':
-                self.write(u"&gt;")
+            if ch == '&':
+                self.write("&amp;")
+            elif ch == '<':
+                self.write("&lt;")
+            elif ch == '>':
+                self.write("&gt;")
             elif ch == qc:
                 self.write(qt)
             else:
@@ -67,38 +67,38 @@ class formatter(object):
 
     def attr(self, k, v):
         self.write(k)
-        self.write(u'=')
+        self.write('=')
         self.attrval(v)
 
     def shorttag(self, el, **extra):
-        self.write(u'<' + self.elname(el))
-        for k, v in el.attrs.iteritems():
-            self.write(u' ')
+        self.write('<' + self.elname(el))
+        for k, v in el.attrs.items():
+            self.write(' ')
             self.attr(k, v)
-        for k, v in extra.iteritems():
-            self.write(u' ')
+        for k, v in extra.items():
+            self.write(' ')
             self.attr(k, v)
-        self.write(u" />")
+        self.write(" />")
 
     def elname(self, el):
         ns = self.nsnames[el.ns]
         if ns is None:
             return el.name
         else:
-            return ns + u':' + el.name
+            return ns + ':' + el.name
 
     def starttag(self, el, **extra):
-        self.write(u'<' + self.elname(el))
-        for k, v in el.attrs.iteritems():
-            self.write(u' ')
+        self.write('<' + self.elname(el))
+        for k, v in el.attrs.items():
+            self.write(' ')
             self.attr(k, v)
-        for k, v in extra.iteritems():
-            self.write(u' ')
+        for k, v in extra.items():
+            self.write(' ')
             self.attr(k, v)
-        self.write(u'>')
+        self.write('>')
 
     def endtag(self, el):
-        self.write(u'</' + self.elname(el) + u'>')
+        self.write('</' + self.elname(el) + '>')
 
     def longtag(self, el, **extra):
         self.starttag(el, **extra)
@@ -123,19 +123,19 @@ class formatter(object):
             raise Exception("Unknown object in element tree: " + el)
 
     def start(self):
-        self.write(u'<?xml version="1.0" encoding="' + self.charset + u'" ?>\n')
+        self.write('<?xml version="1.0" encoding="' + self.charset + '" ?>\n')
         if self.doctype:
-            self.write(u'<!DOCTYPE %s PUBLIC "%s" "%s">\n' % (self.root.name,
-                                                              self.doctype[0],
-                                                              self.doctype[1]))
+            self.write('<!DOCTYPE %s PUBLIC "%s" "%s">\n' % (self.root.name,
+                                                             self.doctype[0],
+                                                             self.doctype[1]))
         extra = {}
-        for uri, nm in self.nsnames.iteritems():
+        for uri, nm in self.nsnames.items():
             if uri is None:
                 continue
             if nm is None:
-                extra[u"xmlns"] = uri
+                extra["xmlns"] = uri
             else:
-                extra[u"xmlns:" + nm] = uri
+                extra["xmlns:" + nm] = uri
         self.element(self.root, **extra)
 
     @classmethod
@@ -148,7 +148,7 @@ class formatter(object):
 
     @classmethod
     def format(cls, el, *args, **kw):
-        buf = StringIO.StringIO()
+        buf = io.BytesIO()
         cls.output(buf, el, *args, **kw)
         return buf.getvalue()
 
@@ -165,8 +165,9 @@ class iwriter(object):
         self.col = 0
 
     def write(self, buf):
-        for c in buf:
-            if c == '\n':
+        for i in range(len(buf)):
+            c = buf[i:i + 1]
+            if c == b'\n':
                 self.col = 0
             else:
                 self.col += 1
@@ -177,16 +178,16 @@ class iwriter(object):
         if self.atbol:
             return
         if self.col != 0:
-            self.write('\n')
+            self.write(b'\n')
         self.write(indent)
         self.atbol = True
 
 class indenter(formatter):
-    def __init__(self, indent=u"  ", *args, **kw):
+    def __init__(self, indent="  ", *args, **kw):
         super(indenter, self).__init__(*args, **kw)
         self.out = iwriter(self.out)
         self.indent = indent
-        self.curind = u""
+        self.curind = ""
 
     def simple(self, el):
         for ch in el.children:
@@ -226,7 +227,7 @@ class response(dispatch.restart):
     formatter = indenter
 
     def __init__(self, root):
-        super(response, self).__init__()
+        super().__init__()
         self.root = root
 
     @property
index f10d315..50c70b6 100644 (file)
@@ -1,11 +1,11 @@
-import xml.dom.minidom, StringIO
-import cons as _cons
-import util
+import xml.dom.minidom, io
+from . import cons as _cons
+from . import util
 dom = xml.dom.minidom.getDOMImplementation()
 
-ns = u"http://www.w3.org/1999/xhtml"
-doctype = u"-//W3C//DTD XHTML 1.1//EN"
-dtd = u"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
+ns = "http://www.w3.org/1999/xhtml"
+doctype = "-//W3C//DTD XHTML 1.1//EN"
+dtd = "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
 
 class htmlelement(_cons.element):
     def __todoc__(self):
@@ -15,11 +15,11 @@ class htmlelement(_cons.element):
         return doc
 
 class xhtmlcontext(_cons.context):
-    attrmap = {u"klass": u"class"}
+    attrmap = {"klass": "class"}
 
     def addattr(self, node, k, v):
-        k = unicode(k)
-        super(xhtmlcontext, self).addattr(node, self.attrmap.get(k, k), v)
+        k = str(k)
+        super().addattr(node, self.attrmap.get(k, k), v)
 
 def cons(ctx=None):
     if ctx is None: ctx = xhtmlcontext()
@@ -30,7 +30,7 @@ def head(title=None, css=None):
     head = h.head
     if title:
         head(h.title(title))
-    if isinstance(css, str) or isinstance(css, unicode):
+    if isinstance(css, str) or isinstance(css, bytes):
         head(h.link(rel="stylesheet", type="text/css", href=css))
     elif css:
         for ss in css:
@@ -38,7 +38,7 @@ def head(title=None, css=None):
     return head
 
 class htmlformatter(util.formatter):
-    allowshort = set([u"br", u"hr", u"img", u"input", u"meta", u"link"])
+    allowshort = set(["br", "hr", "img", "input", "meta", "link"])
     def element(self, el, **extra):
         if el.name in self.allowshort:
             super(htmlformatter, self).element(el, **extra)
@@ -51,7 +51,7 @@ class htmlindenter(util.indenter, htmlformatter):
 def forreq(req, tree):
     # XXX: Use proper Content-Type for clients accepting it.
     req.ohead["Content-Type"] = "text/html; charset=utf-8"
-    buf = StringIO.StringIO()
+    buf = io.BytesIO()
     htmlindenter.output(buf, tree, doctype=(doctype, dtd), charset="utf-8")
     ret = buf.getvalue()
     req.ohead["Content-Length"] = len(ret)
index 7459b6d..79a8240 100644 (file)
@@ -1,5 +1,5 @@
 import inspect, math
-import req, dispatch, session, form, resp, proto
+from . import req, dispatch, session, form, resp, proto
 
 def wsgiwrap(callable):
     def wrapper(env, startreq):
@@ -7,6 +7,15 @@ def wsgiwrap(callable):
     wrapper.__wrapped__ = callable
     return wrapper
 
+def stringwrap(charset):
+    def dec(callable):
+        def wrapper(*args, **kwargs):
+            bk = callable(*args, **kwargs)
+            for string in bk:
+                yield string.encode(charset)
+        return wrapper
+    return dec
+
 def formparams(callable):
     spec = inspect.getargspec(callable)
     def wrapper(req):
@@ -17,7 +26,7 @@ def formparams(callable):
             for arg in list(args):
                 if arg not in spec.args:
                     del args[arg]
-        for i in xrange(len(spec.args) - (len(spec.defaults) if spec.defaults else 0)):
+        for i in range(len(spec.args) - (len(spec.defaults) if spec.defaults else 0)):
             if spec.args[i] not in args:
                 raise resp.httperror(400, "Missing parameter", ("The query parameter `", resp.h.code(spec.args[i]), "' is required but not supplied."))
         return callable(**args)
@@ -85,12 +94,12 @@ class preiter(object):
         self.bk = real
         self.bki = iter(real)
         self._next = None
-        self.next()
+        self.__next__()
 
     def __iter__(self):
         return self
 
-    def next(self):
+    def __next__(self):
         if self._next is self.end:
             raise StopIteration()
         ret = self._next
@@ -131,7 +140,7 @@ class sessiondata(object):
 class autodirty(sessiondata):
     @classmethod
     def get(cls, req):
-        ret = super(autodirty, cls).get(req)
+        ret = super().get(req)
         if "_is_dirty" not in ret.__dict__:
             ret.__dict__["_is_dirty"] = False
         return ret
@@ -143,18 +152,18 @@ class autodirty(sessiondata):
         return self._is_dirty
 
     def __setattr__(self, name, value):
-        super(autodirty, self).__setattr__(name, value)
+        super().__setattr__(name, value)
         if "_is_dirty" in self.__dict__:
             self.__dict__["_is_dirty"] = True
 
     def __delattr__(self, name):
-        super(autodirty, self).__delattr__(name, value)
+        super().__delattr__(name, value)
         if "_is_dirty" in self.__dict__:
             self.__dict__["_is_dirty"] = True
 
 class manudirty(object):
     def __init__(self, *args, **kwargs):
-        super(manudirty, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.__dirty = False
 
     def sessfrozen(self):
@@ -197,7 +206,7 @@ class specslot(object):
 
 class specclass(type):
     def __init__(self, name, bases, tdict):
-        super(specclass, self).__init__(name, bases, tdict)
+        super().__init__(name, bases, tdict)
         sslots = set()
         dslots = set()
         for cls in self.__mro__:
@@ -209,8 +218,7 @@ class specclass(type):
         for i, slot in enumerate(self.__sslots_a__):
             setattr(self, slot, specslot(slot, i, slot in dslots))
 
-class specdirty(sessiondata):
-    __metaclass__ = specclass
+class specdirty(sessiondata, metaclass=specclass):
     __slots__ = ["session", "__sslots__", "_is_dirty"]
     
     def __specinit__(self):
@@ -218,7 +226,7 @@ class specdirty(sessiondata):
 
     @staticmethod
     def __new__(cls, req, sess):
-        self = super(specdirty, cls).__new__(cls)
+        self = super().__new__(cls)
         self.session = sess
         self.__sslots__ = [specslot.unbound] * len(cls.__sslots_a__)
         self.__specinit__()
index 13ce342..ef886fb 100644 (file)
@@ -1,6 +1,6 @@
 import os, threading
 from mako import template, lookup, filters
-import util, form, session, env, resp
+from . import util, form, session, env, resp
 
 # It seems Mako isn't thread-safe.
 makolock = threading.Lock()