Merge branch 'master' into python3
authorFredrik Tolf <fredrik@dolda2000.com>
Mon, 2 Sep 2013 01:26:39 +0000 (03:26 +0200)
committerFredrik Tolf <fredrik@dolda2000.com>
Mon, 2 Sep 2013 01:26:39 +0000 (03:26 +0200)
Conflicts:
wrw/resp.py
wrw/util.py

1  2 
wrw/cookie.py
wrw/form.py
wrw/proto.py
wrw/req.py
wrw/resp.py
wrw/session.py
wrw/sp/cons.py
wrw/util.py

diff --combined wrw/cookie.py
@@@ -1,5 -1,5 +1,5 @@@
 -import Cookie, time
 -import proto
 +import http.cookies, time
 +from . import proto
  
  __all__ = ["cookies", "get", "add"]
  
@@@ -11,10 -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):
      def __contains__(self, name):
          return name in self.bk
  
-     def get(self, name, default = None):
+     def get(self, name, default=None):
          if name not in self.bk:
              return default
          return self.bk[name].value
  
      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):
@@@ -39,7 -39,7 +39,7 @@@
  def cookies(req):
      return req.item(cookiedict)
  
- def get(req, name, default = None):
+ def get(req, name, default=None):
      return cookies(req).get(name, default)
  
  def add(req, name, value, **kw):
diff --combined wrw/form.py
@@@ -1,5 -1,5 +1,5 @@@
  import cgi
 -import proto
 +from . import proto
  
  __all__ = ["formdata"]
  
@@@ -13,7 -13,7 +13,7 @@@ class formwrap(object)
      def __getitem__(self, key):
          return self.cf[key][0]
  
-     def get(self, key, default = ""):
+     def get(self, key, default=""):
          if key in self:
              return self.cf[key][0]
          return default
@@@ -32,7 -32,7 +32,7 @@@
          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 +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 +52,8 @@@
  
      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:
@@@ -78,7 -78,7 +78,7 @@@
                      raise badmultipart("Missing last multipart boundary")
                  self.form.buf += ret
  
-     def read(self, limit = -1):
+     def read(self, limit=-1):
          self.fillbuf(limit)
          if limit >= 0:
              ret = self.buf[:limit]
              self.buf = ""
          return ret
  
-     def readline(self, limit = -1):
+     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
          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:
              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 --combined wrw/proto.py
@@@ -98,14 -98,14 +98,15 @@@ def simpleerror(env, startreq, code, ti
  </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]
  
  def urlq(url):
      ret = ""
+     invalid = "&=#?/\"'"
      for c in url:
-         if c == "&" or c == "=" or c == "#" or c == "?" or c == "/" or (ord(c) <= 32):
+         if c in invalid or (ord(c) <= 32):
              ret += "%%%02X" % ord(c)
          else:
              ret += c
diff --combined wrw/req.py
@@@ -19,9 -19,9 +19,9 @@@ 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 = ""):
+     def get(self, key, default=""):
          if key.lower() in self.dict:
              return self.dict[key.lower()][1]
          return default
@@@ -67,29 -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 +97,7 @@@
                  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)
          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 +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 --combined wrw/resp.py
@@@ -1,5 -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 +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
  
          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):
-         return skelfor(req).error(req, self.message, *self.detail)
+         return skelfor(req).message(req, self.message, *self.detail)
  
  class httperror(usererror):
-     def __init__(self, status, message = None, detail = None):
+     def __init__(self, status, message=None, detail=None):
          if message is None:
              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):
+     bases = {"url": proto.requrl,
+              "script": proto.scripturl,
+              "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]
+         self.base = base
  
      def handle(self, req):
          req.status(self.status, "Redirect")
-         req.ohead["Location"] = proto.appendurl(proto.requrl(req), self.url)
+         req.ohead["Location"] = proto.appendurl(self.bases[self.base](req), self.url)
          req.ohead["Content-Length"] = 0
          return []
  
diff --combined wrw/session.py
@@@ -1,5 -1,5 +1,5 @@@
  import threading, time, pickle, random, os
 -import cookie, env
 +from . import cookie, env
  
  __all__ = ["db", "get"]
  
@@@ -11,12 -11,12 +11,12 @@@ 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
  
  class session(object):
-     def __init__(self, lock, expire = 86400 * 7):
+     def __init__(self, lock, expire=86400 * 7):
          self.id = hexencode(gennonce(16))
          self.dict = {}
          self.lock = lock
@@@ -39,7 -39,7 +39,7 @@@
      def __getitem__(self, key):
          return self.dict[key]
  
-     def get(self, key, default = None):
+     def get(self, key, default=None):
          return self.dict.get(key, default)
  
      def __setitem__(self, key, value):
@@@ -74,7 -74,7 +74,7 @@@
          return "<session %s>" % self.id
  
  class db(object):
-     def __init__(self, backdb = None, cookiename = "wrwsess", path = "/"):
+     def __init__(self, backdb=None, cookiename="wrwsess", path="/"):
          self.live = {}
          self.cookiename = cookiename
          self.path = path
@@@ -86,7 -86,7 +86,7 @@@
      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:
          data = self.backdb[sessid]
          try:
              return pickle.loads(data)
 -        except Exception, e:
 +        except:
              raise KeyError()
  
      def freeze(self, sess):
@@@ -235,7 -235,7 +235,7 @@@ class dirback(object)
          with open(os.path.join(self.path, key), "w") as out:
              out.write(value)
  
- default = env.var(db(backdb = dirback(os.path.join("/tmp", "wrwsess-" + str(os.getuid())))))
+ default = env.var(db(backdb=dirback(os.path.join("/tmp", "wrwsess-" + str(os.getuid())))))
  
  def get(req):
      return default.val.get(req)
diff --combined wrw/sp/cons.py
@@@ -3,18 -3,18 +3,18 @@@ import xml.dom.minido
  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 = []
      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))
  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):
          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):
+     def __init__(self, ns, elcls=element, ctx=None):
          self._ns = ns
          self._elcls = elcls
          if ctx is None: ctx = context()
diff --combined wrw/util.py
@@@ -1,5 -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,26 -7,17 +7,26 @@@
      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):
          data = form.formdata(req)
-         spec = inspect.getargspec(callable)
          args = dict(data.items())
          args["req"] = req
          if not spec.keywords:
              for arg in list(args):
                  if arg not in spec.args:
                      del args[arg]
-         for i in range(len(spec.args) - len(spec.defaults)):
 -        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)
@@@ -71,7 -62,7 +71,7 @@@ class funplex(object)
              return fun
          return dec
  
- def persession(data = None):
+ def persession(data=None):
      def dec(callable):
          def wrapper(req):
              sess = session.get(req)
@@@ -94,12 -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
@@@ -121,7 -112,7 +121,7 @@@ def pregen(callable)
  
  class sessiondata(object):
      @classmethod
-     def get(cls, req, create = True):
+     def get(cls, req, create=True):
          sess = cls.sessdb().get(req)
          with sess.lock:
              try:
  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
          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):
@@@ -206,7 -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__:
          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):
  
      @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__()