From: Fredrik Tolf Date: Sat, 11 Jan 2014 04:46:51 +0000 (+0100) Subject: Merge branch 'master' into python3 X-Git-Url: http://dolda2000.com/gitweb/?p=wrw.git;a=commitdiff_plain;h=6afbb1788a98c5707f88b2c6b027b345f3b053f8;hp=d9ce65914152036450263d9b0103c0b81f35e052 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/auth.py b/wrw/auth.py index 4ae292d..1858214 100644 --- a/wrw/auth.py +++ b/wrw/auth.py @@ -1,9 +1,9 @@ import binascii, hashlib, threading, time -import resp +from . import resp class unauthorized(resp.httperror): def __init__(self, challenge, message=None, detail=None): - super(unauthorized, self).__init__(401, message, detail) + super().__init__(401, message, detail) if isinstance(challenge, str): challenge = [challenge] self.challenge = challenge @@ -11,11 +11,11 @@ class unauthorized(resp.httperror): def handle(self, req): for challenge in self.challenge: req.ohead.add("WWW-Authenticate", challenge) - return super(unauthorized, self).handle(req) + return super().handle(req) class forbidden(resp.httperror): def __init__(self, message=None, detail=None): - super(forbidden, self).__init__(403, message, detail) + super().__init__(403, message, detail) def parsemech(req): h = req.ihead.get("Authorization", None) @@ -31,9 +31,17 @@ def parsebasic(req): if mech != "basic": return None, None try: + data = data.encode("us-ascii") + except UnicodeError: + return None, None + try: raw = binascii.a2b_base64(data) except binascii.Error: return None, None + try: + raw = raw.decode("utf-8") + except UnicodeError: + raw = raw.decode("latin1") p = raw.find(":") if p < 0: return None, None @@ -51,9 +59,9 @@ class basiccache(object): def _obscure(self, nm, pw): dig = hashlib.sha256() - dig.update(self.realm) - dig.update(nm) - dig.update(pw) + dig.update(self.realm.encode("utf-8")) + dig.update(nm.encode("utf-8")) + dig.update(pw.encode("utf-8")) return dig.digest() def check(self, req): @@ -76,7 +84,7 @@ class basiccache(object): with lock: try: ret = self.auth(req, nm, pw) - except forbidden, exc: + except forbidden as exc: with self._lock: self._cache[nm, pwh] = (lock, now, "f", exc) raise diff --git a/wrw/cookie.py b/wrw/cookie.py index 140ba8f..c6e848f 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 666d7a7..7f1066a 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) sys.stderr.flush() raise resp.httperror(500) @@ -38,9 +38,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 0363b0b..c97b0f9 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 3dcf0d7..f03fd2b 100644 --- a/wrw/proto.py +++ b/wrw/proto.py @@ -98,19 +98,20 @@ 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] def urlq(url): - if isinstance(url, unicode): + if isinstance(url, str): url = url.encode("utf-8") ret = "" - invalid = "&=#?/\"'" + invalid = b"&=#?/\"'" for c in url: - if c in invalid or (ord(c) <= 32) or (ord(c) >= 128): - ret += "%%%02X" % ord(c) + if c in invalid or (c <= 32) or (c >= 128): + ret += "%%%02X" % c else: - ret += c + ret += chr(c) return ret class urlerror(ValueError): diff --git a/wrw/req.py b/wrw/req.py index 41fd330..016d2d8 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 7fae787..965df6c 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,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] diff --git a/wrw/session.py b/wrw/session.py index 1e615e3..e7d8581 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 bc9bfa2..0c36458 100644 --- a/wrw/sp/cons.py +++ b/wrw/sp/cons.py @@ -4,18 +4,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 = [] @@ -23,13 +23,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)) @@ -44,10 +44,9 @@ class context(object): def __init__(self): self.nodeconv = {} - self.nodeconv[str] = lambda ob: text(ob, self.charset) - self.nodeconv[unicode] = text + self.nodeconv[bytes] = lambda ob: text(ob, self.charset) + self.nodeconv[str] = text self.nodeconv[int] = text - self.nodeconv[long] = text self.nodeconv[float] = text def nodefrom(self, ob): @@ -66,7 +65,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): diff --git a/wrw/sp/util.py b/wrw/sp/util.py index 2599206..931cbdb 100644 --- a/wrw/sp/util.py +++ b/wrw/sp/util.py @@ -1,6 +1,6 @@ -import itertools, StringIO -from wrw import dispatch -import cons +import itertools, 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) @@ -56,9 +56,9 @@ class formatter(object): self.buf.extend(text.encode(self.charset)) def quotewrite(self, buf): - buf = buf.replace(u'&', u"&") - buf = buf.replace(u'<', u"<") - buf = buf.replace(u'>', u">") + buf = buf.replace('&', "&") + buf = buf.replace('<', "<") + buf = buf.replace('>', ">") self.write(buf) def __iter__(self): @@ -69,53 +69,53 @@ class formatter(object): if ns is None: return el.name else: - return ns + u":" + el.name + return ns + ":" + el.name def attrval(self, v): - qc, qt = (u"'", u"'") if u'"' in v else (u'"', u""") + qc, qt = ("'", "'") if '"' in v else ('"', """) self.write(qc) - v = v.replace(u'&', u"&") - v = v.replace(u'<', u"<") - v = v.replace(u'>', u">") + v = v.replace('&', "&") + v = v.replace('<', "<") + v = v.replace('>', ">") v = v.replace(qc, qt) self.write(v) self.write(qc) def attr(self, k, v): self.write(k) - self.write(u"=") + self.write("=") self.attrval(v) def attrs(self, attrs): for k, v in attrs: - self.write(u" ") + self.write(" ") self.attr(k, v) def inittag(self, el): - self.write(u"<" + self.elname(el)) - attrs = el.attrs.iteritems() + self.write("<" + self.elname(el)) + attrs = el.attrs.items() if self.first: nsnames = [] - for ns, name in self.nsnames.iteritems(): + for ns, name in self.nsnames.items(): if ns is None: if name is not None: raise Exception("null namespace must have null name, not" + name) continue - nsnames.append((u"xmlns" if name is None else (u"xmlns:" + name), ns)) + nsnames.append(("xmlns" if name is None else ("xmlns:" + name), ns)) attrs = itertools.chain(attrs, iter(nsnames)) self.first = False self.attrs(attrs) def starttag(self, el): self.inittag(el) - self.write(u">") + self.write(">") def shorttag(self, el): self.inittag(el) - self.write(u" />") + self.write(" />") def endtag(self, el): - self.write(u"") + self.write("") def text(self, el): self.quotewrite(el) @@ -124,9 +124,9 @@ class formatter(object): self.write(el) def start(self, el): - self.write(u'\n') + self.write('\n') if isinstance(el, cons.doctype): - self.write(u'\n' % (el.rootname, + self.write('\n' % (el.rootname, el.pubid, el.dtdid)) self.first = True @@ -150,7 +150,7 @@ class formatter(object): elif ev == "$": self.end(el) - def next(self): + def __next__(self): if self.src is None: raise StopIteration() try: @@ -159,8 +159,8 @@ class formatter(object): self.src = None ev, el = "$", None self.handle(ev, el) - ret = str(self.buf) - self.buf[:] = "" + ret = bytes(self.buf) + self.buf[:] = b"" return ret def nsname(self, el): @@ -170,7 +170,7 @@ class formatter(object): return ret if el.ns is None: return None - ret = u"n" + unicode(self.nextns) + ret = "n" + str(self.nextns) self.nextns += 1 return ret @@ -215,16 +215,16 @@ class formatter(object): @classmethod def format(cls, root, **kw): - buf = StringIO.StringIO() + buf = io.BytesIO() cls.output(buf, root, **kw) return buf.getvalue() class indenter(formatter): - def __init__(self, indent=u" ", *args, **kw): - super(indenter, self).__init__(*args, **kw) + def __init__(self, indent=" ", *args, **kw): + super().__init__(*args, **kw) self.indent = indent self.col = 0 - self.curind = u"" + self.curind = "" self.atbreak = True self.inline = False self.stack = [] @@ -232,11 +232,11 @@ class indenter(formatter): self.lastendbr = True def write(self, text): - lines = text.split(u"\n") + lines = text.split("\n") if len(lines) > 1: for ln in lines[:-1]: self.buf.extend(ln.encode(self.charset)) - self.buf.extend("\n") + self.buf.extend(b"\n") self.col = 0 self.buf.extend(lines[-1].encode(self.charset)) self.col += len(lines[-1]) @@ -244,7 +244,7 @@ class indenter(formatter): def br(self): if not self.atbreak: - self.buf.extend((u"\n" + self.curind).encode(self.charset)) + self.buf.extend(("\n" + self.curind).encode(self.charset)) self.col = 0 self.atbreak = True @@ -270,12 +270,12 @@ class indenter(formatter): self.push(el) self.inline = self.inline or self.inlinep(el) self.curind += self.indent - super(indenter, self).starttag(el) + super().starttag(el) def shorttag(self, el): if not self.inline: self.br() - super(indenter, self).shorttag(el) + super().shorttag(el) def endtag(self, el): il = self.inline @@ -285,33 +285,33 @@ class indenter(formatter): else: self.br() self.lastendbr = True - super(indenter, self).endtag(el) + super().endtag(el) def start(self, el): - super(indenter, self).start(el) + super().start(el) self.atbreak = True def end(self, el): self.br() def handle(self, ev, el): - super(indenter, self).handle(ev, el) + super().handle(ev, el) self.last = ev, el class textindenter(indenter): maxcol = 70 def text(self, el): - left = unicode(el) + left = str(el) while True: if len(left) + self.col > self.maxcol: bp = max(self.maxcol - self.col, 0) - for i in xrange(bp, -1, -1): + for i in range(bp, -1, -1): if left[i].isspace(): while i > 0 and left[i - 1].isspace(): i -= 1 break else: - for i in xrange(bp + 1, len(left)): + for i in range(bp + 1, len(left)): if left[i].isspace(): break else: @@ -333,7 +333,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 a2c1eb1..eb82f57 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) - 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,10 +38,10 @@ 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 = {"br", "hr", "img", "input", "meta", "link"} def shorttag(self, el): if el.name in self.allowshort: - super(htmlformatter, self).shorttag(el) + super().shorttag(el) else: self.handle(">", el) self.handle("<", el) @@ -52,7 +52,7 @@ class htmlindenter(util.textindenter, 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) diff --git a/wrw/util.py b/wrw/util.py index 7459b6d..79a8240 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): 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__() 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()