From: Fredrik Tolf Date: Tue, 4 Jun 2013 13:31:09 +0000 (+0200) Subject: Merge branch 'master' into python3 X-Git-Url: http://www.dolda2000.com/gitweb/?a=commitdiff_plain;h=8d8d1f703090499006efeb51299b8a267b00aa09;hp=d22f3483df089fc239b4182807c12aaab5ab1c7d;p=wrw.git Merge branch 'master' into python3 --- diff --git a/setup.py b/setup.py index 3775c63..d8eb073 100755 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 from distutils.core import setup, Extension diff --git a/wrw/__init__.py b/wrw/__init__.py index 95ff4cd..eb4a0a0 100644 --- a/wrw/__init__.py +++ b/wrw/__init__.py @@ -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 diff --git a/wrw/cookie.py b/wrw/cookie.py index 4f5071a..b66e293 100644 --- a/wrw/cookie.py +++ b/wrw/cookie.py @@ -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): diff --git a/wrw/dispatch.py b/wrw/dispatch.py index 1dedbe6..5276892 100644 --- a/wrw/dispatch.py +++ b/wrw/dispatch.py @@ -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()) diff --git a/wrw/filesys.py b/wrw/filesys.py index dcdc802..56d58aa 100644 --- a/wrw/filesys.py +++ b/wrw/filesys.py @@ -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)): diff --git a/wrw/form.py b/wrw/form.py index 98157c0..98d2ed9 100644 --- a/wrw/form.py +++ b/wrw/form.py @@ -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): diff --git a/wrw/proto.py b/wrw/proto.py index 4cf1951..0a6a430 100644 --- a/wrw/proto.py +++ b/wrw/proto.py @@ -98,6 +98,7 @@ def simpleerror(env, startreq, code, title, msg): """ % (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] diff --git a/wrw/req.py b/wrw/req.py index 5fbc2fe..11f856c 100644 --- a/wrw/req.py +++ b/wrw/req.py @@ -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"] diff --git a/wrw/resp.py b/wrw/resp.py index 159126a..69635f8 100644 --- a/wrw/resp.py +++ b/wrw/resp.py @@ -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,20 +50,20 @@ 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): def __init__(self, url, status = 303): - super(redirect, self).__init__() + super().__init__() self.url = url self.status = status diff --git a/wrw/session.py b/wrw/session.py index 43b9087..0a17c6d 100644 --- a/wrw/session.py +++ b/wrw/session.py @@ -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): diff --git a/wrw/sp/cons.py b/wrw/sp/cons.py index 168f297..74a6ea6 100644 --- a/wrw/sp/cons.py +++ b/wrw/sp/cons.py @@ -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): @@ -60,7 +59,7 @@ class context(object): node.children.append(self.nodefrom(child)) def addattr(self, node, k, v): - node.attrs[unicode(k)] = unicode(v) + node.attrs[str(k)] = str(v) class constructor(object): def __init__(self, ns, elcls = element, ctx=None): diff --git a/wrw/sp/util.py b/wrw/sp/util.py index 3ea7a8a..ad99e8b 100644 --- a/wrw/sp/util.py +++ b/wrw/sp/util.py @@ -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"&") - elif ch == u'<': - self.write(u"<") - elif ch == u'>': - self.write(u">") + if ch == '&': + self.write("&") + elif ch == '<': + self.write("<") + elif ch == '>': + self.write(">") else: self.write(ch) @@ -50,15 +50,15 @@ class formatter(object): self.write(el) def attrval(self, buf): - qc, qt = (u"'", u"'") if u'"' in buf else (u'"', u""") + qc, qt = ("'", "'") if '"' in buf else ('"', """) self.write(qc) for ch in buf: - if ch == u'&': - self.write(u"&") - elif ch == u'<': - self.write(u"<") - elif ch == u'>': - self.write(u">") + if ch == '&': + self.write("&") + elif ch == '<': + self.write("<") + elif ch == '>': + self.write(">") 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.write('') 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'\n') + self.write('\n') if self.doctype: - self.write(u'\n' % (self.root.name, - self.doctype[0], - self.doctype[1])) + self.write('\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 diff --git a/wrw/sp/xhtml.py b/wrw/sp/xhtml.py index 447c1b1..662c171 100644 --- a/wrw/sp/xhtml.py +++ b/wrw/sp/xhtml.py @@ -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) - node.attrs[self.attrmap.get(k, k)] = unicode(v) + k = str(k) + node.attrs[self.attrmap.get(k, k)] = str(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") return [buf.getvalue()] diff --git a/wrw/util.py b/wrw/util.py index 4306e9b..f70de0d 100644 --- a/wrw/util.py +++ b/wrw/util.py @@ -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): def wrapper(req): data = form.formdata(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)): + for i in range(len(spec.args) - len(spec.defaults)): 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) @@ -72,12 +81,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 @@ -118,7 +127,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 @@ -130,18 +139,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): @@ -184,7 +193,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__: @@ -196,8 +205,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): @@ -205,7 +213,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__() diff --git a/wrw/wmako.py b/wrw/wmako.py index 13ce342..ef886fb 100644 --- a/wrw/wmako.py +++ b/wrw/wmako.py @@ -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()