Jason R. Coombs avatar Jason R. Coombs committed ed118d1 Merge

Merged in rp/cherrypy/default (pull request #20)

Comments (0)

Files changed (30)

cherrypy/__init__.py

  * WSGI API
 
 These API's are described in the CherryPy specification:
-http://www.cherrypy.org/wiki/CherryPySpec
+https://bitbucket.org/cherrypy/cherrypy/wiki/CherryPySpec
 """
 
 __version__ = "3.2.2"
 
     def __call__(self, *args, **kwargs):
         """Log the given message to the app.log or global log as appropriate."""
-        # Do NOT use try/except here. See http://www.cherrypy.org/ticket/945
+        # Do NOT use try/except here. See https://bitbucket.org/cherrypy/cherrypy/issue/945
         if hasattr(request, 'app') and hasattr(request.app, 'log'):
             log = request.app.log
         else:

cherrypy/_cpcompat.py

     if not isinstance(n, nativestr):
         raise TypeError("n must be a native str (got %s)" % type(n).__name__)
 
+def always_bytes(str, encoding='utf-8'):
+    if isinstance(str, unicodestr):
+        str = str.encode(encoding)
+    return str
+
 try:
     set = set
 except NameError:

cherrypy/_cpdispatch.py

                     conf = getattr(defhandler, "_cp_config", {})
                     object_trail.insert(i+1, ["default", defhandler, conf, segleft])
                     request.config = set_conf()
-                    # See http://www.cherrypy.org/ticket/613
+                    # See https://bitbucket.org/cherrypy/cherrypy/issue/613
                     request.is_index = path.endswith("/")
                     return defhandler, fullpath[fullpath_len - segleft:-1]
 
 
         result = next_dispatcher(path_info)
 
-        # Touch up staticdir config. See http://www.cherrypy.org/ticket/614.
+        # Touch up staticdir config. See https://bitbucket.org/cherrypy/cherrypy/issue/614.
         section = request.config.get('tools.staticdir.section')
         if section:
             section = section[len(prefix):]

cherrypy/_cperror.py

 
 class InternalRedirect(CherryPyException):
     """Exception raised to switch to the handler for a different URL.
-    
+
     This exception will redirect processing to another path within the site
     (without informing the client). Provide the new path as an argument when
     raising the exception. Provide any params in the querystring for the new URL.
     """
-    
+
     def __init__(self, path, query_string=""):
         import cherrypy
         self.request = cherrypy.serving.request
-        
+
         self.query_string = query_string
         if "?" in path:
             # Separate any params included in the path
             path, self.query_string = path.split("?", 1)
-        
+
         # Note that urljoin will "do the right thing" whether url is:
         #  1. a URL relative to root (e.g. "/dummy")
         #  2. a URL relative to the current path
         # Note that any query string will be discarded.
         path = _urljoin(self.request.path_info, path)
-        
+
         # Set a 'path' member attribute so that code which traps this
         # error can have access to it.
         self.path = path
-        
+
         CherryPyException.__init__(self, path, self.query_string)
 
 
 class HTTPRedirect(CherryPyException):
     """Exception raised when the request should be redirected.
-    
+
     This exception will force a HTTP redirect to the URL or URL's you give it.
     The new URL must be passed as the first argument to the Exception,
     e.g., HTTPRedirect(newUrl). Multiple URLs are allowed in a list.
 
     If one of the provided URL is a unicode object, it will be encoded
     using the default encoding or the one passed in parameter.
-    
+
     There are multiple types of redirect, from which you can select via the
     ``status`` argument. If you do not provide a ``status`` arg, it defaults to
     303 (or 302 if responding with HTTP/1.0).
-    
+
     Examples::
-    
+
         raise cherrypy.HTTPRedirect("")
         raise cherrypy.HTTPRedirect("/abs/path", 307)
         raise cherrypy.HTTPRedirect(["path1", "path2?a=1&b=2"], 301)
-    
+
     See :ref:`redirectingpost` for additional caveats.
     """
-    
+
     status = None
     """The integer HTTP status code to emit."""
-    
+
     urls = None
     """The list of URL's to emit."""
 
     encoding = 'utf-8'
     """The encoding when passed urls are not native strings"""
-    
+
     def __init__(self, urls, status=None, encoding=None):
         import cherrypy
         request = cherrypy.serving.request
-        
+
         if isinstance(urls, basestring):
             urls = [urls]
-        
+
         abs_urls = []
         for url in urls:
             url = tonative(url, encoding or self.encoding)
-                
+
             # Note that urljoin will "do the right thing" whether url is:
             #  1. a complete URL with host (e.g. "http://www.example.com/test")
             #  2. a URL relative to root (e.g. "/dummy")
             url = _urljoin(cherrypy.url(), url)
             abs_urls.append(url)
         self.urls = abs_urls
-        
+
         # RFC 2616 indicates a 301 response code fits our goal; however,
         # browser support for 301 is quite messy. Do 302/303 instead. See
         # http://www.alanflavell.org.uk/www/post-redirect.html
             status = int(status)
             if status < 300 or status > 399:
                 raise ValueError("status must be between 300 and 399.")
-        
+
         self.status = status
         CherryPyException.__init__(self, abs_urls, status)
-    
+
     def set_response(self):
         """Modify cherrypy.response status, headers, and body to represent self.
-        
+
         CherryPy uses this internally, but you can also use it to create an
         HTTPRedirect object and set its output without *raising* the exception.
         """
         import cherrypy
         response = cherrypy.serving.response
         response.status = status = self.status
-        
+
         if status in (300, 301, 302, 303, 307):
             response.headers['Content-Type'] = "text/html;charset=utf-8"
             # "The ... URI SHOULD be given by the Location field
             # in the response."
             response.headers['Location'] = self.urls[0]
-            
+
             # "Unless the request method was HEAD, the entity of the response
             # SHOULD contain a short hypertext note with a hyperlink to the
             # new URI(s)."
             # "The response MUST include the following header fields:
             # Date, unless its omission is required by section 14.18.1"
             # The "Date" header should have been set in Response.__init__
-            
+
             # "...the response SHOULD NOT include other entity-headers."
             for key in ('Allow', 'Content-Encoding', 'Content-Language',
                         'Content-Length', 'Content-Location', 'Content-MD5',
                         'Last-Modified'):
                 if key in response.headers:
                     del response.headers[key]
-            
+
             # "The 304 response MUST NOT contain a message-body."
             response.body = None
             # Previous code may have set C-L, so we have to reset it.
             response.headers.pop('Content-Length', None)
         else:
             raise ValueError("The %s status code is unknown." % status)
-    
+
     def __call__(self):
         """Use this exception as a request.handler (raise self)."""
         raise self
 def clean_headers(status):
     """Remove any headers which should not apply to an error response."""
     import cherrypy
-    
+
     response = cherrypy.serving.response
-    
+
     # Remove headers which applied to the original content,
     # but do not apply to the error page.
     respheaders = response.headers
                 "Content-Location", "Content-MD5", "Last-Modified"]:
         if key in respheaders:
             del respheaders[key]
-    
+
     if status != 416:
         # A server sending a response with status code 416 (Requested
         # range not satisfiable) SHOULD include a Content-Range field
 
 class HTTPError(CherryPyException):
     """Exception used to return an HTTP error code (4xx-5xx) to the client.
-    
+
     This exception can be used to automatically send a response using a http status
     code, with an appropriate error page. It takes an optional
     ``status`` argument (which must be between 400 and 599); it defaults to 500
     which will be returned in the response body. See
     `RFC 2616 <http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4>`_
     for a complete list of available error codes and when to use them.
-    
+
     Examples::
-        
+
         raise cherrypy.HTTPError(403)
         raise cherrypy.HTTPError("403 Forbidden", "You are not allowed to access this resource.")
     """
-    
+
     status = None
     """The HTTP status code. May be of type int or str (with a Reason-Phrase)."""
-    
+
     code = None
     """The integer HTTP status code."""
-    
+
     reason = None
     """The HTTP Reason-Phrase string."""
-    
+
     def __init__(self, status=500, message=None):
         self.status = status
         try:
             self.code, self.reason, defaultmsg = _httputil.valid_status(status)
         except ValueError:
             raise self.__class__(500, _exc_info()[1].args[0])
-        
+
         if self.code < 400 or self.code > 599:
             raise ValueError("status must be between 400 and 599.")
-        
+
         # See http://www.python.org/dev/peps/pep-0352/
         # self.message = message
         self._message = message or defaultmsg
         CherryPyException.__init__(self, status, message)
-    
+
     def set_response(self):
         """Modify cherrypy.response status, headers, and body to represent self.
-        
+
         CherryPy uses this internally, but you can also use it to create an
         HTTPError object and set its output without *raising* the exception.
         """
         import cherrypy
-        
+
         response = cherrypy.serving.response
-        
+
         clean_headers(self.code)
-        
+
         # In all cases, finalize will be called after this method,
         # so don't bother cleaning up response values here.
         response.status = self.status
             tb = format_exc()
         response.headers['Content-Type'] = "text/html;charset=utf-8"
         response.headers.pop('Content-Length', None)
-        
+
         content = ntob(self.get_error_page(self.status, traceback=tb,
                                            message=self._message), 'utf-8')
         response.body = content
-        
+
         _be_ie_unfriendly(self.code)
-    
+
     def get_error_page(self, *args, **kwargs):
         return get_error_page(*args, **kwargs)
-    
+
     def __call__(self):
         """Use this exception as a request.handler (raise self)."""
         raise self
 
 class NotFound(HTTPError):
     """Exception raised when a URL could not be mapped to any handler (404).
-    
+
     This is equivalent to raising
     :class:`HTTPError("404 Not Found") <cherrypy._cperror.HTTPError>`.
     """
-    
+
     def __init__(self, path=None):
         if path is None:
             import cherrypy
 
 def get_error_page(status, **kwargs):
     """Return an HTML page, containing a pretty error response.
-    
+
     status should be an int or a str.
     kwargs will be interpolated into the page template.
     """
     import cherrypy
-    
+
     try:
         code, reason, message = _httputil.valid_status(status)
     except ValueError:
         raise cherrypy.HTTPError(500, _exc_info()[1].args[0])
-    
+
     # We can't use setdefault here, because some
     # callers send None for kwarg values.
     if kwargs.get('status') is None:
         kwargs['traceback'] = ''
     if kwargs.get('version') is None:
         kwargs['version'] = cherrypy.__version__
-    
+
     for k, v in iteritems(kwargs):
         if v is None:
             kwargs[k] = ""
         else:
-            kwargs[k] = _escape(kwargs[k])
-    
+            kwargs[k] = tonative(_escape(kwargs[k]))
+
     # Use a custom template or callable for the error page?
     pages = cherrypy.serving.request.error_page
     error_page = pages.get(code) or pages.get('default')
                 m += "<br />"
             m += "In addition, the custom error page failed:\n<br />%s" % e
             kwargs['message'] = m
-    
+
     return _HTTPErrorTemplate % kwargs
 
 
 def _be_ie_unfriendly(status):
     import cherrypy
     response = cherrypy.serving.response
-    
+
     # For some statuses, Internet Explorer 5+ shows "friendly error
     # messages" instead of our response.body if the body is smaller
     # than a given size. Fix this by returning a body over that size
 
 def bare_error(extrabody=None):
     """Produce status, headers, body for a critical error.
-    
+
     Returns a triple without calling any other questionable functions,
     so it should be as error-free as possible. Call it from an HTTP server
     if you get errors outside of the request.
-    
+
     If extrabody is None, a friendly but rather unhelpful error message
     is set in the body. If extrabody is a string, it will be appended
     as-is to the body.
     """
-    
+
     # The whole point of this function is to be a last line-of-defense
     # in handling errors. That is, it must not raise any errors itself;
     # it cannot be allowed to fail. Therefore, don't add to it!
     # In particular, don't call any other CP functions.
-    
+
     body = ntob("Unrecoverable error in the server.")
     if extrabody is not None:
         if not isinstance(extrabody, bytestr):
             extrabody = extrabody.encode('utf-8')
         body += ntob("\n") + extrabody
-    
+
     return (ntob("500 Internal Server Error"),
             [(ntob('Content-Type'), ntob('text/plain')),
              (ntob('Content-Length'), ntob(str(len(body)),'ISO-8859-1'))],

cherrypy/_cpreqbody.py

     """The buffer size used when reading the socket."""
 
     # Don't parse the request body at all if the client didn't provide
-    # a Content-Type header. See http://www.cherrypy.org/ticket/790
+    # a Content-Type header. See https://bitbucket.org/cherrypy/cherrypy/issue/790
     default_content_type = ''
     """This defines a default ``Content-Type`` to use if no Content-Type header
     is given. The empty string is used for RequestBody, which results in the

cherrypy/_cptools.py

         When 'early', the session will be locked before reading the request
         body. This is off by default for safety reasons; for example,
         a large upload would block the session, denying an AJAX
-        progress meter (see http://www.cherrypy.org/ticket/630).
+        progress meter (see https://bitbucket.org/cherrypy/cherrypy/issue/630).
 
         When 'explicit' (or any other value), you need to call
         cherrypy.session.acquire_lock() yourself before using
             body = subhandler(*(vpath + rpcparams), **params)
 
         else:
-            # http://www.cherrypy.org/ticket/533
+            # https://bitbucket.org/cherrypy/cherrypy/issue/533
             # if a method is not found, an xmlrpclib.Fault should be returned
             # raising an exception here will do that; see
             # cherrypy.lib.xmlrpcutil.on_error

cherrypy/lib/auth_digest.py

     argument to digest_auth().
     """
     def get_ha1(realm, username):
-        return user_ha1_dict.get(user)
+        return user_ha1_dict.get(username)
 
     return get_ha1
 
 
     key
         A secret string known only to the server.
-    
+
     timestamp
         An integer seconds-since-the-epoch timestamp
-    
+
     """
     if timestamp is None:
         timestamp = int(time.time())
 
         s
             A string related to the resource, such as the hostname of the server.
-            
+
         key
             A secret string known only to the server.
-        
+
         Both s and key must be the same values which were used to synthesize the nonce
         we are trying to validate.
         """
             4.3.  This refers to the entity the user agent sent in the request which
             has the Authorization header. Typically GET requests don't have an entity,
             and POST requests do.
-        
+
         """
         ha2 = self.HA2(entity_body)
         # Request-Digest -- RFC 2617 3.2.2.1
 def digest_auth(realm, get_ha1, key, debug=False):
     """A CherryPy tool which hooks at before_handler to perform
     HTTP Digest Access Authentication, as specified in :rfc:`2617`.
-    
+
     If the request has an 'authorization' header with a 'Digest' scheme, this
     tool authenticates the credentials supplied in that header.  If
     the request has no 'authorization' header, or if it does but the scheme is
     not "Digest", or if authentication fails, the tool sends a 401 response with
     a 'WWW-Authenticate' Digest header.
-    
+
     realm
         A string containing the authentication realm.
-    
+
     get_ha1
         A callable which looks up a username in a credentials store
         and returns the HA1 string, which is defined in the RFC to be
         where username is obtained from the request's 'authorization' header.
         If username is not found in the credentials store, get_ha1() returns
         None.
-    
+
     key
         A secret string known only to the server, used in the synthesis of nonces.
-    
+
     """
     request = cherrypy.serving.request
-    
+
     auth_header = request.headers.get('authorization')
     nonce_is_stale = False
     if auth_header is not None:
             auth = HttpDigestAuthorization(auth_header, request.method, debug=debug)
         except ValueError:
             raise cherrypy.HTTPError(400, "The Authorization header could not be parsed.")
-        
+
         if debug:
             TRACE(str(auth))
-        
+
         if auth.validate_nonce(realm, key):
             ha1 = get_ha1(realm, auth.username)
             if ha1 is not None:
                         if debug:
                             TRACE("authentication of %s successful" % auth.username)
                         return
-    
+
     # Respond with 401 status and a WWW-Authenticate header
     header = www_authenticate(realm, key, stale=nonce_is_stale)
     if debug:

cherrypy/lib/caching.py

             request.cacheable = True
             return False
 
-        # Copy the response headers. See http://www.cherrypy.org/ticket/721.
+        # Copy the response headers. See https://bitbucket.org/cherrypy/cherrypy/issue/721.
         response.headers = rh = httputil.HeaderMap()
         for k in h:
             dict.__setitem__(rh, k, dict.__getitem__(h, k))

cherrypy/lib/httputil.py

 from cherrypy._cpcompat import basestring, bytestr, iteritems, nativestr, unicodestr, unquote_qs
 response_codes = BaseHTTPRequestHandler.responses.copy()
 
-# From http://www.cherrypy.org/ticket/361
+# From https://bitbucket.org/cherrypy/cherrypy/issue/361
 response_codes[500] = ('Internal Server Error',
                       'The server encountered an unexpected condition '
                       'which prevented it from fulfilling the request.')

cherrypy/lib/reprconf.py

 
 class NamespaceSet(dict):
     """A dict of config namespace names and handlers.
-    
+
     Each config entry should begin with a namespace name; the corresponding
     namespace handler will be called once for each config entry in that
     namespace, and will be passed two arguments: the config key (with the
     namespace removed) and the config value.
-    
+
     Namespace handlers may be any Python callable; they may also be
     Python 2.5-style 'context managers', in which case their __enter__
     method should return a callable to be used as the handler.
     See cherrypy.tools (the Toolbox class) for an example.
     """
-    
+
     def __call__(self, config):
         """Iterate through config and pass it to each namespace handler.
-        
+
         config
             A flat dict, where keys use dots to separate
             namespaces, and values are arbitrary.
-        
+
         The first name in each config key is used to look up the corresponding
         namespace handler. For example, a config entry of {'tools.gzip.on': v}
         will call the 'tools' namespace handler with the args: ('gzip.on', v)
                 ns, name = k.split(".", 1)
                 bucket = ns_confs.setdefault(ns, {})
                 bucket[name] = config[k]
-        
+
         # I chose __enter__ and __exit__ so someday this could be
         # rewritten using Python 2.5's 'with' statement:
         # for ns, handler in self.iteritems():
             else:
                 for k, v in ns_confs.get(ns, {}).items():
                     handler(k, v)
-    
+
     def __repr__(self):
         return "%s.%s(%s)" % (self.__module__, self.__class__.__name__,
                               dict.__repr__(self))
-    
+
     def __copy__(self):
         newobj = self.__class__()
         newobj.update(self)
 
 class Config(dict):
     """A dict-like set of configuration data, with defaults and namespaces.
-    
+
     May take a file, filename, or dict.
     """
-    
+
     defaults = {}
     environments = {}
     namespaces = NamespaceSet()
-    
+
     def __init__(self, file=None, **kwargs):
         self.reset()
         if file is not None:
             self.update(file)
         if kwargs:
             self.update(kwargs)
-    
+
     def reset(self):
         """Reset self to default values."""
         self.clear()
         dict.update(self, self.defaults)
-    
+
     def update(self, config):
         """Update self from a dict, file or filename."""
         if isinstance(config, basestring):
         else:
             config = config.copy()
         self._apply(config)
-    
+
     def _apply(self, config):
         """Update self from a dict."""
         which_env = config.get('environment')
             for k in env:
                 if k not in config:
                     config[k] = env[k]
-        
+
         dict.update(self, config)
         self.namespaces(config)
-    
+
     def __setitem__(self, k, v):
         dict.__setitem__(self, k, v)
         self.namespaces({k: v})
 
 
 class Parser(ConfigParser):
-    """Sub-class of ConfigParser that keeps the case of options and that 
+    """Sub-class of ConfigParser that keeps the case of options and that
     raises an exception if the file cannot be read.
     """
-    
+
     def optionxform(self, optionstr):
         return optionstr
-    
+
     def read(self, filenames):
         if isinstance(filenames, basestring):
             filenames = [filenames]
                 self._read(fp, filename)
             finally:
                 fp.close()
-    
+
     def as_dict(self, raw=False, vars=None):
         """Convert an INI file to a dictionary"""
         # Load INI file into a dict
                     raise ValueError(msg, x.__class__.__name__, x.args)
                 result[section][option] = value
         return result
-    
+
     def dict_from_file(self, file):
         if hasattr(file, 'read'):
             self.readfp(file)
 
 
 class _Builder2:
-    
+
     def build(self, o):
         m = getattr(self, 'build_' + o.__class__.__name__, None)
         if m is None:
             raise TypeError("unrepr does not recognize %s" %
                             repr(o.__class__.__name__))
         return m(o)
-    
+
     def astnode(self, s):
         """Return a Python2 ast Node compiled from a string."""
         try:
             # Fallback to eval when compiler package is not available,
             # e.g. IronPython 1.0.
             return eval(s)
-        
+
         p = compiler.parse("__tempvalue__ = " + s)
         return p.getChildren()[1].getChildren()[0].getChildren()[1]
-    
+
     def build_Subscript(self, o):
         expr, flags, subs = o.getChildren()
         expr = self.build(expr)
         subs = self.build(subs)
         return expr[subs]
-    
+
     def build_CallFunc(self, o):
         children = map(self.build, o.getChildren())
         callee = children.pop(0)
         starargs = children.pop() or ()
         args = tuple(children) + tuple(starargs)
         return callee(*args, **kwargs)
-    
+
     def build_List(self, o):
         return map(self.build, o.getChildren())
-    
+
     def build_Const(self, o):
         return o.value
-    
+
     def build_Dict(self, o):
         d = {}
         i = iter(map(self.build, o.getChildren()))
         for el in i:
             d[el] = i.next()
         return d
-    
+
     def build_Tuple(self, o):
         return tuple(self.build_List(o))
-    
+
     def build_Name(self, o):
         name = o.name
         if name == 'None':
             return True
         if name == 'False':
             return False
-        
+
         # See if the Name is a package or module. If it is, import it.
         try:
             return modules(name)
         except ImportError:
             pass
-        
+
         # See if the Name is in builtins.
         try:
             return getattr(builtins, name)
         except AttributeError:
             pass
-        
+
         raise TypeError("unrepr could not resolve the name %s" % repr(name))
-    
+
     def build_Add(self, o):
         left, right = map(self.build, o.getChildren())
         return left + right
     def build_Mul(self, o):
         left, right = map(self.build, o.getChildren())
         return left * right
-    
+
     def build_Getattr(self, o):
         parent = self.build(o.expr)
         return getattr(parent, o.attrname)
-    
+
     def build_NoneType(self, o):
         return None
-    
+
     def build_UnarySub(self, o):
         return -self.build(o.getChildren()[0])
-    
+
     def build_UnaryAdd(self, o):
         return self.build(o.getChildren()[0])
 
 
 class _Builder3:
-    
+
     def build(self, o):
         m = getattr(self, 'build_' + o.__class__.__name__, None)
         if m is None:
             raise TypeError("unrepr does not recognize %s" %
                             repr(o.__class__.__name__))
         return m(o)
-    
+
     def astnode(self, s):
         """Return a Python3 ast Node compiled from a string."""
         try:
 
     def build_Subscript(self, o):
         return self.build(o.value)[self.build(o.slice)]
-    
+
     def build_Index(self, o):
         return self.build(o.value)
-    
+
     def build_Call(self, o):
         callee = self.build(o.func)
-        
+
         if o.args is None:
             args = ()
-        else: 
-            args = tuple([self.build(a) for a in o.args]) 
-        
+        else:
+            args = tuple([self.build(a) for a in o.args])
+
         if o.starargs is None:
             starargs = ()
         else:
             starargs = self.build(o.starargs)
-        
+
         if o.kwargs is None:
             kwargs = {}
         else:
             kwargs = self.build(o.kwargs)
-        
+
         return callee(*(args + starargs), **kwargs)
-    
+
     def build_List(self, o):
         return list(map(self.build, o.elts))
-    
+
     def build_Str(self, o):
         return o.s
-    
+
     def build_Num(self, o):
         return o.n
-    
+
     def build_Dict(self, o):
         return dict([(self.build(k), self.build(v))
                      for k, v in zip(o.keys, o.values)])
-    
+
     def build_Tuple(self, o):
         return tuple(self.build_List(o))
-    
+
     def build_Name(self, o):
         name = o.id
         if name == 'None':
             return True
         if name == 'False':
             return False
-        
+
         # See if the Name is a package or module. If it is, import it.
         try:
             return modules(name)
         except ImportError:
             pass
-        
+
         # See if the Name is in builtins.
         try:
             import builtins
             return getattr(builtins, name)
         except AttributeError:
             pass
-        
+
         raise TypeError("unrepr could not resolve the name %s" % repr(name))
-        
+
     def build_UnaryOp(self, o):
         op, operand = map(self.build, [o.op, o.operand])
         return op(operand)
-    
+
     def build_BinOp(self, o):
-        left, op, right = map(self.build, [o.left, o.op, o.right]) 
+        left, op, right = map(self.build, [o.left, o.op, o.right])
         return op(left, right)
 
     def build_Add(self, o):
 
     def build_Mult(self, o):
         return _operator.mul
-        
+
     def build_USub(self, o):
         return _operator.neg
 
 
 def modules(modulePath):
     """Load a module and retrieve a reference to that module."""
-    try:
-        mod = sys.modules[modulePath]
-        if mod is None:
-            raise KeyError()
-    except KeyError:
-        __import__(modulePath)
-        mod = sys.modules[modulePath]   
-    return mod
+    __import__(modulePath)
+    return sys.modules[modulePath]
 
 def attributes(full_attribute_name):
     """Load a module and retrieve an attribute of that module."""
-    
+
     # Parse out the path, module, and attribute
     last_dot = full_attribute_name.rfind(".")
     attr_name = full_attribute_name[last_dot + 1:]
     mod_path = full_attribute_name[:last_dot]
-    
+
     mod = modules(mod_path)
     # Let an AttributeError propagate outward.
     try:
     except AttributeError:
         raise AttributeError("'%s' object has no attribute '%s'"
                              % (mod_path, attr_name))
-    
+
     # Return a reference to the attribute.
     return attr
 

cherrypy/lib/sessions.py

 
 class Session(object):
     """A CherryPy dict-like Session object (one per request)."""
-    
+
     _id = None
-    
+
     id_observers = None
     "A list of callbacks to which to pass new id's."
-    
+
     def _get_id(self):
         return self._id
     def _set_id(self, value):
         for o in self.id_observers:
             o(value)
     id = property(_get_id, _set_id, doc="The current session ID.")
-    
+
     timeout = 60
     "Number of minutes after which to delete session data."
-    
+
     locked = False
     """
     If True, this session instance has exclusive read/write access
     to session data."""
-    
+
     loaded = False
     """
     If True, data has been retrieved from storage. This should happen
     automatically on the first attempt to access session data."""
-    
+
     clean_thread = None
     "Class-level Monitor which calls self.clean_up."
-    
+
     clean_freq = 5
     "The poll rate for expired session cleanup in minutes."
-    
+
     originalid = None
     "The session id passed by the client. May be missing or unsafe."
-    
+
     missing = False
     "True if the session requested by the client did not exist."
-    
+
     regenerated = False
     """
     True if the application called session.regenerate(). This is not set by
     internal calls to regenerate the session id."""
-    
+
     debug=False
-    
+
     def __init__(self, id=None, **kwargs):
         self.id_observers = []
         self._data = {}
-        
+
         for k, v in kwargs.items():
             setattr(self, k, v)
-        
+
         self.originalid = id
         self.missing = False
         if id is None:
                     cherrypy.log('Expired or malicious session %r; '
                                  'making a new one' % id, 'TOOLS.SESSIONS')
                 # Expired or malicious session. Make a new one.
-                # See http://www.cherrypy.org/ticket/709.
+                # See https://bitbucket.org/cherrypy/cherrypy/issue/709.
                 self.id = None
                 self.missing = True
                 self._regenerate()
         """Replace the current session (with a new id)."""
         self.regenerated = True
         self._regenerate()
-    
+
     def _regenerate(self):
         if self.id is not None:
             self.delete()
-        
+
         old_session_was_locked = self.locked
         if old_session_was_locked:
             self.release_lock()
-        
+
         self.id = None
         while self.id is None:
             self.id = self.generate_id()
             # Assert that the generated id is not already stored.
             if self._exists():
                 self.id = None
-        
+
         if old_session_was_locked:
             self.acquire_lock()
-    
+
     def clean_up(self):
         """Clean up expired sessions."""
         pass
-    
+
     def generate_id(self):
         """Return a new session id."""
         return random20()
-    
+
     def save(self):
         """Save session data."""
         try:
                     cherrypy.log('Saving with expiry %s' % expiration_time,
                                  'TOOLS.SESSIONS')
                 self._save(expiration_time)
-            
+
         finally:
             if self.locked:
                 # Always release the lock if the user didn't release it
                 self.release_lock()
-    
+
     def load(self):
         """Copy stored session data into this session instance."""
         data = self._load()
         else:
             self._data = data[0]
         self.loaded = True
-        
+
         # Stick the clean_thread in the class, not the instance.
         # The instances are created and destroyed per-request.
         cls = self.__class__
             t.subscribe()
             cls.clean_thread = t
             t.start()
-    
+
     def delete(self):
         """Delete stored session data."""
         self._delete()
-    
+
     def __getitem__(self, key):
         if not self.loaded: self.load()
         return self._data[key]
-    
+
     def __setitem__(self, key, value):
         if not self.loaded: self.load()
         self._data[key] = value
-    
+
     def __delitem__(self, key):
         if not self.loaded: self.load()
         del self._data[key]
-    
+
     def pop(self, key, default=missing):
         """Remove the specified key and return the corresponding value.
         If key is not found, default is returned if given,
             return self._data.pop(key)
         else:
             return self._data.pop(key, default)
-    
+
     def __contains__(self, key):
         if not self.loaded: self.load()
         return key in self._data
-    
+
     if hasattr({}, 'has_key'):
         def has_key(self, key):
             """D.has_key(k) -> True if D has a key k, else False."""
             if not self.loaded: self.load()
             return key in self._data
-    
+
     def get(self, key, default=None):
         """D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None."""
         if not self.loaded: self.load()
         return self._data.get(key, default)
-    
+
     def update(self, d):
         """D.update(E) -> None.  Update D from E: for k in E: D[k] = E[k]."""
         if not self.loaded: self.load()
         self._data.update(d)
-    
+
     def setdefault(self, key, default=None):
         """D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D."""
         if not self.loaded: self.load()
         return self._data.setdefault(key, default)
-    
+
     def clear(self):
         """D.clear() -> None.  Remove all items from D."""
         if not self.loaded: self.load()
         self._data.clear()
-    
+
     def keys(self):
         """D.keys() -> list of D's keys."""
         if not self.loaded: self.load()
         return self._data.keys()
-    
+
     def items(self):
         """D.items() -> list of D's (key, value) pairs, as 2-tuples."""
         if not self.loaded: self.load()
         return self._data.items()
-    
+
     def values(self):
         """D.values() -> list of D's values."""
         if not self.loaded: self.load()
 
 
 class RamSession(Session):
-    
+
     # Class-level objects. Don't rebind these!
     cache = {}
     locks = {}
-    
+
     def clean_up(self):
         """Clean up expired sessions."""
         now = self.now()
                     del self.locks[id]
                 except KeyError:
                     pass
-        
+
         # added to remove obsolete lock objects
         for id in list(self.locks):
             if id not in self.cache:
                 self.locks.pop(id, None)
-    
+
     def _exists(self):
         return self.id in self.cache
-    
+
     def _load(self):
         return self.cache.get(self.id)
-    
+
     def _save(self, expiration_time):
         self.cache[self.id] = (self._data, expiration_time)
-    
+
     def _delete(self):
         self.cache.pop(self.id, None)
-    
+
     def acquire_lock(self):
         """Acquire an exclusive lock on the currently-loaded session data."""
         self.locked = True
         self.locks.setdefault(self.id, threading.RLock()).acquire()
-    
+
     def release_lock(self):
         """Release the lock on the currently-loaded session data."""
         self.locks[self.id].release()
         self.locked = False
-    
+
     def __len__(self):
         """Return the number of active sessions."""
         return len(self.cache)
 
 class FileSession(Session):
     """Implementation of the File backend for sessions
-    
+
     storage_path
         The folder where session data will be saved. Each session
         will be saved as pickle.dump(data, expiration_time) in its own file;
         the filename will be self.SESSION_PREFIX + self.id.
-    
+
     """
-    
+
     SESSION_PREFIX = 'session-'
     LOCK_SUFFIX = '.lock'
     pickle_protocol = pickle.HIGHEST_PROTOCOL
-    
+
     def __init__(self, id=None, **kwargs):
         # The 'storage_path' arg is required for file-based sessions.
         kwargs['storage_path'] = os.path.abspath(kwargs['storage_path'])
         Session.__init__(self, id=id, **kwargs)
-    
+
     def setup(cls, **kwargs):
         """Set up the storage system for file-based sessions.
-        
+
         This should only be called once per process; this will be done
         automatically when using sessions.init (as the built-in Tool does).
         """
         # The 'storage_path' arg is required for file-based sessions.
         kwargs['storage_path'] = os.path.abspath(kwargs['storage_path'])
-        
+
         for k, v in kwargs.items():
             setattr(cls, k, v)
-        
+
         # Warn if any lock files exist at startup.
         lockfiles = [fname for fname in os.listdir(cls.storage_path)
                      if (fname.startswith(cls.SESSION_PREFIX)
                  "manually delete the lockfiles found at %r."
                  % (len(lockfiles), plural, cls.storage_path))
     setup = classmethod(setup)
-    
+
     def _get_file_path(self):
         f = os.path.join(self.storage_path, self.SESSION_PREFIX + self.id)
         if not os.path.abspath(f).startswith(self.storage_path):
             raise cherrypy.HTTPError(400, "Invalid session id in cookie.")
         return f
-    
+
     def _exists(self):
         path = self._get_file_path()
         return os.path.exists(path)
-    
+
     def _load(self, path=None):
         if path is None:
             path = self._get_file_path()
                 f.close()
         except (IOError, EOFError):
             return None
-    
+
     def _save(self, expiration_time):
         f = open(self._get_file_path(), "wb")
         try:
             pickle.dump((self._data, expiration_time), f, self.pickle_protocol)
         finally:
             f.close()
-    
+
     def _delete(self):
         try:
             os.unlink(self._get_file_path())
         except OSError:
             pass
-    
+
     def acquire_lock(self, path=None):
         """Acquire an exclusive lock on the currently-loaded session data."""
         if path is None:
             except OSError:
                 time.sleep(0.1)
             else:
-                os.close(lockfd) 
+                os.close(lockfd)
                 break
         self.locked = True
-    
+
     def release_lock(self, path=None):
         """Release the lock on the currently-loaded session data."""
         if path is None:
             path = self._get_file_path()
         os.unlink(path + self.LOCK_SUFFIX)
         self.locked = False
-    
+
     def clean_up(self):
         """Clean up expired sessions."""
         now = self.now()
                             os.unlink(path)
                 finally:
                     self.release_lock(path)
-    
+
     def __len__(self):
         """Return the number of active sessions."""
         return len([fname for fname in os.listdir(self.storage_path)
                 data text,
                 expiration_time timestamp
             )
-    
+
     You must provide your own get_db function.
     """
-    
+
     pickle_protocol = pickle.HIGHEST_PROTOCOL
-    
+
     def __init__(self, id=None, **kwargs):
         Session.__init__(self, id, **kwargs)
         self.cursor = self.db.cursor()
-    
+
     def setup(cls, **kwargs):
         """Set up the storage system for Postgres-based sessions.
-        
+
         This should only be called once per process; this will be done
         automatically when using sessions.init (as the built-in Tool does).
         """
         for k, v in kwargs.items():
             setattr(cls, k, v)
-        
+
         self.db = self.get_db()
     setup = classmethod(setup)
-    
+
     def __del__(self):
         if self.cursor:
             self.cursor.close()
         self.db.commit()
-    
+
     def _exists(self):
         # Select session data from table
         self.cursor.execute('select data, expiration_time from session '
                             'where id=%s', (self.id,))
         rows = self.cursor.fetchall()
         return bool(rows)
-    
+
     def _load(self):
         # Select session data from table
         self.cursor.execute('select data, expiration_time from session '
         rows = self.cursor.fetchall()
         if not rows:
             return None
-        
+
         pickled_data, expiration_time = rows[0]
         data = pickle.loads(pickled_data)
         return data, expiration_time
-    
+
     def _save(self, expiration_time):
         pickled_data = pickle.dumps(self._data, self.pickle_protocol)
         self.cursor.execute('update session set data = %s, '
                             'expiration_time = %s where id = %s',
                             (pickled_data, expiration_time, self.id))
-    
+
     def _delete(self):
         self.cursor.execute('delete from session where id=%s', (self.id,))
-   
+
     def acquire_lock(self):
         """Acquire an exclusive lock on the currently-loaded session data."""
         # We use the "for update" clause to lock the row
         self.locked = True
         self.cursor.execute('select id from session where id=%s for update',
                             (self.id,))
-    
+
     def release_lock(self):
         """Release the lock on the currently-loaded session data."""
         # We just close the cursor and that will remove the lock
         #   introduced by the "for update" clause
         self.cursor.close()
         self.locked = False
-    
+
     def clean_up(self):
         """Clean up expired sessions."""
         self.cursor.execute('delete from session where expiration_time < %s',
 
 
 class MemcachedSession(Session):
-    
+
     # The most popular memcached client for Python isn't thread-safe.
     # Wrap all .get and .set operations in a single lock.
     mc_lock = threading.RLock()
-    
+
     # This is a seperate set of locks per session id.
     locks = {}
-    
+
     servers = ['127.0.0.1:11211']
-    
+
     def setup(cls, **kwargs):
         """Set up the storage system for memcached-based sessions.
-        
+
         This should only be called once per process; this will be done
         automatically when using sessions.init (as the built-in Tool does).
         """
         for k, v in kwargs.items():
             setattr(cls, k, v)
-        
+
         import memcache
         cls.cache = memcache.Client(cls.servers)
     setup = classmethod(setup)
-    
+
     def _get_id(self):
         return self._id
     def _set_id(self, value):
         for o in self.id_observers:
             o(value)
     id = property(_get_id, _set_id, doc="The current session ID.")
-    
+
     def _exists(self):
         self.mc_lock.acquire()
         try:
             return bool(self.cache.get(self.id))
         finally:
             self.mc_lock.release()
-    
+
     def _load(self):
         self.mc_lock.acquire()
         try:
             return self.cache.get(self.id)
         finally:
             self.mc_lock.release()
-    
+
     def _save(self, expiration_time):
         # Send the expiration time as "Unix time" (seconds since 1/1/1970)
         td = int(time.mktime(expiration_time.timetuple()))
                 raise AssertionError("Session data for id %r not set." % self.id)
         finally:
             self.mc_lock.release()
-    
+
     def _delete(self):
         self.cache.delete(self.id)
-    
+
     def acquire_lock(self):
         """Acquire an exclusive lock on the currently-loaded session data."""
         self.locked = True
         self.locks.setdefault(self.id, threading.RLock()).acquire()
-    
+
     def release_lock(self):
         """Release the lock on the currently-loaded session data."""
         self.locks[self.id].release()
         self.locked = False
-    
+
     def __len__(self):
         """Return the number of active sessions."""
         raise NotImplementedError
 
 def save():
     """Save any changed session data."""
-    
+
     if not hasattr(cherrypy.serving, "session"):
         return
     request = cherrypy.serving.request
     response = cherrypy.serving.response
-    
+
     # Guard against running twice
     if hasattr(request, "_sessionsaved"):
         return
     request._sessionsaved = True
-    
+
     if response.stream:
         # If the body is being streamed, we have to save the data
         #   *after* the response has been written out
          timeout=60, domain=None, secure=False, clean_freq=5,
          persistent=True, httponly=False, debug=False, **kwargs):
     """Initialize session object (using cookies).
-    
+
     storage_type
         One of 'ram', 'file', 'postgresql', 'memcached'. This will be
         used to look up the corresponding class in cherrypy.lib.sessions
         globals. For example, 'file' will use the FileSession class.
-    
+
     path
         The 'path' value to stick in the response cookie metadata.
-    
+
     path_header
         If 'path' is None (the default), then the response
         cookie 'path' will be pulled from request.headers[path_header].
-    
+
     name
         The name of the cookie.
-    
+
     timeout
         The expiration timeout (in minutes) for the stored session data.
         If 'persistent' is True (the default), this is also the timeout
         for the cookie.
-    
+
     domain
         The cookie domain.
-    
+
     secure
         If False (the default) the cookie 'secure' value will not
         be set. If True, the cookie 'secure' value will be set (to 1).
-    
+
     clean_freq (minutes)
         The poll rate for expired session cleanup.
-    
+
     persistent
         If True (the default), the 'timeout' argument will be used
         to expire the cookie. If False, the cookie will not have an expiry,
         and the cookie will be a "session cookie" which expires when the
         browser is closed.
-    
+
     httponly
         If False (the default) the cookie 'httponly' value will not be set.
         If True, the cookie 'httponly' value will be set (to 1).
-    
+
     Any additional kwargs will be bound to the new Session instance,
     and may be specific to the storage type. See the subclass of Session
     you're using for more information.
     """
-    
+
     request = cherrypy.serving.request
-    
+
     # Guard against running twice
     if hasattr(request, "_session_init_flag"):
         return
     request._session_init_flag = True
-    
+
     # Check if request came with a session ID
     id = None
     if name in request.cookie:
         if debug:
             cherrypy.log('ID obtained from request.cookie: %r' % id,
                          'TOOLS.SESSIONS')
-    
+
     # Find the storage class and call setup (first time only).
     storage_class = storage_type.title() + 'Session'
     storage_class = globals()[storage_class]
     if not hasattr(cherrypy, "session"):
         if hasattr(storage_class, "setup"):
             storage_class.setup(**kwargs)
-    
+
     # Create and attach a new Session instance to cherrypy.serving.
     # It will possess a reference to (and lock, and lazily load)
     # the requested session data.
         """Update the cookie every time the session id changes."""
         cherrypy.serving.response.cookie[name] = id
     sess.id_observers.append(update_cookie)
-    
+
     # Create cherrypy.session which will proxy to cherrypy.serving.session
     if not hasattr(cherrypy, "session"):
         cherrypy.session = cherrypy._ThreadLocalProxy('session')
-    
+
     if persistent:
         cookie_timeout = timeout
     else:
 def set_response_cookie(path=None, path_header=None, name='session_id',
                         timeout=60, domain=None, secure=False, httponly=False):
     """Set a response cookie for the client.
-    
+
     path
         the 'path' value to stick in the response cookie metadata.
 
     cookie[name] = cherrypy.serving.session.id
     cookie[name]['path'] = (path or cherrypy.serving.request.headers.get(path_header)
                             or '/')
-    
+
     # We'd like to use the "max-age" param as indicated in
     # http://www.faqs.org/rfcs/rfc2109.html but IE doesn't
     # save it to disk and the session is lost if people close

cherrypy/process/plugins.py

 class DropPrivileges(SimplePlugin):
     """Drop privileges. uid/gid arguments not available on Windows.
 
-    Special thanks to Gavin Baker: http://antonym.org/node/100.
+    Special thanks to Gavin Baker: http://antonym.org/2005/12/dropping-privileges-in-python.html
     """
 
     def __init__(self, bus, umask=None, uid=None, gid=None):
     it won't delay stopping the whole process.
     """
 
-    def __init__(self, interval, function, args=[], kwargs={}, bus=None):
+    def __init__(self, interval, function, args=[], kwargs={}, bus=None, daemon=True):
         threading.Thread.__init__(self)
         self.interval = interval
         self.function = function
         self.kwargs = kwargs
         self.running = False
         self.bus = bus
+        if daemon is not None:
+            self.daemon = daemon
+        else:
+            self.daemon = current_thread().daemon
 
     def cancel(self):
         self.running = False

cherrypy/process/wspbus.py

 
     def __init__(self, *args, **kwargs):
         # Don't use 'super' here; Exceptions are old-style in Py2.4
-        # See http://www.cherrypy.org/ticket/959
+        # See https://bitbucket.org/cherrypy/cherrypy/issue/959
         Exception.__init__(self, *args, **kwargs)
         self._exceptions = list()
 
             raise
 
         # Waiting for ALL child threads to finish is necessary on OS X.
-        # See http://www.cherrypy.org/ticket/581.
+        # See https://bitbucket.org/cherrypy/cherrypy/issue/581.
         # It's also good to let them all shut down before allowing
         # the main thread to call atexit handlers.
-        # See http://www.cherrypy.org/ticket/751.
+        # See https://bitbucket.org/cherrypy/cherrypy/issue/751.
         self.log("Waiting for child threads to terminate...")
         for t in threading.enumerate():
             if t != threading.currentThread() and t.isAlive():

cherrypy/test/test_config_server.py

             self.getPage("/", headers=[('From', "x" * 500)])
             self.assertStatus(413)
 
-        # Test for http://www.cherrypy.org/ticket/421
+        # Test for https://bitbucket.org/cherrypy/cherrypy/issue/421
         # (Incorrect border condition in readline of SizeCheckWrapper).
         # This hangs in rev 891 and earlier.
         lines256 = "x" * 248

cherrypy/test/test_conn.py

                     # Make another request on the same connection, which should error.
                     self.assertRaises(NotConnected, self.getPage, "/")
 
-                # Try HEAD. See http://www.cherrypy.org/ticket/864.
+                # Try HEAD. See https://bitbucket.org/cherrypy/cherrypy/issue/864.
                 self.getPage("/stream", method='HEAD')
                 self.assertStatus('200 OK')
                 self.assertBody('')

cherrypy/test/test_core.py

         self.assertStatus(('302 Found', '303 See Other'))
 
         # check injection protection
-        # See http://www.cherrypy.org/ticket/1003
+        # See https://bitbucket.org/cherrypy/cherrypy/issue/1003
         self.getPage("/redirect/custom?code=303&url=/foobar/%0d%0aSet-Cookie:%20somecookie=someval")
         self.assertStatus(303)
         loc = self.assertHeader('Location')

cherrypy/test/test_http.py

             c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
         c.putrequest('GET', '/')
         c.putheader('Content-Type', 'text/plain')
-        # See http://www.cherrypy.org/ticket/941
+        # See https://bitbucket.org/cherrypy/cherrypy/issue/941
         c._output(ntob('Re, 1.2.3.4#015#012'))
         c.endheaders()
 

cherrypy/test/test_mime.py

                           'bar',
                           '--X',
                           # Test a param with more than one value.
-                          # See http://www.cherrypy.org/ticket/1028
+                          # See https://bitbucket.org/cherrypy/cherrypy/issue/1028
                           'Content-Disposition: form-data; name="baz"',
                           '',
                           '111',

cherrypy/test/test_objectmapping.py

         self.assertStatus("404 Not Found")
 
         # Make sure /foobar maps to Root.foobar and not to the app
-        # mounted at /foo. See http://www.cherrypy.org/ticket/573
+        # mounted at /foo. See https://bitbucket.org/cherrypy/cherrypy/issue/573
         self.getPage("/foobar")
         self.assertBody("bar")
 
         self.assertBody("default for dir1, param is:('dir2', '5', '3', 'sir')")
 
         # test that extra positional args raises an 404 Not Found
-        # See http://www.cherrypy.org/ticket/733.
+        # See https://bitbucket.org/cherrypy/cherrypy/issue/733.
         self.getPage("/dir1/dir2/script_name/extra/stuff")
         self.assertStatus(404)
 

cherrypy/test/test_proxy.py

             self.getPage(sn + "/pageurl")
             self.assertBody(expected)
 
-        # Test trailing slash (see http://www.cherrypy.org/ticket/562).
+        # Test trailing slash (see https://bitbucket.org/cherrypy/cherrypy/issue/562).
         self.getPage("/xhost/", headers=[('X-Host', 'www.example.test')])
         self.assertHeader('Location', "%s://www.example.test/xhost"
                           % self.scheme)

cherrypy/test/test_request_obj.py

                 return cherrypy.request.headers[headername]
 
             def doubledheaders(self):
-                # From http://www.cherrypy.org/ticket/165:
+                # From https://bitbucket.org/cherrypy/cherrypy/issue/165:
                 # "header field names should not be case sensitive sayes the rfc.
                 # if i set a headerfield in complete lowercase i end up with two
                 # header fields, one in lowercase, the other in mixed-case."
                         "en-gb;q=0.8\n"
                         "en;q=0.7")
 
-        # Test malformed header parsing. See http://www.cherrypy.org/ticket/763.
+        # Test malformed header parsing. See https://bitbucket.org/cherrypy/cherrypy/issue/763.
         self.getPage("/headerelements/get_elements?headername=Content-Type",
                      # Note the illegal trailing ";"
                      headers=[('Content-Type', 'text/html; charset=utf-8;')])
 
     def test_repeated_headers(self):
         # Test that two request headers are collapsed into one.
-        # See http://www.cherrypy.org/ticket/542.
+        # See https://bitbucket.org/cherrypy/cherrypy/issue/542.
         self.getPage("/headers/Accept-Charset",
                      headers=[("Accept-Charset", "iso-8859-5"),
                               ("Accept-Charset", "unicode-1-1;q=0.8")])
         self.assertBody(b)
 
         # Request a PUT method with a file body but no Content-Type.
-        # See http://www.cherrypy.org/ticket/790.
+        # See https://bitbucket.org/cherrypy/cherrypy/issue/790.
         b = ntob("one thing on top of another")
         self.persistent = True
         try:
             self.persistent = False
 
         # Request a PUT method with no body whatsoever (not an empty one).
-        # See http://www.cherrypy.org/ticket/650.
+        # See https://bitbucket.org/cherrypy/cherrypy/issue/650.
         # Provide a C-T or webtest will provide one (and a C-L) for us.
         h = [("Content-Type", "text/plain")]
         self.getPage("/method/reachable", headers=h, method="PUT")

cherrypy/test/test_tutorials.py

 
     def test05DerivedObjects(self):
         self.getPage("/load_tut_module/tut05_derived_objects")
-        msg = '''
-            <html>
-            <head>
-                <title>Another Page</title>
-            <head>
-            <body>
-            <h2>Another Page</h2>
-
-            <p>
-            And this is the amazing second page!
-            </p>
-
-            </body>
-            </html>
-        '''
+        #Formatted to not get overzealously whitespace fixed
+        msg = '\n            <html>\n            <head>\n                <title>Another Page</title>\n            <head>\n            <body>\n            <h2>Another Page</h2>\n        \n            <p>\n            And this is the amazing second page!\n            </p>\n        \n            </body>\n            </html>\n        '
         self.getPage("/another/")
         self.assertBody(msg)
 

cherrypy/test/test_xmlrpc.py

         else:
             self.fail("Expected xmlrpclib.Fault")
 
-        # http://www.cherrypy.org/ticket/533
+        # https://bitbucket.org/cherrypy/cherrypy/issue/533
         # if a method is not found, an xmlrpclib.Fault should be raised
         try:
             proxy.non_method()

cherrypy/test/webtest.py

 from unittest import *
 from unittest import _TextTestResult
 
-from cherrypy._cpcompat import basestring, ntob, py3k, HTTPConnection, HTTPSConnection, unicodestr
+from cherrypy._cpcompat import basestring, ntob, py3k, HTTPConnection, HTTPSConnection, unicodestr, always_bytes
 
 
 def interface(host):
         """Open the url with debugging support. Return status, headers, body."""
         ServerError.on = False
 
-        if isinstance(url, unicodestr):
-            url = url.encode('utf-8')
-        if isinstance(body, unicodestr):
-            body = body.encode('utf-8')
+        url = always_bytes(url)
+        body = always_bytes(body)
 
         self.url = url
         self.time = None
                 conn.putrequest(method.upper(), url)
 
             for key, value in headers:
-                conn.putheader(key, ntob(value, "Latin-1"))
+                conn.putheader(key, always_bytes(value, "Latin-1"))
             conn.endheaders()
 
             if body is not None:

cherrypy/wsgiserver/wsgiserver2.py

 (without using CherryPy's application machinery)::
 
     from cherrypy import wsgiserver
-    
+
     def my_crazy_app(environ, start_response):
         status = '200 OK'
         response_headers = [('Content-type','text/plain')]
         start_response(status, response_headers)
         return ['Hello world!']
-    
+
     server = wsgiserver.CherryPyWSGIServer(
                 ('0.0.0.0', 8070), my_crazy_app,
                 server_name='www.cherrypy.example')
     server.start()
-    
-The CherryPy WSGI server can serve as many WSGI applications 
+
+The CherryPy WSGI server can serve as many WSGI applications
 as you want in one instance by using a WSGIPathInfoDispatcher::
-    
+
     d = WSGIPathInfoDispatcher({'/': my_crazy_app, '/blog': my_blog_app})
     server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 80), d)
-    
+
 Want SSL support? Just set server.ssl_adapter to an SSLAdapter instance.
 
 This won't call the CherryPy engine (application side) at all, only the
 
 def plat_specific_errors(*errnames):
     """Return error numbers for all errors in errnames on this platform.
-    
+
     The 'errno' module contains different global constants depending on
     the specific platform (OS). This function will return the list of
     numeric values for a given list of potential names.
 
 def read_headers(rfile, hdict=None):
     """Read headers from the given stream into the given header dict.
-    
+
     If hdict is None, a new header dict is created. Returns the populated
     header dict.
-    
+
     Headers which are repeated are folded together using a comma if their
     specification so dictates.
-    
+
     This function raises ValueError when the read bytes violate the HTTP spec.
     You should probably return "400 Bad Request" if this happens.
     """
     if hdict is None:
         hdict = {}
-    
+
     while True:
         line = rfile.readline()
         if not line:
             # No more data--illegal end of headers
             raise ValueError("Illegal end of headers.")
-        
+
         if line == CRLF:
             # Normal end of headers
             break
         if not line.endswith(CRLF):
             raise ValueError("HTTP requires CRLF terminators")
-        
+
         if line[0] in (SPACE, TAB):
             # It's a continuation line.
             v = line.strip()
             k = k.strip().title()
             v = v.strip()
             hname = k
-        
+
         if k in comma_separated_headers:
             existing = hdict.get(hname)
             if existing:
                 v = ", ".join((existing, v))
         hdict[hname] = v
-    
+
     return hdict
 
 
 
 class SizeCheckWrapper(object):
     """Wraps a file-like object, raising MaxSizeExceeded if too large."""
-    
+
     def __init__(self, rfile, maxlen):
         self.rfile = rfile