Robert Brewer avatar Robert Brewer committed d66e2f4

More version numbers bumped. Some sync between wsgiserver2 and 3.

Comments (0)

Files changed (5)

cherrypy/__init__.py

 http://www.cherrypy.org/wiki/CherryPySpec
 """
 
-__version__ = "3.2.0"
+__version__ = "3.2.1"
 
 from cherrypy._cpcompat import urljoin as _urljoin, urlencode as _urlencode
 from cherrypy._cpcompat import basestring, unicodestr, set

cherrypy/lib/cptools.py

 #                                Tool code                                #
 
 def allow(methods=None, debug=False):
-    """Raise 405 if request.method not in methods (default GET/HEAD).
+    """Raise 405 if request.method not in methods (default ['GET', 'HEAD']).
     
     The given methods are case-insensitive, and may be in any order.
     If only one method is allowed, you may supply a single string;

cherrypy/wsgiserver/__init__.py

+__all__ = ['HTTPRequest', 'HTTPConnection', 'HTTPServer',
+           'SizeCheckWrapper', 'KnownLengthRFile', 'ChunkedRFile',
+           'MaxSizeExceeded', 'NoSSLError', 'FatalSSLAlert',
+           'WorkerThread', 'ThreadPool', 'SSLAdapter',
+           'CherryPyWSGIServer',
+           'Gateway', 'WSGIGateway', 'WSGIGateway_10', 'WSGIGateway_u0',
+           'WSGIPathInfoDispatcher']
+
 import sys
 if sys.version_info < (3, 0):
     from wsgiserver2 import *

cherrypy/wsgiserver/wsgiserver2.py

                     return
 """
 
-CRLF = '\r\n'
+__all__ = ['HTTPRequest', 'HTTPConnection', 'HTTPServer',
+           'SizeCheckWrapper', 'KnownLengthRFile', 'ChunkedRFile',
+           'CP_fileobject',
+           'MaxSizeExceeded', 'NoSSLError', 'FatalSSLAlert',
+           'WorkerThread', 'ThreadPool', 'SSLAdapter',
+           'CherryPyWSGIServer',
+           'Gateway', 'WSGIGateway', 'WSGIGateway_10', 'WSGIGateway_u0',
+           'WSGIPathInfoDispatcher']
+
 import os
-import Queue
+try:
+    import queue
+except:
+    import Queue as queue
 import re
-quoted_slash = re.compile("(?i)%2F")
 import rfc822
 import socket
 import sys
 from urlparse import urlparse
 import warnings
 
+if sys.version_info >= (3, 0):
+    bytestr = bytes
+    unicodestr = str
+    basestring = (bytes, str)
+    def ntob(n, encoding='ISO-8859-1'):
+        """Return the given native string as a byte string in the given encoding."""
+        # In Python 3, the native string type is unicode
+        return n.encode(encoding)
+else:
+    bytestr = str
+    unicodestr = unicode
+    basestring = basestring
+    def ntob(n, encoding='ISO-8859-1'):
+        """Return the given native string as a byte string in the given encoding."""
+        # In Python 2, the native string type is bytes. Assume it's already
+        # in the given encoding, which for ISO-8859-1 is almost always what
+        # was intended.
+        return n
+
+LF = ntob('\n')
+CRLF = ntob('\r\n')
+TAB = ntob('\t')
+SPACE = ntob(' ')
+COLON = ntob(':')
+SEMICOLON = ntob(';')
+EMPTY = ntob('')
+NUMBER_SIGN = ntob('#')
+QUESTION_MARK = ntob('?')
+ASTERISK = ntob('*')
+FORWARD_SLASH = ntob('/')
+quoted_slash = re.compile(ntob("(?i)%2F"))
+
 import errno
 
 def plat_specific_errors(*errnames):
     errno_names = dir(errno)
     nums = [getattr(errno, k) for k in errnames if k in errno_names]
     # de-dupe the list
-    return dict.fromkeys(nums).keys()
+    return list(dict.fromkeys(nums).keys())
 
 socket_error_eintr = plat_specific_errors("EINTR", "WSAEINTR")
 
 socket_errors_nonblocking = plat_specific_errors(
     'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
 
-comma_separated_headers = ['Accept', 'Accept-Charset', 'Accept-Encoding',
-    'Accept-Language', 'Accept-Ranges', 'Allow', 'Cache-Control',
-    'Connection', 'Content-Encoding', 'Content-Language', 'Expect',
-    'If-Match', 'If-None-Match', 'Pragma', 'Proxy-Authenticate', 'TE',
-    'Trailer', 'Transfer-Encoding', 'Upgrade', 'Vary', 'Via', 'Warning',
-    'WWW-Authenticate']
+comma_separated_headers = [ntob(h) for h in
+    ['Accept', 'Accept-Charset', 'Accept-Encoding',
+     'Accept-Language', 'Accept-Ranges', 'Allow', 'Cache-Control',
+     'Connection', 'Content-Encoding', 'Content-Language', 'Expect',
+     'If-Match', 'If-None-Match', 'Pragma', 'Proxy-Authenticate', 'TE',
+     'Trailer', 'Transfer-Encoding', 'Upgrade', 'Vary', 'Via', 'Warning',
+     'WWW-Authenticate']]
 
 
 import logging
         if not line.endswith(CRLF):
             raise ValueError("HTTP requires CRLF terminators")
         
-        if line[0] in ' \t':
+        if line[0] in (SPACE, TAB):
             # It's a continuation line.
             v = line.strip()
         else:
             try:
-                k, v = line.split(":", 1)
+                k, v = line.split(COLON, 1)
             except ValueError:
                 raise ValueError("Illegal header line.")
             # TODO: what about TE and WWW-Authenticate?
             res.append(data)
             # See http://www.cherrypy.org/ticket/421
             if len(data) < 256 or data[-1:] == "\n":
-                return ''.join(res)
+                return EMPTY.join(res)
     
     def readlines(self, sizehint=0):
         # Shamelessly stolen from StringIO
     def __iter__(self):
         return self
     
+    def __next__(self):
+        data = next(self.rfile)
+        self.bytes_read += len(data)
+        self._check_length()
+        return data
+    
     def next(self):
         data = self.rfile.next()
         self.bytes_read += len(data)
         self.rfile = rfile
         self.maxlen = maxlen
         self.bytes_read = 0
-        self.buffer = ''
+        self.buffer = EMPTY
         self.bufsize = bufsize
         self.closed = False
     
         if self.maxlen and self.bytes_read > self.maxlen:
             raise MaxSizeExceeded("Request Entity Too Large", self.maxlen)
         
-        line = line.strip().split(";", 1)
+        line = line.strip().split(SEMICOLON, 1)
         
         try:
             chunk_size = line.pop(0)
                  "got " + repr(crlf) + ")")
     
     def read(self, size=None):
-        data = ''
+        data = EMPTY
         while True:
             if size and len(data) >= size:
                 return data
                 data += self.buffer
     
     def readline(self, size=None):
-        data = ''
+        data = EMPTY
         while True:
             if size and len(data) >= size:
                 return data
                     # EOF
                     return data
             
-            newline_pos = self.buffer.find('\n')
+            newline_pos = self.buffer.find(LF)
             if size:
                 if newline_pos == -1:
                     remaining = size - len(data)
         
         self.ready = False
         self.started_request = False
-        self.scheme = "http"
+        self.scheme = ntob("http")
         if self.server.ssl_adapter is not None:
-            self.scheme = "https"
+            self.scheme = ntob("https")
         # Use the lowest-common protocol in case read_request_line errors.
         self.response_protocol = 'HTTP/1.0'
         self.inheaders = {}
             return
         
         try:
-            method, uri, req_protocol = request_line.strip().split(" ", 2)
+            method, uri, req_protocol = request_line.strip().split(SPACE, 2)
             rp = int(req_protocol[5]), int(req_protocol[7])
         except (ValueError, IndexError):
             self.simple_response("400 Bad Request", "Malformed Request-Line")
         
         # uri may be an abs_path (including "http://host.domain.tld");
         scheme, authority, path = self.parse_request_uri(uri)
-        if '#' in path:
+        if NUMBER_SIGN in path:
             self.simple_response("400 Bad Request",
                                  "Illegal #fragment in Request-URI.")
             return
         if scheme:
             self.scheme = scheme
         
-        qs = ''
-        if '?' in path:
-            path, qs = path.split('?', 1)
+        qs = EMPTY
+        if QUESTION_MARK in path:
+            path, qs = path.split(QUESTION_MARK, 1)
         
         # Unquote the path+params (e.g. "/this%20path" -> "/this path").
         # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
         # Therefore, "/this%2Fpath" becomes "/this%2Fpath", not "/this/path".
         try:
             atoms = [unquote(x) for x in quoted_slash.split(path)]
-        except ValueError, ex:
+        except ValueError:
+            ex = sys.exc_info()[1]
             self.simple_response("400 Bad Request", ex.args[0])
             return
         path = "%2F".join(atoms)
         # then all the http headers
         try:
             read_headers(self.rfile, self.inheaders)
-        except ValueError, ex:
+        except ValueError:
+            ex = sys.exc_info()[1]
             self.simple_response("400 Bad Request", ex.args[0])
             return False
         
             msg = self.server.protocol + " 100 Continue\r\n\r\n"
             try:
                 self.conn.wfile.sendall(msg)
-            except socket.error, x:
+            except socket.error:
+                x = sys.exc_info()[1]
                 if x.args[0] not in socket_errors_to_ignore:
                     raise
         return True
             segment       = *pchar *( ";" param )
             param         = *pchar
         """
-        if uri == "*":
+        if uri == ASTERISK:
             return None, None, uri
         
         i = uri.find('://')
-        if i > 0 and '?' not in uri[:i]:
+        if i > 0 and QUESTION_MARK not in uri[:i]:
             # An absoluteURI.
             # If there's a scheme (and it must be http or https), then:
             # http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
             scheme, remainder = uri[:i].lower(), uri[i + 3:]
-            authority, path = remainder.split("/", 1)
-            path = '/' + path
+            authority, path = remainder.split(FORWARD_SLASH, 1)
+            path = FORWARD_SLASH + path
             return scheme, authority, path
         
-        if uri.startswith('/'):
+        if uri.startswith(FORWARD_SLASH):
             # An abs_path.
             return None, None, uri
         else:
     def simple_response(self, status, msg=""):
         """Write a simple response back to the client."""
         status = str(status)
-        buf = [self.server.protocol + " " +
+        buf = [self.server.protocol + SPACE +
                status + CRLF,
                "Content-Length: %s\r\n" % len(msg),
                "Content-Type: text/plain\r\n"]
         
         buf.append(CRLF)
         if msg:
-            if isinstance(msg, unicode):
+            if isinstance(msg, unicodestr):
                 msg = msg.encode("ISO-8859-1")
             buf.append(msg)
         
         try:
             self.conn.wfile.sendall("".join(buf))
-        except socket.error, x:
+        except socket.error:
+            x = sys.exc_info()[1]
             if x.args[0] not in socket_errors_to_ignore:
                 raise
     
         """Write unbuffered data to the client."""
         if self.chunked_write and chunk:
             buf = [hex(len(chunk))[2:], CRLF, chunk, CRLF]
-            self.conn.wfile.sendall("".join(buf))
+            self.conn.wfile.sendall(EMPTY.join(buf))
         else:
             self.conn.wfile.sendall(chunk)
     
         if "server" not in hkeys:
             self.outheaders.append(("Server", self.server.server_name))
         
-        buf = [self.server.protocol + " " + self.status + CRLF]
+        buf = [self.server.protocol + SPACE + self.status + CRLF]
         for k, v in self.outheaders:
-            buf.append(k + ": " + v + CRLF)
+            buf.append(k + COLON + SPACE + v + CRLF)
         buf.append(CRLF)
-        self.conn.wfile.sendall("".join(buf))
+        self.conn.wfile.sendall(EMPTY.join(buf))
 
 
 class NoSSLError(Exception):
                 req.respond()
                 if req.close_connection:
                     return
-        except socket.error, e:
+        except socket.error:
+            e = sys.exc_info()[1]
             errnum = e.args[0]
             # sadly SSL sockets return a different (longer) time out string
             if errnum == 'timed out' or errnum == 'The read operation timed out':
                         self.work_time += time.time() - self.start_time
                         self.start_time = None
                     self.conn = None
-        except (KeyboardInterrupt, SystemExit), exc:
+        except (KeyboardInterrupt, SystemExit):
+            exc = sys.exc_info()[1]
             self.server.interrupt = exc
 
 
 class ThreadPool(object):
-    """A Request Queue for the CherryPyWSGIServer which pools threads.
+    """A Request Queue for an HTTPServer which pools threads.
     
     ThreadPool objects must provide min, get(), put(obj), start()
     and stop(timeout) attributes.
         self.min = min
         self.max = max
         self._threads = []
-        self._queue = Queue.Queue()
+        self._queue = queue.Queue()
         self.get = self._queue.get
     
     def start(self):
                 except (AssertionError,
                         # Ignore repeated Ctrl-C.
                         # See http://www.cherrypy.org/ticket/691.
-                        KeyboardInterrupt), exc1:
+                        KeyboardInterrupt):
                     pass
     
     def _get_qsize(self):
     timeout = 10
     """The timeout in seconds for accepted connections (default 10)."""
     
-    version = "CherryPy/3.2.0"
+    version = "CherryPy/3.2.1"
     """A version string for the HTTPServer."""
     
     software = None
             except: pass
             
             # So everyone can access the socket...
-            try: os.chmod(self.bind_addr, 0777)
+            try: os.chmod(self.bind_addr, 511) # 0777
             except: pass
             
             info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
                            "Content-Type: text/plain\r\n\r\n",
                            msg]
                     
-                    wfile = CP_fileobject(s, "wb", DEFAULT_BUFFER_SIZE)
+                    wfile = makefile(s, "wb", DEFAULT_BUFFER_SIZE)
                     try:
                         wfile.sendall("".join(buf))
-                    except socket.error, x:
+                    except socket.error:
+                        x = sys.exc_info()[1]
                         if x.args[0] not in socket_errors_to_ignore:
                             raise
                     return
             # notice keyboard interrupts on Win32, which don't interrupt
             # accept() by default
             return
-        except socket.error, x:
+        except socket.error:
+            x = sys.exc_info()[1]
             if self.stats['Enabled']:
                 self.stats['Socket Errors'] += 1
             if x.args[0] in socket_error_eintr:
                 # Touch our own socket to make accept() return immediately.
                 try:
                     host, port = sock.getsockname()[:2]
-                except socket.error, x:
+                except socket.error:
+                    x = sys.exc_info()[1]
                     if x.args[0] not in socket_errors_to_ignore:
                         # Changed to use error code and not message
                         # See http://www.cherrypy.org/ticket/860.
 
 
 class Gateway(object):
+    """A base class to interface HTTPServer with other systems, such as WSGI."""
     
     def __init__(self, req):
         self.req = req
     
     def respond(self):
+        """Process the current request. Must be overridden in a subclass."""
         raise NotImplemented
 
 
     }
 
 def get_ssl_adapter_class(name='pyopenssl'):
+    """Return an SSL adapter class for the given name."""
     adapter = ssl_adapters[name.lower()]
     if isinstance(adapter, basestring):
         last_dot = adapter.rfind(".")
 
 
 class CherryPyWSGIServer(HTTPServer):
+    """A subclass of HTTPServer which calls a WSGI application."""
     
     wsgi_version = (1, 0)
+    """The version of WSGI to produce."""
     
     def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
                  max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):
 
 
 class WSGIGateway(Gateway):
+    """A base class to interface HTTPServer with WSGI."""
     
     def __init__(self, req):
         self.req = req
         raise NotImplemented
     
     def respond(self):
+        """Process the current request."""
         response = self.req.server.wsgi_app(self.env, self.start_response)
         try:
             for chunk in response:
                 # a NON-EMPTY string, or upon the application's first
                 # invocation of the write() callable." (PEP 333)
                 if chunk:
-                    if isinstance(chunk, unicode):
+                    if isinstance(chunk, unicodestr):
                         chunk = chunk.encode('ISO-8859-1')
                     self.write(chunk)
         finally:
         
         self.req.status = status
         for k, v in headers:
-            if not isinstance(k, str):
+            if not isinstance(k, bytestr):
                 raise TypeError("WSGI response header key %r is not a byte string." % k)
-            if not isinstance(v, str):
+            if not isinstance(v, bytestr):
                 raise TypeError("WSGI response header value %r is not a byte string." % v)
             if k.lower() == 'content-length':
                 self.remaining_bytes_out = int(v)
 
 
 class WSGIGateway_10(WSGIGateway):
+    """A Gateway class to interface HTTPServer with WSGI 1.0.x."""
     
     def get_environ(self):
         """Return a new environ dict targeting the given wsgi.version"""
 
 
 class WSGIGateway_u0(WSGIGateway_10):
+    """A Gateway class to interface HTTPServer with WSGI u.0.
+    
+    WSGI u.0 is an experimental protocol, which uses unicode for keys and values
+    in both Python 2 and Python 3.
+    """
     
     def get_environ(self):
         """Return a new environ dict targeting the given wsgi.version"""
     
     def __init__(self, apps):
         try:
-            apps = apps.items()
+            apps = list(apps.items())
         except AttributeError:
             pass
         

cherrypy/wsgiserver/wsgiserver3.py

                     return
 """
 
-CRLF = b'\r\n'
+__all__ = ['HTTPRequest', 'HTTPConnection', 'HTTPServer',
+           'SizeCheckWrapper', 'KnownLengthRFile', 'ChunkedRFile',
+           'CP_makefile',
+           'MaxSizeExceeded', 'NoSSLError', 'FatalSSLAlert',
+           'WorkerThread', 'ThreadPool', 'SSLAdapter',
+           'CherryPyWSGIServer',
+           'Gateway', 'WSGIGateway', 'WSGIGateway_10', 'WSGIGateway_u0',
+           'WSGIPathInfoDispatcher']
+
 import os
-import queue
+try:
+    import queue
+except:
+    import Queue as queue
 import re
-quoted_slash = re.compile(b"(?i)%2F")
 import email.utils
 import socket
 import sys
 from urllib.parse import scheme_chars
 import warnings
 
+if sys.version_info >= (3, 0):
+    bytestr = bytes
+    unicodestr = str
+    basestring = (bytes, str)
+    def ntob(n, encoding='ISO-8859-1'):
+        """Return the given native string as a byte string in the given encoding."""
+        # In Python 3, the native string type is unicode
+        return n.encode(encoding)
+else:
+    bytestr = str
+    unicodestr = unicode
+    basestring = basestring
+    def ntob(n, encoding='ISO-8859-1'):
+        """Return the given native string as a byte string in the given encoding."""
+        # In Python 2, the native string type is bytes. Assume it's already
+        # in the given encoding, which for ISO-8859-1 is almost always what
+        # was intended.
+        return n
+
+LF = ntob('\n')
+CRLF = ntob('\r\n')
+TAB = ntob('\t')
+SPACE = ntob(' ')
+COLON = ntob(':')
+SEMICOLON = ntob(';')
+EMPTY = ntob('')
+NUMBER_SIGN = ntob('#')
+QUESTION_MARK = ntob('?')
+ASTERISK = ntob('*')
+FORWARD_SLASH = ntob('/')
+quoted_slash = re.compile(ntob("(?i)%2F"))
+
 import errno
 
 def plat_specific_errors(*errnames):
 socket_errors_nonblocking = plat_specific_errors(
     'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
 
-comma_separated_headers = [b'Accept', b'Accept-Charset', b'Accept-Encoding',
-    b'Accept-Language', b'Accept-Ranges', b'Allow', b'Cache-Control',
-    b'Connection', b'Content-Encoding', b'Content-Language', b'Expect',
-    b'If-Match', b'If-None-Match', b'Pragma', b'Proxy-Authenticate', b'TE',
-    b'Trailer', b'Transfer-Encoding', b'Upgrade', b'Vary', b'Via', b'Warning',
-    b'WWW-Authenticate']
+comma_separated_headers = [ntob(h) for h in
+    ['Accept', 'Accept-Charset', 'Accept-Encoding',
+     'Accept-Language', 'Accept-Ranges', 'Allow', 'Cache-Control',
+     'Connection', 'Content-Encoding', 'Content-Language', 'Expect',
+     'If-Match', 'If-None-Match', 'Pragma', 'Proxy-Authenticate', 'TE',
+     'Trailer', 'Transfer-Encoding', 'Upgrade', 'Vary', 'Via', 'Warning',
+     'WWW-Authenticate']]
 
 
 import logging
         if not line.endswith(CRLF):
             raise ValueError("HTTP requires CRLF terminators")
         
-        if line[0] in b' \t':
+        if line[0] in (SPACE, TAB):
             # It's a continuation line.
             v = line.strip()
         else:
             try:
-                k, v = line.split(b":", 1)
+                k, v = line.split(COLON, 1)
             except ValueError:
                 raise ValueError("Illegal header line.")
             # TODO: what about TE and WWW-Authenticate?
             res.append(data)
             # See http://www.cherrypy.org/ticket/421
             if len(data) < 256 or data[-1:] == "\n":
-                return b''.join(res)
+                return EMPTY.join(res)
     
     def readlines(self, sizehint=0):
         # Shamelessly stolen from StringIO
         self.bytes_read += len(data)
         self._check_length()
         return data
+    
+    def next(self):
+        data = self.rfile.next()
+        self.bytes_read += len(data)
+        self._check_length()
+        return data
 
 
 class KnownLengthRFile(object):
         self.rfile = rfile
         self.maxlen = maxlen
         self.bytes_read = 0
-        self.buffer = b''
+        self.buffer = EMPTY
         self.bufsize = bufsize
         self.closed = False
     
         if self.maxlen and self.bytes_read > self.maxlen:
             raise MaxSizeExceeded("Request Entity Too Large", self.maxlen)
         
-        line = line.strip().split(b";", 1)
+        line = line.strip().split(SEMICOLON, 1)
         
         try:
             chunk_size = line.pop(0)
                  "got " + repr(crlf) + ")")
     
     def read(self, size=None):
-        data = b''
+        data = EMPTY
         while True:
             if size and len(data) >= size:
                 return data
                 data += self.buffer
     
     def readline(self, size=None):
-        data = b''
+        data = EMPTY
         while True:
             if size and len(data) >= size:
                 return data
                     # EOF
                     return data
             
-            newline_pos = self.buffer.find(b'\n')
+            newline_pos = self.buffer.find(LF)
             if size:
                 if newline_pos == -1:
                     remaining = size - len(data)
         
         self.ready = False
         self.started_request = False
-        self.scheme = b"http"
+        self.scheme = ntob("http")
         if self.server.ssl_adapter is not None:
-            self.scheme = b"https"
+            self.scheme = ntob("https")
         # Use the lowest-common protocol in case read_request_line errors.
         self.response_protocol = 'HTTP/1.0'
         self.inheaders = {}
             return
         
         try:
-            method, uri, req_protocol = request_line.strip().split(b" ", 2)
+            method, uri, req_protocol = request_line.strip().split(SPACE, 2)
             # The [x:y] slicing is necessary for byte strings to avoid getting ord's
             rp = int(req_protocol[5:6]), int(req_protocol[7:8])
         except ValueError:
         
         # uri may be an abs_path (including "http://host.domain.tld");
         scheme, authority, path = self.parse_request_uri(uri)
-        if b'#' in path:
+        if NUMBER_SIGN in path:
             self.simple_response("400 Bad Request",
                                  "Illegal #fragment in Request-URI.")
             return
         if scheme:
             self.scheme = scheme
         
-        qs = b''
-        if b'?' in path:
-            path, qs = path.split(b'?', 1)
+        qs = EMPTY
+        if QUESTION_MARK in path:
+            path, qs = path.split(QUESTION_MARK, 1)
         
         # Unquote the path+params (e.g. "/this%20path" -> "/this path").
         # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
         # Therefore, "/this%2Fpath" becomes "/this%2Fpath", not "/this/path".
         try:
             atoms = [self.unquote_bytes(x) for x in quoted_slash.split(path)]
-        except ValueError as ex:
+        except ValueError:
+            ex = sys.exc_info()[1]
             self.simple_response("400 Bad Request", ex.args[0])
             return
         path = b"%2F".join(atoms)
         # then all the http headers
         try:
             read_headers(self.rfile, self.inheaders)
-        except ValueError as ex:
+        except ValueError:
+            ex = sys.exc_info()[1]
             self.simple_response("400 Bad Request", ex.args[0])
             return False
         
             msg = self.server.protocol.encode('ascii') + b" 100 Continue\r\n\r\n"
             try:
                 self.conn.wfile.write(msg)
-            except socket.error as x:
+            except socket.error:
+                x = sys.exc_info()[1]
                 if x.args[0] not in socket_errors_to_ignore:
                     raise
         return True
             segment       = *pchar *( ";" param )
             param         = *pchar
         """
-        if uri == b"*":
+        if uri == ASTERISK:
             return None, None, uri
 
         scheme, sep, remainder = uri.partition(b'://')
-        if sep and b'?' not in scheme:
+        if sep and QUESTION_MARK not in scheme:
             # An absoluteURI.
             # If there's a scheme (and it must be http or https), then:
             # http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
-            authority, path_a, path_b = remainder.partition(b'/')
+            authority, path_a, path_b = remainder.partition(FORWARD_SLASH)
             return scheme.lower(), authority, path_a+path_b
 
-        if uri.startswith(b'/'):
+        if uri.startswith(FORWARD_SLASH):
             # An abs_path.
             return None, None, uri
         else:
     def simple_response(self, status, msg=""):
         """Write a simple response back to the client."""
         status = str(status)
-        buf = [bytes(self.server.protocol, "ascii") + b" " +
+        buf = [bytes(self.server.protocol, "ascii") + SPACE +
                bytes(status, "ISO-8859-1") + CRLF,
                bytes("Content-Length: %s\r\n" % len(msg), "ISO-8859-1"),
                b"Content-Type: text/plain\r\n"]
         
         buf.append(CRLF)
         if msg:
-            if isinstance(msg, str):
+            if isinstance(msg, unicodestr):
                 msg = msg.encode("ISO-8859-1")
             buf.append(msg)
         
         try:
             self.conn.wfile.write(b"".join(buf))
-        except socket.error as x:
+        except socket.error:
+            x = sys.exc_info()[1]
             if x.args[0] not in socket_errors_to_ignore:
                 raise
     
         """Write unbuffered data to the client."""
         if self.chunked_write and chunk:
             buf = [bytes(hex(len(chunk)), 'ASCII')[2:], CRLF, chunk, CRLF]
-            self.conn.wfile.write(b"".join(buf))
+            self.conn.wfile.write(EMPTY.join(buf))
         else:
             self.conn.wfile.write(chunk)
     
             self.outheaders.append(
                 (b"Server", self.server.server_name.encode('ISO-8859-1')))
         
-        buf = [self.server.protocol.encode('ascii') + b" " + self.status + CRLF]
+        buf = [self.server.protocol.encode('ascii') + SPACE + self.status + CRLF]
         for k, v in self.outheaders:
-            buf.append(k + b": " + v + CRLF)
+            buf.append(k + COLON + SPACE + v + CRLF)
         buf.append(CRLF)
-        self.conn.wfile.write(b"".join(buf))
+        self.conn.wfile.write(EMPTY.join(buf))
 
 
 class NoSSLError(Exception):
                 req.respond()
                 if req.close_connection:
                     return
-        except socket.error as e:
+        except socket.error:
+            e = sys.exc_info()[1]
             errnum = e.args[0]
             # sadly SSL sockets return a different (longer) time out string
             if errnum == 'timed out' or errnum == 'The read operation timed out':
                         self.work_time += time.time() - self.start_time
                         self.start_time = None
                     self.conn = None
-        except (KeyboardInterrupt, SystemExit) as exc:
+        except (KeyboardInterrupt, SystemExit):
+            exc = sys.exc_info()[1]
             self.server.interrupt = exc
 
 
 class ThreadPool(object):
-    """A Request Queue for the CherryPyWSGIServer which pools threads.
+    """A Request Queue for an HTTPServer which pools threads.
     
     ThreadPool objects must provide min, get(), put(obj), start()
     and stop(timeout) attributes.
                 except (AssertionError,
                         # Ignore repeated Ctrl-C.
                         # See http://www.cherrypy.org/ticket/691.
-                        KeyboardInterrupt) as exc1:
+                        KeyboardInterrupt):
                     pass
     
     def _get_qsize(self):
     timeout = 10
     """The timeout in seconds for accepted connections (default 10)."""
     
-    version = "CherryPy/3.2.0"
+    version = "CherryPy/3.2.1"
     """A version string for the HTTPServer."""
     
     software = None
             self.software = "%s Server" % self.version
         
         # Select the appropriate socket
-        if isinstance(self.bind_addr, str):
+        if isinstance(self.bind_addr, basestring):
             # AF_UNIX socket
             
             # So we can reuse the socket...
             except: pass
             
             # So everyone can access the socket...
-            try: os.chmod(self.bind_addr, 0o777)
+            try: os.chmod(self.bind_addr, 511) # 0777
             except: pass
             
             info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
         
         # If listening on the IPV6 any address ('::' = IN6ADDR_ANY),
         # activate dual-stack. See http://www.cherrypy.org/ticket/871.
-        if (family == socket.AF_INET6
+        if (hasattr(socket, 'AF_INET6') and family == socket.AF_INET6
             and self.bind_addr[0] in ('::', '::0', '::0.0.0.0')):
             try:
                 self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
                            "Content-Type: text/plain\r\n\r\n",
                            msg]
                     
-                    wfile = CP_makefile(s, "wb", DEFAULT_BUFFER_SIZE)
+                    wfile = makefile(s, "wb", DEFAULT_BUFFER_SIZE)
                     try:
                         wfile.write("".join(buf).encode('ISO-8859-1'))
-                    except socket.error as x:
+                    except socket.error:
+                        x = sys.exc_info()[1]
                         if x.args[0] not in socket_errors_to_ignore:
                             raise
                     return
             
             conn = self.ConnectionClass(self, s, makefile)
             
-            if not isinstance(self.bind_addr, str):
+            if not isinstance(self.bind_addr, basestring):
                 # optional values
                 # Until we do DNS lookups, omit REMOTE_HOST
                 if addr is None: # sometimes this can happen
             # notice keyboard interrupts on Win32, which don't interrupt
             # accept() by default
             return
-        except socket.error as x:
+        except socket.error:
+            x = sys.exc_info()[1]
             if self.stats['Enabled']:
                 self.stats['Socket Errors'] += 1
             if x.args[0] in socket_error_eintr:
         
         sock = getattr(self, "socket", None)
         if sock:
-            if not isinstance(self.bind_addr, str):
+            if not isinstance(self.bind_addr, basestring):
                 # Touch our own socket to make accept() return immediately.
                 try:
                     host, port = sock.getsockname()[:2]
-                except socket.error as x:
+                except socket.error:
+                    x = sys.exc_info()[1]
                     if x.args[0] not in socket_errors_to_ignore:
                         # Changed to use error code and not message
                         # See http://www.cherrypy.org/ticket/860.
 
 
 class Gateway(object):
+    """A base class to interface HTTPServer with other systems, such as WSGI."""
     
     def __init__(self, req):
         self.req = req
     
     def respond(self):
+        """Process the current request. Must be overridden in a subclass."""
         raise NotImplemented
 
 
     }
 
 def get_ssl_adapter_class(name='builtin'):
+    """Return an SSL adapter class for the given name."""
     adapter = ssl_adapters[name.lower()]
-    if isinstance(adapter, str):
+    if isinstance(adapter, basestring):
         last_dot = adapter.rfind(".")
         attr_name = adapter[last_dot + 1:]
         mod_path = adapter[:last_dot]
 
 
 class CherryPyWSGIServer(HTTPServer):
+    """A subclass of HTTPServer which calls a WSGI application."""
     
     wsgi_version = (1, 0)
+    """The version of WSGI to produce."""
     
     def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
                  max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):
 
 
 class WSGIGateway(Gateway):
+    """A base class to interface HTTPServer with WSGI."""
     
     def __init__(self, req):
         self.req = req
         raise NotImplemented
     
     def respond(self):
+        """Process the current request."""
         response = self.req.server.wsgi_app(self.env, self.start_response)
         try:
             for chunk in response:
                 # a NON-EMPTY string, or upon the application's first
                 # invocation of the write() callable." (PEP 333)
                 if chunk:
-                    if isinstance(chunk, str):
+                    if isinstance(chunk, unicodestr):
                         chunk = chunk.encode('ISO-8859-1')
                     self.write(chunk)
         finally:
         
         self.req.status = status
         for k, v in headers:
-            if not isinstance(k, bytes):
+            if not isinstance(k, bytestr):
                 raise TypeError("WSGI response header key %r is not a byte string." % k)
-            if not isinstance(v, bytes):
+            if not isinstance(v, bytestr):
                 raise TypeError("WSGI response header value %r is not a byte string." % v)
             if k.lower() == b'content-length':
                 self.remaining_bytes_out = int(v)
 
 
 class WSGIGateway_10(WSGIGateway):
+    """A Gateway class to interface HTTPServer with WSGI 1.0.x."""
     
     def get_environ(self):
         """Return a new environ dict targeting the given wsgi.version"""
             'wsgi.version': (1, 0),
             }
         
-        if isinstance(req.server.bind_addr, str):
+        if isinstance(req.server.bind_addr, basestring):
             # AF_UNIX. This isn't really allowed by WSGI, which doesn't
             # address unix domain sockets. But it's better than nothing.
             env["SERVER_PORT"] = ""
 
 
 class WSGIGateway_u0(WSGIGateway_10):
+    """A Gateway class to interface HTTPServer with WSGI u.0.
+    
+    WSGI u.0 is an experimental protocol, which uses unicode for keys and values
+    in both Python 2 and Python 3.
+    """
     
     def get_environ(self):
         """Return a new environ dict targeting the given wsgi.version"""
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.