diff --git a/libraries/backports/__init__.py b/libraries/backports/__init__.py
deleted file mode 100644
index 69e3be50..00000000
--- a/libraries/backports/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__path__ = __import__('pkgutil').extend_path(__path__, __name__)
diff --git a/libraries/backports/functools_lru_cache.py b/libraries/backports/functools_lru_cache.py
deleted file mode 100644
index 707c6c76..00000000
--- a/libraries/backports/functools_lru_cache.py
+++ /dev/null
@@ -1,184 +0,0 @@
-from __future__ import absolute_import
-
-import functools
-from collections import namedtuple
-from threading import RLock
-
-_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])
-
-
-@functools.wraps(functools.update_wrapper)
-def update_wrapper(wrapper,
-                   wrapped,
-                   assigned = functools.WRAPPER_ASSIGNMENTS,
-                   updated = functools.WRAPPER_UPDATES):
-    """
-    Patch two bugs in functools.update_wrapper.
-    """
-    # workaround for http://bugs.python.org/issue3445
-    assigned = tuple(attr for attr in assigned if hasattr(wrapped, attr))
-    wrapper = functools.update_wrapper(wrapper, wrapped, assigned, updated)
-    # workaround for https://bugs.python.org/issue17482
-    wrapper.__wrapped__ = wrapped
-    return wrapper
-
-
-class _HashedSeq(list):
-    __slots__ = 'hashvalue'
-
-    def __init__(self, tup, hash=hash):
-        self[:] = tup
-        self.hashvalue = hash(tup)
-
-    def __hash__(self):
-        return self.hashvalue
-
-
-def _make_key(args, kwds, typed,
-              kwd_mark=(object(),),
-              fasttypes=set([int, str, frozenset, type(None)]),
-              sorted=sorted, tuple=tuple, type=type, len=len):
-    'Make a cache key from optionally typed positional and keyword arguments'
-    key = args
-    if kwds:
-        sorted_items = sorted(kwds.items())
-        key += kwd_mark
-        for item in sorted_items:
-            key += item
-    if typed:
-        key += tuple(type(v) for v in args)
-        if kwds:
-            key += tuple(type(v) for k, v in sorted_items)
-    elif len(key) == 1 and type(key[0]) in fasttypes:
-        return key[0]
-    return _HashedSeq(key)
-
-
-def lru_cache(maxsize=100, typed=False):
-    """Least-recently-used cache decorator.
-
-    If *maxsize* is set to None, the LRU features are disabled and the cache
-    can grow without bound.
-
-    If *typed* is True, arguments of different types will be cached separately.
-    For example, f(3.0) and f(3) will be treated as distinct calls with
-    distinct results.
-
-    Arguments to the cached function must be hashable.
-
-    View the cache statistics named tuple (hits, misses, maxsize, currsize) with
-    f.cache_info().  Clear the cache and statistics with f.cache_clear().
-    Access the underlying function with f.__wrapped__.
-
-    See:  http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
-
-    """
-
-    # Users should only access the lru_cache through its public API:
-    #       cache_info, cache_clear, and f.__wrapped__
-    # The internals of the lru_cache are encapsulated for thread safety and
-    # to allow the implementation to change (including a possible C version).
-
-    def decorating_function(user_function):
-
-        cache = dict()
-        stats = [0, 0]                  # make statistics updateable non-locally
-        HITS, MISSES = 0, 1             # names for the stats fields
-        make_key = _make_key
-        cache_get = cache.get           # bound method to lookup key or return None
-        _len = len                      # localize the global len() function
-        lock = RLock()                  # because linkedlist updates aren't threadsafe
-        root = []                       # root of the circular doubly linked list
-        root[:] = [root, root, None, None]      # initialize by pointing to self
-        nonlocal_root = [root]                  # make updateable non-locally
-        PREV, NEXT, KEY, RESULT = 0, 1, 2, 3    # names for the link fields
-
-        if maxsize == 0:
-
-            def wrapper(*args, **kwds):
-                # no caching, just do a statistics update after a successful call
-                result = user_function(*args, **kwds)
-                stats[MISSES] += 1
-                return result
-
-        elif maxsize is None:
-
-            def wrapper(*args, **kwds):
-                # simple caching without ordering or size limit
-                key = make_key(args, kwds, typed)
-                result = cache_get(key, root)   # root used here as a unique not-found sentinel
-                if result is not root:
-                    stats[HITS] += 1
-                    return result
-                result = user_function(*args, **kwds)
-                cache[key] = result
-                stats[MISSES] += 1
-                return result
-
-        else:
-
-            def wrapper(*args, **kwds):
-                # size limited caching that tracks accesses by recency
-                key = make_key(args, kwds, typed) if kwds or typed else args
-                with lock:
-                    link = cache_get(key)
-                    if link is not None:
-                        # record recent use of the key by moving it to the front of the list
-                        root, = nonlocal_root
-                        link_prev, link_next, key, result = link
-                        link_prev[NEXT] = link_next
-                        link_next[PREV] = link_prev
-                        last = root[PREV]
-                        last[NEXT] = root[PREV] = link
-                        link[PREV] = last
-                        link[NEXT] = root
-                        stats[HITS] += 1
-                        return result
-                result = user_function(*args, **kwds)
-                with lock:
-                    root, = nonlocal_root
-                    if key in cache:
-                        # getting here means that this same key was added to the
-                        # cache while the lock was released.  since the link
-                        # update is already done, we need only return the
-                        # computed result and update the count of misses.
-                        pass
-                    elif _len(cache) >= maxsize:
-                        # use the old root to store the new key and result
-                        oldroot = root
-                        oldroot[KEY] = key
-                        oldroot[RESULT] = result
-                        # empty the oldest link and make it the new root
-                        root = nonlocal_root[0] = oldroot[NEXT]
-                        oldkey = root[KEY]
-                        root[KEY] = root[RESULT] = None
-                        # now update the cache dictionary for the new links
-                        del cache[oldkey]
-                        cache[key] = oldroot
-                    else:
-                        # put result in a new link at the front of the list
-                        last = root[PREV]
-                        link = [last, root, key, result]
-                        last[NEXT] = root[PREV] = cache[key] = link
-                    stats[MISSES] += 1
-                return result
-
-        def cache_info():
-            """Report cache statistics"""
-            with lock:
-                return _CacheInfo(stats[HITS], stats[MISSES], maxsize, len(cache))
-
-        def cache_clear():
-            """Clear the cache and cache statistics"""
-            with lock:
-                cache.clear()
-                root = nonlocal_root[0]
-                root[:] = [root, root, None, None]
-                stats[:] = [0, 0]
-
-        wrapper.__wrapped__ = user_function
-        wrapper.cache_info = cache_info
-        wrapper.cache_clear = cache_clear
-        return update_wrapper(wrapper, user_function)
-
-    return decorating_function
diff --git a/libraries/cheroot/__init__.py b/libraries/cheroot/__init__.py
deleted file mode 100644
index a313660e..00000000
--- a/libraries/cheroot/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-"""High-performance, pure-Python HTTP server used by CherryPy."""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-__version__ = '6.4.0'
diff --git a/libraries/cheroot/__main__.py b/libraries/cheroot/__main__.py
deleted file mode 100644
index d2e27c10..00000000
--- a/libraries/cheroot/__main__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-"""Stub for accessing the Cheroot CLI tool."""
-
-from .cli import main
-
-if __name__ == '__main__':
-    main()
diff --git a/libraries/cheroot/_compat.py b/libraries/cheroot/_compat.py
deleted file mode 100644
index e98f91f9..00000000
--- a/libraries/cheroot/_compat.py
+++ /dev/null
@@ -1,66 +0,0 @@
-"""Compatibility code for using Cheroot with various versions of Python."""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import re
-
-import six
-
-if six.PY3:
-    def ntob(n, encoding='ISO-8859-1'):
-        """Return the native string as bytes in the given encoding."""
-        assert_native(n)
-        # In Python 3, the native string type is unicode
-        return n.encode(encoding)
-
-    def ntou(n, encoding='ISO-8859-1'):
-        """Return the native string as unicode with the given encoding."""
-        assert_native(n)
-        # In Python 3, the native string type is unicode
-        return n
-
-    def bton(b, encoding='ISO-8859-1'):
-        """Return the byte string as native string in the given encoding."""
-        return b.decode(encoding)
-else:
-    # Python 2
-    def ntob(n, encoding='ISO-8859-1'):
-        """Return the native string as bytes in the given encoding."""
-        assert_native(n)
-        # 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
-
-    def ntou(n, encoding='ISO-8859-1'):
-        """Return the native string as unicode with the given encoding."""
-        assert_native(n)
-        # In Python 2, the native string type is bytes.
-        # First, check for the special encoding 'escape'. The test suite uses
-        # this to signal that it wants to pass a string with embedded \uXXXX
-        # escapes, but without having to prefix it with u'' for Python 2,
-        # but no prefix for Python 3.
-        if encoding == 'escape':
-            return six.u(
-                re.sub(r'\\u([0-9a-zA-Z]{4})',
-                       lambda m: six.unichr(int(m.group(1), 16)),
-                       n.decode('ISO-8859-1')))
-        # Assume it's already in the given encoding, which for ISO-8859-1
-        # is almost always what was intended.
-        return n.decode(encoding)
-
-    def bton(b, encoding='ISO-8859-1'):
-        """Return the byte string as native string in the given encoding."""
-        return b
-
-
-def assert_native(n):
-    """Check whether the input is of nativ ``str`` type.
-
-    Raises:
-        TypeError: in case of failed check
-
-    """
-    if not isinstance(n, str):
-        raise TypeError('n must be a native str (got %s)' % type(n).__name__)
diff --git a/libraries/cheroot/cli.py b/libraries/cheroot/cli.py
deleted file mode 100644
index 6d59fb5c..00000000
--- a/libraries/cheroot/cli.py
+++ /dev/null
@@ -1,233 +0,0 @@
-"""Command line tool for starting a Cheroot WSGI/HTTP server instance.
-
-Basic usage::
-
-    # Start a server on 127.0.0.1:8000 with the default settings
-    # for the WSGI app myapp/wsgi.py:application()
-    cheroot myapp.wsgi
-
-    # Start a server on 0.0.0.0:9000 with 8 threads
-    # for the WSGI app myapp/wsgi.py:main_app()
-    cheroot myapp.wsgi:main_app --bind 0.0.0.0:9000 --threads 8
-
-    # Start a server for the cheroot.server.Gateway subclass
-    # myapp/gateway.py:HTTPGateway
-    cheroot myapp.gateway:HTTPGateway
-
-    # Start a server on the UNIX socket /var/spool/myapp.sock
-    cheroot myapp.wsgi --bind /var/spool/myapp.sock
-
-    # Start a server on the abstract UNIX socket CherootServer
-    cheroot myapp.wsgi --bind @CherootServer
-"""
-
-import argparse
-from importlib import import_module
-import os
-import sys
-import contextlib
-
-import six
-
-from . import server
-from . import wsgi
-
-
-__metaclass__ = type
-
-
-class BindLocation:
-    """A class for storing the bind location for a Cheroot instance."""
-
-
-class TCPSocket(BindLocation):
-    """TCPSocket."""
-
-    def __init__(self, address, port):
-        """Initialize.
-
-        Args:
-            address (str): Host name or IP address
-            port (int): TCP port number
-        """
-        self.bind_addr = address, port
-
-
-class UnixSocket(BindLocation):
-    """UnixSocket."""
-
-    def __init__(self, path):
-        """Initialize."""
-        self.bind_addr = path
-
-
-class AbstractSocket(BindLocation):
-    """AbstractSocket."""
-
-    def __init__(self, addr):
-        """Initialize."""
-        self.bind_addr = '\0{}'.format(self.abstract_socket)
-
-
-class Application:
-    """Application."""
-
-    @classmethod
-    def resolve(cls, full_path):
-        """Read WSGI app/Gateway path string and import application module."""
-        mod_path, _, app_path = full_path.partition(':')
-        app = getattr(import_module(mod_path), app_path or 'application')
-
-        with contextlib.suppress(TypeError):
-            if issubclass(app, server.Gateway):
-                return GatewayYo(app)
-
-        return cls(app)
-
-    def __init__(self, wsgi_app):
-        """Initialize."""
-        if not callable(wsgi_app):
-            raise TypeError(
-                'Application must be a callable object or '
-                'cheroot.server.Gateway subclass'
-            )
-        self.wsgi_app = wsgi_app
-
-    def server_args(self, parsed_args):
-        """Return keyword args for Server class."""
-        args = {
-            arg: value
-            for arg, value in vars(parsed_args).items()
-            if not arg.startswith('_') and value is not None
-        }
-        args.update(vars(self))
-        return args
-
-    def server(self, parsed_args):
-        """Server."""
-        return wsgi.Server(**self.server_args(parsed_args))
-
-
-class GatewayYo:
-    """Gateway."""
-
-    def __init__(self, gateway):
-        """Init."""
-        self.gateway = gateway
-
-    def server(self, parsed_args):
-        """Server."""
-        server_args = vars(self)
-        server_args['bind_addr'] = parsed_args['bind_addr']
-        if parsed_args.max is not None:
-            server_args['maxthreads'] = parsed_args.max
-        if parsed_args.numthreads is not None:
-            server_args['minthreads'] = parsed_args.numthreads
-        return server.HTTPServer(**server_args)
-
-
-def parse_wsgi_bind_location(bind_addr_string):
-    """Convert bind address string to a BindLocation."""
-    # try and match for an IP/hostname and port
-    match = six.moves.urllib.parse.urlparse('//{}'.format(bind_addr_string))
-    try:
-        addr = match.hostname
-        port = match.port
-        if addr is not None or port is not None:
-            return TCPSocket(addr, port)
-    except ValueError:
-        pass
-
-    # else, assume a UNIX socket path
-    # if the string begins with an @ symbol, use an abstract socket
-    if bind_addr_string.startswith('@'):
-        return AbstractSocket(bind_addr_string[1:])
-    return UnixSocket(path=bind_addr_string)
-
-
-def parse_wsgi_bind_addr(bind_addr_string):
-    """Convert bind address string to bind address parameter."""
-    return parse_wsgi_bind_location(bind_addr_string).bind_addr
-
-
-_arg_spec = {
-    '_wsgi_app': dict(
-        metavar='APP_MODULE',
-        type=Application.resolve,
-        help='WSGI application callable or cheroot.server.Gateway subclass',
-    ),
-    '--bind': dict(
-        metavar='ADDRESS',
-        dest='bind_addr',
-        type=parse_wsgi_bind_addr,
-        default='[::1]:8000',
-        help='Network interface to listen on (default: [::1]:8000)',
-    ),
-    '--chdir': dict(
-        metavar='PATH',
-        type=os.chdir,
-        help='Set the working directory',
-    ),
-    '--server-name': dict(
-        dest='server_name',
-        type=str,
-        help='Web server name to be advertised via Server HTTP header',
-    ),
-    '--threads': dict(
-        metavar='INT',
-        dest='numthreads',
-        type=int,
-        help='Minimum number of worker threads',
-    ),
-    '--max-threads': dict(
-        metavar='INT',
-        dest='max',
-        type=int,
-        help='Maximum number of worker threads',
-    ),
-    '--timeout': dict(
-        metavar='INT',
-        dest='timeout',
-        type=int,
-        help='Timeout in seconds for accepted connections',
-    ),
-    '--shutdown-timeout': dict(
-        metavar='INT',
-        dest='shutdown_timeout',
-        type=int,
-        help='Time in seconds to wait for worker threads to cleanly exit',
-    ),
-    '--request-queue-size': dict(
-        metavar='INT',
-        dest='request_queue_size',
-        type=int,
-        help='Maximum number of queued connections',
-    ),
-    '--accepted-queue-size': dict(
-        metavar='INT',
-        dest='accepted_queue_size',
-        type=int,
-        help='Maximum number of active requests in queue',
-    ),
-    '--accepted-queue-timeout': dict(
-        metavar='INT',
-        dest='accepted_queue_timeout',
-        type=int,
-        help='Timeout in seconds for putting requests into queue',
-    ),
-}
-
-
-def main():
-    """Create a new Cheroot instance with arguments from the command line."""
-    parser = argparse.ArgumentParser(
-        description='Start an instance of the Cheroot WSGI/HTTP server.')
-    for arg, spec in _arg_spec.items():
-        parser.add_argument(arg, **spec)
-    raw_args = parser.parse_args()
-
-    # ensure cwd in sys.path
-    '' in sys.path or sys.path.insert(0, '')
-
-    # create a server based on the arguments provided
-    raw_args._wsgi_app.server(raw_args).safe_start()
diff --git a/libraries/cheroot/errors.py b/libraries/cheroot/errors.py
deleted file mode 100644
index 82412b42..00000000
--- a/libraries/cheroot/errors.py
+++ /dev/null
@@ -1,58 +0,0 @@
-"""Collection of exceptions raised and/or processed by Cheroot."""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import errno
-import sys
-
-
-class MaxSizeExceeded(Exception):
-    """Exception raised when a client sends more data then acceptable within limit.
-
-    Depends on ``request.body.maxbytes`` config option if used within CherryPy
-    """
-
-
-class NoSSLError(Exception):
-    """Exception raised when a client speaks HTTP to an HTTPS socket."""
-
-
-class FatalSSLAlert(Exception):
-    """Exception raised when the SSL implementation signals a fatal alert."""
-
-
-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.
-    """
-    errno_names = dir(errno)
-    nums = [getattr(errno, k) for k in errnames if k in errno_names]
-    # de-dupe the list
-    return list(dict.fromkeys(nums).keys())
-
-
-socket_error_eintr = plat_specific_errors('EINTR', 'WSAEINTR')
-
-socket_errors_to_ignore = plat_specific_errors(
-    'EPIPE',
-    'EBADF', 'WSAEBADF',
-    'ENOTSOCK', 'WSAENOTSOCK',
-    'ETIMEDOUT', 'WSAETIMEDOUT',
-    'ECONNREFUSED', 'WSAECONNREFUSED',
-    'ECONNRESET', 'WSAECONNRESET',
-    'ECONNABORTED', 'WSAECONNABORTED',
-    'ENETRESET', 'WSAENETRESET',
-    'EHOSTDOWN', 'EHOSTUNREACH',
-)
-socket_errors_to_ignore.append('timed out')
-socket_errors_to_ignore.append('The read operation timed out')
-socket_errors_nonblocking = plat_specific_errors(
-    'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
-
-if sys.platform == 'darwin':
-    socket_errors_to_ignore.extend(plat_specific_errors('EPROTOTYPE'))
-    socket_errors_nonblocking.extend(plat_specific_errors('EPROTOTYPE'))
diff --git a/libraries/cheroot/makefile.py b/libraries/cheroot/makefile.py
deleted file mode 100644
index a76f2eda..00000000
--- a/libraries/cheroot/makefile.py
+++ /dev/null
@@ -1,387 +0,0 @@
-"""Socket file object."""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import socket
-
-try:
-    # prefer slower Python-based io module
-    import _pyio as io
-except ImportError:
-    # Python 2.6
-    import io
-
-import six
-
-from . import errors
-
-
-class BufferedWriter(io.BufferedWriter):
-    """Faux file object attached to a socket object."""
-
-    def write(self, b):
-        """Write bytes to buffer."""
-        self._checkClosed()
-        if isinstance(b, str):
-            raise TypeError("can't write str to binary stream")
-
-        with self._write_lock:
-            self._write_buf.extend(b)
-            self._flush_unlocked()
-            return len(b)
-
-    def _flush_unlocked(self):
-        self._checkClosed('flush of closed file')
-        while self._write_buf:
-            try:
-                # ssl sockets only except 'bytes', not bytearrays
-                # so perhaps we should conditionally wrap this for perf?
-                n = self.raw.write(bytes(self._write_buf))
-            except io.BlockingIOError as e:
-                n = e.characters_written
-            del self._write_buf[:n]
-
-
-def MakeFile_PY3(sock, mode='r', bufsize=io.DEFAULT_BUFFER_SIZE):
-    """File object attached to a socket object."""
-    if 'r' in mode:
-        return io.BufferedReader(socket.SocketIO(sock, mode), bufsize)
-    else:
-        return BufferedWriter(socket.SocketIO(sock, mode), bufsize)
-
-
-class MakeFile_PY2(getattr(socket, '_fileobject', object)):
-    """Faux file object attached to a socket object."""
-
-    def __init__(self, *args, **kwargs):
-        """Initialize faux file object."""
-        self.bytes_read = 0
-        self.bytes_written = 0
-        socket._fileobject.__init__(self, *args, **kwargs)
-
-    def write(self, data):
-        """Sendall for non-blocking sockets."""
-        while data:
-            try:
-                bytes_sent = self.send(data)
-                data = data[bytes_sent:]
-            except socket.error as e:
-                if e.args[0] not in errors.socket_errors_nonblocking:
-                    raise
-
-    def send(self, data):
-        """Send some part of message to the socket."""
-        bytes_sent = self._sock.send(data)
-        self.bytes_written += bytes_sent
-        return bytes_sent
-
-    def flush(self):
-        """Write all data from buffer to socket and reset write buffer."""
-        if self._wbuf:
-            buffer = ''.join(self._wbuf)
-            self._wbuf = []
-            self.write(buffer)
-
-    def recv(self, size):
-        """Receive message of a size from the socket."""
-        while True:
-            try:
-                data = self._sock.recv(size)
-                self.bytes_read += len(data)
-                return data
-            except socket.error as e:
-                what = (
-                    e.args[0] not in errors.socket_errors_nonblocking
-                    and e.args[0] not in errors.socket_error_eintr
-                )
-                if what:
-                    raise
-
-    class FauxSocket:
-        """Faux socket with the minimal interface required by pypy."""
-
-        def _reuse(self):
-            pass
-
-    _fileobject_uses_str_type = six.PY2 and isinstance(
-        socket._fileobject(FauxSocket())._rbuf, six.string_types)
-
-    # FauxSocket is no longer needed
-    del FauxSocket
-
-    if not _fileobject_uses_str_type:
-        def read(self, size=-1):
-            """Read data from the socket to buffer."""
-            # Use max, disallow tiny reads in a loop as they are very
-            # inefficient.
-            # We never leave read() with any leftover data from a new recv()
-            # call in our internal buffer.
-            rbufsize = max(self._rbufsize, self.default_bufsize)
-            # Our use of StringIO rather than lists of string objects returned
-            # by recv() minimizes memory usage and fragmentation that occurs
-            # when rbufsize is large compared to the typical return value of
-            # recv().
-            buf = self._rbuf
-            buf.seek(0, 2)  # seek end
-            if size < 0:
-                # Read until EOF
-                # reset _rbuf.  we consume it via buf.
-                self._rbuf = io.BytesIO()
-                while True:
-                    data = self.recv(rbufsize)
-                    if not data:
-                        break
-                    buf.write(data)
-                return buf.getvalue()
-            else:
-                # Read until size bytes or EOF seen, whichever comes first
-                buf_len = buf.tell()
-                if buf_len >= size:
-                    # Already have size bytes in our buffer?  Extract and
-                    # return.
-                    buf.seek(0)
-                    rv = buf.read(size)
-                    self._rbuf = io.BytesIO()
-                    self._rbuf.write(buf.read())
-                    return rv
-
-                # reset _rbuf.  we consume it via buf.
-                self._rbuf = io.BytesIO()
-                while True:
-                    left = size - buf_len
-                    # recv() will malloc the amount of memory given as its
-                    # parameter even though it often returns much less data
-                    # than that.  The returned data string is short lived
-                    # as we copy it into a StringIO and free it.  This avoids
-                    # fragmentation issues on many platforms.
-                    data = self.recv(left)
-                    if not data:
-                        break
-                    n = len(data)
-                    if n == size and not buf_len:
-                        # Shortcut.  Avoid buffer data copies when:
-                        # - We have no data in our buffer.
-                        # AND
-                        # - Our call to recv returned exactly the
-                        #   number of bytes we were asked to read.
-                        return data
-                    if n == left:
-                        buf.write(data)
-                        del data  # explicit free
-                        break
-                    assert n <= left, 'recv(%d) returned %d bytes' % (left, n)
-                    buf.write(data)
-                    buf_len += n
-                    del data  # explicit free
-                    # assert buf_len == buf.tell()
-                return buf.getvalue()
-
-        def readline(self, size=-1):
-            """Read line from the socket to buffer."""
-            buf = self._rbuf
-            buf.seek(0, 2)  # seek end
-            if buf.tell() > 0:
-                # check if we already have it in our buffer
-                buf.seek(0)
-                bline = buf.readline(size)
-                if bline.endswith('\n') or len(bline) == size:
-                    self._rbuf = io.BytesIO()
-                    self._rbuf.write(buf.read())
-                    return bline
-                del bline
-            if size < 0:
-                # Read until \n or EOF, whichever comes first
-                if self._rbufsize <= 1:
-                    # Speed up unbuffered case
-                    buf.seek(0)
-                    buffers = [buf.read()]
-                    # reset _rbuf.  we consume it via buf.
-                    self._rbuf = io.BytesIO()
-                    data = None
-                    recv = self.recv
-                    while data != '\n':
-                        data = recv(1)
-                        if not data:
-                            break
-                        buffers.append(data)
-                    return ''.join(buffers)
-
-                buf.seek(0, 2)  # seek end
-                # reset _rbuf.  we consume it via buf.
-                self._rbuf = io.BytesIO()
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    nl = data.find('\n')
-                    if nl >= 0:
-                        nl += 1
-                        buf.write(data[:nl])
-                        self._rbuf.write(data[nl:])
-                        del data
-                        break
-                    buf.write(data)
-                return buf.getvalue()
-            else:
-                # Read until size bytes or \n or EOF seen, whichever comes
-                # first
-                buf.seek(0, 2)  # seek end
-                buf_len = buf.tell()
-                if buf_len >= size:
-                    buf.seek(0)
-                    rv = buf.read(size)
-                    self._rbuf = io.BytesIO()
-                    self._rbuf.write(buf.read())
-                    return rv
-                # reset _rbuf.  we consume it via buf.
-                self._rbuf = io.BytesIO()
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    left = size - buf_len
-                    # did we just receive a newline?
-                    nl = data.find('\n', 0, left)
-                    if nl >= 0:
-                        nl += 1
-                        # save the excess data to _rbuf
-                        self._rbuf.write(data[nl:])
-                        if buf_len:
-                            buf.write(data[:nl])
-                            break
-                        else:
-                            # Shortcut.  Avoid data copy through buf when
-                            # returning a substring of our first recv().
-                            return data[:nl]
-                    n = len(data)
-                    if n == size and not buf_len:
-                        # Shortcut.  Avoid data copy through buf when
-                        # returning exactly all of our first recv().
-                        return data
-                    if n >= left:
-                        buf.write(data[:left])
-                        self._rbuf.write(data[left:])
-                        break
-                    buf.write(data)
-                    buf_len += n
-                    # assert buf_len == buf.tell()
-                return buf.getvalue()
-    else:
-        def read(self, size=-1):
-            """Read data from the socket to buffer."""
-            if size < 0:
-                # Read until EOF
-                buffers = [self._rbuf]
-                self._rbuf = ''
-                if self._rbufsize <= 1:
-                    recv_size = self.default_bufsize
-                else:
-                    recv_size = self._rbufsize
-
-                while True:
-                    data = self.recv(recv_size)
-                    if not data:
-                        break
-                    buffers.append(data)
-                return ''.join(buffers)
-            else:
-                # Read until size bytes or EOF seen, whichever comes first
-                data = self._rbuf
-                buf_len = len(data)
-                if buf_len >= size:
-                    self._rbuf = data[size:]
-                    return data[:size]
-                buffers = []
-                if data:
-                    buffers.append(data)
-                self._rbuf = ''
-                while True:
-                    left = size - buf_len
-                    recv_size = max(self._rbufsize, left)
-                    data = self.recv(recv_size)
-                    if not data:
-                        break
-                    buffers.append(data)
-                    n = len(data)
-                    if n >= left:
-                        self._rbuf = data[left:]
-                        buffers[-1] = data[:left]
-                        break
-                    buf_len += n
-                return ''.join(buffers)
-
-        def readline(self, size=-1):
-            """Read line from the socket to buffer."""
-            data = self._rbuf
-            if size < 0:
-                # Read until \n or EOF, whichever comes first
-                if self._rbufsize <= 1:
-                    # Speed up unbuffered case
-                    assert data == ''
-                    buffers = []
-                    while data != '\n':
-                        data = self.recv(1)
-                        if not data:
-                            break
-                        buffers.append(data)
-                    return ''.join(buffers)
-                nl = data.find('\n')
-                if nl >= 0:
-                    nl += 1
-                    self._rbuf = data[nl:]
-                    return data[:nl]
-                buffers = []
-                if data:
-                    buffers.append(data)
-                self._rbuf = ''
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    buffers.append(data)
-                    nl = data.find('\n')
-                    if nl >= 0:
-                        nl += 1
-                        self._rbuf = data[nl:]
-                        buffers[-1] = data[:nl]
-                        break
-                return ''.join(buffers)
-            else:
-                # Read until size bytes or \n or EOF seen, whichever comes
-                # first
-                nl = data.find('\n', 0, size)
-                if nl >= 0:
-                    nl += 1
-                    self._rbuf = data[nl:]
-                    return data[:nl]
-                buf_len = len(data)
-                if buf_len >= size:
-                    self._rbuf = data[size:]
-                    return data[:size]
-                buffers = []
-                if data:
-                    buffers.append(data)
-                self._rbuf = ''
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    buffers.append(data)
-                    left = size - buf_len
-                    nl = data.find('\n', 0, left)
-                    if nl >= 0:
-                        nl += 1
-                        self._rbuf = data[nl:]
-                        buffers[-1] = data[:nl]
-                        break
-                    n = len(data)
-                    if n >= left:
-                        self._rbuf = data[left:]
-                        buffers[-1] = data[:left]
-                        break
-                    buf_len += n
-                return ''.join(buffers)
-
-
-MakeFile = MakeFile_PY2 if six.PY2 else MakeFile_PY3
diff --git a/libraries/cheroot/server.py b/libraries/cheroot/server.py
deleted file mode 100644
index 44070490..00000000
--- a/libraries/cheroot/server.py
+++ /dev/null
@@ -1,2001 +0,0 @@
-"""
-A high-speed, production ready, thread pooled, generic HTTP server.
-
-For those of you wanting to understand internals of this module, here's the
-basic call flow. The server's listening thread runs a very tight loop,
-sticking incoming connections onto a Queue::
-
-    server = HTTPServer(...)
-    server.start()
-    ->  while True:
-            tick()
-            # This blocks until a request comes in:
-            child = socket.accept()
-            conn = HTTPConnection(child, ...)
-            server.requests.put(conn)
-
-Worker threads are kept in a pool and poll the Queue, popping off and then
-handling each connection in turn. Each connection can consist of an arbitrary
-number of requests and their responses, so we run a nested loop::
-
-    while True:
-        conn = server.requests.get()
-        conn.communicate()
-        ->  while True:
-                req = HTTPRequest(...)
-                req.parse_request()
-                ->  # Read the Request-Line, e.g. "GET /page HTTP/1.1"
-                    req.rfile.readline()
-                    read_headers(req.rfile, req.inheaders)
-                req.respond()
-                ->  response = app(...)
-                    try:
-                        for chunk in response:
-                            if chunk:
-                                req.write(chunk)
-                    finally:
-                        if hasattr(response, "close"):
-                            response.close()
-                if req.close_connection:
-                    return
-
-For running a server you can invoke :func:`start() <HTTPServer.start()>` (it
-will run the server forever) or use invoking :func:`prepare()
-<HTTPServer.prepare()>` and :func:`serve() <HTTPServer.serve()>` like this::
-
-    server = HTTPServer(...)
-    server.prepare()
-    try:
-        threading.Thread(target=server.serve).start()
-
-        # waiting/detecting some appropriate stop condition here
-        ...
-
-    finally:
-        server.stop()
-
-And now for a trivial doctest to exercise the test suite
-
->>> 'HTTPServer' in globals()
-True
-
-"""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import os
-import io
-import re
-import email.utils
-import socket
-import sys
-import time
-import traceback as traceback_
-import logging
-import platform
-import xbmc
-
-try:
-    from functools import lru_cache
-except ImportError:
-    from backports.functools_lru_cache import lru_cache
-
-import six
-from six.moves import queue
-from six.moves import urllib
-
-from . import errors, __version__
-from ._compat import bton, ntou
-from .workers import threadpool
-from .makefile import MakeFile
-
-
-__all__ = ('HTTPRequest', 'HTTPConnection', 'HTTPServer',
-           'SizeCheckWrapper', 'KnownLengthRFile', 'ChunkedRFile',
-           'Gateway', 'get_ssl_adapter_class')
-
-"""
-Special KODI case:
-Android does not have support for grp and pwd
-But Python has issues reporting that this is running on Android (it shows as Linux2).
-We're instead using xbmc library to detect that.
-"""
-IS_WINDOWS = platform.system() == 'Windows'
-IS_ANDROID = xbmc.getCondVisibility('system.platform.linux') and xbmc.getCondVisibility('system.platform.android')
-
-if not (IS_WINDOWS or IS_ANDROID):
-    import grp
-    import pwd
-    import struct
-
-
-if IS_WINDOWS and hasattr(socket, 'AF_INET6'):
-    if not hasattr(socket, 'IPPROTO_IPV6'):
-        socket.IPPROTO_IPV6 = 41
-    if not hasattr(socket, 'IPV6_V6ONLY'):
-        socket.IPV6_V6ONLY = 27
-
-
-if not hasattr(socket, 'SO_PEERCRED'):
-    """
-    NOTE: the value for SO_PEERCRED can be architecture specific, in
-    which case the getsockopt() will hopefully fail. The arch
-    specific value could be derived from platform.processor()
-    """
-    socket.SO_PEERCRED = 17
-
-
-LF = b'\n'
-CRLF = b'\r\n'
-TAB = b'\t'
-SPACE = b' '
-COLON = b':'
-SEMICOLON = b';'
-EMPTY = b''
-ASTERISK = b'*'
-FORWARD_SLASH = b'/'
-QUOTED_SLASH = b'%2F'
-QUOTED_SLASH_REGEX = re.compile(b'(?i)' + QUOTED_SLASH)
-
-
-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',
-]
-
-
-if not hasattr(logging, 'statistics'):
-    logging.statistics = {}
-
-
-class HeaderReader:
-    """Object for reading headers from an HTTP request.
-
-    Interface and default implementation.
-    """
-
-    def __call__(self, 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()
-            else:
-                try:
-                    k, v = line.split(COLON, 1)
-                except ValueError:
-                    raise ValueError('Illegal header line.')
-                v = v.strip()
-                k = self._transform_key(k)
-                hname = k
-
-            if not self._allow_header(k):
-                continue
-
-            if k in comma_separated_headers:
-                existing = hdict.get(hname)
-                if existing:
-                    v = b', '.join((existing, v))
-            hdict[hname] = v
-
-        return hdict
-
-    def _allow_header(self, key_name):
-        return True
-
-    def _transform_key(self, key_name):
-        # TODO: what about TE and WWW-Authenticate?
-        return key_name.strip().title()
-
-
-class DropUnderscoreHeaderReader(HeaderReader):
-    """Custom HeaderReader to exclude any headers with underscores in them."""
-
-    def _allow_header(self, key_name):
-        orig = super(DropUnderscoreHeaderReader, self)._allow_header(key_name)
-        return orig and '_' not in key_name
-
-
-class SizeCheckWrapper:
-    """Wraps a file-like object, raising MaxSizeExceeded if too large."""
-
-    def __init__(self, rfile, maxlen):
-        """Initialize SizeCheckWrapper instance.
-
-        Args:
-            rfile (file): file of a limited size
-            maxlen (int): maximum length of the file being read
-        """
-        self.rfile = rfile
-        self.maxlen = maxlen
-        self.bytes_read = 0
-
-    def _check_length(self):
-        if self.maxlen and self.bytes_read > self.maxlen:
-            raise errors.MaxSizeExceeded()
-
-    def read(self, size=None):
-        """Read a chunk from rfile buffer and return it.
-
-        Args:
-            size (int): amount of data to read
-
-        Returns:
-            bytes: Chunk from rfile, limited by size if specified.
-
-        """
-        data = self.rfile.read(size)
-        self.bytes_read += len(data)
-        self._check_length()
-        return data
-
-    def readline(self, size=None):
-        """Read a single line from rfile buffer and return it.
-
-        Args:
-            size (int): minimum amount of data to read
-
-        Returns:
-            bytes: One line from rfile.
-
-        """
-        if size is not None:
-            data = self.rfile.readline(size)
-            self.bytes_read += len(data)
-            self._check_length()
-            return data
-
-        # User didn't specify a size ...
-        # We read the line in chunks to make sure it's not a 100MB line !
-        res = []
-        while True:
-            data = self.rfile.readline(256)
-            self.bytes_read += len(data)
-            self._check_length()
-            res.append(data)
-            # See https://github.com/cherrypy/cherrypy/issues/421
-            if len(data) < 256 or data[-1:] == LF:
-                return EMPTY.join(res)
-
-    def readlines(self, sizehint=0):
-        """Read all lines from rfile buffer and return them.
-
-        Args:
-            sizehint (int): hint of minimum amount of data to read
-
-        Returns:
-            list[bytes]: Lines of bytes read from rfile.
-
-        """
-        # Shamelessly stolen from StringIO
-        total = 0
-        lines = []
-        line = self.readline(sizehint)
-        while line:
-            lines.append(line)
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline(sizehint)
-        return lines
-
-    def close(self):
-        """Release resources allocated for rfile."""
-        self.rfile.close()
-
-    def __iter__(self):
-        """Return file iterator."""
-        return self
-
-    def __next__(self):
-        """Generate next file chunk."""
-        data = next(self.rfile)
-        self.bytes_read += len(data)
-        self._check_length()
-        return data
-
-    next = __next__
-
-
-class KnownLengthRFile:
-    """Wraps a file-like object, returning an empty string when exhausted."""
-
-    def __init__(self, rfile, content_length):
-        """Initialize KnownLengthRFile instance.
-
-        Args:
-            rfile (file): file of a known size
-            content_length (int): length of the file being read
-
-        """
-        self.rfile = rfile
-        self.remaining = content_length
-
-    def read(self, size=None):
-        """Read a chunk from rfile buffer and return it.
-
-        Args:
-            size (int): amount of data to read
-
-        Returns:
-            bytes: Chunk from rfile, limited by size if specified.
-
-        """
-        if self.remaining == 0:
-            return b''
-        if size is None:
-            size = self.remaining
-        else:
-            size = min(size, self.remaining)
-
-        data = self.rfile.read(size)
-        self.remaining -= len(data)
-        return data
-
-    def readline(self, size=None):
-        """Read a single line from rfile buffer and return it.
-
-        Args:
-            size (int): minimum amount of data to read
-
-        Returns:
-            bytes: One line from rfile.
-
-        """
-        if self.remaining == 0:
-            return b''
-        if size is None:
-            size = self.remaining
-        else:
-            size = min(size, self.remaining)
-
-        data = self.rfile.readline(size)
-        self.remaining -= len(data)
-        return data
-
-    def readlines(self, sizehint=0):
-        """Read all lines from rfile buffer and return them.
-
-        Args:
-            sizehint (int): hint of minimum amount of data to read
-
-        Returns:
-            list[bytes]: Lines of bytes read from rfile.
-
-        """
-        # Shamelessly stolen from StringIO
-        total = 0
-        lines = []
-        line = self.readline(sizehint)
-        while line:
-            lines.append(line)
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline(sizehint)
-        return lines
-
-    def close(self):
-        """Release resources allocated for rfile."""
-        self.rfile.close()
-
-    def __iter__(self):
-        """Return file iterator."""
-        return self
-
-    def __next__(self):
-        """Generate next file chunk."""
-        data = next(self.rfile)
-        self.remaining -= len(data)
-        return data
-
-    next = __next__
-
-
-class ChunkedRFile:
-    """Wraps a file-like object, returning an empty string when exhausted.
-
-    This class is intended to provide a conforming wsgi.input value for
-    request entities that have been encoded with the 'chunked' transfer
-    encoding.
-    """
-
-    def __init__(self, rfile, maxlen, bufsize=8192):
-        """Initialize ChunkedRFile instance.
-
-        Args:
-            rfile (file): file encoded with the 'chunked' transfer encoding
-            maxlen (int): maximum length of the file being read
-            bufsize (int): size of the buffer used to read the file
-        """
-        self.rfile = rfile
-        self.maxlen = maxlen
-        self.bytes_read = 0
-        self.buffer = EMPTY
-        self.bufsize = bufsize
-        self.closed = False
-
-    def _fetch(self):
-        if self.closed:
-            return
-
-        line = self.rfile.readline()
-        self.bytes_read += len(line)
-
-        if self.maxlen and self.bytes_read > self.maxlen:
-            raise errors.MaxSizeExceeded(
-                'Request Entity Too Large', self.maxlen)
-
-        line = line.strip().split(SEMICOLON, 1)
-
-        try:
-            chunk_size = line.pop(0)
-            chunk_size = int(chunk_size, 16)
-        except ValueError:
-            raise ValueError('Bad chunked transfer size: ' + repr(chunk_size))
-
-        if chunk_size <= 0:
-            self.closed = True
-            return
-
-#            if line: chunk_extension = line[0]
-
-        if self.maxlen and self.bytes_read + chunk_size > self.maxlen:
-            raise IOError('Request Entity Too Large')
-
-        chunk = self.rfile.read(chunk_size)
-        self.bytes_read += len(chunk)
-        self.buffer += chunk
-
-        crlf = self.rfile.read(2)
-        if crlf != CRLF:
-            raise ValueError(
-                "Bad chunked transfer coding (expected '\\r\\n', "
-                'got ' + repr(crlf) + ')')
-
-    def read(self, size=None):
-        """Read a chunk from rfile buffer and return it.
-
-        Args:
-            size (int): amount of data to read
-
-        Returns:
-            bytes: Chunk from rfile, limited by size if specified.
-
-        """
-        data = EMPTY
-
-        if size == 0:
-            return data
-
-        while True:
-            if size and len(data) >= size:
-                return data
-
-            if not self.buffer:
-                self._fetch()
-                if not self.buffer:
-                    # EOF
-                    return data
-
-            if size:
-                remaining = size - len(data)
-                data += self.buffer[:remaining]
-                self.buffer = self.buffer[remaining:]
-            else:
-                data += self.buffer
-                self.buffer = EMPTY
-
-    def readline(self, size=None):
-        """Read a single line from rfile buffer and return it.
-
-        Args:
-            size (int): minimum amount of data to read
-
-        Returns:
-            bytes: One line from rfile.
-
-        """
-        data = EMPTY
-
-        if size == 0:
-            return data
-
-        while True:
-            if size and len(data) >= size:
-                return data
-
-            if not self.buffer:
-                self._fetch()
-                if not self.buffer:
-                    # EOF
-                    return data
-
-            newline_pos = self.buffer.find(LF)
-            if size:
-                if newline_pos == -1:
-                    remaining = size - len(data)
-                    data += self.buffer[:remaining]
-                    self.buffer = self.buffer[remaining:]
-                else:
-                    remaining = min(size - len(data), newline_pos)
-                    data += self.buffer[:remaining]
-                    self.buffer = self.buffer[remaining:]
-            else:
-                if newline_pos == -1:
-                    data += self.buffer
-                    self.buffer = EMPTY
-                else:
-                    data += self.buffer[:newline_pos]
-                    self.buffer = self.buffer[newline_pos:]
-
-    def readlines(self, sizehint=0):
-        """Read all lines from rfile buffer and return them.
-
-        Args:
-            sizehint (int): hint of minimum amount of data to read
-
-        Returns:
-            list[bytes]: Lines of bytes read from rfile.
-
-        """
-        # Shamelessly stolen from StringIO
-        total = 0
-        lines = []
-        line = self.readline(sizehint)
-        while line:
-            lines.append(line)
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline(sizehint)
-        return lines
-
-    def read_trailer_lines(self):
-        """Read HTTP headers and yield them.
-
-        Returns:
-            Generator: yields CRLF separated lines.
-
-        """
-        if not self.closed:
-            raise ValueError(
-                'Cannot read trailers until the request body has been read.')
-
-        while True:
-            line = self.rfile.readline()
-            if not line:
-                # No more data--illegal end of headers
-                raise ValueError('Illegal end of headers.')
-
-            self.bytes_read += len(line)
-            if self.maxlen and self.bytes_read > self.maxlen:
-                raise IOError('Request Entity Too Large')
-
-            if line == CRLF:
-                # Normal end of headers
-                break
-            if not line.endswith(CRLF):
-                raise ValueError('HTTP requires CRLF terminators')
-
-            yield line
-
-    def close(self):
-        """Release resources allocated for rfile."""
-        self.rfile.close()
-
-
-class HTTPRequest:
-    """An HTTP Request (and response).
-
-    A single HTTP connection may consist of multiple request/response pairs.
-    """
-
-    server = None
-    """The HTTPServer object which is receiving this request."""
-
-    conn = None
-    """The HTTPConnection object on which this request connected."""
-
-    inheaders = {}
-    """A dict of request headers."""
-
-    outheaders = []
-    """A list of header tuples to write in the response."""
-
-    ready = False
-    """When True, the request has been parsed and is ready to begin generating
-    the response. When False, signals the calling Connection that the response
-    should not be generated and the connection should close."""
-
-    close_connection = False
-    """Signals the calling Connection that the request should close. This does
-    not imply an error! The client and/or server may each request that the
-    connection be closed."""
-
-    chunked_write = False
-    """If True, output will be encoded with the "chunked" transfer-coding.
-
-    This value is set automatically inside send_headers."""
-
-    header_reader = HeaderReader()
-    """
-    A HeaderReader instance or compatible reader.
-    """
-
-    def __init__(self, server, conn, proxy_mode=False, strict_mode=True):
-        """Initialize HTTP request container instance.
-
-        Args:
-            server (HTTPServer): web server object receiving this request
-            conn (HTTPConnection): HTTP connection object for this request
-            proxy_mode (bool): whether this HTTPServer should behave as a PROXY
-            server for certain requests
-            strict_mode (bool): whether we should return a 400 Bad Request when
-            we encounter a request that a HTTP compliant client should not be
-            making
-        """
-        self.server = server
-        self.conn = conn
-
-        self.ready = False
-        self.started_request = False
-        self.scheme = b'http'
-        if self.server.ssl_adapter is not None:
-            self.scheme = b'https'
-        # Use the lowest-common protocol in case read_request_line errors.
-        self.response_protocol = 'HTTP/1.0'
-        self.inheaders = {}
-
-        self.status = ''
-        self.outheaders = []
-        self.sent_headers = False
-        self.close_connection = self.__class__.close_connection
-        self.chunked_read = False
-        self.chunked_write = self.__class__.chunked_write
-        self.proxy_mode = proxy_mode
-        self.strict_mode = strict_mode
-
-    def parse_request(self):
-        """Parse the next HTTP request start-line and message-headers."""
-        self.rfile = SizeCheckWrapper(self.conn.rfile,
-                                      self.server.max_request_header_size)
-        try:
-            success = self.read_request_line()
-        except errors.MaxSizeExceeded:
-            self.simple_response(
-                '414 Request-URI Too Long',
-                'The Request-URI sent with the request exceeds the maximum '
-                'allowed bytes.')
-            return
-        else:
-            if not success:
-                return
-
-        try:
-            success = self.read_request_headers()
-        except errors.MaxSizeExceeded:
-            self.simple_response(
-                '413 Request Entity Too Large',
-                'The headers sent with the request exceed the maximum '
-                'allowed bytes.')
-            return
-        else:
-            if not success:
-                return
-
-        self.ready = True
-
-    def read_request_line(self):
-        """Read and parse first line of the HTTP request.
-
-        Returns:
-            bool: True if the request line is valid or False if it's malformed.
-
-        """
-        # HTTP/1.1 connections are persistent by default. If a client
-        # requests a page, then idles (leaves the connection open),
-        # then rfile.readline() will raise socket.error("timed out").
-        # Note that it does this based on the value given to settimeout(),
-        # and doesn't need the client to request or acknowledge the close
-        # (although your TCP stack might suffer for it: cf Apache's history
-        # with FIN_WAIT_2).
-        request_line = self.rfile.readline()
-
-        # Set started_request to True so communicate() knows to send 408
-        # from here on out.
-        self.started_request = True
-        if not request_line:
-            return False
-
-        if request_line == CRLF:
-            # RFC 2616 sec 4.1: "...if the server is reading the protocol
-            # stream at the beginning of a message and receives a CRLF
-            # first, it should ignore the CRLF."
-            # But only ignore one leading line! else we enable a DoS.
-            request_line = self.rfile.readline()
-            if not request_line:
-                return False
-
-        if not request_line.endswith(CRLF):
-            self.simple_response(
-                '400 Bad Request', 'HTTP requires CRLF terminators')
-            return False
-
-        try:
-            method, uri, req_protocol = request_line.strip().split(SPACE, 2)
-            if not req_protocol.startswith(b'HTTP/'):
-                self.simple_response(
-                    '400 Bad Request', 'Malformed Request-Line: bad protocol'
-                )
-                return False
-            rp = req_protocol[5:].split(b'.', 1)
-            rp = tuple(map(int, rp))  # Minor.Major must be threat as integers
-            if rp > (1, 1):
-                self.simple_response(
-                    '505 HTTP Version Not Supported', 'Cannot fulfill request'
-                )
-                return False
-        except (ValueError, IndexError):
-            self.simple_response('400 Bad Request', 'Malformed Request-Line')
-            return False
-
-        self.uri = uri
-        self.method = method.upper()
-
-        if self.strict_mode and method != self.method:
-            resp = (
-                'Malformed method name: According to RFC 2616 '
-                '(section 5.1.1) and its successors '
-                'RFC 7230 (section 3.1.1) and RFC 7231 (section 4.1) '
-                'method names are case-sensitive and uppercase.'
-            )
-            self.simple_response('400 Bad Request', resp)
-            return False
-
-        try:
-            if six.PY2:  # FIXME: Figure out better way to do this
-                # Ref: https://stackoverflow.com/a/196392/595220 (like this?)
-                """This is a dummy check for unicode in URI."""
-                ntou(bton(uri, 'ascii'), 'ascii')
-            scheme, authority, path, qs, fragment = urllib.parse.urlsplit(uri)
-        except UnicodeError:
-            self.simple_response('400 Bad Request', 'Malformed Request-URI')
-            return False
-
-        if self.method == b'OPTIONS':
-            # TODO: cover this branch with tests
-            path = (uri
-                    # https://tools.ietf.org/html/rfc7230#section-5.3.4
-                    if self.proxy_mode or uri == ASTERISK
-                    else path)
-        elif self.method == b'CONNECT':
-            # TODO: cover this branch with tests
-            if not self.proxy_mode:
-                self.simple_response('405 Method Not Allowed')
-                return False
-
-            # `urlsplit()` above parses "example.com:3128" as path part of URI.
-            # this is a workaround, which makes it detect netloc correctly
-            uri_split = urllib.parse.urlsplit(b'//' + uri)
-            _scheme, _authority, _path, _qs, _fragment = uri_split
-            _port = EMPTY
-            try:
-                _port = uri_split.port
-            except ValueError:
-                pass
-
-            # FIXME: use third-party validation to make checks against RFC
-            # the validation doesn't take into account, that urllib parses
-            # invalid URIs without raising errors
-            # https://tools.ietf.org/html/rfc7230#section-5.3.3
-            invalid_path = (
-                _authority != uri
-                or not _port
-                or any((_scheme, _path, _qs, _fragment))
-            )
-            if invalid_path:
-                self.simple_response('400 Bad Request',
-                                     'Invalid path in Request-URI: request-'
-                                     'target must match authority-form.')
-                return False
-
-            authority = path = _authority
-            scheme = qs = fragment = EMPTY
-        else:
-            uri_is_absolute_form = (scheme or authority)
-
-            disallowed_absolute = (
-                self.strict_mode
-                and not self.proxy_mode
-                and uri_is_absolute_form
-            )
-            if disallowed_absolute:
-                # https://tools.ietf.org/html/rfc7230#section-5.3.2
-                # (absolute form)
-                """Absolute URI is only allowed within proxies."""
-                self.simple_response(
-                    '400 Bad Request',
-                    'Absolute URI not allowed if server is not a proxy.',
-                )
-                return False
-
-            invalid_path = (
-                self.strict_mode
-                and not uri.startswith(FORWARD_SLASH)
-                and not uri_is_absolute_form
-            )
-            if invalid_path:
-                # https://tools.ietf.org/html/rfc7230#section-5.3.1
-                # (origin_form) and
-                """Path should start with a forward slash."""
-                resp = (
-                    'Invalid path in Request-URI: request-target must contain '
-                    'origin-form which starts with absolute-path (URI '
-                    'starting with a slash "/").'
-                )
-                self.simple_response('400 Bad Request', resp)
-                return False
-
-            if fragment:
-                self.simple_response('400 Bad Request',
-                                     'Illegal #fragment in Request-URI.')
-                return False
-
-            if path is None:
-                # FIXME: It looks like this case cannot happen
-                self.simple_response('400 Bad Request',
-                                     'Invalid path in Request-URI.')
-                return False
-
-            # Unquote the path+params (e.g. "/this%20path" -> "/this path").
-            # https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
-            #
-            # But note that "...a URI must be separated into its components
-            # before the escaped characters within those components can be
-            # safely decoded." https://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2
-            # Therefore, "/this%2Fpath" becomes "/this%2Fpath", not
-            # "/this/path".
-            try:
-                # TODO: Figure out whether exception can really happen here.
-                # It looks like it's caught on urlsplit() call above.
-                atoms = [
-                    urllib.parse.unquote_to_bytes(x)
-                    for x in QUOTED_SLASH_REGEX.split(path)
-                ]
-            except ValueError as ex:
-                self.simple_response('400 Bad Request', ex.args[0])
-                return False
-            path = QUOTED_SLASH.join(atoms)
-
-        if not path.startswith(FORWARD_SLASH):
-            path = FORWARD_SLASH + path
-
-        if scheme is not EMPTY:
-            self.scheme = scheme
-        self.authority = authority
-        self.path = path
-
-        # Note that, like wsgiref and most other HTTP servers,
-        # we "% HEX HEX"-unquote the path but not the query string.
-        self.qs = qs
-
-        # Compare request and server HTTP protocol versions, in case our
-        # server does not support the requested protocol. Limit our output
-        # to min(req, server). We want the following output:
-        #     request    server     actual written   supported response
-        #     protocol   protocol  response protocol    feature set
-        # a     1.0        1.0           1.0                1.0
-        # b     1.0        1.1           1.1                1.0
-        # c     1.1        1.0           1.0                1.0
-        # d     1.1        1.1           1.1                1.1
-        # Notice that, in (b), the response will be "HTTP/1.1" even though
-        # the client only understands 1.0. RFC 2616 10.5.6 says we should
-        # only return 505 if the _major_ version is different.
-        sp = int(self.server.protocol[5]), int(self.server.protocol[7])
-
-        if sp[0] != rp[0]:
-            self.simple_response('505 HTTP Version Not Supported')
-            return False
-
-        self.request_protocol = req_protocol
-        self.response_protocol = 'HTTP/%s.%s' % min(rp, sp)
-
-        return True
-
-    def read_request_headers(self):
-        """Read self.rfile into self.inheaders. Return success."""
-        # then all the http headers
-        try:
-            self.header_reader(self.rfile, self.inheaders)
-        except ValueError as ex:
-            self.simple_response('400 Bad Request', ex.args[0])
-            return False
-
-        mrbs = self.server.max_request_body_size
-
-        try:
-            cl = int(self.inheaders.get(b'Content-Length', 0))
-        except ValueError:
-            self.simple_response(
-                '400 Bad Request',
-                'Malformed Content-Length Header.')
-            return False
-
-        if mrbs and cl > mrbs:
-            self.simple_response(
-                '413 Request Entity Too Large',
-                'The entity sent with the request exceeds the maximum '
-                'allowed bytes.')
-            return False
-
-        # Persistent connection support
-        if self.response_protocol == 'HTTP/1.1':
-            # Both server and client are HTTP/1.1
-            if self.inheaders.get(b'Connection', b'') == b'close':
-                self.close_connection = True
-        else:
-            # Either the server or client (or both) are HTTP/1.0
-            if self.inheaders.get(b'Connection', b'') != b'Keep-Alive':
-                self.close_connection = True
-
-        # Transfer-Encoding support
-        te = None
-        if self.response_protocol == 'HTTP/1.1':
-            te = self.inheaders.get(b'Transfer-Encoding')
-            if te:
-                te = [x.strip().lower() for x in te.split(b',') if x.strip()]
-
-        self.chunked_read = False
-
-        if te:
-            for enc in te:
-                if enc == b'chunked':
-                    self.chunked_read = True
-                else:
-                    # Note that, even if we see "chunked", we must reject
-                    # if there is an extension we don't recognize.
-                    self.simple_response('501 Unimplemented')
-                    self.close_connection = True
-                    return False
-
-        # From PEP 333:
-        # "Servers and gateways that implement HTTP 1.1 must provide
-        # transparent support for HTTP 1.1's "expect/continue" mechanism.
-        # This may be done in any of several ways:
-        #   1. Respond to requests containing an Expect: 100-continue request
-        #      with an immediate "100 Continue" response, and proceed normally.
-        #   2. Proceed with the request normally, but provide the application
-        #      with a wsgi.input stream that will send the "100 Continue"
-        #      response if/when the application first attempts to read from
-        #      the input stream. The read request must then remain blocked
-        #      until the client responds.
-        #   3. Wait until the client decides that the server does not support
-        #      expect/continue, and sends the request body on its own.
-        #      (This is suboptimal, and is not recommended.)
-        #
-        # We used to do 3, but are now doing 1. Maybe we'll do 2 someday,
-        # but it seems like it would be a big slowdown for such a rare case.
-        if self.inheaders.get(b'Expect', b'') == b'100-continue':
-            # Don't use simple_response here, because it emits headers
-            # we don't want. See
-            # https://github.com/cherrypy/cherrypy/issues/951
-            msg = self.server.protocol.encode('ascii')
-            msg += b' 100 Continue\r\n\r\n'
-            try:
-                self.conn.wfile.write(msg)
-            except socket.error as ex:
-                if ex.args[0] not in errors.socket_errors_to_ignore:
-                    raise
-        return True
-
-    def respond(self):
-        """Call the gateway and write its iterable output."""
-        mrbs = self.server.max_request_body_size
-        if self.chunked_read:
-            self.rfile = ChunkedRFile(self.conn.rfile, mrbs)
-        else:
-            cl = int(self.inheaders.get(b'Content-Length', 0))
-            if mrbs and mrbs < cl:
-                if not self.sent_headers:
-                    self.simple_response(
-                        '413 Request Entity Too Large',
-                        'The entity sent with the request exceeds the '
-                        'maximum allowed bytes.')
-                return
-            self.rfile = KnownLengthRFile(self.conn.rfile, cl)
-
-        self.server.gateway(self).respond()
-        self.ready and self.ensure_headers_sent()
-
-        if self.chunked_write:
-            self.conn.wfile.write(b'0\r\n\r\n')
-
-    def simple_response(self, status, msg=''):
-        """Write a simple response back to the client."""
-        status = str(status)
-        proto_status = '%s %s\r\n' % (self.server.protocol, status)
-        content_length = 'Content-Length: %s\r\n' % len(msg)
-        content_type = 'Content-Type: text/plain\r\n'
-        buf = [
-            proto_status.encode('ISO-8859-1'),
-            content_length.encode('ISO-8859-1'),
-            content_type.encode('ISO-8859-1'),
-        ]
-
-        if status[:3] in ('413', '414'):
-            # Request Entity Too Large / Request-URI Too Long
-            self.close_connection = True
-            if self.response_protocol == 'HTTP/1.1':
-                # This will not be true for 414, since read_request_line
-                # usually raises 414 before reading the whole line, and we
-                # therefore cannot know the proper response_protocol.
-                buf.append(b'Connection: close\r\n')
-            else:
-                # HTTP/1.0 had no 413/414 status nor Connection header.
-                # Emit 400 instead and trust the message body is enough.
-                status = '400 Bad Request'
-
-        buf.append(CRLF)
-        if msg:
-            if isinstance(msg, six.text_type):
-                msg = msg.encode('ISO-8859-1')
-            buf.append(msg)
-
-        try:
-            self.conn.wfile.write(EMPTY.join(buf))
-        except socket.error as ex:
-            if ex.args[0] not in errors.socket_errors_to_ignore:
-                raise
-
-    def ensure_headers_sent(self):
-        """Ensure headers are sent to the client if not already sent."""
-        if not self.sent_headers:
-            self.sent_headers = True
-            self.send_headers()
-
-    def write(self, chunk):
-        """Write unbuffered data to the client."""
-        if self.chunked_write and chunk:
-            chunk_size_hex = hex(len(chunk))[2:].encode('ascii')
-            buf = [chunk_size_hex, CRLF, chunk, CRLF]
-            self.conn.wfile.write(EMPTY.join(buf))
-        else:
-            self.conn.wfile.write(chunk)
-
-    def send_headers(self):
-        """Assert, process, and send the HTTP response message-headers.
-
-        You must set self.status, and self.outheaders before calling this.
-        """
-        hkeys = [key.lower() for key, value in self.outheaders]
-        status = int(self.status[:3])
-
-        if status == 413:
-            # Request Entity Too Large. Close conn to avoid garbage.
-            self.close_connection = True
-        elif b'content-length' not in hkeys:
-            # "All 1xx (informational), 204 (no content),
-            # and 304 (not modified) responses MUST NOT
-            # include a message-body." So no point chunking.
-            if status < 200 or status in (204, 205, 304):
-                pass
-            else:
-                needs_chunked = (
-                    self.response_protocol == 'HTTP/1.1'
-                    and self.method != b'HEAD'
-                )
-                if needs_chunked:
-                    # Use the chunked transfer-coding
-                    self.chunked_write = True
-                    self.outheaders.append((b'Transfer-Encoding', b'chunked'))
-                else:
-                    # Closing the conn is the only way to determine len.
-                    self.close_connection = True
-
-        if b'connection' not in hkeys:
-            if self.response_protocol == 'HTTP/1.1':
-                # Both server and client are HTTP/1.1 or better
-                if self.close_connection:
-                    self.outheaders.append((b'Connection', b'close'))
-            else:
-                # Server and/or client are HTTP/1.0
-                if not self.close_connection:
-                    self.outheaders.append((b'Connection', b'Keep-Alive'))
-
-        if (not self.close_connection) and (not self.chunked_read):
-            # Read any remaining request body data on the socket.
-            # "If an origin server receives a request that does not include an
-            # Expect request-header field with the "100-continue" expectation,
-            # the request includes a request body, and the server responds
-            # with a final status code before reading the entire request body
-            # from the transport connection, then the server SHOULD NOT close
-            # the transport connection until it has read the entire request,
-            # or until the client closes the connection. Otherwise, the client
-            # might not reliably receive the response message. However, this
-            # requirement is not be construed as preventing a server from
-            # defending itself against denial-of-service attacks, or from
-            # badly broken client implementations."
-            remaining = getattr(self.rfile, 'remaining', 0)
-            if remaining > 0:
-                self.rfile.read(remaining)
-
-        if b'date' not in hkeys:
-            self.outheaders.append((
-                b'Date',
-                email.utils.formatdate(usegmt=True).encode('ISO-8859-1'),
-            ))
-
-        if b'server' not in hkeys:
-            self.outheaders.append((
-                b'Server',
-                self.server.server_name.encode('ISO-8859-1'),
-            ))
-
-        proto = self.server.protocol.encode('ascii')
-        buf = [proto + SPACE + self.status + CRLF]
-        for k, v in self.outheaders:
-            buf.append(k + COLON + SPACE + v + CRLF)
-        buf.append(CRLF)
-        self.conn.wfile.write(EMPTY.join(buf))
-
-
-class HTTPConnection:
-    """An HTTP connection (active socket)."""
-
-    remote_addr = None
-    remote_port = None
-    ssl_env = None
-    rbufsize = io.DEFAULT_BUFFER_SIZE
-    wbufsize = io.DEFAULT_BUFFER_SIZE
-    RequestHandlerClass = HTTPRequest
-    peercreds_enabled = False
-    peercreds_resolve_enabled = False
-
-    def __init__(self, server, sock, makefile=MakeFile):
-        """Initialize HTTPConnection instance.
-
-        Args:
-            server (HTTPServer): web server object receiving this request
-            socket (socket._socketobject): the raw socket object (usually
-                TCP) for this connection
-            makefile (file): a fileobject class for reading from the socket
-        """
-        self.server = server
-        self.socket = sock
-        self.rfile = makefile(sock, 'rb', self.rbufsize)
-        self.wfile = makefile(sock, 'wb', self.wbufsize)
-        self.requests_seen = 0
-
-        self.peercreds_enabled = self.server.peercreds_enabled
-        self.peercreds_resolve_enabled = self.server.peercreds_resolve_enabled
-
-        # LRU cached methods:
-        # Ref: https://stackoverflow.com/a/14946506/595220
-        self.resolve_peer_creds = (
-            lru_cache(maxsize=1)(self.resolve_peer_creds)
-        )
-        self.get_peer_creds = (
-            lru_cache(maxsize=1)(self.get_peer_creds)
-        )
-
-    def communicate(self):
-        """Read each request and respond appropriately."""
-        request_seen = False
-        try:
-            while True:
-                # (re)set req to None so that if something goes wrong in
-                # the RequestHandlerClass constructor, the error doesn't
-                # get written to the previous request.
-                req = None
-                req = self.RequestHandlerClass(self.server, self)
-
-                # This order of operations should guarantee correct pipelining.
-                req.parse_request()
-                if self.server.stats['Enabled']:
-                    self.requests_seen += 1
-                if not req.ready:
-                    # Something went wrong in the parsing (and the server has
-                    # probably already made a simple_response). Return and
-                    # let the conn close.
-                    return
-
-                request_seen = True
-                req.respond()
-                if req.close_connection:
-                    return
-        except socket.error as ex:
-            errnum = ex.args[0]
-            # sadly SSL sockets return a different (longer) time out string
-            timeout_errs = 'timed out', 'The read operation timed out'
-            if errnum in timeout_errs:
-                # Don't error if we're between requests; only error
-                # if 1) no request has been started at all, or 2) we're
-                # in the middle of a request.
-                # See https://github.com/cherrypy/cherrypy/issues/853
-                if (not request_seen) or (req and req.started_request):
-                    self._conditional_error(req, '408 Request Timeout')
-            elif errnum not in errors.socket_errors_to_ignore:
-                self.server.error_log('socket.error %s' % repr(errnum),
-                                      level=logging.WARNING, traceback=True)
-                self._conditional_error(req, '500 Internal Server Error')
-        except (KeyboardInterrupt, SystemExit):
-            raise
-        except errors.FatalSSLAlert:
-            pass
-        except errors.NoSSLError:
-            self._handle_no_ssl(req)
-        except Exception as ex:
-            self.server.error_log(
-                repr(ex), level=logging.ERROR, traceback=True)
-            self._conditional_error(req, '500 Internal Server Error')
-
-    linger = False
-
-    def _handle_no_ssl(self, req):
-        if not req or req.sent_headers:
-            return
-        # Unwrap wfile
-        self.wfile = MakeFile(self.socket._sock, 'wb', self.wbufsize)
-        msg = (
-            'The client sent a plain HTTP request, but '
-            'this server only speaks HTTPS on this port.'
-        )
-        req.simple_response('400 Bad Request', msg)
-        self.linger = True
-
-    def _conditional_error(self, req, response):
-        """Respond with an error.
-
-        Don't bother writing if a response
-        has already started being written.
-        """
-        if not req or req.sent_headers:
-            return
-
-        try:
-            req.simple_response(response)
-        except errors.FatalSSLAlert:
-            pass
-        except errors.NoSSLError:
-            self._handle_no_ssl(req)
-
-    def close(self):
-        """Close the socket underlying this connection."""
-        self.rfile.close()
-
-        if not self.linger:
-            self._close_kernel_socket()
-            self.socket.close()
-        else:
-            # On the other hand, sometimes we want to hang around for a bit
-            # to make sure the client has a chance to read our entire
-            # response. Skipping the close() calls here delays the FIN
-            # packet until the socket object is garbage-collected later.
-            # Someday, perhaps, we'll do the full lingering_close that
-            # Apache does, but not today.
-            pass
-
-    def get_peer_creds(self):  # LRU cached on per-instance basis, see __init__
-        """Return the PID/UID/GID tuple of the peer socket for UNIX sockets.
-
-        This function uses SO_PEERCRED to query the UNIX PID, UID, GID
-        of the peer, which is only available if the bind address is
-        a UNIX domain socket.
-
-        Raises:
-            NotImplementedError: in case of unsupported socket type
-            RuntimeError: in case of SO_PEERCRED lookup unsupported or disabled
-
-        """
-        PEERCRED_STRUCT_DEF = '3i'
-
-        if IS_WINDOWS or self.socket.family != socket.AF_UNIX:
-            raise NotImplementedError(
-                'SO_PEERCRED is only supported in Linux kernel and WSL'
-            )
-        elif not self.peercreds_enabled:
-            raise RuntimeError(
-                'Peer creds lookup is disabled within this server'
-            )
-
-        try:
-            peer_creds = self.socket.getsockopt(
-                socket.SOL_SOCKET, socket.SO_PEERCRED,
-                struct.calcsize(PEERCRED_STRUCT_DEF)
-            )
-        except socket.error as socket_err:
-            """Non-Linux kernels don't support SO_PEERCRED.
-
-            Refs:
-            http://welz.org.za/notes/on-peer-cred.html
-            https://github.com/daveti/tcpSockHack
-            msdn.microsoft.com/en-us/commandline/wsl/release_notes#build-15025
-            """
-            six.raise_from(  # 3.6+: raise RuntimeError from socket_err
-                RuntimeError,
-                socket_err,
-            )
-        else:
-            pid, uid, gid = struct.unpack(PEERCRED_STRUCT_DEF, peer_creds)
-            return pid, uid, gid
-
-    @property
-    def peer_pid(self):
-        """Return the id of the connected peer process."""
-        pid, _, _ = self.get_peer_creds()
-        return pid
-
-    @property
-    def peer_uid(self):
-        """Return the user id of the connected peer process."""
-        _, uid, _ = self.get_peer_creds()
-        return uid
-
-    @property
-    def peer_gid(self):
-        """Return the group id of the connected peer process."""
-        _, _, gid = self.get_peer_creds()
-        return gid
-
-    def resolve_peer_creds(self):  # LRU cached on per-instance basis
-        """Return the username and group tuple of the peercreds if available.
-
-        Raises:
-            NotImplementedError: in case of unsupported OS
-            RuntimeError: in case of UID/GID lookup unsupported or disabled
-
-        """
-        if (IS_WINDOWS or IS_ANDROID):
-            raise NotImplementedError(
-                'UID/GID lookup can only be done under UNIX-like OS'
-            )
-        elif not self.peercreds_resolve_enabled:
-            raise RuntimeError(
-                'UID/GID lookup is disabled within this server'
-            )
-
-        user = pwd.getpwuid(self.peer_uid).pw_name  # [0]
-        group = grp.getgrgid(self.peer_gid).gr_name  # [0]
-
-        return user, group
-
-    @property
-    def peer_user(self):
-        """Return the username of the connected peer process."""
-        user, _ = self.resolve_peer_creds()
-        return user
-
-    @property
-    def peer_group(self):
-        """Return the group of the connected peer process."""
-        _, group = self.resolve_peer_creds()
-        return group
-
-    def _close_kernel_socket(self):
-        """Close kernel socket in outdated Python versions.
-
-        On old Python versions,
-        Python's socket module does NOT call close on the kernel
-        socket when you call socket.close(). We do so manually here
-        because we want this server to send a FIN TCP segment
-        immediately. Note this must be called *before* calling
-        socket.close(), because the latter drops its reference to
-        the kernel socket.
-        """
-        if six.PY2 and hasattr(self.socket, '_sock'):
-            self.socket._sock.close()
-
-
-def prevent_socket_inheritance(sock):
-    """Stub inheritance prevention.
-
-    Dummy function, since neither fcntl nor ctypes are available.
-    """
-    pass
-
-
-class HTTPServer:
-    """An HTTP server."""
-
-    _bind_addr = '127.0.0.1'
-    _interrupt = None
-
-    gateway = None
-    """A Gateway instance."""
-
-    minthreads = None
-    """The minimum number of worker threads to create (default 10)."""
-
-    maxthreads = None
-    """The maximum number of worker threads to create.
-
-    (default -1 = no limit)"""
-
-    server_name = None
-    """The name of the server; defaults to ``self.version``."""
-
-    protocol = 'HTTP/1.1'
-    """The version string to write in the Status-Line of all HTTP responses.
-
-    For example, "HTTP/1.1" is the default. This also limits the supported
-    features used in the response."""
-
-    request_queue_size = 5
-    """The 'backlog' arg to socket.listen(); max queued connections.
-
-    (default 5)."""
-
-    shutdown_timeout = 5
-    """The total time to wait for worker threads to cleanly exit.
-
-    Specified in seconds."""
-
-    timeout = 10
-    """The timeout in seconds for accepted connections (default 10)."""
-
-    version = 'Cheroot/' + __version__
-    """A version string for the HTTPServer."""
-
-    software = None
-    """The value to set for the SERVER_SOFTWARE entry in the WSGI environ.
-
-    If None, this defaults to ``'%s Server' % self.version``.
-    """
-
-    ready = False
-    """Internal flag which indicating the socket is accepting connections."""
-
-    max_request_header_size = 0
-    """The maximum size, in bytes, for request headers, or 0 for no limit."""
-
-    max_request_body_size = 0
-    """The maximum size, in bytes, for request bodies, or 0 for no limit."""
-
-    nodelay = True
-    """If True (the default since 3.1), sets the TCP_NODELAY socket option."""
-
-    ConnectionClass = HTTPConnection
-    """The class to use for handling HTTP connections."""
-
-    ssl_adapter = None
-    """An instance of ssl.Adapter (or a subclass).
-
-    You must have the corresponding SSL driver library installed.
-    """
-
-    peercreds_enabled = False
-    """If True, peer cred lookup can be performed via UNIX domain socket."""
-
-    peercreds_resolve_enabled = False
-    """If True, username/group will be looked up in the OS from peercreds."""
-
-    def __init__(
-        self, bind_addr, gateway,
-        minthreads=10, maxthreads=-1, server_name=None,
-        peercreds_enabled=False, peercreds_resolve_enabled=False,
-    ):
-        """Initialize HTTPServer instance.
-
-        Args:
-            bind_addr (tuple): network interface to listen to
-            gateway (Gateway): gateway for processing HTTP requests
-            minthreads (int): minimum number of threads for HTTP thread pool
-            maxthreads (int): maximum number of threads for HTTP thread pool
-            server_name (str): web server name to be advertised via Server
-                HTTP header
-        """
-        self.bind_addr = bind_addr
-        self.gateway = gateway
-
-        self.requests = threadpool.ThreadPool(
-            self, min=minthreads or 1, max=maxthreads)
-
-        if not server_name:
-            server_name = self.version
-        self.server_name = server_name
-        self.peercreds_enabled = peercreds_enabled
-        self.peercreds_resolve_enabled = (
-            peercreds_resolve_enabled and peercreds_enabled
-        )
-        self.clear_stats()
-
-    def clear_stats(self):
-        """Reset server stat counters.."""
-        self._start_time = None
-        self._run_time = 0
-        self.stats = {
-            'Enabled': False,
-            'Bind Address': lambda s: repr(self.bind_addr),
-            'Run time': lambda s: (not s['Enabled']) and -1 or self.runtime(),
-            'Accepts': 0,
-            'Accepts/sec': lambda s: s['Accepts'] / self.runtime(),
-            'Queue': lambda s: getattr(self.requests, 'qsize', None),
-            'Threads': lambda s: len(getattr(self.requests, '_threads', [])),
-            'Threads Idle': lambda s: getattr(self.requests, 'idle', None),
-            'Socket Errors': 0,
-            'Requests': lambda s: (not s['Enabled']) and -1 or sum(
-                [w['Requests'](w) for w in s['Worker Threads'].values()], 0),
-            'Bytes Read': lambda s: (not s['Enabled']) and -1 or sum(
-                [w['Bytes Read'](w) for w in s['Worker Threads'].values()], 0),
-            'Bytes Written': lambda s: (not s['Enabled']) and -1 or sum(
-                [w['Bytes Written'](w) for w in s['Worker Threads'].values()],
-                0),
-            'Work Time': lambda s: (not s['Enabled']) and -1 or sum(
-                [w['Work Time'](w) for w in s['Worker Threads'].values()], 0),
-            'Read Throughput': lambda s: (not s['Enabled']) and -1 or sum(
-                [w['Bytes Read'](w) / (w['Work Time'](w) or 1e-6)
-                 for w in s['Worker Threads'].values()], 0),
-            'Write Throughput': lambda s: (not s['Enabled']) and -1 or sum(
-                [w['Bytes Written'](w) / (w['Work Time'](w) or 1e-6)
-                 for w in s['Worker Threads'].values()], 0),
-            'Worker Threads': {},
-        }
-        logging.statistics['Cheroot HTTPServer %d' % id(self)] = self.stats
-
-    def runtime(self):
-        """Return server uptime."""
-        if self._start_time is None:
-            return self._run_time
-        else:
-            return self._run_time + (time.time() - self._start_time)
-
-    def __str__(self):
-        """Render Server instance representing bind address."""
-        return '%s.%s(%r)' % (self.__module__, self.__class__.__name__,
-                              self.bind_addr)
-
-    @property
-    def bind_addr(self):
-        """Return the interface on which to listen for connections.
-
-        For TCP sockets, a (host, port) tuple. Host values may be any IPv4
-        or IPv6 address, or any valid hostname. The string 'localhost' is a
-        synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
-        The string '0.0.0.0' is a special IPv4 entry meaning "any active
-        interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
-        IPv6. The empty string or None are not allowed.
-
-        For UNIX sockets, supply the filename as a string.
-
-        Systemd socket activation is automatic and doesn't require tempering
-        with this variable.
-        """
-        return self._bind_addr
-
-    @bind_addr.setter
-    def bind_addr(self, value):
-        """Set the interface on which to listen for connections."""
-        if isinstance(value, tuple) and value[0] in ('', None):
-            # Despite the socket module docs, using '' does not
-            # allow AI_PASSIVE to work. Passing None instead
-            # returns '0.0.0.0' like we want. In other words:
-            #     host    AI_PASSIVE     result
-            #      ''         Y         192.168.x.y
-            #      ''         N         192.168.x.y
-            #     None        Y         0.0.0.0
-            #     None        N         127.0.0.1
-            # But since you can get the same effect with an explicit
-            # '0.0.0.0', we deny both the empty string and None as values.
-            raise ValueError("Host values of '' or None are not allowed. "
-                             "Use '0.0.0.0' (IPv4) or '::' (IPv6) instead "
-                             'to listen on all active interfaces.')
-        self._bind_addr = value
-
-    def safe_start(self):
-        """Run the server forever, and stop it cleanly on exit."""
-        try:
-            self.start()
-        except (KeyboardInterrupt, IOError):
-            # The time.sleep call might raise
-            # "IOError: [Errno 4] Interrupted function call" on KBInt.
-            self.error_log('Keyboard Interrupt: shutting down')
-            self.stop()
-            raise
-        except SystemExit:
-            self.error_log('SystemExit raised: shutting down')
-            self.stop()
-            raise
-
-    def prepare(self):
-        """Prepare server to serving requests.
-
-        It binds a socket's port, setups the socket to ``listen()`` and does
-        other preparing things.
-        """
-        self._interrupt = None
-
-        if self.software is None:
-            self.software = '%s Server' % self.version
-
-        # Select the appropriate socket
-        self.socket = None
-        if os.getenv('LISTEN_PID', None):
-            # systemd socket activation
-            self.socket = socket.fromfd(3, socket.AF_INET, socket.SOCK_STREAM)
-        elif isinstance(self.bind_addr, six.string_types):
-            # AF_UNIX socket
-
-            # So we can reuse the socket...
-            try:
-                os.unlink(self.bind_addr)
-            except Exception:
-                pass
-
-            # So everyone can access the socket...
-            try:
-                os.chmod(self.bind_addr, 0o777)
-            except Exception:
-                pass
-
-            info = [
-                (socket.AF_UNIX, socket.SOCK_STREAM, 0, '', self.bind_addr)]
-        else:
-            # AF_INET or AF_INET6 socket
-            # Get the correct address family for our host (allows IPv6
-            # addresses)
-            host, port = self.bind_addr
-            try:
-                info = socket.getaddrinfo(
-                    host, port, socket.AF_UNSPEC,
-                    socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
-            except socket.gaierror:
-                sock_type = socket.AF_INET
-                bind_addr = self.bind_addr
-
-                if ':' in host:
-                    sock_type = socket.AF_INET6
-                    bind_addr = bind_addr + (0, 0)
-
-                info = [(sock_type, socket.SOCK_STREAM, 0, '', bind_addr)]
-
-        if not self.socket:
-            msg = 'No socket could be created'
-            for res in info:
-                af, socktype, proto, canonname, sa = res
-                try:
-                    self.bind(af, socktype, proto)
-                    break
-                except socket.error as serr:
-                    msg = '%s -- (%s: %s)' % (msg, sa, serr)
-                    if self.socket:
-                        self.socket.close()
-                    self.socket = None
-
-            if not self.socket:
-                raise socket.error(msg)
-
-        # Timeout so KeyboardInterrupt can be caught on Win32
-        self.socket.settimeout(1)
-        self.socket.listen(self.request_queue_size)
-
-        # Create worker threads
-        self.requests.start()
-
-        self.ready = True
-        self._start_time = time.time()
-
-    def serve(self):
-        """Serve requests, after invoking :func:`prepare()`."""
-        while self.ready:
-            try:
-                self.tick()
-            except (KeyboardInterrupt, SystemExit):
-                raise
-            except Exception:
-                self.error_log('Error in HTTPServer.tick', level=logging.ERROR,
-                               traceback=True)
-
-            if self.interrupt:
-                while self.interrupt is True:
-                    # Wait for self.stop() to complete. See _set_interrupt.
-                    time.sleep(0.1)
-                if self.interrupt:
-                    raise self.interrupt
-
-    def start(self):
-        """Run the server forever.
-
-        It is shortcut for invoking :func:`prepare()` then :func:`serve()`.
-        """
-        # We don't have to trap KeyboardInterrupt or SystemExit here,
-        # because cherrypy.server already does so, calling self.stop() for us.
-        # If you're using this server with another framework, you should
-        # trap those exceptions in whatever code block calls start().
-        self.prepare()
-        self.serve()
-
-    def error_log(self, msg='', level=20, traceback=False):
-        """Write error message to log.
-
-        Args:
-            msg (str): error message
-            level (int): logging level
-            traceback (bool): add traceback to output or not
-        """
-        # Override this in subclasses as desired
-        sys.stderr.write(msg + '\n')
-        sys.stderr.flush()
-        if traceback:
-            tblines = traceback_.format_exc()
-            sys.stderr.write(tblines)
-            sys.stderr.flush()
-
-    def bind(self, family, type, proto=0):
-        """Create (or recreate) the actual socket object."""
-        self.socket = socket.socket(family, type, proto)
-        prevent_socket_inheritance(self.socket)
-        if not IS_WINDOWS:
-            # Windows has different semantics for SO_REUSEADDR,
-            # so don't set it.
-            # https://msdn.microsoft.com/en-us/library/ms740621(v=vs.85).aspx
-            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        if self.nodelay and not isinstance(self.bind_addr, str):
-            self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
-
-        if self.ssl_adapter is not None:
-            self.socket = self.ssl_adapter.bind(self.socket)
-
-        host, port = self.bind_addr[:2]
-
-        # If listening on the IPV6 any address ('::' = IN6ADDR_ANY),
-        # activate dual-stack. See
-        # https://github.com/cherrypy/cherrypy/issues/871.
-        listening_ipv6 = (
-            hasattr(socket, 'AF_INET6')
-            and family == socket.AF_INET6
-            and host in ('::', '::0', '::0.0.0.0')
-        )
-        if listening_ipv6:
-            try:
-                self.socket.setsockopt(
-                    socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
-            except (AttributeError, socket.error):
-                # Apparently, the socket option is not available in
-                # this machine's TCP stack
-                pass
-
-        self.socket.bind(self.bind_addr)
-        # TODO: keep requested bind_addr separate real bound_addr (port is
-        # different in case of ephemeral port 0)
-        self.bind_addr = self.socket.getsockname()
-        if family in (
-            # Windows doesn't have socket.AF_UNIX, so not using it in check
-            socket.AF_INET,
-            socket.AF_INET6,
-        ):
-            """UNIX domain sockets are strings or bytes.
-
-            In case of bytes with a leading null-byte it's an abstract socket.
-            """
-            self.bind_addr = self.bind_addr[:2]
-
-    def tick(self):
-        """Accept a new connection and put it on the Queue."""
-        try:
-            s, addr = self.socket.accept()
-            if self.stats['Enabled']:
-                self.stats['Accepts'] += 1
-            if not self.ready:
-                return
-
-            prevent_socket_inheritance(s)
-            if hasattr(s, 'settimeout'):
-                s.settimeout(self.timeout)
-
-            mf = MakeFile
-            ssl_env = {}
-            # if ssl cert and key are set, we try to be a secure HTTP server
-            if self.ssl_adapter is not None:
-                try:
-                    s, ssl_env = self.ssl_adapter.wrap(s)
-                except errors.NoSSLError:
-                    msg = ('The client sent a plain HTTP request, but '
-                           'this server only speaks HTTPS on this port.')
-                    buf = ['%s 400 Bad Request\r\n' % self.protocol,
-                           'Content-Length: %s\r\n' % len(msg),
-                           'Content-Type: text/plain\r\n\r\n',
-                           msg]
-
-                    sock_to_make = s if six.PY3 else s._sock
-                    wfile = mf(sock_to_make, 'wb', io.DEFAULT_BUFFER_SIZE)
-                    try:
-                        wfile.write(''.join(buf).encode('ISO-8859-1'))
-                    except socket.error as ex:
-                        if ex.args[0] not in errors.socket_errors_to_ignore:
-                            raise
-                    return
-                if not s:
-                    return
-                mf = self.ssl_adapter.makefile
-                # Re-apply our timeout since we may have a new socket object
-                if hasattr(s, 'settimeout'):
-                    s.settimeout(self.timeout)
-
-            conn = self.ConnectionClass(self, s, mf)
-
-            if not isinstance(self.bind_addr, six.string_types):
-                # optional values
-                # Until we do DNS lookups, omit REMOTE_HOST
-                if addr is None:  # sometimes this can happen
-                    # figure out if AF_INET or AF_INET6.
-                    if len(s.getsockname()) == 2:
-                        # AF_INET
-                        addr = ('0.0.0.0', 0)
-                    else:
-                        # AF_INET6
-                        addr = ('::', 0)
-                conn.remote_addr = addr[0]
-                conn.remote_port = addr[1]
-
-            conn.ssl_env = ssl_env
-
-            try:
-                self.requests.put(conn)
-            except queue.Full:
-                # Just drop the conn. TODO: write 503 back?
-                conn.close()
-                return
-        except socket.timeout:
-            # The only reason for the timeout in start() is so we can
-            # notice keyboard interrupts on Win32, which don't interrupt
-            # accept() by default
-            return
-        except socket.error as ex:
-            if self.stats['Enabled']:
-                self.stats['Socket Errors'] += 1
-            if ex.args[0] in errors.socket_error_eintr:
-                # I *think* this is right. EINTR should occur when a signal
-                # is received during the accept() call; all docs say retry
-                # the call, and I *think* I'm reading it right that Python
-                # will then go ahead and poll for and handle the signal
-                # elsewhere. See
-                # https://github.com/cherrypy/cherrypy/issues/707.
-                return
-            if ex.args[0] in errors.socket_errors_nonblocking:
-                # Just try again. See
-                # https://github.com/cherrypy/cherrypy/issues/479.
-                return
-            if ex.args[0] in errors.socket_errors_to_ignore:
-                # Our socket was closed.
-                # See https://github.com/cherrypy/cherrypy/issues/686.
-                return
-            raise
-
-    @property
-    def interrupt(self):
-        """Flag interrupt of the server."""
-        return self._interrupt
-
-    @interrupt.setter
-    def interrupt(self, interrupt):
-        """Perform the shutdown of this server and save the exception."""
-        self._interrupt = True
-        self.stop()
-        self._interrupt = interrupt
-
-    def stop(self):
-        """Gracefully shutdown a server that is serving forever."""
-        self.ready = False
-        if self._start_time is not None:
-            self._run_time += (time.time() - self._start_time)
-        self._start_time = None
-
-        sock = getattr(self, 'socket', None)
-        if sock:
-            if not isinstance(self.bind_addr, six.string_types):
-                # Touch our own socket to make accept() return immediately.
-                try:
-                    host, port = sock.getsockname()[:2]
-                except socket.error as ex:
-                    if ex.args[0] not in errors.socket_errors_to_ignore:
-                        # Changed to use error code and not message
-                        # See
-                        # https://github.com/cherrypy/cherrypy/issues/860.
-                        raise
-                else:
-                    # Note that we're explicitly NOT using AI_PASSIVE,
-                    # here, because we want an actual IP to touch.
-                    # localhost won't work if we've bound to a public IP,
-                    # but it will if we bound to '0.0.0.0' (INADDR_ANY).
-                    for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
-                                                  socket.SOCK_STREAM):
-                        af, socktype, proto, canonname, sa = res
-                        s = None
-                        try:
-                            s = socket.socket(af, socktype, proto)
-                            # See
-                            # https://groups.google.com/group/cherrypy-users/
-                            #     browse_frm/thread/bbfe5eb39c904fe0
-                            s.settimeout(1.0)
-                            s.connect((host, port))
-                            s.close()
-                        except socket.error:
-                            if s:
-                                s.close()
-            if hasattr(sock, 'close'):
-                sock.close()
-            self.socket = None
-
-        self.requests.stop(self.shutdown_timeout)
-
-
-class Gateway:
-    """Base class to interface HTTPServer with other systems, such as WSGI."""
-
-    def __init__(self, req):
-        """Initialize Gateway instance with request.
-
-        Args:
-            req (HTTPRequest): current HTTP request
-        """
-        self.req = req
-
-    def respond(self):
-        """Process the current request. Must be overridden in a subclass."""
-        raise NotImplementedError
-
-
-# These may either be ssl.Adapter subclasses or the string names
-# of such classes (in which case they will be lazily loaded).
-ssl_adapters = {
-    'builtin': 'cheroot.ssl.builtin.BuiltinSSLAdapter',
-    'pyopenssl': 'cheroot.ssl.pyopenssl.pyOpenSSLAdapter',
-}
-
-
-def get_ssl_adapter_class(name='builtin'):
-    """Return an SSL adapter class for the given name."""
-    adapter = ssl_adapters[name.lower()]
-    if isinstance(adapter, six.string_types):
-        last_dot = adapter.rfind('.')
-        attr_name = adapter[last_dot + 1:]
-        mod_path = adapter[:last_dot]
-
-        try:
-            mod = sys.modules[mod_path]
-            if mod is None:
-                raise KeyError()
-        except KeyError:
-            # The last [''] is important.
-            mod = __import__(mod_path, globals(), locals(), [''])
-
-        # Let an AttributeError propagate outward.
-        try:
-            adapter = getattr(mod, attr_name)
-        except AttributeError:
-            raise AttributeError("'%s' object has no attribute '%s'"
-                                 % (mod_path, attr_name))
-
-    return adapter
diff --git a/libraries/cheroot/ssl/__init__.py b/libraries/cheroot/ssl/__init__.py
deleted file mode 100644
index ec1a0d90..00000000
--- a/libraries/cheroot/ssl/__init__.py
+++ /dev/null
@@ -1,51 +0,0 @@
-"""Implementation of the SSL adapter base interface."""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-from abc import ABCMeta, abstractmethod
-
-from six import add_metaclass
-
-
-@add_metaclass(ABCMeta)
-class Adapter:
-    """Base class for SSL driver library adapters.
-
-    Required methods:
-
-        * ``wrap(sock) -> (wrapped socket, ssl environ dict)``
-        * ``makefile(sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE) ->
-          socket file object``
-    """
-
-    @abstractmethod
-    def __init__(
-            self, certificate, private_key, certificate_chain=None,
-            ciphers=None):
-        """Set up certificates, private key ciphers and reset context."""
-        self.certificate = certificate
-        self.private_key = private_key
-        self.certificate_chain = certificate_chain
-        self.ciphers = ciphers
-        self.context = None
-
-    @abstractmethod
-    def bind(self, sock):
-        """Wrap and return the given socket."""
-        return sock
-
-    @abstractmethod
-    def wrap(self, sock):
-        """Wrap and return the given socket, plus WSGI environ entries."""
-        raise NotImplementedError
-
-    @abstractmethod
-    def get_environ(self):
-        """Return WSGI environ entries to be merged into each request."""
-        raise NotImplementedError
-
-    @abstractmethod
-    def makefile(self, sock, mode='r', bufsize=-1):
-        """Return socket file object."""
-        raise NotImplementedError
diff --git a/libraries/cheroot/ssl/builtin.py b/libraries/cheroot/ssl/builtin.py
deleted file mode 100644
index a19f7eef..00000000
--- a/libraries/cheroot/ssl/builtin.py
+++ /dev/null
@@ -1,162 +0,0 @@
-"""
-A library for integrating Python's builtin ``ssl`` library with Cheroot.
-
-The ssl module must be importable for SSL functionality.
-
-To use this module, set ``HTTPServer.ssl_adapter`` to an instance of
-``BuiltinSSLAdapter``.
-"""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-try:
-    import ssl
-except ImportError:
-    ssl = None
-
-try:
-    from _pyio import DEFAULT_BUFFER_SIZE
-except ImportError:
-    try:
-        from io import DEFAULT_BUFFER_SIZE
-    except ImportError:
-        DEFAULT_BUFFER_SIZE = -1
-
-import six
-
-from . import Adapter
-from .. import errors
-from ..makefile import MakeFile
-
-
-if six.PY3:
-    generic_socket_error = OSError
-else:
-    import socket
-    generic_socket_error = socket.error
-    del socket
-
-
-def _assert_ssl_exc_contains(exc, *msgs):
-    """Check whether SSL exception contains either of messages provided."""
-    if len(msgs) < 1:
-        raise TypeError(
-            '_assert_ssl_exc_contains() requires '
-            'at least one message to be passed.'
-        )
-    err_msg_lower = exc.args[1].lower()
-    return any(m.lower() in err_msg_lower for m in msgs)
-
-
-class BuiltinSSLAdapter(Adapter):
-    """A wrapper for integrating Python's builtin ssl module with Cheroot."""
-
-    certificate = None
-    """The filename of the server SSL certificate."""
-
-    private_key = None
-    """The filename of the server's private key file."""
-
-    certificate_chain = None
-    """The filename of the certificate chain file."""
-
-    context = None
-    """The ssl.SSLContext that will be used to wrap sockets."""
-
-    ciphers = None
-    """The ciphers list of SSL."""
-
-    def __init__(
-            self, certificate, private_key, certificate_chain=None,
-            ciphers=None):
-        """Set up context in addition to base class properties if available."""
-        if ssl is None:
-            raise ImportError('You must install the ssl module to use HTTPS.')
-
-        super(BuiltinSSLAdapter, self).__init__(
-            certificate, private_key, certificate_chain, ciphers)
-
-        self.context = ssl.create_default_context(
-            purpose=ssl.Purpose.CLIENT_AUTH,
-            cafile=certificate_chain
-        )
-        self.context.load_cert_chain(certificate, private_key)
-        if self.ciphers is not None:
-            self.context.set_ciphers(ciphers)
-
-    def bind(self, sock):
-        """Wrap and return the given socket."""
-        return super(BuiltinSSLAdapter, self).bind(sock)
-
-    def wrap(self, sock):
-        """Wrap and return the given socket, plus WSGI environ entries."""
-        EMPTY_RESULT = None, {}
-        try:
-            s = self.context.wrap_socket(
-                sock, do_handshake_on_connect=True, server_side=True,
-            )
-        except ssl.SSLError as ex:
-            if ex.errno == ssl.SSL_ERROR_EOF:
-                # This is almost certainly due to the cherrypy engine
-                # 'pinging' the socket to assert it's connectable;
-                # the 'ping' isn't SSL.
-                return EMPTY_RESULT
-            elif ex.errno == ssl.SSL_ERROR_SSL:
-                if _assert_ssl_exc_contains(ex, 'http request'):
-                    # The client is speaking HTTP to an HTTPS server.
-                    raise errors.NoSSLError
-
-                # Check if it's one of the known errors
-                # Errors that are caught by PyOpenSSL, but thrown by
-                # built-in ssl
-                _block_errors = (
-                    'unknown protocol', 'unknown ca', 'unknown_ca',
-                    'unknown error',
-                    'https proxy request', 'inappropriate fallback',
-                    'wrong version number',
-                    'no shared cipher', 'certificate unknown',
-                    'ccs received early',
-                )
-                if _assert_ssl_exc_contains(ex, *_block_errors):
-                    # Accepted error, let's pass
-                    return EMPTY_RESULT
-            elif _assert_ssl_exc_contains(ex, 'handshake operation timed out'):
-                # This error is thrown by builtin SSL after a timeout
-                # when client is speaking HTTP to an HTTPS server.
-                # The connection can safely be dropped.
-                return EMPTY_RESULT
-            raise
-        except generic_socket_error as exc:
-            """It is unclear why exactly this happens.
-
-            It's reproducible only under Python 2 with openssl>1.0 and stdlib
-            ``ssl`` wrapper, and only with CherryPy.
-            So it looks like some healthcheck tries to connect to this socket
-            during startup (from the same process).
-
-
-            Ref: https://github.com/cherrypy/cherrypy/issues/1618
-            """
-            if six.PY2 and exc.args == (0, 'Error'):
-                return EMPTY_RESULT
-            raise
-        return s, self.get_environ(s)
-
-    # TODO: fill this out more with mod ssl env
-    def get_environ(self, sock):
-        """Create WSGI environ entries to be merged into each request."""
-        cipher = sock.cipher()
-        ssl_environ = {
-            'wsgi.url_scheme': 'https',
-            'HTTPS': 'on',
-            'SSL_PROTOCOL': cipher[1],
-            'SSL_CIPHER': cipher[0]
-            # SSL_VERSION_INTERFACE     string  The mod_ssl program version
-            # SSL_VERSION_LIBRARY   string  The OpenSSL program version
-        }
-        return ssl_environ
-
-    def makefile(self, sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE):
-        """Return socket file object."""
-        return MakeFile(sock, mode, bufsize)
diff --git a/libraries/cheroot/ssl/pyopenssl.py b/libraries/cheroot/ssl/pyopenssl.py
deleted file mode 100644
index 2185f851..00000000
--- a/libraries/cheroot/ssl/pyopenssl.py
+++ /dev/null
@@ -1,267 +0,0 @@
-"""
-A library for integrating pyOpenSSL with Cheroot.
-
-The OpenSSL module must be importable for SSL functionality.
-You can obtain it from `here <https://launchpad.net/pyopenssl>`_.
-
-To use this module, set HTTPServer.ssl_adapter to an instance of
-ssl.Adapter. There are two ways to use SSL:
-
-Method One
-----------
-
- * ``ssl_adapter.context``: an instance of SSL.Context.
-
-If this is not None, it is assumed to be an SSL.Context instance,
-and will be passed to SSL.Connection on bind(). The developer is
-responsible for forming a valid Context object. This approach is
-to be preferred for more flexibility, e.g. if the cert and key are
-streams instead of files, or need decryption, or SSL.SSLv3_METHOD
-is desired instead of the default SSL.SSLv23_METHOD, etc. Consult
-the pyOpenSSL documentation for complete options.
-
-Method Two (shortcut)
----------------------
-
- * ``ssl_adapter.certificate``: the filename of the server SSL certificate.
- * ``ssl_adapter.private_key``: the filename of the server's private key file.
-
-Both are None by default. If ssl_adapter.context is None, but .private_key
-and .certificate are both given and valid, they will be read, and the
-context will be automatically created from them.
-"""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import socket
-import threading
-import time
-
-try:
-    from OpenSSL import SSL
-    from OpenSSL import crypto
-except ImportError:
-    SSL = None
-
-from . import Adapter
-from .. import errors, server as cheroot_server
-from ..makefile import MakeFile
-
-
-class SSL_fileobject(MakeFile):
-    """SSL file object attached to a socket object."""
-
-    ssl_timeout = 3
-    ssl_retry = .01
-
-    def _safe_call(self, is_reader, call, *args, **kwargs):
-        """Wrap the given call with SSL error-trapping.
-
-        is_reader: if False EOF errors will be raised. If True, EOF errors
-        will return "" (to emulate normal sockets).
-        """
-        start = time.time()
-        while True:
-            try:
-                return call(*args, **kwargs)
-            except SSL.WantReadError:
-                # Sleep and try again. This is dangerous, because it means
-                # the rest of the stack has no way of differentiating
-                # between a "new handshake" error and "client dropped".
-                # Note this isn't an endless loop: there's a timeout below.
-                time.sleep(self.ssl_retry)
-            except SSL.WantWriteError:
-                time.sleep(self.ssl_retry)
-            except SSL.SysCallError as e:
-                if is_reader and e.args == (-1, 'Unexpected EOF'):
-                    return ''
-
-                errnum = e.args[0]
-                if is_reader and errnum in errors.socket_errors_to_ignore:
-                    return ''
-                raise socket.error(errnum)
-            except SSL.Error as e:
-                if is_reader and e.args == (-1, 'Unexpected EOF'):
-                    return ''
-
-                thirdarg = None
-                try:
-                    thirdarg = e.args[0][0][2]
-                except IndexError:
-                    pass
-
-                if thirdarg == 'http request':
-                    # The client is talking HTTP to an HTTPS server.
-                    raise errors.NoSSLError()
-
-                raise errors.FatalSSLAlert(*e.args)
-
-            if time.time() - start > self.ssl_timeout:
-                raise socket.timeout('timed out')
-
-    def recv(self, size):
-        """Receive message of a size from the socket."""
-        return self._safe_call(True, super(SSL_fileobject, self).recv, size)
-
-    def sendall(self, *args, **kwargs):
-        """Send whole message to the socket."""
-        return self._safe_call(False, super(SSL_fileobject, self).sendall,
-                               *args, **kwargs)
-
-    def send(self, *args, **kwargs):
-        """Send some part of message to the socket."""
-        return self._safe_call(False, super(SSL_fileobject, self).send,
-                               *args, **kwargs)
-
-
-class SSLConnection:
-    """A thread-safe wrapper for an SSL.Connection.
-
-    ``*args``: the arguments to create the wrapped ``SSL.Connection(*args)``.
-    """
-
-    def __init__(self, *args):
-        """Initialize SSLConnection instance."""
-        self._ssl_conn = SSL.Connection(*args)
-        self._lock = threading.RLock()
-
-    for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read',
-              'renegotiate', 'bind', 'listen', 'connect', 'accept',
-              'setblocking', 'fileno', 'close', 'get_cipher_list',
-              'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
-              'makefile', 'get_app_data', 'set_app_data', 'state_string',
-              'sock_shutdown', 'get_peer_certificate', 'want_read',
-              'want_write', 'set_connect_state', 'set_accept_state',
-              'connect_ex', 'sendall', 'settimeout', 'gettimeout'):
-        exec("""def %s(self, *args):
-        self._lock.acquire()
-        try:
-            return self._ssl_conn.%s(*args)
-        finally:
-            self._lock.release()
-""" % (f, f))
-
-    def shutdown(self, *args):
-        """Shutdown the SSL connection.
-
-        Ignore all incoming args since pyOpenSSL.socket.shutdown takes no args.
-        """
-        self._lock.acquire()
-        try:
-            return self._ssl_conn.shutdown()
-        finally:
-            self._lock.release()
-
-
-class pyOpenSSLAdapter(Adapter):
-    """A wrapper for integrating pyOpenSSL with Cheroot."""
-
-    certificate = None
-    """The filename of the server SSL certificate."""
-
-    private_key = None
-    """The filename of the server's private key file."""
-
-    certificate_chain = None
-    """Optional. The filename of CA's intermediate certificate bundle.
-
-    This is needed for cheaper "chained root" SSL certificates, and should be
-    left as None if not required."""
-
-    context = None
-    """An instance of SSL.Context."""
-
-    ciphers = None
-    """The ciphers list of SSL."""
-
-    def __init__(
-            self, certificate, private_key, certificate_chain=None,
-            ciphers=None):
-        """Initialize OpenSSL Adapter instance."""
-        if SSL is None:
-            raise ImportError('You must install pyOpenSSL to use HTTPS.')
-
-        super(pyOpenSSLAdapter, self).__init__(
-            certificate, private_key, certificate_chain, ciphers)
-
-        self._environ = None
-
-    def bind(self, sock):
-        """Wrap and return the given socket."""
-        if self.context is None:
-            self.context = self.get_context()
-        conn = SSLConnection(self.context, sock)
-        self._environ = self.get_environ()
-        return conn
-
-    def wrap(self, sock):
-        """Wrap and return the given socket, plus WSGI environ entries."""
-        return sock, self._environ.copy()
-
-    def get_context(self):
-        """Return an SSL.Context from self attributes."""
-        # See https://code.activestate.com/recipes/442473/
-        c = SSL.Context(SSL.SSLv23_METHOD)
-        c.use_privatekey_file(self.private_key)
-        if self.certificate_chain:
-            c.load_verify_locations(self.certificate_chain)
-        c.use_certificate_file(self.certificate)
-        return c
-
-    def get_environ(self):
-        """Return WSGI environ entries to be merged into each request."""
-        ssl_environ = {
-            'HTTPS': 'on',
-            # pyOpenSSL doesn't provide access to any of these AFAICT
-            # 'SSL_PROTOCOL': 'SSLv2',
-            # SSL_CIPHER    string  The cipher specification name
-            # SSL_VERSION_INTERFACE     string  The mod_ssl program version
-            # SSL_VERSION_LIBRARY   string  The OpenSSL program version
-        }
-
-        if self.certificate:
-            # Server certificate attributes
-            cert = open(self.certificate, 'rb').read()
-            cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
-            ssl_environ.update({
-                'SSL_SERVER_M_VERSION': cert.get_version(),
-                'SSL_SERVER_M_SERIAL': cert.get_serial_number(),
-                # 'SSL_SERVER_V_START':
-                #   Validity of server's certificate (start time),
-                # 'SSL_SERVER_V_END':
-                #   Validity of server's certificate (end time),
-            })
-
-            for prefix, dn in [('I', cert.get_issuer()),
-                               ('S', cert.get_subject())]:
-                # X509Name objects don't seem to have a way to get the
-                # complete DN string. Use str() and slice it instead,
-                # because str(dn) == "<X509Name object '/C=US/ST=...'>"
-                dnstr = str(dn)[18:-2]
-
-                wsgikey = 'SSL_SERVER_%s_DN' % prefix
-                ssl_environ[wsgikey] = dnstr
-
-                # The DN should be of the form: /k1=v1/k2=v2, but we must allow
-                # for any value to contain slashes itself (in a URL).
-                while dnstr:
-                    pos = dnstr.rfind('=')
-                    dnstr, value = dnstr[:pos], dnstr[pos + 1:]
-                    pos = dnstr.rfind('/')
-                    dnstr, key = dnstr[:pos], dnstr[pos + 1:]
-                    if key and value:
-                        wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key)
-                        ssl_environ[wsgikey] = value
-
-        return ssl_environ
-
-    def makefile(self, sock, mode='r', bufsize=-1):
-        """Return socket file object."""
-        if SSL and isinstance(sock, SSL.ConnectionType):
-            timeout = sock.gettimeout()
-            f = SSL_fileobject(sock, mode, bufsize)
-            f.ssl_timeout = timeout
-            return f
-        else:
-            return cheroot_server.CP_fileobject(sock, mode, bufsize)
diff --git a/libraries/cheroot/test/__init__.py b/libraries/cheroot/test/__init__.py
deleted file mode 100644
index e2a7b348..00000000
--- a/libraries/cheroot/test/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""Cheroot test suite."""
diff --git a/libraries/cheroot/test/conftest.py b/libraries/cheroot/test/conftest.py
deleted file mode 100644
index 9f5f9284..00000000
--- a/libraries/cheroot/test/conftest.py
+++ /dev/null
@@ -1,27 +0,0 @@
-"""Pytest configuration module.
-
-Contains fixtures, which are tightly bound to the Cheroot framework
-itself, useless for end-users' app testing.
-"""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import pytest
-
-from ..testing import (  # noqa: F401
-    native_server, wsgi_server,
-)
-from ..testing import get_server_client
-
-
-@pytest.fixture  # noqa: F811
-def wsgi_server_client(wsgi_server):
-    """Create a test client out of given WSGI server."""
-    return get_server_client(wsgi_server)
-
-
-@pytest.fixture  # noqa: F811
-def native_server_client(native_server):
-    """Create a test client out of given HTTP server."""
-    return get_server_client(native_server)
diff --git a/libraries/cheroot/test/helper.py b/libraries/cheroot/test/helper.py
deleted file mode 100644
index 38f40b26..00000000
--- a/libraries/cheroot/test/helper.py
+++ /dev/null
@@ -1,169 +0,0 @@
-"""A library of helper functions for the Cheroot test suite."""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import datetime
-import logging
-import os
-import sys
-import time
-import threading
-import types
-
-from six.moves import http_client
-
-import six
-
-import cheroot.server
-import cheroot.wsgi
-
-from cheroot.test import webtest
-
-log = logging.getLogger(__name__)
-thisdir = os.path.abspath(os.path.dirname(__file__))
-serverpem = os.path.join(os.getcwd(), thisdir, 'test.pem')
-
-
-config = {
-    'bind_addr': ('127.0.0.1', 54583),
-    'server': 'wsgi',
-    'wsgi_app': None,
-}
-
-
-class CherootWebCase(webtest.WebCase):
-    """Helper class for a web app test suite."""
-
-    script_name = ''
-    scheme = 'http'
-
-    available_servers = {
-        'wsgi': cheroot.wsgi.Server,
-        'native': cheroot.server.HTTPServer,
-    }
-
-    @classmethod
-    def setup_class(cls):
-        """Create and run one HTTP server per class."""
-        conf = config.copy()
-        conf.update(getattr(cls, 'config', {}))
-
-        s_class = conf.pop('server', 'wsgi')
-        server_factory = cls.available_servers.get(s_class)
-        if server_factory is None:
-            raise RuntimeError('Unknown server in config: %s' % conf['server'])
-        cls.httpserver = server_factory(**conf)
-
-        cls.HOST, cls.PORT = cls.httpserver.bind_addr
-        if cls.httpserver.ssl_adapter is None:
-            ssl = ''
-            cls.scheme = 'http'
-        else:
-            ssl = ' (ssl)'
-            cls.HTTP_CONN = http_client.HTTPSConnection
-            cls.scheme = 'https'
-
-        v = sys.version.split()[0]
-        log.info('Python version used to run this test script: %s' % v)
-        log.info('Cheroot version: %s' % cheroot.__version__)
-        log.info('HTTP server version: %s%s' % (cls.httpserver.protocol, ssl))
-        log.info('PID: %s' % os.getpid())
-
-        if hasattr(cls, 'setup_server'):
-            # Clear the wsgi server so that
-            # it can be updated with the new root
-            cls.setup_server()
-            cls.start()
-
-    @classmethod
-    def teardown_class(cls):
-        """Cleanup HTTP server."""
-        if hasattr(cls, 'setup_server'):
-            cls.stop()
-
-    @classmethod
-    def start(cls):
-        """Load and start the HTTP server."""
-        threading.Thread(target=cls.httpserver.safe_start).start()
-        while not cls.httpserver.ready:
-            time.sleep(0.1)
-
-    @classmethod
-    def stop(cls):
-        """Terminate HTTP server."""
-        cls.httpserver.stop()
-        td = getattr(cls, 'teardown', None)
-        if td:
-            td()
-
-    date_tolerance = 2
-
-    def assertEqualDates(self, dt1, dt2, seconds=None):
-        """Assert abs(dt1 - dt2) is within Y seconds."""
-        if seconds is None:
-            seconds = self.date_tolerance
-
-        if dt1 > dt2:
-            diff = dt1 - dt2
-        else:
-            diff = dt2 - dt1
-        if not diff < datetime.timedelta(seconds=seconds):
-            raise AssertionError('%r and %r are not within %r seconds.' %
-                                 (dt1, dt2, seconds))
-
-
-class Request:
-    """HTTP request container."""
-
-    def __init__(self, environ):
-        """Initialize HTTP request."""
-        self.environ = environ
-
-
-class Response:
-    """HTTP response container."""
-
-    def __init__(self):
-        """Initialize HTTP response."""
-        self.status = '200 OK'
-        self.headers = {'Content-Type': 'text/html'}
-        self.body = None
-
-    def output(self):
-        """Generate iterable response body object."""
-        if self.body is None:
-            return []
-        elif isinstance(self.body, six.text_type):
-            return [self.body.encode('iso-8859-1')]
-        elif isinstance(self.body, six.binary_type):
-            return [self.body]
-        else:
-            return [x.encode('iso-8859-1') for x in self.body]
-
-
-class Controller:
-    """WSGI app for tests."""
-
-    def __call__(self, environ, start_response):
-        """WSGI request handler."""
-        req, resp = Request(environ), Response()
-        try:
-            # Python 3 supports unicode attribute names
-            # Python 2 encodes them
-            handler = self.handlers[environ['PATH_INFO']]
-        except KeyError:
-            resp.status = '404 Not Found'
-        else:
-            output = handler(req, resp)
-            if (output is not None and
-                    not any(resp.status.startswith(status_code)
-                            for status_code in ('204', '304'))):
-                resp.body = output
-                try:
-                    resp.headers.setdefault('Content-Length', str(len(output)))
-                except TypeError:
-                    if not isinstance(output, types.GeneratorType):
-                        raise
-        start_response(resp.status, resp.headers.items())
-        return resp.output()
diff --git a/libraries/cheroot/test/test.pem b/libraries/cheroot/test/test.pem
deleted file mode 100644
index 47a47042..00000000
--- a/libraries/cheroot/test/test.pem
+++ /dev/null
@@ -1,38 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQDBKo554mzIMY+AByUNpaUOP9bJnQ7ZLQe9XgHwoLJR4VzpyZZZ
-R9L4WtImEew05FY3Izerfm3MN3+MC0tJ6yQU9sOiU3vBW6RrLIMlfKsnRwBRZ0Kn
-da+O6xldVSosu8Ev3z9VZ94iC/ZgKzrH7Mjj/U8/MQO7RBS/LAqee8bFNQIDAQAB
-AoGAWOCF0ZrWxn3XMucWq2LNwPKqlvVGwbIwX3cDmX22zmnM4Fy6arXbYh4XlyCj
-9+ofqRrxIFz5k/7tFriTmZ0xag5+Jdx+Kwg0/twiP7XCNKipFogwe1Hznw8OFAoT
-enKBdj2+/n2o0Bvo/tDB59m9L/538d46JGQUmJlzMyqYikECQQDyoq+8CtMNvE18
-8VgHcR/KtApxWAjj4HpaHYL637ATjThetUZkW92mgDgowyplthusxdNqhHWyv7E8
-tWNdYErZAkEAy85ShTR0M5aWmrE7o0r0SpWInAkNBH9aXQRRARFYsdBtNfRu6I0i
-0lvU9wiu3eF57FMEC86yViZ5UBnQfTu7vQJAVesj/Zt7pwaCDfdMa740OsxMUlyR
-MVhhGx4OLpYdPJ8qUecxGQKq13XZ7R1HGyNEY4bd2X80Smq08UFuATfC6QJAH8UB
-yBHtKz2GLIcELOg6PIYizW/7v3+6rlVF60yw7sb2vzpjL40QqIn4IKoR2DSVtOkb
-8FtAIX3N21aq0VrGYQJBAIPiaEc2AZ8Bq2GC4F3wOz/BxJ/izvnkiotR12QK4fh5
-yjZMhTjWCas5zwHR5PDjlD88AWGDMsZ1PicD4348xJQ=
------END RSA PRIVATE KEY-----
------BEGIN CERTIFICATE-----
-MIIDxTCCAy6gAwIBAgIJAI18BD7eQxlGMA0GCSqGSIb3DQEBBAUAMIGeMQswCQYD
-VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAGA1UEBxMJU2FuIERpZWdv
-MRkwFwYDVQQKExBDaGVycnlQeSBQcm9qZWN0MREwDwYDVQQLEwhkZXYtdGVzdDEW
-MBQGA1UEAxMNQ2hlcnJ5UHkgVGVhbTEgMB4GCSqGSIb3DQEJARYRcmVtaUBjaGVy
-cnlweS5vcmcwHhcNMDYwOTA5MTkyMDIwWhcNMzQwMTI0MTkyMDIwWjCBnjELMAkG
-A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVNhbiBEaWVn
-bzEZMBcGA1UEChMQQ2hlcnJ5UHkgUHJvamVjdDERMA8GA1UECxMIZGV2LXRlc3Qx
-FjAUBgNVBAMTDUNoZXJyeVB5IFRlYW0xIDAeBgkqhkiG9w0BCQEWEXJlbWlAY2hl
-cnJ5cHkub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBKo554mzIMY+A
-ByUNpaUOP9bJnQ7ZLQe9XgHwoLJR4VzpyZZZR9L4WtImEew05FY3Izerfm3MN3+M
-C0tJ6yQU9sOiU3vBW6RrLIMlfKsnRwBRZ0Knda+O6xldVSosu8Ev3z9VZ94iC/Zg
-KzrH7Mjj/U8/MQO7RBS/LAqee8bFNQIDAQABo4IBBzCCAQMwHQYDVR0OBBYEFDIQ
-2feb71tVZCWpU0qJ/Tw+wdtoMIHTBgNVHSMEgcswgciAFDIQ2feb71tVZCWpU0qJ
-/Tw+wdtooYGkpIGhMIGeMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5p
-YTESMBAGA1UEBxMJU2FuIERpZWdvMRkwFwYDVQQKExBDaGVycnlQeSBQcm9qZWN0
-MREwDwYDVQQLEwhkZXYtdGVzdDEWMBQGA1UEAxMNQ2hlcnJ5UHkgVGVhbTEgMB4G
-CSqGSIb3DQEJARYRcmVtaUBjaGVycnlweS5vcmeCCQCNfAQ+3kMZRjAMBgNVHRME
-BTADAQH/MA0GCSqGSIb3DQEBBAUAA4GBAL7AAQz7IePV48ZTAFHKr88ntPALsL5S
-8vHCZPNMevNkLTj3DYUw2BcnENxMjm1kou2F2BkvheBPNZKIhc6z4hAml3ed1xa2
-D7w6e6OTcstdK/+KrPDDHeOP1dhMWNs2JE1bNlfF1LiXzYKSXpe88eCKjCXsCT/T
-NluCaWQys3MS
------END CERTIFICATE-----
diff --git a/libraries/cheroot/test/test__compat.py b/libraries/cheroot/test/test__compat.py
deleted file mode 100644
index d34e5eb8..00000000
--- a/libraries/cheroot/test/test__compat.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# -*- coding: utf-8 -*-
-"""Test suite for cross-python compatibility helpers."""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import pytest
-import six
-
-from cheroot._compat import ntob, ntou, bton
-
-
-@pytest.mark.parametrize(
-    'func,inp,out',
-    [
-        (ntob, 'bar', b'bar'),
-        (ntou, 'bar', u'bar'),
-        (bton, b'bar', 'bar'),
-    ],
-)
-def test_compat_functions_positive(func, inp, out):
-    """Check that compat functions work with correct input."""
-    assert func(inp, encoding='utf-8') == out
-
-
-@pytest.mark.parametrize(
-    'func',
-    [
-        ntob,
-        ntou,
-    ],
-)
-def test_compat_functions_negative_nonnative(func):
-    """Check that compat functions fail loudly for incorrect input."""
-    non_native_test_str = b'bar' if six.PY3 else u'bar'
-    with pytest.raises(TypeError):
-        func(non_native_test_str, encoding='utf-8')
-
-
-@pytest.mark.skip(reason='This test does not work now')
-@pytest.mark.skipif(
-    six.PY3,
-    reason='This code path only appears in Python 2 version.',
-)
-def test_ntou_escape():
-    """Check that ntou supports escape-encoding under Python 2."""
-    expected = u''
-    actual = ntou('hi'.encode('ISO-8859-1'), encoding='escape')
-    assert actual == expected
diff --git a/libraries/cheroot/test/test_conn.py b/libraries/cheroot/test/test_conn.py
deleted file mode 100644
index f543dd9b..00000000
--- a/libraries/cheroot/test/test_conn.py
+++ /dev/null
@@ -1,897 +0,0 @@
-"""Tests for TCP connection handling, including proper and timely close."""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import socket
-import time
-
-from six.moves import range, http_client, urllib
-
-import six
-import pytest
-
-from cheroot.test import helper, webtest
-
-
-timeout = 1
-pov = 'pPeErRsSiIsStTeEnNcCeE oOfF vViIsSiIoOnN'
-
-
-class Controller(helper.Controller):
-    """Controller for serving WSGI apps."""
-
-    def hello(req, resp):
-        """Render Hello world."""
-        return 'Hello, world!'
-
-    def pov(req, resp):
-        """Render pov value."""
-        return pov
-
-    def stream(req, resp):
-        """Render streaming response."""
-        if 'set_cl' in req.environ['QUERY_STRING']:
-            resp.headers['Content-Length'] = str(10)
-
-        def content():
-            for x in range(10):
-                yield str(x)
-
-        return content()
-
-    def upload(req, resp):
-        """Process file upload and render thank."""
-        if not req.environ['REQUEST_METHOD'] == 'POST':
-            raise AssertionError("'POST' != request.method %r" %
-                                 req.environ['REQUEST_METHOD'])
-        return "thanks for '%s'" % req.environ['wsgi.input'].read()
-
-    def custom_204(req, resp):
-        """Render response with status 204."""
-        resp.status = '204'
-        return 'Code = 204'
-
-    def custom_304(req, resp):
-        """Render response with status 304."""
-        resp.status = '304'
-        return 'Code = 304'
-
-    def err_before_read(req, resp):
-        """Render response with status 500."""
-        resp.status = '500 Internal Server Error'
-        return 'ok'
-
-    def one_megabyte_of_a(req, resp):
-        """Render 1MB response."""
-        return ['a' * 1024] * 1024
-
-    def wrong_cl_buffered(req, resp):
-        """Render buffered response with invalid length value."""
-        resp.headers['Content-Length'] = '5'
-        return 'I have too many bytes'
-
-    def wrong_cl_unbuffered(req, resp):
-        """Render unbuffered response with invalid length value."""
-        resp.headers['Content-Length'] = '5'
-        return ['I too', ' have too many bytes']
-
-    def _munge(string):
-        """Encode PATH_INFO correctly depending on Python version.
-
-        WSGI 1.0 is a mess around unicode. Create endpoints
-        that match the PATH_INFO that it produces.
-        """
-        if six.PY3:
-            return string.encode('utf-8').decode('latin-1')
-        return string
-
-    handlers = {
-        '/hello': hello,
-        '/pov': pov,
-        '/page1': pov,
-        '/page2': pov,
-        '/page3': pov,
-        '/stream': stream,
-        '/upload': upload,
-        '/custom/204': custom_204,
-        '/custom/304': custom_304,
-        '/err_before_read': err_before_read,
-        '/one_megabyte_of_a': one_megabyte_of_a,
-        '/wrong_cl_buffered': wrong_cl_buffered,
-        '/wrong_cl_unbuffered': wrong_cl_unbuffered,
-    }
-
-
-@pytest.fixture
-def testing_server(wsgi_server_client):
-    """Attach a WSGI app to the given server and pre-configure it."""
-    app = Controller()
-
-    def _timeout(req, resp):
-        return str(wsgi_server.timeout)
-    app.handlers['/timeout'] = _timeout
-    wsgi_server = wsgi_server_client.server_instance
-    wsgi_server.wsgi_app = app
-    wsgi_server.max_request_body_size = 1001
-    wsgi_server.timeout = timeout
-    wsgi_server.server_client = wsgi_server_client
-    return wsgi_server
-
-
-@pytest.fixture
-def test_client(testing_server):
-    """Get and return a test client out of the given server."""
-    return testing_server.server_client
-
-
-def header_exists(header_name, headers):
-    """Check that a header is present."""
-    return header_name.lower() in (k.lower() for (k, _) in headers)
-
-
-def header_has_value(header_name, header_value, headers):
-    """Check that a header with a given value is present."""
-    return header_name.lower() in (k.lower() for (k, v) in headers
-                                   if v == header_value)
-
-
-def test_HTTP11_persistent_connections(test_client):
-    """Test persistent HTTP/1.1 connections."""
-    # Initialize a persistent HTTP connection
-    http_connection = test_client.get_connection()
-    http_connection.auto_open = False
-    http_connection.connect()
-
-    # Make the first request and assert there's no "Connection: close".
-    status_line, actual_headers, actual_resp_body = test_client.get(
-        '/pov', http_conn=http_connection
-    )
-    actual_status = int(status_line[:3])
-    assert actual_status == 200
-    assert status_line[4:] == 'OK'
-    assert actual_resp_body == pov.encode()
-    assert not header_exists('Connection', actual_headers)
-
-    # Make another request on the same connection.
-    status_line, actual_headers, actual_resp_body = test_client.get(
-        '/page1', http_conn=http_connection
-    )
-    actual_status = int(status_line[:3])
-    assert actual_status == 200
-    assert status_line[4:] == 'OK'
-    assert actual_resp_body == pov.encode()
-    assert not header_exists('Connection', actual_headers)
-
-    # Test client-side close.
-    status_line, actual_headers, actual_resp_body = test_client.get(
-        '/page2', http_conn=http_connection,
-        headers=[('Connection', 'close')]
-    )
-    actual_status = int(status_line[:3])
-    assert actual_status == 200
-    assert status_line[4:] == 'OK'
-    assert actual_resp_body == pov.encode()
-    assert header_has_value('Connection', 'close', actual_headers)
-
-    # Make another request on the same connection, which should error.
-    with pytest.raises(http_client.NotConnected):
-        test_client.get('/pov', http_conn=http_connection)
-
-
-@pytest.mark.parametrize(
-    'set_cl',
-    (
-        False,  # Without Content-Length
-        True,  # With Content-Length
-    )
-)
-def test_streaming_11(test_client, set_cl):
-    """Test serving of streaming responses with HTTP/1.1 protocol."""
-    # Initialize a persistent HTTP connection
-    http_connection = test_client.get_connection()
-    http_connection.auto_open = False
-    http_connection.connect()
-
-    # Make the first request and assert there's no "Connection: close".
-    status_line, actual_headers, actual_resp_body = test_client.get(
-        '/pov', http_conn=http_connection
-    )
-    actual_status = int(status_line[:3])
-    assert actual_status == 200
-    assert status_line[4:] == 'OK'
-    assert actual_resp_body == pov.encode()
-    assert not header_exists('Connection', actual_headers)
-
-    # Make another, streamed request on the same connection.
-    if set_cl:
-        # When a Content-Length is provided, the content should stream
-        # without closing the connection.
-        status_line, actual_headers, actual_resp_body = test_client.get(
-            '/stream?set_cl=Yes', http_conn=http_connection
-        )
-        assert header_exists('Content-Length', actual_headers)
-        assert not header_has_value('Connection', 'close', actual_headers)
-        assert not header_exists('Transfer-Encoding', actual_headers)
-
-        assert actual_status == 200
-        assert status_line[4:] == 'OK'
-        assert actual_resp_body == b'0123456789'
-    else:
-        # When no Content-Length response header is provided,
-        # streamed output will either close the connection, or use
-        # chunked encoding, to determine transfer-length.
-        status_line, actual_headers, actual_resp_body = test_client.get(
-            '/stream', http_conn=http_connection
-        )
-        assert not header_exists('Content-Length', actual_headers)
-        assert actual_status == 200
-        assert status_line[4:] == 'OK'
-        assert actual_resp_body == b'0123456789'
-
-        chunked_response = False
-        for k, v in actual_headers:
-            if k.lower() == 'transfer-encoding':
-                if str(v) == 'chunked':
-                    chunked_response = True
-
-        if chunked_response:
-            assert not header_has_value('Connection', 'close', actual_headers)
-        else:
-            assert header_has_value('Connection', 'close', actual_headers)
-
-            # Make another request on the same connection, which should
-            # error.
-            with pytest.raises(http_client.NotConnected):
-                test_client.get('/pov', http_conn=http_connection)
-
-        # Try HEAD.
-        # See https://www.bitbucket.org/cherrypy/cherrypy/issue/864.
-        # TODO: figure out how can this be possible on an closed connection
-        # (chunked_response case)
-        status_line, actual_headers, actual_resp_body = test_client.head(
-            '/stream', http_conn=http_connection
-        )
-        assert actual_status == 200
-        assert status_line[4:] == 'OK'
-        assert actual_resp_body == b''
-        assert not header_exists('Transfer-Encoding', actual_headers)
-
-
-@pytest.mark.parametrize(
-    'set_cl',
-    (
-        False,  # Without Content-Length
-        True,  # With Content-Length
-    )
-)
-def test_streaming_10(test_client, set_cl):
-    """Test serving of streaming responses with HTTP/1.0 protocol."""
-    original_server_protocol = test_client.server_instance.protocol
-    test_client.server_instance.protocol = 'HTTP/1.0'
-
-    # Initialize a persistent HTTP connection
-    http_connection = test_client.get_connection()
-    http_connection.auto_open = False
-    http_connection.connect()
-
-    # Make the first request and assert Keep-Alive.
-    status_line, actual_headers, actual_resp_body = test_client.get(
-        '/pov', http_conn=http_connection,
-        headers=[('Connection', 'Keep-Alive')],
-        protocol='HTTP/1.0',
-    )
-    actual_status = int(status_line[:3])
-    assert actual_status == 200
-    assert status_line[4:] == 'OK'
-    assert actual_resp_body == pov.encode()
-    assert header_has_value('Connection', 'Keep-Alive', actual_headers)
-
-    # Make another, streamed request on the same connection.
-    if set_cl:
-        # When a Content-Length is provided, the content should
-        # stream without closing the connection.
-        status_line, actual_headers, actual_resp_body = test_client.get(
-            '/stream?set_cl=Yes', http_conn=http_connection,
-            headers=[('Connection', 'Keep-Alive')],
-            protocol='HTTP/1.0',
-        )
-        actual_status = int(status_line[:3])
-        assert actual_status == 200
-        assert status_line[4:] == 'OK'
-        assert actual_resp_body == b'0123456789'
-
-        assert header_exists('Content-Length', actual_headers)
-        assert header_has_value('Connection', 'Keep-Alive', actual_headers)
-        assert not header_exists('Transfer-Encoding', actual_headers)
-    else:
-        # When a Content-Length is not provided,
-        # the server should close the connection.
-        status_line, actual_headers, actual_resp_body = test_client.get(
-            '/stream', http_conn=http_connection,
-            headers=[('Connection', 'Keep-Alive')],
-            protocol='HTTP/1.0',
-        )
-        actual_status = int(status_line[:3])
-        assert actual_status == 200
-        assert status_line[4:] == 'OK'
-        assert actual_resp_body == b'0123456789'
-
-        assert not header_exists('Content-Length', actual_headers)
-        assert not header_has_value('Connection', 'Keep-Alive', actual_headers)
-        assert not header_exists('Transfer-Encoding', actual_headers)
-
-        # Make another request on the same connection, which should error.
-        with pytest.raises(http_client.NotConnected):
-            test_client.get(
-                '/pov', http_conn=http_connection,
-                protocol='HTTP/1.0',
-            )
-
-    test_client.server_instance.protocol = original_server_protocol
-
-
-@pytest.mark.parametrize(
-    'http_server_protocol',
-    (
-        'HTTP/1.0',
-        'HTTP/1.1',
-    )
-)
-def test_keepalive(test_client, http_server_protocol):
-    """Test Keep-Alive enabled connections."""
-    original_server_protocol = test_client.server_instance.protocol
-    test_client.server_instance.protocol = http_server_protocol
-
-    http_client_protocol = 'HTTP/1.0'
-
-    # Initialize a persistent HTTP connection
-    http_connection = test_client.get_connection()
-    http_connection.auto_open = False
-    http_connection.connect()
-
-    # Test a normal HTTP/1.0 request.
-    status_line, actual_headers, actual_resp_body = test_client.get(
-        '/page2',
-        protocol=http_client_protocol,
-    )
-    actual_status = int(status_line[:3])
-    assert actual_status == 200
-    assert status_line[4:] == 'OK'
-    assert actual_resp_body == pov.encode()
-    assert not header_exists('Connection', actual_headers)
-
-    # Test a keep-alive HTTP/1.0 request.
-
-    status_line, actual_headers, actual_resp_body = test_client.get(
-        '/page3', headers=[('Connection', 'Keep-Alive')],
-        http_conn=http_connection, protocol=http_client_protocol,
-    )
-    actual_status = int(status_line[:3])
-    assert actual_status == 200
-    assert status_line[4:] == 'OK'
-    assert actual_resp_body == pov.encode()
-    assert header_has_value('Connection', 'Keep-Alive', actual_headers)
-
-    # Remove the keep-alive header again.
-    status_line, actual_headers, actual_resp_body = test_client.get(
-        '/page3', http_conn=http_connection,
-        protocol=http_client_protocol,
-    )
-    actual_status = int(status_line[:3])
-    assert actual_status == 200
-    assert status_line[4:] == 'OK'
-    assert actual_resp_body == pov.encode()
-    assert not header_exists('Connection', actual_headers)
-
-    test_client.server_instance.protocol = original_server_protocol
-
-
-@pytest.mark.parametrize(
-    'timeout_before_headers',
-    (
-        True,
-        False,
-    )
-)
-def test_HTTP11_Timeout(test_client, timeout_before_headers):
-    """Check timeout without sending any data.
-
-    The server will close the conn with a 408.
-    """
-    conn = test_client.get_connection()
-    conn.auto_open = False
-    conn.connect()
-
-    if not timeout_before_headers:
-        # Connect but send half the headers only.
-        conn.send(b'GET /hello HTTP/1.1')
-        conn.send(('Host: %s' % conn.host).encode('ascii'))
-    # else: Connect but send nothing.
-
-    # Wait for our socket timeout
-    time.sleep(timeout * 2)
-
-    # The request should have returned 408 already.
-    response = conn.response_class(conn.sock, method='GET')
-    response.begin()
-    assert response.status == 408
-    conn.close()
-
-
-def test_HTTP11_Timeout_after_request(test_client):
-    """Check timeout after at least one request has succeeded.
-
-    The server should close the connection without 408.
-    """
-    fail_msg = "Writing to timed out socket didn't fail as it should have: %s"
-
-    # Make an initial request
-    conn = test_client.get_connection()
-    conn.putrequest('GET', '/timeout?t=%s' % timeout, skip_host=True)
-    conn.putheader('Host', conn.host)
-    conn.endheaders()
-    response = conn.response_class(conn.sock, method='GET')
-    response.begin()
-    assert response.status == 200
-    actual_body = response.read()
-    expected_body = str(timeout).encode()
-    assert actual_body == expected_body
-
-    # Make a second request on the same socket
-    conn._output(b'GET /hello HTTP/1.1')
-    conn._output(('Host: %s' % conn.host).encode('ascii'))
-    conn._send_output()
-    response = conn.response_class(conn.sock, method='GET')
-    response.begin()
-    assert response.status == 200
-    actual_body = response.read()
-    expected_body = b'Hello, world!'
-    assert actual_body == expected_body
-
-    # Wait for our socket timeout
-    time.sleep(timeout * 2)
-
-    # Make another request on the same socket, which should error
-    conn._output(b'GET /hello HTTP/1.1')
-    conn._output(('Host: %s' % conn.host).encode('ascii'))
-    conn._send_output()
-    response = conn.response_class(conn.sock, method='GET')
-    try:
-        response.begin()
-    except (socket.error, http_client.BadStatusLine):
-        pass
-    except Exception as ex:
-        pytest.fail(fail_msg % ex)
-    else:
-        if response.status != 408:
-            pytest.fail(fail_msg % response.read())
-
-    conn.close()
-
-    # Make another request on a new socket, which should work
-    conn = test_client.get_connection()
-    conn.putrequest('GET', '/pov', skip_host=True)
-    conn.putheader('Host', conn.host)
-    conn.endheaders()
-    response = conn.response_class(conn.sock, method='GET')
-    response.begin()
-    assert response.status == 200
-    actual_body = response.read()
-    expected_body = pov.encode()
-    assert actual_body == expected_body
-
-    # Make another request on the same socket,
-    # but timeout on the headers
-    conn.send(b'GET /hello HTTP/1.1')
-    # Wait for our socket timeout
-    time.sleep(timeout * 2)
-    response = conn.response_class(conn.sock, method='GET')
-    try:
-        response.begin()
-    except (socket.error, http_client.BadStatusLine):
-        pass
-    except Exception as ex:
-        pytest.fail(fail_msg % ex)
-    else:
-        if response.status != 408:
-            pytest.fail(fail_msg % response.read())
-
-    conn.close()
-
-    # Retry the request on a new connection, which should work
-    conn = test_client.get_connection()
-    conn.putrequest('GET', '/pov', skip_host=True)
-    conn.putheader('Host', conn.host)
-    conn.endheaders()
-    response = conn.response_class(conn.sock, method='GET')
-    response.begin()
-    assert response.status == 200
-    actual_body = response.read()
-    expected_body = pov.encode()
-    assert actual_body == expected_body
-    conn.close()
-
-
-def test_HTTP11_pipelining(test_client):
-    """Test HTTP/1.1 pipelining.
-
-    httplib doesn't support this directly.
-    """
-    conn = test_client.get_connection()
-
-    # Put request 1
-    conn.putrequest('GET', '/hello', skip_host=True)
-    conn.putheader('Host', conn.host)
-    conn.endheaders()
-
-    for trial in range(5):
-        # Put next request
-        conn._output(
-            ('GET /hello?%s HTTP/1.1' % trial).encode('iso-8859-1')
-        )
-        conn._output(('Host: %s' % conn.host).encode('ascii'))
-        conn._send_output()
-
-        # Retrieve previous response
-        response = conn.response_class(conn.sock, method='GET')
-        # there is a bug in python3 regarding the buffering of
-        # ``conn.sock``. Until that bug get's fixed we will
-        # monkey patch the ``reponse`` instance.
-        # https://bugs.python.org/issue23377
-        if six.PY3:
-            response.fp = conn.sock.makefile('rb', 0)
-        response.begin()
-        body = response.read(13)
-        assert response.status == 200
-        assert body == b'Hello, world!'
-
-    # Retrieve final response
-    response = conn.response_class(conn.sock, method='GET')
-    response.begin()
-    body = response.read()
-    assert response.status == 200
-    assert body == b'Hello, world!'
-
-    conn.close()
-
-
-def test_100_Continue(test_client):
-    """Test 100-continue header processing."""
-    conn = test_client.get_connection()
-
-    # Try a page without an Expect request header first.
-    # Note that httplib's response.begin automatically ignores
-    # 100 Continue responses, so we must manually check for it.
-    conn.putrequest('POST', '/upload', skip_host=True)
-    conn.putheader('Host', conn.host)
-    conn.putheader('Content-Type', 'text/plain')
-    conn.putheader('Content-Length', '4')
-    conn.endheaders()
-    conn.send(b"d'oh")
-    response = conn.response_class(conn.sock, method='POST')
-    version, status, reason = response._read_status()
-    assert status != 100
-    conn.close()
-
-    # Now try a page with an Expect header...
-    conn.connect()
-    conn.putrequest('POST', '/upload', skip_host=True)
-    conn.putheader('Host', conn.host)
-    conn.putheader('Content-Type', 'text/plain')
-    conn.putheader('Content-Length', '17')
-    conn.putheader('Expect', '100-continue')
-    conn.endheaders()
-    response = conn.response_class(conn.sock, method='POST')
-
-    # ...assert and then skip the 100 response
-    version, status, reason = response._read_status()
-    assert status == 100
-    while True:
-        line = response.fp.readline().strip()
-        if line:
-            pytest.fail(
-                '100 Continue should not output any headers. Got %r' %
-                line)
-        else:
-            break
-
-    # ...send the body
-    body = b'I am a small file'
-    conn.send(body)
-
-    # ...get the final response
-    response.begin()
-    status_line, actual_headers, actual_resp_body = webtest.shb(response)
-    actual_status = int(status_line[:3])
-    assert actual_status == 200
-    expected_resp_body = ("thanks for '%s'" % body).encode()
-    assert actual_resp_body == expected_resp_body
-    conn.close()
-
-
-@pytest.mark.parametrize(
-    'max_request_body_size',
-    (
-        0,
-        1001,
-    )
-)
-def test_readall_or_close(test_client, max_request_body_size):
-    """Test a max_request_body_size of 0 (the default) and 1001."""
-    old_max = test_client.server_instance.max_request_body_size
-
-    test_client.server_instance.max_request_body_size = max_request_body_size
-
-    conn = test_client.get_connection()
-
-    # Get a POST page with an error
-    conn.putrequest('POST', '/err_before_read', skip_host=True)
-    conn.putheader('Host', conn.host)
-    conn.putheader('Content-Type', 'text/plain')
-    conn.putheader('Content-Length', '1000')
-    conn.putheader('Expect', '100-continue')
-    conn.endheaders()
-    response = conn.response_class(conn.sock, method='POST')
-
-    # ...assert and then skip the 100 response
-    version, status, reason = response._read_status()
-    assert status == 100
-    skip = True
-    while skip:
-        skip = response.fp.readline().strip()
-
-    # ...send the body
-    conn.send(b'x' * 1000)
-
-    # ...get the final response
-    response.begin()
-    status_line, actual_headers, actual_resp_body = webtest.shb(response)
-    actual_status = int(status_line[:3])
-    assert actual_status == 500
-
-    # Now try a working page with an Expect header...
-    conn._output(b'POST /upload HTTP/1.1')
-    conn._output(('Host: %s' % conn.host).encode('ascii'))
-    conn._output(b'Content-Type: text/plain')
-    conn._output(b'Content-Length: 17')
-    conn._output(b'Expect: 100-continue')
-    conn._send_output()
-    response = conn.response_class(conn.sock, method='POST')
-
-    # ...assert and then skip the 100 response
-    version, status, reason = response._read_status()
-    assert status == 100
-    skip = True
-    while skip:
-        skip = response.fp.readline().strip()
-
-    # ...send the body
-    body = b'I am a small file'
-    conn.send(body)
-
-    # ...get the final response
-    response.begin()
-    status_line, actual_headers, actual_resp_body = webtest.shb(response)
-    actual_status = int(status_line[:3])
-    assert actual_status == 200
-    expected_resp_body = ("thanks for '%s'" % body).encode()
-    assert actual_resp_body == expected_resp_body
-    conn.close()
-
-    test_client.server_instance.max_request_body_size = old_max
-
-
-def test_No_Message_Body(test_client):
-    """Test HTTP queries with an empty response body."""
-    # Initialize a persistent HTTP connection
-    http_connection = test_client.get_connection()
-    http_connection.auto_open = False
-    http_connection.connect()
-
-    # Make the first request and assert there's no "Connection: close".
-    status_line, actual_headers, actual_resp_body = test_client.get(
-        '/pov', http_conn=http_connection
-    )
-    actual_status = int(status_line[:3])
-    assert actual_status == 200
-    assert status_line[4:] == 'OK'
-    assert actual_resp_body == pov.encode()
-    assert not header_exists('Connection', actual_headers)
-
-    # Make a 204 request on the same connection.
-    status_line, actual_headers, actual_resp_body = test_client.get(
-        '/custom/204', http_conn=http_connection
-    )
-    actual_status = int(status_line[:3])
-    assert actual_status == 204
-    assert not header_exists('Content-Length', actual_headers)
-    assert actual_resp_body == b''
-    assert not header_exists('Connection', actual_headers)
-
-    # Make a 304 request on the same connection.
-    status_line, actual_headers, actual_resp_body = test_client.get(
-        '/custom/304', http_conn=http_connection
-    )
-    actual_status = int(status_line[:3])
-    assert actual_status == 304
-    assert not header_exists('Content-Length', actual_headers)
-    assert actual_resp_body == b''
-    assert not header_exists('Connection', actual_headers)
-
-
-@pytest.mark.xfail(
-    reason='Server does not correctly read trailers/ending of the previous '
-           'HTTP request, thus the second request fails as the server tries '
-           r"to parse b'Content-Type: application/json\r\n' as a "
-           'Request-Line. This results in HTTP status code 400, instead of 413'
-           'Ref: https://github.com/cherrypy/cheroot/issues/69'
-)
-def test_Chunked_Encoding(test_client):
-    """Test HTTP uploads with chunked transfer-encoding."""
-    # Initialize a persistent HTTP connection
-    conn = test_client.get_connection()
-
-    # Try a normal chunked request (with extensions)
-    body = (
-        b'8;key=value\r\nxx\r\nxxxx\r\n5\r\nyyyyy\r\n0\r\n'
-        b'Content-Type: application/json\r\n'
-        b'\r\n'
-    )
-    conn.putrequest('POST', '/upload', skip_host=True)
-    conn.putheader('Host', conn.host)
-    conn.putheader('Transfer-Encoding', 'chunked')
-    conn.putheader('Trailer', 'Content-Type')
-    # Note that this is somewhat malformed:
-    # we shouldn't be sending Content-Length.
-    # RFC 2616 says the server should ignore it.
-    conn.putheader('Content-Length', '3')
-    conn.endheaders()
-    conn.send(body)
-    response = conn.getresponse()
-    status_line, actual_headers, actual_resp_body = webtest.shb(response)
-    actual_status = int(status_line[:3])
-    assert actual_status == 200
-    assert status_line[4:] == 'OK'
-    expected_resp_body = ("thanks for '%s'" % b'xx\r\nxxxxyyyyy').encode()
-    assert actual_resp_body == expected_resp_body
-
-    # Try a chunked request that exceeds server.max_request_body_size.
-    # Note that the delimiters and trailer are included.
-    body = b'3e3\r\n' + (b'x' * 995) + b'\r\n0\r\n\r\n'
-    conn.putrequest('POST', '/upload', skip_host=True)
-    conn.putheader('Host', conn.host)
-    conn.putheader('Transfer-Encoding', 'chunked')
-    conn.putheader('Content-Type', 'text/plain')
-    # Chunked requests don't need a content-length
-    # conn.putheader("Content-Length", len(body))
-    conn.endheaders()
-    conn.send(body)
-    response = conn.getresponse()
-    status_line, actual_headers, actual_resp_body = webtest.shb(response)
-    actual_status = int(status_line[:3])
-    assert actual_status == 413
-    conn.close()
-
-
-def test_Content_Length_in(test_client):
-    """Try a non-chunked request where Content-Length exceeds limit.
-
-    (server.max_request_body_size).
-    Assert error before body send.
-    """
-    # Initialize a persistent HTTP connection
-    conn = test_client.get_connection()
-
-    conn.putrequest('POST', '/upload', skip_host=True)
-    conn.putheader('Host', conn.host)
-    conn.putheader('Content-Type', 'text/plain')
-    conn.putheader('Content-Length', '9999')
-    conn.endheaders()
-    response = conn.getresponse()
-    status_line, actual_headers, actual_resp_body = webtest.shb(response)
-    actual_status = int(status_line[:3])
-    assert actual_status == 413
-    expected_resp_body = (
-        b'The entity sent with the request exceeds '
-        b'the maximum allowed bytes.'
-    )
-    assert actual_resp_body == expected_resp_body
-    conn.close()
-
-
-def test_Content_Length_not_int(test_client):
-    """Test that malicious Content-Length header returns 400."""
-    status_line, actual_headers, actual_resp_body = test_client.post(
-        '/upload',
-        headers=[
-            ('Content-Type', 'text/plain'),
-            ('Content-Length', 'not-an-integer'),
-        ],
-    )
-    actual_status = int(status_line[:3])
-
-    assert actual_status == 400
-    assert actual_resp_body == b'Malformed Content-Length Header.'
-
-
-@pytest.mark.parametrize(
-    'uri,expected_resp_status,expected_resp_body',
-    (
-        ('/wrong_cl_buffered', 500,
-         (b'The requested resource returned more bytes than the '
-          b'declared Content-Length.')),
-        ('/wrong_cl_unbuffered', 200, b'I too'),
-    )
-)
-def test_Content_Length_out(
-    test_client,
-    uri, expected_resp_status, expected_resp_body
-):
-    """Test response with Content-Length less than the response body.
-
-    (non-chunked response)
-    """
-    conn = test_client.get_connection()
-    conn.putrequest('GET', uri, skip_host=True)
-    conn.putheader('Host', conn.host)
-    conn.endheaders()
-
-    response = conn.getresponse()
-    status_line, actual_headers, actual_resp_body = webtest.shb(response)
-    actual_status = int(status_line[:3])
-
-    assert actual_status == expected_resp_status
-    assert actual_resp_body == expected_resp_body
-
-    conn.close()
-
-
-@pytest.mark.xfail(
-    reason='Sometimes this test fails due to low timeout. '
-           'Ref: https://github.com/cherrypy/cherrypy/issues/598'
-)
-def test_598(test_client):
-    """Test serving large file with a read timeout in place."""
-    # Initialize a persistent HTTP connection
-    conn = test_client.get_connection()
-    remote_data_conn = urllib.request.urlopen(
-        '%s://%s:%s/one_megabyte_of_a'
-        % ('http', conn.host, conn.port)
-    )
-    buf = remote_data_conn.read(512)
-    time.sleep(timeout * 0.6)
-    remaining = (1024 * 1024) - 512
-    while remaining:
-        data = remote_data_conn.read(remaining)
-        if not data:
-            break
-        buf += data
-        remaining -= len(data)
-
-    assert len(buf) == 1024 * 1024
-    assert buf == b'a' * 1024 * 1024
-    assert remaining == 0
-    remote_data_conn.close()
-
-
-@pytest.mark.parametrize(
-    'invalid_terminator',
-    (
-        b'\n\n',
-        b'\r\n\n',
-    )
-)
-def test_No_CRLF(test_client, invalid_terminator):
-    """Test HTTP queries with no valid CRLF terminators."""
-    # Initialize a persistent HTTP connection
-    conn = test_client.get_connection()
-
-    # (b'%s' % b'') is not supported in Python 3.4, so just use +
-    conn.send(b'GET /hello HTTP/1.1' + invalid_terminator)
-    response = conn.response_class(conn.sock, method='GET')
-    response.begin()
-    actual_resp_body = response.read()
-    expected_resp_body = b'HTTP requires CRLF terminators'
-    assert actual_resp_body == expected_resp_body
-    conn.close()
diff --git a/libraries/cheroot/test/test_core.py b/libraries/cheroot/test/test_core.py
deleted file mode 100644
index 7c91b13e..00000000
--- a/libraries/cheroot/test/test_core.py
+++ /dev/null
@@ -1,405 +0,0 @@
-"""Tests for managing HTTP issues (malformed requests, etc)."""
-# -*- coding: utf-8 -*-
-# vim: set fileencoding=utf-8 :
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import errno
-import socket
-
-import pytest
-import six
-from six.moves import urllib
-
-from cheroot.test import helper
-
-
-HTTP_BAD_REQUEST = 400
-HTTP_LENGTH_REQUIRED = 411
-HTTP_NOT_FOUND = 404
-HTTP_OK = 200
-HTTP_VERSION_NOT_SUPPORTED = 505
-
-
-class HelloController(helper.Controller):
-    """Controller for serving WSGI apps."""
-
-    def hello(req, resp):
-        """Render Hello world."""
-        return 'Hello world!'
-
-    def body_required(req, resp):
-        """Render Hello world or set 411."""
-        if req.environ.get('Content-Length', None) is None:
-            resp.status = '411 Length Required'
-            return
-        return 'Hello world!'
-
-    def query_string(req, resp):
-        """Render QUERY_STRING value."""
-        return req.environ.get('QUERY_STRING', '')
-
-    def asterisk(req, resp):
-        """Render request method value."""
-        method = req.environ.get('REQUEST_METHOD', 'NO METHOD FOUND')
-        tmpl = 'Got asterisk URI path with {method} method'
-        return tmpl.format(**locals())
-
-    def _munge(string):
-        """Encode PATH_INFO correctly depending on Python version.
-
-        WSGI 1.0 is a mess around unicode. Create endpoints
-        that match the PATH_INFO that it produces.
-        """
-        if six.PY3:
-            return string.encode('utf-8').decode('latin-1')
-        return string
-
-    handlers = {
-        '/hello': hello,
-        '/no_body': hello,
-        '/body_required': body_required,
-        '/query_string': query_string,
-        _munge('/привіт'): hello,
-        _munge('/Юххууу'): hello,
-        '/\xa0Ðblah key 0 900 4 data': hello,
-        '/*': asterisk,
-    }
-
-
-def _get_http_response(connection, method='GET'):
-    c = connection
-    kwargs = {'strict': c.strict} if hasattr(c, 'strict') else {}
-    # Python 3.2 removed the 'strict' feature, saying:
-    # "http.client now always assumes HTTP/1.x compliant servers."
-    return c.response_class(c.sock, method=method, **kwargs)
-
-
-@pytest.fixture
-def testing_server(wsgi_server_client):
-    """Attach a WSGI app to the given server and pre-configure it."""
-    wsgi_server = wsgi_server_client.server_instance
-    wsgi_server.wsgi_app = HelloController()
-    wsgi_server.max_request_body_size = 30000000
-    wsgi_server.server_client = wsgi_server_client
-    return wsgi_server
-
-
-@pytest.fixture
-def test_client(testing_server):
-    """Get and return a test client out of the given server."""
-    return testing_server.server_client
-
-
-def test_http_connect_request(test_client):
-    """Check that CONNECT query results in Method Not Allowed status."""
-    status_line = test_client.connect('/anything')[0]
-    actual_status = int(status_line[:3])
-    assert actual_status == 405
-
-
-def test_normal_request(test_client):
-    """Check that normal GET query succeeds."""
-    status_line, _, actual_resp_body = test_client.get('/hello')
-    actual_status = int(status_line[:3])
-    assert actual_status == HTTP_OK
-    assert actual_resp_body == b'Hello world!'
-
-
-def test_query_string_request(test_client):
-    """Check that GET param is parsed well."""
-    status_line, _, actual_resp_body = test_client.get(
-        '/query_string?test=True'
-    )
-    actual_status = int(status_line[:3])
-    assert actual_status == HTTP_OK
-    assert actual_resp_body == b'test=True'
-
-
-@pytest.mark.parametrize(
-    'uri',
-    (
-        '/hello',  # plain
-        '/query_string?test=True',  # query
-        '/{0}?{1}={2}'.format(  # quoted unicode
-            *map(urllib.parse.quote, ('Юххууу', 'ї', 'йо'))
-        ),
-    )
-)
-def test_parse_acceptable_uri(test_client, uri):
-    """Check that server responds with OK to valid GET queries."""
-    status_line = test_client.get(uri)[0]
-    actual_status = int(status_line[:3])
-    assert actual_status == HTTP_OK
-
-
-@pytest.mark.xfail(six.PY2, reason='Fails on Python 2')
-def test_parse_uri_unsafe_uri(test_client):
-    """Test that malicious URI does not allow HTTP injection.
-
-    This effectively checks that sending GET request with URL
-
-    /%A0%D0blah%20key%200%20900%204%20data
-
-    is not converted into
-
-    GET /
-    blah key 0 900 4 data
-    HTTP/1.1
-
-    which would be a security issue otherwise.
-    """
-    c = test_client.get_connection()
-    resource = '/\xa0Ðblah key 0 900 4 data'.encode('latin-1')
-    quoted = urllib.parse.quote(resource)
-    assert quoted == '/%A0%D0blah%20key%200%20900%204%20data'
-    request = 'GET {quoted} HTTP/1.1'.format(**locals())
-    c._output(request.encode('utf-8'))
-    c._send_output()
-    response = _get_http_response(c, method='GET')
-    response.begin()
-    assert response.status == HTTP_OK
-    assert response.fp.read(12) == b'Hello world!'
-    c.close()
-
-
-def test_parse_uri_invalid_uri(test_client):
-    """Check that server responds with Bad Request to invalid GET queries.
-
-    Invalid request line test case: it should only contain US-ASCII.
-    """
-    c = test_client.get_connection()
-    c._output(u'GET /йопта! HTTP/1.1'.encode('utf-8'))
-    c._send_output()
-    response = _get_http_response(c, method='GET')
-    response.begin()
-    assert response.status == HTTP_BAD_REQUEST
-    assert response.fp.read(21) == b'Malformed Request-URI'
-    c.close()
-
-
-@pytest.mark.parametrize(
-    'uri',
-    (
-        'hello',  # ascii
-        'привіт',  # non-ascii
-    )
-)
-def test_parse_no_leading_slash_invalid(test_client, uri):
-    """Check that server responds with Bad Request to invalid GET queries.
-
-    Invalid request line test case: it should have leading slash (be absolute).
-    """
-    status_line, _, actual_resp_body = test_client.get(
-        urllib.parse.quote(uri)
-    )
-    actual_status = int(status_line[:3])
-    assert actual_status == HTTP_BAD_REQUEST
-    assert b'starting with a slash' in actual_resp_body
-
-
-def test_parse_uri_absolute_uri(test_client):
-    """Check that server responds with Bad Request to Absolute URI.
-
-    Only proxy servers should allow this.
-    """
-    status_line, _, actual_resp_body = test_client.get('http://google.com/')
-    actual_status = int(status_line[:3])
-    assert actual_status == HTTP_BAD_REQUEST
-    expected_body = b'Absolute URI not allowed if server is not a proxy.'
-    assert actual_resp_body == expected_body
-
-
-def test_parse_uri_asterisk_uri(test_client):
-    """Check that server responds with OK to OPTIONS with "*" Absolute URI."""
-    status_line, _, actual_resp_body = test_client.options('*')
-    actual_status = int(status_line[:3])
-    assert actual_status == HTTP_OK
-    expected_body = b'Got asterisk URI path with OPTIONS method'
-    assert actual_resp_body == expected_body
-
-
-def test_parse_uri_fragment_uri(test_client):
-    """Check that server responds with Bad Request to URI with fragment."""
-    status_line, _, actual_resp_body = test_client.get(
-        '/hello?test=something#fake',
-    )
-    actual_status = int(status_line[:3])
-    assert actual_status == HTTP_BAD_REQUEST
-    expected_body = b'Illegal #fragment in Request-URI.'
-    assert actual_resp_body == expected_body
-
-
-def test_no_content_length(test_client):
-    """Test POST query with an empty body being successful."""
-    # "The presence of a message-body in a request is signaled by the
-    # inclusion of a Content-Length or Transfer-Encoding header field in
-    # the request's message-headers."
-    #
-    # Send a message with neither header and no body.
-    c = test_client.get_connection()
-    c.request('POST', '/no_body')
-    response = c.getresponse()
-    actual_resp_body = response.fp.read()
-    actual_status = response.status
-    assert actual_status == HTTP_OK
-    assert actual_resp_body == b'Hello world!'
-
-
-def test_content_length_required(test_client):
-    """Test POST query with body failing because of missing Content-Length."""
-    # Now send a message that has no Content-Length, but does send a body.
-    # Verify that CP times out the socket and responds
-    # with 411 Length Required.
-
-    c = test_client.get_connection()
-    c.request('POST', '/body_required')
-    response = c.getresponse()
-    response.fp.read()
-
-    actual_status = response.status
-    assert actual_status == HTTP_LENGTH_REQUIRED
-
-
-@pytest.mark.parametrize(
-    'request_line,status_code,expected_body',
-    (
-        (b'GET /',  # missing proto
-         HTTP_BAD_REQUEST, b'Malformed Request-Line'),
-        (b'GET / HTTPS/1.1',  # invalid proto
-         HTTP_BAD_REQUEST, b'Malformed Request-Line: bad protocol'),
-        (b'GET / HTTP/2.15',  # invalid ver
-         HTTP_VERSION_NOT_SUPPORTED, b'Cannot fulfill request'),
-    )
-)
-def test_malformed_request_line(
-    test_client, request_line,
-    status_code, expected_body
-):
-    """Test missing or invalid HTTP version in Request-Line."""
-    c = test_client.get_connection()
-    c._output(request_line)
-    c._send_output()
-    response = _get_http_response(c, method='GET')
-    response.begin()
-    assert response.status == status_code
-    assert response.fp.read(len(expected_body)) == expected_body
-    c.close()
-
-
-def test_malformed_http_method(test_client):
-    """Test non-uppercase HTTP method."""
-    c = test_client.get_connection()
-    c.putrequest('GeT', '/malformed_method_case')
-    c.putheader('Content-Type', 'text/plain')
-    c.endheaders()
-
-    response = c.getresponse()
-    actual_status = response.status
-    assert actual_status == HTTP_BAD_REQUEST
-    actual_resp_body = response.fp.read(21)
-    assert actual_resp_body == b'Malformed method name'
-
-
-def test_malformed_header(test_client):
-    """Check that broken HTTP header results in Bad Request."""
-    c = test_client.get_connection()
-    c.putrequest('GET', '/')
-    c.putheader('Content-Type', 'text/plain')
-    # See https://www.bitbucket.org/cherrypy/cherrypy/issue/941
-    c._output(b'Re, 1.2.3.4#015#012')
-    c.endheaders()
-
-    response = c.getresponse()
-    actual_status = response.status
-    assert actual_status == HTTP_BAD_REQUEST
-    actual_resp_body = response.fp.read(20)
-    assert actual_resp_body == b'Illegal header line.'
-
-
-def test_request_line_split_issue_1220(test_client):
-    """Check that HTTP request line of exactly 256 chars length is OK."""
-    Request_URI = (
-        '/hello?'
-        'intervenant-entreprise-evenement_classaction='
-        'evenement-mailremerciements'
-        '&_path=intervenant-entreprise-evenement'
-        '&intervenant-entreprise-evenement_action-id=19404'
-        '&intervenant-entreprise-evenement_id=19404'
-        '&intervenant-entreprise_id=28092'
-    )
-    assert len('GET %s HTTP/1.1\r\n' % Request_URI) == 256
-
-    actual_resp_body = test_client.get(Request_URI)[2]
-    assert actual_resp_body == b'Hello world!'
-
-
-def test_garbage_in(test_client):
-    """Test that server sends an error for garbage received over TCP."""
-    # Connect without SSL regardless of server.scheme
-
-    c = test_client.get_connection()
-    c._output(b'gjkgjklsgjklsgjkljklsg')
-    c._send_output()
-    response = c.response_class(c.sock, method='GET')
-    try:
-        response.begin()
-        actual_status = response.status
-        assert actual_status == HTTP_BAD_REQUEST
-        actual_resp_body = response.fp.read(22)
-        assert actual_resp_body == b'Malformed Request-Line'
-        c.close()
-    except socket.error as ex:
-        # "Connection reset by peer" is also acceptable.
-        if ex.errno != errno.ECONNRESET:
-            raise
-
-
-class CloseController:
-    """Controller for testing the close callback."""
-
-    def __call__(self, environ, start_response):
-        """Get the req to know header sent status."""
-        self.req = start_response.__self__.req
-        resp = CloseResponse(self.close)
-        start_response(resp.status, resp.headers.items())
-        return resp
-
-    def close(self):
-        """Close, writing hello."""
-        self.req.write(b'hello')
-
-
-class CloseResponse:
-    """Dummy empty response to trigger the no body status."""
-
-    def __init__(self, close):
-        """Use some defaults to ensure we have a header."""
-        self.status = '200 OK'
-        self.headers = {'Content-Type': 'text/html'}
-        self.close = close
-
-    def __getitem__(self, index):
-        """Ensure we don't have a body."""
-        raise IndexError()
-
-    def output(self):
-        """Return self to hook the close method."""
-        return self
-
-
-@pytest.fixture
-def testing_server_close(wsgi_server_client):
-    """Attach a WSGI app to the given server and pre-configure it."""
-    wsgi_server = wsgi_server_client.server_instance
-    wsgi_server.wsgi_app = CloseController()
-    wsgi_server.max_request_body_size = 30000000
-    wsgi_server.server_client = wsgi_server_client
-    return wsgi_server
-
-
-def test_send_header_before_closing(testing_server_close):
-    """Test we are actually sending the headers before calling 'close'."""
-    _, _, resp_body = testing_server_close.server_client.get('/')
-    assert resp_body == b'hello'
diff --git a/libraries/cheroot/test/test_server.py b/libraries/cheroot/test/test_server.py
deleted file mode 100644
index c53f7a81..00000000
--- a/libraries/cheroot/test/test_server.py
+++ /dev/null
@@ -1,193 +0,0 @@
-"""Tests for the HTTP server."""
-# -*- coding: utf-8 -*-
-# vim: set fileencoding=utf-8 :
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import os
-import socket
-import tempfile
-import threading
-import time
-
-import pytest
-
-from .._compat import bton
-from ..server import Gateway, HTTPServer
-from ..testing import (
-    ANY_INTERFACE_IPV4,
-    ANY_INTERFACE_IPV6,
-    EPHEMERAL_PORT,
-    get_server_client,
-)
-
-
-def make_http_server(bind_addr):
-    """Create and start an HTTP server bound to bind_addr."""
-    httpserver = HTTPServer(
-        bind_addr=bind_addr,
-        gateway=Gateway,
-    )
-
-    threading.Thread(target=httpserver.safe_start).start()
-
-    while not httpserver.ready:
-        time.sleep(0.1)
-
-    return httpserver
-
-
-non_windows_sock_test = pytest.mark.skipif(
-    not hasattr(socket, 'AF_UNIX'),
-    reason='UNIX domain sockets are only available under UNIX-based OS',
-)
-
-
-@pytest.fixture
-def http_server():
-    """Provision a server creator as a fixture."""
-    def start_srv():
-        bind_addr = yield
-        httpserver = make_http_server(bind_addr)
-        yield httpserver
-        yield httpserver
-
-    srv_creator = iter(start_srv())
-    next(srv_creator)
-    yield srv_creator
-    try:
-        while True:
-            httpserver = next(srv_creator)
-            if httpserver is not None:
-                httpserver.stop()
-    except StopIteration:
-        pass
-
-
-@pytest.fixture
-def unix_sock_file():
-    """Check that bound UNIX socket address is stored in server."""
-    tmp_sock_fh, tmp_sock_fname = tempfile.mkstemp()
-
-    yield tmp_sock_fname
-
-    os.close(tmp_sock_fh)
-    os.unlink(tmp_sock_fname)
-
-
-def test_prepare_makes_server_ready():
-    """Check that prepare() makes the server ready, and stop() clears it."""
-    httpserver = HTTPServer(
-        bind_addr=(ANY_INTERFACE_IPV4, EPHEMERAL_PORT),
-        gateway=Gateway,
-    )
-
-    assert not httpserver.ready
-    assert not httpserver.requests._threads
-
-    httpserver.prepare()
-
-    assert httpserver.ready
-    assert httpserver.requests._threads
-    for thr in httpserver.requests._threads:
-        assert thr.ready
-
-    httpserver.stop()
-
-    assert not httpserver.requests._threads
-    assert not httpserver.ready
-
-
-def test_stop_interrupts_serve():
-    """Check that stop() interrupts running of serve()."""
-    httpserver = HTTPServer(
-        bind_addr=(ANY_INTERFACE_IPV4, EPHEMERAL_PORT),
-        gateway=Gateway,
-    )
-
-    httpserver.prepare()
-    serve_thread = threading.Thread(target=httpserver.serve)
-    serve_thread.start()
-
-    serve_thread.join(0.5)
-    assert serve_thread.is_alive()
-
-    httpserver.stop()
-
-    serve_thread.join(0.5)
-    assert not serve_thread.is_alive()
-
-
-@pytest.mark.parametrize(
-    'ip_addr',
-    (
-        ANY_INTERFACE_IPV4,
-        ANY_INTERFACE_IPV6,
-    )
-)
-def test_bind_addr_inet(http_server, ip_addr):
-    """Check that bound IP address is stored in server."""
-    httpserver = http_server.send((ip_addr, EPHEMERAL_PORT))
-
-    assert httpserver.bind_addr[0] == ip_addr
-    assert httpserver.bind_addr[1] != EPHEMERAL_PORT
-
-
-@non_windows_sock_test
-def test_bind_addr_unix(http_server, unix_sock_file):
-    """Check that bound UNIX socket address is stored in server."""
-    httpserver = http_server.send(unix_sock_file)
-
-    assert httpserver.bind_addr == unix_sock_file
-
-
-@pytest.mark.skip(reason="Abstract sockets don't work currently")
-@non_windows_sock_test
-def test_bind_addr_unix_abstract(http_server):
-    """Check that bound UNIX socket address is stored in server."""
-    unix_abstract_sock = b'\x00cheroot/test/socket/here.sock'
-    httpserver = http_server.send(unix_abstract_sock)
-
-    assert httpserver.bind_addr == unix_abstract_sock
-
-
-PEERCRED_IDS_URI = '/peer_creds/ids'
-PEERCRED_TEXTS_URI = '/peer_creds/texts'
-
-
-class _TestGateway(Gateway):
-    def respond(self):
-        req = self.req
-        conn = req.conn
-        req_uri = bton(req.uri)
-        if req_uri == PEERCRED_IDS_URI:
-            peer_creds = conn.peer_pid, conn.peer_uid, conn.peer_gid
-            return ['|'.join(map(str, peer_creds))]
-        elif req_uri == PEERCRED_TEXTS_URI:
-            return ['!'.join((conn.peer_user, conn.peer_group))]
-        return super(_TestGateway, self).respond()
-
-
-@pytest.mark.skip(
-    reason='Test HTTP client is not able to work through UNIX socket currently'
-)
-@non_windows_sock_test
-def test_peercreds_unix_sock(http_server, unix_sock_file):
-    """Check that peercred lookup and resolution work when enabled."""
-    httpserver = http_server.send(unix_sock_file)
-    httpserver.gateway = _TestGateway
-    httpserver.peercreds_enabled = True
-
-    testclient = get_server_client(httpserver)
-
-    expected_peercreds = os.getpid(), os.getuid(), os.getgid()
-    expected_peercreds = '|'.join(map(str, expected_peercreds))
-    assert testclient.get(PEERCRED_IDS_URI) == expected_peercreds
-    assert 'RuntimeError' in testclient.get(PEERCRED_TEXTS_URI)
-
-    httpserver.peercreds_resolve_enabled = True
-    import grp
-    expected_textcreds = os.getlogin(), grp.getgrgid(os.getgid()).gr_name
-    expected_textcreds = '!'.join(map(str, expected_textcreds))
-    assert testclient.get(PEERCRED_TEXTS_URI) == expected_textcreds
diff --git a/libraries/cheroot/test/webtest.py b/libraries/cheroot/test/webtest.py
deleted file mode 100644
index 43448f5b..00000000
--- a/libraries/cheroot/test/webtest.py
+++ /dev/null
@@ -1,581 +0,0 @@
-"""Extensions to unittest for web frameworks.
-
-Use the WebCase.getPage method to request a page from your HTTP server.
-Framework Integration
-=====================
-If you have control over your server process, you can handle errors
-in the server-side of the HTTP conversation a bit better. You must run
-both the client (your WebCase tests) and the server in the same process
-(but in separate threads, obviously).
-When an error occurs in the framework, call server_error. It will print
-the traceback to stdout, and keep any assertions you have from running
-(the assumption is that, if the server errors, the page output will not
-be of further significance to your tests).
-"""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import pprint
-import re
-import socket
-import sys
-import time
-import traceback
-import os
-import json
-import unittest
-import warnings
-
-from six.moves import range, http_client, map, urllib_parse
-import six
-
-from more_itertools.more import always_iterable
-
-
-def interface(host):
-    """Return an IP address for a client connection given the server host.
-
-    If the server is listening on '0.0.0.0' (INADDR_ANY)
-    or '::' (IN6ADDR_ANY), this will return the proper localhost.
-    """
-    if host == '0.0.0.0':
-        # INADDR_ANY, which should respond on localhost.
-        return '127.0.0.1'
-    if host == '::':
-        # IN6ADDR_ANY, which should respond on localhost.
-        return '::1'
-    return host
-
-
-try:
-    # Jython support
-    if sys.platform[:4] == 'java':
-        def getchar():
-            """Get a key press."""
-            # Hopefully this is enough
-            return sys.stdin.read(1)
-    else:
-        # On Windows, msvcrt.getch reads a single char without output.
-        import msvcrt
-
-        def getchar():
-            """Get a key press."""
-            return msvcrt.getch()
-except ImportError:
-    # Unix getchr
-    import tty
-    import termios
-
-    def getchar():
-        """Get a key press."""
-        fd = sys.stdin.fileno()
-        old_settings = termios.tcgetattr(fd)
-        try:
-            tty.setraw(sys.stdin.fileno())
-            ch = sys.stdin.read(1)
-        finally:
-            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
-        return ch
-
-
-# from jaraco.properties
-class NonDataProperty:
-    """Non-data property decorator."""
-
-    def __init__(self, fget):
-        """Initialize a non-data property."""
-        assert fget is not None, 'fget cannot be none'
-        assert callable(fget), 'fget must be callable'
-        self.fget = fget
-
-    def __get__(self, obj, objtype=None):
-        """Return a class property."""
-        if obj is None:
-            return self
-        return self.fget(obj)
-
-
-class WebCase(unittest.TestCase):
-    """Helper web test suite base."""
-
-    HOST = '127.0.0.1'
-    PORT = 8000
-    HTTP_CONN = http_client.HTTPConnection
-    PROTOCOL = 'HTTP/1.1'
-
-    scheme = 'http'
-    url = None
-
-    status = None
-    headers = None
-    body = None
-
-    encoding = 'utf-8'
-
-    time = None
-
-    @property
-    def _Conn(self):
-        """Return HTTPConnection or HTTPSConnection based on self.scheme.
-
-        * from http.client.
-        """
-        cls_name = '{scheme}Connection'.format(scheme=self.scheme.upper())
-        return getattr(http_client, cls_name)
-
-    def get_conn(self, auto_open=False):
-        """Return a connection to our HTTP server."""
-        conn = self._Conn(self.interface(), self.PORT)
-        # Automatically re-connect?
-        conn.auto_open = auto_open
-        conn.connect()
-        return conn
-
-    def set_persistent(self, on=True, auto_open=False):
-        """Make our HTTP_CONN persistent (or not).
-
-        If the 'on' argument is True (the default), then self.HTTP_CONN
-        will be set to an instance of HTTP(S)?Connection
-        to persist across requests.
-        As this class only allows for a single open connection, if
-        self already has an open connection, it will be closed.
-        """
-        try:
-            self.HTTP_CONN.close()
-        except (TypeError, AttributeError):
-            pass
-
-        self.HTTP_CONN = (
-            self.get_conn(auto_open=auto_open)
-            if on
-            else self._Conn
-        )
-
-    @property
-    def persistent(self):  # noqa: D401; irrelevant for properties
-        """Presense of the persistent HTTP connection."""
-        return hasattr(self.HTTP_CONN, '__class__')
-
-    @persistent.setter
-    def persistent(self, on):
-        self.set_persistent(on)
-
-    def interface(self):
-        """Return an IP address for a client connection.
-
-        If the server is listening on '0.0.0.0' (INADDR_ANY)
-        or '::' (IN6ADDR_ANY), this will return the proper localhost.
-        """
-        return interface(self.HOST)
-
-    def getPage(self, url, headers=None, method='GET', body=None,
-                protocol=None, raise_subcls=None):
-        """Open the url with debugging support. Return status, headers, body.
-
-        url should be the identifier passed to the server, typically a
-        server-absolute path and query string (sent between method and
-        protocol), and should only be an absolute URI if proxy support is
-        enabled in the server.
-
-        If the application under test generates absolute URIs, be sure
-        to wrap them first with strip_netloc::
-
-            class MyAppWebCase(WebCase):
-                def getPage(url, *args, **kwargs):
-                    super(MyAppWebCase, self).getPage(
-                        cheroot.test.webtest.strip_netloc(url),
-                        *args, **kwargs
-                    )
-
-        `raise_subcls` must be a tuple with the exceptions classes
-        or a single exception class that are not going to be considered
-        a socket.error regardless that they were are subclass of a
-        socket.error and therefore not considered for a connection retry.
-        """
-        ServerError.on = False
-
-        if isinstance(url, six.text_type):
-            url = url.encode('utf-8')
-        if isinstance(body, six.text_type):
-            body = body.encode('utf-8')
-
-        self.url = url
-        self.time = None
-        start = time.time()
-        result = openURL(url, headers, method, body, self.HOST, self.PORT,
-                         self.HTTP_CONN, protocol or self.PROTOCOL,
-                         raise_subcls)
-        self.time = time.time() - start
-        self.status, self.headers, self.body = result
-
-        # Build a list of request cookies from the previous response cookies.
-        self.cookies = [('Cookie', v) for k, v in self.headers
-                        if k.lower() == 'set-cookie']
-
-        if ServerError.on:
-            raise ServerError()
-        return result
-
-    @NonDataProperty
-    def interactive(self):
-        """Determine whether tests are run in interactive mode.
-
-        Load interactivity setting from environment, where
-        the value can be numeric or a string like true or
-        False or 1 or 0.
-        """
-        env_str = os.environ.get('WEBTEST_INTERACTIVE', 'True')
-        is_interactive = bool(json.loads(env_str.lower()))
-        if is_interactive:
-            warnings.warn(
-                'Interactive test failure interceptor support via '
-                'WEBTEST_INTERACTIVE environment variable is deprecated.',
-                DeprecationWarning
-            )
-        return is_interactive
-
-    console_height = 30
-
-    def _handlewebError(self, msg):
-        print('')
-        print('    ERROR: %s' % msg)
-
-        if not self.interactive:
-            raise self.failureException(msg)
-
-        p = ('    Show: '
-             '[B]ody [H]eaders [S]tatus [U]RL; '
-             '[I]gnore, [R]aise, or sys.e[X]it >> ')
-        sys.stdout.write(p)
-        sys.stdout.flush()
-        while True:
-            i = getchar().upper()
-            if not isinstance(i, type('')):
-                i = i.decode('ascii')
-            if i not in 'BHSUIRX':
-                continue
-            print(i.upper())  # Also prints new line
-            if i == 'B':
-                for x, line in enumerate(self.body.splitlines()):
-                    if (x + 1) % self.console_height == 0:
-                        # The \r and comma should make the next line overwrite
-                        sys.stdout.write('<-- More -->\r')
-                        m = getchar().lower()
-                        # Erase our "More" prompt
-                        sys.stdout.write('            \r')
-                        if m == 'q':
-                            break
-                    print(line)
-            elif i == 'H':
-                pprint.pprint(self.headers)
-            elif i == 'S':
-                print(self.status)
-            elif i == 'U':
-                print(self.url)
-            elif i == 'I':
-                # return without raising the normal exception
-                return
-            elif i == 'R':
-                raise self.failureException(msg)
-            elif i == 'X':
-                sys.exit()
-            sys.stdout.write(p)
-            sys.stdout.flush()
-
-    @property
-    def status_code(self):  # noqa: D401; irrelevant for properties
-        """Integer HTTP status code."""
-        return int(self.status[:3])
-
-    def status_matches(self, expected):
-        """Check whether actual status matches expected."""
-        actual = (
-            self.status_code
-            if isinstance(expected, int) else
-            self.status
-        )
-        return expected == actual
-
-    def assertStatus(self, status, msg=None):
-        """Fail if self.status != status.
-
-        status may be integer code, exact string status, or
-        iterable of allowed possibilities.
-        """
-        if any(map(self.status_matches, always_iterable(status))):
-            return
-
-        tmpl = 'Status {self.status} does not match {status}'
-        msg = msg or tmpl.format(**locals())
-        self._handlewebError(msg)
-
-    def assertHeader(self, key, value=None, msg=None):
-        """Fail if (key, [value]) not in self.headers."""
-        lowkey = key.lower()
-        for k, v in self.headers:
-            if k.lower() == lowkey:
-                if value is None or str(value) == v:
-                    return v
-
-        if msg is None:
-            if value is None:
-                msg = '%r not in headers' % key
-            else:
-                msg = '%r:%r not in headers' % (key, value)
-        self._handlewebError(msg)
-
-    def assertHeaderIn(self, key, values, msg=None):
-        """Fail if header indicated by key doesn't have one of the values."""
-        lowkey = key.lower()
-        for k, v in self.headers:
-            if k.lower() == lowkey:
-                matches = [value for value in values if str(value) == v]
-                if matches:
-                    return matches
-
-        if msg is None:
-            msg = '%(key)r not in %(values)r' % vars()
-        self._handlewebError(msg)
-
-    def assertHeaderItemValue(self, key, value, msg=None):
-        """Fail if the header does not contain the specified value."""
-        actual_value = self.assertHeader(key, msg=msg)
-        header_values = map(str.strip, actual_value.split(','))
-        if value in header_values:
-            return value
-
-        if msg is None:
-            msg = '%r not in %r' % (value, header_values)
-        self._handlewebError(msg)
-
-    def assertNoHeader(self, key, msg=None):
-        """Fail if key in self.headers."""
-        lowkey = key.lower()
-        matches = [k for k, v in self.headers if k.lower() == lowkey]
-        if matches:
-            if msg is None:
-                msg = '%r in headers' % key
-            self._handlewebError(msg)
-
-    def assertNoHeaderItemValue(self, key, value, msg=None):
-        """Fail if the header contains the specified value."""
-        lowkey = key.lower()
-        hdrs = self.headers
-        matches = [k for k, v in hdrs if k.lower() == lowkey and v == value]
-        if matches:
-            if msg is None:
-                msg = '%r:%r in %r' % (key, value, hdrs)
-            self._handlewebError(msg)
-
-    def assertBody(self, value, msg=None):
-        """Fail if value != self.body."""
-        if isinstance(value, six.text_type):
-            value = value.encode(self.encoding)
-        if value != self.body:
-            if msg is None:
-                msg = 'expected body:\n%r\n\nactual body:\n%r' % (
-                    value, self.body)
-            self._handlewebError(msg)
-
-    def assertInBody(self, value, msg=None):
-        """Fail if value not in self.body."""
-        if isinstance(value, six.text_type):
-            value = value.encode(self.encoding)
-        if value not in self.body:
-            if msg is None:
-                msg = '%r not in body: %s' % (value, self.body)
-            self._handlewebError(msg)
-
-    def assertNotInBody(self, value, msg=None):
-        """Fail if value in self.body."""
-        if isinstance(value, six.text_type):
-            value = value.encode(self.encoding)
-        if value in self.body:
-            if msg is None:
-                msg = '%r found in body' % value
-            self._handlewebError(msg)
-
-    def assertMatchesBody(self, pattern, msg=None, flags=0):
-        """Fail if value (a regex pattern) is not in self.body."""
-        if isinstance(pattern, six.text_type):
-            pattern = pattern.encode(self.encoding)
-        if re.search(pattern, self.body, flags) is None:
-            if msg is None:
-                msg = 'No match for %r in body' % pattern
-            self._handlewebError(msg)
-
-
-methods_with_bodies = ('POST', 'PUT', 'PATCH')
-
-
-def cleanHeaders(headers, method, body, host, port):
-    """Return request headers, with required headers added (if missing)."""
-    if headers is None:
-        headers = []
-
-    # Add the required Host request header if not present.
-    # [This specifies the host:port of the server, not the client.]
-    found = False
-    for k, v in headers:
-        if k.lower() == 'host':
-            found = True
-            break
-    if not found:
-        if port == 80:
-            headers.append(('Host', host))
-        else:
-            headers.append(('Host', '%s:%s' % (host, port)))
-
-    if method in methods_with_bodies:
-        # Stick in default type and length headers if not present
-        found = False
-        for k, v in headers:
-            if k.lower() == 'content-type':
-                found = True
-                break
-        if not found:
-            headers.append(
-                ('Content-Type', 'application/x-www-form-urlencoded'))
-            headers.append(('Content-Length', str(len(body or ''))))
-
-    return headers
-
-
-def shb(response):
-    """Return status, headers, body the way we like from a response."""
-    if six.PY3:
-        h = response.getheaders()
-    else:
-        h = []
-        key, value = None, None
-        for line in response.msg.headers:
-            if line:
-                if line[0] in ' \t':
-                    value += line.strip()
-                else:
-                    if key and value:
-                        h.append((key, value))
-                    key, value = line.split(':', 1)
-                    key = key.strip()
-                    value = value.strip()
-        if key and value:
-            h.append((key, value))
-
-    return '%s %s' % (response.status, response.reason), h, response.read()
-
-
-def openURL(url, headers=None, method='GET', body=None,
-            host='127.0.0.1', port=8000, http_conn=http_client.HTTPConnection,
-            protocol='HTTP/1.1', raise_subcls=None):
-    """
-    Open the given HTTP resource and return status, headers, and body.
-
-    `raise_subcls` must be a tuple with the exceptions classes
-    or a single exception class that are not going to be considered
-    a socket.error regardless that they were are subclass of a
-    socket.error and therefore not considered for a connection retry.
-    """
-    headers = cleanHeaders(headers, method, body, host, port)
-
-    # Trying 10 times is simply in case of socket errors.
-    # Normal case--it should run once.
-    for trial in range(10):
-        try:
-            # Allow http_conn to be a class or an instance
-            if hasattr(http_conn, 'host'):
-                conn = http_conn
-            else:
-                conn = http_conn(interface(host), port)
-
-            conn._http_vsn_str = protocol
-            conn._http_vsn = int(''.join([x for x in protocol if x.isdigit()]))
-
-            if six.PY3 and isinstance(url, bytes):
-                url = url.decode()
-            conn.putrequest(method.upper(), url, skip_host=True,
-                            skip_accept_encoding=True)
-
-            for key, value in headers:
-                conn.putheader(key, value.encode('Latin-1'))
-            conn.endheaders()
-
-            if body is not None:
-                conn.send(body)
-
-            # Handle response
-            response = conn.getresponse()
-
-            s, h, b = shb(response)
-
-            if not hasattr(http_conn, 'host'):
-                # We made our own conn instance. Close it.
-                conn.close()
-
-            return s, h, b
-        except socket.error as e:
-            if raise_subcls is not None and isinstance(e, raise_subcls):
-                raise
-            else:
-                time.sleep(0.5)
-                if trial == 9:
-                    raise
-
-
-def strip_netloc(url):
-    """Return absolute-URI path from URL.
-
-    Strip the scheme and host from the URL, returning the
-    server-absolute portion.
-
-    Useful for wrapping an absolute-URI for which only the
-    path is expected (such as in calls to getPage).
-
-    >>> strip_netloc('https://google.com/foo/bar?bing#baz')
-    '/foo/bar?bing'
-
-    >>> strip_netloc('//google.com/foo/bar?bing#baz')
-    '/foo/bar?bing'
-
-    >>> strip_netloc('/foo/bar?bing#baz')
-    '/foo/bar?bing'
-    """
-    parsed = urllib_parse.urlparse(url)
-    scheme, netloc, path, params, query, fragment = parsed
-    stripped = '', '', path, params, query, ''
-    return urllib_parse.urlunparse(stripped)
-
-
-# Add any exceptions which your web framework handles
-# normally (that you don't want server_error to trap).
-ignored_exceptions = []
-
-# You'll want set this to True when you can't guarantee
-# that each response will immediately follow each request;
-# for example, when handling requests via multiple threads.
-ignore_all = False
-
-
-class ServerError(Exception):
-    """Exception for signalling server error."""
-
-    on = False
-
-
-def server_error(exc=None):
-    """Server debug hook.
-
-    Return True if exception handled, False if ignored.
-    You probably want to wrap this, so you can still handle an error using
-    your framework when it's ignored.
-    """
-    if exc is None:
-        exc = sys.exc_info()
-
-    if ignore_all or exc[0] in ignored_exceptions:
-        return False
-    else:
-        ServerError.on = True
-        print('')
-        print(''.join(traceback.format_exception(*exc)))
-        return True
diff --git a/libraries/cheroot/testing.py b/libraries/cheroot/testing.py
deleted file mode 100644
index f01d0aa1..00000000
--- a/libraries/cheroot/testing.py
+++ /dev/null
@@ -1,144 +0,0 @@
-"""Pytest fixtures and other helpers for doing testing by end-users."""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-from contextlib import closing
-import errno
-import socket
-import threading
-import time
-
-import pytest
-from six.moves import http_client
-
-import cheroot.server
-from cheroot.test import webtest
-import cheroot.wsgi
-
-EPHEMERAL_PORT = 0
-NO_INTERFACE = None  # Using this or '' will cause an exception
-ANY_INTERFACE_IPV4 = '0.0.0.0'
-ANY_INTERFACE_IPV6 = '::'
-
-config = {
-    cheroot.wsgi.Server: {
-        'bind_addr': (NO_INTERFACE, EPHEMERAL_PORT),
-        'wsgi_app': None,
-    },
-    cheroot.server.HTTPServer: {
-        'bind_addr': (NO_INTERFACE, EPHEMERAL_PORT),
-        'gateway': cheroot.server.Gateway,
-    },
-}
-
-
-def cheroot_server(server_factory):
-    """Set up and tear down a Cheroot server instance."""
-    conf = config[server_factory].copy()
-    bind_port = conf.pop('bind_addr')[-1]
-
-    for interface in ANY_INTERFACE_IPV6, ANY_INTERFACE_IPV4:
-        try:
-            actual_bind_addr = (interface, bind_port)
-            httpserver = server_factory(  # create it
-                bind_addr=actual_bind_addr,
-                **conf
-            )
-        except OSError:
-            pass
-        else:
-            break
-
-    threading.Thread(target=httpserver.safe_start).start()  # spawn it
-    while not httpserver.ready:  # wait until fully initialized and bound
-        time.sleep(0.1)
-
-    yield httpserver
-
-    httpserver.stop()  # destroy it
-
-
-@pytest.fixture(scope='module')
-def wsgi_server():
-    """Set up and tear down a Cheroot WSGI server instance."""
-    for srv in cheroot_server(cheroot.wsgi.Server):
-        yield srv
-
-
-@pytest.fixture(scope='module')
-def native_server():
-    """Set up and tear down a Cheroot HTTP server instance."""
-    for srv in cheroot_server(cheroot.server.HTTPServer):
-        yield srv
-
-
-class _TestClient:
-    def __init__(self, server):
-        self._interface, self._host, self._port = _get_conn_data(server)
-        self._http_connection = self.get_connection()
-        self.server_instance = server
-
-    def get_connection(self):
-        name = '{interface}:{port}'.format(
-            interface=self._interface,
-            port=self._port,
-        )
-        return http_client.HTTPConnection(name)
-
-    def request(
-        self, uri, method='GET', headers=None, http_conn=None,
-        protocol='HTTP/1.1',
-    ):
-        return webtest.openURL(
-            uri, method=method,
-            headers=headers,
-            host=self._host, port=self._port,
-            http_conn=http_conn or self._http_connection,
-            protocol=protocol,
-        )
-
-    def __getattr__(self, attr_name):
-        def _wrapper(uri, **kwargs):
-            http_method = attr_name.upper()
-            return self.request(uri, method=http_method, **kwargs)
-
-        return _wrapper
-
-
-def _probe_ipv6_sock(interface):
-    # Alternate way is to check IPs on interfaces using glibc, like:
-    # github.com/Gautier/minifail/blob/master/minifail/getifaddrs.py
-    try:
-        with closing(socket.socket(family=socket.AF_INET6)) as sock:
-            sock.bind((interface, 0))
-    except (OSError, socket.error) as sock_err:
-        # In Python 3 socket.error is an alias for OSError
-        # In Python 2 socket.error is a subclass of IOError
-        if sock_err.errno != errno.EADDRNOTAVAIL:
-            raise
-    else:
-        return True
-
-    return False
-
-
-def _get_conn_data(server):
-    if isinstance(server.bind_addr, tuple):
-        host, port = server.bind_addr
-    else:
-        host, port = server.bind_addr, 0
-
-    interface = webtest.interface(host)
-
-    if ':' in interface and not _probe_ipv6_sock(interface):
-        interface = '127.0.0.1'
-        if ':' in host:
-            host = interface
-
-    return interface, host, port
-
-
-def get_server_client(server):
-    """Create and return a test client for the given server."""
-    return _TestClient(server)
diff --git a/libraries/cheroot/workers/__init__.py b/libraries/cheroot/workers/__init__.py
deleted file mode 100644
index 098b8f25..00000000
--- a/libraries/cheroot/workers/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""HTTP workers pool."""
diff --git a/libraries/cheroot/workers/threadpool.py b/libraries/cheroot/workers/threadpool.py
deleted file mode 100644
index ff8fbcee..00000000
--- a/libraries/cheroot/workers/threadpool.py
+++ /dev/null
@@ -1,271 +0,0 @@
-"""A thread-based worker pool."""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-import threading
-import time
-import socket
-
-from six.moves import queue
-
-
-__all__ = ('WorkerThread', 'ThreadPool')
-
-
-class TrueyZero:
-    """Object which equals and does math like the integer 0 but evals True."""
-
-    def __add__(self, other):
-        return other
-
-    def __radd__(self, other):
-        return other
-
-
-trueyzero = TrueyZero()
-
-_SHUTDOWNREQUEST = None
-
-
-class WorkerThread(threading.Thread):
-    """Thread which continuously polls a Queue for Connection objects.
-
-    Due to the timing issues of polling a Queue, a WorkerThread does not
-    check its own 'ready' flag after it has started. To stop the thread,
-    it is necessary to stick a _SHUTDOWNREQUEST object onto the Queue
-    (one for each running WorkerThread).
-    """
-
-    conn = None
-    """The current connection pulled off the Queue, or None."""
-
-    server = None
-    """The HTTP Server which spawned this thread, and which owns the
-    Queue and is placing active connections into it."""
-
-    ready = False
-    """A simple flag for the calling server to know when this thread
-    has begun polling the Queue."""
-
-    def __init__(self, server):
-        """Initialize WorkerThread instance.
-
-        Args:
-            server (cheroot.server.HTTPServer): web server object
-                receiving this request
-        """
-        self.ready = False
-        self.server = server
-
-        self.requests_seen = 0
-        self.bytes_read = 0
-        self.bytes_written = 0
-        self.start_time = None
-        self.work_time = 0
-        self.stats = {
-            'Requests': lambda s: self.requests_seen + (
-                (self.start_time is None) and
-                trueyzero or
-                self.conn.requests_seen
-            ),
-            'Bytes Read': lambda s: self.bytes_read + (
-                (self.start_time is None) and
-                trueyzero or
-                self.conn.rfile.bytes_read
-            ),
-            'Bytes Written': lambda s: self.bytes_written + (
-                (self.start_time is None) and
-                trueyzero or
-                self.conn.wfile.bytes_written
-            ),
-            'Work Time': lambda s: self.work_time + (
-                (self.start_time is None) and
-                trueyzero or
-                time.time() - self.start_time
-            ),
-            'Read Throughput': lambda s: s['Bytes Read'](s) / (
-                s['Work Time'](s) or 1e-6),
-            'Write Throughput': lambda s: s['Bytes Written'](s) / (
-                s['Work Time'](s) or 1e-6),
-        }
-        threading.Thread.__init__(self)
-
-    def run(self):
-        """Process incoming HTTP connections.
-
-        Retrieves incoming connections from thread pool.
-        """
-        self.server.stats['Worker Threads'][self.getName()] = self.stats
-        try:
-            self.ready = True
-            while True:
-                conn = self.server.requests.get()
-                if conn is _SHUTDOWNREQUEST:
-                    return
-
-                self.conn = conn
-                if self.server.stats['Enabled']:
-                    self.start_time = time.time()
-                try:
-                    conn.communicate()
-                finally:
-                    conn.close()
-                    if self.server.stats['Enabled']:
-                        self.requests_seen += self.conn.requests_seen
-                        self.bytes_read += self.conn.rfile.bytes_read
-                        self.bytes_written += self.conn.wfile.bytes_written
-                        self.work_time += time.time() - self.start_time
-                        self.start_time = None
-                    self.conn = None
-        except (KeyboardInterrupt, SystemExit) as ex:
-            self.server.interrupt = ex
-
-
-class ThreadPool:
-    """A Request Queue for an HTTPServer which pools threads.
-
-    ThreadPool objects must provide min, get(), put(obj), start()
-    and stop(timeout) attributes.
-    """
-
-    def __init__(
-            self, server, min=10, max=-1, accepted_queue_size=-1,
-            accepted_queue_timeout=10):
-        """Initialize HTTP requests queue instance.
-
-        Args:
-            server (cheroot.server.HTTPServer): web server object
-                receiving this request
-            min (int): minimum number of worker threads
-            max (int): maximum number of worker threads
-            accepted_queue_size (int): maximum number of active
-                requests in queue
-            accepted_queue_timeout (int): timeout for putting request
-                into queue
-        """
-        self.server = server
-        self.min = min
-        self.max = max
-        self._threads = []
-        self._queue = queue.Queue(maxsize=accepted_queue_size)
-        self._queue_put_timeout = accepted_queue_timeout
-        self.get = self._queue.get
-
-    def start(self):
-        """Start the pool of threads."""
-        for i in range(self.min):
-            self._threads.append(WorkerThread(self.server))
-        for worker in self._threads:
-            worker.setName('CP Server ' + worker.getName())
-            worker.start()
-        for worker in self._threads:
-            while not worker.ready:
-                time.sleep(.1)
-
-    @property
-    def idle(self):  # noqa: D401; irrelevant for properties
-        """Number of worker threads which are idle. Read-only."""
-        return len([t for t in self._threads if t.conn is None])
-
-    def put(self, obj):
-        """Put request into queue.
-
-        Args:
-            obj (cheroot.server.HTTPConnection): HTTP connection
-                waiting to be processed
-        """
-        self._queue.put(obj, block=True, timeout=self._queue_put_timeout)
-        if obj is _SHUTDOWNREQUEST:
-            return
-
-    def grow(self, amount):
-        """Spawn new worker threads (not above self.max)."""
-        if self.max > 0:
-            budget = max(self.max - len(self._threads), 0)
-        else:
-            # self.max <= 0 indicates no maximum
-            budget = float('inf')
-
-        n_new = min(amount, budget)
-
-        workers = [self._spawn_worker() for i in range(n_new)]
-        while not all(worker.ready for worker in workers):
-            time.sleep(.1)
-        self._threads.extend(workers)
-
-    def _spawn_worker(self):
-        worker = WorkerThread(self.server)
-        worker.setName('CP Server ' + worker.getName())
-        worker.start()
-        return worker
-
-    def shrink(self, amount):
-        """Kill off worker threads (not below self.min)."""
-        # Grow/shrink the pool if necessary.
-        # Remove any dead threads from our list
-        for t in self._threads:
-            if not t.isAlive():
-                self._threads.remove(t)
-                amount -= 1
-
-        # calculate the number of threads above the minimum
-        n_extra = max(len(self._threads) - self.min, 0)
-
-        # don't remove more than amount
-        n_to_remove = min(amount, n_extra)
-
-        # put shutdown requests on the queue equal to the number of threads
-        # to remove. As each request is processed by a worker, that worker
-        # will terminate and be culled from the list.
-        for n in range(n_to_remove):
-            self._queue.put(_SHUTDOWNREQUEST)
-
-    def stop(self, timeout=5):
-        """Terminate all worker threads.
-
-        Args:
-            timeout (int): time to wait for threads to stop gracefully
-        """
-        # Must shut down threads here so the code that calls
-        # this method can know when all threads are stopped.
-        for worker in self._threads:
-            self._queue.put(_SHUTDOWNREQUEST)
-
-        # Don't join currentThread (when stop is called inside a request).
-        current = threading.currentThread()
-        if timeout is not None and timeout >= 0:
-            endtime = time.time() + timeout
-        while self._threads:
-            worker = self._threads.pop()
-            if worker is not current and worker.isAlive():
-                try:
-                    if timeout is None or timeout < 0:
-                        worker.join()
-                    else:
-                        remaining_time = endtime - time.time()
-                        if remaining_time > 0:
-                            worker.join(remaining_time)
-                        if worker.isAlive():
-                            # We exhausted the timeout.
-                            # Forcibly shut down the socket.
-                            c = worker.conn
-                            if c and not c.rfile.closed:
-                                try:
-                                    c.socket.shutdown(socket.SHUT_RD)
-                                except TypeError:
-                                    # pyOpenSSL sockets don't take an arg
-                                    c.socket.shutdown()
-                            worker.join()
-                except (AssertionError,
-                        # Ignore repeated Ctrl-C.
-                        # See
-                        # https://github.com/cherrypy/cherrypy/issues/691.
-                        KeyboardInterrupt):
-                    pass
-
-    @property
-    def qsize(self):
-        """Return the queue size."""
-        return self._queue.qsize()
diff --git a/libraries/cheroot/wsgi.py b/libraries/cheroot/wsgi.py
deleted file mode 100644
index a04c9438..00000000
--- a/libraries/cheroot/wsgi.py
+++ /dev/null
@@ -1,423 +0,0 @@
-"""This class holds Cheroot WSGI server implementation.
-
-Simplest example on how to use this server::
-
-    from cheroot import wsgi
-
-    def my_crazy_app(environ, start_response):
-        status = '200 OK'
-        response_headers = [('Content-type','text/plain')]
-        start_response(status, response_headers)
-        return [b'Hello world!']
-
-    addr = '0.0.0.0', 8070
-    server = wsgi.Server(addr, my_crazy_app)
-    server.start()
-
-The Cheroot WSGI server can serve as many WSGI applications
-as you want in one instance by using a PathInfoDispatcher::
-
-    path_map = {
-        '/': my_crazy_app,
-        '/blog': my_blog_app,
-    }
-    d = wsgi.PathInfoDispatcher(path_map)
-    server = wsgi.Server(addr, d)
-"""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import sys
-
-import six
-from six.moves import filter
-
-from . import server
-from .workers import threadpool
-from ._compat import ntob, bton
-
-
-class Server(server.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,
-        accepted_queue_size=-1, accepted_queue_timeout=10,
-        peercreds_enabled=False, peercreds_resolve_enabled=False,
-    ):
-        """Initialize WSGI Server instance.
-
-        Args:
-            bind_addr (tuple): network interface to listen to
-            wsgi_app (callable): WSGI application callable
-            numthreads (int): number of threads for WSGI thread pool
-            server_name (str): web server name to be advertised via
-                Server HTTP header
-            max (int): maximum number of worker threads
-            request_queue_size (int): the 'backlog' arg to
-                socket.listen(); max queued connections
-            timeout (int): the timeout in seconds for accepted connections
-            shutdown_timeout (int): the total time, in seconds, to
-                wait for worker threads to cleanly exit
-            accepted_queue_size (int): maximum number of active
-                requests in queue
-            accepted_queue_timeout (int): timeout for putting request
-                into queue
-        """
-        super(Server, self).__init__(
-            bind_addr,
-            gateway=wsgi_gateways[self.wsgi_version],
-            server_name=server_name,
-            peercreds_enabled=peercreds_enabled,
-            peercreds_resolve_enabled=peercreds_resolve_enabled,
-        )
-        self.wsgi_app = wsgi_app
-        self.request_queue_size = request_queue_size
-        self.timeout = timeout
-        self.shutdown_timeout = shutdown_timeout
-        self.requests = threadpool.ThreadPool(
-            self, min=numthreads or 1, max=max,
-            accepted_queue_size=accepted_queue_size,
-            accepted_queue_timeout=accepted_queue_timeout)
-
-    @property
-    def numthreads(self):
-        """Set minimum number of threads."""
-        return self.requests.min
-
-    @numthreads.setter
-    def numthreads(self, value):
-        self.requests.min = value
-
-
-class Gateway(server.Gateway):
-    """A base class to interface HTTPServer with WSGI."""
-
-    def __init__(self, req):
-        """Initialize WSGI Gateway instance with request.
-
-        Args:
-            req (HTTPRequest): current HTTP request
-        """
-        super(Gateway, self).__init__(req)
-        self.started_response = False
-        self.env = self.get_environ()
-        self.remaining_bytes_out = None
-
-    @classmethod
-    def gateway_map(cls):
-        """Create a mapping of gateways and their versions.
-
-        Returns:
-            dict[tuple[int,int],class]: map of gateway version and
-                corresponding class
-
-        """
-        return dict(
-            (gw.version, gw)
-            for gw in cls.__subclasses__()
-        )
-
-    def get_environ(self):
-        """Return a new environ dict targeting the given wsgi.version."""
-        raise NotImplementedError
-
-    def respond(self):
-        """Process the current request.
-
-        From :pep:`333`:
-
-            The start_response callable must not actually transmit
-            the response headers. Instead, it must store them for the
-            server or gateway to transmit only after the first
-            iteration of the application return value that yields
-            a NON-EMPTY string, or upon the application's first
-            invocation of the write() callable.
-        """
-        response = self.req.server.wsgi_app(self.env, self.start_response)
-        try:
-            for chunk in filter(None, response):
-                if not isinstance(chunk, six.binary_type):
-                    raise ValueError('WSGI Applications must yield bytes')
-                self.write(chunk)
-        finally:
-            # Send headers if not already sent
-            self.req.ensure_headers_sent()
-            if hasattr(response, 'close'):
-                response.close()
-
-    def start_response(self, status, headers, exc_info=None):
-        """WSGI callable to begin the HTTP response."""
-        # "The application may call start_response more than once,
-        # if and only if the exc_info argument is provided."
-        if self.started_response and not exc_info:
-            raise AssertionError('WSGI start_response called a second '
-                                 'time with no exc_info.')
-        self.started_response = True
-
-        # "if exc_info is provided, and the HTTP headers have already been
-        # sent, start_response must raise an error, and should raise the
-        # exc_info tuple."
-        if self.req.sent_headers:
-            try:
-                six.reraise(*exc_info)
-            finally:
-                exc_info = None
-
-        self.req.status = self._encode_status(status)
-
-        for k, v in headers:
-            if not isinstance(k, str):
-                raise TypeError(
-                    'WSGI response header key %r is not of type str.' % k)
-            if not isinstance(v, str):
-                raise TypeError(
-                    'WSGI response header value %r is not of type str.' % v)
-            if k.lower() == 'content-length':
-                self.remaining_bytes_out = int(v)
-            out_header = ntob(k), ntob(v)
-            self.req.outheaders.append(out_header)
-
-        return self.write
-
-    @staticmethod
-    def _encode_status(status):
-        """Cast status to bytes representation of current Python version.
-
-        According to :pep:`3333`, when using Python 3, the response status
-        and headers must be bytes masquerading as unicode; that is, they
-        must be of type "str" but are restricted to code points in the
-        "latin-1" set.
-        """
-        if six.PY2:
-            return status
-        if not isinstance(status, str):
-            raise TypeError('WSGI response status is not of type str.')
-        return status.encode('ISO-8859-1')
-
-    def write(self, chunk):
-        """WSGI callable to write unbuffered data to the client.
-
-        This method is also used internally by start_response (to write
-        data from the iterable returned by the WSGI application).
-        """
-        if not self.started_response:
-            raise AssertionError('WSGI write called before start_response.')
-
-        chunklen = len(chunk)
-        rbo = self.remaining_bytes_out
-        if rbo is not None and chunklen > rbo:
-            if not self.req.sent_headers:
-                # Whew. We can send a 500 to the client.
-                self.req.simple_response(
-                    '500 Internal Server Error',
-                    'The requested resource returned more bytes than the '
-                    'declared Content-Length.')
-            else:
-                # Dang. We have probably already sent data. Truncate the chunk
-                # to fit (so the client doesn't hang) and raise an error later.
-                chunk = chunk[:rbo]
-
-        self.req.ensure_headers_sent()
-
-        self.req.write(chunk)
-
-        if rbo is not None:
-            rbo -= chunklen
-            if rbo < 0:
-                raise ValueError(
-                    'Response body exceeds the declared Content-Length.')
-
-
-class Gateway_10(Gateway):
-    """A Gateway class to interface HTTPServer with WSGI 1.0.x."""
-
-    version = 1, 0
-
-    def get_environ(self):
-        """Return a new environ dict targeting the given wsgi.version."""
-        req = self.req
-        req_conn = req.conn
-        env = {
-            # set a non-standard environ entry so the WSGI app can know what
-            # the *real* server protocol is (and what features to support).
-            # See http://www.faqs.org/rfcs/rfc2145.html.
-            'ACTUAL_SERVER_PROTOCOL': req.server.protocol,
-            'PATH_INFO': bton(req.path),
-            'QUERY_STRING': bton(req.qs),
-            'REMOTE_ADDR': req_conn.remote_addr or '',
-            'REMOTE_PORT': str(req_conn.remote_port or ''),
-            'REQUEST_METHOD': bton(req.method),
-            'REQUEST_URI': bton(req.uri),
-            'SCRIPT_NAME': '',
-            'SERVER_NAME': req.server.server_name,
-            # Bah. "SERVER_PROTOCOL" is actually the REQUEST protocol.
-            'SERVER_PROTOCOL': bton(req.request_protocol),
-            'SERVER_SOFTWARE': req.server.software,
-            'wsgi.errors': sys.stderr,
-            'wsgi.input': req.rfile,
-            'wsgi.input_terminated': bool(req.chunked_read),
-            'wsgi.multiprocess': False,
-            'wsgi.multithread': True,
-            'wsgi.run_once': False,
-            'wsgi.url_scheme': bton(req.scheme),
-            'wsgi.version': self.version,
-        }
-
-        if isinstance(req.server.bind_addr, six.string_types):
-            # 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'] = ''
-            try:
-                env['X_REMOTE_PID'] = str(req_conn.peer_pid)
-                env['X_REMOTE_UID'] = str(req_conn.peer_uid)
-                env['X_REMOTE_GID'] = str(req_conn.peer_gid)
-
-                env['X_REMOTE_USER'] = str(req_conn.peer_user)
-                env['X_REMOTE_GROUP'] = str(req_conn.peer_group)
-
-                env['REMOTE_USER'] = env['X_REMOTE_USER']
-            except RuntimeError:
-                """Unable to retrieve peer creds data.
-
-                Unsupported by current kernel or socket error happened, or
-                unsupported socket type, or disabled.
-                """
-        else:
-            env['SERVER_PORT'] = str(req.server.bind_addr[1])
-
-        # Request headers
-        env.update(
-            ('HTTP_' + bton(k).upper().replace('-', '_'), bton(v))
-            for k, v in req.inheaders.items()
-        )
-
-        # CONTENT_TYPE/CONTENT_LENGTH
-        ct = env.pop('HTTP_CONTENT_TYPE', None)
-        if ct is not None:
-            env['CONTENT_TYPE'] = ct
-        cl = env.pop('HTTP_CONTENT_LENGTH', None)
-        if cl is not None:
-            env['CONTENT_LENGTH'] = cl
-
-        if req.conn.ssl_env:
-            env.update(req.conn.ssl_env)
-
-        return env
-
-
-class Gateway_u0(Gateway_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.
-    """
-
-    version = 'u', 0
-
-    def get_environ(self):
-        """Return a new environ dict targeting the given wsgi.version."""
-        req = self.req
-        env_10 = super(Gateway_u0, self).get_environ()
-        env = dict(map(self._decode_key, env_10.items()))
-
-        # Request-URI
-        enc = env.setdefault(six.u('wsgi.url_encoding'), six.u('utf-8'))
-        try:
-            env['PATH_INFO'] = req.path.decode(enc)
-            env['QUERY_STRING'] = req.qs.decode(enc)
-        except UnicodeDecodeError:
-            # Fall back to latin 1 so apps can transcode if needed.
-            env['wsgi.url_encoding'] = 'ISO-8859-1'
-            env['PATH_INFO'] = env_10['PATH_INFO']
-            env['QUERY_STRING'] = env_10['QUERY_STRING']
-
-        env.update(map(self._decode_value, env.items()))
-
-        return env
-
-    @staticmethod
-    def _decode_key(item):
-        k, v = item
-        if six.PY2:
-            k = k.decode('ISO-8859-1')
-        return k, v
-
-    @staticmethod
-    def _decode_value(item):
-        k, v = item
-        skip_keys = 'REQUEST_URI', 'wsgi.input'
-        if six.PY3 or not isinstance(v, bytes) or k in skip_keys:
-            return k, v
-        return k, v.decode('ISO-8859-1')
-
-
-wsgi_gateways = Gateway.gateway_map()
-
-
-class PathInfoDispatcher:
-    """A WSGI dispatcher for dispatch based on the PATH_INFO."""
-
-    def __init__(self, apps):
-        """Initialize path info WSGI app dispatcher.
-
-        Args:
-            apps (dict[str,object]|list[tuple[str,object]]): URI prefix
-                and WSGI app pairs
-        """
-        try:
-            apps = list(apps.items())
-        except AttributeError:
-            pass
-
-        # Sort the apps by len(path), descending
-        def by_path_len(app):
-            return len(app[0])
-        apps.sort(key=by_path_len, reverse=True)
-
-        # The path_prefix strings must start, but not end, with a slash.
-        # Use "" instead of "/".
-        self.apps = [(p.rstrip('/'), a) for p, a in apps]
-
-    def __call__(self, environ, start_response):
-        """Process incoming WSGI request.
-
-        Ref: :pep:`3333`
-
-        Args:
-            environ (Mapping): a dict containing WSGI environment variables
-            start_response (callable): function, which sets response
-                status and headers
-
-        Returns:
-            list[bytes]: iterable containing bytes to be returned in
-                HTTP response body
-
-        """
-        path = environ['PATH_INFO'] or '/'
-        for p, app in self.apps:
-            # The apps list should be sorted by length, descending.
-            if path.startswith(p + '/') or path == p:
-                environ = environ.copy()
-                environ['SCRIPT_NAME'] = environ['SCRIPT_NAME'] + p
-                environ['PATH_INFO'] = path[len(p):]
-                return app(environ, start_response)
-
-        start_response('404 Not Found', [('Content-Type', 'text/plain'),
-                                         ('Content-Length', '0')])
-        return ['']
-
-
-# compatibility aliases
-globals().update(
-    WSGIServer=Server,
-    WSGIGateway=Gateway,
-    WSGIGateway_u0=Gateway_u0,
-    WSGIGateway_10=Gateway_10,
-    WSGIPathInfoDispatcher=PathInfoDispatcher,
-)
diff --git a/libraries/cherrypy/__init__.py b/libraries/cherrypy/__init__.py
deleted file mode 100644
index c5925980..00000000
--- a/libraries/cherrypy/__init__.py
+++ /dev/null
@@ -1,362 +0,0 @@
-"""CherryPy is a pythonic, object-oriented HTTP framework.
-
-CherryPy consists of not one, but four separate API layers.
-
-The APPLICATION LAYER is the simplest. CherryPy applications are written as
-a tree of classes and methods, where each branch in the tree corresponds to
-a branch in the URL path. Each method is a 'page handler', which receives
-GET and POST params as keyword arguments, and returns or yields the (HTML)
-body of the response. The special method name 'index' is used for paths
-that end in a slash, and the special method name 'default' is used to
-handle multiple paths via a single handler. This layer also includes:
-
- * the 'exposed' attribute (and cherrypy.expose)
- * cherrypy.quickstart()
- * _cp_config attributes
- * cherrypy.tools (including cherrypy.session)
- * cherrypy.url()
-
-The ENVIRONMENT LAYER is used by developers at all levels. It provides
-information about the current request and response, plus the application
-and server environment, via a (default) set of top-level objects:
-
- * cherrypy.request
- * cherrypy.response
- * cherrypy.engine
- * cherrypy.server
- * cherrypy.tree
- * cherrypy.config
- * cherrypy.thread_data
- * cherrypy.log
- * cherrypy.HTTPError, NotFound, and HTTPRedirect
- * cherrypy.lib
-
-The EXTENSION LAYER allows advanced users to construct and share their own
-plugins. It consists of:
-
- * Hook API
- * Tool API
- * Toolbox API
- * Dispatch API
- * Config Namespace API
-
-Finally, there is the CORE LAYER, which uses the core API's to construct
-the default components which are available at higher layers. You can think
-of the default components as the 'reference implementation' for CherryPy.
-Megaframeworks (and advanced users) may replace the default components
-with customized or extended components. The core API's are:
-
- * Application API
- * Engine API
- * Request API
- * Server API
- * WSGI API
-
-These API's are described in the `CherryPy specification
-<https://github.com/cherrypy/cherrypy/wiki/CherryPySpec>`_.
-"""
-
-from threading import local as _local
-
-from ._cperror import (
-    HTTPError, HTTPRedirect, InternalRedirect,
-    NotFound, CherryPyException,
-)
-
-from . import _cpdispatch as dispatch
-
-from ._cptools import default_toolbox as tools, Tool
-from ._helper import expose, popargs, url
-
-from . import _cprequest, _cpserver, _cptree, _cplogging, _cpconfig
-
-import cherrypy.lib.httputil as _httputil
-
-from ._cptree import Application
-from . import _cpwsgi as wsgi
-
-from . import process
-try:
-    from .process import win32
-    engine = win32.Win32Bus()
-    engine.console_control_handler = win32.ConsoleCtrlHandler(engine)
-    del win32
-except ImportError:
-    engine = process.bus
-
-from . import _cpchecker
-
-__all__ = (
-    'HTTPError', 'HTTPRedirect', 'InternalRedirect',
-    'NotFound', 'CherryPyException',
-    'dispatch', 'tools', 'Tool', 'Application',
-    'wsgi', 'process', 'tree', 'engine',
-    'quickstart', 'serving', 'request', 'response', 'thread_data',
-    'log', 'expose', 'popargs', 'url', 'config',
-)
-
-
-__import__('cherrypy._cptools')
-__import__('cherrypy._cprequest')
-
-
-tree = _cptree.Tree()
-
-
-__version__ = '17.4.0'
-
-
-engine.listeners['before_request'] = set()
-engine.listeners['after_request'] = set()
-
-
-engine.autoreload = process.plugins.Autoreloader(engine)
-engine.autoreload.subscribe()
-
-engine.thread_manager = process.plugins.ThreadManager(engine)
-engine.thread_manager.subscribe()
-
-engine.signal_handler = process.plugins.SignalHandler(engine)
-
-
-class _HandleSignalsPlugin(object):
-    """Handle signals from other processes.
-
-    Based on the configured platform handlers above.
-    """
-
-    def __init__(self, bus):
-        self.bus = bus
-
-    def subscribe(self):
-        """Add the handlers based on the platform."""
-        if hasattr(self.bus, 'signal_handler'):
-            self.bus.signal_handler.subscribe()
-        if hasattr(self.bus, 'console_control_handler'):
-            self.bus.console_control_handler.subscribe()
-
-
-engine.signals = _HandleSignalsPlugin(engine)
-
-
-server = _cpserver.Server()
-server.subscribe()
-
-
-def quickstart(root=None, script_name='', config=None):
-    """Mount the given root, start the builtin server (and engine), then block.
-
-    root: an instance of a "controller class" (a collection of page handler
-        methods) which represents the root of the application.
-    script_name: a string containing the "mount point" of the application.
-        This should start with a slash, and be the path portion of the URL
-        at which to mount the given root. For example, if root.index() will
-        handle requests to "http://www.example.com:8080/dept/app1/", then
-        the script_name argument would be "/dept/app1".
-
-        It MUST NOT end in a slash. If the script_name refers to the root
-        of the URI, it MUST be an empty string (not "/").
-    config: a file or dict containing application config. If this contains
-        a [global] section, those entries will be used in the global
-        (site-wide) config.
-    """
-    if config:
-        _global_conf_alias.update(config)
-
-    tree.mount(root, script_name, config)
-
-    engine.signals.subscribe()
-    engine.start()
-    engine.block()
-
-
-class _Serving(_local):
-    """An interface for registering request and response objects.
-
-    Rather than have a separate "thread local" object for the request and
-    the response, this class works as a single threadlocal container for
-    both objects (and any others which developers wish to define). In this
-    way, we can easily dump those objects when we stop/start a new HTTP
-    conversation, yet still refer to them as module-level globals in a
-    thread-safe way.
-    """
-
-    request = _cprequest.Request(_httputil.Host('127.0.0.1', 80),
-                                 _httputil.Host('127.0.0.1', 1111))
-    """
-    The request object for the current thread. In the main thread,
-    and any threads which are not receiving HTTP requests, this is None."""
-
-    response = _cprequest.Response()
-    """
-    The response object for the current thread. In the main thread,
-    and any threads which are not receiving HTTP requests, this is None."""
-
-    def load(self, request, response):
-        self.request = request
-        self.response = response
-
-    def clear(self):
-        """Remove all attributes of self."""
-        self.__dict__.clear()
-
-
-serving = _Serving()
-
-
-class _ThreadLocalProxy(object):
-
-    __slots__ = ['__attrname__', '__dict__']
-
-    def __init__(self, attrname):
-        self.__attrname__ = attrname
-
-    def __getattr__(self, name):
-        child = getattr(serving, self.__attrname__)
-        return getattr(child, name)
-
-    def __setattr__(self, name, value):
-        if name in ('__attrname__', ):
-            object.__setattr__(self, name, value)
-        else:
-            child = getattr(serving, self.__attrname__)
-            setattr(child, name, value)
-
-    def __delattr__(self, name):
-        child = getattr(serving, self.__attrname__)
-        delattr(child, name)
-
-    @property
-    def __dict__(self):
-        child = getattr(serving, self.__attrname__)
-        d = child.__class__.__dict__.copy()
-        d.update(child.__dict__)
-        return d
-
-    def __getitem__(self, key):
-        child = getattr(serving, self.__attrname__)
-        return child[key]
-
-    def __setitem__(self, key, value):
-        child = getattr(serving, self.__attrname__)
-        child[key] = value
-
-    def __delitem__(self, key):
-        child = getattr(serving, self.__attrname__)
-        del child[key]
-
-    def __contains__(self, key):
-        child = getattr(serving, self.__attrname__)
-        return key in child
-
-    def __len__(self):
-        child = getattr(serving, self.__attrname__)
-        return len(child)
-
-    def __nonzero__(self):
-        child = getattr(serving, self.__attrname__)
-        return bool(child)
-    # Python 3
-    __bool__ = __nonzero__
-
-
-# Create request and response object (the same objects will be used
-#   throughout the entire life of the webserver, but will redirect
-#   to the "serving" object)
-request = _ThreadLocalProxy('request')
-response = _ThreadLocalProxy('response')
-
-# Create thread_data object as a thread-specific all-purpose storage
-
-
-class _ThreadData(_local):
-    """A container for thread-specific data."""
-
-
-thread_data = _ThreadData()
-
-
-# Monkeypatch pydoc to allow help() to go through the threadlocal proxy.
-# Jan 2007: no Googleable examples of anyone else replacing pydoc.resolve.
-# The only other way would be to change what is returned from type(request)
-# and that's not possible in pure Python (you'd have to fake ob_type).
-def _cherrypy_pydoc_resolve(thing, forceload=0):
-    """Given an object or a path to an object, get the object and its name."""
-    if isinstance(thing, _ThreadLocalProxy):
-        thing = getattr(serving, thing.__attrname__)
-    return _pydoc._builtin_resolve(thing, forceload)
-
-
-try:
-    import pydoc as _pydoc
-    _pydoc._builtin_resolve = _pydoc.resolve
-    _pydoc.resolve = _cherrypy_pydoc_resolve
-except ImportError:
-    pass
-
-
-class _GlobalLogManager(_cplogging.LogManager):
-    """A site-wide LogManager; routes to app.log or global log as appropriate.
-
-    This :class:`LogManager<cherrypy._cplogging.LogManager>` implements
-    cherrypy.log() and cherrypy.log.access(). If either
-    function is called during a request, the message will be sent to the
-    logger for the current Application. If they are called outside of a
-    request, the message will be sent to the site-wide logger.
-    """
-
-    def __call__(self, *args, **kwargs):
-        """Log the given message to the app.log or global log.
-
-        Log the given message to the app.log or global
-        log as appropriate.
-        """
-        # Do NOT use try/except here. See
-        # https://github.com/cherrypy/cherrypy/issues/945
-        if hasattr(request, 'app') and hasattr(request.app, 'log'):
-            log = request.app.log
-        else:
-            log = self
-        return log.error(*args, **kwargs)
-
-    def access(self):
-        """Log an access message to the app.log or global log.
-
-        Log the given message to the app.log or global
-        log as appropriate.
-        """
-        try:
-            return request.app.log.access()
-        except AttributeError:
-            return _cplogging.LogManager.access(self)
-
-
-log = _GlobalLogManager()
-# Set a default screen handler on the global log.
-log.screen = True
-log.error_file = ''
-# Using an access file makes CP about 10% slower. Leave off by default.
-log.access_file = ''
-
-
-@engine.subscribe('log')
-def _buslog(msg, level):
-    log.error(msg, 'ENGINE', severity=level)
-
-
-# Use _global_conf_alias so quickstart can use 'config' as an arg
-# without shadowing cherrypy.config.
-config = _global_conf_alias = _cpconfig.Config()
-config.defaults = {
-    'tools.log_tracebacks.on': True,
-    'tools.log_headers.on': True,
-    'tools.trailing_slash.on': True,
-    'tools.encode.on': True
-}
-config.namespaces['log'] = lambda k, v: setattr(log, k, v)
-config.namespaces['checker'] = lambda k, v: setattr(checker, k, v)
-# Must reset to get our defaults applied.
-config.reset()
-
-checker = _cpchecker.Checker()
-engine.subscribe('start', checker)
diff --git a/libraries/cherrypy/__main__.py b/libraries/cherrypy/__main__.py
deleted file mode 100644
index 6674f7cb..00000000
--- a/libraries/cherrypy/__main__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""CherryPy'd cherryd daemon runner."""
-from cherrypy.daemon import run
-
-
-__name__ == '__main__' and run()
diff --git a/libraries/cherrypy/_cpchecker.py b/libraries/cherrypy/_cpchecker.py
deleted file mode 100644
index 39b7c972..00000000
--- a/libraries/cherrypy/_cpchecker.py
+++ /dev/null
@@ -1,325 +0,0 @@
-"""Checker for CherryPy sites and mounted apps."""
-import os
-import warnings
-
-import six
-from six.moves import builtins
-
-import cherrypy
-
-
-class Checker(object):
-    """A checker for CherryPy sites and their mounted applications.
-
-    When this object is called at engine startup, it executes each
-    of its own methods whose names start with ``check_``. If you wish
-    to disable selected checks, simply add a line in your global
-    config which sets the appropriate method to False::
-
-        [global]
-        checker.check_skipped_app_config = False
-
-    You may also dynamically add or replace ``check_*`` methods in this way.
-    """
-
-    on = True
-    """If True (the default), run all checks; if False, turn off all checks."""
-
-    def __init__(self):
-        """Initialize Checker instance."""
-        self._populate_known_types()
-
-    def __call__(self):
-        """Run all check_* methods."""
-        if self.on:
-            oldformatwarning = warnings.formatwarning
-            warnings.formatwarning = self.formatwarning
-            try:
-                for name in dir(self):
-                    if name.startswith('check_'):
-                        method = getattr(self, name)
-                        if method and hasattr(method, '__call__'):
-                            method()
-            finally:
-                warnings.formatwarning = oldformatwarning
-
-    def formatwarning(self, message, category, filename, lineno, line=None):
-        """Format a warning."""
-        return 'CherryPy Checker:\n%s\n\n' % message
-
-    # This value should be set inside _cpconfig.
-    global_config_contained_paths = False
-
-    def check_app_config_entries_dont_start_with_script_name(self):
-        """Check for App config with sections that repeat script_name."""
-        for sn, app in cherrypy.tree.apps.items():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            if not app.config:
-                continue
-            if sn == '':
-                continue
-            sn_atoms = sn.strip('/').split('/')
-            for key in app.config.keys():
-                key_atoms = key.strip('/').split('/')
-                if key_atoms[:len(sn_atoms)] == sn_atoms:
-                    warnings.warn(
-                        'The application mounted at %r has config '
-                        'entries that start with its script name: %r' % (sn,
-                                                                         key))
-
-    def check_site_config_entries_in_app_config(self):
-        """Check for mounted Applications that have site-scoped config."""
-        for sn, app in six.iteritems(cherrypy.tree.apps):
-            if not isinstance(app, cherrypy.Application):
-                continue
-
-            msg = []
-            for section, entries in six.iteritems(app.config):
-                if section.startswith('/'):
-                    for key, value in six.iteritems(entries):
-                        for n in ('engine.', 'server.', 'tree.', 'checker.'):
-                            if key.startswith(n):
-                                msg.append('[%s] %s = %s' %
-                                           (section, key, value))
-            if msg:
-                msg.insert(0,
-                           'The application mounted at %r contains the '
-                           'following config entries, which are only allowed '
-                           'in site-wide config. Move them to a [global] '
-                           'section and pass them to cherrypy.config.update() '
-                           'instead of tree.mount().' % sn)
-                warnings.warn(os.linesep.join(msg))
-
-    def check_skipped_app_config(self):
-        """Check for mounted Applications that have no config."""
-        for sn, app in cherrypy.tree.apps.items():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            if not app.config:
-                msg = 'The Application mounted at %r has an empty config.' % sn
-                if self.global_config_contained_paths:
-                    msg += (' It looks like the config you passed to '
-                            'cherrypy.config.update() contains application-'
-                            'specific sections. You must explicitly pass '
-                            'application config via '
-                            'cherrypy.tree.mount(..., config=app_config)')
-                warnings.warn(msg)
-                return
-
-    def check_app_config_brackets(self):
-        """Check for App config with extraneous brackets in section names."""
-        for sn, app in cherrypy.tree.apps.items():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            if not app.config:
-                continue
-            for key in app.config.keys():
-                if key.startswith('[') or key.endswith(']'):
-                    warnings.warn(
-                        'The application mounted at %r has config '
-                        'section names with extraneous brackets: %r. '
-                        'Config *files* need brackets; config *dicts* '
-                        '(e.g. passed to tree.mount) do not.' % (sn, key))
-
-    def check_static_paths(self):
-        """Check Application config for incorrect static paths."""
-        # Use the dummy Request object in the main thread.
-        request = cherrypy.request
-        for sn, app in cherrypy.tree.apps.items():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            request.app = app
-            for section in app.config:
-                # get_resource will populate request.config
-                request.get_resource(section + '/dummy.html')
-                conf = request.config.get
-
-                if conf('tools.staticdir.on', False):
-                    msg = ''
-                    root = conf('tools.staticdir.root')
-                    dir = conf('tools.staticdir.dir')
-                    if dir is None:
-                        msg = 'tools.staticdir.dir is not set.'
-                    else:
-                        fulldir = ''
-                        if os.path.isabs(dir):
-                            fulldir = dir
-                            if root:
-                                msg = ('dir is an absolute path, even '
-                                       'though a root is provided.')
-                                testdir = os.path.join(root, dir[1:])
-                                if os.path.exists(testdir):
-                                    msg += (
-                                        '\nIf you meant to serve the '
-                                        'filesystem folder at %r, remove the '
-                                        'leading slash from dir.' % (testdir,))
-                        else:
-                            if not root:
-                                msg = (
-                                    'dir is a relative path and '
-                                    'no root provided.')
-                            else:
-                                fulldir = os.path.join(root, dir)
-                                if not os.path.isabs(fulldir):
-                                    msg = ('%r is not an absolute path.' % (
-                                        fulldir,))
-
-                        if fulldir and not os.path.exists(fulldir):
-                            if msg:
-                                msg += '\n'
-                            msg += ('%r (root + dir) is not an existing '
-                                    'filesystem path.' % fulldir)
-
-                    if msg:
-                        warnings.warn('%s\nsection: [%s]\nroot: %r\ndir: %r'
-                                      % (msg, section, root, dir))
-
-    # -------------------------- Compatibility -------------------------- #
-    obsolete = {
-        'server.default_content_type': 'tools.response_headers.headers',
-        'log_access_file': 'log.access_file',
-        'log_config_options': None,
-        'log_file': 'log.error_file',
-        'log_file_not_found': None,
-        'log_request_headers': 'tools.log_headers.on',
-        'log_to_screen': 'log.screen',
-        'show_tracebacks': 'request.show_tracebacks',
-        'throw_errors': 'request.throw_errors',
-        'profiler.on': ('cherrypy.tree.mount(profiler.make_app('
-                        'cherrypy.Application(Root())))'),
-    }
-
-    deprecated = {}
-
-    def _compat(self, config):
-        """Process config and warn on each obsolete or deprecated entry."""
-        for section, conf in config.items():
-            if isinstance(conf, dict):
-                for k in conf:
-                    if k in self.obsolete:
-                        warnings.warn('%r is obsolete. Use %r instead.\n'
-                                      'section: [%s]' %
-                                      (k, self.obsolete[k], section))
-                    elif k in self.deprecated:
-                        warnings.warn('%r is deprecated. Use %r instead.\n'
-                                      'section: [%s]' %
-                                      (k, self.deprecated[k], section))
-            else:
-                if section in self.obsolete:
-                    warnings.warn('%r is obsolete. Use %r instead.'
-                                  % (section, self.obsolete[section]))
-                elif section in self.deprecated:
-                    warnings.warn('%r is deprecated. Use %r instead.'
-                                  % (section, self.deprecated[section]))
-
-    def check_compatibility(self):
-        """Process config and warn on each obsolete or deprecated entry."""
-        self._compat(cherrypy.config)
-        for sn, app in cherrypy.tree.apps.items():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            self._compat(app.config)
-
-    # ------------------------ Known Namespaces ------------------------ #
-    extra_config_namespaces = []
-
-    def _known_ns(self, app):
-        ns = ['wsgi']
-        ns.extend(app.toolboxes)
-        ns.extend(app.namespaces)
-        ns.extend(app.request_class.namespaces)
-        ns.extend(cherrypy.config.namespaces)
-        ns += self.extra_config_namespaces
-
-        for section, conf in app.config.items():
-            is_path_section = section.startswith('/')
-            if is_path_section and isinstance(conf, dict):
-                for k in conf:
-                    atoms = k.split('.')
-                    if len(atoms) > 1:
-                        if atoms[0] not in ns:
-                            # Spit out a special warning if a known
-                            # namespace is preceded by "cherrypy."
-                            if atoms[0] == 'cherrypy' and atoms[1] in ns:
-                                msg = (
-                                    'The config entry %r is invalid; '
-                                    'try %r instead.\nsection: [%s]'
-                                    % (k, '.'.join(atoms[1:]), section))
-                            else:
-                                msg = (
-                                    'The config entry %r is invalid, '
-                                    'because the %r config namespace '
-                                    'is unknown.\n'
-                                    'section: [%s]' % (k, atoms[0], section))
-                            warnings.warn(msg)
-                        elif atoms[0] == 'tools':
-                            if atoms[1] not in dir(cherrypy.tools):
-                                msg = (
-                                    'The config entry %r may be invalid, '
-                                    'because the %r tool was not found.\n'
-                                    'section: [%s]' % (k, atoms[1], section))
-                                warnings.warn(msg)
-
-    def check_config_namespaces(self):
-        """Process config and warn on each unknown config namespace."""
-        for sn, app in cherrypy.tree.apps.items():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            self._known_ns(app)
-
-    # -------------------------- Config Types -------------------------- #
-    known_config_types = {}
-
-    def _populate_known_types(self):
-        b = [x for x in vars(builtins).values()
-             if type(x) is type(str)]
-
-        def traverse(obj, namespace):
-            for name in dir(obj):
-                # Hack for 3.2's warning about body_params
-                if name == 'body_params':
-                    continue
-                vtype = type(getattr(obj, name, None))
-                if vtype in b:
-                    self.known_config_types[namespace + '.' + name] = vtype
-
-        traverse(cherrypy.request, 'request')
-        traverse(cherrypy.response, 'response')
-        traverse(cherrypy.server, 'server')
-        traverse(cherrypy.engine, 'engine')
-        traverse(cherrypy.log, 'log')
-
-    def _known_types(self, config):
-        msg = ('The config entry %r in section %r is of type %r, '
-               'which does not match the expected type %r.')
-
-        for section, conf in config.items():
-            if not isinstance(conf, dict):
-                conf = {section: conf}
-            for k, v in conf.items():
-                if v is not None:
-                    expected_type = self.known_config_types.get(k, None)
-                    vtype = type(v)
-                    if expected_type and vtype != expected_type:
-                        warnings.warn(msg % (k, section, vtype.__name__,
-                                             expected_type.__name__))
-
-    def check_config_types(self):
-        """Assert that config values are of the same type as default values."""
-        self._known_types(cherrypy.config)
-        for sn, app in cherrypy.tree.apps.items():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            self._known_types(app.config)
-
-    # -------------------- Specific config warnings -------------------- #
-    def check_localhost(self):
-        """Warn if any socket_host is 'localhost'. See #711."""
-        for k, v in cherrypy.config.items():
-            if k == 'server.socket_host' and v == 'localhost':
-                warnings.warn("The use of 'localhost' as a socket host can "
-                              'cause problems on newer systems, since '
-                              "'localhost' can map to either an IPv4 or an "
-                              "IPv6 address. You should use '127.0.0.1' "
-                              "or '[::1]' instead.")
diff --git a/libraries/cherrypy/_cpcompat.py b/libraries/cherrypy/_cpcompat.py
deleted file mode 100644
index f454505c..00000000
--- a/libraries/cherrypy/_cpcompat.py
+++ /dev/null
@@ -1,162 +0,0 @@
-"""Compatibility code for using CherryPy with various versions of Python.
-
-To retain compatibility with older Python versions, this module provides a
-useful abstraction over the differences between Python versions, sometimes by
-preferring a newer idiom, sometimes an older one, and sometimes a custom one.
-
-In particular, Python 2 uses str and '' for byte strings, while Python 3
-uses str and '' for unicode strings. We will call each of these the 'native
-string' type for each version. Because of this major difference, this module
-provides
-two functions: 'ntob', which translates native strings (of type 'str') into
-byte strings regardless of Python version, and 'ntou', which translates native
-strings to unicode strings.
-
-Try not to use the compatibility functions 'ntob', 'ntou', 'tonative'.
-They were created with Python 2.3-2.5 compatibility in mind.
-Instead, use unicode literals (from __future__) and bytes literals
-and their .encode/.decode methods as needed.
-"""
-
-import re
-import sys
-import threading
-
-import six
-from six.moves import urllib
-
-
-if six.PY3:
-    def ntob(n, encoding='ISO-8859-1'):
-        """Return the given native string as a byte string in the given
-        encoding.
-        """
-        assert_native(n)
-        # In Python 3, the native string type is unicode
-        return n.encode(encoding)
-
-    def ntou(n, encoding='ISO-8859-1'):
-        """Return the given native string as a unicode string with the given
-        encoding.
-        """
-        assert_native(n)
-        # In Python 3, the native string type is unicode
-        return n
-
-    def tonative(n, encoding='ISO-8859-1'):
-        """Return the given string as a native string in the given encoding."""
-        # In Python 3, the native string type is unicode
-        if isinstance(n, bytes):
-            return n.decode(encoding)
-        return n
-else:
-    # Python 2
-    def ntob(n, encoding='ISO-8859-1'):
-        """Return the given native string as a byte string in the given
-        encoding.
-        """
-        assert_native(n)
-        # 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
-
-    def ntou(n, encoding='ISO-8859-1'):
-        """Return the given native string as a unicode string with the given
-        encoding.
-        """
-        assert_native(n)
-        # In Python 2, the native string type is bytes.
-        # First, check for the special encoding 'escape'. The test suite uses
-        # this to signal that it wants to pass a string with embedded \uXXXX
-        # escapes, but without having to prefix it with u'' for Python 2,
-        # but no prefix for Python 3.
-        if encoding == 'escape':
-            return six.text_type(  # unicode for Python 2
-                re.sub(r'\\u([0-9a-zA-Z]{4})',
-                       lambda m: six.unichr(int(m.group(1), 16)),
-                       n.decode('ISO-8859-1')))
-        # Assume it's already in the given encoding, which for ISO-8859-1
-        # is almost always what was intended.
-        return n.decode(encoding)
-
-    def tonative(n, encoding='ISO-8859-1'):
-        """Return the given string as a native string in the given encoding."""
-        # In Python 2, the native string type is bytes.
-        if isinstance(n, six.text_type):  # unicode for Python 2
-            return n.encode(encoding)
-        return n
-
-
-def assert_native(n):
-    if not isinstance(n, str):
-        raise TypeError('n must be a native str (got %s)' % type(n).__name__)
-
-
-# Some platforms don't expose HTTPSConnection, so handle it separately
-HTTPSConnection = getattr(six.moves.http_client, 'HTTPSConnection', None)
-
-
-def _unquote_plus_compat(string, encoding='utf-8', errors='replace'):
-    return urllib.parse.unquote_plus(string).decode(encoding, errors)
-
-
-def _unquote_compat(string, encoding='utf-8', errors='replace'):
-    return urllib.parse.unquote(string).decode(encoding, errors)
-
-
-def _quote_compat(string, encoding='utf-8', errors='replace'):
-    return urllib.parse.quote(string.encode(encoding, errors))
-
-
-unquote_plus = urllib.parse.unquote_plus if six.PY3 else _unquote_plus_compat
-unquote = urllib.parse.unquote if six.PY3 else _unquote_compat
-quote = urllib.parse.quote if six.PY3 else _quote_compat
-
-try:
-    # Prefer simplejson
-    import simplejson as json
-except ImportError:
-    import json
-
-
-json_decode = json.JSONDecoder().decode
-_json_encode = json.JSONEncoder().iterencode
-
-
-if six.PY3:
-    # Encode to bytes on Python 3
-    def json_encode(value):
-        for chunk in _json_encode(value):
-            yield chunk.encode('utf-8')
-else:
-    json_encode = _json_encode
-
-
-text_or_bytes = six.text_type, bytes
-
-
-if sys.version_info >= (3, 3):
-    Timer = threading.Timer
-    Event = threading.Event
-else:
-    # Python 3.2 and earlier
-    Timer = threading._Timer
-    Event = threading._Event
-
-# html module come in 3.2 version
-try:
-    from html import escape
-except ImportError:
-    from cgi import escape
-
-
-# html module needed the argument quote=False because in cgi the default
-# is False. With quote=True the results differ.
-
-def escape_html(s, escape_quote=False):
-    """Replace special characters "&", "<" and ">" to HTML-safe sequences.
-
-    When escape_quote=True, escape (') and (") chars.
-    """
-    return escape(s, quote=escape_quote)
diff --git a/libraries/cherrypy/_cpconfig.py b/libraries/cherrypy/_cpconfig.py
deleted file mode 100644
index 79d9d911..00000000
--- a/libraries/cherrypy/_cpconfig.py
+++ /dev/null
@@ -1,300 +0,0 @@
-"""
-Configuration system for CherryPy.
-
-Configuration in CherryPy is implemented via dictionaries. Keys are strings
-which name the mapped value, which may be of any type.
-
-
-Architecture
-------------
-
-CherryPy Requests are part of an Application, which runs in a global context,
-and configuration data may apply to any of those three scopes:
-
-Global
-    Configuration entries which apply everywhere are stored in
-    cherrypy.config.
-
-Application
-    Entries which apply to each mounted application are stored
-    on the Application object itself, as 'app.config'. This is a two-level
-    dict where each key is a path, or "relative URL" (for example, "/" or
-    "/path/to/my/page"), and each value is a config dict. Usually, this
-    data is provided in the call to tree.mount(root(), config=conf),
-    although you may also use app.merge(conf).
-
-Request
-    Each Request object possesses a single 'Request.config' dict.
-    Early in the request process, this dict is populated by merging global
-    config entries, Application entries (whose path equals or is a parent
-    of Request.path_info), and any config acquired while looking up the
-    page handler (see next).
-
-
-Declaration
------------
-
-Configuration data may be supplied as a Python dictionary, as a filename,
-or as an open file object. When you supply a filename or file, CherryPy
-uses Python's builtin ConfigParser; you declare Application config by
-writing each path as a section header::
-
-    [/path/to/my/page]
-    request.stream = True
-
-To declare global configuration entries, place them in a [global] section.
-
-You may also declare config entries directly on the classes and methods
-(page handlers) that make up your CherryPy application via the ``_cp_config``
-attribute, set with the ``cherrypy.config`` decorator. For example::
-
-    @cherrypy.config(**{'tools.gzip.on': True})
-    class Demo:
-
-        @cherrypy.expose
-        @cherrypy.config(**{'request.show_tracebacks': False})
-        def index(self):
-            return "Hello world"
-
-.. note::
-
-    This behavior is only guaranteed for the default dispatcher.
-    Other dispatchers may have different restrictions on where
-    you can attach config attributes.
-
-
-Namespaces
-----------
-
-Configuration keys are separated into namespaces by the first "." in the key.
-Current namespaces:
-
-engine
-    Controls the 'application engine', including autoreload.
-    These can only be declared in the global config.
-
-tree
-    Grafts cherrypy.Application objects onto cherrypy.tree.
-    These can only be declared in the global config.
-
-hooks
-    Declares additional request-processing functions.
-
-log
-    Configures the logging for each application.
-    These can only be declared in the global or / config.
-
-request
-    Adds attributes to each Request.
-
-response
-    Adds attributes to each Response.
-
-server
-    Controls the default HTTP server via cherrypy.server.
-    These can only be declared in the global config.
-
-tools
-    Runs and configures additional request-processing packages.
-
-wsgi
-    Adds WSGI middleware to an Application's "pipeline".
-    These can only be declared in the app's root config ("/").
-
-checker
-    Controls the 'checker', which looks for common errors in
-    app state (including config) when the engine starts.
-    Global config only.
-
-The only key that does not exist in a namespace is the "environment" entry.
-This special entry 'imports' other config entries from a template stored in
-cherrypy._cpconfig.environments[environment]. It only applies to the global
-config, and only when you use cherrypy.config.update.
-
-You can define your own namespaces to be called at the Global, Application,
-or Request level, by adding a named handler to cherrypy.config.namespaces,
-app.namespaces, or app.request_class.namespaces. The name can
-be any string, and the handler must be either a callable or a (Python 2.5
-style) context manager.
-"""
-
-import cherrypy
-from cherrypy._cpcompat import text_or_bytes
-from cherrypy.lib import reprconf
-
-
-def _if_filename_register_autoreload(ob):
-    """Register for autoreload if ob is a string (presumed filename)."""
-    is_filename = isinstance(ob, text_or_bytes)
-    is_filename and cherrypy.engine.autoreload.files.add(ob)
-
-
-def merge(base, other):
-    """Merge one app config (from a dict, file, or filename) into another.
-
-    If the given config is a filename, it will be appended to
-    the list of files to monitor for "autoreload" changes.
-    """
-    _if_filename_register_autoreload(other)
-
-    # Load other into base
-    for section, value_map in reprconf.Parser.load(other).items():
-        if not isinstance(value_map, dict):
-            raise ValueError(
-                'Application config must include section headers, but the '
-                "config you tried to merge doesn't have any sections. "
-                'Wrap your config in another dict with paths as section '
-                "headers, for example: {'/': config}.")
-        base.setdefault(section, {}).update(value_map)
-
-
-class Config(reprconf.Config):
-    """The 'global' configuration data for the entire CherryPy process."""
-
-    def update(self, config):
-        """Update self from a dict, file or filename."""
-        _if_filename_register_autoreload(config)
-        super(Config, self).update(config)
-
-    def _apply(self, config):
-        """Update self from a dict."""
-        if isinstance(config.get('global'), dict):
-            if len(config) > 1:
-                cherrypy.checker.global_config_contained_paths = True
-            config = config['global']
-        if 'tools.staticdir.dir' in config:
-            config['tools.staticdir.section'] = 'global'
-        super(Config, self)._apply(config)
-
-    @staticmethod
-    def __call__(**kwargs):
-        """Decorate for page handlers to set _cp_config."""
-        def tool_decorator(f):
-            _Vars(f).setdefault('_cp_config', {}).update(kwargs)
-            return f
-        return tool_decorator
-
-
-class _Vars(object):
-    """Adapter allowing setting a default attribute on a function or class."""
-
-    def __init__(self, target):
-        self.target = target
-
-    def setdefault(self, key, default):
-        if not hasattr(self.target, key):
-            setattr(self.target, key, default)
-        return getattr(self.target, key)
-
-
-# Sphinx begin config.environments
-Config.environments = environments = {
-    'staging': {
-        'engine.autoreload.on': False,
-        'checker.on': False,
-        'tools.log_headers.on': False,
-        'request.show_tracebacks': False,
-        'request.show_mismatched_params': False,
-    },
-    'production': {
-        'engine.autoreload.on': False,
-        'checker.on': False,
-        'tools.log_headers.on': False,
-        'request.show_tracebacks': False,
-        'request.show_mismatched_params': False,
-        'log.screen': False,
-    },
-    'embedded': {
-        # For use with CherryPy embedded in another deployment stack.
-        'engine.autoreload.on': False,
-        'checker.on': False,
-        'tools.log_headers.on': False,
-        'request.show_tracebacks': False,
-        'request.show_mismatched_params': False,
-        'log.screen': False,
-        'engine.SIGHUP': None,
-        'engine.SIGTERM': None,
-    },
-    'test_suite': {
-        'engine.autoreload.on': False,
-        'checker.on': False,
-        'tools.log_headers.on': False,
-        'request.show_tracebacks': True,
-        'request.show_mismatched_params': True,
-        'log.screen': False,
-    },
-}
-# Sphinx end config.environments
-
-
-def _server_namespace_handler(k, v):
-    """Config handler for the "server" namespace."""
-    atoms = k.split('.', 1)
-    if len(atoms) > 1:
-        # Special-case config keys of the form 'server.servername.socket_port'
-        # to configure additional HTTP servers.
-        if not hasattr(cherrypy, 'servers'):
-            cherrypy.servers = {}
-
-        servername, k = atoms
-        if servername not in cherrypy.servers:
-            from cherrypy import _cpserver
-            cherrypy.servers[servername] = _cpserver.Server()
-            # On by default, but 'on = False' can unsubscribe it (see below).
-            cherrypy.servers[servername].subscribe()
-
-        if k == 'on':
-            if v:
-                cherrypy.servers[servername].subscribe()
-            else:
-                cherrypy.servers[servername].unsubscribe()
-        else:
-            setattr(cherrypy.servers[servername], k, v)
-    else:
-        setattr(cherrypy.server, k, v)
-
-
-Config.namespaces['server'] = _server_namespace_handler
-
-
-def _engine_namespace_handler(k, v):
-    """Config handler for the "engine" namespace."""
-    engine = cherrypy.engine
-
-    if k in {'SIGHUP', 'SIGTERM'}:
-        engine.subscribe(k, v)
-        return
-
-    if '.' in k:
-        plugin, attrname = k.split('.', 1)
-        try:
-            plugin = getattr(engine, plugin)
-        except Exception as error:
-            setattr(engine, k, v)
-        else:
-            op = 'subscribe' if v else 'unsubscribe'
-            sub_unsub = getattr(plugin, op, None)
-            if attrname == 'on' and callable(sub_unsub):
-                sub_unsub()
-                return
-            setattr(plugin, attrname, v)
-    else:
-        setattr(engine, k, v)
-
-
-Config.namespaces['engine'] = _engine_namespace_handler
-
-
-def _tree_namespace_handler(k, v):
-    """Namespace handler for the 'tree' config namespace."""
-    if isinstance(v, dict):
-        for script_name, app in v.items():
-            cherrypy.tree.graft(app, script_name)
-            msg = 'Mounted: %s on %s' % (app, script_name or '/')
-            cherrypy.engine.log(msg)
-    else:
-        cherrypy.tree.graft(v, v.script_name)
-        cherrypy.engine.log('Mounted: %s on %s' % (v, v.script_name or '/'))
-
-
-Config.namespaces['tree'] = _tree_namespace_handler
diff --git a/libraries/cherrypy/_cpdispatch.py b/libraries/cherrypy/_cpdispatch.py
deleted file mode 100644
index 83eb79cb..00000000
--- a/libraries/cherrypy/_cpdispatch.py
+++ /dev/null
@@ -1,686 +0,0 @@
-"""CherryPy dispatchers.
-
-A 'dispatcher' is the object which looks up the 'page handler' callable
-and collects config for the current request based on the path_info, other
-request attributes, and the application architecture. The core calls the
-dispatcher as early as possible, passing it a 'path_info' argument.
-
-The default dispatcher discovers the page handler by matching path_info
-to a hierarchical arrangement of objects, starting at request.app.root.
-"""
-
-import string
-import sys
-import types
-try:
-    classtype = (type, types.ClassType)
-except AttributeError:
-    classtype = type
-
-import cherrypy
-
-
-class PageHandler(object):
-
-    """Callable which sets response.body."""
-
-    def __init__(self, callable, *args, **kwargs):
-        self.callable = callable
-        self.args = args
-        self.kwargs = kwargs
-
-    @property
-    def args(self):
-        """The ordered args should be accessible from post dispatch hooks."""
-        return cherrypy.serving.request.args
-
-    @args.setter
-    def args(self, args):
-        cherrypy.serving.request.args = args
-        return cherrypy.serving.request.args
-
-    @property
-    def kwargs(self):
-        """The named kwargs should be accessible from post dispatch hooks."""
-        return cherrypy.serving.request.kwargs
-
-    @kwargs.setter
-    def kwargs(self, kwargs):
-        cherrypy.serving.request.kwargs = kwargs
-        return cherrypy.serving.request.kwargs
-
-    def __call__(self):
-        try:
-            return self.callable(*self.args, **self.kwargs)
-        except TypeError:
-            x = sys.exc_info()[1]
-            try:
-                test_callable_spec(self.callable, self.args, self.kwargs)
-            except cherrypy.HTTPError:
-                raise sys.exc_info()[1]
-            except Exception:
-                raise x
-            raise
-
-
-def test_callable_spec(callable, callable_args, callable_kwargs):
-    """
-    Inspect callable and test to see if the given args are suitable for it.
-
-    When an error occurs during the handler's invoking stage there are 2
-    erroneous cases:
-    1.  Too many parameters passed to a function which doesn't define
-        one of *args or **kwargs.
-    2.  Too little parameters are passed to the function.
-
-    There are 3 sources of parameters to a cherrypy handler.
-    1.  query string parameters are passed as keyword parameters to the
-        handler.
-    2.  body parameters are also passed as keyword parameters.
-    3.  when partial matching occurs, the final path atoms are passed as
-        positional args.
-    Both the query string and path atoms are part of the URI.  If they are
-    incorrect, then a 404 Not Found should be raised. Conversely the body
-    parameters are part of the request; if they are invalid a 400 Bad Request.
-    """
-    show_mismatched_params = getattr(
-        cherrypy.serving.request, 'show_mismatched_params', False)
-    try:
-        (args, varargs, varkw, defaults) = getargspec(callable)
-    except TypeError:
-        if isinstance(callable, object) and hasattr(callable, '__call__'):
-            (args, varargs, varkw,
-             defaults) = getargspec(callable.__call__)
-        else:
-            # If it wasn't one of our own types, re-raise
-            # the original error
-            raise
-
-    if args and (
-            # For callable objects, which have a __call__(self) method
-            hasattr(callable, '__call__') or
-            # For normal methods
-            inspect.ismethod(callable)
-    ):
-        # Strip 'self'
-        args = args[1:]
-
-    arg_usage = dict([(arg, 0,) for arg in args])
-    vararg_usage = 0
-    varkw_usage = 0
-    extra_kwargs = set()
-
-    for i, value in enumerate(callable_args):
-        try:
-            arg_usage[args[i]] += 1
-        except IndexError:
-            vararg_usage += 1
-
-    for key in callable_kwargs.keys():
-        try:
-            arg_usage[key] += 1
-        except KeyError:
-            varkw_usage += 1
-            extra_kwargs.add(key)
-
-    # figure out which args have defaults.
-    args_with_defaults = args[-len(defaults or []):]
-    for i, val in enumerate(defaults or []):
-        # Defaults take effect only when the arg hasn't been used yet.
-        if arg_usage[args_with_defaults[i]] == 0:
-            arg_usage[args_with_defaults[i]] += 1
-
-    missing_args = []
-    multiple_args = []
-    for key, usage in arg_usage.items():
-        if usage == 0:
-            missing_args.append(key)
-        elif usage > 1:
-            multiple_args.append(key)
-
-    if missing_args:
-        # In the case where the method allows body arguments
-        # there are 3 potential errors:
-        # 1. not enough query string parameters -> 404
-        # 2. not enough body parameters -> 400
-        # 3. not enough path parts (partial matches) -> 404
-        #
-        # We can't actually tell which case it is,
-        # so I'm raising a 404 because that covers 2/3 of the
-        # possibilities
-        #
-        # In the case where the method does not allow body
-        # arguments it's definitely a 404.
-        message = None
-        if show_mismatched_params:
-            message = 'Missing parameters: %s' % ','.join(missing_args)
-        raise cherrypy.HTTPError(404, message=message)
-
-    # the extra positional arguments come from the path - 404 Not Found
-    if not varargs and vararg_usage > 0:
-        raise cherrypy.HTTPError(404)
-
-    body_params = cherrypy.serving.request.body.params or {}
-    body_params = set(body_params.keys())
-    qs_params = set(callable_kwargs.keys()) - body_params
-
-    if multiple_args:
-        if qs_params.intersection(set(multiple_args)):
-            # If any of the multiple parameters came from the query string then
-            # it's a 404 Not Found
-            error = 404
-        else:
-            # Otherwise it's a 400 Bad Request
-            error = 400
-
-        message = None
-        if show_mismatched_params:
-            message = 'Multiple values for parameters: '\
-                '%s' % ','.join(multiple_args)
-        raise cherrypy.HTTPError(error, message=message)
-
-    if not varkw and varkw_usage > 0:
-
-        # If there were extra query string parameters, it's a 404 Not Found
-        extra_qs_params = set(qs_params).intersection(extra_kwargs)
-        if extra_qs_params:
-            message = None
-            if show_mismatched_params:
-                message = 'Unexpected query string '\
-                    'parameters: %s' % ', '.join(extra_qs_params)
-            raise cherrypy.HTTPError(404, message=message)
-
-        # If there were any extra body parameters, it's a 400 Not Found
-        extra_body_params = set(body_params).intersection(extra_kwargs)
-        if extra_body_params:
-            message = None
-            if show_mismatched_params:
-                message = 'Unexpected body parameters: '\
-                    '%s' % ', '.join(extra_body_params)
-            raise cherrypy.HTTPError(400, message=message)
-
-
-try:
-    import inspect
-except ImportError:
-    def test_callable_spec(callable, args, kwargs):  # noqa: F811
-        return None
-else:
-    getargspec = inspect.getargspec
-    # Python 3 requires using getfullargspec if
-    # keyword-only arguments are present
-    if hasattr(inspect, 'getfullargspec'):
-        def getargspec(callable):
-            return inspect.getfullargspec(callable)[:4]
-
-
-class LateParamPageHandler(PageHandler):
-
-    """When passing cherrypy.request.params to the page handler, we do not
-    want to capture that dict too early; we want to give tools like the
-    decoding tool a chance to modify the params dict in-between the lookup
-    of the handler and the actual calling of the handler. This subclass
-    takes that into account, and allows request.params to be 'bound late'
-    (it's more complicated than that, but that's the effect).
-    """
-
-    @property
-    def kwargs(self):
-        """Page handler kwargs (with cherrypy.request.params copied in)."""
-        kwargs = cherrypy.serving.request.params.copy()
-        if self._kwargs:
-            kwargs.update(self._kwargs)
-        return kwargs
-
-    @kwargs.setter
-    def kwargs(self, kwargs):
-        cherrypy.serving.request.kwargs = kwargs
-        self._kwargs = kwargs
-
-
-if sys.version_info < (3, 0):
-    punctuation_to_underscores = string.maketrans(
-        string.punctuation, '_' * len(string.punctuation))
-
-    def validate_translator(t):
-        if not isinstance(t, str) or len(t) != 256:
-            raise ValueError(
-                'The translate argument must be a str of len 256.')
-else:
-    punctuation_to_underscores = str.maketrans(
-        string.punctuation, '_' * len(string.punctuation))
-
-    def validate_translator(t):
-        if not isinstance(t, dict):
-            raise ValueError('The translate argument must be a dict.')
-
-
-class Dispatcher(object):
-
-    """CherryPy Dispatcher which walks a tree of objects to find a handler.
-
-    The tree is rooted at cherrypy.request.app.root, and each hierarchical
-    component in the path_info argument is matched to a corresponding nested
-    attribute of the root object. Matching handlers must have an 'exposed'
-    attribute which evaluates to True. The special method name "index"
-    matches a URI which ends in a slash ("/"). The special method name
-    "default" may match a portion of the path_info (but only when no longer
-    substring of the path_info matches some other object).
-
-    This is the default, built-in dispatcher for CherryPy.
-    """
-
-    dispatch_method_name = '_cp_dispatch'
-    """
-    The name of the dispatch method that nodes may optionally implement
-    to provide their own dynamic dispatch algorithm.
-    """
-
-    def __init__(self, dispatch_method_name=None,
-                 translate=punctuation_to_underscores):
-        validate_translator(translate)
-        self.translate = translate
-        if dispatch_method_name:
-            self.dispatch_method_name = dispatch_method_name
-
-    def __call__(self, path_info):
-        """Set handler and config for the current request."""
-        request = cherrypy.serving.request
-        func, vpath = self.find_handler(path_info)
-
-        if func:
-            # Decode any leftover %2F in the virtual_path atoms.
-            vpath = [x.replace('%2F', '/') for x in vpath]
-            request.handler = LateParamPageHandler(func, *vpath)
-        else:
-            request.handler = cherrypy.NotFound()
-
-    def find_handler(self, path):
-        """Return the appropriate page handler, plus any virtual path.
-
-        This will return two objects. The first will be a callable,
-        which can be used to generate page output. Any parameters from
-        the query string or request body will be sent to that callable
-        as keyword arguments.
-
-        The callable is found by traversing the application's tree,
-        starting from cherrypy.request.app.root, and matching path
-        components to successive objects in the tree. For example, the
-        URL "/path/to/handler" might return root.path.to.handler.
-
-        The second object returned will be a list of names which are
-        'virtual path' components: parts of the URL which are dynamic,
-        and were not used when looking up the handler.
-        These virtual path components are passed to the handler as
-        positional arguments.
-        """
-        request = cherrypy.serving.request
-        app = request.app
-        root = app.root
-        dispatch_name = self.dispatch_method_name
-
-        # Get config for the root object/path.
-        fullpath = [x for x in path.strip('/').split('/') if x] + ['index']
-        fullpath_len = len(fullpath)
-        segleft = fullpath_len
-        nodeconf = {}
-        if hasattr(root, '_cp_config'):
-            nodeconf.update(root._cp_config)
-        if '/' in app.config:
-            nodeconf.update(app.config['/'])
-        object_trail = [['root', root, nodeconf, segleft]]
-
-        node = root
-        iternames = fullpath[:]
-        while iternames:
-            name = iternames[0]
-            # map to legal Python identifiers (e.g. replace '.' with '_')
-            objname = name.translate(self.translate)
-
-            nodeconf = {}
-            subnode = getattr(node, objname, None)
-            pre_len = len(iternames)
-            if subnode is None:
-                dispatch = getattr(node, dispatch_name, None)
-                if dispatch and hasattr(dispatch, '__call__') and not \
-                        getattr(dispatch, 'exposed', False) and \
-                        pre_len > 1:
-                    # Don't expose the hidden 'index' token to _cp_dispatch
-                    # We skip this if pre_len == 1 since it makes no sense
-                    # to call a dispatcher when we have no tokens left.
-                    index_name = iternames.pop()
-                    subnode = dispatch(vpath=iternames)
-                    iternames.append(index_name)
-                else:
-                    # We didn't find a path, but keep processing in case there
-                    # is a default() handler.
-                    iternames.pop(0)
-            else:
-                # We found the path, remove the vpath entry
-                iternames.pop(0)
-            segleft = len(iternames)
-            if segleft > pre_len:
-                # No path segment was removed.  Raise an error.
-                raise cherrypy.CherryPyException(
-                    'A vpath segment was added.  Custom dispatchers may only '
-                    'remove elements.  While trying to process '
-                    '{0} in {1}'.format(name, fullpath)
-                )
-            elif segleft == pre_len:
-                # Assume that the handler used the current path segment, but
-                # did not pop it.  This allows things like
-                # return getattr(self, vpath[0], None)
-                iternames.pop(0)
-                segleft -= 1
-            node = subnode
-
-            if node is not None:
-                # Get _cp_config attached to this node.
-                if hasattr(node, '_cp_config'):
-                    nodeconf.update(node._cp_config)
-
-            # Mix in values from app.config for this path.
-            existing_len = fullpath_len - pre_len
-            if existing_len != 0:
-                curpath = '/' + '/'.join(fullpath[0:existing_len])
-            else:
-                curpath = ''
-            new_segs = fullpath[fullpath_len - pre_len:fullpath_len - segleft]
-            for seg in new_segs:
-                curpath += '/' + seg
-                if curpath in app.config:
-                    nodeconf.update(app.config[curpath])
-
-            object_trail.append([name, node, nodeconf, segleft])
-
-        def set_conf():
-            """Collapse all object_trail config into cherrypy.request.config.
-            """
-            base = cherrypy.config.copy()
-            # Note that we merge the config from each node
-            # even if that node was None.
-            for name, obj, conf, segleft in object_trail:
-                base.update(conf)
-                if 'tools.staticdir.dir' in conf:
-                    base['tools.staticdir.section'] = '/' + \
-                        '/'.join(fullpath[0:fullpath_len - segleft])
-            return base
-
-        # Try successive objects (reverse order)
-        num_candidates = len(object_trail) - 1
-        for i in range(num_candidates, -1, -1):
-
-            name, candidate, nodeconf, segleft = object_trail[i]
-            if candidate is None:
-                continue
-
-            # Try a "default" method on the current leaf.
-            if hasattr(candidate, 'default'):
-                defhandler = candidate.default
-                if getattr(defhandler, 'exposed', False):
-                    # Insert any extra _cp_config from the default handler.
-                    conf = getattr(defhandler, '_cp_config', {})
-                    object_trail.insert(
-                        i + 1, ['default', defhandler, conf, segleft])
-                    request.config = set_conf()
-                    # See https://github.com/cherrypy/cherrypy/issues/613
-                    request.is_index = path.endswith('/')
-                    return defhandler, fullpath[fullpath_len - segleft:-1]
-
-            # Uncomment the next line to restrict positional params to
-            # "default".
-            # if i < num_candidates - 2: continue
-
-            # Try the current leaf.
-            if getattr(candidate, 'exposed', False):
-                request.config = set_conf()
-                if i == num_candidates:
-                    # We found the extra ".index". Mark request so tools
-                    # can redirect if path_info has no trailing slash.
-                    request.is_index = True
-                else:
-                    # We're not at an 'index' handler. Mark request so tools
-                    # can redirect if path_info has NO trailing slash.
-                    # Note that this also includes handlers which take
-                    # positional parameters (virtual paths).
-                    request.is_index = False
-                return candidate, fullpath[fullpath_len - segleft:-1]
-
-        # We didn't find anything
-        request.config = set_conf()
-        return None, []
-
-
-class MethodDispatcher(Dispatcher):
-
-    """Additional dispatch based on cherrypy.request.method.upper().
-
-    Methods named GET, POST, etc will be called on an exposed class.
-    The method names must be all caps; the appropriate Allow header
-    will be output showing all capitalized method names as allowable
-    HTTP verbs.
-
-    Note that the containing class must be exposed, not the methods.
-    """
-
-    def __call__(self, path_info):
-        """Set handler and config for the current request."""
-        request = cherrypy.serving.request
-        resource, vpath = self.find_handler(path_info)
-
-        if resource:
-            # Set Allow header
-            avail = [m for m in dir(resource) if m.isupper()]
-            if 'GET' in avail and 'HEAD' not in avail:
-                avail.append('HEAD')
-            avail.sort()
-            cherrypy.serving.response.headers['Allow'] = ', '.join(avail)
-
-            # Find the subhandler
-            meth = request.method.upper()
-            func = getattr(resource, meth, None)
-            if func is None and meth == 'HEAD':
-                func = getattr(resource, 'GET', None)
-            if func:
-                # Grab any _cp_config on the subhandler.
-                if hasattr(func, '_cp_config'):
-                    request.config.update(func._cp_config)
-
-                # Decode any leftover %2F in the virtual_path atoms.
-                vpath = [x.replace('%2F', '/') for x in vpath]
-                request.handler = LateParamPageHandler(func, *vpath)
-            else:
-                request.handler = cherrypy.HTTPError(405)
-        else:
-            request.handler = cherrypy.NotFound()
-
-
-class RoutesDispatcher(object):
-
-    """A Routes based dispatcher for CherryPy."""
-
-    def __init__(self, full_result=False, **mapper_options):
-        """
-        Routes dispatcher
-
-        Set full_result to True if you wish the controller
-        and the action to be passed on to the page handler
-        parameters. By default they won't be.
-        """
-        import routes
-        self.full_result = full_result
-        self.controllers = {}
-        self.mapper = routes.Mapper(**mapper_options)
-        self.mapper.controller_scan = self.controllers.keys
-
-    def connect(self, name, route, controller, **kwargs):
-        self.controllers[name] = controller
-        self.mapper.connect(name, route, controller=name, **kwargs)
-
-    def redirect(self, url):
-        raise cherrypy.HTTPRedirect(url)
-
-    def __call__(self, path_info):
-        """Set handler and config for the current request."""
-        func = self.find_handler(path_info)
-        if func:
-            cherrypy.serving.request.handler = LateParamPageHandler(func)
-        else:
-            cherrypy.serving.request.handler = cherrypy.NotFound()
-
-    def find_handler(self, path_info):
-        """Find the right page handler, and set request.config."""
-        import routes
-
-        request = cherrypy.serving.request
-
-        config = routes.request_config()
-        config.mapper = self.mapper
-        if hasattr(request, 'wsgi_environ'):
-            config.environ = request.wsgi_environ
-        config.host = request.headers.get('Host', None)
-        config.protocol = request.scheme
-        config.redirect = self.redirect
-
-        result = self.mapper.match(path_info)
-
-        config.mapper_dict = result
-        params = {}
-        if result:
-            params = result.copy()
-        if not self.full_result:
-            params.pop('controller', None)
-            params.pop('action', None)
-        request.params.update(params)
-
-        # Get config for the root object/path.
-        request.config = base = cherrypy.config.copy()
-        curpath = ''
-
-        def merge(nodeconf):
-            if 'tools.staticdir.dir' in nodeconf:
-                nodeconf['tools.staticdir.section'] = curpath or '/'
-            base.update(nodeconf)
-
-        app = request.app
-        root = app.root
-        if hasattr(root, '_cp_config'):
-            merge(root._cp_config)
-        if '/' in app.config:
-            merge(app.config['/'])
-
-        # Mix in values from app.config.
-        atoms = [x for x in path_info.split('/') if x]
-        if atoms:
-            last = atoms.pop()
-        else:
-            last = None
-        for atom in atoms:
-            curpath = '/'.join((curpath, atom))
-            if curpath in app.config:
-                merge(app.config[curpath])
-
-        handler = None
-        if result:
-            controller = result.get('controller')
-            controller = self.controllers.get(controller, controller)
-            if controller:
-                if isinstance(controller, classtype):
-                    controller = controller()
-                # Get config from the controller.
-                if hasattr(controller, '_cp_config'):
-                    merge(controller._cp_config)
-
-            action = result.get('action')
-            if action is not None:
-                handler = getattr(controller, action, None)
-                # Get config from the handler
-                if hasattr(handler, '_cp_config'):
-                    merge(handler._cp_config)
-            else:
-                handler = controller
-
-        # Do the last path atom here so it can
-        # override the controller's _cp_config.
-        if last:
-            curpath = '/'.join((curpath, last))
-            if curpath in app.config:
-                merge(app.config[curpath])
-
-        return handler
-
-
-def XMLRPCDispatcher(next_dispatcher=Dispatcher()):
-    from cherrypy.lib import xmlrpcutil
-
-    def xmlrpc_dispatch(path_info):
-        path_info = xmlrpcutil.patched_path(path_info)
-        return next_dispatcher(path_info)
-    return xmlrpc_dispatch
-
-
-def VirtualHost(next_dispatcher=Dispatcher(), use_x_forwarded_host=True,
-                **domains):
-    """
-    Select a different handler based on the Host header.
-
-    This can be useful when running multiple sites within one CP server.
-    It allows several domains to point to different parts of a single
-    website structure. For example::
-
-        http://www.domain.example  ->  root
-        http://www.domain2.example  ->  root/domain2/
-        http://www.domain2.example:443  ->  root/secure
-
-    can be accomplished via the following config::
-
-        [/]
-        request.dispatch = cherrypy.dispatch.VirtualHost(
-            **{'www.domain2.example': '/domain2',
-               'www.domain2.example:443': '/secure',
-              })
-
-    next_dispatcher
-        The next dispatcher object in the dispatch chain.
-        The VirtualHost dispatcher adds a prefix to the URL and calls
-        another dispatcher. Defaults to cherrypy.dispatch.Dispatcher().
-
-    use_x_forwarded_host
-        If True (the default), any "X-Forwarded-Host"
-        request header will be used instead of the "Host" header. This
-        is commonly added by HTTP servers (such as Apache) when proxying.
-
-    ``**domains``
-        A dict of {host header value: virtual prefix} pairs.
-        The incoming "Host" request header is looked up in this dict,
-        and, if a match is found, the corresponding "virtual prefix"
-        value will be prepended to the URL path before calling the
-        next dispatcher. Note that you often need separate entries
-        for "example.com" and "www.example.com". In addition, "Host"
-        headers may contain the port number.
-    """
-    from cherrypy.lib import httputil
-
-    def vhost_dispatch(path_info):
-        request = cherrypy.serving.request
-        header = request.headers.get
-
-        domain = header('Host', '')
-        if use_x_forwarded_host:
-            domain = header('X-Forwarded-Host', domain)
-
-        prefix = domains.get(domain, '')
-        if prefix:
-            path_info = httputil.urljoin(prefix, path_info)
-
-        result = next_dispatcher(path_info)
-
-        # Touch up staticdir config. See
-        # https://github.com/cherrypy/cherrypy/issues/614.
-        section = request.config.get('tools.staticdir.section')
-        if section:
-            section = section[len(prefix):]
-            request.config['tools.staticdir.section'] = section
-
-        return result
-    return vhost_dispatch
diff --git a/libraries/cherrypy/_cperror.py b/libraries/cherrypy/_cperror.py
deleted file mode 100644
index e2a8fad8..00000000
--- a/libraries/cherrypy/_cperror.py
+++ /dev/null
@@ -1,619 +0,0 @@
-"""Exception classes for CherryPy.
-
-CherryPy provides (and uses) exceptions for declaring that the HTTP response
-should be a status other than the default "200 OK". You can ``raise`` them like
-normal Python exceptions. You can also call them and they will raise
-themselves; this means you can set an
-:class:`HTTPError<cherrypy._cperror.HTTPError>`
-or :class:`HTTPRedirect<cherrypy._cperror.HTTPRedirect>` as the
-:attr:`request.handler<cherrypy._cprequest.Request.handler>`.
-
-.. _redirectingpost:
-
-Redirecting POST
-================
-
-When you GET a resource and are redirected by the server to another Location,
-there's generally no problem since GET is both a "safe method" (there should
-be no side-effects) and an "idempotent method" (multiple calls are no different
-than a single call).
-
-POST, however, is neither safe nor idempotent--if you
-charge a credit card, you don't want to be charged twice by a redirect!
-
-For this reason, *none* of the 3xx responses permit a user-agent (browser) to
-resubmit a POST on redirection without first confirming the action with the
-user:
-
-=====    =================================    ===========
-300      Multiple Choices                     Confirm with the user
-301      Moved Permanently                    Confirm with the user
-302      Found (Object moved temporarily)     Confirm with the user
-303      See Other                            GET the new URI; no confirmation
-304      Not modified                         for conditional GET only;
-                                              POST should not raise this error
-305      Use Proxy                            Confirm with the user
-307      Temporary Redirect                   Confirm with the user
-=====    =================================    ===========
-
-However, browsers have historically implemented these restrictions poorly;
-in particular, many browsers do not force the user to confirm 301, 302
-or 307 when redirecting POST. For this reason, CherryPy defaults to 303,
-which most user-agents appear to have implemented correctly. Therefore, if
-you raise HTTPRedirect for a POST request, the user-agent will most likely
-attempt to GET the new URI (without asking for confirmation from the user).
-We realize this is confusing for developers, but it's the safest thing we
-could do. You are of course free to raise ``HTTPRedirect(uri, status=302)``
-or any other 3xx status if you know what you're doing, but given the
-environment, we couldn't let any of those be the default.
-
-Custom Error Handling
-=====================
-
-.. image:: /refman/cperrors.gif
-
-Anticipated HTTP responses
---------------------------
-
-The 'error_page' config namespace can be used to provide custom HTML output for
-expected responses (like 404 Not Found). Supply a filename from which the
-output will be read. The contents will be interpolated with the values
-%(status)s, %(message)s, %(traceback)s, and %(version)s using plain old Python
-`string formatting
-<http://docs.python.org/2/library/stdtypes.html#string-formatting-operations>`_.
-
-::
-
-    _cp_config = {
-        'error_page.404': os.path.join(localDir, "static/index.html")
-    }
-
-
-Beginning in version 3.1, you may also provide a function or other callable as
-an error_page entry. It will be passed the same status, message, traceback and
-version arguments that are interpolated into templates::
-
-    def error_page_402(status, message, traceback, version):
-        return "Error %s - Well, I'm very sorry but you haven't paid!" % status
-    cherrypy.config.update({'error_page.402': error_page_402})
-
-Also in 3.1, in addition to the numbered error codes, you may also supply
-"error_page.default" to handle all codes which do not have their own error_page
-entry.
-
-
-
-Unanticipated errors
---------------------
-
-CherryPy also has a generic error handling mechanism: whenever an unanticipated
-error occurs in your code, it will call
-:func:`Request.error_response<cherrypy._cprequest.Request.error_response>` to
-set the response status, headers, and body. By default, this is the same
-output as
-:class:`HTTPError(500) <cherrypy._cperror.HTTPError>`. If you want to provide
-some other behavior, you generally replace "request.error_response".
-
-Here is some sample code that shows how to display a custom error message and
-send an e-mail containing the error::
-
-    from cherrypy import _cperror
-
-    def handle_error():
-        cherrypy.response.status = 500
-        cherrypy.response.body = [
-            "<html><body>Sorry, an error occurred</body></html>"
-        ]
-        sendMail('error@domain.com',
-                 'Error in your web app',
-                 _cperror.format_exc())
-
-    @cherrypy.config(**{'request.error_response': handle_error})
-    class Root:
-        pass
-
-Note that you have to explicitly set
-:attr:`response.body <cherrypy._cprequest.Response.body>`
-and not simply return an error message as a result.
-"""
-
-import io
-import contextlib
-from sys import exc_info as _exc_info
-from traceback import format_exception as _format_exception
-from xml.sax import saxutils
-
-import six
-from six.moves import urllib
-
-from more_itertools import always_iterable
-
-import cherrypy
-from cherrypy._cpcompat import escape_html
-from cherrypy._cpcompat import ntob
-from cherrypy._cpcompat import tonative
-from cherrypy._helper import classproperty
-from cherrypy.lib import httputil as _httputil
-
-
-class CherryPyException(Exception):
-
-    """A base class for CherryPy exceptions."""
-    pass
-
-
-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=''):
-        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 = urllib.parse.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 a URL is absolute, it will be used as-is. If it is relative, it is
-    assumed to be relative to the current cherrypy.request.path_info.
-
-    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.
-    """
-
-    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):
-        self.urls = abs_urls = [
-            # 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")
-            #  3. a URL relative to the current path
-            # Note that any query string in cherrypy.request is discarded.
-            urllib.parse.urljoin(
-                cherrypy.url(),
-                tonative(url, encoding or self.encoding),
-            )
-            for url in always_iterable(urls)
-        ]
-
-        status = (
-            int(status)
-            if status is not None
-            else self.default_status
-        )
-        if not 300 <= status <= 399:
-            raise ValueError('status must be between 300 and 399.')
-
-        CherryPyException.__init__(self, abs_urls, status)
-
-    @classproperty
-    def default_status(cls):
-        """
-        The default redirect status for the request.
-
-        RFC 2616 indicates a 301 response code fits our goal; however,
-        browser support for 301 is quite messy. Use 302/303 instead. See
-        http://www.alanflavell.org.uk/www/post-redirect.html
-        """
-        return 303 if cherrypy.serving.request.protocol >= (1, 1) else 302
-
-    @property
-    def status(self):
-        """The integer HTTP status code to emit."""
-        _, status = self.args[:2]
-        return 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.
-        """
-        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)."
-            msg = {
-                300: 'This resource can be found at ',
-                301: 'This resource has permanently moved to ',
-                302: 'This resource resides temporarily at ',
-                303: 'This resource can be found at ',
-                307: 'This resource has moved temporarily to ',
-            }[status]
-            msg += '<a href=%s>%s</a>.'
-            msgs = [
-                msg % (saxutils.quoteattr(u), escape_html(u))
-                for u in self.urls
-            ]
-            response.body = ntob('<br />\n'.join(msgs), 'utf-8')
-            # Previous code may have set C-L, so we have to reset it
-            # (allow finalize to set it).
-            response.headers.pop('Content-Length', None)
-        elif status == 304:
-            # Not Modified.
-            # "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',
-                        'Content-Range', 'Content-Type', 'Expires',
-                        '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)
-        elif status == 305:
-            # Use Proxy.
-            # self.urls[0] should be the URI of the proxy.
-            response.headers['Location'] = ntob(self.urls[0], 'utf-8')
-            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."""
-    response = cherrypy.serving.response
-
-    # Remove headers which applied to the original content,
-    # but do not apply to the error page.
-    respheaders = response.headers
-    for key in ['Accept-Ranges', 'Age', 'ETag', 'Location', 'Retry-After',
-                'Vary', 'Content-Encoding', 'Content-Length', 'Expires',
-                '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
-        # with a byte-range-resp-spec of "*". The instance-length
-        # specifies the current length of the selected resource.
-        # A response with status code 206 (Partial Content) MUST NOT
-        # include a Content-Range field with a byte-range- resp-spec of "*".
-        if 'Content-Range' in respheaders:
-            del respheaders['Content-Range']
-
-
-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
-    ("Internal Server Error"). It also takes an optional ``message`` argument,
-    which will be returned in the response body. See
-    `RFC2616 <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.
-        """
-        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 = None
-        if cherrypy.serving.request.show_tracebacks:
-            tb = format_exc()
-
-        response.headers.pop('Content-Length', None)
-
-        content = self.get_error_page(self.status, traceback=tb,
-                                      message=self._message)
-        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
-
-    @classmethod
-    @contextlib.contextmanager
-    def handle(cls, exception, status=500, message=''):
-        """Translate exception into an HTTPError."""
-        try:
-            yield
-        except exception as exc:
-            raise cls(status, message or str(exc))
-
-
-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:
-            request = cherrypy.serving.request
-            path = request.script_name + request.path_info
-        self.args = (path,)
-        HTTPError.__init__(self, 404, "The path '%s' was not found." % path)
-
-
-_HTTPErrorTemplate = '''<!DOCTYPE html PUBLIC
-"-//W3C//DTD XHTML 1.0 Transitional//EN"
-"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html>
-<head>
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
-    <title>%(status)s</title>
-    <style type="text/css">
-    #powered_by {
-        margin-top: 20px;
-        border-top: 2px solid black;
-        font-style: italic;
-    }
-
-    #traceback {
-        color: red;
-    }
-    </style>
-</head>
-    <body>
-        <h2>%(status)s</h2>
-        <p>%(message)s</p>
-        <pre id="traceback">%(traceback)s</pre>
-    <div id="powered_by">
-      <span>
-        Powered by <a href="http://www.cherrypy.org">CherryPy %(version)s</a>
-      </span>
-    </div>
-    </body>
-</html>
-'''
-
-
-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.
-    """
-    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['status'] = '%s %s' % (code, reason)
-    if kwargs.get('message') is None:
-        kwargs['message'] = message
-    if kwargs.get('traceback') is None:
-        kwargs['traceback'] = ''
-    if kwargs.get('version') is None:
-        kwargs['version'] = cherrypy.__version__
-
-    for k, v in six.iteritems(kwargs):
-        if v is None:
-            kwargs[k] = ''
-        else:
-            kwargs[k] = escape_html(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')
-
-    # Default template, can be overridden below.
-    template = _HTTPErrorTemplate
-    if error_page:
-        try:
-            if hasattr(error_page, '__call__'):
-                # The caller function may be setting headers manually,
-                # so we delegate to it completely. We may be returning
-                # an iterator as well as a string here.
-                #
-                # We *must* make sure any content is not unicode.
-                result = error_page(**kwargs)
-                if cherrypy.lib.is_iterator(result):
-                    from cherrypy.lib.encoding import UTF8StreamEncoder
-                    return UTF8StreamEncoder(result)
-                elif isinstance(result, six.text_type):
-                    return result.encode('utf-8')
-                else:
-                    if not isinstance(result, bytes):
-                        raise ValueError(
-                            'error page function did not '
-                            'return a bytestring, six.text_type or an '
-                            'iterator - returned object of type %s.'
-                            % (type(result).__name__))
-                    return result
-            else:
-                # Load the template from this path.
-                template = io.open(error_page, newline='').read()
-        except Exception:
-            e = _format_exception(*_exc_info())[-1]
-            m = kwargs['message']
-            if m:
-                m += '<br />'
-            m += 'In addition, the custom error page failed:\n<br />%s' % e
-            kwargs['message'] = m
-
-    response = cherrypy.serving.response
-    response.headers['Content-Type'] = 'text/html;charset=utf-8'
-    result = template % kwargs
-    return result.encode('utf-8')
-
-
-_ie_friendly_error_sizes = {
-    400: 512, 403: 256, 404: 512, 405: 256,
-    406: 512, 408: 512, 409: 512, 410: 256,
-    500: 512, 501: 512, 505: 512,
-}
-
-
-def _be_ie_unfriendly(status):
-    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
-    # (by adding whitespace).
-    # See http://support.microsoft.com/kb/q218155/
-    s = _ie_friendly_error_sizes.get(status, 0)
-    if s:
-        s += 1
-        # Since we are issuing an HTTP error status, we assume that
-        # the entity is short, and we should just collapse it.
-        content = response.collapse_body()
-        content_length = len(content)
-        if content_length and content_length < s:
-            # IN ADDITION: the response must be written to IE
-            # in one chunk or it will still get replaced! Bah.
-            content = content + (b' ' * (s - content_length))
-        response.body = content
-        response.headers['Content-Length'] = str(len(content))
-
-
-def format_exc(exc=None):
-    """Return exc (or sys.exc_info if None), formatted."""
-    try:
-        if exc is None:
-            exc = _exc_info()
-        if exc == (None, None, None):
-            return ''
-        import traceback
-        return ''.join(traceback.format_exception(*exc))
-    finally:
-        del exc
-
-
-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 = b'Unrecoverable error in the server.'
-    if extrabody is not None:
-        if not isinstance(extrabody, bytes):
-            extrabody = extrabody.encode('utf-8')
-        body += b'\n' + extrabody
-
-    return (b'500 Internal Server Error',
-            [(b'Content-Type', b'text/plain'),
-             (b'Content-Length', ntob(str(len(body)), 'ISO-8859-1'))],
-            [body])
diff --git a/libraries/cherrypy/_cplogging.py b/libraries/cherrypy/_cplogging.py
deleted file mode 100644
index 53b9addb..00000000
--- a/libraries/cherrypy/_cplogging.py
+++ /dev/null
@@ -1,482 +0,0 @@
-"""
-Simple config
-=============
-
-Although CherryPy uses the :mod:`Python logging module <logging>`, it does so
-behind the scenes so that simple logging is simple, but complicated logging
-is still possible. "Simple" logging means that you can log to the screen
-(i.e. console/stdout) or to a file, and that you can easily have separate
-error and access log files.
-
-Here are the simplified logging settings. You use these by adding lines to
-your config file or dict. You should set these at either the global level or
-per application (see next), but generally not both.
-
- * ``log.screen``: Set this to True to have both "error" and "access" messages
-   printed to stdout.
- * ``log.access_file``: Set this to an absolute filename where you want
-   "access" messages written.
- * ``log.error_file``: Set this to an absolute filename where you want "error"
-   messages written.
-
-Many events are automatically logged; to log your own application events, call
-:func:`cherrypy.log`.
-
-Architecture
-============
-
-Separate scopes
----------------
-
-CherryPy provides log managers at both the global and application layers.
-This means you can have one set of logging rules for your entire site,
-and another set of rules specific to each application. The global log
-manager is found at :func:`cherrypy.log`, and the log manager for each
-application is found at :attr:`app.log<cherrypy._cptree.Application.log>`.
-If you're inside a request, the latter is reachable from
-``cherrypy.request.app.log``; if you're outside a request, you'll have to
-obtain a reference to the ``app``: either the return value of
-:func:`tree.mount()<cherrypy._cptree.Tree.mount>` or, if you used
-:func:`quickstart()<cherrypy.quickstart>` instead, via
-``cherrypy.tree.apps['/']``.
-
-By default, the global logs are named "cherrypy.error" and "cherrypy.access",
-and the application logs are named "cherrypy.error.2378745" and
-"cherrypy.access.2378745" (the number is the id of the Application object).
-This means that the application logs "bubble up" to the site logs, so if your
-application has no log handlers, the site-level handlers will still log the
-messages.
-
-Errors vs. Access
------------------
-
-Each log manager handles both "access" messages (one per HTTP request) and
-"error" messages (everything else). Note that the "error" log is not just for
-errors! The format of access messages is highly formalized, but the error log
-isn't--it receives messages from a variety of sources (including full error
-tracebacks, if enabled).
-
-If you are logging the access log and error log to the same source, then there
-is a possibility that a specially crafted error message may replicate an access
-log message as described in CWE-117.  In this case it is the application
-developer's responsibility to manually escape data before
-using CherryPy's log()
-functionality, or they may create an application that is vulnerable to CWE-117.
-This would be achieved by using a custom handler escape any special characters,
-and attached as described below.
-
-Custom Handlers
-===============
-
-The simple settings above work by manipulating Python's standard :mod:`logging`
-module. So when you need something more complex, the full power of the standard
-module is yours to exploit. You can borrow or create custom handlers, formats,
-filters, and much more. Here's an example that skips the standard FileHandler
-and uses a RotatingFileHandler instead:
-
-::
-
-    #python
-    log = app.log
-
-    # Remove the default FileHandlers if present.
-    log.error_file = ""
-    log.access_file = ""
-
-    maxBytes = getattr(log, "rot_maxBytes", 10000000)
-    backupCount = getattr(log, "rot_backupCount", 1000)
-
-    # Make a new RotatingFileHandler for the error log.
-    fname = getattr(log, "rot_error_file", "error.log")
-    h = handlers.RotatingFileHandler(fname, 'a', maxBytes, backupCount)
-    h.setLevel(DEBUG)
-    h.setFormatter(_cplogging.logfmt)
-    log.error_log.addHandler(h)
-
-    # Make a new RotatingFileHandler for the access log.
-    fname = getattr(log, "rot_access_file", "access.log")
-    h = handlers.RotatingFileHandler(fname, 'a', maxBytes, backupCount)
-    h.setLevel(DEBUG)
-    h.setFormatter(_cplogging.logfmt)
-    log.access_log.addHandler(h)
-
-
-The ``rot_*`` attributes are pulled straight from the application log object.
-Since "log.*" config entries simply set attributes on the log object, you can
-add custom attributes to your heart's content. Note that these handlers are
-used ''instead'' of the default, simple handlers outlined above (so don't set
-the "log.error_file" config entry, for example).
-"""
-
-import datetime
-import logging
-import os
-import sys
-
-import six
-
-import cherrypy
-from cherrypy import _cperror
-
-
-# Silence the no-handlers "warning" (stderr write!) in stdlib logging
-logging.Logger.manager.emittedNoHandlerWarning = 1
-logfmt = logging.Formatter('%(message)s')
-
-
-class NullHandler(logging.Handler):
-
-    """A no-op logging handler to silence the logging.lastResort handler."""
-
-    def handle(self, record):
-        pass
-
-    def emit(self, record):
-        pass
-
-    def createLock(self):
-        self.lock = None
-
-
-class LogManager(object):
-
-    """An object to assist both simple and advanced logging.
-
-    ``cherrypy.log`` is an instance of this class.
-    """
-
-    appid = None
-    """The id() of the Application object which owns this log manager. If this
-    is a global log manager, appid is None."""
-
-    error_log = None
-    """The actual :class:`logging.Logger` instance for error messages."""
-
-    access_log = None
-    """The actual :class:`logging.Logger` instance for access messages."""
-
-    access_log_format = (
-        '{h} {l} {u} {t} "{r}" {s} {b} "{f}" "{a}"'
-        if six.PY3 else
-        '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
-    )
-
-    logger_root = None
-    """The "top-level" logger name.
-
-    This string will be used as the first segment in the Logger names.
-    The default is "cherrypy", for example, in which case the Logger names
-    will be of the form::
-
-        cherrypy.error.<appid>
-        cherrypy.access.<appid>
-    """
-
-    def __init__(self, appid=None, logger_root='cherrypy'):
-        self.logger_root = logger_root
-        self.appid = appid
-        if appid is None:
-            self.error_log = logging.getLogger('%s.error' % logger_root)
-            self.access_log = logging.getLogger('%s.access' % logger_root)
-        else:
-            self.error_log = logging.getLogger(
-                '%s.error.%s' % (logger_root, appid))
-            self.access_log = logging.getLogger(
-                '%s.access.%s' % (logger_root, appid))
-        self.error_log.setLevel(logging.INFO)
-        self.access_log.setLevel(logging.INFO)
-
-        # Silence the no-handlers "warning" (stderr write!) in stdlib logging
-        self.error_log.addHandler(NullHandler())
-        self.access_log.addHandler(NullHandler())
-
-        cherrypy.engine.subscribe('graceful', self.reopen_files)
-
-    def reopen_files(self):
-        """Close and reopen all file handlers."""
-        for log in (self.error_log, self.access_log):
-            for h in log.handlers:
-                if isinstance(h, logging.FileHandler):
-                    h.acquire()
-                    h.stream.close()
-                    h.stream = open(h.baseFilename, h.mode)
-                    h.release()
-
-    def error(self, msg='', context='', severity=logging.INFO,
-              traceback=False):
-        """Write the given ``msg`` to the error log.
-
-        This is not just for errors! Applications may call this at any time
-        to log application-specific information.
-
-        If ``traceback`` is True, the traceback of the current exception
-        (if any) will be appended to ``msg``.
-        """
-        exc_info = None
-        if traceback:
-            exc_info = _cperror._exc_info()
-
-        self.error_log.log(
-            severity,
-            ' '.join((self.time(), context, msg)),
-            exc_info=exc_info,
-        )
-
-    def __call__(self, *args, **kwargs):
-        """An alias for ``error``."""
-        return self.error(*args, **kwargs)
-
-    def access(self):
-        """Write to the access log (in Apache/NCSA Combined Log format).
-
-        See the
-        `apache documentation
-        <http://httpd.apache.org/docs/current/logs.html#combined>`_
-        for format details.
-
-        CherryPy calls this automatically for you. Note there are no arguments;
-        it collects the data itself from
-        :class:`cherrypy.request<cherrypy._cprequest.Request>`.
-
-        Like Apache started doing in 2.0.46, non-printable and other special
-        characters in %r (and we expand that to all parts) are escaped using
-        \\xhh sequences, where hh stands for the hexadecimal representation
-        of the raw byte. Exceptions from this rule are " and \\, which are
-        escaped by prepending a backslash, and all whitespace characters,
-        which are written in their C-style notation (\\n, \\t, etc).
-        """
-        request = cherrypy.serving.request
-        remote = request.remote
-        response = cherrypy.serving.response
-        outheaders = response.headers
-        inheaders = request.headers
-        if response.output_status is None:
-            status = '-'
-        else:
-            status = response.output_status.split(b' ', 1)[0]
-            if six.PY3:
-                status = status.decode('ISO-8859-1')
-
-        atoms = {'h': remote.name or remote.ip,
-                 'l': '-',
-                 'u': getattr(request, 'login', None) or '-',
-                 't': self.time(),
-                 'r': request.request_line,
-                 's': status,
-                 'b': dict.get(outheaders, 'Content-Length', '') or '-',
-                 'f': dict.get(inheaders, 'Referer', ''),
-                 'a': dict.get(inheaders, 'User-Agent', ''),
-                 'o': dict.get(inheaders, 'Host', '-'),
-                 'i': request.unique_id,
-                 'z': LazyRfc3339UtcTime(),
-                 }
-        if six.PY3:
-            for k, v in atoms.items():
-                if not isinstance(v, str):
-                    v = str(v)
-                v = v.replace('"', '\\"').encode('utf8')
-                # Fortunately, repr(str) escapes unprintable chars, \n, \t, etc
-                # and backslash for us. All we have to do is strip the quotes.
-                v = repr(v)[2:-1]
-
-                # in python 3.0 the repr of bytes (as returned by encode)
-                # uses double \'s.  But then the logger escapes them yet, again
-                # resulting in quadruple slashes.  Remove the extra one here.
-                v = v.replace('\\\\', '\\')
-
-                # Escape double-quote.
-                atoms[k] = v
-
-            try:
-                self.access_log.log(
-                    logging.INFO, self.access_log_format.format(**atoms))
-            except Exception:
-                self(traceback=True)
-        else:
-            for k, v in atoms.items():
-                if isinstance(v, six.text_type):
-                    v = v.encode('utf8')
-                elif not isinstance(v, str):
-                    v = str(v)
-                # Fortunately, repr(str) escapes unprintable chars, \n, \t, etc
-                # and backslash for us. All we have to do is strip the quotes.
-                v = repr(v)[1:-1]
-                # Escape double-quote.
-                atoms[k] = v.replace('"', '\\"')
-
-            try:
-                self.access_log.log(
-                    logging.INFO, self.access_log_format % atoms)
-            except Exception:
-                self(traceback=True)
-
-    def time(self):
-        """Return now() in Apache Common Log Format (no timezone)."""
-        now = datetime.datetime.now()
-        monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun',
-                      'jul', 'aug', 'sep', 'oct', 'nov', 'dec']
-        month = monthnames[now.month - 1].capitalize()
-        return ('[%02d/%s/%04d:%02d:%02d:%02d]' %
-                (now.day, month, now.year, now.hour, now.minute, now.second))
-
-    def _get_builtin_handler(self, log, key):
-        for h in log.handlers:
-            if getattr(h, '_cpbuiltin', None) == key:
-                return h
-
-    # ------------------------- Screen handlers ------------------------- #
-    def _set_screen_handler(self, log, enable, stream=None):
-        h = self._get_builtin_handler(log, 'screen')
-        if enable:
-            if not h:
-                if stream is None:
-                    stream = sys.stderr
-                h = logging.StreamHandler(stream)
-                h.setFormatter(logfmt)
-                h._cpbuiltin = 'screen'
-                log.addHandler(h)
-        elif h:
-            log.handlers.remove(h)
-
-    @property
-    def screen(self):
-        """Turn stderr/stdout logging on or off.
-
-        If you set this to True, it'll add the appropriate StreamHandler for
-        you. If you set it to False, it will remove the handler.
-        """
-        h = self._get_builtin_handler
-        has_h = h(self.error_log, 'screen') or h(self.access_log, 'screen')
-        return bool(has_h)
-
-    @screen.setter
-    def screen(self, newvalue):
-        self._set_screen_handler(self.error_log, newvalue, stream=sys.stderr)
-        self._set_screen_handler(self.access_log, newvalue, stream=sys.stdout)
-
-    # -------------------------- File handlers -------------------------- #
-
-    def _add_builtin_file_handler(self, log, fname):
-        h = logging.FileHandler(fname)
-        h.setFormatter(logfmt)
-        h._cpbuiltin = 'file'
-        log.addHandler(h)
-
-    def _set_file_handler(self, log, filename):
-        h = self._get_builtin_handler(log, 'file')
-        if filename:
-            if h:
-                if h.baseFilename != os.path.abspath(filename):
-                    h.close()
-                    log.handlers.remove(h)
-                    self._add_builtin_file_handler(log, filename)
-            else:
-                self._add_builtin_file_handler(log, filename)
-        else:
-            if h:
-                h.close()
-                log.handlers.remove(h)
-
-    @property
-    def error_file(self):
-        """The filename for self.error_log.
-
-        If you set this to a string, it'll add the appropriate FileHandler for
-        you. If you set it to ``None`` or ``''``, it will remove the handler.
-        """
-        h = self._get_builtin_handler(self.error_log, 'file')
-        if h:
-            return h.baseFilename
-        return ''
-
-    @error_file.setter
-    def error_file(self, newvalue):
-        self._set_file_handler(self.error_log, newvalue)
-
-    @property
-    def access_file(self):
-        """The filename for self.access_log.
-
-        If you set this to a string, it'll add the appropriate FileHandler for
-        you. If you set it to ``None`` or ``''``, it will remove the handler.
-        """
-        h = self._get_builtin_handler(self.access_log, 'file')
-        if h:
-            return h.baseFilename
-        return ''
-
-    @access_file.setter
-    def access_file(self, newvalue):
-        self._set_file_handler(self.access_log, newvalue)
-
-    # ------------------------- WSGI handlers ------------------------- #
-
-    def _set_wsgi_handler(self, log, enable):
-        h = self._get_builtin_handler(log, 'wsgi')
-        if enable:
-            if not h:
-                h = WSGIErrorHandler()
-                h.setFormatter(logfmt)
-                h._cpbuiltin = 'wsgi'
-                log.addHandler(h)
-        elif h:
-            log.handlers.remove(h)
-
-    @property
-    def wsgi(self):
-        """Write errors to wsgi.errors.
-
-        If you set this to True, it'll add the appropriate
-        :class:`WSGIErrorHandler<cherrypy._cplogging.WSGIErrorHandler>` for you
-        (which writes errors to ``wsgi.errors``).
-        If you set it to False, it will remove the handler.
-        """
-        return bool(self._get_builtin_handler(self.error_log, 'wsgi'))
-
-    @wsgi.setter
-    def wsgi(self, newvalue):
-        self._set_wsgi_handler(self.error_log, newvalue)
-
-
-class WSGIErrorHandler(logging.Handler):
-
-    "A handler class which writes logging records to environ['wsgi.errors']."
-
-    def flush(self):
-        """Flushes the stream."""
-        try:
-            stream = cherrypy.serving.request.wsgi_environ.get('wsgi.errors')
-        except (AttributeError, KeyError):
-            pass
-        else:
-            stream.flush()
-
-    def emit(self, record):
-        """Emit a record."""
-        try:
-            stream = cherrypy.serving.request.wsgi_environ.get('wsgi.errors')
-        except (AttributeError, KeyError):
-            pass
-        else:
-            try:
-                msg = self.format(record)
-                fs = '%s\n'
-                import types
-                # if no unicode support...
-                if not hasattr(types, 'UnicodeType'):
-                    stream.write(fs % msg)
-                else:
-                    try:
-                        stream.write(fs % msg)
-                    except UnicodeError:
-                        stream.write(fs % msg.encode('UTF-8'))
-                self.flush()
-            except Exception:
-                self.handleError(record)
-
-
-class LazyRfc3339UtcTime(object):
-    def __str__(self):
-        """Return now() in RFC3339 UTC Format."""
-        now = datetime.datetime.now()
-        return now.isoformat('T') + 'Z'
diff --git a/libraries/cherrypy/_cpmodpy.py b/libraries/cherrypy/_cpmodpy.py
deleted file mode 100644
index ac91e625..00000000
--- a/libraries/cherrypy/_cpmodpy.py
+++ /dev/null
@@ -1,356 +0,0 @@
-"""Native adapter for serving CherryPy via mod_python
-
-Basic usage:
-
-##########################################
-# Application in a module called myapp.py
-##########################################
-
-import cherrypy
-
-class Root:
-    @cherrypy.expose
-    def index(self):
-        return 'Hi there, Ho there, Hey there'
-
-
-# We will use this method from the mod_python configuration
-# as the entry point to our application
-def setup_server():
-    cherrypy.tree.mount(Root())
-    cherrypy.config.update({'environment': 'production',
-                            'log.screen': False,
-                            'show_tracebacks': False})
-
-##########################################
-# mod_python settings for apache2
-# This should reside in your httpd.conf
-# or a file that will be loaded at
-# apache startup
-##########################################
-
-# Start
-DocumentRoot "/"
-Listen 8080
-LoadModule python_module /usr/lib/apache2/modules/mod_python.so
-
-<Location "/">
-    PythonPath "sys.path+['/path/to/my/application']"
-    SetHandler python-program
-    PythonHandler cherrypy._cpmodpy::handler
-    PythonOption cherrypy.setup myapp::setup_server
-    PythonDebug On
-</Location>
-# End
-
-The actual path to your mod_python.so is dependent on your
-environment. In this case we suppose a global mod_python
-installation on a Linux distribution such as Ubuntu.
-
-We do set the PythonPath configuration setting so that
-your application can be found by from the user running
-the apache2 instance. Of course if your application
-resides in the global site-package this won't be needed.
-
-Then restart apache2 and access http://127.0.0.1:8080
-"""
-
-import io
-import logging
-import os
-import re
-import sys
-
-import six
-
-from more_itertools import always_iterable
-
-import cherrypy
-from cherrypy._cperror import format_exc, bare_error
-from cherrypy.lib import httputil
-
-
-# ------------------------------ Request-handling
-
-
-def setup(req):
-    from mod_python import apache
-
-    # Run any setup functions defined by a "PythonOption cherrypy.setup"
-    # directive.
-    options = req.get_options()
-    if 'cherrypy.setup' in options:
-        for function in options['cherrypy.setup'].split():
-            atoms = function.split('::', 1)
-            if len(atoms) == 1:
-                mod = __import__(atoms[0], globals(), locals())
-            else:
-                modname, fname = atoms
-                mod = __import__(modname, globals(), locals(), [fname])
-                func = getattr(mod, fname)
-                func()
-
-    cherrypy.config.update({'log.screen': False,
-                            'tools.ignore_headers.on': True,
-                            'tools.ignore_headers.headers': ['Range'],
-                            })
-
-    engine = cherrypy.engine
-    if hasattr(engine, 'signal_handler'):
-        engine.signal_handler.unsubscribe()
-    if hasattr(engine, 'console_control_handler'):
-        engine.console_control_handler.unsubscribe()
-    engine.autoreload.unsubscribe()
-    cherrypy.server.unsubscribe()
-
-    @engine.subscribe('log')
-    def _log(msg, level):
-        newlevel = apache.APLOG_ERR
-        if logging.DEBUG >= level:
-            newlevel = apache.APLOG_DEBUG
-        elif logging.INFO >= level:
-            newlevel = apache.APLOG_INFO
-        elif logging.WARNING >= level:
-            newlevel = apache.APLOG_WARNING
-        # On Windows, req.server is required or the msg will vanish. See
-        # http://www.modpython.org/pipermail/mod_python/2003-October/014291.html
-        # Also, "When server is not specified...LogLevel does not apply..."
-        apache.log_error(msg, newlevel, req.server)
-
-    engine.start()
-
-    def cherrypy_cleanup(data):
-        engine.exit()
-    try:
-        # apache.register_cleanup wasn't available until 3.1.4.
-        apache.register_cleanup(cherrypy_cleanup)
-    except AttributeError:
-        req.server.register_cleanup(req, cherrypy_cleanup)
-
-
-class _ReadOnlyRequest:
-    expose = ('read', 'readline', 'readlines')
-
-    def __init__(self, req):
-        for method in self.expose:
-            self.__dict__[method] = getattr(req, method)
-
-
-recursive = False
-
-_isSetUp = False
-
-
-def handler(req):
-    from mod_python import apache
-    try:
-        global _isSetUp
-        if not _isSetUp:
-            setup(req)
-            _isSetUp = True
-
-        # Obtain a Request object from CherryPy
-        local = req.connection.local_addr
-        local = httputil.Host(
-            local[0], local[1], req.connection.local_host or '')
-        remote = req.connection.remote_addr
-        remote = httputil.Host(
-            remote[0], remote[1], req.connection.remote_host or '')
-
-        scheme = req.parsed_uri[0] or 'http'
-        req.get_basic_auth_pw()
-
-        try:
-            # apache.mpm_query only became available in mod_python 3.1
-            q = apache.mpm_query
-            threaded = q(apache.AP_MPMQ_IS_THREADED)
-            forked = q(apache.AP_MPMQ_IS_FORKED)
-        except AttributeError:
-            bad_value = ("You must provide a PythonOption '%s', "
-                         "either 'on' or 'off', when running a version "
-                         'of mod_python < 3.1')
-
-            options = req.get_options()
-
-            threaded = options.get('multithread', '').lower()
-            if threaded == 'on':
-                threaded = True
-            elif threaded == 'off':
-                threaded = False
-            else:
-                raise ValueError(bad_value % 'multithread')
-
-            forked = options.get('multiprocess', '').lower()
-            if forked == 'on':
-                forked = True
-            elif forked == 'off':
-                forked = False
-            else:
-                raise ValueError(bad_value % 'multiprocess')
-
-        sn = cherrypy.tree.script_name(req.uri or '/')
-        if sn is None:
-            send_response(req, '404 Not Found', [], '')
-        else:
-            app = cherrypy.tree.apps[sn]
-            method = req.method
-            path = req.uri
-            qs = req.args or ''
-            reqproto = req.protocol
-            headers = list(six.iteritems(req.headers_in))
-            rfile = _ReadOnlyRequest(req)
-            prev = None
-
-            try:
-                redirections = []
-                while True:
-                    request, response = app.get_serving(local, remote, scheme,
-                                                        'HTTP/1.1')
-                    request.login = req.user
-                    request.multithread = bool(threaded)
-                    request.multiprocess = bool(forked)
-                    request.app = app
-                    request.prev = prev
-
-                    # Run the CherryPy Request object and obtain the response
-                    try:
-                        request.run(method, path, qs, reqproto, headers, rfile)
-                        break
-                    except cherrypy.InternalRedirect:
-                        ir = sys.exc_info()[1]
-                        app.release_serving()
-                        prev = request
-
-                        if not recursive:
-                            if ir.path in redirections:
-                                raise RuntimeError(
-                                    'InternalRedirector visited the same URL '
-                                    'twice: %r' % ir.path)
-                            else:
-                                # Add the *previous* path_info + qs to
-                                # redirections.
-                                if qs:
-                                    qs = '?' + qs
-                                redirections.append(sn + path + qs)
-
-                        # Munge environment and try again.
-                        method = 'GET'
-                        path = ir.path
-                        qs = ir.query_string
-                        rfile = io.BytesIO()
-
-                send_response(
-                    req, response.output_status, response.header_list,
-                    response.body, response.stream)
-            finally:
-                app.release_serving()
-    except Exception:
-        tb = format_exc()
-        cherrypy.log(tb, 'MOD_PYTHON', severity=logging.ERROR)
-        s, h, b = bare_error()
-        send_response(req, s, h, b)
-    return apache.OK
-
-
-def send_response(req, status, headers, body, stream=False):
-    # Set response status
-    req.status = int(status[:3])
-
-    # Set response headers
-    req.content_type = 'text/plain'
-    for header, value in headers:
-        if header.lower() == 'content-type':
-            req.content_type = value
-            continue
-        req.headers_out.add(header, value)
-
-    if stream:
-        # Flush now so the status and headers are sent immediately.
-        req.flush()
-
-    # Set response body
-    for seg in always_iterable(body):
-        req.write(seg)
-
-
-# --------------- Startup tools for CherryPy + mod_python --------------- #
-try:
-    import subprocess
-
-    def popen(fullcmd):
-        p = subprocess.Popen(fullcmd, shell=True,
-                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
-                             close_fds=True)
-        return p.stdout
-except ImportError:
-    def popen(fullcmd):
-        pipein, pipeout = os.popen4(fullcmd)
-        return pipeout
-
-
-def read_process(cmd, args=''):
-    fullcmd = '%s %s' % (cmd, args)
-    pipeout = popen(fullcmd)
-    try:
-        firstline = pipeout.readline()
-        cmd_not_found = re.search(
-            b'(not recognized|No such file|not found)',
-            firstline,
-            re.IGNORECASE
-        )
-        if cmd_not_found:
-            raise IOError('%s must be on your system path.' % cmd)
-        output = firstline + pipeout.read()
-    finally:
-        pipeout.close()
-    return output
-
-
-class ModPythonServer(object):
-
-    template = """
-# Apache2 server configuration file for running CherryPy with mod_python.
-
-DocumentRoot "/"
-Listen %(port)s
-LoadModule python_module modules/mod_python.so
-
-<Location %(loc)s>
-    SetHandler python-program
-    PythonHandler %(handler)s
-    PythonDebug On
-%(opts)s
-</Location>
-"""
-
-    def __init__(self, loc='/', port=80, opts=None, apache_path='apache',
-                 handler='cherrypy._cpmodpy::handler'):
-        self.loc = loc
-        self.port = port
-        self.opts = opts
-        self.apache_path = apache_path
-        self.handler = handler
-
-    def start(self):
-        opts = ''.join(['    PythonOption %s %s\n' % (k, v)
-                        for k, v in self.opts])
-        conf_data = self.template % {'port': self.port,
-                                     'loc': self.loc,
-                                     'opts': opts,
-                                     'handler': self.handler,
-                                     }
-
-        mpconf = os.path.join(os.path.dirname(__file__), 'cpmodpy.conf')
-        f = open(mpconf, 'wb')
-        try:
-            f.write(conf_data)
-        finally:
-            f.close()
-
-        response = read_process(self.apache_path, '-k start -f %s' % mpconf)
-        self.ready = True
-        return response
-
-    def stop(self):
-        os.popen('apache -k stop')
-        self.ready = False
diff --git a/libraries/cherrypy/_cpnative_server.py b/libraries/cherrypy/_cpnative_server.py
deleted file mode 100644
index 55653c35..00000000
--- a/libraries/cherrypy/_cpnative_server.py
+++ /dev/null
@@ -1,160 +0,0 @@
-"""Native adapter for serving CherryPy via its builtin server."""
-
-import logging
-import sys
-import io
-
-import cheroot.server
-
-import cherrypy
-from cherrypy._cperror import format_exc, bare_error
-from cherrypy.lib import httputil
-
-
-class NativeGateway(cheroot.server.Gateway):
-    """Native gateway implementation allowing to bypass WSGI."""
-
-    recursive = False
-
-    def respond(self):
-        """Obtain response from CherryPy machinery and then send it."""
-        req = self.req
-        try:
-            # Obtain a Request object from CherryPy
-            local = req.server.bind_addr
-            local = httputil.Host(local[0], local[1], '')
-            remote = req.conn.remote_addr, req.conn.remote_port
-            remote = httputil.Host(remote[0], remote[1], '')
-
-            scheme = req.scheme
-            sn = cherrypy.tree.script_name(req.uri or '/')
-            if sn is None:
-                self.send_response('404 Not Found', [], [''])
-            else:
-                app = cherrypy.tree.apps[sn]
-                method = req.method
-                path = req.path
-                qs = req.qs or ''
-                headers = req.inheaders.items()
-                rfile = req.rfile
-                prev = None
-
-                try:
-                    redirections = []
-                    while True:
-                        request, response = app.get_serving(
-                            local, remote, scheme, 'HTTP/1.1')
-                        request.multithread = True
-                        request.multiprocess = False
-                        request.app = app
-                        request.prev = prev
-
-                        # Run the CherryPy Request object and obtain the
-                        # response
-                        try:
-                            request.run(method, path, qs,
-                                        req.request_protocol, headers, rfile)
-                            break
-                        except cherrypy.InternalRedirect:
-                            ir = sys.exc_info()[1]
-                            app.release_serving()
-                            prev = request
-
-                            if not self.recursive:
-                                if ir.path in redirections:
-                                    raise RuntimeError(
-                                        'InternalRedirector visited the same '
-                                        'URL twice: %r' % ir.path)
-                                else:
-                                    # Add the *previous* path_info + qs to
-                                    # redirections.
-                                    if qs:
-                                        qs = '?' + qs
-                                    redirections.append(sn + path + qs)
-
-                            # Munge environment and try again.
-                            method = 'GET'
-                            path = ir.path
-                            qs = ir.query_string
-                            rfile = io.BytesIO()
-
-                    self.send_response(
-                        response.output_status, response.header_list,
-                        response.body)
-                finally:
-                    app.release_serving()
-        except Exception:
-            tb = format_exc()
-            # print tb
-            cherrypy.log(tb, 'NATIVE_ADAPTER', severity=logging.ERROR)
-            s, h, b = bare_error()
-            self.send_response(s, h, b)
-
-    def send_response(self, status, headers, body):
-        """Send response to HTTP request."""
-        req = self.req
-
-        # Set response status
-        req.status = status or b'500 Server Error'
-
-        # Set response headers
-        for header, value in headers:
-            req.outheaders.append((header, value))
-        if (req.ready and not req.sent_headers):
-            req.sent_headers = True
-            req.send_headers()
-
-        # Set response body
-        for seg in body:
-            req.write(seg)
-
-
-class CPHTTPServer(cheroot.server.HTTPServer):
-    """Wrapper for cheroot.server.HTTPServer.
-
-    cheroot has been designed to not reference CherryPy in any way,
-    so that it can be used in other frameworks and applications.
-    Therefore, we wrap it here, so we can apply some attributes
-    from config -> cherrypy.server -> HTTPServer.
-    """
-
-    def __init__(self, server_adapter=cherrypy.server):
-        """Initialize CPHTTPServer."""
-        self.server_adapter = server_adapter
-
-        server_name = (self.server_adapter.socket_host or
-                       self.server_adapter.socket_file or
-                       None)
-
-        cheroot.server.HTTPServer.__init__(
-            self, server_adapter.bind_addr, NativeGateway,
-            minthreads=server_adapter.thread_pool,
-            maxthreads=server_adapter.thread_pool_max,
-            server_name=server_name)
-
-        self.max_request_header_size = (
-            self.server_adapter.max_request_header_size or 0)
-        self.max_request_body_size = (
-            self.server_adapter.max_request_body_size or 0)
-        self.request_queue_size = self.server_adapter.socket_queue_size
-        self.timeout = self.server_adapter.socket_timeout
-        self.shutdown_timeout = self.server_adapter.shutdown_timeout
-        self.protocol = self.server_adapter.protocol_version
-        self.nodelay = self.server_adapter.nodelay
-
-        ssl_module = self.server_adapter.ssl_module or 'pyopenssl'
-        if self.server_adapter.ssl_context:
-            adapter_class = cheroot.server.get_ssl_adapter_class(ssl_module)
-            self.ssl_adapter = adapter_class(
-                self.server_adapter.ssl_certificate,
-                self.server_adapter.ssl_private_key,
-                self.server_adapter.ssl_certificate_chain,
-                self.server_adapter.ssl_ciphers)
-            self.ssl_adapter.context = self.server_adapter.ssl_context
-        elif self.server_adapter.ssl_certificate:
-            adapter_class = cheroot.server.get_ssl_adapter_class(ssl_module)
-            self.ssl_adapter = adapter_class(
-                self.server_adapter.ssl_certificate,
-                self.server_adapter.ssl_private_key,
-                self.server_adapter.ssl_certificate_chain,
-                self.server_adapter.ssl_ciphers)
diff --git a/libraries/cherrypy/_cpreqbody.py b/libraries/cherrypy/_cpreqbody.py
deleted file mode 100644
index 893fe5f5..00000000
--- a/libraries/cherrypy/_cpreqbody.py
+++ /dev/null
@@ -1,1000 +0,0 @@
-"""Request body processing for CherryPy.
-
-.. versionadded:: 3.2
-
-Application authors have complete control over the parsing of HTTP request
-entities. In short,
-:attr:`cherrypy.request.body<cherrypy._cprequest.Request.body>`
-is now always set to an instance of
-:class:`RequestBody<cherrypy._cpreqbody.RequestBody>`,
-and *that* class is a subclass of :class:`Entity<cherrypy._cpreqbody.Entity>`.
-
-When an HTTP request includes an entity body, it is often desirable to
-provide that information to applications in a form other than the raw bytes.
-Different content types demand different approaches. Examples:
-
- * For a GIF file, we want the raw bytes in a stream.
- * An HTML form is better parsed into its component fields, and each text field
-   decoded from bytes to unicode.
- * A JSON body should be deserialized into a Python dict or list.
-
-When the request contains a Content-Type header, the media type is used as a
-key to look up a value in the
-:attr:`request.body.processors<cherrypy._cpreqbody.Entity.processors>` dict.
-If the full media
-type is not found, then the major type is tried; for example, if no processor
-is found for the 'image/jpeg' type, then we look for a processor for the
-'image' types altogether. If neither the full type nor the major type has a
-matching processor, then a default processor is used
-(:func:`default_proc<cherrypy._cpreqbody.Entity.default_proc>`). For most
-types, this means no processing is done, and the body is left unread as a
-raw byte stream. Processors are configurable in an 'on_start_resource' hook.
-
-Some processors, especially those for the 'text' types, attempt to decode bytes
-to unicode. If the Content-Type request header includes a 'charset' parameter,
-this is used to decode the entity. Otherwise, one or more default charsets may
-be attempted, although this decision is up to each processor. If a processor
-successfully decodes an Entity or Part, it should set the
-:attr:`charset<cherrypy._cpreqbody.Entity.charset>` attribute
-on the Entity or Part to the name of the successful charset, so that
-applications can easily re-encode or transcode the value if they wish.
-
-If the Content-Type of the request entity is of major type 'multipart', then
-the above parsing process, and possibly a decoding process, is performed for
-each part.
-
-For both the full entity and multipart parts, a Content-Disposition header may
-be used to fill :attr:`name<cherrypy._cpreqbody.Entity.name>` and
-:attr:`filename<cherrypy._cpreqbody.Entity.filename>` attributes on the
-request.body or the Part.
-
-.. _custombodyprocessors:
-
-Custom Processors
-=================
-
-You can add your own processors for any specific or major MIME type. Simply add
-it to the :attr:`processors<cherrypy._cprequest.Entity.processors>` dict in a
-hook/tool that runs at ``on_start_resource`` or ``before_request_body``.
-Here's the built-in JSON tool for an example::
-
-    def json_in(force=True, debug=False):
-        request = cherrypy.serving.request
-        def json_processor(entity):
-            '''Read application/json data into request.json.'''
-            if not entity.headers.get("Content-Length", ""):
-                raise cherrypy.HTTPError(411)
-
-            body = entity.fp.read()
-            try:
-                request.json = json_decode(body)
-            except ValueError:
-                raise cherrypy.HTTPError(400, 'Invalid JSON document')
-        if force:
-            request.body.processors.clear()
-            request.body.default_proc = cherrypy.HTTPError(
-                415, 'Expected an application/json content type')
-        request.body.processors['application/json'] = json_processor
-
-We begin by defining a new ``json_processor`` function to stick in the
-``processors`` dictionary. All processor functions take a single argument,
-the ``Entity`` instance they are to process. It will be called whenever a
-request is received (for those URI's where the tool is turned on) which
-has a ``Content-Type`` of "application/json".
-
-First, it checks for a valid ``Content-Length`` (raising 411 if not valid),
-then reads the remaining bytes on the socket. The ``fp`` object knows its
-own length, so it won't hang waiting for data that never arrives. It will
-return when all data has been read. Then, we decode those bytes using
-Python's built-in ``json`` module, and stick the decoded result onto
-``request.json`` . If it cannot be decoded, we raise 400.
-
-If the "force" argument is True (the default), the ``Tool`` clears the
-``processors`` dict so that request entities of other ``Content-Types``
-aren't parsed at all. Since there's no entry for those invalid MIME
-types, the ``default_proc`` method of ``cherrypy.request.body`` is
-called. But this does nothing by default (usually to provide the page
-handler an opportunity to handle it.)
-But in our case, we want to raise 415, so we replace
-``request.body.default_proc``
-with the error (``HTTPError`` instances, when called, raise themselves).
-
-If we were defining a custom processor, we can do so without making a ``Tool``.
-Just add the config entry::
-
-    request.body.processors = {'application/json': json_processor}
-
-Note that you can only replace the ``processors`` dict wholesale this way,
-not update the existing one.
-"""
-
-try:
-    from io import DEFAULT_BUFFER_SIZE
-except ImportError:
-    DEFAULT_BUFFER_SIZE = 8192
-import re
-import sys
-import tempfile
-try:
-    from urllib import unquote_plus
-except ImportError:
-    def unquote_plus(bs):
-        """Bytes version of urllib.parse.unquote_plus."""
-        bs = bs.replace(b'+', b' ')
-        atoms = bs.split(b'%')
-        for i in range(1, len(atoms)):
-            item = atoms[i]
-            try:
-                pct = int(item[:2], 16)
-                atoms[i] = bytes([pct]) + item[2:]
-            except ValueError:
-                pass
-        return b''.join(atoms)
-
-import six
-import cheroot.server
-
-import cherrypy
-from cherrypy._cpcompat import ntou, unquote
-from cherrypy.lib import httputil
-
-
-# ------------------------------- Processors -------------------------------- #
-
-def process_urlencoded(entity):
-    """Read application/x-www-form-urlencoded data into entity.params."""
-    qs = entity.fp.read()
-    for charset in entity.attempt_charsets:
-        try:
-            params = {}
-            for aparam in qs.split(b'&'):
-                for pair in aparam.split(b';'):
-                    if not pair:
-                        continue
-
-                    atoms = pair.split(b'=', 1)
-                    if len(atoms) == 1:
-                        atoms.append(b'')
-
-                    key = unquote_plus(atoms[0]).decode(charset)
-                    value = unquote_plus(atoms[1]).decode(charset)
-
-                    if key in params:
-                        if not isinstance(params[key], list):
-                            params[key] = [params[key]]
-                        params[key].append(value)
-                    else:
-                        params[key] = value
-        except UnicodeDecodeError:
-            pass
-        else:
-            entity.charset = charset
-            break
-    else:
-        raise cherrypy.HTTPError(
-            400, 'The request entity could not be decoded. The following '
-            'charsets were attempted: %s' % repr(entity.attempt_charsets))
-
-    # Now that all values have been successfully parsed and decoded,
-    # apply them to the entity.params dict.
-    for key, value in params.items():
-        if key in entity.params:
-            if not isinstance(entity.params[key], list):
-                entity.params[key] = [entity.params[key]]
-            entity.params[key].append(value)
-        else:
-            entity.params[key] = value
-
-
-def process_multipart(entity):
-    """Read all multipart parts into entity.parts."""
-    ib = ''
-    if 'boundary' in entity.content_type.params:
-        # http://tools.ietf.org/html/rfc2046#section-5.1.1
-        # "The grammar for parameters on the Content-type field is such that it
-        # is often necessary to enclose the boundary parameter values in quotes
-        # on the Content-type line"
-        ib = entity.content_type.params['boundary'].strip('"')
-
-    if not re.match('^[ -~]{0,200}[!-~]$', ib):
-        raise ValueError('Invalid boundary in multipart form: %r' % (ib,))
-
-    ib = ('--' + ib).encode('ascii')
-
-    # Find the first marker
-    while True:
-        b = entity.readline()
-        if not b:
-            return
-
-        b = b.strip()
-        if b == ib:
-            break
-
-    # Read all parts
-    while True:
-        part = entity.part_class.from_fp(entity.fp, ib)
-        entity.parts.append(part)
-        part.process()
-        if part.fp.done:
-            break
-
-
-def process_multipart_form_data(entity):
-    """Read all multipart/form-data parts into entity.parts or entity.params.
-    """
-    process_multipart(entity)
-
-    kept_parts = []
-    for part in entity.parts:
-        if part.name is None:
-            kept_parts.append(part)
-        else:
-            if part.filename is None:
-                # It's a regular field
-                value = part.fullvalue()
-            else:
-                # It's a file upload. Retain the whole part so consumer code
-                # has access to its .file and .filename attributes.
-                value = part
-
-            if part.name in entity.params:
-                if not isinstance(entity.params[part.name], list):
-                    entity.params[part.name] = [entity.params[part.name]]
-                entity.params[part.name].append(value)
-            else:
-                entity.params[part.name] = value
-
-    entity.parts = kept_parts
-
-
-def _old_process_multipart(entity):
-    """The behavior of 3.2 and lower. Deprecated and will be changed in 3.3."""
-    process_multipart(entity)
-
-    params = entity.params
-
-    for part in entity.parts:
-        if part.name is None:
-            key = ntou('parts')
-        else:
-            key = part.name
-
-        if part.filename is None:
-            # It's a regular field
-            value = part.fullvalue()
-        else:
-            # It's a file upload. Retain the whole part so consumer code
-            # has access to its .file and .filename attributes.
-            value = part
-
-        if key in params:
-            if not isinstance(params[key], list):
-                params[key] = [params[key]]
-            params[key].append(value)
-        else:
-            params[key] = value
-
-
-# -------------------------------- Entities --------------------------------- #
-class Entity(object):
-
-    """An HTTP request body, or MIME multipart body.
-
-    This class collects information about the HTTP request entity. When a
-    given entity is of MIME type "multipart", each part is parsed into its own
-    Entity instance, and the set of parts stored in
-    :attr:`entity.parts<cherrypy._cpreqbody.Entity.parts>`.
-
-    Between the ``before_request_body`` and ``before_handler`` tools, CherryPy
-    tries to process the request body (if any) by calling
-    :func:`request.body.process<cherrypy._cpreqbody.RequestBody.process>`.
-    This uses the ``content_type`` of the Entity to look up a suitable
-    processor in
-    :attr:`Entity.processors<cherrypy._cpreqbody.Entity.processors>`,
-    a dict.
-    If a matching processor cannot be found for the complete Content-Type,
-    it tries again using the major type. For example, if a request with an
-    entity of type "image/jpeg" arrives, but no processor can be found for
-    that complete type, then one is sought for the major type "image". If a
-    processor is still not found, then the
-    :func:`default_proc<cherrypy._cpreqbody.Entity.default_proc>` method
-    of the Entity is called (which does nothing by default; you can
-    override this too).
-
-    CherryPy includes processors for the "application/x-www-form-urlencoded"
-    type, the "multipart/form-data" type, and the "multipart" major type.
-    CherryPy 3.2 processes these types almost exactly as older versions.
-    Parts are passed as arguments to the page handler using their
-    ``Content-Disposition.name`` if given, otherwise in a generic "parts"
-    argument. Each such part is either a string, or the
-    :class:`Part<cherrypy._cpreqbody.Part>` itself if it's a file. (In this
-    case it will have ``file`` and ``filename`` attributes, or possibly a
-    ``value`` attribute). Each Part is itself a subclass of
-    Entity, and has its own ``process`` method and ``processors`` dict.
-
-    There is a separate processor for the "multipart" major type which is more
-    flexible, and simply stores all multipart parts in
-    :attr:`request.body.parts<cherrypy._cpreqbody.Entity.parts>`. You can
-    enable it with::
-
-        cherrypy.request.body.processors['multipart'] = \
-            _cpreqbody.process_multipart
-
-    in an ``on_start_resource`` tool.
-    """
-
-    # http://tools.ietf.org/html/rfc2046#section-4.1.2:
-    # "The default character set, which must be assumed in the
-    # absence of a charset parameter, is US-ASCII."
-    # However, many browsers send data in utf-8 with no charset.
-    attempt_charsets = ['utf-8']
-    r"""A list of strings, each of which should be a known encoding.
-
-    When the Content-Type of the request body warrants it, each of the given
-    encodings will be tried in order. The first one to successfully decode the
-    entity without raising an error is stored as
-    :attr:`entity.charset<cherrypy._cpreqbody.Entity.charset>`. This defaults
-    to ``['utf-8']`` (plus 'ISO-8859-1' for "text/\*" types, as required by
-    `HTTP/1.1
-    <http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1>`_),
-    but ``['us-ascii', 'utf-8']`` for multipart parts.
-    """
-
-    charset = None
-    """The successful decoding; see "attempt_charsets" above."""
-
-    content_type = None
-    """The value of the Content-Type request header.
-
-    If the Entity is part of a multipart payload, this will be the Content-Type
-    given in the MIME headers for this part.
-    """
-
-    default_content_type = 'application/x-www-form-urlencoded'
-    """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
-    request body not being read or parsed at all. This is by design; a missing
-    ``Content-Type`` header in the HTTP request entity is an error at best,
-    and a security hole at worst. For multipart parts, however, the MIME spec
-    declares that a part with no Content-Type defaults to "text/plain"
-    (see :class:`Part<cherrypy._cpreqbody.Part>`).
-    """
-
-    filename = None
-    """The ``Content-Disposition.filename`` header, if available."""
-
-    fp = None
-    """The readable socket file object."""
-
-    headers = None
-    """A dict of request/multipart header names and values.
-
-    This is a copy of the ``request.headers`` for the ``request.body``;
-    for multipart parts, it is the set of headers for that part.
-    """
-
-    length = None
-    """The value of the ``Content-Length`` header, if provided."""
-
-    name = None
-    """The "name" parameter of the ``Content-Disposition`` header, if any."""
-
-    params = None
-    """
-    If the request Content-Type is 'application/x-www-form-urlencoded' or
-    multipart, this will be a dict of the params pulled from the entity
-    body; that is, it will be the portion of request.params that come
-    from the message body (sometimes called "POST params", although they
-    can be sent with various HTTP method verbs). This value is set between
-    the 'before_request_body' and 'before_handler' hooks (assuming that
-    process_request_body is True)."""
-
-    processors = {'application/x-www-form-urlencoded': process_urlencoded,
-                  'multipart/form-data': process_multipart_form_data,
-                  'multipart': process_multipart,
-                  }
-    """A dict of Content-Type names to processor methods."""
-
-    parts = None
-    """A list of Part instances if ``Content-Type`` is of major type
-    "multipart"."""
-
-    part_class = None
-    """The class used for multipart parts.
-
-    You can replace this with custom subclasses to alter the processing of
-    multipart parts.
-    """
-
-    def __init__(self, fp, headers, params=None, parts=None):
-        # Make an instance-specific copy of the class processors
-        # so Tools, etc. can replace them per-request.
-        self.processors = self.processors.copy()
-
-        self.fp = fp
-        self.headers = headers
-
-        if params is None:
-            params = {}
-        self.params = params
-
-        if parts is None:
-            parts = []
-        self.parts = parts
-
-        # Content-Type
-        self.content_type = headers.elements('Content-Type')
-        if self.content_type:
-            self.content_type = self.content_type[0]
-        else:
-            self.content_type = httputil.HeaderElement.from_str(
-                self.default_content_type)
-
-        # Copy the class 'attempt_charsets', prepending any Content-Type
-        # charset
-        dec = self.content_type.params.get('charset', None)
-        if dec:
-            self.attempt_charsets = [dec] + [c for c in self.attempt_charsets
-                                             if c != dec]
-        else:
-            self.attempt_charsets = self.attempt_charsets[:]
-
-        # Length
-        self.length = None
-        clen = headers.get('Content-Length', None)
-        # If Transfer-Encoding is 'chunked', ignore any Content-Length.
-        if (
-            clen is not None and
-            'chunked' not in headers.get('Transfer-Encoding', '')
-        ):
-            try:
-                self.length = int(clen)
-            except ValueError:
-                pass
-
-        # Content-Disposition
-        self.name = None
-        self.filename = None
-        disp = headers.elements('Content-Disposition')
-        if disp:
-            disp = disp[0]
-            if 'name' in disp.params:
-                self.name = disp.params['name']
-                if self.name.startswith('"') and self.name.endswith('"'):
-                    self.name = self.name[1:-1]
-            if 'filename' in disp.params:
-                self.filename = disp.params['filename']
-                if (
-                    self.filename.startswith('"') and
-                    self.filename.endswith('"')
-                ):
-                    self.filename = self.filename[1:-1]
-            if 'filename*' in disp.params:
-                # @see https://tools.ietf.org/html/rfc5987
-                encoding, lang, filename = disp.params['filename*'].split("'")
-                self.filename = unquote(str(filename), encoding)
-
-    def read(self, size=None, fp_out=None):
-        return self.fp.read(size, fp_out)
-
-    def readline(self, size=None):
-        return self.fp.readline(size)
-
-    def readlines(self, sizehint=None):
-        return self.fp.readlines(sizehint)
-
-    def __iter__(self):
-        return self
-
-    def __next__(self):
-        line = self.readline()
-        if not line:
-            raise StopIteration
-        return line
-
-    def next(self):
-        return self.__next__()
-
-    def read_into_file(self, fp_out=None):
-        """Read the request body into fp_out (or make_file() if None).
-
-        Return fp_out.
-        """
-        if fp_out is None:
-            fp_out = self.make_file()
-        self.read(fp_out=fp_out)
-        return fp_out
-
-    def make_file(self):
-        """Return a file-like object into which the request body will be read.
-
-        By default, this will return a TemporaryFile. Override as needed.
-        See also :attr:`cherrypy._cpreqbody.Part.maxrambytes`."""
-        return tempfile.TemporaryFile()
-
-    def fullvalue(self):
-        """Return this entity as a string, whether stored in a file or not."""
-        if self.file:
-            # It was stored in a tempfile. Read it.
-            self.file.seek(0)
-            value = self.file.read()
-            self.file.seek(0)
-        else:
-            value = self.value
-        value = self.decode_entity(value)
-        return value
-
-    def decode_entity(self, value):
-        """Return a given byte encoded value as a string"""
-        for charset in self.attempt_charsets:
-            try:
-                value = value.decode(charset)
-            except UnicodeDecodeError:
-                pass
-            else:
-                self.charset = charset
-                return value
-        else:
-            raise cherrypy.HTTPError(
-                400,
-                'The request entity could not be decoded. The following '
-                'charsets were attempted: %s' % repr(self.attempt_charsets)
-            )
-
-    def process(self):
-        """Execute the best-match processor for the given media type."""
-        proc = None
-        ct = self.content_type.value
-        try:
-            proc = self.processors[ct]
-        except KeyError:
-            toptype = ct.split('/', 1)[0]
-            try:
-                proc = self.processors[toptype]
-            except KeyError:
-                pass
-        if proc is None:
-            self.default_proc()
-        else:
-            proc(self)
-
-    def default_proc(self):
-        """Called if a more-specific processor is not found for the
-        ``Content-Type``.
-        """
-        # Leave the fp alone for someone else to read. This works fine
-        # for request.body, but the Part subclasses need to override this
-        # so they can move on to the next part.
-        pass
-
-
-class Part(Entity):
-
-    """A MIME part entity, part of a multipart entity."""
-
-    # "The default character set, which must be assumed in the absence of a
-    # charset parameter, is US-ASCII."
-    attempt_charsets = ['us-ascii', 'utf-8']
-    r"""A list of strings, each of which should be a known encoding.
-
-    When the Content-Type of the request body warrants it, each of the given
-    encodings will be tried in order. The first one to successfully decode the
-    entity without raising an error is stored as
-    :attr:`entity.charset<cherrypy._cpreqbody.Entity.charset>`. This defaults
-    to ``['utf-8']`` (plus 'ISO-8859-1' for "text/\*" types, as required by
-    `HTTP/1.1
-    <http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1>`_),
-    but ``['us-ascii', 'utf-8']`` for multipart parts.
-    """
-
-    boundary = None
-    """The MIME multipart boundary."""
-
-    default_content_type = 'text/plain'
-    """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
-    request body not being read or parsed at all. This is by design; a missing
-    ``Content-Type`` header in the HTTP request entity is an error at best,
-    and a security hole at worst. For multipart parts, however (this class),
-    the MIME spec declares that a part with no Content-Type defaults to
-    "text/plain".
-    """
-
-    # This is the default in stdlib cgi. We may want to increase it.
-    maxrambytes = 1000
-    """The threshold of bytes after which point the ``Part`` will store
-    its data in a file (generated by
-    :func:`make_file<cherrypy._cprequest.Entity.make_file>`)
-    instead of a string. Defaults to 1000, just like the :mod:`cgi`
-    module in Python's standard library.
-    """
-
-    def __init__(self, fp, headers, boundary):
-        Entity.__init__(self, fp, headers)
-        self.boundary = boundary
-        self.file = None
-        self.value = None
-
-    @classmethod
-    def from_fp(cls, fp, boundary):
-        headers = cls.read_headers(fp)
-        return cls(fp, headers, boundary)
-
-    @classmethod
-    def read_headers(cls, fp):
-        headers = httputil.HeaderMap()
-        while True:
-            line = fp.readline()
-            if not line:
-                # No more data--illegal end of headers
-                raise EOFError('Illegal end of headers.')
-
-            if line == b'\r\n':
-                # Normal end of headers
-                break
-            if not line.endswith(b'\r\n'):
-                raise ValueError('MIME requires CRLF terminators: %r' % line)
-
-            if line[0] in b' \t':
-                # It's a continuation line.
-                v = line.strip().decode('ISO-8859-1')
-            else:
-                k, v = line.split(b':', 1)
-                k = k.strip().decode('ISO-8859-1')
-                v = v.strip().decode('ISO-8859-1')
-
-            existing = headers.get(k)
-            if existing:
-                v = ', '.join((existing, v))
-            headers[k] = v
-
-        return headers
-
-    def read_lines_to_boundary(self, fp_out=None):
-        """Read bytes from self.fp and return or write them to a file.
-
-        If the 'fp_out' argument is None (the default), all bytes read are
-        returned in a single byte string.
-
-        If the 'fp_out' argument is not None, it must be a file-like
-        object that supports the 'write' method; all bytes read will be
-        written to the fp, and that fp is returned.
-        """
-        endmarker = self.boundary + b'--'
-        delim = b''
-        prev_lf = True
-        lines = []
-        seen = 0
-        while True:
-            line = self.fp.readline(1 << 16)
-            if not line:
-                raise EOFError('Illegal end of multipart body.')
-            if line.startswith(b'--') and prev_lf:
-                strippedline = line.strip()
-                if strippedline == self.boundary:
-                    break
-                if strippedline == endmarker:
-                    self.fp.finish()
-                    break
-
-            line = delim + line
-
-            if line.endswith(b'\r\n'):
-                delim = b'\r\n'
-                line = line[:-2]
-                prev_lf = True
-            elif line.endswith(b'\n'):
-                delim = b'\n'
-                line = line[:-1]
-                prev_lf = True
-            else:
-                delim = b''
-                prev_lf = False
-
-            if fp_out is None:
-                lines.append(line)
-                seen += len(line)
-                if seen > self.maxrambytes:
-                    fp_out = self.make_file()
-                    for line in lines:
-                        fp_out.write(line)
-            else:
-                fp_out.write(line)
-
-        if fp_out is None:
-            result = b''.join(lines)
-            return result
-        else:
-            fp_out.seek(0)
-            return fp_out
-
-    def default_proc(self):
-        """Called if a more-specific processor is not found for the
-        ``Content-Type``.
-        """
-        if self.filename:
-            # Always read into a file if a .filename was given.
-            self.file = self.read_into_file()
-        else:
-            result = self.read_lines_to_boundary()
-            if isinstance(result, bytes):
-                self.value = result
-            else:
-                self.file = result
-
-    def read_into_file(self, fp_out=None):
-        """Read the request body into fp_out (or make_file() if None).
-
-        Return fp_out.
-        """
-        if fp_out is None:
-            fp_out = self.make_file()
-        self.read_lines_to_boundary(fp_out=fp_out)
-        return fp_out
-
-
-Entity.part_class = Part
-
-inf = float('inf')
-
-
-class SizedReader:
-
-    def __init__(self, fp, length, maxbytes, bufsize=DEFAULT_BUFFER_SIZE,
-                 has_trailers=False):
-        # Wrap our fp in a buffer so peek() works
-        self.fp = fp
-        self.length = length
-        self.maxbytes = maxbytes
-        self.buffer = b''
-        self.bufsize = bufsize
-        self.bytes_read = 0
-        self.done = False
-        self.has_trailers = has_trailers
-
-    def read(self, size=None, fp_out=None):
-        """Read bytes from the request body and return or write them to a file.
-
-        A number of bytes less than or equal to the 'size' argument are read
-        off the socket. The actual number of bytes read are tracked in
-        self.bytes_read. The number may be smaller than 'size' when 1) the
-        client sends fewer bytes, 2) the 'Content-Length' request header
-        specifies fewer bytes than requested, or 3) the number of bytes read
-        exceeds self.maxbytes (in which case, 413 is raised).
-
-        If the 'fp_out' argument is None (the default), all bytes read are
-        returned in a single byte string.
-
-        If the 'fp_out' argument is not None, it must be a file-like
-        object that supports the 'write' method; all bytes read will be
-        written to the fp, and None is returned.
-        """
-
-        if self.length is None:
-            if size is None:
-                remaining = inf
-            else:
-                remaining = size
-        else:
-            remaining = self.length - self.bytes_read
-            if size and size < remaining:
-                remaining = size
-        if remaining == 0:
-            self.finish()
-            if fp_out is None:
-                return b''
-            else:
-                return None
-
-        chunks = []
-
-        # Read bytes from the buffer.
-        if self.buffer:
-            if remaining is inf:
-                data = self.buffer
-                self.buffer = b''
-            else:
-                data = self.buffer[:remaining]
-                self.buffer = self.buffer[remaining:]
-            datalen = len(data)
-            remaining -= datalen
-
-            # Check lengths.
-            self.bytes_read += datalen
-            if self.maxbytes and self.bytes_read > self.maxbytes:
-                raise cherrypy.HTTPError(413)
-
-            # Store the data.
-            if fp_out is None:
-                chunks.append(data)
-            else:
-                fp_out.write(data)
-
-        # Read bytes from the socket.
-        while remaining > 0:
-            chunksize = min(remaining, self.bufsize)
-            try:
-                data = self.fp.read(chunksize)
-            except Exception:
-                e = sys.exc_info()[1]
-                if e.__class__.__name__ == 'MaxSizeExceeded':
-                    # Post data is too big
-                    raise cherrypy.HTTPError(
-                        413, 'Maximum request length: %r' % e.args[1])
-                else:
-                    raise
-            if not data:
-                self.finish()
-                break
-            datalen = len(data)
-            remaining -= datalen
-
-            # Check lengths.
-            self.bytes_read += datalen
-            if self.maxbytes and self.bytes_read > self.maxbytes:
-                raise cherrypy.HTTPError(413)
-
-            # Store the data.
-            if fp_out is None:
-                chunks.append(data)
-            else:
-                fp_out.write(data)
-
-        if fp_out is None:
-            return b''.join(chunks)
-
-    def readline(self, size=None):
-        """Read a line from the request body and return it."""
-        chunks = []
-        while size is None or size > 0:
-            chunksize = self.bufsize
-            if size is not None and size < self.bufsize:
-                chunksize = size
-            data = self.read(chunksize)
-            if not data:
-                break
-            pos = data.find(b'\n') + 1
-            if pos:
-                chunks.append(data[:pos])
-                remainder = data[pos:]
-                self.buffer += remainder
-                self.bytes_read -= len(remainder)
-                break
-            else:
-                chunks.append(data)
-        return b''.join(chunks)
-
-    def readlines(self, sizehint=None):
-        """Read lines from the request body and return them."""
-        if self.length is not None:
-            if sizehint is None:
-                sizehint = self.length - self.bytes_read
-            else:
-                sizehint = min(sizehint, self.length - self.bytes_read)
-
-        lines = []
-        seen = 0
-        while True:
-            line = self.readline()
-            if not line:
-                break
-            lines.append(line)
-            seen += len(line)
-            if seen >= sizehint:
-                break
-        return lines
-
-    def finish(self):
-        self.done = True
-        if self.has_trailers and hasattr(self.fp, 'read_trailer_lines'):
-            self.trailers = {}
-
-            try:
-                for line in self.fp.read_trailer_lines():
-                    if line[0] in b' \t':
-                        # It's a continuation line.
-                        v = line.strip()
-                    else:
-                        try:
-                            k, v = line.split(b':', 1)
-                        except ValueError:
-                            raise ValueError('Illegal header line.')
-                        k = k.strip().title()
-                        v = v.strip()
-
-                    if k in cheroot.server.comma_separated_headers:
-                        existing = self.trailers.get(k)
-                        if existing:
-                            v = b', '.join((existing, v))
-                    self.trailers[k] = v
-            except Exception:
-                e = sys.exc_info()[1]
-                if e.__class__.__name__ == 'MaxSizeExceeded':
-                    # Post data is too big
-                    raise cherrypy.HTTPError(
-                        413, 'Maximum request length: %r' % e.args[1])
-                else:
-                    raise
-
-
-class RequestBody(Entity):
-
-    """The entity of the HTTP request."""
-
-    bufsize = 8 * 1024
-    """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
-    # https://github.com/cherrypy/cherrypy/issues/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
-    request body not being read or parsed at all. This is by design; a missing
-    ``Content-Type`` header in the HTTP request entity is an error at best,
-    and a security hole at worst. For multipart parts, however, the MIME spec
-    declares that a part with no Content-Type defaults to "text/plain"
-    (see :class:`Part<cherrypy._cpreqbody.Part>`).
-    """
-
-    maxbytes = None
-    """Raise ``MaxSizeExceeded`` if more bytes than this are read from
-    the socket.
-    """
-
-    def __init__(self, fp, headers, params=None, request_params=None):
-        Entity.__init__(self, fp, headers, params)
-
-        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1
-        # When no explicit charset parameter is provided by the
-        # sender, media subtypes of the "text" type are defined
-        # to have a default charset value of "ISO-8859-1" when
-        # received via HTTP.
-        if self.content_type.value.startswith('text/'):
-            for c in ('ISO-8859-1', 'iso-8859-1', 'Latin-1', 'latin-1'):
-                if c in self.attempt_charsets:
-                    break
-            else:
-                self.attempt_charsets.append('ISO-8859-1')
-
-        # Temporary fix while deprecating passing .parts as .params.
-        self.processors['multipart'] = _old_process_multipart
-
-        if request_params is None:
-            request_params = {}
-        self.request_params = request_params
-
-    def process(self):
-        """Process the request entity based on its Content-Type."""
-        # "The presence of a message-body in a request is signaled by the
-        # inclusion of a Content-Length or Transfer-Encoding header field in
-        # the request's message-headers."
-        # It is possible to send a POST request with no body, for example;
-        # however, app developers are responsible in that case to set
-        # cherrypy.request.process_body to False so this method isn't called.
-        h = cherrypy.serving.request.headers
-        if 'Content-Length' not in h and 'Transfer-Encoding' not in h:
-            raise cherrypy.HTTPError(411)
-
-        self.fp = SizedReader(self.fp, self.length,
-                              self.maxbytes, bufsize=self.bufsize,
-                              has_trailers='Trailer' in h)
-        super(RequestBody, self).process()
-
-        # Body params should also be a part of the request_params
-        # add them in here.
-        request_params = self.request_params
-        for key, value in self.params.items():
-            # Python 2 only: keyword arguments must be byte strings (type
-            # 'str').
-            if sys.version_info < (3, 0):
-                if isinstance(key, six.text_type):
-                    key = key.encode('ISO-8859-1')
-
-            if key in request_params:
-                if not isinstance(request_params[key], list):
-                    request_params[key] = [request_params[key]]
-                request_params[key].append(value)
-            else:
-                request_params[key] = value
diff --git a/libraries/cherrypy/_cprequest.py b/libraries/cherrypy/_cprequest.py
deleted file mode 100644
index 3cc0c811..00000000
--- a/libraries/cherrypy/_cprequest.py
+++ /dev/null
@@ -1,930 +0,0 @@
-import sys
-import time
-
-import uuid
-
-import six
-from six.moves.http_cookies import SimpleCookie, CookieError
-
-from more_itertools import consume
-
-import cherrypy
-from cherrypy._cpcompat import ntob
-from cherrypy import _cpreqbody
-from cherrypy._cperror import format_exc, bare_error
-from cherrypy.lib import httputil, reprconf, encoding
-
-
-class Hook(object):
-
-    """A callback and its metadata: failsafe, priority, and kwargs."""
-
-    callback = None
-    """
-    The bare callable that this Hook object is wrapping, which will
-    be called when the Hook is called."""
-
-    failsafe = False
-    """
-    If True, the callback is guaranteed to run even if other callbacks
-    from the same call point raise exceptions."""
-
-    priority = 50
-    """
-    Defines the order of execution for a list of Hooks. Priority numbers
-    should be limited to the closed interval [0, 100], but values outside
-    this range are acceptable, as are fractional values."""
-
-    kwargs = {}
-    """
-    A set of keyword arguments that will be passed to the
-    callable on each call."""
-
-    def __init__(self, callback, failsafe=None, priority=None, **kwargs):
-        self.callback = callback
-
-        if failsafe is None:
-            failsafe = getattr(callback, 'failsafe', False)
-        self.failsafe = failsafe
-
-        if priority is None:
-            priority = getattr(callback, 'priority', 50)
-        self.priority = priority
-
-        self.kwargs = kwargs
-
-    def __lt__(self, other):
-        """
-        Hooks sort by priority, ascending, such that
-        hooks of lower priority are run first.
-        """
-        return self.priority < other.priority
-
-    def __call__(self):
-        """Run self.callback(**self.kwargs)."""
-        return self.callback(**self.kwargs)
-
-    def __repr__(self):
-        cls = self.__class__
-        return ('%s.%s(callback=%r, failsafe=%r, priority=%r, %s)'
-                % (cls.__module__, cls.__name__, self.callback,
-                   self.failsafe, self.priority,
-                   ', '.join(['%s=%r' % (k, v)
-                              for k, v in self.kwargs.items()])))
-
-
-class HookMap(dict):
-
-    """A map of call points to lists of callbacks (Hook objects)."""
-
-    def __new__(cls, points=None):
-        d = dict.__new__(cls)
-        for p in points or []:
-            d[p] = []
-        return d
-
-    def __init__(self, *a, **kw):
-        pass
-
-    def attach(self, point, callback, failsafe=None, priority=None, **kwargs):
-        """Append a new Hook made from the supplied arguments."""
-        self[point].append(Hook(callback, failsafe, priority, **kwargs))
-
-    def run(self, point):
-        """Execute all registered Hooks (callbacks) for the given point."""
-        exc = None
-        hooks = self[point]
-        hooks.sort()
-        for hook in hooks:
-            # Some hooks are guaranteed to run even if others at
-            # the same hookpoint fail. We will still log the failure,
-            # but proceed on to the next hook. The only way
-            # to stop all processing from one of these hooks is
-            # to raise SystemExit and stop the whole server.
-            if exc is None or hook.failsafe:
-                try:
-                    hook()
-                except (KeyboardInterrupt, SystemExit):
-                    raise
-                except (cherrypy.HTTPError, cherrypy.HTTPRedirect,
-                        cherrypy.InternalRedirect):
-                    exc = sys.exc_info()[1]
-                except Exception:
-                    exc = sys.exc_info()[1]
-                    cherrypy.log(traceback=True, severity=40)
-        if exc:
-            raise exc
-
-    def __copy__(self):
-        newmap = self.__class__()
-        # We can't just use 'update' because we want copies of the
-        # mutable values (each is a list) as well.
-        for k, v in self.items():
-            newmap[k] = v[:]
-        return newmap
-    copy = __copy__
-
-    def __repr__(self):
-        cls = self.__class__
-        return '%s.%s(points=%r)' % (
-            cls.__module__,
-            cls.__name__,
-            list(self)
-        )
-
-
-# Config namespace handlers
-
-def hooks_namespace(k, v):
-    """Attach bare hooks declared in config."""
-    # Use split again to allow multiple hooks for a single
-    # hookpoint per path (e.g. "hooks.before_handler.1").
-    # Little-known fact you only get from reading source ;)
-    hookpoint = k.split('.', 1)[0]
-    if isinstance(v, six.string_types):
-        v = cherrypy.lib.reprconf.attributes(v)
-    if not isinstance(v, Hook):
-        v = Hook(v)
-    cherrypy.serving.request.hooks[hookpoint].append(v)
-
-
-def request_namespace(k, v):
-    """Attach request attributes declared in config."""
-    # Provides config entries to set request.body attrs (like
-    # attempt_charsets).
-    if k[:5] == 'body.':
-        setattr(cherrypy.serving.request.body, k[5:], v)
-    else:
-        setattr(cherrypy.serving.request, k, v)
-
-
-def response_namespace(k, v):
-    """Attach response attributes declared in config."""
-    # Provides config entries to set default response headers
-    # http://cherrypy.org/ticket/889
-    if k[:8] == 'headers.':
-        cherrypy.serving.response.headers[k.split('.', 1)[1]] = v
-    else:
-        setattr(cherrypy.serving.response, k, v)
-
-
-def error_page_namespace(k, v):
-    """Attach error pages declared in config."""
-    if k != 'default':
-        k = int(k)
-    cherrypy.serving.request.error_page[k] = v
-
-
-hookpoints = ['on_start_resource', 'before_request_body',
-              'before_handler', 'before_finalize',
-              'on_end_resource', 'on_end_request',
-              'before_error_response', 'after_error_response']
-
-
-class Request(object):
-
-    """An HTTP request.
-
-    This object represents the metadata of an HTTP request message;
-    that is, it contains attributes which describe the environment
-    in which the request URL, headers, and body were sent (if you
-    want tools to interpret the headers and body, those are elsewhere,
-    mostly in Tools). This 'metadata' consists of socket data,
-    transport characteristics, and the Request-Line. This object
-    also contains data regarding the configuration in effect for
-    the given URL, and the execution plan for generating a response.
-    """
-
-    prev = None
-    """
-    The previous Request object (if any). This should be None
-    unless we are processing an InternalRedirect."""
-
-    # Conversation/connection attributes
-    local = httputil.Host('127.0.0.1', 80)
-    'An httputil.Host(ip, port, hostname) object for the server socket.'
-
-    remote = httputil.Host('127.0.0.1', 1111)
-    'An httputil.Host(ip, port, hostname) object for the client socket.'
-
-    scheme = 'http'
-    """
-    The protocol used between client and server. In most cases,
-    this will be either 'http' or 'https'."""
-
-    server_protocol = 'HTTP/1.1'
-    """
-    The HTTP version for which the HTTP server is at least
-    conditionally compliant."""
-
-    base = ''
-    """The (scheme://host) portion of the requested URL.
-    In some cases (e.g. when proxying via mod_rewrite), this may contain
-    path segments which cherrypy.url uses when constructing url's, but
-    which otherwise are ignored by CherryPy. Regardless, this value
-    MUST NOT end in a slash."""
-
-    # Request-Line attributes
-    request_line = ''
-    """
-    The complete Request-Line received from the client. This is a
-    single string consisting of the request method, URI, and protocol
-    version (joined by spaces). Any final CRLF is removed."""
-
-    method = 'GET'
-    """
-    Indicates the HTTP method to be performed on the resource identified
-    by the Request-URI. Common methods include GET, HEAD, POST, PUT, and
-    DELETE. CherryPy allows any extension method; however, various HTTP
-    servers and gateways may restrict the set of allowable methods.
-    CherryPy applications SHOULD restrict the set (on a per-URI basis)."""
-
-    query_string = ''
-    """
-    The query component of the Request-URI, a string of information to be
-    interpreted by the resource. The query portion of a URI follows the
-    path component, and is separated by a '?'. For example, the URI
-    'http://www.cherrypy.org/wiki?a=3&b=4' has the query component,
-    'a=3&b=4'."""
-
-    query_string_encoding = 'utf8'
-    """
-    The encoding expected for query string arguments after % HEX HEX decoding).
-    If a query string is provided that cannot be decoded with this encoding,
-    404 is raised (since technically it's a different URI). If you want
-    arbitrary encodings to not error, set this to 'Latin-1'; you can then
-    encode back to bytes and re-decode to whatever encoding you like later.
-    """
-
-    protocol = (1, 1)
-    """The HTTP protocol version corresponding to the set
-    of features which should be allowed in the response. If BOTH
-    the client's request message AND the server's level of HTTP
-    compliance is HTTP/1.1, this attribute will be the tuple (1, 1).
-    If either is 1.0, this attribute will be the tuple (1, 0).
-    Lower HTTP protocol versions are not explicitly supported."""
-
-    params = {}
-    """
-    A dict which combines query string (GET) and request entity (POST)
-    variables. This is populated in two stages: GET params are added
-    before the 'on_start_resource' hook, and POST params are added
-    between the 'before_request_body' and 'before_handler' hooks."""
-
-    # Message attributes
-    header_list = []
-    """
-    A list of the HTTP request headers as (name, value) tuples.
-    In general, you should use request.headers (a dict) instead."""
-
-    headers = httputil.HeaderMap()
-    """
-    A dict-like object containing the request headers. Keys are header
-    names (in Title-Case format); however, you may get and set them in
-    a case-insensitive manner. That is, headers['Content-Type'] and
-    headers['content-type'] refer to the same value. Values are header
-    values (decoded according to :rfc:`2047` if necessary). See also:
-    httputil.HeaderMap, httputil.HeaderElement."""
-
-    cookie = SimpleCookie()
-    """See help(Cookie)."""
-
-    rfile = None
-    """
-    If the request included an entity (body), it will be available
-    as a stream in this attribute. However, the rfile will normally
-    be read for you between the 'before_request_body' hook and the
-    'before_handler' hook, and the resulting string is placed into
-    either request.params or the request.body attribute.
-
-    You may disable the automatic consumption of the rfile by setting
-    request.process_request_body to False, either in config for the desired
-    path, or in an 'on_start_resource' or 'before_request_body' hook.
-
-    WARNING: In almost every case, you should not attempt to read from the
-    rfile stream after CherryPy's automatic mechanism has read it. If you
-    turn off the automatic parsing of rfile, you should read exactly the
-    number of bytes specified in request.headers['Content-Length'].
-    Ignoring either of these warnings may result in a hung request thread
-    or in corruption of the next (pipelined) request.
-    """
-
-    process_request_body = True
-    """
-    If True, the rfile (if any) is automatically read and parsed,
-    and the result placed into request.params or request.body."""
-
-    methods_with_bodies = ('POST', 'PUT', 'PATCH')
-    """
-    A sequence of HTTP methods for which CherryPy will automatically
-    attempt to read a body from the rfile. If you are going to change
-    this property, modify it on the configuration (recommended)
-    or on the "hook point" `on_start_resource`.
-    """
-
-    body = None
-    """
-    If the request Content-Type is 'application/x-www-form-urlencoded'
-    or multipart, this will be None. Otherwise, this will be an instance
-    of :class:`RequestBody<cherrypy._cpreqbody.RequestBody>` (which you
-    can .read()); this value is set between the 'before_request_body' and
-    'before_handler' hooks (assuming that process_request_body is True)."""
-
-    # Dispatch attributes
-    dispatch = cherrypy.dispatch.Dispatcher()
-    """
-    The object which looks up the 'page handler' callable and collects
-    config for the current request based on the path_info, other
-    request attributes, and the application architecture. The core
-    calls the dispatcher as early as possible, passing it a 'path_info'
-    argument.
-
-    The default dispatcher discovers the page handler by matching path_info
-    to a hierarchical arrangement of objects, starting at request.app.root.
-    See help(cherrypy.dispatch) for more information."""
-
-    script_name = ''
-    """
-    The 'mount point' of the application which is handling this request.
-
-    This attribute MUST NOT end in a slash. If the script_name refers to
-    the root of the URI, it MUST be an empty string (not "/").
-    """
-
-    path_info = '/'
-    """
-    The 'relative path' portion of the Request-URI. This is relative
-    to the script_name ('mount point') of the application which is
-    handling this request."""
-
-    login = None
-    """
-    When authentication is used during the request processing this is
-    set to 'False' if it failed and to the 'username' value if it succeeded.
-    The default 'None' implies that no authentication happened."""
-
-    # Note that cherrypy.url uses "if request.app:" to determine whether
-    # the call is during a real HTTP request or not. So leave this None.
-    app = None
-    """The cherrypy.Application object which is handling this request."""
-
-    handler = None
-    """
-    The function, method, or other callable which CherryPy will call to
-    produce the response. The discovery of the handler and the arguments
-    it will receive are determined by the request.dispatch object.
-    By default, the handler is discovered by walking a tree of objects
-    starting at request.app.root, and is then passed all HTTP params
-    (from the query string and POST body) as keyword arguments."""
-
-    toolmaps = {}
-    """
-    A nested dict of all Toolboxes and Tools in effect for this request,
-    of the form: {Toolbox.namespace: {Tool.name: config dict}}."""
-
-    config = None
-    """
-    A flat dict of all configuration entries which apply to the
-    current request. These entries are collected from global config,
-    application config (based on request.path_info), and from handler
-    config (exactly how is governed by the request.dispatch object in
-    effect for this request; by default, handler config can be attached
-    anywhere in the tree between request.app.root and the final handler,
-    and inherits downward)."""
-
-    is_index = None
-    """
-    This will be True if the current request is mapped to an 'index'
-    resource handler (also, a 'default' handler if path_info ends with
-    a slash). The value may be used to automatically redirect the
-    user-agent to a 'more canonical' URL which either adds or removes
-    the trailing slash. See cherrypy.tools.trailing_slash."""
-
-    hooks = HookMap(hookpoints)
-    """
-    A HookMap (dict-like object) of the form: {hookpoint: [hook, ...]}.
-    Each key is a str naming the hook point, and each value is a list
-    of hooks which will be called at that hook point during this request.
-    The list of hooks is generally populated as early as possible (mostly
-    from Tools specified in config), but may be extended at any time.
-    See also: _cprequest.Hook, _cprequest.HookMap, and cherrypy.tools."""
-
-    error_response = cherrypy.HTTPError(500).set_response
-    """
-    The no-arg callable which will handle unexpected, untrapped errors
-    during request processing. This is not used for expected exceptions
-    (like NotFound, HTTPError, or HTTPRedirect) which are raised in
-    response to expected conditions (those should be customized either
-    via request.error_page or by overriding HTTPError.set_response).
-    By default, error_response uses HTTPError(500) to return a generic
-    error response to the user-agent."""
-
-    error_page = {}
-    """
-    A dict of {error code: response filename or callable} pairs.
-
-    The error code must be an int representing a given HTTP error code,
-    or the string 'default', which will be used if no matching entry
-    is found for a given numeric code.
-
-    If a filename is provided, the file should contain a Python string-
-    formatting template, and can expect by default to receive format
-    values with the mapping keys %(status)s, %(message)s, %(traceback)s,
-    and %(version)s. The set of format mappings can be extended by
-    overriding HTTPError.set_response.
-
-    If a callable is provided, it will be called by default with keyword
-    arguments 'status', 'message', 'traceback', and 'version', as for a
-    string-formatting template. The callable must return a string or
-    iterable of strings which will be set to response.body. It may also
-    override headers or perform any other processing.
-
-    If no entry is given for an error code, and no 'default' entry exists,
-    a default template will be used.
-    """
-
-    show_tracebacks = True
-    """
-    If True, unexpected errors encountered during request processing will
-    include a traceback in the response body."""
-
-    show_mismatched_params = True
-    """
-    If True, mismatched parameters encountered during PageHandler invocation
-    processing will be included in the response body."""
-
-    throws = (KeyboardInterrupt, SystemExit, cherrypy.InternalRedirect)
-    """The sequence of exceptions which Request.run does not trap."""
-
-    throw_errors = False
-    """
-    If True, Request.run will not trap any errors (except HTTPRedirect and
-    HTTPError, which are more properly called 'exceptions', not errors)."""
-
-    closed = False
-    """True once the close method has been called, False otherwise."""
-
-    stage = None
-    """
-    A string containing the stage reached in the request-handling process.
-    This is useful when debugging a live server with hung requests."""
-
-    unique_id = None
-    """A lazy object generating and memorizing UUID4 on ``str()`` render."""
-
-    namespaces = reprconf.NamespaceSet(
-        **{'hooks': hooks_namespace,
-           'request': request_namespace,
-           'response': response_namespace,
-           'error_page': error_page_namespace,
-           'tools': cherrypy.tools,
-           })
-
-    def __init__(self, local_host, remote_host, scheme='http',
-                 server_protocol='HTTP/1.1'):
-        """Populate a new Request object.
-
-        local_host should be an httputil.Host object with the server info.
-        remote_host should be an httputil.Host object with the client info.
-        scheme should be a string, either "http" or "https".
-        """
-        self.local = local_host
-        self.remote = remote_host
-        self.scheme = scheme
-        self.server_protocol = server_protocol
-
-        self.closed = False
-
-        # Put a *copy* of the class error_page into self.
-        self.error_page = self.error_page.copy()
-
-        # Put a *copy* of the class namespaces into self.
-        self.namespaces = self.namespaces.copy()
-
-        self.stage = None
-
-        self.unique_id = LazyUUID4()
-
-    def close(self):
-        """Run cleanup code. (Core)"""
-        if not self.closed:
-            self.closed = True
-            self.stage = 'on_end_request'
-            self.hooks.run('on_end_request')
-            self.stage = 'close'
-
-    def run(self, method, path, query_string, req_protocol, headers, rfile):
-        r"""Process the Request. (Core)
-
-        method, path, query_string, and req_protocol should be pulled directly
-        from the Request-Line (e.g. "GET /path?key=val HTTP/1.0").
-
-        path
-            This should be %XX-unquoted, but query_string should not be.
-
-            When using Python 2, they both MUST be byte strings,
-            not unicode strings.
-
-            When using Python 3, they both MUST be unicode strings,
-            not byte strings, and preferably not bytes \x00-\xFF
-            disguised as unicode.
-
-        headers
-            A list of (name, value) tuples.
-
-        rfile
-            A file-like object containing the HTTP request entity.
-
-        When run() is done, the returned object should have 3 attributes:
-
-          * status, e.g. "200 OK"
-          * header_list, a list of (name, value) tuples
-          * body, an iterable yielding strings
-
-        Consumer code (HTTP servers) should then access these response
-        attributes to build the outbound stream.
-
-        """
-        response = cherrypy.serving.response
-        self.stage = 'run'
-        try:
-            self.error_response = cherrypy.HTTPError(500).set_response
-
-            self.method = method
-            path = path or '/'
-            self.query_string = query_string or ''
-            self.params = {}
-
-            # Compare request and server HTTP protocol versions, in case our
-            # server does not support the requested protocol. Limit our output
-            # to min(req, server). We want the following output:
-            #     request    server     actual written   supported response
-            #     protocol   protocol  response protocol    feature set
-            # a     1.0        1.0           1.0                1.0
-            # b     1.0        1.1           1.1                1.0
-            # c     1.1        1.0           1.0                1.0
-            # d     1.1        1.1           1.1                1.1
-            # Notice that, in (b), the response will be "HTTP/1.1" even though
-            # the client only understands 1.0. RFC 2616 10.5.6 says we should
-            # only return 505 if the _major_ version is different.
-            rp = int(req_protocol[5]), int(req_protocol[7])
-            sp = int(self.server_protocol[5]), int(self.server_protocol[7])
-            self.protocol = min(rp, sp)
-            response.headers.protocol = self.protocol
-
-            # Rebuild first line of the request (e.g. "GET /path HTTP/1.0").
-            url = path
-            if query_string:
-                url += '?' + query_string
-            self.request_line = '%s %s %s' % (method, url, req_protocol)
-
-            self.header_list = list(headers)
-            self.headers = httputil.HeaderMap()
-
-            self.rfile = rfile
-            self.body = None
-
-            self.cookie = SimpleCookie()
-            self.handler = None
-
-            # path_info should be the path from the
-            # app root (script_name) to the handler.
-            self.script_name = self.app.script_name
-            self.path_info = pi = path[len(self.script_name):]
-
-            self.stage = 'respond'
-            self.respond(pi)
-
-        except self.throws:
-            raise
-        except Exception:
-            if self.throw_errors:
-                raise
-            else:
-                # Failure in setup, error handler or finalize. Bypass them.
-                # Can't use handle_error because we may not have hooks yet.
-                cherrypy.log(traceback=True, severity=40)
-                if self.show_tracebacks:
-                    body = format_exc()
-                else:
-                    body = ''
-                r = bare_error(body)
-                response.output_status, response.header_list, response.body = r
-
-        if self.method == 'HEAD':
-            # HEAD requests MUST NOT return a message-body in the response.
-            response.body = []
-
-        try:
-            cherrypy.log.access()
-        except Exception:
-            cherrypy.log.error(traceback=True)
-
-        return response
-
-    def respond(self, path_info):
-        """Generate a response for the resource at self.path_info. (Core)"""
-        try:
-            try:
-                try:
-                    self._do_respond(path_info)
-                except (cherrypy.HTTPRedirect, cherrypy.HTTPError):
-                    inst = sys.exc_info()[1]
-                    inst.set_response()
-                    self.stage = 'before_finalize (HTTPError)'
-                    self.hooks.run('before_finalize')
-                    cherrypy.serving.response.finalize()
-            finally:
-                self.stage = 'on_end_resource'
-                self.hooks.run('on_end_resource')
-        except self.throws:
-            raise
-        except Exception:
-            if self.throw_errors:
-                raise
-            self.handle_error()
-
-    def _do_respond(self, path_info):
-        response = cherrypy.serving.response
-
-        if self.app is None:
-            raise cherrypy.NotFound()
-
-        self.hooks = self.__class__.hooks.copy()
-        self.toolmaps = {}
-
-        # Get the 'Host' header, so we can HTTPRedirect properly.
-        self.stage = 'process_headers'
-        self.process_headers()
-
-        self.stage = 'get_resource'
-        self.get_resource(path_info)
-
-        self.body = _cpreqbody.RequestBody(
-            self.rfile, self.headers, request_params=self.params)
-
-        self.namespaces(self.config)
-
-        self.stage = 'on_start_resource'
-        self.hooks.run('on_start_resource')
-
-        # Parse the querystring
-        self.stage = 'process_query_string'
-        self.process_query_string()
-
-        # Process the body
-        if self.process_request_body:
-            if self.method not in self.methods_with_bodies:
-                self.process_request_body = False
-        self.stage = 'before_request_body'
-        self.hooks.run('before_request_body')
-        if self.process_request_body:
-            self.body.process()
-
-        # Run the handler
-        self.stage = 'before_handler'
-        self.hooks.run('before_handler')
-        if self.handler:
-            self.stage = 'handler'
-            response.body = self.handler()
-
-        # Finalize
-        self.stage = 'before_finalize'
-        self.hooks.run('before_finalize')
-        response.finalize()
-
-    def process_query_string(self):
-        """Parse the query string into Python structures. (Core)"""
-        try:
-            p = httputil.parse_query_string(
-                self.query_string, encoding=self.query_string_encoding)
-        except UnicodeDecodeError:
-            raise cherrypy.HTTPError(
-                404, 'The given query string could not be processed. Query '
-                'strings for this resource must be encoded with %r.' %
-                self.query_string_encoding)
-
-        # Python 2 only: keyword arguments must be byte strings (type 'str').
-        if six.PY2:
-            for key, value in p.items():
-                if isinstance(key, six.text_type):
-                    del p[key]
-                    p[key.encode(self.query_string_encoding)] = value
-        self.params.update(p)
-
-    def process_headers(self):
-        """Parse HTTP header data into Python structures. (Core)"""
-        # Process the headers into self.headers
-        headers = self.headers
-        for name, value in self.header_list:
-            # Call title() now (and use dict.__method__(headers))
-            # so title doesn't have to be called twice.
-            name = name.title()
-            value = value.strip()
-
-            headers[name] = httputil.decode_TEXT_maybe(value)
-
-            # Some clients, notably Konquoror, supply multiple
-            # cookies on different lines with the same key. To
-            # handle this case, store all cookies in self.cookie.
-            if name == 'Cookie':
-                try:
-                    self.cookie.load(value)
-                except CookieError as exc:
-                    raise cherrypy.HTTPError(400, str(exc))
-
-        if not dict.__contains__(headers, 'Host'):
-            # All Internet-based HTTP/1.1 servers MUST respond with a 400
-            # (Bad Request) status code to any HTTP/1.1 request message
-            # which lacks a Host header field.
-            if self.protocol >= (1, 1):
-                msg = "HTTP/1.1 requires a 'Host' request header."
-                raise cherrypy.HTTPError(400, msg)
-        host = dict.get(headers, 'Host')
-        if not host:
-            host = self.local.name or self.local.ip
-        self.base = '%s://%s' % (self.scheme, host)
-
-    def get_resource(self, path):
-        """Call a dispatcher (which sets self.handler and .config). (Core)"""
-        # First, see if there is a custom dispatch at this URI. Custom
-        # dispatchers can only be specified in app.config, not in _cp_config
-        # (since custom dispatchers may not even have an app.root).
-        dispatch = self.app.find_config(
-            path, 'request.dispatch', self.dispatch)
-
-        # dispatch() should set self.handler and self.config
-        dispatch(path)
-
-    def handle_error(self):
-        """Handle the last unanticipated exception. (Core)"""
-        try:
-            self.hooks.run('before_error_response')
-            if self.error_response:
-                self.error_response()
-            self.hooks.run('after_error_response')
-            cherrypy.serving.response.finalize()
-        except cherrypy.HTTPRedirect:
-            inst = sys.exc_info()[1]
-            inst.set_response()
-            cherrypy.serving.response.finalize()
-
-
-class ResponseBody(object):
-
-    """The body of the HTTP response (the response entity)."""
-
-    unicode_err = ('Page handlers MUST return bytes. Use tools.encode '
-                   'if you wish to return unicode.')
-
-    def __get__(self, obj, objclass=None):
-        if obj is None:
-            # When calling on the class instead of an instance...
-            return self
-        else:
-            return obj._body
-
-    def __set__(self, obj, value):
-        # Convert the given value to an iterable object.
-        if isinstance(value, six.text_type):
-            raise ValueError(self.unicode_err)
-        elif isinstance(value, list):
-            # every item in a list must be bytes...
-            if any(isinstance(item, six.text_type) for item in value):
-                raise ValueError(self.unicode_err)
-
-        obj._body = encoding.prepare_iter(value)
-
-
-class Response(object):
-
-    """An HTTP Response, including status, headers, and body."""
-
-    status = ''
-    """The HTTP Status-Code and Reason-Phrase."""
-
-    header_list = []
-    """
-    A list of the HTTP response headers as (name, value) tuples.
-    In general, you should use response.headers (a dict) instead. This
-    attribute is generated from response.headers and is not valid until
-    after the finalize phase."""
-
-    headers = httputil.HeaderMap()
-    """
-    A dict-like object containing the response headers. Keys are header
-    names (in Title-Case format); however, you may get and set them in
-    a case-insensitive manner. That is, headers['Content-Type'] and
-    headers['content-type'] refer to the same value. Values are header
-    values (decoded according to :rfc:`2047` if necessary).
-
-    .. seealso:: classes :class:`HeaderMap`, :class:`HeaderElement`
-    """
-
-    cookie = SimpleCookie()
-    """See help(Cookie)."""
-
-    body = ResponseBody()
-    """The body (entity) of the HTTP response."""
-
-    time = None
-    """The value of time.time() when created. Use in HTTP dates."""
-
-    stream = False
-    """If False, buffer the response body."""
-
-    def __init__(self):
-        self.status = None
-        self.header_list = None
-        self._body = []
-        self.time = time.time()
-
-        self.headers = httputil.HeaderMap()
-        # Since we know all our keys are titled strings, we can
-        # bypass HeaderMap.update and get a big speed boost.
-        dict.update(self.headers, {
-            'Content-Type': 'text/html',
-            'Server': 'CherryPy/' + cherrypy.__version__,
-            'Date': httputil.HTTPDate(self.time),
-        })
-        self.cookie = SimpleCookie()
-
-    def collapse_body(self):
-        """Collapse self.body to a single string; replace it and return it."""
-        new_body = b''.join(self.body)
-        self.body = new_body
-        return new_body
-
-    def _flush_body(self):
-        """
-        Discard self.body but consume any generator such that
-        any finalization can occur, such as is required by
-        caching.tee_output().
-        """
-        consume(iter(self.body))
-
-    def finalize(self):
-        """Transform headers (and cookies) into self.header_list. (Core)"""
-        try:
-            code, reason, _ = httputil.valid_status(self.status)
-        except ValueError:
-            raise cherrypy.HTTPError(500, sys.exc_info()[1].args[0])
-
-        headers = self.headers
-
-        self.status = '%s %s' % (code, reason)
-        self.output_status = ntob(str(code), 'ascii') + \
-            b' ' + headers.encode(reason)
-
-        if self.stream:
-            # The upshot: wsgiserver will chunk the response if
-            # you pop Content-Length (or set it explicitly to None).
-            # Note that lib.static sets C-L to the file's st_size.
-            if dict.get(headers, 'Content-Length') is None:
-                dict.pop(headers, 'Content-Length', None)
-        elif code < 200 or code in (204, 205, 304):
-            # "All 1xx (informational), 204 (no content),
-            # and 304 (not modified) responses MUST NOT
-            # include a message-body."
-            dict.pop(headers, 'Content-Length', None)
-            self._flush_body()
-            self.body = b''
-        else:
-            # Responses which are not streamed should have a Content-Length,
-            # but allow user code to set Content-Length if desired.
-            if dict.get(headers, 'Content-Length') is None:
-                content = self.collapse_body()
-                dict.__setitem__(headers, 'Content-Length', len(content))
-
-        # Transform our header dict into a list of tuples.
-        self.header_list = h = headers.output()
-
-        cookie = self.cookie.output()
-        if cookie:
-            for line in cookie.split('\r\n'):
-                name, value = line.split(': ', 1)
-                if isinstance(name, six.text_type):
-                    name = name.encode('ISO-8859-1')
-                if isinstance(value, six.text_type):
-                    value = headers.encode(value)
-                h.append((name, value))
-
-
-class LazyUUID4(object):
-    def __str__(self):
-        """Return UUID4 and keep it for future calls."""
-        return str(self.uuid4)
-
-    @property
-    def uuid4(self):
-        """Provide unique id on per-request basis using UUID4.
-
-        It's evaluated lazily on render.
-        """
-        try:
-            self._uuid4
-        except AttributeError:
-            # evaluate on first access
-            self._uuid4 = uuid.uuid4()
-
-        return self._uuid4
diff --git a/libraries/cherrypy/_cpserver.py b/libraries/cherrypy/_cpserver.py
deleted file mode 100644
index 0f60e2c8..00000000
--- a/libraries/cherrypy/_cpserver.py
+++ /dev/null
@@ -1,252 +0,0 @@
-"""Manage HTTP servers with CherryPy."""
-
-import six
-
-import cherrypy
-from cherrypy.lib.reprconf import attributes
-from cherrypy._cpcompat import text_or_bytes
-from cherrypy.process.servers import ServerAdapter
-
-
-__all__ = ('Server', )
-
-
-class Server(ServerAdapter):
-    """An adapter for an HTTP server.
-
-    You can set attributes (like socket_host and socket_port)
-    on *this* object (which is probably cherrypy.server), and call
-    quickstart. For example::
-
-        cherrypy.server.socket_port = 80
-        cherrypy.quickstart()
-    """
-
-    socket_port = 8080
-    """The TCP port on which to listen for connections."""
-
-    _socket_host = '127.0.0.1'
-
-    @property
-    def socket_host(self):  # noqa: D401; irrelevant for properties
-        """The hostname or IP address on which to listen for connections.
-
-        Host values may be any IPv4 or IPv6 address, or any valid hostname.
-        The string 'localhost' is a synonym for '127.0.0.1' (or '::1', if
-        your hosts file prefers IPv6). The string '0.0.0.0' is a special
-        IPv4 entry meaning "any active interface" (INADDR_ANY), and '::'
-        is the similar IN6ADDR_ANY for IPv6. The empty string or None are
-        not allowed.
-        """
-        return self._socket_host
-
-    @socket_host.setter
-    def socket_host(self, value):
-        if value == '':
-            raise ValueError("The empty string ('') is not an allowed value. "
-                             "Use '0.0.0.0' instead to listen on all active "
-                             'interfaces (INADDR_ANY).')
-        self._socket_host = value
-
-    socket_file = None
-    """If given, the name of the UNIX socket to use instead of TCP/IP.
-
-    When this option is not None, the `socket_host` and `socket_port` options
-    are ignored."""
-
-    socket_queue_size = 5
-    """The 'backlog' argument to socket.listen(); specifies the maximum number
-    of queued connections (default 5)."""
-
-    socket_timeout = 10
-    """The timeout in seconds for accepted connections (default 10)."""
-
-    accepted_queue_size = -1
-    """The maximum number of requests which will be queued up before
-    the server refuses to accept it (default -1, meaning no limit)."""
-
-    accepted_queue_timeout = 10
-    """The timeout in seconds for attempting to add a request to the
-    queue when the queue is full (default 10)."""
-
-    shutdown_timeout = 5
-    """The time to wait for HTTP worker threads to clean up."""
-
-    protocol_version = 'HTTP/1.1'
-    """The version string to write in the Status-Line of all HTTP responses,
-    for example, "HTTP/1.1" (the default). Depending on the HTTP server used,
-    this should also limit the supported features used in the response."""
-
-    thread_pool = 10
-    """The number of worker threads to start up in the pool."""
-
-    thread_pool_max = -1
-    """The maximum size of the worker-thread pool. Use -1 to indicate no limit.
-    """
-
-    max_request_header_size = 500 * 1024
-    """The maximum number of bytes allowable in the request headers.
-    If exceeded, the HTTP server should return "413 Request Entity Too Large".
-    """
-
-    max_request_body_size = 100 * 1024 * 1024
-    """The maximum number of bytes allowable in the request body. If exceeded,
-    the HTTP server should return "413 Request Entity Too Large"."""
-
-    instance = None
-    """If not None, this should be an HTTP server instance (such as
-    cheroot.wsgi.Server) which cherrypy.server will control.
-    Use this when you need
-    more control over object instantiation than is available in the various
-    configuration options."""
-
-    ssl_context = None
-    """When using PyOpenSSL, an instance of SSL.Context."""
-
-    ssl_certificate = None
-    """The filename of the SSL certificate to use."""
-
-    ssl_certificate_chain = None
-    """When using PyOpenSSL, the certificate chain to pass to
-    Context.load_verify_locations."""
-
-    ssl_private_key = None
-    """The filename of the private key to use with SSL."""
-
-    ssl_ciphers = None
-    """The ciphers list of SSL."""
-
-    if six.PY3:
-        ssl_module = 'builtin'
-        """The name of a registered SSL adaptation module to use with
-        the builtin WSGI server. Builtin options are: 'builtin' (to
-        use the SSL library built into recent versions of Python).
-        You may also register your own classes in the
-        cheroot.server.ssl_adapters dict."""
-    else:
-        ssl_module = 'pyopenssl'
-        """The name of a registered SSL adaptation module to use with the
-        builtin WSGI server. Builtin options are 'builtin' (to use the SSL
-        library built into recent versions of Python) and 'pyopenssl' (to
-        use the PyOpenSSL project, which you must install separately). You
-        may also register your own classes in the cheroot.server.ssl_adapters
-        dict."""
-
-    statistics = False
-    """Turns statistics-gathering on or off for aware HTTP servers."""
-
-    nodelay = True
-    """If True (the default since 3.1), sets the TCP_NODELAY socket option."""
-
-    wsgi_version = (1, 0)
-    """The WSGI version tuple to use with the builtin WSGI server.
-    The provided options are (1, 0) [which includes support for PEP 3333,
-    which declares it covers WSGI version 1.0.1 but still mandates the
-    wsgi.version (1, 0)] and ('u', 0), an experimental unicode version.
-    You may create and register your own experimental versions of the WSGI
-    protocol by adding custom classes to the cheroot.server.wsgi_gateways dict.
-    """
-
-    peercreds = False
-    """If True, peer cred lookup for UNIX domain socket will put to WSGI env.
-
-    This information will then be available through WSGI env vars:
-    * X_REMOTE_PID
-    * X_REMOTE_UID
-    * X_REMOTE_GID
-    """
-
-    peercreds_resolve = False
-    """If True, username/group will be looked up in the OS from peercreds.
-
-    This information will then be available through WSGI env vars:
-    * REMOTE_USER
-    * X_REMOTE_USER
-    * X_REMOTE_GROUP
-    """
-
-    def __init__(self):
-        """Initialize Server instance."""
-        self.bus = cherrypy.engine
-        self.httpserver = None
-        self.interrupt = None
-        self.running = False
-
-    def httpserver_from_self(self, httpserver=None):
-        """Return a (httpserver, bind_addr) pair based on self attributes."""
-        if httpserver is None:
-            httpserver = self.instance
-        if httpserver is None:
-            from cherrypy import _cpwsgi_server
-            httpserver = _cpwsgi_server.CPWSGIServer(self)
-        if isinstance(httpserver, text_or_bytes):
-            # Is anyone using this? Can I add an arg?
-            httpserver = attributes(httpserver)(self)
-        return httpserver, self.bind_addr
-
-    def start(self):
-        """Start the HTTP server."""
-        if not self.httpserver:
-            self.httpserver, self.bind_addr = self.httpserver_from_self()
-        super(Server, self).start()
-    start.priority = 75
-
-    @property
-    def bind_addr(self):
-        """Return bind address.
-
-        A (host, port) tuple for TCP sockets or a str for Unix domain sockts.
-        """
-        if self.socket_file:
-            return self.socket_file
-        if self.socket_host is None and self.socket_port is None:
-            return None
-        return (self.socket_host, self.socket_port)
-
-    @bind_addr.setter
-    def bind_addr(self, value):
-        if value is None:
-            self.socket_file = None
-            self.socket_host = None
-            self.socket_port = None
-        elif isinstance(value, text_or_bytes):
-            self.socket_file = value
-            self.socket_host = None
-            self.socket_port = None
-        else:
-            try:
-                self.socket_host, self.socket_port = value
-                self.socket_file = None
-            except ValueError:
-                raise ValueError('bind_addr must be a (host, port) tuple '
-                                 '(for TCP sockets) or a string (for Unix '
-                                 'domain sockets), not %r' % value)
-
-    def base(self):
-        """Return the base for this server.
-
-        e.i. scheme://host[:port] or sock file
-        """
-        if self.socket_file:
-            return self.socket_file
-
-        host = self.socket_host
-        if host in ('0.0.0.0', '::'):
-            # 0.0.0.0 is INADDR_ANY and :: is IN6ADDR_ANY.
-            # Look up the host name, which should be the
-            # safest thing to spit out in a URL.
-            import socket
-            host = socket.gethostname()
-
-        port = self.socket_port
-
-        if self.ssl_certificate:
-            scheme = 'https'
-            if port != 443:
-                host += ':%s' % port
-        else:
-            scheme = 'http'
-            if port != 80:
-                host += ':%s' % port
-
-        return '%s://%s' % (scheme, host)
diff --git a/libraries/cherrypy/_cptools.py b/libraries/cherrypy/_cptools.py
deleted file mode 100644
index 57460285..00000000
--- a/libraries/cherrypy/_cptools.py
+++ /dev/null
@@ -1,509 +0,0 @@
-"""CherryPy tools. A "tool" is any helper, adapted to CP.
-
-Tools are usually designed to be used in a variety of ways (although some
-may only offer one if they choose):
-
-    Library calls
-        All tools are callables that can be used wherever needed.
-        The arguments are straightforward and should be detailed within the
-        docstring.
-
-    Function decorators
-        All tools, when called, may be used as decorators which configure
-        individual CherryPy page handlers (methods on the CherryPy tree).
-        That is, "@tools.anytool()" should "turn on" the tool via the
-        decorated function's _cp_config attribute.
-
-    CherryPy config
-        If a tool exposes a "_setup" callable, it will be called
-        once per Request (if the feature is "turned on" via config).
-
-Tools may be implemented as any object with a namespace. The builtins
-are generally either modules or instances of the tools.Tool class.
-"""
-
-import six
-
-import cherrypy
-from cherrypy._helper import expose
-
-from cherrypy.lib import cptools, encoding, static, jsontools
-from cherrypy.lib import sessions as _sessions, xmlrpcutil as _xmlrpc
-from cherrypy.lib import caching as _caching
-from cherrypy.lib import auth_basic, auth_digest
-
-
-def _getargs(func):
-    """Return the names of all static arguments to the given function."""
-    # Use this instead of importing inspect for less mem overhead.
-    import types
-    if six.PY3:
-        if isinstance(func, types.MethodType):
-            func = func.__func__
-        co = func.__code__
-    else:
-        if isinstance(func, types.MethodType):
-            func = func.im_func
-        co = func.func_code
-    return co.co_varnames[:co.co_argcount]
-
-
-_attr_error = (
-    'CherryPy Tools cannot be turned on directly. Instead, turn them '
-    'on via config, or use them as decorators on your page handlers.'
-)
-
-
-class Tool(object):
-
-    """A registered function for use with CherryPy request-processing hooks.
-
-    help(tool.callable) should give you more information about this Tool.
-    """
-
-    namespace = 'tools'
-
-    def __init__(self, point, callable, name=None, priority=50):
-        self._point = point
-        self.callable = callable
-        self._name = name
-        self._priority = priority
-        self.__doc__ = self.callable.__doc__
-        self._setargs()
-
-    @property
-    def on(self):
-        raise AttributeError(_attr_error)
-
-    @on.setter
-    def on(self, value):
-        raise AttributeError(_attr_error)
-
-    def _setargs(self):
-        """Copy func parameter names to obj attributes."""
-        try:
-            for arg in _getargs(self.callable):
-                setattr(self, arg, None)
-        except (TypeError, AttributeError):
-            if hasattr(self.callable, '__call__'):
-                for arg in _getargs(self.callable.__call__):
-                    setattr(self, arg, None)
-        # IronPython 1.0 raises NotImplementedError because
-        # inspect.getargspec tries to access Python bytecode
-        # in co_code attribute.
-        except NotImplementedError:
-            pass
-        # IronPython 1B1 may raise IndexError in some cases,
-        # but if we trap it here it doesn't prevent CP from working.
-        except IndexError:
-            pass
-
-    def _merged_args(self, d=None):
-        """Return a dict of configuration entries for this Tool."""
-        if d:
-            conf = d.copy()
-        else:
-            conf = {}
-
-        tm = cherrypy.serving.request.toolmaps[self.namespace]
-        if self._name in tm:
-            conf.update(tm[self._name])
-
-        if 'on' in conf:
-            del conf['on']
-
-        return conf
-
-    def __call__(self, *args, **kwargs):
-        """Compile-time decorator (turn on the tool in config).
-
-        For example::
-
-            @expose
-            @tools.proxy()
-            def whats_my_base(self):
-                return cherrypy.request.base
-        """
-        if args:
-            raise TypeError('The %r Tool does not accept positional '
-                            'arguments; you must use keyword arguments.'
-                            % self._name)
-
-        def tool_decorator(f):
-            if not hasattr(f, '_cp_config'):
-                f._cp_config = {}
-            subspace = self.namespace + '.' + self._name + '.'
-            f._cp_config[subspace + 'on'] = True
-            for k, v in kwargs.items():
-                f._cp_config[subspace + k] = v
-            return f
-        return tool_decorator
-
-    def _setup(self):
-        """Hook this tool into cherrypy.request.
-
-        The standard CherryPy request object will automatically call this
-        method when the tool is "turned on" in config.
-        """
-        conf = self._merged_args()
-        p = conf.pop('priority', None)
-        if p is None:
-            p = getattr(self.callable, 'priority', self._priority)
-        cherrypy.serving.request.hooks.attach(self._point, self.callable,
-                                              priority=p, **conf)
-
-
-class HandlerTool(Tool):
-
-    """Tool which is called 'before main', that may skip normal handlers.
-
-    If the tool successfully handles the request (by setting response.body),
-    if should return True. This will cause CherryPy to skip any 'normal' page
-    handler. If the tool did not handle the request, it should return False
-    to tell CherryPy to continue on and call the normal page handler. If the
-    tool is declared AS a page handler (see the 'handler' method), returning
-    False will raise NotFound.
-    """
-
-    def __init__(self, callable, name=None):
-        Tool.__init__(self, 'before_handler', callable, name)
-
-    def handler(self, *args, **kwargs):
-        """Use this tool as a CherryPy page handler.
-
-        For example::
-
-            class Root:
-                nav = tools.staticdir.handler(section="/nav", dir="nav",
-                                              root=absDir)
-        """
-        @expose
-        def handle_func(*a, **kw):
-            handled = self.callable(*args, **self._merged_args(kwargs))
-            if not handled:
-                raise cherrypy.NotFound()
-            return cherrypy.serving.response.body
-        return handle_func
-
-    def _wrapper(self, **kwargs):
-        if self.callable(**kwargs):
-            cherrypy.serving.request.handler = None
-
-    def _setup(self):
-        """Hook this tool into cherrypy.request.
-
-        The standard CherryPy request object will automatically call this
-        method when the tool is "turned on" in config.
-        """
-        conf = self._merged_args()
-        p = conf.pop('priority', None)
-        if p is None:
-            p = getattr(self.callable, 'priority', self._priority)
-        cherrypy.serving.request.hooks.attach(self._point, self._wrapper,
-                                              priority=p, **conf)
-
-
-class HandlerWrapperTool(Tool):
-
-    """Tool which wraps request.handler in a provided wrapper function.
-
-    The 'newhandler' arg must be a handler wrapper function that takes a
-    'next_handler' argument, plus ``*args`` and ``**kwargs``. Like all
-    page handler
-    functions, it must return an iterable for use as cherrypy.response.body.
-
-    For example, to allow your 'inner' page handlers to return dicts
-    which then get interpolated into a template::
-
-        def interpolator(next_handler, *args, **kwargs):
-            filename = cherrypy.request.config.get('template')
-            cherrypy.response.template = env.get_template(filename)
-            response_dict = next_handler(*args, **kwargs)
-            return cherrypy.response.template.render(**response_dict)
-        cherrypy.tools.jinja = HandlerWrapperTool(interpolator)
-    """
-
-    def __init__(self, newhandler, point='before_handler', name=None,
-                 priority=50):
-        self.newhandler = newhandler
-        self._point = point
-        self._name = name
-        self._priority = priority
-
-    def callable(self, *args, **kwargs):
-        innerfunc = cherrypy.serving.request.handler
-
-        def wrap(*args, **kwargs):
-            return self.newhandler(innerfunc, *args, **kwargs)
-        cherrypy.serving.request.handler = wrap
-
-
-class ErrorTool(Tool):
-
-    """Tool which is used to replace the default request.error_response."""
-
-    def __init__(self, callable, name=None):
-        Tool.__init__(self, None, callable, name)
-
-    def _wrapper(self):
-        self.callable(**self._merged_args())
-
-    def _setup(self):
-        """Hook this tool into cherrypy.request.
-
-        The standard CherryPy request object will automatically call this
-        method when the tool is "turned on" in config.
-        """
-        cherrypy.serving.request.error_response = self._wrapper
-
-
-#                              Builtin tools                              #
-
-
-class SessionTool(Tool):
-
-    """Session Tool for CherryPy.
-
-    sessions.locking
-        When 'implicit' (the default), the session will be locked for you,
-        just before running the page handler.
-
-        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
-        (`issue <https://github.com/cherrypy/cherrypy/issues/630>`_).
-
-        When 'explicit' (or any other value), you need to call
-        cherrypy.session.acquire_lock() yourself before using
-        session data.
-    """
-
-    def __init__(self):
-        # _sessions.init must be bound after headers are read
-        Tool.__init__(self, 'before_request_body', _sessions.init)
-
-    def _lock_session(self):
-        cherrypy.serving.session.acquire_lock()
-
-    def _setup(self):
-        """Hook this tool into cherrypy.request.
-
-        The standard CherryPy request object will automatically call this
-        method when the tool is "turned on" in config.
-        """
-        hooks = cherrypy.serving.request.hooks
-
-        conf = self._merged_args()
-
-        p = conf.pop('priority', None)
-        if p is None:
-            p = getattr(self.callable, 'priority', self._priority)
-
-        hooks.attach(self._point, self.callable, priority=p, **conf)
-
-        locking = conf.pop('locking', 'implicit')
-        if locking == 'implicit':
-            hooks.attach('before_handler', self._lock_session)
-        elif locking == 'early':
-            # Lock before the request body (but after _sessions.init runs!)
-            hooks.attach('before_request_body', self._lock_session,
-                         priority=60)
-        else:
-            # Don't lock
-            pass
-
-        hooks.attach('before_finalize', _sessions.save)
-        hooks.attach('on_end_request', _sessions.close)
-
-    def regenerate(self):
-        """Drop the current session and make a new one (with a new id)."""
-        sess = cherrypy.serving.session
-        sess.regenerate()
-
-        # Grab cookie-relevant tool args
-        relevant = 'path', 'path_header', 'name', 'timeout', 'domain', 'secure'
-        conf = dict(
-            (k, v)
-            for k, v in self._merged_args().items()
-            if k in relevant
-        )
-        _sessions.set_response_cookie(**conf)
-
-
-class XMLRPCController(object):
-
-    """A Controller (page handler collection) for XML-RPC.
-
-    To use it, have your controllers subclass this base class (it will
-    turn on the tool for you).
-
-    You can also supply the following optional config entries::
-
-        tools.xmlrpc.encoding: 'utf-8'
-        tools.xmlrpc.allow_none: 0
-
-    XML-RPC is a rather discontinuous layer over HTTP; dispatching to the
-    appropriate handler must first be performed according to the URL, and
-    then a second dispatch step must take place according to the RPC method
-    specified in the request body. It also allows a superfluous "/RPC2"
-    prefix in the URL, supplies its own handler args in the body, and
-    requires a 200 OK "Fault" response instead of 404 when the desired
-    method is not found.
-
-    Therefore, XML-RPC cannot be implemented for CherryPy via a Tool alone.
-    This Controller acts as the dispatch target for the first half (based
-    on the URL); it then reads the RPC method from the request body and
-    does its own second dispatch step based on that method. It also reads
-    body params, and returns a Fault on error.
-
-    The XMLRPCDispatcher strips any /RPC2 prefix; if you aren't using /RPC2
-    in your URL's, you can safely skip turning on the XMLRPCDispatcher.
-    Otherwise, you need to use declare it in config::
-
-        request.dispatch: cherrypy.dispatch.XMLRPCDispatcher()
-    """
-
-    # Note we're hard-coding this into the 'tools' namespace. We could do
-    # a huge amount of work to make it relocatable, but the only reason why
-    # would be if someone actually disabled the default_toolbox. Meh.
-    _cp_config = {'tools.xmlrpc.on': True}
-
-    @expose
-    def default(self, *vpath, **params):
-        rpcparams, rpcmethod = _xmlrpc.process_body()
-
-        subhandler = self
-        for attr in str(rpcmethod).split('.'):
-            subhandler = getattr(subhandler, attr, None)
-
-        if subhandler and getattr(subhandler, 'exposed', False):
-            body = subhandler(*(vpath + rpcparams), **params)
-
-        else:
-            # https://github.com/cherrypy/cherrypy/issues/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
-            raise Exception('method "%s" is not supported' % attr)
-
-        conf = cherrypy.serving.request.toolmaps['tools'].get('xmlrpc', {})
-        _xmlrpc.respond(body,
-                        conf.get('encoding', 'utf-8'),
-                        conf.get('allow_none', 0))
-        return cherrypy.serving.response.body
-
-
-class SessionAuthTool(HandlerTool):
-    pass
-
-
-class CachingTool(Tool):
-
-    """Caching Tool for CherryPy."""
-
-    def _wrapper(self, **kwargs):
-        request = cherrypy.serving.request
-        if _caching.get(**kwargs):
-            request.handler = None
-        else:
-            if request.cacheable:
-                # Note the devious technique here of adding hooks on the fly
-                request.hooks.attach('before_finalize', _caching.tee_output,
-                                     priority=100)
-    _wrapper.priority = 90
-
-    def _setup(self):
-        """Hook caching into cherrypy.request."""
-        conf = self._merged_args()
-
-        p = conf.pop('priority', None)
-        cherrypy.serving.request.hooks.attach('before_handler', self._wrapper,
-                                              priority=p, **conf)
-
-
-class Toolbox(object):
-
-    """A collection of Tools.
-
-    This object also functions as a config namespace handler for itself.
-    Custom toolboxes should be added to each Application's toolboxes dict.
-    """
-
-    def __init__(self, namespace):
-        self.namespace = namespace
-
-    def __setattr__(self, name, value):
-        # If the Tool._name is None, supply it from the attribute name.
-        if isinstance(value, Tool):
-            if value._name is None:
-                value._name = name
-            value.namespace = self.namespace
-        object.__setattr__(self, name, value)
-
-    def __enter__(self):
-        """Populate request.toolmaps from tools specified in config."""
-        cherrypy.serving.request.toolmaps[self.namespace] = map = {}
-
-        def populate(k, v):
-            toolname, arg = k.split('.', 1)
-            bucket = map.setdefault(toolname, {})
-            bucket[arg] = v
-        return populate
-
-    def __exit__(self, exc_type, exc_val, exc_tb):
-        """Run tool._setup() for each tool in our toolmap."""
-        map = cherrypy.serving.request.toolmaps.get(self.namespace)
-        if map:
-            for name, settings in map.items():
-                if settings.get('on', False):
-                    tool = getattr(self, name)
-                    tool._setup()
-
-    def register(self, point, **kwargs):
-        """
-        Return a decorator which registers the function
-        at the given hook point.
-        """
-        def decorator(func):
-            attr_name = kwargs.get('name', func.__name__)
-            tool = Tool(point, func, **kwargs)
-            setattr(self, attr_name, tool)
-            return func
-        return decorator
-
-
-default_toolbox = _d = Toolbox('tools')
-_d.session_auth = SessionAuthTool(cptools.session_auth)
-_d.allow = Tool('on_start_resource', cptools.allow)
-_d.proxy = Tool('before_request_body', cptools.proxy, priority=30)
-_d.response_headers = Tool('on_start_resource', cptools.response_headers)
-_d.log_tracebacks = Tool('before_error_response', cptools.log_traceback)
-_d.log_headers = Tool('before_error_response', cptools.log_request_headers)
-_d.log_hooks = Tool('on_end_request', cptools.log_hooks, priority=100)
-_d.err_redirect = ErrorTool(cptools.redirect)
-_d.etags = Tool('before_finalize', cptools.validate_etags, priority=75)
-_d.decode = Tool('before_request_body', encoding.decode)
-# the order of encoding, gzip, caching is important
-_d.encode = Tool('before_handler', encoding.ResponseEncoder, priority=70)
-_d.gzip = Tool('before_finalize', encoding.gzip, priority=80)
-_d.staticdir = HandlerTool(static.staticdir)
-_d.staticfile = HandlerTool(static.staticfile)
-_d.sessions = SessionTool()
-_d.xmlrpc = ErrorTool(_xmlrpc.on_error)
-_d.caching = CachingTool('before_handler', _caching.get, 'caching')
-_d.expires = Tool('before_finalize', _caching.expires)
-_d.ignore_headers = Tool('before_request_body', cptools.ignore_headers)
-_d.referer = Tool('before_request_body', cptools.referer)
-_d.trailing_slash = Tool('before_handler', cptools.trailing_slash, priority=60)
-_d.flatten = Tool('before_finalize', cptools.flatten)
-_d.accept = Tool('on_start_resource', cptools.accept)
-_d.redirect = Tool('on_start_resource', cptools.redirect)
-_d.autovary = Tool('on_start_resource', cptools.autovary, priority=0)
-_d.json_in = Tool('before_request_body', jsontools.json_in, priority=30)
-_d.json_out = Tool('before_handler', jsontools.json_out, priority=30)
-_d.auth_basic = Tool('before_handler', auth_basic.basic_auth, priority=1)
-_d.auth_digest = Tool('before_handler', auth_digest.digest_auth, priority=1)
-_d.params = Tool('before_handler', cptools.convert_params, priority=15)
-
-del _d, cptools, encoding, static
diff --git a/libraries/cherrypy/_cptree.py b/libraries/cherrypy/_cptree.py
deleted file mode 100644
index ceb54379..00000000
--- a/libraries/cherrypy/_cptree.py
+++ /dev/null
@@ -1,313 +0,0 @@
-"""CherryPy Application and Tree objects."""
-
-import os
-
-import six
-
-import cherrypy
-from cherrypy._cpcompat import ntou
-from cherrypy import _cpconfig, _cplogging, _cprequest, _cpwsgi, tools
-from cherrypy.lib import httputil, reprconf
-
-
-class Application(object):
-    """A CherryPy Application.
-
-    Servers and gateways should not instantiate Request objects directly.
-    Instead, they should ask an Application object for a request object.
-
-    An instance of this class may also be used as a WSGI callable
-    (WSGI application object) for itself.
-    """
-
-    root = None
-    """The top-most container of page handlers for this app. Handlers should
-    be arranged in a hierarchy of attributes, matching the expected URI
-    hierarchy; the default dispatcher then searches this hierarchy for a
-    matching handler. When using a dispatcher other than the default,
-    this value may be None."""
-
-    config = {}
-    """A dict of {path: pathconf} pairs, where 'pathconf' is itself a dict
-    of {key: value} pairs."""
-
-    namespaces = reprconf.NamespaceSet()
-    toolboxes = {'tools': cherrypy.tools}
-
-    log = None
-    """A LogManager instance. See _cplogging."""
-
-    wsgiapp = None
-    """A CPWSGIApp instance. See _cpwsgi."""
-
-    request_class = _cprequest.Request
-    response_class = _cprequest.Response
-
-    relative_urls = False
-
-    def __init__(self, root, script_name='', config=None):
-        """Initialize Application with given root."""
-        self.log = _cplogging.LogManager(id(self), cherrypy.log.logger_root)
-        self.root = root
-        self.script_name = script_name
-        self.wsgiapp = _cpwsgi.CPWSGIApp(self)
-
-        self.namespaces = self.namespaces.copy()
-        self.namespaces['log'] = lambda k, v: setattr(self.log, k, v)
-        self.namespaces['wsgi'] = self.wsgiapp.namespace_handler
-
-        self.config = self.__class__.config.copy()
-        if config:
-            self.merge(config)
-
-    def __repr__(self):
-        """Generate a representation of the Application instance."""
-        return '%s.%s(%r, %r)' % (self.__module__, self.__class__.__name__,
-                                  self.root, self.script_name)
-
-    script_name_doc = """The URI "mount point" for this app. A mount point
-    is that portion of the URI which is constant for all URIs that are
-    serviced by this application; it does not include scheme, host, or proxy
-    ("virtual host") portions of the URI.
-
-    For example, if script_name is "/my/cool/app", then the URL
-    "http://www.example.com/my/cool/app/page1" might be handled by a
-    "page1" method on the root object.
-
-    The value of script_name MUST NOT end in a slash. If the script_name
-    refers to the root of the URI, it MUST be an empty string (not "/").
-
-    If script_name is explicitly set to None, then the script_name will be
-    provided for each call from request.wsgi_environ['SCRIPT_NAME'].
-    """
-
-    @property
-    def script_name(self):  # noqa: D401; irrelevant for properties
-        """The URI "mount point" for this app.
-
-        A mount point is that portion of the URI which is constant for all URIs
-        that are serviced by this application; it does not include scheme,
-        host, or proxy ("virtual host") portions of the URI.
-
-        For example, if script_name is "/my/cool/app", then the URL
-        "http://www.example.com/my/cool/app/page1" might be handled by a
-        "page1" method on the root object.
-
-        The value of script_name MUST NOT end in a slash. If the script_name
-        refers to the root of the URI, it MUST be an empty string (not "/").
-
-        If script_name is explicitly set to None, then the script_name will be
-        provided for each call from request.wsgi_environ['SCRIPT_NAME'].
-        """
-        if self._script_name is not None:
-            return self._script_name
-
-        # A `_script_name` with a value of None signals that the script name
-        # should be pulled from WSGI environ.
-        return cherrypy.serving.request.wsgi_environ['SCRIPT_NAME'].rstrip('/')
-
-    @script_name.setter
-    def script_name(self, value):
-        if value:
-            value = value.rstrip('/')
-        self._script_name = value
-
-    def merge(self, config):
-        """Merge the given config into self.config."""
-        _cpconfig.merge(self.config, config)
-
-        # Handle namespaces specified in config.
-        self.namespaces(self.config.get('/', {}))
-
-    def find_config(self, path, key, default=None):
-        """Return the most-specific value for key along path, or default."""
-        trail = path or '/'
-        while trail:
-            nodeconf = self.config.get(trail, {})
-
-            if key in nodeconf:
-                return nodeconf[key]
-
-            lastslash = trail.rfind('/')
-            if lastslash == -1:
-                break
-            elif lastslash == 0 and trail != '/':
-                trail = '/'
-            else:
-                trail = trail[:lastslash]
-
-        return default
-
-    def get_serving(self, local, remote, scheme, sproto):
-        """Create and return a Request and Response object."""
-        req = self.request_class(local, remote, scheme, sproto)
-        req.app = self
-
-        for name, toolbox in self.toolboxes.items():
-            req.namespaces[name] = toolbox
-
-        resp = self.response_class()
-        cherrypy.serving.load(req, resp)
-        cherrypy.engine.publish('acquire_thread')
-        cherrypy.engine.publish('before_request')
-
-        return req, resp
-
-    def release_serving(self):
-        """Release the current serving (request and response)."""
-        req = cherrypy.serving.request
-
-        cherrypy.engine.publish('after_request')
-
-        try:
-            req.close()
-        except Exception:
-            cherrypy.log(traceback=True, severity=40)
-
-        cherrypy.serving.clear()
-
-    def __call__(self, environ, start_response):
-        """Call a WSGI-callable."""
-        return self.wsgiapp(environ, start_response)
-
-
-class Tree(object):
-    """A registry of CherryPy applications, mounted at diverse points.
-
-    An instance of this class may also be used as a WSGI callable
-    (WSGI application object), in which case it dispatches to all
-    mounted apps.
-    """
-
-    apps = {}
-    """
-    A dict of the form {script name: application}, where "script name"
-    is a string declaring the URI mount point (no trailing slash), and
-    "application" is an instance of cherrypy.Application (or an arbitrary
-    WSGI callable if you happen to be using a WSGI server)."""
-
-    def __init__(self):
-        """Initialize registry Tree."""
-        self.apps = {}
-
-    def mount(self, root, script_name='', config=None):
-        """Mount a new app from a root object, script_name, and config.
-
-        root
-            An instance of a "controller class" (a collection of page
-            handler methods) which represents the root of the application.
-            This may also be an Application instance, or None if using
-            a dispatcher other than the default.
-
-        script_name
-            A string containing the "mount point" of the application.
-            This should start with a slash, and be the path portion of the
-            URL at which to mount the given root. For example, if root.index()
-            will handle requests to "http://www.example.com:8080/dept/app1/",
-            then the script_name argument would be "/dept/app1".
-
-            It MUST NOT end in a slash. If the script_name refers to the
-            root of the URI, it MUST be an empty string (not "/").
-
-        config
-            A file or dict containing application config.
-        """
-        if script_name is None:
-            raise TypeError(
-                "The 'script_name' argument may not be None. Application "
-                'objects may, however, possess a script_name of None (in '
-                'order to inpect the WSGI environ for SCRIPT_NAME upon each '
-                'request). You cannot mount such Applications on this Tree; '
-                'you must pass them to a WSGI server interface directly.')
-
-        # Next line both 1) strips trailing slash and 2) maps "/" -> "".
-        script_name = script_name.rstrip('/')
-
-        if isinstance(root, Application):
-            app = root
-            if script_name != '' and script_name != app.script_name:
-                raise ValueError(
-                    'Cannot specify a different script name and pass an '
-                    'Application instance to cherrypy.mount')
-            script_name = app.script_name
-        else:
-            app = Application(root, script_name)
-
-            # If mounted at "", add favicon.ico
-            needs_favicon = (
-                script_name == ''
-                and root is not None
-                and not hasattr(root, 'favicon_ico')
-            )
-            if needs_favicon:
-                favicon = os.path.join(
-                    os.getcwd(),
-                    os.path.dirname(__file__),
-                    'favicon.ico',
-                )
-                root.favicon_ico = tools.staticfile.handler(favicon)
-
-        if config:
-            app.merge(config)
-
-        self.apps[script_name] = app
-
-        return app
-
-    def graft(self, wsgi_callable, script_name=''):
-        """Mount a wsgi callable at the given script_name."""
-        # Next line both 1) strips trailing slash and 2) maps "/" -> "".
-        script_name = script_name.rstrip('/')
-        self.apps[script_name] = wsgi_callable
-
-    def script_name(self, path=None):
-        """Return the script_name of the app at the given path, or None.
-
-        If path is None, cherrypy.request is used.
-        """
-        if path is None:
-            try:
-                request = cherrypy.serving.request
-                path = httputil.urljoin(request.script_name,
-                                        request.path_info)
-            except AttributeError:
-                return None
-
-        while True:
-            if path in self.apps:
-                return path
-
-            if path == '':
-                return None
-
-            # Move one node up the tree and try again.
-            path = path[:path.rfind('/')]
-
-    def __call__(self, environ, start_response):
-        """Pre-initialize WSGI env and call WSGI-callable."""
-        # If you're calling this, then you're probably setting SCRIPT_NAME
-        # to '' (some WSGI servers always set SCRIPT_NAME to '').
-        # Try to look up the app using the full path.
-        env1x = environ
-        if six.PY2 and environ.get(ntou('wsgi.version')) == (ntou('u'), 0):
-            env1x = _cpwsgi.downgrade_wsgi_ux_to_1x(environ)
-        path = httputil.urljoin(env1x.get('SCRIPT_NAME', ''),
-                                env1x.get('PATH_INFO', ''))
-        sn = self.script_name(path or '/')
-        if sn is None:
-            start_response('404 Not Found', [])
-            return []
-
-        app = self.apps[sn]
-
-        # Correct the SCRIPT_NAME and PATH_INFO environ entries.
-        environ = environ.copy()
-        if six.PY2 and environ.get(ntou('wsgi.version')) == (ntou('u'), 0):
-            # Python 2/WSGI u.0: all strings MUST be of type unicode
-            enc = environ[ntou('wsgi.url_encoding')]
-            environ[ntou('SCRIPT_NAME')] = sn.decode(enc)
-            environ[ntou('PATH_INFO')] = path[len(sn.rstrip('/')):].decode(enc)
-        else:
-            environ['SCRIPT_NAME'] = sn
-            environ['PATH_INFO'] = path[len(sn.rstrip('/')):]
-        return app(environ, start_response)
diff --git a/libraries/cherrypy/_cpwsgi.py b/libraries/cherrypy/_cpwsgi.py
deleted file mode 100644
index 0b4942ff..00000000
--- a/libraries/cherrypy/_cpwsgi.py
+++ /dev/null
@@ -1,467 +0,0 @@
-"""WSGI interface (see PEP 333 and 3333).
-
-Note that WSGI environ keys and values are 'native strings'; that is,
-whatever the type of "" is. For Python 2, that's a byte string; for Python 3,
-it's a unicode string. But PEP 3333 says: "even if Python's str type is
-actually Unicode "under the hood", the content of native strings must
-still be translatable to bytes via the Latin-1 encoding!"
-"""
-
-import sys as _sys
-import io
-
-import six
-
-import cherrypy as _cherrypy
-from cherrypy._cpcompat import ntou
-from cherrypy import _cperror
-from cherrypy.lib import httputil
-from cherrypy.lib import is_closable_iterator
-
-
-def downgrade_wsgi_ux_to_1x(environ):
-    """Return a new environ dict for WSGI 1.x from the given WSGI u.x environ.
-    """
-    env1x = {}
-
-    url_encoding = environ[ntou('wsgi.url_encoding')]
-    for k, v in list(environ.items()):
-        if k in [ntou('PATH_INFO'), ntou('SCRIPT_NAME'), ntou('QUERY_STRING')]:
-            v = v.encode(url_encoding)
-        elif isinstance(v, six.text_type):
-            v = v.encode('ISO-8859-1')
-        env1x[k.encode('ISO-8859-1')] = v
-
-    return env1x
-
-
-class VirtualHost(object):
-
-    """Select a different WSGI application based on the Host header.
-
-    This can be useful when running multiple sites within one CP server.
-    It allows several domains to point to different applications. For example::
-
-        root = Root()
-        RootApp = cherrypy.Application(root)
-        Domain2App = cherrypy.Application(root)
-        SecureApp = cherrypy.Application(Secure())
-
-        vhost = cherrypy._cpwsgi.VirtualHost(
-            RootApp,
-            domains={
-                'www.domain2.example': Domain2App,
-                'www.domain2.example:443': SecureApp,
-            },
-        )
-
-        cherrypy.tree.graft(vhost)
-    """
-    default = None
-    """Required. The default WSGI application."""
-
-    use_x_forwarded_host = True
-    """If True (the default), any "X-Forwarded-Host"
-    request header will be used instead of the "Host" header. This
-    is commonly added by HTTP servers (such as Apache) when proxying."""
-
-    domains = {}
-    """A dict of {host header value: application} pairs.
-    The incoming "Host" request header is looked up in this dict,
-    and, if a match is found, the corresponding WSGI application
-    will be called instead of the default. Note that you often need
-    separate entries for "example.com" and "www.example.com".
-    In addition, "Host" headers may contain the port number.
-    """
-
-    def __init__(self, default, domains=None, use_x_forwarded_host=True):
-        self.default = default
-        self.domains = domains or {}
-        self.use_x_forwarded_host = use_x_forwarded_host
-
-    def __call__(self, environ, start_response):
-        domain = environ.get('HTTP_HOST', '')
-        if self.use_x_forwarded_host:
-            domain = environ.get('HTTP_X_FORWARDED_HOST', domain)
-
-        nextapp = self.domains.get(domain)
-        if nextapp is None:
-            nextapp = self.default
-        return nextapp(environ, start_response)
-
-
-class InternalRedirector(object):
-
-    """WSGI middleware that handles raised cherrypy.InternalRedirect."""
-
-    def __init__(self, nextapp, recursive=False):
-        self.nextapp = nextapp
-        self.recursive = recursive
-
-    def __call__(self, environ, start_response):
-        redirections = []
-        while True:
-            environ = environ.copy()
-            try:
-                return self.nextapp(environ, start_response)
-            except _cherrypy.InternalRedirect:
-                ir = _sys.exc_info()[1]
-                sn = environ.get('SCRIPT_NAME', '')
-                path = environ.get('PATH_INFO', '')
-                qs = environ.get('QUERY_STRING', '')
-
-                # Add the *previous* path_info + qs to redirections.
-                old_uri = sn + path
-                if qs:
-                    old_uri += '?' + qs
-                redirections.append(old_uri)
-
-                if not self.recursive:
-                    # Check to see if the new URI has been redirected to
-                    # already
-                    new_uri = sn + ir.path
-                    if ir.query_string:
-                        new_uri += '?' + ir.query_string
-                    if new_uri in redirections:
-                        ir.request.close()
-                        tmpl = (
-                            'InternalRedirector visited the same URL twice: %r'
-                        )
-                        raise RuntimeError(tmpl % new_uri)
-
-                # Munge the environment and try again.
-                environ['REQUEST_METHOD'] = 'GET'
-                environ['PATH_INFO'] = ir.path
-                environ['QUERY_STRING'] = ir.query_string
-                environ['wsgi.input'] = io.BytesIO()
-                environ['CONTENT_LENGTH'] = '0'
-                environ['cherrypy.previous_request'] = ir.request
-
-
-class ExceptionTrapper(object):
-
-    """WSGI middleware that traps exceptions."""
-
-    def __init__(self, nextapp, throws=(KeyboardInterrupt, SystemExit)):
-        self.nextapp = nextapp
-        self.throws = throws
-
-    def __call__(self, environ, start_response):
-        return _TrappedResponse(
-            self.nextapp,
-            environ,
-            start_response,
-            self.throws
-        )
-
-
-class _TrappedResponse(object):
-
-    response = iter([])
-
-    def __init__(self, nextapp, environ, start_response, throws):
-        self.nextapp = nextapp
-        self.environ = environ
-        self.start_response = start_response
-        self.throws = throws
-        self.started_response = False
-        self.response = self.trap(
-            self.nextapp, self.environ, self.start_response,
-        )
-        self.iter_response = iter(self.response)
-
-    def __iter__(self):
-        self.started_response = True
-        return self
-
-    def __next__(self):
-        return self.trap(next, self.iter_response)
-
-    # todo: https://pythonhosted.org/six/#six.Iterator
-    if six.PY2:
-        next = __next__
-
-    def close(self):
-        if hasattr(self.response, 'close'):
-            self.response.close()
-
-    def trap(self, func, *args, **kwargs):
-        try:
-            return func(*args, **kwargs)
-        except self.throws:
-            raise
-        except StopIteration:
-            raise
-        except Exception:
-            tb = _cperror.format_exc()
-            _cherrypy.log(tb, severity=40)
-            if not _cherrypy.request.show_tracebacks:
-                tb = ''
-            s, h, b = _cperror.bare_error(tb)
-            if six.PY3:
-                # What fun.
-                s = s.decode('ISO-8859-1')
-                h = [
-                    (k.decode('ISO-8859-1'), v.decode('ISO-8859-1'))
-                    for k, v in h
-                ]
-            if self.started_response:
-                # Empty our iterable (so future calls raise StopIteration)
-                self.iter_response = iter([])
-            else:
-                self.iter_response = iter(b)
-
-            try:
-                self.start_response(s, h, _sys.exc_info())
-            except Exception:
-                # "The application must not trap any exceptions raised by
-                # start_response, if it called start_response with exc_info.
-                # Instead, it should allow such exceptions to propagate
-                # back to the server or gateway."
-                # But we still log and call close() to clean up ourselves.
-                _cherrypy.log(traceback=True, severity=40)
-                raise
-
-            if self.started_response:
-                return b''.join(b)
-            else:
-                return b
-
-
-#                           WSGI-to-CP Adapter                           #
-
-
-class AppResponse(object):
-
-    """WSGI response iterable for CherryPy applications."""
-
-    def __init__(self, environ, start_response, cpapp):
-        self.cpapp = cpapp
-        try:
-            if six.PY2:
-                if environ.get(ntou('wsgi.version')) == (ntou('u'), 0):
-                    environ = downgrade_wsgi_ux_to_1x(environ)
-            self.environ = environ
-            self.run()
-
-            r = _cherrypy.serving.response
-
-            outstatus = r.output_status
-            if not isinstance(outstatus, bytes):
-                raise TypeError('response.output_status is not a byte string.')
-
-            outheaders = []
-            for k, v in r.header_list:
-                if not isinstance(k, bytes):
-                    tmpl = 'response.header_list key %r is not a byte string.'
-                    raise TypeError(tmpl % k)
-                if not isinstance(v, bytes):
-                    tmpl = (
-                        'response.header_list value %r is not a byte string.'
-                    )
-                    raise TypeError(tmpl % v)
-                outheaders.append((k, v))
-
-            if six.PY3:
-                # According to PEP 3333, when using Python 3, the response
-                # status and headers must be bytes masquerading as unicode;
-                # that is, they must be of type "str" but are restricted to
-                # code points in the "latin-1" set.
-                outstatus = outstatus.decode('ISO-8859-1')
-                outheaders = [
-                    (k.decode('ISO-8859-1'), v.decode('ISO-8859-1'))
-                    for k, v in outheaders
-                ]
-
-            self.iter_response = iter(r.body)
-            self.write = start_response(outstatus, outheaders)
-        except BaseException:
-            self.close()
-            raise
-
-    def __iter__(self):
-        return self
-
-    def __next__(self):
-        return next(self.iter_response)
-
-    # todo: https://pythonhosted.org/six/#six.Iterator
-    if six.PY2:
-        next = __next__
-
-    def close(self):
-        """Close and de-reference the current request and response. (Core)"""
-        streaming = _cherrypy.serving.response.stream
-        self.cpapp.release_serving()
-
-        # We avoid the expense of examining the iterator to see if it's
-        # closable unless we are streaming the response, as that's the
-        # only situation where we are going to have an iterator which
-        # may not have been exhausted yet.
-        if streaming and is_closable_iterator(self.iter_response):
-            iter_close = self.iter_response.close
-            try:
-                iter_close()
-            except Exception:
-                _cherrypy.log(traceback=True, severity=40)
-
-    def run(self):
-        """Create a Request object using environ."""
-        env = self.environ.get
-
-        local = httputil.Host(
-            '',
-            int(env('SERVER_PORT', 80) or -1),
-            env('SERVER_NAME', ''),
-        )
-        remote = httputil.Host(
-            env('REMOTE_ADDR', ''),
-            int(env('REMOTE_PORT', -1) or -1),
-            env('REMOTE_HOST', ''),
-        )
-        scheme = env('wsgi.url_scheme')
-        sproto = env('ACTUAL_SERVER_PROTOCOL', 'HTTP/1.1')
-        request, resp = self.cpapp.get_serving(local, remote, scheme, sproto)
-
-        # LOGON_USER is served by IIS, and is the name of the
-        # user after having been mapped to a local account.
-        # Both IIS and Apache set REMOTE_USER, when possible.
-        request.login = env('LOGON_USER') or env('REMOTE_USER') or None
-        request.multithread = self.environ['wsgi.multithread']
-        request.multiprocess = self.environ['wsgi.multiprocess']
-        request.wsgi_environ = self.environ
-        request.prev = env('cherrypy.previous_request', None)
-
-        meth = self.environ['REQUEST_METHOD']
-
-        path = httputil.urljoin(
-            self.environ.get('SCRIPT_NAME', ''),
-            self.environ.get('PATH_INFO', ''),
-        )
-        qs = self.environ.get('QUERY_STRING', '')
-
-        path, qs = self.recode_path_qs(path, qs) or (path, qs)
-
-        rproto = self.environ.get('SERVER_PROTOCOL')
-        headers = self.translate_headers(self.environ)
-        rfile = self.environ['wsgi.input']
-        request.run(meth, path, qs, rproto, headers, rfile)
-
-    headerNames = {
-        'HTTP_CGI_AUTHORIZATION': 'Authorization',
-        'CONTENT_LENGTH': 'Content-Length',
-        'CONTENT_TYPE': 'Content-Type',
-        'REMOTE_HOST': 'Remote-Host',
-        'REMOTE_ADDR': 'Remote-Addr',
-    }
-
-    def recode_path_qs(self, path, qs):
-        if not six.PY3:
-            return
-
-        # This isn't perfect; if the given PATH_INFO is in the
-        # wrong encoding, it may fail to match the appropriate config
-        # section URI. But meh.
-        old_enc = self.environ.get('wsgi.url_encoding', 'ISO-8859-1')
-        new_enc = self.cpapp.find_config(
-            self.environ.get('PATH_INFO', ''),
-            'request.uri_encoding', 'utf-8',
-        )
-        if new_enc.lower() == old_enc.lower():
-            return
-
-        # Even though the path and qs are unicode, the WSGI server
-        # is required by PEP 3333 to coerce them to ISO-8859-1
-        # masquerading as unicode. So we have to encode back to
-        # bytes and then decode again using the "correct" encoding.
-        try:
-            return (
-                path.encode(old_enc).decode(new_enc),
-                qs.encode(old_enc).decode(new_enc),
-            )
-        except (UnicodeEncodeError, UnicodeDecodeError):
-            # Just pass them through without transcoding and hope.
-            pass
-
-    def translate_headers(self, environ):
-        """Translate CGI-environ header names to HTTP header names."""
-        for cgiName in environ:
-            # We assume all incoming header keys are uppercase already.
-            if cgiName in self.headerNames:
-                yield self.headerNames[cgiName], environ[cgiName]
-            elif cgiName[:5] == 'HTTP_':
-                # Hackish attempt at recovering original header names.
-                translatedHeader = cgiName[5:].replace('_', '-')
-                yield translatedHeader, environ[cgiName]
-
-
-class CPWSGIApp(object):
-
-    """A WSGI application object for a CherryPy Application."""
-
-    pipeline = [
-        ('ExceptionTrapper', ExceptionTrapper),
-        ('InternalRedirector', InternalRedirector),
-    ]
-    """A list of (name, wsgiapp) pairs. Each 'wsgiapp' MUST be a
-    constructor that takes an initial, positional 'nextapp' argument,
-    plus optional keyword arguments, and returns a WSGI application
-    (that takes environ and start_response arguments). The 'name' can
-    be any you choose, and will correspond to keys in self.config."""
-
-    head = None
-    """Rather than nest all apps in the pipeline on each call, it's only
-    done the first time, and the result is memoized into self.head. Set
-    this to None again if you change self.pipeline after calling self."""
-
-    config = {}
-    """A dict whose keys match names listed in the pipeline. Each
-    value is a further dict which will be passed to the corresponding
-    named WSGI callable (from the pipeline) as keyword arguments."""
-
-    response_class = AppResponse
-    """The class to instantiate and return as the next app in the WSGI chain.
-    """
-
-    def __init__(self, cpapp, pipeline=None):
-        self.cpapp = cpapp
-        self.pipeline = self.pipeline[:]
-        if pipeline:
-            self.pipeline.extend(pipeline)
-        self.config = self.config.copy()
-
-    def tail(self, environ, start_response):
-        """WSGI application callable for the actual CherryPy application.
-
-        You probably shouldn't call this; call self.__call__ instead,
-        so that any WSGI middleware in self.pipeline can run first.
-        """
-        return self.response_class(environ, start_response, self.cpapp)
-
-    def __call__(self, environ, start_response):
-        head = self.head
-        if head is None:
-            # Create and nest the WSGI apps in our pipeline (in reverse order).
-            # Then memoize the result in self.head.
-            head = self.tail
-            for name, callable in self.pipeline[::-1]:
-                conf = self.config.get(name, {})
-                head = callable(head, **conf)
-            self.head = head
-        return head(environ, start_response)
-
-    def namespace_handler(self, k, v):
-        """Config handler for the 'wsgi' namespace."""
-        if k == 'pipeline':
-            # Note this allows multiple 'wsgi.pipeline' config entries
-            # (but each entry will be processed in a 'random' order).
-            # It should also allow developers to set default middleware
-            # in code (passed to self.__init__) that deployers can add to
-            # (but not remove) via config.
-            self.pipeline.extend(v)
-        elif k == 'response_class':
-            self.response_class = v
-        else:
-            name, arg = k.split('.', 1)
-            bucket = self.config.setdefault(name, {})
-            bucket[arg] = v
diff --git a/libraries/cherrypy/_cpwsgi_server.py b/libraries/cherrypy/_cpwsgi_server.py
deleted file mode 100644
index 11dd846a..00000000
--- a/libraries/cherrypy/_cpwsgi_server.py
+++ /dev/null
@@ -1,110 +0,0 @@
-"""
-WSGI server interface (see PEP 333).
-
-This adds some CP-specific bits to the framework-agnostic cheroot package.
-"""
-import sys
-
-import cheroot.wsgi
-import cheroot.server
-
-import cherrypy
-
-
-class CPWSGIHTTPRequest(cheroot.server.HTTPRequest):
-    """Wrapper for cheroot.server.HTTPRequest.
-
-    This is a layer, which preserves URI parsing mode like it which was
-    before Cheroot v5.8.0.
-    """
-
-    def __init__(self, server, conn):
-        """Initialize HTTP request container instance.
-
-        Args:
-            server (cheroot.server.HTTPServer):
-                web server object receiving this request
-            conn (cheroot.server.HTTPConnection):
-                HTTP connection object for this request
-        """
-        super(CPWSGIHTTPRequest, self).__init__(
-            server, conn, proxy_mode=True
-        )
-
-
-class CPWSGIServer(cheroot.wsgi.Server):
-    """Wrapper for cheroot.wsgi.Server.
-
-    cheroot has been designed to not reference CherryPy in any way,
-    so that it can be used in other frameworks and applications. Therefore,
-    we wrap it here, so we can set our own mount points from cherrypy.tree
-    and apply some attributes from config -> cherrypy.server -> wsgi.Server.
-    """
-
-    fmt = 'CherryPy/{cherrypy.__version__} {cheroot.wsgi.Server.version}'
-    version = fmt.format(**globals())
-
-    def __init__(self, server_adapter=cherrypy.server):
-        """Initialize CPWSGIServer instance.
-
-        Args:
-            server_adapter (cherrypy._cpserver.Server): ...
-        """
-        self.server_adapter = server_adapter
-        self.max_request_header_size = (
-            self.server_adapter.max_request_header_size or 0
-        )
-        self.max_request_body_size = (
-            self.server_adapter.max_request_body_size or 0
-        )
-
-        server_name = (self.server_adapter.socket_host or
-                       self.server_adapter.socket_file or
-                       None)
-
-        self.wsgi_version = self.server_adapter.wsgi_version
-
-        super(CPWSGIServer, self).__init__(
-            server_adapter.bind_addr, cherrypy.tree,
-            self.server_adapter.thread_pool,
-            server_name,
-            max=self.server_adapter.thread_pool_max,
-            request_queue_size=self.server_adapter.socket_queue_size,
-            timeout=self.server_adapter.socket_timeout,
-            shutdown_timeout=self.server_adapter.shutdown_timeout,
-            accepted_queue_size=self.server_adapter.accepted_queue_size,
-            accepted_queue_timeout=self.server_adapter.accepted_queue_timeout,
-            peercreds_enabled=self.server_adapter.peercreds,
-            peercreds_resolve_enabled=self.server_adapter.peercreds_resolve,
-        )
-        self.ConnectionClass.RequestHandlerClass = CPWSGIHTTPRequest
-
-        self.protocol = self.server_adapter.protocol_version
-        self.nodelay = self.server_adapter.nodelay
-
-        if sys.version_info >= (3, 0):
-            ssl_module = self.server_adapter.ssl_module or 'builtin'
-        else:
-            ssl_module = self.server_adapter.ssl_module or 'pyopenssl'
-        if self.server_adapter.ssl_context:
-            adapter_class = cheroot.server.get_ssl_adapter_class(ssl_module)
-            self.ssl_adapter = adapter_class(
-                self.server_adapter.ssl_certificate,
-                self.server_adapter.ssl_private_key,
-                self.server_adapter.ssl_certificate_chain,
-                self.server_adapter.ssl_ciphers)
-            self.ssl_adapter.context = self.server_adapter.ssl_context
-        elif self.server_adapter.ssl_certificate:
-            adapter_class = cheroot.server.get_ssl_adapter_class(ssl_module)
-            self.ssl_adapter = adapter_class(
-                self.server_adapter.ssl_certificate,
-                self.server_adapter.ssl_private_key,
-                self.server_adapter.ssl_certificate_chain,
-                self.server_adapter.ssl_ciphers)
-
-        self.stats['Enabled'] = getattr(
-            self.server_adapter, 'statistics', False)
-
-    def error_log(self, msg='', level=20, traceback=False):
-        """Write given message to the error log."""
-        cherrypy.engine.log(msg, level, traceback)
diff --git a/libraries/cherrypy/_helper.py b/libraries/cherrypy/_helper.py
deleted file mode 100644
index 314550cb..00000000
--- a/libraries/cherrypy/_helper.py
+++ /dev/null
@@ -1,344 +0,0 @@
-"""Helper functions for CP apps."""
-
-import six
-from six.moves import urllib
-
-from cherrypy._cpcompat import text_or_bytes
-
-import cherrypy
-
-
-def expose(func=None, alias=None):
-    """Expose the function or class.
-
-    Optionally provide an alias or set of aliases.
-    """
-    def expose_(func):
-        func.exposed = True
-        if alias is not None:
-            if isinstance(alias, text_or_bytes):
-                parents[alias.replace('.', '_')] = func
-            else:
-                for a in alias:
-                    parents[a.replace('.', '_')] = func
-        return func
-
-    import sys
-    import types
-    decoratable_types = types.FunctionType, types.MethodType, type,
-    if six.PY2:
-        # Old-style classes are type types.ClassType.
-        decoratable_types += types.ClassType,
-    if isinstance(func, decoratable_types):
-        if alias is None:
-            # @expose
-            func.exposed = True
-            return func
-        else:
-            # func = expose(func, alias)
-            parents = sys._getframe(1).f_locals
-            return expose_(func)
-    elif func is None:
-        if alias is None:
-            # @expose()
-            parents = sys._getframe(1).f_locals
-            return expose_
-        else:
-            # @expose(alias="alias") or
-            # @expose(alias=["alias1", "alias2"])
-            parents = sys._getframe(1).f_locals
-            return expose_
-    else:
-        # @expose("alias") or
-        # @expose(["alias1", "alias2"])
-        parents = sys._getframe(1).f_locals
-        alias = func
-        return expose_
-
-
-def popargs(*args, **kwargs):
-    """Decorate _cp_dispatch.
-
-    (cherrypy.dispatch.Dispatcher.dispatch_method_name)
-
-    Optional keyword argument: handler=(Object or Function)
-
-    Provides a _cp_dispatch function that pops off path segments into
-    cherrypy.request.params under the names specified.  The dispatch
-    is then forwarded on to the next vpath element.
-
-    Note that any existing (and exposed) member function of the class that
-    popargs is applied to will override that value of the argument.  For
-    instance, if you have a method named "list" on the class decorated with
-    popargs, then accessing "/list" will call that function instead of popping
-    it off as the requested parameter.  This restriction applies to all
-    _cp_dispatch functions.  The only way around this restriction is to create
-    a "blank class" whose only function is to provide _cp_dispatch.
-
-    If there are path elements after the arguments, or more arguments
-    are requested than are available in the vpath, then the 'handler'
-    keyword argument specifies the next object to handle the parameterized
-    request.  If handler is not specified or is None, then self is used.
-    If handler is a function rather than an instance, then that function
-    will be called with the args specified and the return value from that
-    function used as the next object INSTEAD of adding the parameters to
-    cherrypy.request.args.
-
-    This decorator may be used in one of two ways:
-
-    As a class decorator:
-    @cherrypy.popargs('year', 'month', 'day')
-    class Blog:
-        def index(self, year=None, month=None, day=None):
-            #Process the parameters here; any url like
-            #/, /2009, /2009/12, or /2009/12/31
-            #will fill in the appropriate parameters.
-
-        def create(self):
-            #This link will still be available at /create.  Defined functions
-            #take precedence over arguments.
-
-    Or as a member of a class:
-    class Blog:
-        _cp_dispatch = cherrypy.popargs('year', 'month', 'day')
-        #...
-
-    The handler argument may be used to mix arguments with built in functions.
-    For instance, the following setup allows different activities at the
-    day, month, and year level:
-
-    class DayHandler:
-        def index(self, year, month, day):
-            #Do something with this day; probably list entries
-
-        def delete(self, year, month, day):
-            #Delete all entries for this day
-
-    @cherrypy.popargs('day', handler=DayHandler())
-    class MonthHandler:
-        def index(self, year, month):
-            #Do something with this month; probably list entries
-
-        def delete(self, year, month):
-            #Delete all entries for this month
-
-    @cherrypy.popargs('month', handler=MonthHandler())
-    class YearHandler:
-        def index(self, year):
-            #Do something with this year
-
-        #...
-
-    @cherrypy.popargs('year', handler=YearHandler())
-    class Root:
-        def index(self):
-            #...
-
-    """
-    # Since keyword arg comes after *args, we have to process it ourselves
-    # for lower versions of python.
-
-    handler = None
-    handler_call = False
-    for k, v in kwargs.items():
-        if k == 'handler':
-            handler = v
-        else:
-            tm = "cherrypy.popargs() got an unexpected keyword argument '{0}'"
-            raise TypeError(tm.format(k))
-
-    import inspect
-
-    if handler is not None \
-            and (hasattr(handler, '__call__') or inspect.isclass(handler)):
-        handler_call = True
-
-    def decorated(cls_or_self=None, vpath=None):
-        if inspect.isclass(cls_or_self):
-            # cherrypy.popargs is a class decorator
-            cls = cls_or_self
-            name = cherrypy.dispatch.Dispatcher.dispatch_method_name
-            setattr(cls, name, decorated)
-            return cls
-
-        # We're in the actual function
-        self = cls_or_self
-        parms = {}
-        for arg in args:
-            if not vpath:
-                break
-            parms[arg] = vpath.pop(0)
-
-        if handler is not None:
-            if handler_call:
-                return handler(**parms)
-            else:
-                cherrypy.request.params.update(parms)
-                return handler
-
-        cherrypy.request.params.update(parms)
-
-        # If we are the ultimate handler, then to prevent our _cp_dispatch
-        # from being called again, we will resolve remaining elements through
-        # getattr() directly.
-        if vpath:
-            return getattr(self, vpath.pop(0), None)
-        else:
-            return self
-
-    return decorated
-
-
-def url(path='', qs='', script_name=None, base=None, relative=None):
-    """Create an absolute URL for the given path.
-
-    If 'path' starts with a slash ('/'), this will return
-        (base + script_name + path + qs).
-    If it does not start with a slash, this returns
-        (base + script_name [+ request.path_info] + path + qs).
-
-    If script_name is None, cherrypy.request will be used
-    to find a script_name, if available.
-
-    If base is None, cherrypy.request.base will be used (if available).
-    Note that you can use cherrypy.tools.proxy to change this.
-
-    Finally, note that this function can be used to obtain an absolute URL
-    for the current request path (minus the querystring) by passing no args.
-    If you call url(qs=cherrypy.request.query_string), you should get the
-    original browser URL (assuming no internal redirections).
-
-    If relative is None or not provided, request.app.relative_urls will
-    be used (if available, else False). If False, the output will be an
-    absolute URL (including the scheme, host, vhost, and script_name).
-    If True, the output will instead be a URL that is relative to the
-    current request path, perhaps including '..' atoms. If relative is
-    the string 'server', the output will instead be a URL that is
-    relative to the server root; i.e., it will start with a slash.
-    """
-    if isinstance(qs, (tuple, list, dict)):
-        qs = urllib.parse.urlencode(qs)
-    if qs:
-        qs = '?' + qs
-
-    if cherrypy.request.app:
-        if not path.startswith('/'):
-            # Append/remove trailing slash from path_info as needed
-            # (this is to support mistyped URL's without redirecting;
-            # if you want to redirect, use tools.trailing_slash).
-            pi = cherrypy.request.path_info
-            if cherrypy.request.is_index is True:
-                if not pi.endswith('/'):
-                    pi = pi + '/'
-            elif cherrypy.request.is_index is False:
-                if pi.endswith('/') and pi != '/':
-                    pi = pi[:-1]
-
-            if path == '':
-                path = pi
-            else:
-                path = urllib.parse.urljoin(pi, path)
-
-        if script_name is None:
-            script_name = cherrypy.request.script_name
-        if base is None:
-            base = cherrypy.request.base
-
-        newurl = base + script_name + normalize_path(path) + qs
-    else:
-        # No request.app (we're being called outside a request).
-        # We'll have to guess the base from server.* attributes.
-        # This will produce very different results from the above
-        # if you're using vhosts or tools.proxy.
-        if base is None:
-            base = cherrypy.server.base()
-
-        path = (script_name or '') + path
-        newurl = base + normalize_path(path) + qs
-
-    # At this point, we should have a fully-qualified absolute URL.
-
-    if relative is None:
-        relative = getattr(cherrypy.request.app, 'relative_urls', False)
-
-    # See http://www.ietf.org/rfc/rfc2396.txt
-    if relative == 'server':
-        # "A relative reference beginning with a single slash character is
-        # termed an absolute-path reference, as defined by <abs_path>..."
-        # This is also sometimes called "server-relative".
-        newurl = '/' + '/'.join(newurl.split('/', 3)[3:])
-    elif relative:
-        # "A relative reference that does not begin with a scheme name
-        # or a slash character is termed a relative-path reference."
-        old = url(relative=False).split('/')[:-1]
-        new = newurl.split('/')
-        while old and new:
-            a, b = old[0], new[0]
-            if a != b:
-                break
-            old.pop(0)
-            new.pop(0)
-        new = (['..'] * len(old)) + new
-        newurl = '/'.join(new)
-
-    return newurl
-
-
-def normalize_path(path):
-    """Resolve given path from relative into absolute form."""
-    if './' not in path:
-        return path
-
-    # Normalize the URL by removing ./ and ../
-    atoms = []
-    for atom in path.split('/'):
-        if atom == '.':
-            pass
-        elif atom == '..':
-            # Don't pop from empty list
-            # (i.e. ignore redundant '..')
-            if atoms:
-                atoms.pop()
-        elif atom:
-            atoms.append(atom)
-
-    newpath = '/'.join(atoms)
-    # Preserve leading '/'
-    if path.startswith('/'):
-        newpath = '/' + newpath
-
-    return newpath
-
-
-####
-# Inlined from jaraco.classes 1.4.3
-# Ref #1673
-class _ClassPropertyDescriptor(object):
-    """Descript for read-only class-based property.
-
-    Turns a classmethod-decorated func into a read-only property of that class
-    type (means the value cannot be set).
-    """
-
-    def __init__(self, fget, fset=None):
-        """Initialize a class property descriptor.
-
-        Instantiated by ``_helper.classproperty``.
-        """
-        self.fget = fget
-        self.fset = fset
-
-    def __get__(self, obj, klass=None):
-        """Return property value."""
-        if klass is None:
-            klass = type(obj)
-        return self.fget.__get__(obj, klass)()
-
-
-def classproperty(func):  # noqa: D401; irrelevant for properties
-    """Decorator like classmethod to implement a static class property."""
-    if not isinstance(func, (classmethod, staticmethod)):
-        func = classmethod(func)
-
-    return _ClassPropertyDescriptor(func)
-####
diff --git a/libraries/cherrypy/daemon.py b/libraries/cherrypy/daemon.py
deleted file mode 100644
index 74488c06..00000000
--- a/libraries/cherrypy/daemon.py
+++ /dev/null
@@ -1,107 +0,0 @@
-"""The CherryPy daemon."""
-
-import sys
-
-import cherrypy
-from cherrypy.process import plugins, servers
-from cherrypy import Application
-
-
-def start(configfiles=None, daemonize=False, environment=None,
-          fastcgi=False, scgi=False, pidfile=None, imports=None,
-          cgi=False):
-    """Subscribe all engine plugins and start the engine."""
-    sys.path = [''] + sys.path
-    for i in imports or []:
-        exec('import %s' % i)
-
-    for c in configfiles or []:
-        cherrypy.config.update(c)
-        # If there's only one app mounted, merge config into it.
-        if len(cherrypy.tree.apps) == 1:
-            for app in cherrypy.tree.apps.values():
-                if isinstance(app, Application):
-                    app.merge(c)
-
-    engine = cherrypy.engine
-
-    if environment is not None:
-        cherrypy.config.update({'environment': environment})
-
-    # Only daemonize if asked to.
-    if daemonize:
-        # Don't print anything to stdout/sterr.
-        cherrypy.config.update({'log.screen': False})
-        plugins.Daemonizer(engine).subscribe()
-
-    if pidfile:
-        plugins.PIDFile(engine, pidfile).subscribe()
-
-    if hasattr(engine, 'signal_handler'):
-        engine.signal_handler.subscribe()
-    if hasattr(engine, 'console_control_handler'):
-        engine.console_control_handler.subscribe()
-
-    if (fastcgi and (scgi or cgi)) or (scgi and cgi):
-        cherrypy.log.error('You may only specify one of the cgi, fastcgi, and '
-                           'scgi options.', 'ENGINE')
-        sys.exit(1)
-    elif fastcgi or scgi or cgi:
-        # Turn off autoreload when using *cgi.
-        cherrypy.config.update({'engine.autoreload.on': False})
-        # Turn off the default HTTP server (which is subscribed by default).
-        cherrypy.server.unsubscribe()
-
-        addr = cherrypy.server.bind_addr
-        cls = (
-            servers.FlupFCGIServer if fastcgi else
-            servers.FlupSCGIServer if scgi else
-            servers.FlupCGIServer
-        )
-        f = cls(application=cherrypy.tree, bindAddress=addr)
-        s = servers.ServerAdapter(engine, httpserver=f, bind_addr=addr)
-        s.subscribe()
-
-    # Always start the engine; this will start all other services
-    try:
-        engine.start()
-    except Exception:
-        # Assume the error has been logged already via bus.log.
-        sys.exit(1)
-    else:
-        engine.block()
-
-
-def run():
-    """Run cherryd CLI."""
-    from optparse import OptionParser
-
-    p = OptionParser()
-    p.add_option('-c', '--config', action='append', dest='config',
-                 help='specify config file(s)')
-    p.add_option('-d', action='store_true', dest='daemonize',
-                 help='run the server as a daemon')
-    p.add_option('-e', '--environment', dest='environment', default=None,
-                 help='apply the given config environment')
-    p.add_option('-f', action='store_true', dest='fastcgi',
-                 help='start a fastcgi server instead of the default HTTP '
-                      'server')
-    p.add_option('-s', action='store_true', dest='scgi',
-                 help='start a scgi server instead of the default HTTP server')
-    p.add_option('-x', action='store_true', dest='cgi',
-                 help='start a cgi server instead of the default HTTP server')
-    p.add_option('-i', '--import', action='append', dest='imports',
-                 help='specify modules to import')
-    p.add_option('-p', '--pidfile', dest='pidfile', default=None,
-                 help='store the process id in the given file')
-    p.add_option('-P', '--Path', action='append', dest='Path',
-                 help='add the given paths to sys.path')
-    options, args = p.parse_args()
-
-    if options.Path:
-        for p in options.Path:
-            sys.path.insert(0, p)
-
-    start(options.config, options.daemonize,
-          options.environment, options.fastcgi, options.scgi,
-          options.pidfile, options.imports, options.cgi)
diff --git a/libraries/cherrypy/favicon.ico b/libraries/cherrypy/favicon.ico
deleted file mode 100644
index f0d7e61b..00000000
Binary files a/libraries/cherrypy/favicon.ico and /dev/null differ
diff --git a/libraries/cherrypy/lib/__init__.py b/libraries/cherrypy/lib/__init__.py
deleted file mode 100644
index f815f76a..00000000
--- a/libraries/cherrypy/lib/__init__.py
+++ /dev/null
@@ -1,96 +0,0 @@
-"""CherryPy Library."""
-
-
-def is_iterator(obj):
-    """Detect if the object provided implements the iterator protocol.
-
-    (i.e. like a generator).
-
-    This will return False for objects which are iterable,
-    but not iterators themselves.
-    """
-    from types import GeneratorType
-    if isinstance(obj, GeneratorType):
-        return True
-    elif not hasattr(obj, '__iter__'):
-        return False
-    else:
-        # Types which implement the protocol must return themselves when
-        # invoking 'iter' upon them.
-        return iter(obj) is obj
-
-
-def is_closable_iterator(obj):
-    """Detect if the given object is both closable and iterator."""
-    # Not an iterator.
-    if not is_iterator(obj):
-        return False
-
-    # A generator - the easiest thing to deal with.
-    import inspect
-    if inspect.isgenerator(obj):
-        return True
-
-    # A custom iterator. Look for a close method...
-    if not (hasattr(obj, 'close') and callable(obj.close)):
-        return False
-
-    #  ... which doesn't require any arguments.
-    try:
-        inspect.getcallargs(obj.close)
-    except TypeError:
-        return False
-    else:
-        return True
-
-
-class file_generator(object):
-    """Yield the given input (a file object) in chunks (default 64k).
-
-    (Core)
-    """
-
-    def __init__(self, input, chunkSize=65536):
-        """Initialize file_generator with file ``input`` for chunked access."""
-        self.input = input
-        self.chunkSize = chunkSize
-
-    def __iter__(self):
-        """Return iterator."""
-        return self
-
-    def __next__(self):
-        """Return next chunk of file."""
-        chunk = self.input.read(self.chunkSize)
-        if chunk:
-            return chunk
-        else:
-            if hasattr(self.input, 'close'):
-                self.input.close()
-            raise StopIteration()
-    next = __next__
-
-
-def file_generator_limited(fileobj, count, chunk_size=65536):
-    """Yield the given file object in chunks.
-
-    Stopps after `count` bytes has been emitted.
-    Default chunk size is 64kB. (Core)
-    """
-    remaining = count
-    while remaining > 0:
-        chunk = fileobj.read(min(chunk_size, remaining))
-        chunklen = len(chunk)
-        if chunklen == 0:
-            return
-        remaining -= chunklen
-        yield chunk
-
-
-def set_vary_header(response, header_name):
-    """Add a Vary header to a response."""
-    varies = response.headers.get('Vary', '')
-    varies = [x.strip() for x in varies.split(',') if x.strip()]
-    if header_name not in varies:
-        varies.append(header_name)
-    response.headers['Vary'] = ', '.join(varies)
diff --git a/libraries/cherrypy/lib/auth_basic.py b/libraries/cherrypy/lib/auth_basic.py
deleted file mode 100644
index ad379a26..00000000
--- a/libraries/cherrypy/lib/auth_basic.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# This file is part of CherryPy <http://www.cherrypy.org/>
-# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:expandtab:fileencoding=utf-8
-"""HTTP Basic Authentication tool.
-
-This module provides a CherryPy 3.x tool which implements
-the server-side of HTTP Basic Access Authentication, as described in
-:rfc:`2617`.
-
-Example usage, using the built-in checkpassword_dict function which uses a dict
-as the credentials store::
-
-    userpassdict = {'bird' : 'bebop', 'ornette' : 'wayout'}
-    checkpassword = cherrypy.lib.auth_basic.checkpassword_dict(userpassdict)
-    basic_auth = {'tools.auth_basic.on': True,
-                  'tools.auth_basic.realm': 'earth',
-                  'tools.auth_basic.checkpassword': checkpassword,
-                  'tools.auth_basic.accept_charset': 'UTF-8',
-    }
-    app_config = { '/' : basic_auth }
-
-"""
-
-import binascii
-import unicodedata
-import base64
-
-import cherrypy
-from cherrypy._cpcompat import ntou, tonative
-
-
-__author__ = 'visteya'
-__date__ = 'April 2009'
-
-
-def checkpassword_dict(user_password_dict):
-    """Returns a checkpassword function which checks credentials
-    against a dictionary of the form: {username : password}.
-
-    If you want a simple dictionary-based authentication scheme, use
-    checkpassword_dict(my_credentials_dict) as the value for the
-    checkpassword argument to basic_auth().
-    """
-    def checkpassword(realm, user, password):
-        p = user_password_dict.get(user)
-        return p and p == password or False
-
-    return checkpassword
-
-
-def basic_auth(realm, checkpassword, debug=False, accept_charset='utf-8'):
-    """A CherryPy tool which hooks at before_handler to perform
-    HTTP Basic Access Authentication, as specified in :rfc:`2617`
-    and :rfc:`7617`.
-
-    If the request has an 'authorization' header with a 'Basic' scheme, this
-    tool attempts to authenticate the credentials supplied in that header.  If
-    the request has no 'authorization' header, or if it does but the scheme is
-    not 'Basic', or if authentication fails, the tool sends a 401 response with
-    a 'WWW-Authenticate' Basic header.
-
-    realm
-        A string containing the authentication realm.
-
-    checkpassword
-        A callable which checks the authentication credentials.
-        Its signature is checkpassword(realm, username, password). where
-        username and password are the values obtained from the request's
-        'authorization' header.  If authentication succeeds, checkpassword
-        returns True, else it returns False.
-
-    """
-
-    fallback_charset = 'ISO-8859-1'
-
-    if '"' in realm:
-        raise ValueError('Realm cannot contain the " (quote) character.')
-    request = cherrypy.serving.request
-
-    auth_header = request.headers.get('authorization')
-    if auth_header is not None:
-        # split() error, base64.decodestring() error
-        msg = 'Bad Request'
-        with cherrypy.HTTPError.handle((ValueError, binascii.Error), 400, msg):
-            scheme, params = auth_header.split(' ', 1)
-            if scheme.lower() == 'basic':
-                charsets = accept_charset, fallback_charset
-                decoded_params = base64.b64decode(params.encode('ascii'))
-                decoded_params = _try_decode(decoded_params, charsets)
-                decoded_params = ntou(decoded_params)
-                decoded_params = unicodedata.normalize('NFC', decoded_params)
-                decoded_params = tonative(decoded_params)
-                username, password = decoded_params.split(':', 1)
-                if checkpassword(realm, username, password):
-                    if debug:
-                        cherrypy.log('Auth succeeded', 'TOOLS.AUTH_BASIC')
-                    request.login = username
-                    return  # successful authentication
-
-    charset = accept_charset.upper()
-    charset_declaration = (
-        (', charset="%s"' % charset)
-        if charset != fallback_charset
-        else ''
-    )
-    # Respond with 401 status and a WWW-Authenticate header
-    cherrypy.serving.response.headers['www-authenticate'] = (
-        'Basic realm="%s"%s' % (realm, charset_declaration)
-    )
-    raise cherrypy.HTTPError(
-        401, 'You are not authorized to access that resource')
-
-
-def _try_decode(subject, charsets):
-    for charset in charsets[:-1]:
-        try:
-            return tonative(subject, charset)
-        except ValueError:
-            pass
-    return tonative(subject, charsets[-1])
diff --git a/libraries/cherrypy/lib/auth_digest.py b/libraries/cherrypy/lib/auth_digest.py
deleted file mode 100644
index 9b4f55c8..00000000
--- a/libraries/cherrypy/lib/auth_digest.py
+++ /dev/null
@@ -1,464 +0,0 @@
-# This file is part of CherryPy <http://www.cherrypy.org/>
-# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:expandtab:fileencoding=utf-8
-"""HTTP Digest Authentication tool.
-
-An implementation of the server-side of HTTP Digest Access
-Authentication, which is described in :rfc:`2617`.
-
-Example usage, using the built-in get_ha1_dict_plain function which uses a dict
-of plaintext passwords as the credentials store::
-
-    userpassdict = {'alice' : '4x5istwelve'}
-    get_ha1 = cherrypy.lib.auth_digest.get_ha1_dict_plain(userpassdict)
-    digest_auth = {'tools.auth_digest.on': True,
-                   'tools.auth_digest.realm': 'wonderland',
-                   'tools.auth_digest.get_ha1': get_ha1,
-                   'tools.auth_digest.key': 'a565c27146791cfb',
-                   'tools.auth_digest.accept_charset': 'UTF-8',
-    }
-    app_config = { '/' : digest_auth }
-"""
-
-import time
-import functools
-from hashlib import md5
-
-from six.moves.urllib.request import parse_http_list, parse_keqv_list
-
-import cherrypy
-from cherrypy._cpcompat import ntob, tonative
-
-
-__author__ = 'visteya'
-__date__ = 'April 2009'
-
-
-def md5_hex(s):
-    return md5(ntob(s, 'utf-8')).hexdigest()
-
-
-qop_auth = 'auth'
-qop_auth_int = 'auth-int'
-valid_qops = (qop_auth, qop_auth_int)
-
-valid_algorithms = ('MD5', 'MD5-sess')
-
-FALLBACK_CHARSET = 'ISO-8859-1'
-DEFAULT_CHARSET = 'UTF-8'
-
-
-def TRACE(msg):
-    cherrypy.log(msg, context='TOOLS.AUTH_DIGEST')
-
-# Three helper functions for users of the tool, providing three variants
-# of get_ha1() functions for three different kinds of credential stores.
-
-
-def get_ha1_dict_plain(user_password_dict):
-    """Returns a get_ha1 function which obtains a plaintext password from a
-    dictionary of the form: {username : password}.
-
-    If you want a simple dictionary-based authentication scheme, with plaintext
-    passwords, use get_ha1_dict_plain(my_userpass_dict) as the value for the
-    get_ha1 argument to digest_auth().
-    """
-    def get_ha1(realm, username):
-        password = user_password_dict.get(username)
-        if password:
-            return md5_hex('%s:%s:%s' % (username, realm, password))
-        return None
-
-    return get_ha1
-
-
-def get_ha1_dict(user_ha1_dict):
-    """Returns a get_ha1 function which obtains a HA1 password hash from a
-    dictionary of the form: {username : HA1}.
-
-    If you want a dictionary-based authentication scheme, but with
-    pre-computed HA1 hashes instead of plain-text passwords, use
-    get_ha1_dict(my_userha1_dict) as the value for the get_ha1
-    argument to digest_auth().
-    """
-    def get_ha1(realm, username):
-        return user_ha1_dict.get(username)
-
-    return get_ha1
-
-
-def get_ha1_file_htdigest(filename):
-    """Returns a get_ha1 function which obtains a HA1 password hash from a
-    flat file with lines of the same format as that produced by the Apache
-    htdigest utility. For example, for realm 'wonderland', username 'alice',
-    and password '4x5istwelve', the htdigest line would be::
-
-        alice:wonderland:3238cdfe91a8b2ed8e39646921a02d4c
-
-    If you want to use an Apache htdigest file as the credentials store,
-    then use get_ha1_file_htdigest(my_htdigest_file) as the value for the
-    get_ha1 argument to digest_auth().  It is recommended that the filename
-    argument be an absolute path, to avoid problems.
-    """
-    def get_ha1(realm, username):
-        result = None
-        f = open(filename, 'r')
-        for line in f:
-            u, r, ha1 = line.rstrip().split(':')
-            if u == username and r == realm:
-                result = ha1
-                break
-        f.close()
-        return result
-
-    return get_ha1
-
-
-def synthesize_nonce(s, key, timestamp=None):
-    """Synthesize a nonce value which resists spoofing and can be checked
-    for staleness. Returns a string suitable as the value for 'nonce' in
-    the www-authenticate header.
-
-    s
-        A string related to the resource, such as the hostname of the server.
-
-    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())
-    h = md5_hex('%s:%s:%s' % (timestamp, s, key))
-    nonce = '%s:%s' % (timestamp, h)
-    return nonce
-
-
-def H(s):
-    """The hash function H"""
-    return md5_hex(s)
-
-
-def _try_decode_header(header, charset):
-    global FALLBACK_CHARSET
-
-    for enc in (charset, FALLBACK_CHARSET):
-        try:
-            return tonative(ntob(tonative(header, 'latin1'), 'latin1'), enc)
-        except ValueError as ve:
-            last_err = ve
-    else:
-        raise last_err
-
-
-class HttpDigestAuthorization(object):
-    """
-    Parses a Digest Authorization header and performs
-    re-calculation of the digest.
-    """
-
-    scheme = 'digest'
-
-    def errmsg(self, s):
-        return 'Digest Authorization header: %s' % s
-
-    @classmethod
-    def matches(cls, header):
-        scheme, _, _ = header.partition(' ')
-        return scheme.lower() == cls.scheme
-
-    def __init__(
-        self, auth_header, http_method,
-        debug=False, accept_charset=DEFAULT_CHARSET[:],
-    ):
-        self.http_method = http_method
-        self.debug = debug
-
-        if not self.matches(auth_header):
-            raise ValueError('Authorization scheme is not "Digest"')
-
-        self.auth_header = _try_decode_header(auth_header, accept_charset)
-
-        scheme, params = self.auth_header.split(' ', 1)
-
-        # make a dict of the params
-        items = parse_http_list(params)
-        paramsd = parse_keqv_list(items)
-
-        self.realm = paramsd.get('realm')
-        self.username = paramsd.get('username')
-        self.nonce = paramsd.get('nonce')
-        self.uri = paramsd.get('uri')
-        self.method = paramsd.get('method')
-        self.response = paramsd.get('response')  # the response digest
-        self.algorithm = paramsd.get('algorithm', 'MD5').upper()
-        self.cnonce = paramsd.get('cnonce')
-        self.opaque = paramsd.get('opaque')
-        self.qop = paramsd.get('qop')  # qop
-        self.nc = paramsd.get('nc')  # nonce count
-
-        # perform some correctness checks
-        if self.algorithm not in valid_algorithms:
-            raise ValueError(
-                self.errmsg("Unsupported value for algorithm: '%s'" %
-                            self.algorithm))
-
-        has_reqd = (
-            self.username and
-            self.realm and
-            self.nonce and
-            self.uri and
-            self.response
-        )
-        if not has_reqd:
-            raise ValueError(
-                self.errmsg('Not all required parameters are present.'))
-
-        if self.qop:
-            if self.qop not in valid_qops:
-                raise ValueError(
-                    self.errmsg("Unsupported value for qop: '%s'" % self.qop))
-            if not (self.cnonce and self.nc):
-                raise ValueError(
-                    self.errmsg('If qop is sent then '
-                                'cnonce and nc MUST be present'))
-        else:
-            if self.cnonce or self.nc:
-                raise ValueError(
-                    self.errmsg('If qop is not sent, '
-                                'neither cnonce nor nc can be present'))
-
-    def __str__(self):
-        return 'authorization : %s' % self.auth_header
-
-    def validate_nonce(self, s, key):
-        """Validate the nonce.
-        Returns True if nonce was generated by synthesize_nonce() and the
-        timestamp is not spoofed, else returns False.
-
-        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.
-        """
-        try:
-            timestamp, hashpart = self.nonce.split(':', 1)
-            s_timestamp, s_hashpart = synthesize_nonce(
-                s, key, timestamp).split(':', 1)
-            is_valid = s_hashpart == hashpart
-            if self.debug:
-                TRACE('validate_nonce: %s' % is_valid)
-            return is_valid
-        except ValueError:  # split() error
-            pass
-        return False
-
-    def is_nonce_stale(self, max_age_seconds=600):
-        """Returns True if a validated nonce is stale. The nonce contains a
-        timestamp in plaintext and also a secure hash of the timestamp.
-        You should first validate the nonce to ensure the plaintext
-        timestamp is not spoofed.
-        """
-        try:
-            timestamp, hashpart = self.nonce.split(':', 1)
-            if int(timestamp) + max_age_seconds > int(time.time()):
-                return False
-        except ValueError:  # int() error
-            pass
-        if self.debug:
-            TRACE('nonce is stale')
-        return True
-
-    def HA2(self, entity_body=''):
-        """Returns the H(A2) string. See :rfc:`2617` section 3.2.2.3."""
-        # RFC 2617 3.2.2.3
-        # If the "qop" directive's value is "auth" or is unspecified,
-        # then A2 is:
-        #    A2 = method ":" digest-uri-value
-        #
-        # If the "qop" value is "auth-int", then A2 is:
-        #    A2 = method ":" digest-uri-value ":" H(entity-body)
-        if self.qop is None or self.qop == 'auth':
-            a2 = '%s:%s' % (self.http_method, self.uri)
-        elif self.qop == 'auth-int':
-            a2 = '%s:%s:%s' % (self.http_method, self.uri, H(entity_body))
-        else:
-            # in theory, this should never happen, since I validate qop in
-            # __init__()
-            raise ValueError(self.errmsg('Unrecognized value for qop!'))
-        return H(a2)
-
-    def request_digest(self, ha1, entity_body=''):
-        """Calculates the Request-Digest. See :rfc:`2617` section 3.2.2.1.
-
-        ha1
-            The HA1 string obtained from the credentials store.
-
-        entity_body
-            If 'qop' is set to 'auth-int', then A2 includes a hash
-            of the "entity body".  The entity body is the part of the
-            message which follows the HTTP headers. See :rfc:`2617` section
-            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
-        if self.qop:
-            req = '%s:%s:%s:%s:%s' % (
-                self.nonce, self.nc, self.cnonce, self.qop, ha2)
-        else:
-            req = '%s:%s' % (self.nonce, ha2)
-
-        # RFC 2617 3.2.2.2
-        #
-        # If the "algorithm" directive's value is "MD5" or is unspecified,
-        # then A1 is:
-        #    A1 = unq(username-value) ":" unq(realm-value) ":" passwd
-        #
-        # If the "algorithm" directive's value is "MD5-sess", then A1 is
-        # calculated only once - on the first request by the client following
-        # receipt of a WWW-Authenticate challenge from the server.
-        # A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
-        #         ":" unq(nonce-value) ":" unq(cnonce-value)
-        if self.algorithm == 'MD5-sess':
-            ha1 = H('%s:%s:%s' % (ha1, self.nonce, self.cnonce))
-
-        digest = H('%s:%s' % (ha1, req))
-        return digest
-
-
-def _get_charset_declaration(charset):
-    global FALLBACK_CHARSET
-    charset = charset.upper()
-    return (
-        (', charset="%s"' % charset)
-        if charset != FALLBACK_CHARSET
-        else ''
-    )
-
-
-def www_authenticate(
-    realm, key, algorithm='MD5', nonce=None, qop=qop_auth,
-    stale=False, accept_charset=DEFAULT_CHARSET[:],
-):
-    """Constructs a WWW-Authenticate header for Digest authentication."""
-    if qop not in valid_qops:
-        raise ValueError("Unsupported value for qop: '%s'" % qop)
-    if algorithm not in valid_algorithms:
-        raise ValueError("Unsupported value for algorithm: '%s'" % algorithm)
-
-    HEADER_PATTERN = (
-        'Digest realm="%s", nonce="%s", algorithm="%s", qop="%s"%s%s'
-    )
-
-    if nonce is None:
-        nonce = synthesize_nonce(realm, key)
-
-    stale_param = ', stale="true"' if stale else ''
-
-    charset_declaration = _get_charset_declaration(accept_charset)
-
-    return HEADER_PATTERN % (
-        realm, nonce, algorithm, qop, stale_param, charset_declaration,
-    )
-
-
-def digest_auth(realm, get_ha1, key, debug=False, accept_charset='utf-8'):
-    """A CherryPy tool that 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 that looks up a username in a credentials store
-        and returns the HA1 string, which is defined in the RFC to be
-        MD5(username : realm : password).  The function's signature is:
-        ``get_ha1(realm, username)``
-        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')
-
-    respond_401 = functools.partial(
-        _respond_401, realm, key, accept_charset, debug)
-
-    if not HttpDigestAuthorization.matches(auth_header or ''):
-        respond_401()
-
-    msg = 'The Authorization header could not be parsed.'
-    with cherrypy.HTTPError.handle(ValueError, 400, msg):
-        auth = HttpDigestAuthorization(
-            auth_header, request.method,
-            debug=debug, accept_charset=accept_charset,
-        )
-
-    if debug:
-        TRACE(str(auth))
-
-    if not auth.validate_nonce(realm, key):
-        respond_401()
-
-    ha1 = get_ha1(realm, auth.username)
-
-    if ha1 is None:
-        respond_401()
-
-    # note that for request.body to be available we need to
-    # hook in at before_handler, not on_start_resource like
-    # 3.1.x digest_auth does.
-    digest = auth.request_digest(ha1, entity_body=request.body)
-    if digest != auth.response:
-        respond_401()
-
-    # authenticated
-    if debug:
-        TRACE('digest matches auth.response')
-    # Now check if nonce is stale.
-    # The choice of ten minutes' lifetime for nonce is somewhat
-    # arbitrary
-    if auth.is_nonce_stale(max_age_seconds=600):
-        respond_401(stale=True)
-
-    request.login = auth.username
-    if debug:
-        TRACE('authentication of %s successful' % auth.username)
-
-
-def _respond_401(realm, key, accept_charset, debug, **kwargs):
-    """
-    Respond with 401 status and a WWW-Authenticate header
-    """
-    header = www_authenticate(
-        realm, key,
-        accept_charset=accept_charset,
-        **kwargs
-    )
-    if debug:
-        TRACE(header)
-    cherrypy.serving.response.headers['WWW-Authenticate'] = header
-    raise cherrypy.HTTPError(
-        401, 'You are not authorized to access that resource')
diff --git a/libraries/cherrypy/lib/caching.py b/libraries/cherrypy/lib/caching.py
deleted file mode 100644
index fed325a6..00000000
--- a/libraries/cherrypy/lib/caching.py
+++ /dev/null
@@ -1,482 +0,0 @@
-"""
-CherryPy implements a simple caching system as a pluggable Tool. This tool
-tries to be an (in-process) HTTP/1.1-compliant cache. It's not quite there
-yet, but it's probably good enough for most sites.
-
-In general, GET responses are cached (along with selecting headers) and, if
-another request arrives for the same resource, the caching Tool will return 304
-Not Modified if possible, or serve the cached response otherwise. It also sets
-request.cached to True if serving a cached representation, and sets
-request.cacheable to False (so it doesn't get cached again).
-
-If POST, PUT, or DELETE requests are made for a cached resource, they
-invalidate (delete) any cached response.
-
-Usage
-=====
-
-Configuration file example::
-
-    [/]
-    tools.caching.on = True
-    tools.caching.delay = 3600
-
-You may use a class other than the default
-:class:`MemoryCache<cherrypy.lib.caching.MemoryCache>` by supplying the config
-entry ``cache_class``; supply the full dotted name of the replacement class
-as the config value. It must implement the basic methods ``get``, ``put``,
-``delete``, and ``clear``.
-
-You may set any attribute, including overriding methods, on the cache
-instance by providing them in config. The above sets the
-:attr:`delay<cherrypy.lib.caching.MemoryCache.delay>` attribute, for example.
-"""
-
-import datetime
-import sys
-import threading
-import time
-
-import six
-
-import cherrypy
-from cherrypy.lib import cptools, httputil
-from cherrypy._cpcompat import Event
-
-
-class Cache(object):
-
-    """Base class for Cache implementations."""
-
-    def get(self):
-        """Return the current variant if in the cache, else None."""
-        raise NotImplemented
-
-    def put(self, obj, size):
-        """Store the current variant in the cache."""
-        raise NotImplemented
-
-    def delete(self):
-        """Remove ALL cached variants of the current resource."""
-        raise NotImplemented
-
-    def clear(self):
-        """Reset the cache to its initial, empty state."""
-        raise NotImplemented
-
-
-# ------------------------------ Memory Cache ------------------------------- #
-class AntiStampedeCache(dict):
-
-    """A storage system for cached items which reduces stampede collisions."""
-
-    def wait(self, key, timeout=5, debug=False):
-        """Return the cached value for the given key, or None.
-
-        If timeout is not None, and the value is already
-        being calculated by another thread, wait until the given timeout has
-        elapsed. If the value is available before the timeout expires, it is
-        returned. If not, None is returned, and a sentinel placed in the cache
-        to signal other threads to wait.
-
-        If timeout is None, no waiting is performed nor sentinels used.
-        """
-        value = self.get(key)
-        if isinstance(value, Event):
-            if timeout is None:
-                # Ignore the other thread and recalc it ourselves.
-                if debug:
-                    cherrypy.log('No timeout', 'TOOLS.CACHING')
-                return None
-
-            # Wait until it's done or times out.
-            if debug:
-                cherrypy.log('Waiting up to %s seconds' %
-                             timeout, 'TOOLS.CACHING')
-            value.wait(timeout)
-            if value.result is not None:
-                # The other thread finished its calculation. Use it.
-                if debug:
-                    cherrypy.log('Result!', 'TOOLS.CACHING')
-                return value.result
-            # Timed out. Stick an Event in the slot so other threads wait
-            # on this one to finish calculating the value.
-            if debug:
-                cherrypy.log('Timed out', 'TOOLS.CACHING')
-            e = threading.Event()
-            e.result = None
-            dict.__setitem__(self, key, e)
-
-            return None
-        elif value is None:
-            # Stick an Event in the slot so other threads wait
-            # on this one to finish calculating the value.
-            if debug:
-                cherrypy.log('Timed out', 'TOOLS.CACHING')
-            e = threading.Event()
-            e.result = None
-            dict.__setitem__(self, key, e)
-        return value
-
-    def __setitem__(self, key, value):
-        """Set the cached value for the given key."""
-        existing = self.get(key)
-        dict.__setitem__(self, key, value)
-        if isinstance(existing, Event):
-            # Set Event.result so other threads waiting on it have
-            # immediate access without needing to poll the cache again.
-            existing.result = value
-            existing.set()
-
-
-class MemoryCache(Cache):
-
-    """An in-memory cache for varying response content.
-
-    Each key in self.store is a URI, and each value is an AntiStampedeCache.
-    The response for any given URI may vary based on the values of
-    "selecting request headers"; that is, those named in the Vary
-    response header. We assume the list of header names to be constant
-    for each URI throughout the lifetime of the application, and store
-    that list in ``self.store[uri].selecting_headers``.
-
-    The items contained in ``self.store[uri]`` have keys which are tuples of
-    request header values (in the same order as the names in its
-    selecting_headers), and values which are the actual responses.
-    """
-
-    maxobjects = 1000
-    """The maximum number of cached objects; defaults to 1000."""
-
-    maxobj_size = 100000
-    """The maximum size of each cached object in bytes; defaults to 100 KB."""
-
-    maxsize = 10000000
-    """The maximum size of the entire cache in bytes; defaults to 10 MB."""
-
-    delay = 600
-    """Seconds until the cached content expires; defaults to 600 (10 minutes).
-    """
-
-    antistampede_timeout = 5
-    """Seconds to wait for other threads to release a cache lock."""
-
-    expire_freq = 0.1
-    """Seconds to sleep between cache expiration sweeps."""
-
-    debug = False
-
-    def __init__(self):
-        self.clear()
-
-        # Run self.expire_cache in a separate daemon thread.
-        t = threading.Thread(target=self.expire_cache, name='expire_cache')
-        self.expiration_thread = t
-        t.daemon = True
-        t.start()
-
-    def clear(self):
-        """Reset the cache to its initial, empty state."""
-        self.store = {}
-        self.expirations = {}
-        self.tot_puts = 0
-        self.tot_gets = 0
-        self.tot_hist = 0
-        self.tot_expires = 0
-        self.tot_non_modified = 0
-        self.cursize = 0
-
-    def expire_cache(self):
-        """Continuously examine cached objects, expiring stale ones.
-
-        This function is designed to be run in its own daemon thread,
-        referenced at ``self.expiration_thread``.
-        """
-        # It's possible that "time" will be set to None
-        # arbitrarily, so we check "while time" to avoid exceptions.
-        # See tickets #99 and #180 for more information.
-        while time:
-            now = time.time()
-            # Must make a copy of expirations so it doesn't change size
-            # during iteration
-            items = list(six.iteritems(self.expirations))
-            for expiration_time, objects in items:
-                if expiration_time <= now:
-                    for obj_size, uri, sel_header_values in objects:
-                        try:
-                            del self.store[uri][tuple(sel_header_values)]
-                            self.tot_expires += 1
-                            self.cursize -= obj_size
-                        except KeyError:
-                            # the key may have been deleted elsewhere
-                            pass
-                    del self.expirations[expiration_time]
-            time.sleep(self.expire_freq)
-
-    def get(self):
-        """Return the current variant if in the cache, else None."""
-        request = cherrypy.serving.request
-        self.tot_gets += 1
-
-        uri = cherrypy.url(qs=request.query_string)
-        uricache = self.store.get(uri)
-        if uricache is None:
-            return None
-
-        header_values = [request.headers.get(h, '')
-                         for h in uricache.selecting_headers]
-        variant = uricache.wait(key=tuple(sorted(header_values)),
-                                timeout=self.antistampede_timeout,
-                                debug=self.debug)
-        if variant is not None:
-            self.tot_hist += 1
-        return variant
-
-    def put(self, variant, size):
-        """Store the current variant in the cache."""
-        request = cherrypy.serving.request
-        response = cherrypy.serving.response
-
-        uri = cherrypy.url(qs=request.query_string)
-        uricache = self.store.get(uri)
-        if uricache is None:
-            uricache = AntiStampedeCache()
-            uricache.selecting_headers = [
-                e.value for e in response.headers.elements('Vary')]
-            self.store[uri] = uricache
-
-        if len(self.store) < self.maxobjects:
-            total_size = self.cursize + size
-
-            # checks if there's space for the object
-            if (size < self.maxobj_size and total_size < self.maxsize):
-                # add to the expirations list
-                expiration_time = response.time + self.delay
-                bucket = self.expirations.setdefault(expiration_time, [])
-                bucket.append((size, uri, uricache.selecting_headers))
-
-                # add to the cache
-                header_values = [request.headers.get(h, '')
-                                 for h in uricache.selecting_headers]
-                uricache[tuple(sorted(header_values))] = variant
-                self.tot_puts += 1
-                self.cursize = total_size
-
-    def delete(self):
-        """Remove ALL cached variants of the current resource."""
-        uri = cherrypy.url(qs=cherrypy.serving.request.query_string)
-        self.store.pop(uri, None)
-
-
-def get(invalid_methods=('POST', 'PUT', 'DELETE'), debug=False, **kwargs):
-    """Try to obtain cached output. If fresh enough, raise HTTPError(304).
-
-    If POST, PUT, or DELETE:
-        * invalidates (deletes) any cached response for this resource
-        * sets request.cached = False
-        * sets request.cacheable = False
-
-    else if a cached copy exists:
-        * sets request.cached = True
-        * sets request.cacheable = False
-        * sets response.headers to the cached values
-        * checks the cached Last-Modified response header against the
-          current If-(Un)Modified-Since request headers; raises 304
-          if necessary.
-        * sets response.status and response.body to the cached values
-        * returns True
-
-    otherwise:
-        * sets request.cached = False
-        * sets request.cacheable = True
-        * returns False
-    """
-    request = cherrypy.serving.request
-    response = cherrypy.serving.response
-
-    if not hasattr(cherrypy, '_cache'):
-        # Make a process-wide Cache object.
-        cherrypy._cache = kwargs.pop('cache_class', MemoryCache)()
-
-        # Take all remaining kwargs and set them on the Cache object.
-        for k, v in kwargs.items():
-            setattr(cherrypy._cache, k, v)
-        cherrypy._cache.debug = debug
-
-    # POST, PUT, DELETE should invalidate (delete) the cached copy.
-    # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.10.
-    if request.method in invalid_methods:
-        if debug:
-            cherrypy.log('request.method %r in invalid_methods %r' %
-                         (request.method, invalid_methods), 'TOOLS.CACHING')
-        cherrypy._cache.delete()
-        request.cached = False
-        request.cacheable = False
-        return False
-
-    if 'no-cache' in [e.value for e in request.headers.elements('Pragma')]:
-        request.cached = False
-        request.cacheable = True
-        return False
-
-    cache_data = cherrypy._cache.get()
-    request.cached = bool(cache_data)
-    request.cacheable = not request.cached
-    if request.cached:
-        # Serve the cached copy.
-        max_age = cherrypy._cache.delay
-        for v in [e.value for e in request.headers.elements('Cache-Control')]:
-            atoms = v.split('=', 1)
-            directive = atoms.pop(0)
-            if directive == 'max-age':
-                if len(atoms) != 1 or not atoms[0].isdigit():
-                    raise cherrypy.HTTPError(
-                        400, 'Invalid Cache-Control header')
-                max_age = int(atoms[0])
-                break
-            elif directive == 'no-cache':
-                if debug:
-                    cherrypy.log(
-                        'Ignoring cache due to Cache-Control: no-cache',
-                        'TOOLS.CACHING')
-                request.cached = False
-                request.cacheable = True
-                return False
-
-        if debug:
-            cherrypy.log('Reading response from cache', 'TOOLS.CACHING')
-        s, h, b, create_time = cache_data
-        age = int(response.time - create_time)
-        if (age > max_age):
-            if debug:
-                cherrypy.log('Ignoring cache due to age > %d' % max_age,
-                             'TOOLS.CACHING')
-            request.cached = False
-            request.cacheable = True
-            return False
-
-        # Copy the response headers. See
-        # https://github.com/cherrypy/cherrypy/issues/721.
-        response.headers = rh = httputil.HeaderMap()
-        for k in h:
-            dict.__setitem__(rh, k, dict.__getitem__(h, k))
-
-        # Add the required Age header
-        response.headers['Age'] = str(age)
-
-        try:
-            # Note that validate_since depends on a Last-Modified header;
-            # this was put into the cached copy, and should have been
-            # resurrected just above (response.headers = cache_data[1]).
-            cptools.validate_since()
-        except cherrypy.HTTPRedirect:
-            x = sys.exc_info()[1]
-            if x.status == 304:
-                cherrypy._cache.tot_non_modified += 1
-            raise
-
-        # serve it & get out from the request
-        response.status = s
-        response.body = b
-    else:
-        if debug:
-            cherrypy.log('request is not cached', 'TOOLS.CACHING')
-    return request.cached
-
-
-def tee_output():
-    """Tee response output to cache storage. Internal."""
-    # Used by CachingTool by attaching to request.hooks
-
-    request = cherrypy.serving.request
-    if 'no-store' in request.headers.values('Cache-Control'):
-        return
-
-    def tee(body):
-        """Tee response.body into a list."""
-        if ('no-cache' in response.headers.values('Pragma') or
-                'no-store' in response.headers.values('Cache-Control')):
-            for chunk in body:
-                yield chunk
-            return
-
-        output = []
-        for chunk in body:
-            output.append(chunk)
-            yield chunk
-
-        # Save the cache data, but only if the body isn't empty.
-        # e.g. a 304 Not Modified on a static file response will
-        # have an empty body.
-        # If the body is empty, delete the cache because it
-        # contains a stale Threading._Event object that will
-        # stall all consecutive requests until the _Event times
-        # out
-        body = b''.join(output)
-        if not body:
-            cherrypy._cache.delete()
-        else:
-            cherrypy._cache.put((response.status, response.headers or {},
-                                 body, response.time), len(body))
-
-    response = cherrypy.serving.response
-    response.body = tee(response.body)
-
-
-def expires(secs=0, force=False, debug=False):
-    """Tool for influencing cache mechanisms using the 'Expires' header.
-
-    secs
-        Must be either an int or a datetime.timedelta, and indicates the
-        number of seconds between response.time and when the response should
-        expire. The 'Expires' header will be set to response.time + secs.
-        If secs is zero, the 'Expires' header is set one year in the past, and
-        the following "cache prevention" headers are also set:
-
-            * Pragma: no-cache
-            * Cache-Control': no-cache, must-revalidate
-
-    force
-        If False, the following headers are checked:
-
-            * Etag
-            * Last-Modified
-            * Age
-            * Expires
-
-        If any are already present, none of the above response headers are set.
-
-    """
-
-    response = cherrypy.serving.response
-    headers = response.headers
-
-    cacheable = False
-    if not force:
-        # some header names that indicate that the response can be cached
-        for indicator in ('Etag', 'Last-Modified', 'Age', 'Expires'):
-            if indicator in headers:
-                cacheable = True
-                break
-
-    if not cacheable and not force:
-        if debug:
-            cherrypy.log('request is not cacheable', 'TOOLS.EXPIRES')
-    else:
-        if debug:
-            cherrypy.log('request is cacheable', 'TOOLS.EXPIRES')
-        if isinstance(secs, datetime.timedelta):
-            secs = (86400 * secs.days) + secs.seconds
-
-        if secs == 0:
-            if force or ('Pragma' not in headers):
-                headers['Pragma'] = 'no-cache'
-            if cherrypy.serving.request.protocol >= (1, 1):
-                if force or 'Cache-Control' not in headers:
-                    headers['Cache-Control'] = 'no-cache, must-revalidate'
-            # Set an explicit Expires date in the past.
-            expiry = httputil.HTTPDate(1169942400.0)
-        else:
-            expiry = httputil.HTTPDate(response.time + secs)
-        if force or 'Expires' not in headers:
-            headers['Expires'] = expiry
diff --git a/libraries/cherrypy/lib/covercp.py b/libraries/cherrypy/lib/covercp.py
deleted file mode 100644
index 0bafca13..00000000
--- a/libraries/cherrypy/lib/covercp.py
+++ /dev/null
@@ -1,391 +0,0 @@
-"""Code-coverage tools for CherryPy.
-
-To use this module, or the coverage tools in the test suite,
-you need to download 'coverage.py', either Gareth Rees' `original
-implementation <http://www.garethrees.org/2001/12/04/python-coverage/>`_
-or Ned Batchelder's `enhanced version:
-<http://www.nedbatchelder.com/code/modules/coverage.html>`_
-
-To turn on coverage tracing, use the following code::
-
-    cherrypy.engine.subscribe('start', covercp.start)
-
-DO NOT subscribe anything on the 'start_thread' channel, as previously
-recommended. Calling start once in the main thread should be sufficient
-to start coverage on all threads. Calling start again in each thread
-effectively clears any coverage data gathered up to that point.
-
-Run your code, then use the ``covercp.serve()`` function to browse the
-results in a web browser. If you run this module from the command line,
-it will call ``serve()`` for you.
-"""
-
-import re
-import sys
-import cgi
-import os
-import os.path
-
-from six.moves import urllib
-
-import cherrypy
-
-
-localFile = os.path.join(os.path.dirname(__file__), 'coverage.cache')
-
-the_coverage = None
-try:
-    from coverage import coverage
-    the_coverage = coverage(data_file=localFile)
-
-    def start():
-        the_coverage.start()
-except ImportError:
-    # Setting the_coverage to None will raise errors
-    # that need to be trapped downstream.
-    the_coverage = None
-
-    import warnings
-    warnings.warn(
-        'No code coverage will be performed; '
-        'coverage.py could not be imported.')
-
-    def start():
-        pass
-start.priority = 20
-
-TEMPLATE_MENU = """<html>
-<head>
-    <title>CherryPy Coverage Menu</title>
-    <style>
-        body {font: 9pt Arial, serif;}
-        #tree {
-            font-size: 8pt;
-            font-family: Andale Mono, monospace;
-            white-space: pre;
-            }
-        #tree a:active, a:focus {
-            background-color: black;
-            padding: 1px;
-            color: white;
-            border: 0px solid #9999FF;
-            -moz-outline-style: none;
-            }
-        .fail { color: red;}
-        .pass { color: #888;}
-        #pct { text-align: right;}
-        h3 {
-            font-size: small;
-            font-weight: bold;
-            font-style: italic;
-            margin-top: 5px;
-            }
-        input { border: 1px solid #ccc; padding: 2px; }
-        .directory {
-            color: #933;
-            font-style: italic;
-            font-weight: bold;
-            font-size: 10pt;
-            }
-        .file {
-            color: #400;
-            }
-        a { text-decoration: none; }
-        #crumbs {
-            color: white;
-            font-size: 8pt;
-            font-family: Andale Mono, monospace;
-            width: 100%;
-            background-color: black;
-            }
-        #crumbs a {
-            color: #f88;
-            }
-        #options {
-            line-height: 2.3em;
-            border: 1px solid black;
-            background-color: #eee;
-            padding: 4px;
-            }
-        #exclude {
-            width: 100%;
-            margin-bottom: 3px;
-            border: 1px solid #999;
-            }
-        #submit {
-            background-color: black;
-            color: white;
-            border: 0;
-            margin-bottom: -9px;
-            }
-    </style>
-</head>
-<body>
-<h2>CherryPy Coverage</h2>"""
-
-TEMPLATE_FORM = """
-<div id="options">
-<form action='menu' method=GET>
-    <input type='hidden' name='base' value='%(base)s' />
-    Show percentages
-    <input type='checkbox' %(showpct)s name='showpct' value='checked' /><br />
-    Hide files over
-    <input type='text' id='pct' name='pct' value='%(pct)s' size='3' />%%<br />
-    Exclude files matching<br />
-    <input type='text' id='exclude' name='exclude'
-     value='%(exclude)s' size='20' />
-    <br />
-
-    <input type='submit' value='Change view' id="submit"/>
-</form>
-</div>"""
-
-TEMPLATE_FRAMESET = """<html>
-<head><title>CherryPy coverage data</title></head>
-<frameset cols='250, 1*'>
-    <frame src='menu?base=%s' />
-    <frame name='main' src='' />
-</frameset>
-</html>
-"""
-
-TEMPLATE_COVERAGE = """<html>
-<head>
-    <title>Coverage for %(name)s</title>
-    <style>
-        h2 { margin-bottom: .25em; }
-        p { margin: .25em; }
-        .covered { color: #000; background-color: #fff; }
-        .notcovered { color: #fee; background-color: #500; }
-        .excluded { color: #00f; background-color: #fff; }
-         table .covered, table .notcovered, table .excluded
-             { font-family: Andale Mono, monospace;
-               font-size: 10pt; white-space: pre; }
-
-         .lineno { background-color: #eee;}
-         .notcovered .lineno { background-color: #000;}
-         table { border-collapse: collapse;
-    </style>
-</head>
-<body>
-<h2>%(name)s</h2>
-<p>%(fullpath)s</p>
-<p>Coverage: %(pc)s%%</p>"""
-
-TEMPLATE_LOC_COVERED = """<tr class="covered">
-    <td class="lineno">%s&nbsp;</td>
-    <td>%s</td>
-</tr>\n"""
-TEMPLATE_LOC_NOT_COVERED = """<tr class="notcovered">
-    <td class="lineno">%s&nbsp;</td>
-    <td>%s</td>
-</tr>\n"""
-TEMPLATE_LOC_EXCLUDED = """<tr class="excluded">
-    <td class="lineno">%s&nbsp;</td>
-    <td>%s</td>
-</tr>\n"""
-
-TEMPLATE_ITEM = (
-    "%s%s<a class='file' href='report?name=%s' target='main'>%s</a>\n"
-)
-
-
-def _percent(statements, missing):
-    s = len(statements)
-    e = s - len(missing)
-    if s > 0:
-        return int(round(100.0 * e / s))
-    return 0
-
-
-def _show_branch(root, base, path, pct=0, showpct=False, exclude='',
-                 coverage=the_coverage):
-
-    # Show the directory name and any of our children
-    dirs = [k for k, v in root.items() if v]
-    dirs.sort()
-    for name in dirs:
-        newpath = os.path.join(path, name)
-
-        if newpath.lower().startswith(base):
-            relpath = newpath[len(base):]
-            yield '| ' * relpath.count(os.sep)
-            yield (
-                "<a class='directory' "
-                "href='menu?base=%s&exclude=%s'>%s</a>\n" %
-                (newpath, urllib.parse.quote_plus(exclude), name)
-            )
-
-        for chunk in _show_branch(
-            root[name], base, newpath, pct, showpct,
-            exclude, coverage=coverage
-        ):
-            yield chunk
-
-    # Now list the files
-    if path.lower().startswith(base):
-        relpath = path[len(base):]
-        files = [k for k, v in root.items() if not v]
-        files.sort()
-        for name in files:
-            newpath = os.path.join(path, name)
-
-            pc_str = ''
-            if showpct:
-                try:
-                    _, statements, _, missing, _ = coverage.analysis2(newpath)
-                except Exception:
-                    # Yes, we really want to pass on all errors.
-                    pass
-                else:
-                    pc = _percent(statements, missing)
-                    pc_str = ('%3d%% ' % pc).replace(' ', '&nbsp;')
-                    if pc < float(pct) or pc == -1:
-                        pc_str = "<span class='fail'>%s</span>" % pc_str
-                    else:
-                        pc_str = "<span class='pass'>%s</span>" % pc_str
-
-            yield TEMPLATE_ITEM % ('| ' * (relpath.count(os.sep) + 1),
-                                   pc_str, newpath, name)
-
-
-def _skip_file(path, exclude):
-    if exclude:
-        return bool(re.search(exclude, path))
-
-
-def _graft(path, tree):
-    d = tree
-
-    p = path
-    atoms = []
-    while True:
-        p, tail = os.path.split(p)
-        if not tail:
-            break
-        atoms.append(tail)
-    atoms.append(p)
-    if p != '/':
-        atoms.append('/')
-
-    atoms.reverse()
-    for node in atoms:
-        if node:
-            d = d.setdefault(node, {})
-
-
-def get_tree(base, exclude, coverage=the_coverage):
-    """Return covered module names as a nested dict."""
-    tree = {}
-    runs = coverage.data.executed_files()
-    for path in runs:
-        if not _skip_file(path, exclude) and not os.path.isdir(path):
-            _graft(path, tree)
-    return tree
-
-
-class CoverStats(object):
-
-    def __init__(self, coverage, root=None):
-        self.coverage = coverage
-        if root is None:
-            # Guess initial depth. Files outside this path will not be
-            # reachable from the web interface.
-            root = os.path.dirname(cherrypy.__file__)
-        self.root = root
-
-    @cherrypy.expose
-    def index(self):
-        return TEMPLATE_FRAMESET % self.root.lower()
-
-    @cherrypy.expose
-    def menu(self, base='/', pct='50', showpct='',
-             exclude=r'python\d\.\d|test|tut\d|tutorial'):
-
-        # The coverage module uses all-lower-case names.
-        base = base.lower().rstrip(os.sep)
-
-        yield TEMPLATE_MENU
-        yield TEMPLATE_FORM % locals()
-
-        # Start by showing links for parent paths
-        yield "<div id='crumbs'>"
-        path = ''
-        atoms = base.split(os.sep)
-        atoms.pop()
-        for atom in atoms:
-            path += atom + os.sep
-            yield ("<a href='menu?base=%s&exclude=%s'>%s</a> %s"
-                   % (path, urllib.parse.quote_plus(exclude), atom, os.sep))
-        yield '</div>'
-
-        yield "<div id='tree'>"
-
-        # Then display the tree
-        tree = get_tree(base, exclude, self.coverage)
-        if not tree:
-            yield '<p>No modules covered.</p>'
-        else:
-            for chunk in _show_branch(tree, base, '/', pct,
-                                      showpct == 'checked', exclude,
-                                      coverage=self.coverage):
-                yield chunk
-
-        yield '</div>'
-        yield '</body></html>'
-
-    def annotated_file(self, filename, statements, excluded, missing):
-        source = open(filename, 'r')
-        buffer = []
-        for lineno, line in enumerate(source.readlines()):
-            lineno += 1
-            line = line.strip('\n\r')
-            empty_the_buffer = True
-            if lineno in excluded:
-                template = TEMPLATE_LOC_EXCLUDED
-            elif lineno in missing:
-                template = TEMPLATE_LOC_NOT_COVERED
-            elif lineno in statements:
-                template = TEMPLATE_LOC_COVERED
-            else:
-                empty_the_buffer = False
-                buffer.append((lineno, line))
-            if empty_the_buffer:
-                for lno, pastline in buffer:
-                    yield template % (lno, cgi.escape(pastline))
-                buffer = []
-                yield template % (lineno, cgi.escape(line))
-
-    @cherrypy.expose
-    def report(self, name):
-        filename, statements, excluded, missing, _ = self.coverage.analysis2(
-            name)
-        pc = _percent(statements, missing)
-        yield TEMPLATE_COVERAGE % dict(name=os.path.basename(name),
-                                       fullpath=name,
-                                       pc=pc)
-        yield '<table>\n'
-        for line in self.annotated_file(filename, statements, excluded,
-                                        missing):
-            yield line
-        yield '</table>'
-        yield '</body>'
-        yield '</html>'
-
-
-def serve(path=localFile, port=8080, root=None):
-    if coverage is None:
-        raise ImportError('The coverage module could not be imported.')
-    from coverage import coverage
-    cov = coverage(data_file=path)
-    cov.load()
-
-    cherrypy.config.update({'server.socket_port': int(port),
-                            'server.thread_pool': 10,
-                            'environment': 'production',
-                            })
-    cherrypy.quickstart(CoverStats(cov, root))
-
-
-if __name__ == '__main__':
-    serve(*tuple(sys.argv[1:]))
diff --git a/libraries/cherrypy/lib/cpstats.py b/libraries/cherrypy/lib/cpstats.py
deleted file mode 100644
index ae9f7475..00000000
--- a/libraries/cherrypy/lib/cpstats.py
+++ /dev/null
@@ -1,696 +0,0 @@
-"""CPStats, a package for collecting and reporting on program statistics.
-
-Overview
-========
-
-Statistics about program operation are an invaluable monitoring and debugging
-tool. Unfortunately, the gathering and reporting of these critical values is
-usually ad-hoc. This package aims to add a centralized place for gathering
-statistical performance data, a structure for recording that data which
-provides for extrapolation of that data into more useful information,
-and a method of serving that data to both human investigators and
-monitoring software. Let's examine each of those in more detail.
-
-Data Gathering
---------------
-
-Just as Python's `logging` module provides a common importable for gathering
-and sending messages, performance statistics would benefit from a similar
-common mechanism, and one that does *not* require each package which wishes
-to collect stats to import a third-party module. Therefore, we choose to
-re-use the `logging` module by adding a `statistics` object to it.
-
-That `logging.statistics` object is a nested dict. It is not a custom class,
-because that would:
-
- 1. require libraries and applications to import a third-party module in
-    order to participate
- 2. inhibit innovation in extrapolation approaches and in reporting tools, and
- 3. be slow.
-
-There are, however, some specifications regarding the structure of the dict.::
-
-   {
-     +----"SQLAlchemy": {
-     |        "Inserts": 4389745,
-     |        "Inserts per Second":
-     |            lambda s: s["Inserts"] / (time() - s["Start"]),
-     |  C +---"Table Statistics": {
-     |  o |        "widgets": {-----------+
-   N |  l |            "Rows": 1.3M,      | Record
-   a |  l |            "Inserts": 400,    |
-   m |  e |        },---------------------+
-   e |  c |        "froobles": {
-   s |  t |            "Rows": 7845,
-   p |  i |            "Inserts": 0,
-   a |  o |        },
-   c |  n +---},
-   e |        "Slow Queries":
-     |            [{"Query": "SELECT * FROM widgets;",
-     |              "Processing Time": 47.840923343,
-     |              },
-     |             ],
-     +----},
-   }
-
-The `logging.statistics` dict has four levels. The topmost level is nothing
-more than a set of names to introduce modularity, usually along the lines of
-package names. If the SQLAlchemy project wanted to participate, for example,
-it might populate the item `logging.statistics['SQLAlchemy']`, whose value
-would be a second-layer dict we call a "namespace". Namespaces help multiple
-packages to avoid collisions over key names, and make reports easier to read,
-to boot. The maintainers of SQLAlchemy should feel free to use more than one
-namespace if needed (such as 'SQLAlchemy ORM'). Note that there are no case
-or other syntax constraints on the namespace names; they should be chosen
-to be maximally readable by humans (neither too short nor too long).
-
-Each namespace, then, is a dict of named statistical values, such as
-'Requests/sec' or 'Uptime'. You should choose names which will look
-good on a report: spaces and capitalization are just fine.
-
-In addition to scalars, values in a namespace MAY be a (third-layer)
-dict, or a list, called a "collection". For example, the CherryPy
-:class:`StatsTool` keeps track of what each request is doing (or has most
-recently done) in a 'Requests' collection, where each key is a thread ID; each
-value in the subdict MUST be a fourth dict (whew!) of statistical data about
-each thread. We call each subdict in the collection a "record". Similarly,
-the :class:`StatsTool` also keeps a list of slow queries, where each record
-contains data about each slow query, in order.
-
-Values in a namespace or record may also be functions, which brings us to:
-
-Extrapolation
--------------
-
-The collection of statistical data needs to be fast, as close to unnoticeable
-as possible to the host program. That requires us to minimize I/O, for example,
-but in Python it also means we need to minimize function calls. So when you
-are designing your namespace and record values, try to insert the most basic
-scalar values you already have on hand.
-
-When it comes time to report on the gathered data, however, we usually have
-much more freedom in what we can calculate. Therefore, whenever reporting
-tools (like the provided :class:`StatsPage` CherryPy class) fetch the contents
-of `logging.statistics` for reporting, they first call
-`extrapolate_statistics` (passing the whole `statistics` dict as the only
-argument). This makes a deep copy of the statistics dict so that the
-reporting tool can both iterate over it and even change it without harming
-the original. But it also expands any functions in the dict by calling them.
-For example, you might have a 'Current Time' entry in the namespace with the
-value "lambda scope: time.time()". The "scope" parameter is the current
-namespace dict (or record, if we're currently expanding one of those
-instead), allowing you access to existing static entries. If you're truly
-evil, you can even modify more than one entry at a time.
-
-However, don't try to calculate an entry and then use its value in further
-extrapolations; the order in which the functions are called is not guaranteed.
-This can lead to a certain amount of duplicated work (or a redesign of your
-schema), but that's better than complicating the spec.
-
-After the whole thing has been extrapolated, it's time for:
-
-Reporting
----------
-
-The :class:`StatsPage` class grabs the `logging.statistics` dict, extrapolates
-it all, and then transforms it to HTML for easy viewing. Each namespace gets
-its own header and attribute table, plus an extra table for each collection.
-This is NOT part of the statistics specification; other tools can format how
-they like.
-
-You can control which columns are output and how they are formatted by updating
-StatsPage.formatting, which is a dict that mirrors the keys and nesting of
-`logging.statistics`. The difference is that, instead of data values, it has
-formatting values. Use None for a given key to indicate to the StatsPage that a
-given column should not be output. Use a string with formatting
-(such as '%.3f') to interpolate the value(s), or use a callable (such as
-lambda v: v.isoformat()) for more advanced formatting. Any entry which is not
-mentioned in the formatting dict is output unchanged.
-
-Monitoring
-----------
-
-Although the HTML output takes pains to assign unique id's to each <td> with
-statistical data, you're probably better off fetching /cpstats/data, which
-outputs the whole (extrapolated) `logging.statistics` dict in JSON format.
-That is probably easier to parse, and doesn't have any formatting controls,
-so you get the "original" data in a consistently-serialized format.
-Note: there's no treatment yet for datetime objects. Try time.time() instead
-for now if you can. Nagios will probably thank you.
-
-Turning Collection Off
-----------------------
-
-It is recommended each namespace have an "Enabled" item which, if False,
-stops collection (but not reporting) of statistical data. Applications
-SHOULD provide controls to pause and resume collection by setting these
-entries to False or True, if present.
-
-
-Usage
-=====
-
-To collect statistics on CherryPy applications::
-
-    from cherrypy.lib import cpstats
-    appconfig['/']['tools.cpstats.on'] = True
-
-To collect statistics on your own code::
-
-    import logging
-    # Initialize the repository
-    if not hasattr(logging, 'statistics'): logging.statistics = {}
-    # Initialize my namespace
-    mystats = logging.statistics.setdefault('My Stuff', {})
-    # Initialize my namespace's scalars and collections
-    mystats.update({
-        'Enabled': True,
-        'Start Time': time.time(),
-        'Important Events': 0,
-        'Events/Second': lambda s: (
-            (s['Important Events'] / (time.time() - s['Start Time']))),
-        })
-    ...
-    for event in events:
-        ...
-        # Collect stats
-        if mystats.get('Enabled', False):
-            mystats['Important Events'] += 1
-
-To report statistics::
-
-    root.cpstats = cpstats.StatsPage()
-
-To format statistics reports::
-
-    See 'Reporting', above.
-
-"""
-
-import logging
-import os
-import sys
-import threading
-import time
-
-import six
-
-import cherrypy
-from cherrypy._cpcompat import json
-
-# ------------------------------- Statistics -------------------------------- #
-
-if not hasattr(logging, 'statistics'):
-    logging.statistics = {}
-
-
-def extrapolate_statistics(scope):
-    """Return an extrapolated copy of the given scope."""
-    c = {}
-    for k, v in list(scope.items()):
-        if isinstance(v, dict):
-            v = extrapolate_statistics(v)
-        elif isinstance(v, (list, tuple)):
-            v = [extrapolate_statistics(record) for record in v]
-        elif hasattr(v, '__call__'):
-            v = v(scope)
-        c[k] = v
-    return c
-
-
-# -------------------- CherryPy Applications Statistics --------------------- #
-
-appstats = logging.statistics.setdefault('CherryPy Applications', {})
-appstats.update({
-    'Enabled': True,
-    'Bytes Read/Request': lambda s: (
-        s['Total Requests'] and
-        (s['Total Bytes Read'] / float(s['Total Requests'])) or
-        0.0
-    ),
-    'Bytes Read/Second': lambda s: s['Total Bytes Read'] / s['Uptime'](s),
-    'Bytes Written/Request': lambda s: (
-        s['Total Requests'] and
-        (s['Total Bytes Written'] / float(s['Total Requests'])) or
-        0.0
-    ),
-    'Bytes Written/Second': lambda s: (
-        s['Total Bytes Written'] / s['Uptime'](s)
-    ),
-    'Current Time': lambda s: time.time(),
-    'Current Requests': 0,
-    'Requests/Second': lambda s: float(s['Total Requests']) / s['Uptime'](s),
-    'Server Version': cherrypy.__version__,
-    'Start Time': time.time(),
-    'Total Bytes Read': 0,
-    'Total Bytes Written': 0,
-    'Total Requests': 0,
-    'Total Time': 0,
-    'Uptime': lambda s: time.time() - s['Start Time'],
-    'Requests': {},
-})
-
-
-def proc_time(s):
-    return time.time() - s['Start Time']
-
-
-class ByteCountWrapper(object):
-
-    """Wraps a file-like object, counting the number of bytes read."""
-
-    def __init__(self, rfile):
-        self.rfile = rfile
-        self.bytes_read = 0
-
-    def read(self, size=-1):
-        data = self.rfile.read(size)
-        self.bytes_read += len(data)
-        return data
-
-    def readline(self, size=-1):
-        data = self.rfile.readline(size)
-        self.bytes_read += len(data)
-        return data
-
-    def readlines(self, sizehint=0):
-        # Shamelessly stolen from StringIO
-        total = 0
-        lines = []
-        line = self.readline()
-        while line:
-            lines.append(line)
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline()
-        return lines
-
-    def close(self):
-        self.rfile.close()
-
-    def __iter__(self):
-        return self
-
-    def next(self):
-        data = self.rfile.next()
-        self.bytes_read += len(data)
-        return data
-
-
-def average_uriset_time(s):
-    return s['Count'] and (s['Sum'] / s['Count']) or 0
-
-
-def _get_threading_ident():
-    if sys.version_info >= (3, 3):
-        return threading.get_ident()
-    return threading._get_ident()
-
-
-class StatsTool(cherrypy.Tool):
-
-    """Record various information about the current request."""
-
-    def __init__(self):
-        cherrypy.Tool.__init__(self, 'on_end_request', self.record_stop)
-
-    def _setup(self):
-        """Hook this tool into cherrypy.request.
-
-        The standard CherryPy request object will automatically call this
-        method when the tool is "turned on" in config.
-        """
-        if appstats.get('Enabled', False):
-            cherrypy.Tool._setup(self)
-            self.record_start()
-
-    def record_start(self):
-        """Record the beginning of a request."""
-        request = cherrypy.serving.request
-        if not hasattr(request.rfile, 'bytes_read'):
-            request.rfile = ByteCountWrapper(request.rfile)
-            request.body.fp = request.rfile
-
-        r = request.remote
-
-        appstats['Current Requests'] += 1
-        appstats['Total Requests'] += 1
-        appstats['Requests'][_get_threading_ident()] = {
-            'Bytes Read': None,
-            'Bytes Written': None,
-            # Use a lambda so the ip gets updated by tools.proxy later
-            'Client': lambda s: '%s:%s' % (r.ip, r.port),
-            'End Time': None,
-            'Processing Time': proc_time,
-            'Request-Line': request.request_line,
-            'Response Status': None,
-            'Start Time': time.time(),
-        }
-
-    def record_stop(
-            self, uriset=None, slow_queries=1.0, slow_queries_count=100,
-            debug=False, **kwargs):
-        """Record the end of a request."""
-        resp = cherrypy.serving.response
-        w = appstats['Requests'][_get_threading_ident()]
-
-        r = cherrypy.request.rfile.bytes_read
-        w['Bytes Read'] = r
-        appstats['Total Bytes Read'] += r
-
-        if resp.stream:
-            w['Bytes Written'] = 'chunked'
-        else:
-            cl = int(resp.headers.get('Content-Length', 0))
-            w['Bytes Written'] = cl
-            appstats['Total Bytes Written'] += cl
-
-        w['Response Status'] = getattr(
-            resp, 'output_status', None) or resp.status
-
-        w['End Time'] = time.time()
-        p = w['End Time'] - w['Start Time']
-        w['Processing Time'] = p
-        appstats['Total Time'] += p
-
-        appstats['Current Requests'] -= 1
-
-        if debug:
-            cherrypy.log('Stats recorded: %s' % repr(w), 'TOOLS.CPSTATS')
-
-        if uriset:
-            rs = appstats.setdefault('URI Set Tracking', {})
-            r = rs.setdefault(uriset, {
-                'Min': None, 'Max': None, 'Count': 0, 'Sum': 0,
-                'Avg': average_uriset_time})
-            if r['Min'] is None or p < r['Min']:
-                r['Min'] = p
-            if r['Max'] is None or p > r['Max']:
-                r['Max'] = p
-            r['Count'] += 1
-            r['Sum'] += p
-
-        if slow_queries and p > slow_queries:
-            sq = appstats.setdefault('Slow Queries', [])
-            sq.append(w.copy())
-            if len(sq) > slow_queries_count:
-                sq.pop(0)
-
-
-cherrypy.tools.cpstats = StatsTool()
-
-
-# ---------------------- CherryPy Statistics Reporting ---------------------- #
-
-thisdir = os.path.abspath(os.path.dirname(__file__))
-
-missing = object()
-
-
-def locale_date(v):
-    return time.strftime('%c', time.gmtime(v))
-
-
-def iso_format(v):
-    return time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(v))
-
-
-def pause_resume(ns):
-    def _pause_resume(enabled):
-        pause_disabled = ''
-        resume_disabled = ''
-        if enabled:
-            resume_disabled = 'disabled="disabled" '
-        else:
-            pause_disabled = 'disabled="disabled" '
-        return """
-            <form action="pause" method="POST" style="display:inline">
-            <input type="hidden" name="namespace" value="%s" />
-            <input type="submit" value="Pause" %s/>
-            </form>
-            <form action="resume" method="POST" style="display:inline">
-            <input type="hidden" name="namespace" value="%s" />
-            <input type="submit" value="Resume" %s/>
-            </form>
-            """ % (ns, pause_disabled, ns, resume_disabled)
-    return _pause_resume
-
-
-class StatsPage(object):
-
-    formatting = {
-        'CherryPy Applications': {
-            'Enabled': pause_resume('CherryPy Applications'),
-            'Bytes Read/Request': '%.3f',
-            'Bytes Read/Second': '%.3f',
-            'Bytes Written/Request': '%.3f',
-            'Bytes Written/Second': '%.3f',
-            'Current Time': iso_format,
-            'Requests/Second': '%.3f',
-            'Start Time': iso_format,
-            'Total Time': '%.3f',
-            'Uptime': '%.3f',
-            'Slow Queries': {
-                'End Time': None,
-                'Processing Time': '%.3f',
-                'Start Time': iso_format,
-            },
-            'URI Set Tracking': {
-                'Avg': '%.3f',
-                'Max': '%.3f',
-                'Min': '%.3f',
-                'Sum': '%.3f',
-            },
-            'Requests': {
-                'Bytes Read': '%s',
-                'Bytes Written': '%s',
-                'End Time': None,
-                'Processing Time': '%.3f',
-                'Start Time': None,
-            },
-        },
-        'CherryPy WSGIServer': {
-            'Enabled': pause_resume('CherryPy WSGIServer'),
-            'Connections/second': '%.3f',
-            'Start time': iso_format,
-        },
-    }
-
-    @cherrypy.expose
-    def index(self):
-        # Transform the raw data into pretty output for HTML
-        yield """
-<html>
-<head>
-    <title>Statistics</title>
-<style>
-
-th, td {
-    padding: 0.25em 0.5em;
-    border: 1px solid #666699;
-}
-
-table {
-    border-collapse: collapse;
-}
-
-table.stats1 {
-    width: 100%;
-}
-
-table.stats1 th {
-    font-weight: bold;
-    text-align: right;
-    background-color: #CCD5DD;
-}
-
-table.stats2, h2 {
-    margin-left: 50px;
-}
-
-table.stats2 th {
-    font-weight: bold;
-    text-align: center;
-    background-color: #CCD5DD;
-}
-
-</style>
-</head>
-<body>
-"""
-        for title, scalars, collections in self.get_namespaces():
-            yield """
-<h1>%s</h1>
-
-<table class='stats1'>
-    <tbody>
-""" % title
-            for i, (key, value) in enumerate(scalars):
-                colnum = i % 3
-                if colnum == 0:
-                    yield """
-        <tr>"""
-                yield (
-                    """
-            <th>%(key)s</th><td id='%(title)s-%(key)s'>%(value)s</td>""" %
-                    vars()
-                )
-                if colnum == 2:
-                    yield """
-        </tr>"""
-
-            if colnum == 0:
-                yield """
-            <th></th><td></td>
-            <th></th><td></td>
-        </tr>"""
-            elif colnum == 1:
-                yield """
-            <th></th><td></td>
-        </tr>"""
-            yield """
-    </tbody>
-</table>"""
-
-            for subtitle, headers, subrows in collections:
-                yield """
-<h2>%s</h2>
-<table class='stats2'>
-    <thead>
-        <tr>""" % subtitle
-                for key in headers:
-                    yield """
-            <th>%s</th>""" % key
-                yield """
-        </tr>
-    </thead>
-    <tbody>"""
-                for subrow in subrows:
-                    yield """
-        <tr>"""
-                    for value in subrow:
-                        yield """
-            <td>%s</td>""" % value
-                    yield """
-        </tr>"""
-                yield """
-    </tbody>
-</table>"""
-        yield """
-</body>
-</html>
-"""
-
-    def get_namespaces(self):
-        """Yield (title, scalars, collections) for each namespace."""
-        s = extrapolate_statistics(logging.statistics)
-        for title, ns in sorted(s.items()):
-            scalars = []
-            collections = []
-            ns_fmt = self.formatting.get(title, {})
-            for k, v in sorted(ns.items()):
-                fmt = ns_fmt.get(k, {})
-                if isinstance(v, dict):
-                    headers, subrows = self.get_dict_collection(v, fmt)
-                    collections.append((k, ['ID'] + headers, subrows))
-                elif isinstance(v, (list, tuple)):
-                    headers, subrows = self.get_list_collection(v, fmt)
-                    collections.append((k, headers, subrows))
-                else:
-                    format = ns_fmt.get(k, missing)
-                    if format is None:
-                        # Don't output this column.
-                        continue
-                    if hasattr(format, '__call__'):
-                        v = format(v)
-                    elif format is not missing:
-                        v = format % v
-                    scalars.append((k, v))
-            yield title, scalars, collections
-
-    def get_dict_collection(self, v, formatting):
-        """Return ([headers], [rows]) for the given collection."""
-        # E.g., the 'Requests' dict.
-        headers = []
-        vals = six.itervalues(v)
-        for record in vals:
-            for k3 in record:
-                format = formatting.get(k3, missing)
-                if format is None:
-                    # Don't output this column.
-                    continue
-                if k3 not in headers:
-                    headers.append(k3)
-        headers.sort()
-
-        subrows = []
-        for k2, record in sorted(v.items()):
-            subrow = [k2]
-            for k3 in headers:
-                v3 = record.get(k3, '')
-                format = formatting.get(k3, missing)
-                if format is None:
-                    # Don't output this column.
-                    continue
-                if hasattr(format, '__call__'):
-                    v3 = format(v3)
-                elif format is not missing:
-                    v3 = format % v3
-                subrow.append(v3)
-            subrows.append(subrow)
-
-        return headers, subrows
-
-    def get_list_collection(self, v, formatting):
-        """Return ([headers], [subrows]) for the given collection."""
-        # E.g., the 'Slow Queries' list.
-        headers = []
-        for record in v:
-            for k3 in record:
-                format = formatting.get(k3, missing)
-                if format is None:
-                    # Don't output this column.
-                    continue
-                if k3 not in headers:
-                    headers.append(k3)
-        headers.sort()
-
-        subrows = []
-        for record in v:
-            subrow = []
-            for k3 in headers:
-                v3 = record.get(k3, '')
-                format = formatting.get(k3, missing)
-                if format is None:
-                    # Don't output this column.
-                    continue
-                if hasattr(format, '__call__'):
-                    v3 = format(v3)
-                elif format is not missing:
-                    v3 = format % v3
-                subrow.append(v3)
-            subrows.append(subrow)
-
-        return headers, subrows
-
-    if json is not None:
-        @cherrypy.expose
-        def data(self):
-            s = extrapolate_statistics(logging.statistics)
-            cherrypy.response.headers['Content-Type'] = 'application/json'
-            return json.dumps(s, sort_keys=True, indent=4)
-
-    @cherrypy.expose
-    def pause(self, namespace):
-        logging.statistics.get(namespace, {})['Enabled'] = False
-        raise cherrypy.HTTPRedirect('./')
-    pause.cp_config = {'tools.allow.on': True,
-                       'tools.allow.methods': ['POST']}
-
-    @cherrypy.expose
-    def resume(self, namespace):
-        logging.statistics.get(namespace, {})['Enabled'] = True
-        raise cherrypy.HTTPRedirect('./')
-    resume.cp_config = {'tools.allow.on': True,
-                        'tools.allow.methods': ['POST']}
diff --git a/libraries/cherrypy/lib/cptools.py b/libraries/cherrypy/lib/cptools.py
deleted file mode 100644
index 1c079634..00000000
--- a/libraries/cherrypy/lib/cptools.py
+++ /dev/null
@@ -1,640 +0,0 @@
-"""Functions for builtin CherryPy tools."""
-
-import logging
-import re
-from hashlib import md5
-
-import six
-from six.moves import urllib
-
-import cherrypy
-from cherrypy._cpcompat import text_or_bytes
-from cherrypy.lib import httputil as _httputil
-from cherrypy.lib import is_iterator
-
-
-#                     Conditional HTTP request support                     #
-
-def validate_etags(autotags=False, debug=False):
-    """Validate the current ETag against If-Match, If-None-Match headers.
-
-    If autotags is True, an ETag response-header value will be provided
-    from an MD5 hash of the response body (unless some other code has
-    already provided an ETag header). If False (the default), the ETag
-    will not be automatic.
-
-    WARNING: the autotags feature is not designed for URL's which allow
-    methods other than GET. For example, if a POST to the same URL returns
-    no content, the automatic ETag will be incorrect, breaking a fundamental
-    use for entity tags in a possibly destructive fashion. Likewise, if you
-    raise 304 Not Modified, the response body will be empty, the ETag hash
-    will be incorrect, and your application will break.
-    See :rfc:`2616` Section 14.24.
-    """
-    response = cherrypy.serving.response
-
-    # Guard against being run twice.
-    if hasattr(response, 'ETag'):
-        return
-
-    status, reason, msg = _httputil.valid_status(response.status)
-
-    etag = response.headers.get('ETag')
-
-    # Automatic ETag generation. See warning in docstring.
-    if etag:
-        if debug:
-            cherrypy.log('ETag already set: %s' % etag, 'TOOLS.ETAGS')
-    elif not autotags:
-        if debug:
-            cherrypy.log('Autotags off', 'TOOLS.ETAGS')
-    elif status != 200:
-        if debug:
-            cherrypy.log('Status not 200', 'TOOLS.ETAGS')
-    else:
-        etag = response.collapse_body()
-        etag = '"%s"' % md5(etag).hexdigest()
-        if debug:
-            cherrypy.log('Setting ETag: %s' % etag, 'TOOLS.ETAGS')
-        response.headers['ETag'] = etag
-
-    response.ETag = etag
-
-    # "If the request would, without the If-Match header field, result in
-    # anything other than a 2xx or 412 status, then the If-Match header
-    # MUST be ignored."
-    if debug:
-        cherrypy.log('Status: %s' % status, 'TOOLS.ETAGS')
-    if status >= 200 and status <= 299:
-        request = cherrypy.serving.request
-
-        conditions = request.headers.elements('If-Match') or []
-        conditions = [str(x) for x in conditions]
-        if debug:
-            cherrypy.log('If-Match conditions: %s' % repr(conditions),
-                         'TOOLS.ETAGS')
-        if conditions and not (conditions == ['*'] or etag in conditions):
-            raise cherrypy.HTTPError(412, 'If-Match failed: ETag %r did '
-                                     'not match %r' % (etag, conditions))
-
-        conditions = request.headers.elements('If-None-Match') or []
-        conditions = [str(x) for x in conditions]
-        if debug:
-            cherrypy.log('If-None-Match conditions: %s' % repr(conditions),
-                         'TOOLS.ETAGS')
-        if conditions == ['*'] or etag in conditions:
-            if debug:
-                cherrypy.log('request.method: %s' %
-                             request.method, 'TOOLS.ETAGS')
-            if request.method in ('GET', 'HEAD'):
-                raise cherrypy.HTTPRedirect([], 304)
-            else:
-                raise cherrypy.HTTPError(412, 'If-None-Match failed: ETag %r '
-                                         'matched %r' % (etag, conditions))
-
-
-def validate_since():
-    """Validate the current Last-Modified against If-Modified-Since headers.
-
-    If no code has set the Last-Modified response header, then no validation
-    will be performed.
-    """
-    response = cherrypy.serving.response
-    lastmod = response.headers.get('Last-Modified')
-    if lastmod:
-        status, reason, msg = _httputil.valid_status(response.status)
-
-        request = cherrypy.serving.request
-
-        since = request.headers.get('If-Unmodified-Since')
-        if since and since != lastmod:
-            if (status >= 200 and status <= 299) or status == 412:
-                raise cherrypy.HTTPError(412)
-
-        since = request.headers.get('If-Modified-Since')
-        if since and since == lastmod:
-            if (status >= 200 and status <= 299) or status == 304:
-                if request.method in ('GET', 'HEAD'):
-                    raise cherrypy.HTTPRedirect([], 304)
-                else:
-                    raise cherrypy.HTTPError(412)
-
-
-#                                Tool code                                #
-
-def allow(methods=None, debug=False):
-    """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;
-    if more than one, supply a list of strings.
-
-    Regardless of whether the current method is allowed or not, this
-    also emits an 'Allow' response header, containing the given methods.
-    """
-    if not isinstance(methods, (tuple, list)):
-        methods = [methods]
-    methods = [m.upper() for m in methods if m]
-    if not methods:
-        methods = ['GET', 'HEAD']
-    elif 'GET' in methods and 'HEAD' not in methods:
-        methods.append('HEAD')
-
-    cherrypy.response.headers['Allow'] = ', '.join(methods)
-    if cherrypy.request.method not in methods:
-        if debug:
-            cherrypy.log('request.method %r not in methods %r' %
-                         (cherrypy.request.method, methods), 'TOOLS.ALLOW')
-        raise cherrypy.HTTPError(405)
-    else:
-        if debug:
-            cherrypy.log('request.method %r in methods %r' %
-                         (cherrypy.request.method, methods), 'TOOLS.ALLOW')
-
-
-def proxy(base=None, local='X-Forwarded-Host', remote='X-Forwarded-For',
-          scheme='X-Forwarded-Proto', debug=False):
-    """Change the base URL (scheme://host[:port][/path]).
-
-    For running a CP server behind Apache, lighttpd, or other HTTP server.
-
-    For Apache and lighttpd, you should leave the 'local' argument at the
-    default value of 'X-Forwarded-Host'. For Squid, you probably want to set
-    tools.proxy.local = 'Origin'.
-
-    If you want the new request.base to include path info (not just the host),
-    you must explicitly set base to the full base path, and ALSO set 'local'
-    to '', so that the X-Forwarded-Host request header (which never includes
-    path info) does not override it. Regardless, the value for 'base' MUST
-    NOT end in a slash.
-
-    cherrypy.request.remote.ip (the IP address of the client) will be
-    rewritten if the header specified by the 'remote' arg is valid.
-    By default, 'remote' is set to 'X-Forwarded-For'. If you do not
-    want to rewrite remote.ip, set the 'remote' arg to an empty string.
-    """
-
-    request = cherrypy.serving.request
-
-    if scheme:
-        s = request.headers.get(scheme, None)
-        if debug:
-            cherrypy.log('Testing scheme %r:%r' % (scheme, s), 'TOOLS.PROXY')
-        if s == 'on' and 'ssl' in scheme.lower():
-            # This handles e.g. webfaction's 'X-Forwarded-Ssl: on' header
-            scheme = 'https'
-        else:
-            # This is for lighttpd/pound/Mongrel's 'X-Forwarded-Proto: https'
-            scheme = s
-    if not scheme:
-        scheme = request.base[:request.base.find('://')]
-
-    if local:
-        lbase = request.headers.get(local, None)
-        if debug:
-            cherrypy.log('Testing local %r:%r' % (local, lbase), 'TOOLS.PROXY')
-        if lbase is not None:
-            base = lbase.split(',')[0]
-    if not base:
-        default = urllib.parse.urlparse(request.base).netloc
-        base = request.headers.get('Host', default)
-
-    if base.find('://') == -1:
-        # add http:// or https:// if needed
-        base = scheme + '://' + base
-
-    request.base = base
-
-    if remote:
-        xff = request.headers.get(remote)
-        if debug:
-            cherrypy.log('Testing remote %r:%r' % (remote, xff), 'TOOLS.PROXY')
-        if xff:
-            if remote == 'X-Forwarded-For':
-                # Grab the first IP in a comma-separated list. Ref #1268.
-                xff = next(ip.strip() for ip in xff.split(','))
-            request.remote.ip = xff
-
-
-def ignore_headers(headers=('Range',), debug=False):
-    """Delete request headers whose field names are included in 'headers'.
-
-    This is a useful tool for working behind certain HTTP servers;
-    for example, Apache duplicates the work that CP does for 'Range'
-    headers, and will doubly-truncate the response.
-    """
-    request = cherrypy.serving.request
-    for name in headers:
-        if name in request.headers:
-            if debug:
-                cherrypy.log('Ignoring request header %r' % name,
-                             'TOOLS.IGNORE_HEADERS')
-            del request.headers[name]
-
-
-def response_headers(headers=None, debug=False):
-    """Set headers on the response."""
-    if debug:
-        cherrypy.log('Setting response headers: %s' % repr(headers),
-                     'TOOLS.RESPONSE_HEADERS')
-    for name, value in (headers or []):
-        cherrypy.serving.response.headers[name] = value
-
-
-response_headers.failsafe = True
-
-
-def referer(pattern, accept=True, accept_missing=False, error=403,
-            message='Forbidden Referer header.', debug=False):
-    """Raise HTTPError if Referer header does/does not match the given pattern.
-
-    pattern
-        A regular expression pattern to test against the Referer.
-
-    accept
-        If True, the Referer must match the pattern; if False,
-        the Referer must NOT match the pattern.
-
-    accept_missing
-        If True, permit requests with no Referer header.
-
-    error
-        The HTTP error code to return to the client on failure.
-
-    message
-        A string to include in the response body on failure.
-
-    """
-    try:
-        ref = cherrypy.serving.request.headers['Referer']
-        match = bool(re.match(pattern, ref))
-        if debug:
-            cherrypy.log('Referer %r matches %r' % (ref, pattern),
-                         'TOOLS.REFERER')
-        if accept == match:
-            return
-    except KeyError:
-        if debug:
-            cherrypy.log('No Referer header', 'TOOLS.REFERER')
-        if accept_missing:
-            return
-
-    raise cherrypy.HTTPError(error, message)
-
-
-class SessionAuth(object):
-
-    """Assert that the user is logged in."""
-
-    session_key = 'username'
-    debug = False
-
-    def check_username_and_password(self, username, password):
-        pass
-
-    def anonymous(self):
-        """Provide a temporary user name for anonymous users."""
-        pass
-
-    def on_login(self, username):
-        pass
-
-    def on_logout(self, username):
-        pass
-
-    def on_check(self, username):
-        pass
-
-    def login_screen(self, from_page='..', username='', error_msg='',
-                     **kwargs):
-        return (six.text_type("""<html><body>
-Message: %(error_msg)s
-<form method="post" action="do_login">
-    Login: <input type="text" name="username" value="%(username)s" size="10" />
-    <br />
-    Password: <input type="password" name="password" size="10" />
-    <br />
-    <input type="hidden" name="from_page" value="%(from_page)s" />
-    <br />
-    <input type="submit" />
-</form>
-</body></html>""") % vars()).encode('utf-8')
-
-    def do_login(self, username, password, from_page='..', **kwargs):
-        """Login. May raise redirect, or return True if request handled."""
-        response = cherrypy.serving.response
-        error_msg = self.check_username_and_password(username, password)
-        if error_msg:
-            body = self.login_screen(from_page, username, error_msg)
-            response.body = body
-            if 'Content-Length' in response.headers:
-                # Delete Content-Length header so finalize() recalcs it.
-                del response.headers['Content-Length']
-            return True
-        else:
-            cherrypy.serving.request.login = username
-            cherrypy.session[self.session_key] = username
-            self.on_login(username)
-            raise cherrypy.HTTPRedirect(from_page or '/')
-
-    def do_logout(self, from_page='..', **kwargs):
-        """Logout. May raise redirect, or return True if request handled."""
-        sess = cherrypy.session
-        username = sess.get(self.session_key)
-        sess[self.session_key] = None
-        if username:
-            cherrypy.serving.request.login = None
-            self.on_logout(username)
-        raise cherrypy.HTTPRedirect(from_page)
-
-    def do_check(self):
-        """Assert username. Raise redirect, or return True if request handled.
-        """
-        sess = cherrypy.session
-        request = cherrypy.serving.request
-        response = cherrypy.serving.response
-
-        username = sess.get(self.session_key)
-        if not username:
-            sess[self.session_key] = username = self.anonymous()
-            self._debug_message('No session[username], trying anonymous')
-        if not username:
-            url = cherrypy.url(qs=request.query_string)
-            self._debug_message(
-                'No username, routing to login_screen with from_page %(url)r',
-                locals(),
-            )
-            response.body = self.login_screen(url)
-            if 'Content-Length' in response.headers:
-                # Delete Content-Length header so finalize() recalcs it.
-                del response.headers['Content-Length']
-            return True
-        self._debug_message('Setting request.login to %(username)r', locals())
-        request.login = username
-        self.on_check(username)
-
-    def _debug_message(self, template, context={}):
-        if not self.debug:
-            return
-        cherrypy.log(template % context, 'TOOLS.SESSAUTH')
-
-    def run(self):
-        request = cherrypy.serving.request
-        response = cherrypy.serving.response
-
-        path = request.path_info
-        if path.endswith('login_screen'):
-            self._debug_message('routing %(path)r to login_screen', locals())
-            response.body = self.login_screen()
-            return True
-        elif path.endswith('do_login'):
-            if request.method != 'POST':
-                response.headers['Allow'] = 'POST'
-                self._debug_message('do_login requires POST')
-                raise cherrypy.HTTPError(405)
-            self._debug_message('routing %(path)r to do_login', locals())
-            return self.do_login(**request.params)
-        elif path.endswith('do_logout'):
-            if request.method != 'POST':
-                response.headers['Allow'] = 'POST'
-                raise cherrypy.HTTPError(405)
-            self._debug_message('routing %(path)r to do_logout', locals())
-            return self.do_logout(**request.params)
-        else:
-            self._debug_message('No special path, running do_check')
-            return self.do_check()
-
-
-def session_auth(**kwargs):
-    sa = SessionAuth()
-    for k, v in kwargs.items():
-        setattr(sa, k, v)
-    return sa.run()
-
-
-session_auth.__doc__ = (
-    """Session authentication hook.
-
-    Any attribute of the SessionAuth class may be overridden via a keyword arg
-    to this function:
-
-    """ + '\n'.join(['%s: %s' % (k, type(getattr(SessionAuth, k)).__name__)
-                     for k in dir(SessionAuth) if not k.startswith('__')])
-)
-
-
-def log_traceback(severity=logging.ERROR, debug=False):
-    """Write the last error's traceback to the cherrypy error log."""
-    cherrypy.log('', 'HTTP', severity=severity, traceback=True)
-
-
-def log_request_headers(debug=False):
-    """Write request headers to the cherrypy error log."""
-    h = ['  %s: %s' % (k, v) for k, v in cherrypy.serving.request.header_list]
-    cherrypy.log('\nRequest Headers:\n' + '\n'.join(h), 'HTTP')
-
-
-def log_hooks(debug=False):
-    """Write request.hooks to the cherrypy error log."""
-    request = cherrypy.serving.request
-
-    msg = []
-    # Sort by the standard points if possible.
-    from cherrypy import _cprequest
-    points = _cprequest.hookpoints
-    for k in request.hooks.keys():
-        if k not in points:
-            points.append(k)
-
-    for k in points:
-        msg.append('    %s:' % k)
-        v = request.hooks.get(k, [])
-        v.sort()
-        for h in v:
-            msg.append('        %r' % h)
-    cherrypy.log('\nRequest Hooks for ' + cherrypy.url() +
-                 ':\n' + '\n'.join(msg), 'HTTP')
-
-
-def redirect(url='', internal=True, debug=False):
-    """Raise InternalRedirect or HTTPRedirect to the given url."""
-    if debug:
-        cherrypy.log('Redirecting %sto: %s' %
-                     ({True: 'internal ', False: ''}[internal], url),
-                     'TOOLS.REDIRECT')
-    if internal:
-        raise cherrypy.InternalRedirect(url)
-    else:
-        raise cherrypy.HTTPRedirect(url)
-
-
-def trailing_slash(missing=True, extra=False, status=None, debug=False):
-    """Redirect if path_info has (missing|extra) trailing slash."""
-    request = cherrypy.serving.request
-    pi = request.path_info
-
-    if debug:
-        cherrypy.log('is_index: %r, missing: %r, extra: %r, path_info: %r' %
-                     (request.is_index, missing, extra, pi),
-                     'TOOLS.TRAILING_SLASH')
-    if request.is_index is True:
-        if missing:
-            if not pi.endswith('/'):
-                new_url = cherrypy.url(pi + '/', request.query_string)
-                raise cherrypy.HTTPRedirect(new_url, status=status or 301)
-    elif request.is_index is False:
-        if extra:
-            # If pi == '/', don't redirect to ''!
-            if pi.endswith('/') and pi != '/':
-                new_url = cherrypy.url(pi[:-1], request.query_string)
-                raise cherrypy.HTTPRedirect(new_url, status=status or 301)
-
-
-def flatten(debug=False):
-    """Wrap response.body in a generator that recursively iterates over body.
-
-    This allows cherrypy.response.body to consist of 'nested generators';
-    that is, a set of generators that yield generators.
-    """
-    def flattener(input):
-        numchunks = 0
-        for x in input:
-            if not is_iterator(x):
-                numchunks += 1
-                yield x
-            else:
-                for y in flattener(x):
-                    numchunks += 1
-                    yield y
-        if debug:
-            cherrypy.log('Flattened %d chunks' % numchunks, 'TOOLS.FLATTEN')
-    response = cherrypy.serving.response
-    response.body = flattener(response.body)
-
-
-def accept(media=None, debug=False):
-    """Return the client's preferred media-type (from the given Content-Types).
-
-    If 'media' is None (the default), no test will be performed.
-
-    If 'media' is provided, it should be the Content-Type value (as a string)
-    or values (as a list or tuple of strings) which the current resource
-    can emit. The client's acceptable media ranges (as declared in the
-    Accept request header) will be matched in order to these Content-Type
-    values; the first such string is returned. That is, the return value
-    will always be one of the strings provided in the 'media' arg (or None
-    if 'media' is None).
-
-    If no match is found, then HTTPError 406 (Not Acceptable) is raised.
-    Note that most web browsers send */* as a (low-quality) acceptable
-    media range, which should match any Content-Type. In addition, "...if
-    no Accept header field is present, then it is assumed that the client
-    accepts all media types."
-
-    Matching types are checked in order of client preference first,
-    and then in the order of the given 'media' values.
-
-    Note that this function does not honor accept-params (other than "q").
-    """
-    if not media:
-        return
-    if isinstance(media, text_or_bytes):
-        media = [media]
-    request = cherrypy.serving.request
-
-    # Parse the Accept request header, and try to match one
-    # of the requested media-ranges (in order of preference).
-    ranges = request.headers.elements('Accept')
-    if not ranges:
-        # Any media type is acceptable.
-        if debug:
-            cherrypy.log('No Accept header elements', 'TOOLS.ACCEPT')
-        return media[0]
-    else:
-        # Note that 'ranges' is sorted in order of preference
-        for element in ranges:
-            if element.qvalue > 0:
-                if element.value == '*/*':
-                    # Matches any type or subtype
-                    if debug:
-                        cherrypy.log('Match due to */*', 'TOOLS.ACCEPT')
-                    return media[0]
-                elif element.value.endswith('/*'):
-                    # Matches any subtype
-                    mtype = element.value[:-1]  # Keep the slash
-                    for m in media:
-                        if m.startswith(mtype):
-                            if debug:
-                                cherrypy.log('Match due to %s' % element.value,
-                                             'TOOLS.ACCEPT')
-                            return m
-                else:
-                    # Matches exact value
-                    if element.value in media:
-                        if debug:
-                            cherrypy.log('Match due to %s' % element.value,
-                                         'TOOLS.ACCEPT')
-                        return element.value
-
-    # No suitable media-range found.
-    ah = request.headers.get('Accept')
-    if ah is None:
-        msg = 'Your client did not send an Accept header.'
-    else:
-        msg = 'Your client sent this Accept header: %s.' % ah
-    msg += (' But this resource only emits these media types: %s.' %
-            ', '.join(media))
-    raise cherrypy.HTTPError(406, msg)
-
-
-class MonitoredHeaderMap(_httputil.HeaderMap):
-
-    def transform_key(self, key):
-        self.accessed_headers.add(key)
-        return super(MonitoredHeaderMap, self).transform_key(key)
-
-    def __init__(self):
-        self.accessed_headers = set()
-        super(MonitoredHeaderMap, self).__init__()
-
-
-def autovary(ignore=None, debug=False):
-    """Auto-populate the Vary response header based on request.header access.
-    """
-    request = cherrypy.serving.request
-
-    req_h = request.headers
-    request.headers = MonitoredHeaderMap()
-    request.headers.update(req_h)
-    if ignore is None:
-        ignore = set(['Content-Disposition', 'Content-Length', 'Content-Type'])
-
-    def set_response_header():
-        resp_h = cherrypy.serving.response.headers
-        v = set([e.value for e in resp_h.elements('Vary')])
-        if debug:
-            cherrypy.log(
-                'Accessed headers: %s' % request.headers.accessed_headers,
-                'TOOLS.AUTOVARY')
-        v = v.union(request.headers.accessed_headers)
-        v = v.difference(ignore)
-        v = list(v)
-        v.sort()
-        resp_h['Vary'] = ', '.join(v)
-    request.hooks.attach('before_finalize', set_response_header, 95)
-
-
-def convert_params(exception=ValueError, error=400):
-    """Convert request params based on function annotations, with error handling.
-
-    exception
-        Exception class to catch.
-
-    status
-        The HTTP error code to return to the client on failure.
-    """
-    request = cherrypy.serving.request
-    types = request.handler.callable.__annotations__
-    with cherrypy.HTTPError.handle(exception, error):
-        for key in set(types).intersection(request.params):
-            request.params[key] = types[key](request.params[key])
diff --git a/libraries/cherrypy/lib/encoding.py b/libraries/cherrypy/lib/encoding.py
deleted file mode 100644
index 3d001ca6..00000000
--- a/libraries/cherrypy/lib/encoding.py
+++ /dev/null
@@ -1,436 +0,0 @@
-import struct
-import time
-import io
-
-import six
-
-import cherrypy
-from cherrypy._cpcompat import text_or_bytes
-from cherrypy.lib import file_generator
-from cherrypy.lib import is_closable_iterator
-from cherrypy.lib import set_vary_header
-
-
-def decode(encoding=None, default_encoding='utf-8'):
-    """Replace or extend the list of charsets used to decode a request entity.
-
-    Either argument may be a single string or a list of strings.
-
-    encoding
-        If not None, restricts the set of charsets attempted while decoding
-        a request entity to the given set (even if a different charset is
-        given in the Content-Type request header).
-
-    default_encoding
-        Only in effect if the 'encoding' argument is not given.
-        If given, the set of charsets attempted while decoding a request
-        entity is *extended* with the given value(s).
-
-    """
-    body = cherrypy.request.body
-    if encoding is not None:
-        if not isinstance(encoding, list):
-            encoding = [encoding]
-        body.attempt_charsets = encoding
-    elif default_encoding:
-        if not isinstance(default_encoding, list):
-            default_encoding = [default_encoding]
-        body.attempt_charsets = body.attempt_charsets + default_encoding
-
-
-class UTF8StreamEncoder:
-    def __init__(self, iterator):
-        self._iterator = iterator
-
-    def __iter__(self):
-        return self
-
-    def next(self):
-        return self.__next__()
-
-    def __next__(self):
-        res = next(self._iterator)
-        if isinstance(res, six.text_type):
-            res = res.encode('utf-8')
-        return res
-
-    def close(self):
-        if is_closable_iterator(self._iterator):
-            self._iterator.close()
-
-    def __getattr__(self, attr):
-        if attr.startswith('__'):
-            raise AttributeError(self, attr)
-        return getattr(self._iterator, attr)
-
-
-class ResponseEncoder:
-
-    default_encoding = 'utf-8'
-    failmsg = 'Response body could not be encoded with %r.'
-    encoding = None
-    errors = 'strict'
-    text_only = True
-    add_charset = True
-    debug = False
-
-    def __init__(self, **kwargs):
-        for k, v in kwargs.items():
-            setattr(self, k, v)
-
-        self.attempted_charsets = set()
-        request = cherrypy.serving.request
-        if request.handler is not None:
-            # Replace request.handler with self
-            if self.debug:
-                cherrypy.log('Replacing request.handler', 'TOOLS.ENCODE')
-            self.oldhandler = request.handler
-            request.handler = self
-
-    def encode_stream(self, encoding):
-        """Encode a streaming response body.
-
-        Use a generator wrapper, and just pray it works as the stream is
-        being written out.
-        """
-        if encoding in self.attempted_charsets:
-            return False
-        self.attempted_charsets.add(encoding)
-
-        def encoder(body):
-            for chunk in body:
-                if isinstance(chunk, six.text_type):
-                    chunk = chunk.encode(encoding, self.errors)
-                yield chunk
-        self.body = encoder(self.body)
-        return True
-
-    def encode_string(self, encoding):
-        """Encode a buffered response body."""
-        if encoding in self.attempted_charsets:
-            return False
-        self.attempted_charsets.add(encoding)
-        body = []
-        for chunk in self.body:
-            if isinstance(chunk, six.text_type):
-                try:
-                    chunk = chunk.encode(encoding, self.errors)
-                except (LookupError, UnicodeError):
-                    return False
-            body.append(chunk)
-        self.body = body
-        return True
-
-    def find_acceptable_charset(self):
-        request = cherrypy.serving.request
-        response = cherrypy.serving.response
-
-        if self.debug:
-            cherrypy.log('response.stream %r' %
-                         response.stream, 'TOOLS.ENCODE')
-        if response.stream:
-            encoder = self.encode_stream
-        else:
-            encoder = self.encode_string
-            if 'Content-Length' in response.headers:
-                # Delete Content-Length header so finalize() recalcs it.
-                # Encoded strings may be of different lengths from their
-                # unicode equivalents, and even from each other. For example:
-                # >>> t = u"\u7007\u3040"
-                # >>> len(t)
-                # 2
-                # >>> len(t.encode("UTF-8"))
-                # 6
-                # >>> len(t.encode("utf7"))
-                # 8
-                del response.headers['Content-Length']
-
-        # Parse the Accept-Charset request header, and try to provide one
-        # of the requested charsets (in order of user preference).
-        encs = request.headers.elements('Accept-Charset')
-        charsets = [enc.value.lower() for enc in encs]
-        if self.debug:
-            cherrypy.log('charsets %s' % repr(charsets), 'TOOLS.ENCODE')
-
-        if self.encoding is not None:
-            # If specified, force this encoding to be used, or fail.
-            encoding = self.encoding.lower()
-            if self.debug:
-                cherrypy.log('Specified encoding %r' %
-                             encoding, 'TOOLS.ENCODE')
-            if (not charsets) or '*' in charsets or encoding in charsets:
-                if self.debug:
-                    cherrypy.log('Attempting encoding %r' %
-                                 encoding, 'TOOLS.ENCODE')
-                if encoder(encoding):
-                    return encoding
-        else:
-            if not encs:
-                if self.debug:
-                    cherrypy.log('Attempting default encoding %r' %
-                                 self.default_encoding, 'TOOLS.ENCODE')
-                # Any character-set is acceptable.
-                if encoder(self.default_encoding):
-                    return self.default_encoding
-                else:
-                    raise cherrypy.HTTPError(500, self.failmsg %
-                                             self.default_encoding)
-            else:
-                for element in encs:
-                    if element.qvalue > 0:
-                        if element.value == '*':
-                            # Matches any charset. Try our default.
-                            if self.debug:
-                                cherrypy.log('Attempting default encoding due '
-                                             'to %r' % element, 'TOOLS.ENCODE')
-                            if encoder(self.default_encoding):
-                                return self.default_encoding
-                        else:
-                            encoding = element.value
-                            if self.debug:
-                                cherrypy.log('Attempting encoding %s (qvalue >'
-                                             '0)' % element, 'TOOLS.ENCODE')
-                            if encoder(encoding):
-                                return encoding
-
-                if '*' not in charsets:
-                    # If no "*" is present in an Accept-Charset field, then all
-                    # character sets not explicitly mentioned get a quality
-                    # value of 0, except for ISO-8859-1, which gets a quality
-                    # value of 1 if not explicitly mentioned.
-                    iso = 'iso-8859-1'
-                    if iso not in charsets:
-                        if self.debug:
-                            cherrypy.log('Attempting ISO-8859-1 encoding',
-                                         'TOOLS.ENCODE')
-                        if encoder(iso):
-                            return iso
-
-        # No suitable encoding found.
-        ac = request.headers.get('Accept-Charset')
-        if ac is None:
-            msg = 'Your client did not send an Accept-Charset header.'
-        else:
-            msg = 'Your client sent this Accept-Charset header: %s.' % ac
-        _charsets = ', '.join(sorted(self.attempted_charsets))
-        msg += ' We tried these charsets: %s.' % (_charsets,)
-        raise cherrypy.HTTPError(406, msg)
-
-    def __call__(self, *args, **kwargs):
-        response = cherrypy.serving.response
-        self.body = self.oldhandler(*args, **kwargs)
-
-        self.body = prepare_iter(self.body)
-
-        ct = response.headers.elements('Content-Type')
-        if self.debug:
-            cherrypy.log('Content-Type: %r' % [str(h)
-                         for h in ct], 'TOOLS.ENCODE')
-        if ct and self.add_charset:
-            ct = ct[0]
-            if self.text_only:
-                if ct.value.lower().startswith('text/'):
-                    if self.debug:
-                        cherrypy.log(
-                            'Content-Type %s starts with "text/"' % ct,
-                            'TOOLS.ENCODE')
-                    do_find = True
-                else:
-                    if self.debug:
-                        cherrypy.log('Not finding because Content-Type %s '
-                                     'does not start with "text/"' % ct,
-                                     'TOOLS.ENCODE')
-                    do_find = False
-            else:
-                if self.debug:
-                    cherrypy.log('Finding because not text_only',
-                                 'TOOLS.ENCODE')
-                do_find = True
-
-            if do_find:
-                # Set "charset=..." param on response Content-Type header
-                ct.params['charset'] = self.find_acceptable_charset()
-                if self.debug:
-                    cherrypy.log('Setting Content-Type %s' % ct,
-                                 'TOOLS.ENCODE')
-                response.headers['Content-Type'] = str(ct)
-
-        return self.body
-
-
-def prepare_iter(value):
-    """
-    Ensure response body is iterable and resolves to False when empty.
-    """
-    if isinstance(value, text_or_bytes):
-        # strings get wrapped in a list because iterating over a single
-        # item list is much faster than iterating over every character
-        # in a long string.
-        if value:
-            value = [value]
-        else:
-            # [''] doesn't evaluate to False, so replace it with [].
-            value = []
-    # Don't use isinstance here; io.IOBase which has an ABC takes
-    # 1000 times as long as, say, isinstance(value, str)
-    elif hasattr(value, 'read'):
-        value = file_generator(value)
-    elif value is None:
-        value = []
-    return value
-
-
-# GZIP
-
-
-def compress(body, compress_level):
-    """Compress 'body' at the given compress_level."""
-    import zlib
-
-    # See http://www.gzip.org/zlib/rfc-gzip.html
-    yield b'\x1f\x8b'       # ID1 and ID2: gzip marker
-    yield b'\x08'           # CM: compression method
-    yield b'\x00'           # FLG: none set
-    # MTIME: 4 bytes
-    yield struct.pack('<L', int(time.time()) & int('FFFFFFFF', 16))
-    yield b'\x02'           # XFL: max compression, slowest algo
-    yield b'\xff'           # OS: unknown
-
-    crc = zlib.crc32(b'')
-    size = 0
-    zobj = zlib.compressobj(compress_level,
-                            zlib.DEFLATED, -zlib.MAX_WBITS,
-                            zlib.DEF_MEM_LEVEL, 0)
-    for line in body:
-        size += len(line)
-        crc = zlib.crc32(line, crc)
-        yield zobj.compress(line)
-    yield zobj.flush()
-
-    # CRC32: 4 bytes
-    yield struct.pack('<L', crc & int('FFFFFFFF', 16))
-    # ISIZE: 4 bytes
-    yield struct.pack('<L', size & int('FFFFFFFF', 16))
-
-
-def decompress(body):
-    import gzip
-
-    zbuf = io.BytesIO()
-    zbuf.write(body)
-    zbuf.seek(0)
-    zfile = gzip.GzipFile(mode='rb', fileobj=zbuf)
-    data = zfile.read()
-    zfile.close()
-    return data
-
-
-def gzip(compress_level=5, mime_types=['text/html', 'text/plain'],
-         debug=False):
-    """Try to gzip the response body if Content-Type in mime_types.
-
-    cherrypy.response.headers['Content-Type'] must be set to one of the
-    values in the mime_types arg before calling this function.
-
-    The provided list of mime-types must be of one of the following form:
-        * `type/subtype`
-        * `type/*`
-        * `type/*+subtype`
-
-    No compression is performed if any of the following hold:
-        * The client sends no Accept-Encoding request header
-        * No 'gzip' or 'x-gzip' is present in the Accept-Encoding header
-        * No 'gzip' or 'x-gzip' with a qvalue > 0 is present
-        * The 'identity' value is given with a qvalue > 0.
-
-    """
-    request = cherrypy.serving.request
-    response = cherrypy.serving.response
-
-    set_vary_header(response, 'Accept-Encoding')
-
-    if not response.body:
-        # Response body is empty (might be a 304 for instance)
-        if debug:
-            cherrypy.log('No response body', context='TOOLS.GZIP')
-        return
-
-    # If returning cached content (which should already have been gzipped),
-    # don't re-zip.
-    if getattr(request, 'cached', False):
-        if debug:
-            cherrypy.log('Not gzipping cached response', context='TOOLS.GZIP')
-        return
-
-    acceptable = request.headers.elements('Accept-Encoding')
-    if not acceptable:
-        # If no Accept-Encoding field is present in a request,
-        # the server MAY assume that the client will accept any
-        # content coding. In this case, if "identity" is one of
-        # the available content-codings, then the server SHOULD use
-        # the "identity" content-coding, unless it has additional
-        # information that a different content-coding is meaningful
-        # to the client.
-        if debug:
-            cherrypy.log('No Accept-Encoding', context='TOOLS.GZIP')
-        return
-
-    ct = response.headers.get('Content-Type', '').split(';')[0]
-    for coding in acceptable:
-        if coding.value == 'identity' and coding.qvalue != 0:
-            if debug:
-                cherrypy.log('Non-zero identity qvalue: %s' % coding,
-                             context='TOOLS.GZIP')
-            return
-        if coding.value in ('gzip', 'x-gzip'):
-            if coding.qvalue == 0:
-                if debug:
-                    cherrypy.log('Zero gzip qvalue: %s' % coding,
-                                 context='TOOLS.GZIP')
-                return
-
-            if ct not in mime_types:
-                # If the list of provided mime-types contains tokens
-                # such as 'text/*' or 'application/*+xml',
-                # we go through them and find the most appropriate one
-                # based on the given content-type.
-                # The pattern matching is only caring about the most
-                # common cases, as stated above, and doesn't support
-                # for extra parameters.
-                found = False
-                if '/' in ct:
-                    ct_media_type, ct_sub_type = ct.split('/')
-                    for mime_type in mime_types:
-                        if '/' in mime_type:
-                            media_type, sub_type = mime_type.split('/')
-                            if ct_media_type == media_type:
-                                if sub_type == '*':
-                                    found = True
-                                    break
-                                elif '+' in sub_type and '+' in ct_sub_type:
-                                    ct_left, ct_right = ct_sub_type.split('+')
-                                    left, right = sub_type.split('+')
-                                    if left == '*' and ct_right == right:
-                                        found = True
-                                        break
-
-                if not found:
-                    if debug:
-                        cherrypy.log('Content-Type %s not in mime_types %r' %
-                                     (ct, mime_types), context='TOOLS.GZIP')
-                    return
-
-            if debug:
-                cherrypy.log('Gzipping', context='TOOLS.GZIP')
-            # Return a generator that compresses the page
-            response.headers['Content-Encoding'] = 'gzip'
-            response.body = compress(response.body, compress_level)
-            if 'Content-Length' in response.headers:
-                # Delete Content-Length header so finalize() recalcs it.
-                del response.headers['Content-Length']
-
-            return
-
-    if debug:
-        cherrypy.log('No acceptable encoding found.', context='GZIP')
-    cherrypy.HTTPError(406, 'identity, gzip').set_response()
diff --git a/libraries/cherrypy/lib/gctools.py b/libraries/cherrypy/lib/gctools.py
deleted file mode 100644
index 26746d78..00000000
--- a/libraries/cherrypy/lib/gctools.py
+++ /dev/null
@@ -1,218 +0,0 @@
-import gc
-import inspect
-import sys
-import time
-
-try:
-    import objgraph
-except ImportError:
-    objgraph = None
-
-import cherrypy
-from cherrypy import _cprequest, _cpwsgi
-from cherrypy.process.plugins import SimplePlugin
-
-
-class ReferrerTree(object):
-
-    """An object which gathers all referrers of an object to a given depth."""
-
-    peek_length = 40
-
-    def __init__(self, ignore=None, maxdepth=2, maxparents=10):
-        self.ignore = ignore or []
-        self.ignore.append(inspect.currentframe().f_back)
-        self.maxdepth = maxdepth
-        self.maxparents = maxparents
-
-    def ascend(self, obj, depth=1):
-        """Return a nested list containing referrers of the given object."""
-        depth += 1
-        parents = []
-
-        # Gather all referrers in one step to minimize
-        # cascading references due to repr() logic.
-        refs = gc.get_referrers(obj)
-        self.ignore.append(refs)
-        if len(refs) > self.maxparents:
-            return [('[%s referrers]' % len(refs), [])]
-
-        try:
-            ascendcode = self.ascend.__code__
-        except AttributeError:
-            ascendcode = self.ascend.im_func.func_code
-        for parent in refs:
-            if inspect.isframe(parent) and parent.f_code is ascendcode:
-                continue
-            if parent in self.ignore:
-                continue
-            if depth <= self.maxdepth:
-                parents.append((parent, self.ascend(parent, depth)))
-            else:
-                parents.append((parent, []))
-
-        return parents
-
-    def peek(self, s):
-        """Return s, restricted to a sane length."""
-        if len(s) > (self.peek_length + 3):
-            half = self.peek_length // 2
-            return s[:half] + '...' + s[-half:]
-        else:
-            return s
-
-    def _format(self, obj, descend=True):
-        """Return a string representation of a single object."""
-        if inspect.isframe(obj):
-            filename, lineno, func, context, index = inspect.getframeinfo(obj)
-            return "<frame of function '%s'>" % func
-
-        if not descend:
-            return self.peek(repr(obj))
-
-        if isinstance(obj, dict):
-            return '{' + ', '.join(['%s: %s' % (self._format(k, descend=False),
-                                                self._format(v, descend=False))
-                                    for k, v in obj.items()]) + '}'
-        elif isinstance(obj, list):
-            return '[' + ', '.join([self._format(item, descend=False)
-                                    for item in obj]) + ']'
-        elif isinstance(obj, tuple):
-            return '(' + ', '.join([self._format(item, descend=False)
-                                    for item in obj]) + ')'
-
-        r = self.peek(repr(obj))
-        if isinstance(obj, (str, int, float)):
-            return r
-        return '%s: %s' % (type(obj), r)
-
-    def format(self, tree):
-        """Return a list of string reprs from a nested list of referrers."""
-        output = []
-
-        def ascend(branch, depth=1):
-            for parent, grandparents in branch:
-                output.append(('    ' * depth) + self._format(parent))
-                if grandparents:
-                    ascend(grandparents, depth + 1)
-        ascend(tree)
-        return output
-
-
-def get_instances(cls):
-    return [x for x in gc.get_objects() if isinstance(x, cls)]
-
-
-class RequestCounter(SimplePlugin):
-
-    def start(self):
-        self.count = 0
-
-    def before_request(self):
-        self.count += 1
-
-    def after_request(self):
-        self.count -= 1
-
-
-request_counter = RequestCounter(cherrypy.engine)
-request_counter.subscribe()
-
-
-def get_context(obj):
-    if isinstance(obj, _cprequest.Request):
-        return 'path=%s;stage=%s' % (obj.path_info, obj.stage)
-    elif isinstance(obj, _cprequest.Response):
-        return 'status=%s' % obj.status
-    elif isinstance(obj, _cpwsgi.AppResponse):
-        return 'PATH_INFO=%s' % obj.environ.get('PATH_INFO', '')
-    elif hasattr(obj, 'tb_lineno'):
-        return 'tb_lineno=%s' % obj.tb_lineno
-    return ''
-
-
-class GCRoot(object):
-
-    """A CherryPy page handler for testing reference leaks."""
-
-    classes = [
-        (_cprequest.Request, 2, 2,
-         'Should be 1 in this request thread and 1 in the main thread.'),
-        (_cprequest.Response, 2, 2,
-         'Should be 1 in this request thread and 1 in the main thread.'),
-        (_cpwsgi.AppResponse, 1, 1,
-         'Should be 1 in this request thread only.'),
-    ]
-
-    @cherrypy.expose
-    def index(self):
-        return 'Hello, world!'
-
-    @cherrypy.expose
-    def stats(self):
-        output = ['Statistics:']
-
-        for trial in range(10):
-            if request_counter.count > 0:
-                break
-            time.sleep(0.5)
-        else:
-            output.append('\nNot all requests closed properly.')
-
-        # gc_collect isn't perfectly synchronous, because it may
-        # break reference cycles that then take time to fully
-        # finalize. Call it thrice and hope for the best.
-        gc.collect()
-        gc.collect()
-        unreachable = gc.collect()
-        if unreachable:
-            if objgraph is not None:
-                final = objgraph.by_type('Nondestructible')
-                if final:
-                    objgraph.show_backrefs(final, filename='finalizers.png')
-
-            trash = {}
-            for x in gc.garbage:
-                trash[type(x)] = trash.get(type(x), 0) + 1
-            if trash:
-                output.insert(0, '\n%s unreachable objects:' % unreachable)
-                trash = [(v, k) for k, v in trash.items()]
-                trash.sort()
-                for pair in trash:
-                    output.append('    ' + repr(pair))
-
-        # Check declared classes to verify uncollected instances.
-        # These don't have to be part of a cycle; they can be
-        # any objects that have unanticipated referrers that keep
-        # them from being collected.
-        allobjs = {}
-        for cls, minobj, maxobj, msg in self.classes:
-            allobjs[cls] = get_instances(cls)
-
-        for cls, minobj, maxobj, msg in self.classes:
-            objs = allobjs[cls]
-            lenobj = len(objs)
-            if lenobj < minobj or lenobj > maxobj:
-                if minobj == maxobj:
-                    output.append(
-                        '\nExpected %s %r references, got %s.' %
-                        (minobj, cls, lenobj))
-                else:
-                    output.append(
-                        '\nExpected %s to %s %r references, got %s.' %
-                        (minobj, maxobj, cls, lenobj))
-
-                for obj in objs:
-                    if objgraph is not None:
-                        ig = [id(objs), id(inspect.currentframe())]
-                        fname = 'graph_%s_%s.png' % (cls.__name__, id(obj))
-                        objgraph.show_backrefs(
-                            obj, extra_ignore=ig, max_depth=4, too_many=20,
-                            filename=fname, extra_info=get_context)
-                    output.append('\nReferrers for %s (refcount=%s):' %
-                                  (repr(obj), sys.getrefcount(obj)))
-                    t = ReferrerTree(ignore=[objs], maxdepth=3)
-                    tree = t.ascend(obj)
-                    output.extend(t.format(tree))
-
-        return '\n'.join(output)
diff --git a/libraries/cherrypy/lib/httputil.py b/libraries/cherrypy/lib/httputil.py
deleted file mode 100644
index b68d8dd5..00000000
--- a/libraries/cherrypy/lib/httputil.py
+++ /dev/null
@@ -1,581 +0,0 @@
-"""HTTP library functions.
-
-This module contains functions for building an HTTP application
-framework: any one, not just one whose name starts with "Ch". ;) If you
-reference any modules from some popular framework inside *this* module,
-FuManChu will personally hang you up by your thumbs and submit you
-to a public caning.
-"""
-
-import functools
-import email.utils
-import re
-from binascii import b2a_base64
-from cgi import parse_header
-from email.header import decode_header
-
-import six
-from six.moves import range, builtins, map
-from six.moves.BaseHTTPServer import BaseHTTPRequestHandler
-
-import cherrypy
-from cherrypy._cpcompat import ntob, ntou
-from cherrypy._cpcompat import unquote_plus
-
-response_codes = BaseHTTPRequestHandler.responses.copy()
-
-# From https://github.com/cherrypy/cherrypy/issues/361
-response_codes[500] = ('Internal Server Error',
-                       'The server encountered an unexpected condition '
-                       'which prevented it from fulfilling the request.')
-response_codes[503] = ('Service Unavailable',
-                       'The server is currently unable to handle the '
-                       'request due to a temporary overloading or '
-                       'maintenance of the server.')
-
-
-HTTPDate = functools.partial(email.utils.formatdate, usegmt=True)
-
-
-def urljoin(*atoms):
-    r"""Return the given path \*atoms, joined into a single URL.
-
-    This will correctly join a SCRIPT_NAME and PATH_INFO into the
-    original URL, even if either atom is blank.
-    """
-    url = '/'.join([x for x in atoms if x])
-    while '//' in url:
-        url = url.replace('//', '/')
-    # Special-case the final url of "", and return "/" instead.
-    return url or '/'
-
-
-def urljoin_bytes(*atoms):
-    """Return the given path `*atoms`, joined into a single URL.
-
-    This will correctly join a SCRIPT_NAME and PATH_INFO into the
-    original URL, even if either atom is blank.
-    """
-    url = b'/'.join([x for x in atoms if x])
-    while b'//' in url:
-        url = url.replace(b'//', b'/')
-    # Special-case the final url of "", and return "/" instead.
-    return url or b'/'
-
-
-def protocol_from_http(protocol_str):
-    """Return a protocol tuple from the given 'HTTP/x.y' string."""
-    return int(protocol_str[5]), int(protocol_str[7])
-
-
-def get_ranges(headervalue, content_length):
-    """Return a list of (start, stop) indices from a Range header, or None.
-
-    Each (start, stop) tuple will be composed of two ints, which are suitable
-    for use in a slicing operation. That is, the header "Range: bytes=3-6",
-    if applied against a Python string, is requesting resource[3:7]. This
-    function will return the list [(3, 7)].
-
-    If this function returns an empty list, you should return HTTP 416.
-    """
-
-    if not headervalue:
-        return None
-
-    result = []
-    bytesunit, byteranges = headervalue.split('=', 1)
-    for brange in byteranges.split(','):
-        start, stop = [x.strip() for x in brange.split('-', 1)]
-        if start:
-            if not stop:
-                stop = content_length - 1
-            start, stop = int(start), int(stop)
-            if start >= content_length:
-                # From rfc 2616 sec 14.16:
-                # "If the server receives a request (other than one
-                # including an If-Range request-header field) with an
-                # unsatisfiable Range request-header field (that is,
-                # all of whose byte-range-spec values have a first-byte-pos
-                # value greater than the current length of the selected
-                # resource), it SHOULD return a response code of 416
-                # (Requested range not satisfiable)."
-                continue
-            if stop < start:
-                # From rfc 2616 sec 14.16:
-                # "If the server ignores a byte-range-spec because it
-                # is syntactically invalid, the server SHOULD treat
-                # the request as if the invalid Range header field
-                # did not exist. (Normally, this means return a 200
-                # response containing the full entity)."
-                return None
-            result.append((start, stop + 1))
-        else:
-            if not stop:
-                # See rfc quote above.
-                return None
-            # Negative subscript (last N bytes)
-            #
-            # RFC 2616 Section 14.35.1:
-            #   If the entity is shorter than the specified suffix-length,
-            #   the entire entity-body is used.
-            if int(stop) > content_length:
-                result.append((0, content_length))
-            else:
-                result.append((content_length - int(stop), content_length))
-
-    return result
-
-
-class HeaderElement(object):
-
-    """An element (with parameters) from an HTTP header's element list."""
-
-    def __init__(self, value, params=None):
-        self.value = value
-        if params is None:
-            params = {}
-        self.params = params
-
-    def __cmp__(self, other):
-        return builtins.cmp(self.value, other.value)
-
-    def __lt__(self, other):
-        return self.value < other.value
-
-    def __str__(self):
-        p = [';%s=%s' % (k, v) for k, v in six.iteritems(self.params)]
-        return str('%s%s' % (self.value, ''.join(p)))
-
-    def __bytes__(self):
-        return ntob(self.__str__())
-
-    def __unicode__(self):
-        return ntou(self.__str__())
-
-    @staticmethod
-    def parse(elementstr):
-        """Transform 'token;key=val' to ('token', {'key': 'val'})."""
-        initial_value, params = parse_header(elementstr)
-        return initial_value, params
-
-    @classmethod
-    def from_str(cls, elementstr):
-        """Construct an instance from a string of the form 'token;key=val'."""
-        ival, params = cls.parse(elementstr)
-        return cls(ival, params)
-
-
-q_separator = re.compile(r'; *q *=')
-
-
-class AcceptElement(HeaderElement):
-
-    """An element (with parameters) from an Accept* header's element list.
-
-    AcceptElement objects are comparable; the more-preferred object will be
-    "less than" the less-preferred object. They are also therefore sortable;
-    if you sort a list of AcceptElement objects, they will be listed in
-    priority order; the most preferred value will be first. Yes, it should
-    have been the other way around, but it's too late to fix now.
-    """
-
-    @classmethod
-    def from_str(cls, elementstr):
-        qvalue = None
-        # The first "q" parameter (if any) separates the initial
-        # media-range parameter(s) (if any) from the accept-params.
-        atoms = q_separator.split(elementstr, 1)
-        media_range = atoms.pop(0).strip()
-        if atoms:
-            # The qvalue for an Accept header can have extensions. The other
-            # headers cannot, but it's easier to parse them as if they did.
-            qvalue = HeaderElement.from_str(atoms[0].strip())
-
-        media_type, params = cls.parse(media_range)
-        if qvalue is not None:
-            params['q'] = qvalue
-        return cls(media_type, params)
-
-    @property
-    def qvalue(self):
-        'The qvalue, or priority, of this value.'
-        val = self.params.get('q', '1')
-        if isinstance(val, HeaderElement):
-            val = val.value
-        try:
-            return float(val)
-        except ValueError as val_err:
-            """Fail client requests with invalid quality value.
-
-            Ref: https://github.com/cherrypy/cherrypy/issues/1370
-            """
-            six.raise_from(
-                cherrypy.HTTPError(
-                    400,
-                    'Malformed HTTP header: `{}`'.
-                    format(str(self)),
-                ),
-                val_err,
-            )
-
-    def __cmp__(self, other):
-        diff = builtins.cmp(self.qvalue, other.qvalue)
-        if diff == 0:
-            diff = builtins.cmp(str(self), str(other))
-        return diff
-
-    def __lt__(self, other):
-        if self.qvalue == other.qvalue:
-            return str(self) < str(other)
-        else:
-            return self.qvalue < other.qvalue
-
-
-RE_HEADER_SPLIT = re.compile(',(?=(?:[^"]*"[^"]*")*[^"]*$)')
-
-
-def header_elements(fieldname, fieldvalue):
-    """Return a sorted HeaderElement list from a comma-separated header string.
-    """
-    if not fieldvalue:
-        return []
-
-    result = []
-    for element in RE_HEADER_SPLIT.split(fieldvalue):
-        if fieldname.startswith('Accept') or fieldname == 'TE':
-            hv = AcceptElement.from_str(element)
-        else:
-            hv = HeaderElement.from_str(element)
-        result.append(hv)
-
-    return list(reversed(sorted(result)))
-
-
-def decode_TEXT(value):
-    r"""
-    Decode :rfc:`2047` TEXT
-
-    >>> decode_TEXT("=?utf-8?q?f=C3=BCr?=") == b'f\xfcr'.decode('latin-1')
-    True
-    """
-    atoms = decode_header(value)
-    decodedvalue = ''
-    for atom, charset in atoms:
-        if charset is not None:
-            atom = atom.decode(charset)
-        decodedvalue += atom
-    return decodedvalue
-
-
-def decode_TEXT_maybe(value):
-    """
-    Decode the text but only if '=?' appears in it.
-    """
-    return decode_TEXT(value) if '=?' in value else value
-
-
-def valid_status(status):
-    """Return legal HTTP status Code, Reason-phrase and Message.
-
-    The status arg must be an int, a str that begins with an int
-    or the constant from ``http.client`` stdlib module.
-
-    If status has no reason-phrase is supplied, a default reason-
-    phrase will be provided.
-
-    >>> from six.moves import http_client
-    >>> from six.moves.BaseHTTPServer import BaseHTTPRequestHandler
-    >>> valid_status(http_client.ACCEPTED) == (
-    ...     int(http_client.ACCEPTED),
-    ... ) + BaseHTTPRequestHandler.responses[http_client.ACCEPTED]
-    True
-    """
-
-    if not status:
-        status = 200
-
-    code, reason = status, None
-    if isinstance(status, six.string_types):
-        code, _, reason = status.partition(' ')
-        reason = reason.strip() or None
-
-    try:
-        code = int(code)
-    except (TypeError, ValueError):
-        raise ValueError('Illegal response status from server '
-                         '(%s is non-numeric).' % repr(code))
-
-    if code < 100 or code > 599:
-        raise ValueError('Illegal response status from server '
-                         '(%s is out of range).' % repr(code))
-
-    if code not in response_codes:
-        # code is unknown but not illegal
-        default_reason, message = '', ''
-    else:
-        default_reason, message = response_codes[code]
-
-    if reason is None:
-        reason = default_reason
-
-    return code, reason, message
-
-
-# NOTE: the parse_qs functions that follow are modified version of those
-# in the python3.0 source - we need to pass through an encoding to the unquote
-# method, but the default parse_qs function doesn't allow us to.  These do.
-
-def _parse_qs(qs, keep_blank_values=0, strict_parsing=0, encoding='utf-8'):
-    """Parse a query given as a string argument.
-
-    Arguments:
-
-    qs: URL-encoded query string to be parsed
-
-    keep_blank_values: flag indicating whether blank values in
-        URL encoded queries should be treated as blank strings.  A
-        true value indicates that blanks should be retained as blank
-        strings.  The default false value indicates that blank values
-        are to be ignored and treated as if they were  not included.
-
-    strict_parsing: flag indicating what to do with parsing errors. If
-        false (the default), errors are silently ignored. If true,
-        errors raise a ValueError exception.
-
-    Returns a dict, as G-d intended.
-    """
-    pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
-    d = {}
-    for name_value in pairs:
-        if not name_value and not strict_parsing:
-            continue
-        nv = name_value.split('=', 1)
-        if len(nv) != 2:
-            if strict_parsing:
-                raise ValueError('bad query field: %r' % (name_value,))
-            # Handle case of a control-name with no equal sign
-            if keep_blank_values:
-                nv.append('')
-            else:
-                continue
-        if len(nv[1]) or keep_blank_values:
-            name = unquote_plus(nv[0], encoding, errors='strict')
-            value = unquote_plus(nv[1], encoding, errors='strict')
-            if name in d:
-                if not isinstance(d[name], list):
-                    d[name] = [d[name]]
-                d[name].append(value)
-            else:
-                d[name] = value
-    return d
-
-
-image_map_pattern = re.compile(r'[0-9]+,[0-9]+')
-
-
-def parse_query_string(query_string, keep_blank_values=True, encoding='utf-8'):
-    """Build a params dictionary from a query_string.
-
-    Duplicate key/value pairs in the provided query_string will be
-    returned as {'key': [val1, val2, ...]}. Single key/values will
-    be returned as strings: {'key': 'value'}.
-    """
-    if image_map_pattern.match(query_string):
-        # Server-side image map. Map the coords to 'x' and 'y'
-        # (like CGI::Request does).
-        pm = query_string.split(',')
-        pm = {'x': int(pm[0]), 'y': int(pm[1])}
-    else:
-        pm = _parse_qs(query_string, keep_blank_values, encoding=encoding)
-    return pm
-
-
-####
-# Inlined from jaraco.collections 1.5.2
-# Ref #1673
-class KeyTransformingDict(dict):
-    """
-    A dict subclass that transforms the keys before they're used.
-    Subclasses may override the default transform_key to customize behavior.
-    """
-    @staticmethod
-    def transform_key(key):
-        return key
-
-    def __init__(self, *args, **kargs):
-        super(KeyTransformingDict, self).__init__()
-        # build a dictionary using the default constructs
-        d = dict(*args, **kargs)
-        # build this dictionary using transformed keys.
-        for item in d.items():
-            self.__setitem__(*item)
-
-    def __setitem__(self, key, val):
-        key = self.transform_key(key)
-        super(KeyTransformingDict, self).__setitem__(key, val)
-
-    def __getitem__(self, key):
-        key = self.transform_key(key)
-        return super(KeyTransformingDict, self).__getitem__(key)
-
-    def __contains__(self, key):
-        key = self.transform_key(key)
-        return super(KeyTransformingDict, self).__contains__(key)
-
-    def __delitem__(self, key):
-        key = self.transform_key(key)
-        return super(KeyTransformingDict, self).__delitem__(key)
-
-    def get(self, key, *args, **kwargs):
-        key = self.transform_key(key)
-        return super(KeyTransformingDict, self).get(key, *args, **kwargs)
-
-    def setdefault(self, key, *args, **kwargs):
-        key = self.transform_key(key)
-        return super(KeyTransformingDict, self).setdefault(
-            key, *args, **kwargs)
-
-    def pop(self, key, *args, **kwargs):
-        key = self.transform_key(key)
-        return super(KeyTransformingDict, self).pop(key, *args, **kwargs)
-
-    def matching_key_for(self, key):
-        """
-        Given a key, return the actual key stored in self that matches.
-        Raise KeyError if the key isn't found.
-        """
-        try:
-            return next(e_key for e_key in self.keys() if e_key == key)
-        except StopIteration:
-            raise KeyError(key)
-####
-
-
-class CaseInsensitiveDict(KeyTransformingDict):
-
-    """A case-insensitive dict subclass.
-
-    Each key is changed on entry to str(key).title().
-    """
-
-    @staticmethod
-    def transform_key(key):
-        return str(key).title()
-
-
-#   TEXT = <any OCTET except CTLs, but including LWS>
-#
-# A CRLF is allowed in the definition of TEXT only as part of a header
-# field continuation. It is expected that the folding LWS will be
-# replaced with a single SP before interpretation of the TEXT value."
-if str == bytes:
-    header_translate_table = ''.join([chr(i) for i in range(256)])
-    header_translate_deletechars = ''.join(
-        [chr(i) for i in range(32)]) + chr(127)
-else:
-    header_translate_table = None
-    header_translate_deletechars = bytes(range(32)) + bytes([127])
-
-
-class HeaderMap(CaseInsensitiveDict):
-
-    """A dict subclass for HTTP request and response headers.
-
-    Each key is changed on entry to str(key).title(). This allows headers
-    to be case-insensitive and avoid duplicates.
-
-    Values are header values (decoded according to :rfc:`2047` if necessary).
-    """
-
-    protocol = (1, 1)
-    encodings = ['ISO-8859-1']
-
-    # Someday, when http-bis is done, this will probably get dropped
-    # since few servers, clients, or intermediaries do it. But until then,
-    # we're going to obey the spec as is.
-    # "Words of *TEXT MAY contain characters from character sets other than
-    # ISO-8859-1 only when encoded according to the rules of RFC 2047."
-    use_rfc_2047 = True
-
-    def elements(self, key):
-        """Return a sorted list of HeaderElements for the given header."""
-        key = str(key).title()
-        value = self.get(key)
-        return header_elements(key, value)
-
-    def values(self, key):
-        """Return a sorted list of HeaderElement.value for the given header."""
-        return [e.value for e in self.elements(key)]
-
-    def output(self):
-        """Transform self into a list of (name, value) tuples."""
-        return list(self.encode_header_items(self.items()))
-
-    @classmethod
-    def encode_header_items(cls, header_items):
-        """
-        Prepare the sequence of name, value tuples into a form suitable for
-        transmitting on the wire for HTTP.
-        """
-        for k, v in header_items:
-            if not isinstance(v, six.string_types):
-                v = six.text_type(v)
-
-            yield tuple(map(cls.encode_header_item, (k, v)))
-
-    @classmethod
-    def encode_header_item(cls, item):
-        if isinstance(item, six.text_type):
-            item = cls.encode(item)
-
-        # See header_translate_* constants above.
-        # Replace only if you really know what you're doing.
-        return item.translate(
-            header_translate_table, header_translate_deletechars)
-
-    @classmethod
-    def encode(cls, v):
-        """Return the given header name or value, encoded for HTTP output."""
-        for enc in cls.encodings:
-            try:
-                return v.encode(enc)
-            except UnicodeEncodeError:
-                continue
-
-        if cls.protocol == (1, 1) and cls.use_rfc_2047:
-            # Encode RFC-2047 TEXT
-            # (e.g. u"\u8200" -> "=?utf-8?b?6IiA?=").
-            # We do our own here instead of using the email module
-            # because we never want to fold lines--folding has
-            # been deprecated by the HTTP working group.
-            v = b2a_base64(v.encode('utf-8'))
-            return (b'=?utf-8?b?' + v.strip(b'\n') + b'?=')
-
-        raise ValueError('Could not encode header part %r using '
-                         'any of the encodings %r.' %
-                         (v, cls.encodings))
-
-
-class Host(object):
-
-    """An internet address.
-
-    name
-        Should be the client's host name. If not available (because no DNS
-        lookup is performed), the IP address should be used instead.
-
-    """
-
-    ip = '0.0.0.0'
-    port = 80
-    name = 'unknown.tld'
-
-    def __init__(self, ip, port, name=None):
-        self.ip = ip
-        self.port = port
-        if name is None:
-            name = ip
-        self.name = name
-
-    def __repr__(self):
-        return 'httputil.Host(%r, %r, %r)' % (self.ip, self.port, self.name)
diff --git a/libraries/cherrypy/lib/jsontools.py b/libraries/cherrypy/lib/jsontools.py
deleted file mode 100644
index 48683097..00000000
--- a/libraries/cherrypy/lib/jsontools.py
+++ /dev/null
@@ -1,88 +0,0 @@
-import cherrypy
-from cherrypy._cpcompat import text_or_bytes, ntou, json_encode, json_decode
-
-
-def json_processor(entity):
-    """Read application/json data into request.json."""
-    if not entity.headers.get(ntou('Content-Length'), ntou('')):
-        raise cherrypy.HTTPError(411)
-
-    body = entity.fp.read()
-    with cherrypy.HTTPError.handle(ValueError, 400, 'Invalid JSON document'):
-        cherrypy.serving.request.json = json_decode(body.decode('utf-8'))
-
-
-def json_in(content_type=[ntou('application/json'), ntou('text/javascript')],
-            force=True, debug=False, processor=json_processor):
-    """Add a processor to parse JSON request entities:
-    The default processor places the parsed data into request.json.
-
-    Incoming request entities which match the given content_type(s) will
-    be deserialized from JSON to the Python equivalent, and the result
-    stored at cherrypy.request.json. The 'content_type' argument may
-    be a Content-Type string or a list of allowable Content-Type strings.
-
-    If the 'force' argument is True (the default), then entities of other
-    content types will not be allowed; "415 Unsupported Media Type" is
-    raised instead.
-
-    Supply your own processor to use a custom decoder, or to handle the parsed
-    data differently.  The processor can be configured via
-    tools.json_in.processor or via the decorator method.
-
-    Note that the deserializer requires the client send a Content-Length
-    request header, or it will raise "411 Length Required". If for any
-    other reason the request entity cannot be deserialized from JSON,
-    it will raise "400 Bad Request: Invalid JSON document".
-    """
-    request = cherrypy.serving.request
-    if isinstance(content_type, text_or_bytes):
-        content_type = [content_type]
-
-    if force:
-        if debug:
-            cherrypy.log('Removing body processors %s' %
-                         repr(request.body.processors.keys()), 'TOOLS.JSON_IN')
-        request.body.processors.clear()
-        request.body.default_proc = cherrypy.HTTPError(
-            415, 'Expected an entity of content type %s' %
-            ', '.join(content_type))
-
-    for ct in content_type:
-        if debug:
-            cherrypy.log('Adding body processor for %s' % ct, 'TOOLS.JSON_IN')
-        request.body.processors[ct] = processor
-
-
-def json_handler(*args, **kwargs):
-    value = cherrypy.serving.request._json_inner_handler(*args, **kwargs)
-    return json_encode(value)
-
-
-def json_out(content_type='application/json', debug=False,
-             handler=json_handler):
-    """Wrap request.handler to serialize its output to JSON. Sets Content-Type.
-
-    If the given content_type is None, the Content-Type response header
-    is not set.
-
-    Provide your own handler to use a custom encoder.  For example
-    cherrypy.config['tools.json_out.handler'] = <function>, or
-    @json_out(handler=function).
-    """
-    request = cherrypy.serving.request
-    # request.handler may be set to None by e.g. the caching tool
-    # to signal to all components that a response body has already
-    # been attached, in which case we don't need to wrap anything.
-    if request.handler is None:
-        return
-    if debug:
-        cherrypy.log('Replacing %s with JSON handler' % request.handler,
-                     'TOOLS.JSON_OUT')
-    request._json_inner_handler = request.handler
-    request.handler = handler
-    if content_type is not None:
-        if debug:
-            cherrypy.log('Setting Content-Type to %s' %
-                         content_type, 'TOOLS.JSON_OUT')
-        cherrypy.serving.response.headers['Content-Type'] = content_type
diff --git a/libraries/cherrypy/lib/locking.py b/libraries/cherrypy/lib/locking.py
deleted file mode 100644
index 317fb58c..00000000
--- a/libraries/cherrypy/lib/locking.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import datetime
-
-
-class NeverExpires(object):
-    def expired(self):
-        return False
-
-
-class Timer(object):
-    """
-    A simple timer that will indicate when an expiration time has passed.
-    """
-    def __init__(self, expiration):
-        'Create a timer that expires at `expiration` (UTC datetime)'
-        self.expiration = expiration
-
-    @classmethod
-    def after(cls, elapsed):
-        """
-        Return a timer that will expire after `elapsed` passes.
-        """
-        return cls(datetime.datetime.utcnow() + elapsed)
-
-    def expired(self):
-        return datetime.datetime.utcnow() >= self.expiration
-
-
-class LockTimeout(Exception):
-    'An exception when a lock could not be acquired before a timeout period'
-
-
-class LockChecker(object):
-    """
-    Keep track of the time and detect if a timeout has expired
-    """
-    def __init__(self, session_id, timeout):
-        self.session_id = session_id
-        if timeout:
-            self.timer = Timer.after(timeout)
-        else:
-            self.timer = NeverExpires()
-
-    def expired(self):
-        if self.timer.expired():
-            raise LockTimeout(
-                'Timeout acquiring lock for %(session_id)s' % vars(self))
-        return False
diff --git a/libraries/cherrypy/lib/profiler.py b/libraries/cherrypy/lib/profiler.py
deleted file mode 100644
index fccf2eb8..00000000
--- a/libraries/cherrypy/lib/profiler.py
+++ /dev/null
@@ -1,221 +0,0 @@
-"""Profiler tools for CherryPy.
-
-CherryPy users
-==============
-
-You can profile any of your pages as follows::
-
-    from cherrypy.lib import profiler
-
-    class Root:
-        p = profiler.Profiler("/path/to/profile/dir")
-
-        @cherrypy.expose
-        def index(self):
-            self.p.run(self._index)
-
-        def _index(self):
-            return "Hello, world!"
-
-    cherrypy.tree.mount(Root())
-
-You can also turn on profiling for all requests
-using the ``make_app`` function as WSGI middleware.
-
-CherryPy developers
-===================
-
-This module can be used whenever you make changes to CherryPy,
-to get a quick sanity-check on overall CP performance. Use the
-``--profile`` flag when running the test suite. Then, use the ``serve()``
-function to browse the results in a web browser. If you run this
-module from the command line, it will call ``serve()`` for you.
-
-"""
-
-import io
-import os
-import os.path
-import sys
-import warnings
-
-import cherrypy
-
-
-try:
-    import profile
-    import pstats
-
-    def new_func_strip_path(func_name):
-        """Make profiler output more readable by adding `__init__` modules' parents
-        """
-        filename, line, name = func_name
-        if filename.endswith('__init__.py'):
-            return (
-                os.path.basename(filename[:-12]) + filename[-12:],
-                line,
-                name,
-            )
-        return os.path.basename(filename), line, name
-
-    pstats.func_strip_path = new_func_strip_path
-except ImportError:
-    profile = None
-    pstats = None
-
-
-_count = 0
-
-
-class Profiler(object):
-
-    def __init__(self, path=None):
-        if not path:
-            path = os.path.join(os.path.dirname(__file__), 'profile')
-        self.path = path
-        if not os.path.exists(path):
-            os.makedirs(path)
-
-    def run(self, func, *args, **params):
-        """Dump profile data into self.path."""
-        global _count
-        c = _count = _count + 1
-        path = os.path.join(self.path, 'cp_%04d.prof' % c)
-        prof = profile.Profile()
-        result = prof.runcall(func, *args, **params)
-        prof.dump_stats(path)
-        return result
-
-    def statfiles(self):
-        """:rtype: list of available profiles.
-        """
-        return [f for f in os.listdir(self.path)
-                if f.startswith('cp_') and f.endswith('.prof')]
-
-    def stats(self, filename, sortby='cumulative'):
-        """:rtype stats(index): output of print_stats() for the given profile.
-        """
-        sio = io.StringIO()
-        if sys.version_info >= (2, 5):
-            s = pstats.Stats(os.path.join(self.path, filename), stream=sio)
-            s.strip_dirs()
-            s.sort_stats(sortby)
-            s.print_stats()
-        else:
-            # pstats.Stats before Python 2.5 didn't take a 'stream' arg,
-            # but just printed to stdout. So re-route stdout.
-            s = pstats.Stats(os.path.join(self.path, filename))
-            s.strip_dirs()
-            s.sort_stats(sortby)
-            oldout = sys.stdout
-            try:
-                sys.stdout = sio
-                s.print_stats()
-            finally:
-                sys.stdout = oldout
-        response = sio.getvalue()
-        sio.close()
-        return response
-
-    @cherrypy.expose
-    def index(self):
-        return """<html>
-        <head><title>CherryPy profile data</title></head>
-        <frameset cols='200, 1*'>
-            <frame src='menu' />
-            <frame name='main' src='' />
-        </frameset>
-        </html>
-        """
-
-    @cherrypy.expose
-    def menu(self):
-        yield '<h2>Profiling runs</h2>'
-        yield '<p>Click on one of the runs below to see profiling data.</p>'
-        runs = self.statfiles()
-        runs.sort()
-        for i in runs:
-            yield "<a href='report?filename=%s' target='main'>%s</a><br />" % (
-                i, i)
-
-    @cherrypy.expose
-    def report(self, filename):
-        cherrypy.response.headers['Content-Type'] = 'text/plain'
-        return self.stats(filename)
-
-
-class ProfileAggregator(Profiler):
-
-    def __init__(self, path=None):
-        Profiler.__init__(self, path)
-        global _count
-        self.count = _count = _count + 1
-        self.profiler = profile.Profile()
-
-    def run(self, func, *args, **params):
-        path = os.path.join(self.path, 'cp_%04d.prof' % self.count)
-        result = self.profiler.runcall(func, *args, **params)
-        self.profiler.dump_stats(path)
-        return result
-
-
-class make_app:
-
-    def __init__(self, nextapp, path=None, aggregate=False):
-        """Make a WSGI middleware app which wraps 'nextapp' with profiling.
-
-        nextapp
-            the WSGI application to wrap, usually an instance of
-            cherrypy.Application.
-
-        path
-            where to dump the profiling output.
-
-        aggregate
-            if True, profile data for all HTTP requests will go in
-            a single file. If False (the default), each HTTP request will
-            dump its profile data into a separate file.
-
-        """
-        if profile is None or pstats is None:
-            msg = ('Your installation of Python does not have a profile '
-                   "module. If you're on Debian, try "
-                   '`sudo apt-get install python-profiler`. '
-                   'See http://www.cherrypy.org/wiki/ProfilingOnDebian '
-                   'for details.')
-            warnings.warn(msg)
-
-        self.nextapp = nextapp
-        self.aggregate = aggregate
-        if aggregate:
-            self.profiler = ProfileAggregator(path)
-        else:
-            self.profiler = Profiler(path)
-
-    def __call__(self, environ, start_response):
-        def gather():
-            result = []
-            for line in self.nextapp(environ, start_response):
-                result.append(line)
-            return result
-        return self.profiler.run(gather)
-
-
-def serve(path=None, port=8080):
-    if profile is None or pstats is None:
-        msg = ('Your installation of Python does not have a profile module. '
-               "If you're on Debian, try "
-               '`sudo apt-get install python-profiler`. '
-               'See http://www.cherrypy.org/wiki/ProfilingOnDebian '
-               'for details.')
-        warnings.warn(msg)
-
-    cherrypy.config.update({'server.socket_port': int(port),
-                            'server.thread_pool': 10,
-                            'environment': 'production',
-                            })
-    cherrypy.quickstart(Profiler(path))
-
-
-if __name__ == '__main__':
-    serve(*tuple(sys.argv[1:]))
diff --git a/libraries/cherrypy/lib/reprconf.py b/libraries/cherrypy/lib/reprconf.py
deleted file mode 100644
index 291ab663..00000000
--- a/libraries/cherrypy/lib/reprconf.py
+++ /dev/null
@@ -1,514 +0,0 @@
-"""Generic configuration system using unrepr.
-
-Configuration data may be supplied as a Python dictionary, as a filename,
-or as an open file object. When you supply a filename or file, Python's
-builtin ConfigParser is used (with some extensions).
-
-Namespaces
-----------
-
-Configuration keys are separated into namespaces by the first "." in the key.
-
-The only key that cannot exist in a namespace is the "environment" entry.
-This special entry 'imports' other config entries from a template stored in
-the Config.environments dict.
-
-You can define your own namespaces to be called when new config is merged
-by adding a named handler to Config.namespaces. The name can be any string,
-and the handler must be either a callable or a context manager.
-"""
-
-from cherrypy._cpcompat import text_or_bytes
-from six.moves import configparser
-from six.moves import builtins
-
-import operator
-import sys
-
-
-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)
-        """
-        # Separate the given config into namespaces
-        ns_confs = {}
-        for k in config:
-            if '.' in k:
-                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 six.iteritems(self):
-        #     with handler as callable:
-        #         for k, v in six.iteritems(ns_confs.get(ns, {})):
-        #             callable(k, v)
-        for ns, handler in self.items():
-            exit = getattr(handler, '__exit__', None)
-            if exit:
-                callable = handler.__enter__()
-                no_exc = True
-                try:
-                    try:
-                        for k, v in ns_confs.get(ns, {}).items():
-                            callable(k, v)
-                    except Exception:
-                        # The exceptional case is handled here
-                        no_exc = False
-                        if exit is None:
-                            raise
-                        if not exit(*sys.exc_info()):
-                            raise
-                        # The exception is swallowed if exit() returns true
-                finally:
-                    # The normal and non-local-goto cases are handled here
-                    if no_exc and exit:
-                        exit(None, None, None)
-            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)
-        return newobj
-    copy = __copy__
-
-
-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."""
-        self._apply(Parser.load(config))
-
-    def _apply(self, config):
-        """Update self from a dict."""
-        which_env = config.get('environment')
-        if which_env:
-            env = self.environments[which_env]
-            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.ConfigParser):
-
-    """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, text_or_bytes):
-            filenames = [filenames]
-        for filename in filenames:
-            # try:
-            #     fp = open(filename)
-            # except IOError:
-            #     continue
-            fp = open(filename)
-            try:
-                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
-        result = {}
-        for section in self.sections():
-            if section not in result:
-                result[section] = {}
-            for option in self.options(section):
-                value = self.get(section, option, raw=raw, vars=vars)
-                try:
-                    value = unrepr(value)
-                except Exception:
-                    x = sys.exc_info()[1]
-                    msg = ('Config error in section: %r, option: %r, '
-                           'value: %r. Config values must be valid Python.' %
-                           (section, option, value))
-                    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)
-        else:
-            self.read(file)
-        return self.as_dict()
-
-    @classmethod
-    def load(self, input):
-        """Resolve 'input' to dict from a dict, file, or filename."""
-        is_file = (
-            # Filename
-            isinstance(input, text_or_bytes)
-            # Open file object
-            or hasattr(input, 'read')
-        )
-        return Parser().dict_from_file(input) if is_file else input.copy()
-
-
-# public domain "unrepr" implementation, found on the web and then improved.
-
-
-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:
-            import compiler
-        except ImportError:
-            # 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 = o.getChildren()
-        # Build callee from first child
-        callee = self.build(children[0])
-        # Build args and kwargs from remaining children
-        args = []
-        kwargs = {}
-        for child in children[1:]:
-            class_name = child.__class__.__name__
-            # None is ignored
-            if class_name == 'NoneType':
-                continue
-            # Keywords become kwargs
-            if class_name == 'Keyword':
-                kwargs.update(self.build(child))
-            # Everything else becomes args
-            else:
-                args.append(self.build(child))
-
-        return callee(*args, **kwargs)
-
-    def build_Keyword(self, o):
-        key, value_obj = o.getChildren()
-        value = self.build(value_obj)
-        kw_dict = {key: value}
-        return kw_dict
-
-    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 None
-        if name == 'True':
-            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:
-            import ast
-        except ImportError:
-            # Fallback to eval when ast package is not available,
-            # e.g. IronPython 1.0.
-            return eval(s)
-
-        p = ast.parse('__tempvalue__ = ' + s)
-        return p.body[0].value
-
-    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_call35(self, o):
-        """
-        Workaround for python 3.5 _ast.Call signature, docs found here
-        https://greentreesnakes.readthedocs.org/en/latest/nodes.html
-        """
-        import ast
-        callee = self.build(o.func)
-        args = []
-        if o.args is not None:
-            for a in o.args:
-                if isinstance(a, ast.Starred):
-                    args.append(self.build(a.value))
-                else:
-                    args.append(self.build(a))
-        kwargs = {}
-        for kw in o.keywords:
-            if kw.arg is None:  # double asterix `**`
-                rst = self.build(kw.value)
-                if not isinstance(rst, dict):
-                    raise TypeError('Invalid argument for call.'
-                                    'Must be a mapping object.')
-                # give preference to the keys set directly from arg=value
-                for k, v in rst.items():
-                    if k not in kwargs:
-                        kwargs[k] = v
-            else:  # defined on the call as: arg=value
-                kwargs[kw.arg] = self.build(kw.value)
-        return callee(*args, **kwargs)
-
-    def build_Call(self, o):
-        if sys.version_info >= (3, 5):
-            return self._build_call35(o)
-
-        callee = self.build(o.func)
-
-        if o.args is None:
-            args = ()
-        else:
-            args = tuple([self.build(a) for a in o.args])
-
-        if o.starargs is None:
-            starargs = ()
-        else:
-            starargs = tuple(self.build(o.starargs))
-
-        if o.kwargs is None:
-            kwargs = {}
-        else:
-            kwargs = self.build(o.kwargs)
-        if o.keywords is not None:  # direct a=b keywords
-            for kw in o.keywords:
-                # preference because is a direct keyword against **kwargs
-                kwargs[kw.arg] = self.build(kw.value)
-        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 None
-        if name == 'True':
-            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_NameConstant(self, o):
-        return o.value
-
-    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])
-        return op(left, right)
-
-    def build_Add(self, o):
-        return operator.add
-
-    def build_Mult(self, o):
-        return operator.mul
-
-    def build_USub(self, o):
-        return operator.neg
-
-    def build_Attribute(self, o):
-        parent = self.build(o.value)
-        return getattr(parent, o.attr)
-
-    def build_NoneType(self, o):
-        return None
-
-
-def unrepr(s):
-    """Return a Python object compiled from a string."""
-    if not s:
-        return s
-    if sys.version_info < (3, 0):
-        b = _Builder2()
-    else:
-        b = _Builder3()
-    obj = b.astnode(s)
-    return b.build(obj)
-
-
-def modules(modulePath):
-    """Load a module and retrieve a reference to that module."""
-    __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:
-        attr = getattr(mod, attr_name)
-    except AttributeError:
-        raise AttributeError("'%s' object has no attribute '%s'"
-                             % (mod_path, attr_name))
-
-    # Return a reference to the attribute.
-    return attr
diff --git a/libraries/cherrypy/lib/sessions.py b/libraries/cherrypy/lib/sessions.py
deleted file mode 100644
index 5b49ee13..00000000
--- a/libraries/cherrypy/lib/sessions.py
+++ /dev/null
@@ -1,919 +0,0 @@
-"""Session implementation for CherryPy.
-
-You need to edit your config file to use sessions. Here's an example::
-
-    [/]
-    tools.sessions.on = True
-    tools.sessions.storage_class = cherrypy.lib.sessions.FileSession
-    tools.sessions.storage_path = "/home/site/sessions"
-    tools.sessions.timeout = 60
-
-This sets the session to be stored in files in the directory
-/home/site/sessions, and the session timeout to 60 minutes. If you omit
-``storage_class``, the sessions will be saved in RAM.
-``tools.sessions.on`` is the only required line for working sessions,
-the rest are optional.
-
-By default, the session ID is passed in a cookie, so the client's browser must
-have cookies enabled for your site.
-
-To set data for the current session, use
-``cherrypy.session['fieldname'] = 'fieldvalue'``;
-to get data use ``cherrypy.session.get('fieldname')``.
-
-================
-Locking sessions
-================
-
-By default, the ``'locking'`` mode of sessions is ``'implicit'``, which means
-the session is locked early and unlocked late. Be mindful of this default mode
-for any requests that take a long time to process (streaming responses,
-expensive calculations, database lookups, API calls, etc), as other concurrent
-requests that also utilize sessions will hang until the session is unlocked.
-
-If you want to control when the session data is locked and unlocked,
-set ``tools.sessions.locking = 'explicit'``. Then call
-``cherrypy.session.acquire_lock()`` and ``cherrypy.session.release_lock()``.
-Regardless of which mode you use, the session is guaranteed to be unlocked when
-the request is complete.
-
-=================
-Expiring Sessions
-=================
-
-You can force a session to expire with :func:`cherrypy.lib.sessions.expire`.
-Simply call that function at the point you want the session to expire, and it
-will cause the session cookie to expire client-side.
-
-===========================
-Session Fixation Protection
-===========================
-
-If CherryPy receives, via a request cookie, a session id that it does not
-recognize, it will reject that id and create a new one to return in the
-response cookie. This `helps prevent session fixation attacks
-<http://en.wikipedia.org/wiki/Session_fixation#Regenerate_SID_on_each_request>`_.
-However, CherryPy "recognizes" a session id by looking up the saved session
-data for that id. Therefore, if you never save any session data,
-**you will get a new session id for every request**.
-
-A side effect of CherryPy overwriting unrecognised session ids is that if you
-have multiple, separate CherryPy applications running on a single domain (e.g.
-on different ports), each app will overwrite the other's session id because by
-default they use the same cookie name (``"session_id"``) but do not recognise
-each others sessions. It is therefore a good idea to use a different name for
-each, for example::
-
-    [/]
-    ...
-    tools.sessions.name = "my_app_session_id"
-
-================
-Sharing Sessions
-================
-
-If you run multiple instances of CherryPy (for example via mod_python behind
-Apache prefork), you most likely cannot use the RAM session backend, since each
-instance of CherryPy will have its own memory space. Use a different backend
-instead, and verify that all instances are pointing at the same file or db
-location. Alternately, you might try a load balancer which makes sessions
-"sticky". Google is your friend, there.
-
-================
-Expiration Dates
-================
-
-The response cookie will possess an expiration date to inform the client at
-which point to stop sending the cookie back in requests. If the server time
-and client time differ, expect sessions to be unreliable. **Make sure the
-system time of your server is accurate**.
-
-CherryPy defaults to a 60-minute session timeout, which also applies to the
-cookie which is sent to the client. Unfortunately, some versions of Safari
-("4 public beta" on Windows XP at least) appear to have a bug in their parsing
-of the GMT expiration date--they appear to interpret the date as one hour in
-the past. Sixty minutes minus one hour is pretty close to zero, so you may
-experience this bug as a new session id for every request, unless the requests
-are less than one second apart. To fix, try increasing the session.timeout.
-
-On the other extreme, some users report Firefox sending cookies after their
-expiration date, although this was on a system with an inaccurate system time.
-Maybe FF doesn't trust system time.
-"""
-import sys
-import datetime
-import os
-import time
-import threading
-import binascii
-
-import six
-from six.moves import cPickle as pickle
-import contextlib2
-
-import zc.lockfile
-
-import cherrypy
-from cherrypy.lib import httputil
-from cherrypy.lib import locking
-from cherrypy.lib import is_iterator
-
-
-if six.PY2:
-    FileNotFoundError = OSError
-
-
-missing = object()
-
-
-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."
-
-    @property
-    def id(self):
-        """Return the current session id."""
-        return self._id
-
-    @id.setter
-    def id(self, value):
-        self._id = value
-        for o in self.id_observers:
-            o(value)
-
-    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
-    'If True, log debug information.'
-
-    # --------------------- Session management methods --------------------- #
-
-    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:
-            if self.debug:
-                cherrypy.log('No id given; making a new one', 'TOOLS.SESSIONS')
-            self._regenerate()
-        else:
-            self.id = id
-            if self._exists():
-                if self.debug:
-                    cherrypy.log('Set id to %s.' % id, 'TOOLS.SESSIONS')
-            else:
-                if self.debug:
-                    cherrypy.log('Expired or malicious session %r; '
-                                 'making a new one' % id, 'TOOLS.SESSIONS')
-                # Expired or malicious session. Make a new one.
-                # See https://github.com/cherrypy/cherrypy/issues/709.
-                self.id = None
-                self.missing = True
-                self._regenerate()
-
-    def now(self):
-        """Generate the session specific concept of 'now'.
-
-        Other session providers can override this to use alternative,
-        possibly timezone aware, versions of 'now'.
-        """
-        return datetime.datetime.now()
-
-    def regenerate(self):
-        """Replace the current session (with a new id)."""
-        self.regenerated = True
-        self._regenerate()
-
-    def _regenerate(self):
-        if self.id is not None:
-            if self.debug:
-                cherrypy.log(
-                    'Deleting the existing session %r before '
-                    'regeneration.' % self.id,
-                    'TOOLS.SESSIONS')
-            self.delete()
-
-        old_session_was_locked = self.locked
-        if old_session_was_locked:
-            self.release_lock()
-            if self.debug:
-                cherrypy.log('Old lock released.', 'TOOLS.SESSIONS')
-
-        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 self.debug:
-            cherrypy.log('Set id to generated %s.' % self.id,
-                         'TOOLS.SESSIONS')
-
-        if old_session_was_locked:
-            self.acquire_lock()
-            if self.debug:
-                cherrypy.log('Regenerated lock acquired.', 'TOOLS.SESSIONS')
-
-    def clean_up(self):
-        """Clean up expired sessions."""
-        pass
-
-    def generate_id(self):
-        """Return a new session id."""
-        return binascii.hexlify(os.urandom(20)).decode('ascii')
-
-    def save(self):
-        """Save session data."""
-        try:
-            # If session data has never been loaded then it's never been
-            #   accessed: no need to save it
-            if self.loaded:
-                t = datetime.timedelta(seconds=self.timeout * 60)
-                expiration_time = self.now() + t
-                if self.debug:
-                    cherrypy.log('Saving session %r with expiry %s' %
-                                 (self.id, expiration_time),
-                                 'TOOLS.SESSIONS')
-                self._save(expiration_time)
-            else:
-                if self.debug:
-                    cherrypy.log(
-                        'Skipping save of session %r (no session loaded).' %
-                        self.id, 'TOOLS.SESSIONS')
-        finally:
-            if self.locked:
-                # Always release the lock if the user didn't release it
-                self.release_lock()
-                if self.debug:
-                    cherrypy.log('Lock released after save.', 'TOOLS.SESSIONS')
-
-    def load(self):
-        """Copy stored session data into this session instance."""
-        data = self._load()
-        # data is either None or a tuple (session_data, expiration_time)
-        if data is None or data[1] < self.now():
-            if self.debug:
-                cherrypy.log('Expired session %r, flushing data.' % self.id,
-                             'TOOLS.SESSIONS')
-            self._data = {}
-        else:
-            if self.debug:
-                cherrypy.log('Data loaded for session %r.' % self.id,
-                             'TOOLS.SESSIONS')
-            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__
-        if self.clean_freq and not cls.clean_thread:
-            # clean_up is an instancemethod and not a classmethod,
-            # so that tool config can be accessed inside the method.
-            t = cherrypy.process.plugins.Monitor(
-                cherrypy.engine, self.clean_up, self.clean_freq * 60,
-                name='Session cleanup')
-            t.subscribe()
-            cls.clean_thread = t
-            t.start()
-            if self.debug:
-                cherrypy.log('Started cleanup thread.', 'TOOLS.SESSIONS')
-
-    def delete(self):
-        """Delete stored session data."""
-        self._delete()
-        if self.debug:
-            cherrypy.log('Deleted session %s.' % self.id,
-                         'TOOLS.SESSIONS')
-
-    # -------------------- Application accessor methods -------------------- #
-
-    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,
-        otherwise KeyError is raised.
-        """
-        if not self.loaded:
-            self.load()
-        if default is missing:
-            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
-
-    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()
-        return self._data.values()
-
-
-class RamSession(Session):
-
-    # Class-level objects. Don't rebind these!
-    cache = {}
-    locks = {}
-
-    def clean_up(self):
-        """Clean up expired sessions."""
-
-        now = self.now()
-        for _id, (data, expiration_time) in list(six.iteritems(self.cache)):
-            if expiration_time <= now:
-                try:
-                    del self.cache[_id]
-                except KeyError:
-                    pass
-                try:
-                    if self.locks[_id].acquire(blocking=False):
-                        lock = self.locks.pop(_id)
-                        lock.release()
-                except KeyError:
-                    pass
-
-        # added to remove obsolete lock objects
-        for _id in list(self.locks):
-            locked = (
-                _id not in self.cache
-                and self.locks[_id].acquire(blocking=False)
-            )
-            if locked:
-                lock = self.locks.pop(_id)
-                lock.release()
-
-    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.
-
-    lock_timeout
-        A timedelta or numeric seconds indicating how long
-        to block acquiring a lock. If None (default), acquiring a lock
-        will block indefinitely.
-    """
-
-    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'])
-        kwargs.setdefault('lock_timeout', None)
-
-        Session.__init__(self, id=id, **kwargs)
-
-        # validate self.lock_timeout
-        if isinstance(self.lock_timeout, (int, float)):
-            self.lock_timeout = datetime.timedelta(seconds=self.lock_timeout)
-        if not isinstance(self.lock_timeout, (datetime.timedelta, type(None))):
-            raise ValueError(
-                'Lock timeout must be numeric seconds or a timedelta instance.'
-            )
-
-    @classmethod
-    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)
-
-    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):
-        assert self.locked, ('The session load without being locked.  '
-                             "Check your tools' priority levels.")
-        if path is None:
-            path = self._get_file_path()
-        try:
-            f = open(path, 'rb')
-            try:
-                return pickle.load(f)
-            finally:
-                f.close()
-        except (IOError, EOFError):
-            e = sys.exc_info()[1]
-            if self.debug:
-                cherrypy.log('Error loading the session pickle: %s' %
-                             e, 'TOOLS.SESSIONS')
-            return None
-
-    def _save(self, expiration_time):
-        assert self.locked, ('The session was saved without being locked.  '
-                             "Check your tools' priority levels.")
-        f = open(self._get_file_path(), 'wb')
-        try:
-            pickle.dump((self._data, expiration_time), f, self.pickle_protocol)
-        finally:
-            f.close()
-
-    def _delete(self):
-        assert self.locked, ('The session deletion without being locked.  '
-                             "Check your tools' priority levels.")
-        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:
-            path = self._get_file_path()
-        path += self.LOCK_SUFFIX
-        checker = locking.LockChecker(self.id, self.lock_timeout)
-        while not checker.expired():
-            try:
-                self.lock = zc.lockfile.LockFile(path)
-            except zc.lockfile.LockError:
-                time.sleep(0.1)
-            else:
-                break
-        self.locked = True
-        if self.debug:
-            cherrypy.log('Lock acquired.', 'TOOLS.SESSIONS')
-
-    def release_lock(self, path=None):
-        """Release the lock on the currently-loaded session data."""
-        self.lock.close()
-        with contextlib2.suppress(FileNotFoundError):
-            os.remove(self.lock._path)
-        self.locked = False
-
-    def clean_up(self):
-        """Clean up expired sessions."""
-        now = self.now()
-        # Iterate over all session files in self.storage_path
-        for fname in os.listdir(self.storage_path):
-            have_session = (
-                fname.startswith(self.SESSION_PREFIX)
-                and not fname.endswith(self.LOCK_SUFFIX)
-            )
-            if have_session:
-                # We have a session file: lock and load it and check
-                #   if it's expired. If it fails, nevermind.
-                path = os.path.join(self.storage_path, fname)
-                self.acquire_lock(path)
-                if self.debug:
-                    # This is a bit of a hack, since we're calling clean_up
-                    # on the first instance rather than the entire class,
-                    # so depending on whether you have "debug" set on the
-                    # path of the first session called, this may not run.
-                    cherrypy.log('Cleanup lock acquired.', 'TOOLS.SESSIONS')
-
-                try:
-                    contents = self._load(path)
-                    # _load returns None on IOError
-                    if contents is not None:
-                        data, expiration_time = contents
-                        if expiration_time < now:
-                            # Session expired: deleting it
-                            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)
-                    if (fname.startswith(self.SESSION_PREFIX) and
-                        not fname.endswith(self.LOCK_SUFFIX))])
-
-
-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 separate set of locks per session id.
-    locks = {}
-
-    servers = ['127.0.0.1:11211']
-
-    @classmethod
-    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)
-
-    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()))
-        self.mc_lock.acquire()
-        try:
-            if not self.cache.set(self.id, (self._data, expiration_time), td):
-                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()
-        if self.debug:
-            cherrypy.log('Lock acquired.', 'TOOLS.SESSIONS')
-
-    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
-
-
-# Hook functions (for CherryPy tools)
-
-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
-        request.hooks.attach('on_end_request', cherrypy.session.save)
-    else:
-        # If the body is not being streamed, we save the data now
-        # (so we can release the lock).
-        if is_iterator(response.body):
-            response.collapse_body()
-        cherrypy.session.save()
-
-
-save.failsafe = True
-
-
-def close():
-    """Close the session object for this request."""
-    sess = getattr(cherrypy.serving, 'session', None)
-    if getattr(sess, 'locked', False):
-        # If the session is still locked we release the lock
-        sess.release_lock()
-        if sess.debug:
-            cherrypy.log('Lock released on close.', 'TOOLS.SESSIONS')
-
-
-close.failsafe = True
-close.priority = 90
-
-
-def init(storage_type=None, path=None, path_header=None, name='session_id',
-         timeout=60, domain=None, secure=False, clean_freq=5,
-         persistent=True, httponly=False, debug=False,
-         # Py27 compat
-         # *, storage_class=RamSession,
-         **kwargs):
-    """Initialize session object (using cookies).
-
-    storage_class
-        The Session subclass to use. Defaults to RamSession.
-
-    storage_type
-        (deprecated)
-        One of 'ram', 'file', 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.
-    """
-
-    # Py27 compat
-    storage_class = kwargs.pop('storage_class', RamSession)
-
-    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:
-        id = request.cookie[name].value
-        if debug:
-            cherrypy.log('ID obtained from request.cookie: %r' % id,
-                         'TOOLS.SESSIONS')
-
-    first_time = not hasattr(cherrypy, 'session')
-
-    if storage_type:
-        if first_time:
-            msg = 'storage_type is deprecated. Supply storage_class instead'
-            cherrypy.log(msg)
-        storage_class = storage_type.title() + 'Session'
-        storage_class = globals()[storage_class]
-
-    # call setup first time only
-    if first_time:
-        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.
-    kwargs['timeout'] = timeout
-    kwargs['clean_freq'] = clean_freq
-    cherrypy.serving.session = sess = storage_class(id, **kwargs)
-    sess.debug = debug
-
-    def update_cookie(id):
-        """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:
-        # See http://support.microsoft.com/kb/223799/EN-US/
-        # and http://support.mozilla.com/en-US/kb/Cookies
-        cookie_timeout = None
-    set_response_cookie(path=path, path_header=path_header, name=name,
-                        timeout=cookie_timeout, domain=domain, secure=secure,
-                        httponly=httponly)
-
-
-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.
-
-    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 for the cookie. If 0 or other boolean
-        False, no 'expires' param will be set, and the cookie will be a
-        "session cookie" which expires when the browser is closed.
-
-    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).
-
-    httponly
-        If False (the default) the cookie 'httponly' value will not be set.
-        If True, the cookie 'httponly' value will be set (to 1).
-
-    """
-    # Set response cookie
-    cookie = cherrypy.serving.response.cookie
-    cookie[name] = cherrypy.serving.session.id
-    cookie[name]['path'] = (
-        path or
-        cherrypy.serving.request.headers.get(path_header) or
-        '/'
-    )
-
-    if timeout:
-        cookie[name]['max-age'] = timeout * 60
-        _add_MSIE_max_age_workaround(cookie[name], timeout)
-    if domain is not None:
-        cookie[name]['domain'] = domain
-    if secure:
-        cookie[name]['secure'] = 1
-    if httponly:
-        if not cookie[name].isReservedKey('httponly'):
-            raise ValueError('The httponly cookie token is not supported.')
-        cookie[name]['httponly'] = 1
-
-
-def _add_MSIE_max_age_workaround(cookie, timeout):
-    """
-    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
-    the browser. So we have to use the old "expires" ... sigh ...
-    """
-    expires = time.time() + timeout * 60
-    cookie['expires'] = httputil.HTTPDate(expires)
-
-
-def expire():
-    """Expire the current session cookie."""
-    name = cherrypy.serving.request.config.get(
-        'tools.sessions.name', 'session_id')
-    one_year = 60 * 60 * 24 * 365
-    e = time.time() - one_year
-    cherrypy.serving.response.cookie[name]['expires'] = httputil.HTTPDate(e)
-    cherrypy.serving.response.cookie[name].pop('max-age', None)
diff --git a/libraries/cherrypy/lib/static.py b/libraries/cherrypy/lib/static.py
deleted file mode 100644
index da9d9373..00000000
--- a/libraries/cherrypy/lib/static.py
+++ /dev/null
@@ -1,390 +0,0 @@
-"""Module with helpers for serving static files."""
-
-import os
-import platform
-import re
-import stat
-import mimetypes
-
-from email.generator import _make_boundary as make_boundary
-from io import UnsupportedOperation
-
-from six.moves import urllib
-
-import cherrypy
-from cherrypy._cpcompat import ntob
-from cherrypy.lib import cptools, httputil, file_generator_limited
-
-
-def _setup_mimetypes():
-    """Pre-initialize global mimetype map."""
-    if not mimetypes.inited:
-        mimetypes.init()
-    mimetypes.types_map['.dwg'] = 'image/x-dwg'
-    mimetypes.types_map['.ico'] = 'image/x-icon'
-    mimetypes.types_map['.bz2'] = 'application/x-bzip2'
-    mimetypes.types_map['.gz'] = 'application/x-gzip'
-
-
-_setup_mimetypes()
-
-
-def serve_file(path, content_type=None, disposition=None, name=None,
-               debug=False):
-    """Set status, headers, and body in order to serve the given path.
-
-    The Content-Type header will be set to the content_type arg, if provided.
-    If not provided, the Content-Type will be guessed by the file extension
-    of the 'path' argument.
-
-    If disposition is not None, the Content-Disposition header will be set
-    to "<disposition>; filename=<name>". If name is None, it will be set
-    to the basename of path. If disposition is None, no Content-Disposition
-    header will be written.
-    """
-    response = cherrypy.serving.response
-
-    # If path is relative, users should fix it by making path absolute.
-    # That is, CherryPy should not guess where the application root is.
-    # It certainly should *not* use cwd (since CP may be invoked from a
-    # variety of paths). If using tools.staticdir, you can make your relative
-    # paths become absolute by supplying a value for "tools.staticdir.root".
-    if not os.path.isabs(path):
-        msg = "'%s' is not an absolute path." % path
-        if debug:
-            cherrypy.log(msg, 'TOOLS.STATICFILE')
-        raise ValueError(msg)
-
-    try:
-        st = os.stat(path)
-    except (OSError, TypeError, ValueError):
-        # OSError when file fails to stat
-        # TypeError on Python 2 when there's a null byte
-        # ValueError on Python 3 when there's a null byte
-        if debug:
-            cherrypy.log('os.stat(%r) failed' % path, 'TOOLS.STATIC')
-        raise cherrypy.NotFound()
-
-    # Check if path is a directory.
-    if stat.S_ISDIR(st.st_mode):
-        # Let the caller deal with it as they like.
-        if debug:
-            cherrypy.log('%r is a directory' % path, 'TOOLS.STATIC')
-        raise cherrypy.NotFound()
-
-    # Set the Last-Modified response header, so that
-    # modified-since validation code can work.
-    response.headers['Last-Modified'] = httputil.HTTPDate(st.st_mtime)
-    cptools.validate_since()
-
-    if content_type is None:
-        # Set content-type based on filename extension
-        ext = ''
-        i = path.rfind('.')
-        if i != -1:
-            ext = path[i:].lower()
-        content_type = mimetypes.types_map.get(ext, None)
-    if content_type is not None:
-        response.headers['Content-Type'] = content_type
-    if debug:
-        cherrypy.log('Content-Type: %r' % content_type, 'TOOLS.STATIC')
-
-    cd = None
-    if disposition is not None:
-        if name is None:
-            name = os.path.basename(path)
-        cd = '%s; filename="%s"' % (disposition, name)
-        response.headers['Content-Disposition'] = cd
-    if debug:
-        cherrypy.log('Content-Disposition: %r' % cd, 'TOOLS.STATIC')
-
-    # Set Content-Length and use an iterable (file object)
-    #   this way CP won't load the whole file in memory
-    content_length = st.st_size
-    fileobj = open(path, 'rb')
-    return _serve_fileobj(fileobj, content_type, content_length, debug=debug)
-
-
-def serve_fileobj(fileobj, content_type=None, disposition=None, name=None,
-                  debug=False):
-    """Set status, headers, and body in order to serve the given file object.
-
-    The Content-Type header will be set to the content_type arg, if provided.
-
-    If disposition is not None, the Content-Disposition header will be set
-    to "<disposition>; filename=<name>". If name is None, 'filename' will
-    not be set. If disposition is None, no Content-Disposition header will
-    be written.
-
-    CAUTION: If the request contains a 'Range' header, one or more seek()s will
-    be performed on the file object.  This may cause undesired behavior if
-    the file object is not seekable.  It could also produce undesired results
-    if the caller set the read position of the file object prior to calling
-    serve_fileobj(), expecting that the data would be served starting from that
-    position.
-    """
-    response = cherrypy.serving.response
-
-    try:
-        st = os.fstat(fileobj.fileno())
-    except AttributeError:
-        if debug:
-            cherrypy.log('os has no fstat attribute', 'TOOLS.STATIC')
-        content_length = None
-    except UnsupportedOperation:
-        content_length = None
-    else:
-        # Set the Last-Modified response header, so that
-        # modified-since validation code can work.
-        response.headers['Last-Modified'] = httputil.HTTPDate(st.st_mtime)
-        cptools.validate_since()
-        content_length = st.st_size
-
-    if content_type is not None:
-        response.headers['Content-Type'] = content_type
-    if debug:
-        cherrypy.log('Content-Type: %r' % content_type, 'TOOLS.STATIC')
-
-    cd = None
-    if disposition is not None:
-        if name is None:
-            cd = disposition
-        else:
-            cd = '%s; filename="%s"' % (disposition, name)
-        response.headers['Content-Disposition'] = cd
-    if debug:
-        cherrypy.log('Content-Disposition: %r' % cd, 'TOOLS.STATIC')
-
-    return _serve_fileobj(fileobj, content_type, content_length, debug=debug)
-
-
-def _serve_fileobj(fileobj, content_type, content_length, debug=False):
-    """Internal. Set response.body to the given file object, perhaps ranged."""
-    response = cherrypy.serving.response
-
-    # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code
-    request = cherrypy.serving.request
-    if request.protocol >= (1, 1):
-        response.headers['Accept-Ranges'] = 'bytes'
-        r = httputil.get_ranges(request.headers.get('Range'), content_length)
-        if r == []:
-            response.headers['Content-Range'] = 'bytes */%s' % content_length
-            message = ('Invalid Range (first-byte-pos greater than '
-                       'Content-Length)')
-            if debug:
-                cherrypy.log(message, 'TOOLS.STATIC')
-            raise cherrypy.HTTPError(416, message)
-
-        if r:
-            if len(r) == 1:
-                # Return a single-part response.
-                start, stop = r[0]
-                if stop > content_length:
-                    stop = content_length
-                r_len = stop - start
-                if debug:
-                    cherrypy.log(
-                        'Single part; start: %r, stop: %r' % (start, stop),
-                        'TOOLS.STATIC')
-                response.status = '206 Partial Content'
-                response.headers['Content-Range'] = (
-                    'bytes %s-%s/%s' % (start, stop - 1, content_length))
-                response.headers['Content-Length'] = r_len
-                fileobj.seek(start)
-                response.body = file_generator_limited(fileobj, r_len)
-            else:
-                # Return a multipart/byteranges response.
-                response.status = '206 Partial Content'
-                boundary = make_boundary()
-                ct = 'multipart/byteranges; boundary=%s' % boundary
-                response.headers['Content-Type'] = ct
-                if 'Content-Length' in response.headers:
-                    # Delete Content-Length header so finalize() recalcs it.
-                    del response.headers['Content-Length']
-
-                def file_ranges():
-                    # Apache compatibility:
-                    yield b'\r\n'
-
-                    for start, stop in r:
-                        if debug:
-                            cherrypy.log(
-                                'Multipart; start: %r, stop: %r' % (
-                                    start, stop),
-                                'TOOLS.STATIC')
-                        yield ntob('--' + boundary, 'ascii')
-                        yield ntob('\r\nContent-type: %s' % content_type,
-                                   'ascii')
-                        yield ntob(
-                            '\r\nContent-range: bytes %s-%s/%s\r\n\r\n' % (
-                                start, stop - 1, content_length),
-                            'ascii')
-                        fileobj.seek(start)
-                        gen = file_generator_limited(fileobj, stop - start)
-                        for chunk in gen:
-                            yield chunk
-                        yield b'\r\n'
-                    # Final boundary
-                    yield ntob('--' + boundary + '--', 'ascii')
-
-                    # Apache compatibility:
-                    yield b'\r\n'
-                response.body = file_ranges()
-            return response.body
-        else:
-            if debug:
-                cherrypy.log('No byteranges requested', 'TOOLS.STATIC')
-
-    # Set Content-Length and use an iterable (file object)
-    #   this way CP won't load the whole file in memory
-    response.headers['Content-Length'] = content_length
-    response.body = fileobj
-    return response.body
-
-
-def serve_download(path, name=None):
-    """Serve 'path' as an application/x-download attachment."""
-    # This is such a common idiom I felt it deserved its own wrapper.
-    return serve_file(path, 'application/x-download', 'attachment', name)
-
-
-def _attempt(filename, content_types, debug=False):
-    if debug:
-        cherrypy.log('Attempting %r (content_types %r)' %
-                     (filename, content_types), 'TOOLS.STATICDIR')
-    try:
-        # you can set the content types for a
-        # complete directory per extension
-        content_type = None
-        if content_types:
-            r, ext = os.path.splitext(filename)
-            content_type = content_types.get(ext[1:], None)
-        serve_file(filename, content_type=content_type, debug=debug)
-        return True
-    except cherrypy.NotFound:
-        # If we didn't find the static file, continue handling the
-        # request. We might find a dynamic handler instead.
-        if debug:
-            cherrypy.log('NotFound', 'TOOLS.STATICFILE')
-        return False
-
-
-def staticdir(section, dir, root='', match='', content_types=None, index='',
-              debug=False):
-    """Serve a static resource from the given (root +) dir.
-
-    match
-        If given, request.path_info will be searched for the given
-        regular expression before attempting to serve static content.
-
-    content_types
-        If given, it should be a Python dictionary of
-        {file-extension: content-type} pairs, where 'file-extension' is
-        a string (e.g. "gif") and 'content-type' is the value to write
-        out in the Content-Type response header (e.g. "image/gif").
-
-    index
-        If provided, it should be the (relative) name of a file to
-        serve for directory requests. For example, if the dir argument is
-        '/home/me', the Request-URI is 'myapp', and the index arg is
-        'index.html', the file '/home/me/myapp/index.html' will be sought.
-    """
-    request = cherrypy.serving.request
-    if request.method not in ('GET', 'HEAD'):
-        if debug:
-            cherrypy.log('request.method not GET or HEAD', 'TOOLS.STATICDIR')
-        return False
-
-    if match and not re.search(match, request.path_info):
-        if debug:
-            cherrypy.log('request.path_info %r does not match pattern %r' %
-                         (request.path_info, match), 'TOOLS.STATICDIR')
-        return False
-
-    # Allow the use of '~' to refer to a user's home directory.
-    dir = os.path.expanduser(dir)
-
-    # If dir is relative, make absolute using "root".
-    if not os.path.isabs(dir):
-        if not root:
-            msg = 'Static dir requires an absolute dir (or root).'
-            if debug:
-                cherrypy.log(msg, 'TOOLS.STATICDIR')
-            raise ValueError(msg)
-        dir = os.path.join(root, dir)
-
-    # Determine where we are in the object tree relative to 'section'
-    # (where the static tool was defined).
-    if section == 'global':
-        section = '/'
-    section = section.rstrip(r'\/')
-    branch = request.path_info[len(section) + 1:]
-    branch = urllib.parse.unquote(branch.lstrip(r'\/'))
-
-    # Requesting a file in sub-dir of the staticdir results
-    # in mixing of delimiter styles, e.g. C:\static\js/script.js.
-    # Windows accepts this form except not when the path is
-    # supplied in extended-path notation, e.g. \\?\C:\static\js/script.js.
-    # http://bit.ly/1vdioCX
-    if platform.system() == 'Windows':
-        branch = branch.replace('/', '\\')
-
-    # If branch is "", filename will end in a slash
-    filename = os.path.join(dir, branch)
-    if debug:
-        cherrypy.log('Checking file %r to fulfill %r' %
-                     (filename, request.path_info), 'TOOLS.STATICDIR')
-
-    # There's a chance that the branch pulled from the URL might
-    # have ".." or similar uplevel attacks in it. Check that the final
-    # filename is a child of dir.
-    if not os.path.normpath(filename).startswith(os.path.normpath(dir)):
-        raise cherrypy.HTTPError(403)  # Forbidden
-
-    handled = _attempt(filename, content_types)
-    if not handled:
-        # Check for an index file if a folder was requested.
-        if index:
-            handled = _attempt(os.path.join(filename, index), content_types)
-            if handled:
-                request.is_index = filename[-1] in (r'\/')
-    return handled
-
-
-def staticfile(filename, root=None, match='', content_types=None, debug=False):
-    """Serve a static resource from the given (root +) filename.
-
-    match
-        If given, request.path_info will be searched for the given
-        regular expression before attempting to serve static content.
-
-    content_types
-        If given, it should be a Python dictionary of
-        {file-extension: content-type} pairs, where 'file-extension' is
-        a string (e.g. "gif") and 'content-type' is the value to write
-        out in the Content-Type response header (e.g. "image/gif").
-
-    """
-    request = cherrypy.serving.request
-    if request.method not in ('GET', 'HEAD'):
-        if debug:
-            cherrypy.log('request.method not GET or HEAD', 'TOOLS.STATICFILE')
-        return False
-
-    if match and not re.search(match, request.path_info):
-        if debug:
-            cherrypy.log('request.path_info %r does not match pattern %r' %
-                         (request.path_info, match), 'TOOLS.STATICFILE')
-        return False
-
-    # If filename is relative, make absolute using "root".
-    if not os.path.isabs(filename):
-        if not root:
-            msg = "Static tool requires an absolute filename (got '%s')." % (
-                filename,)
-            if debug:
-                cherrypy.log(msg, 'TOOLS.STATICFILE')
-            raise ValueError(msg)
-        filename = os.path.join(root, filename)
-
-    return _attempt(filename, content_types, debug=debug)
diff --git a/libraries/cherrypy/lib/xmlrpcutil.py b/libraries/cherrypy/lib/xmlrpcutil.py
deleted file mode 100644
index ddaac86a..00000000
--- a/libraries/cherrypy/lib/xmlrpcutil.py
+++ /dev/null
@@ -1,61 +0,0 @@
-"""XML-RPC tool helpers."""
-import sys
-
-from six.moves.xmlrpc_client import (
-    loads as xmlrpc_loads, dumps as xmlrpc_dumps,
-    Fault as XMLRPCFault
-)
-
-import cherrypy
-from cherrypy._cpcompat import ntob
-
-
-def process_body():
-    """Return (params, method) from request body."""
-    try:
-        return xmlrpc_loads(cherrypy.request.body.read())
-    except Exception:
-        return ('ERROR PARAMS', ), 'ERRORMETHOD'
-
-
-def patched_path(path):
-    """Return 'path', doctored for RPC."""
-    if not path.endswith('/'):
-        path += '/'
-    if path.startswith('/RPC2/'):
-        # strip the first /rpc2
-        path = path[5:]
-    return path
-
-
-def _set_response(body):
-    """Set up HTTP status, headers and body within CherryPy."""
-    # The XML-RPC spec (http://www.xmlrpc.com/spec) says:
-    # "Unless there's a lower-level error, always return 200 OK."
-    # Since Python's xmlrpc_client interprets a non-200 response
-    # as a "Protocol Error", we'll just return 200 every time.
-    response = cherrypy.response
-    response.status = '200 OK'
-    response.body = ntob(body, 'utf-8')
-    response.headers['Content-Type'] = 'text/xml'
-    response.headers['Content-Length'] = len(body)
-
-
-def respond(body, encoding='utf-8', allow_none=0):
-    """Construct HTTP response body."""
-    if not isinstance(body, XMLRPCFault):
-        body = (body,)
-
-    _set_response(
-        xmlrpc_dumps(
-            body, methodresponse=1,
-            encoding=encoding,
-            allow_none=allow_none
-        )
-    )
-
-
-def on_error(*args, **kwargs):
-    """Construct HTTP response body for an error response."""
-    body = str(sys.exc_info()[1])
-    _set_response(xmlrpc_dumps(XMLRPCFault(1, body)))
diff --git a/libraries/cherrypy/process/__init__.py b/libraries/cherrypy/process/__init__.py
deleted file mode 100644
index f242d226..00000000
--- a/libraries/cherrypy/process/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-"""Site container for an HTTP server.
-
-A Web Site Process Bus object is used to connect applications, servers,
-and frameworks with site-wide services such as daemonization, process
-reload, signal handling, drop privileges, PID file management, logging
-for all of these, and many more.
-
-The 'plugins' module defines a few abstract and concrete services for
-use with the bus. Some use tool-specific channels; see the documentation
-for each class.
-"""
-
-from .wspbus import bus
-from . import plugins, servers
-
-
-__all__ = ('bus', 'plugins', 'servers')
diff --git a/libraries/cherrypy/process/plugins.py b/libraries/cherrypy/process/plugins.py
deleted file mode 100644
index 8c246c81..00000000
--- a/libraries/cherrypy/process/plugins.py
+++ /dev/null
@@ -1,752 +0,0 @@
-"""Site services for use with a Web Site Process Bus."""
-
-import os
-import re
-import signal as _signal
-import sys
-import time
-import threading
-
-from six.moves import _thread
-
-from cherrypy._cpcompat import text_or_bytes
-from cherrypy._cpcompat import ntob, Timer
-
-# _module__file__base is used by Autoreload to make
-# absolute any filenames retrieved from sys.modules which are not
-# already absolute paths.  This is to work around Python's quirk
-# of importing the startup script and using a relative filename
-# for it in sys.modules.
-#
-# Autoreload examines sys.modules afresh every time it runs. If an application
-# changes the current directory by executing os.chdir(), then the next time
-# Autoreload runs, it will not be able to find any filenames which are
-# not absolute paths, because the current directory is not the same as when the
-# module was first imported.  Autoreload will then wrongly conclude the file
-# has "changed", and initiate the shutdown/re-exec sequence.
-# See ticket #917.
-# For this workaround to have a decent probability of success, this module
-# needs to be imported as early as possible, before the app has much chance
-# to change the working directory.
-_module__file__base = os.getcwd()
-
-
-class SimplePlugin(object):
-
-    """Plugin base class which auto-subscribes methods for known channels."""
-
-    bus = None
-    """A :class:`Bus <cherrypy.process.wspbus.Bus>`, usually cherrypy.engine.
-    """
-
-    def __init__(self, bus):
-        self.bus = bus
-
-    def subscribe(self):
-        """Register this object as a (multi-channel) listener on the bus."""
-        for channel in self.bus.listeners:
-            # Subscribe self.start, self.exit, etc. if present.
-            method = getattr(self, channel, None)
-            if method is not None:
-                self.bus.subscribe(channel, method)
-
-    def unsubscribe(self):
-        """Unregister this object as a listener on the bus."""
-        for channel in self.bus.listeners:
-            # Unsubscribe self.start, self.exit, etc. if present.
-            method = getattr(self, channel, None)
-            if method is not None:
-                self.bus.unsubscribe(channel, method)
-
-
-class SignalHandler(object):
-
-    """Register bus channels (and listeners) for system signals.
-
-    You can modify what signals your application listens for, and what it does
-    when it receives signals, by modifying :attr:`SignalHandler.handlers`,
-    a dict of {signal name: callback} pairs. The default set is::
-
-        handlers = {'SIGTERM': self.bus.exit,
-                    'SIGHUP': self.handle_SIGHUP,
-                    'SIGUSR1': self.bus.graceful,
-                   }
-
-    The :func:`SignalHandler.handle_SIGHUP`` method calls
-    :func:`bus.restart()<cherrypy.process.wspbus.Bus.restart>`
-    if the process is daemonized, but
-    :func:`bus.exit()<cherrypy.process.wspbus.Bus.exit>`
-    if the process is attached to a TTY. This is because Unix window
-    managers tend to send SIGHUP to terminal windows when the user closes them.
-
-    Feel free to add signals which are not available on every platform.
-    The :class:`SignalHandler` will ignore errors raised from attempting
-    to register handlers for unknown signals.
-    """
-
-    handlers = {}
-    """A map from signal names (e.g. 'SIGTERM') to handlers (e.g. bus.exit)."""
-
-    signals = {}
-    """A map from signal numbers to names."""
-
-    for k, v in vars(_signal).items():
-        if k.startswith('SIG') and not k.startswith('SIG_'):
-            signals[v] = k
-    del k, v
-
-    def __init__(self, bus):
-        self.bus = bus
-        # Set default handlers
-        self.handlers = {'SIGTERM': self.bus.exit,
-                         'SIGHUP': self.handle_SIGHUP,
-                         'SIGUSR1': self.bus.graceful,
-                         }
-
-        if sys.platform[:4] == 'java':
-            del self.handlers['SIGUSR1']
-            self.handlers['SIGUSR2'] = self.bus.graceful
-            self.bus.log('SIGUSR1 cannot be set on the JVM platform. '
-                         'Using SIGUSR2 instead.')
-            self.handlers['SIGINT'] = self._jython_SIGINT_handler
-
-        self._previous_handlers = {}
-        # used to determine is the process is a daemon in `self._is_daemonized`
-        self._original_pid = os.getpid()
-
-    def _jython_SIGINT_handler(self, signum=None, frame=None):
-        # See http://bugs.jython.org/issue1313
-        self.bus.log('Keyboard Interrupt: shutting down bus')
-        self.bus.exit()
-
-    def _is_daemonized(self):
-        """Return boolean indicating if the current process is
-        running as a daemon.
-
-        The criteria to determine the `daemon` condition is to verify
-        if the current pid is not the same as the one that got used on
-        the initial construction of the plugin *and* the stdin is not
-        connected to a terminal.
-
-        The sole validation of the tty is not enough when the plugin
-        is executing inside other process like in a CI tool
-        (Buildbot, Jenkins).
-        """
-        return (
-            self._original_pid != os.getpid() and
-            not os.isatty(sys.stdin.fileno())
-        )
-
-    def subscribe(self):
-        """Subscribe self.handlers to signals."""
-        for sig, func in self.handlers.items():
-            try:
-                self.set_handler(sig, func)
-            except ValueError:
-                pass
-
-    def unsubscribe(self):
-        """Unsubscribe self.handlers from signals."""
-        for signum, handler in self._previous_handlers.items():
-            signame = self.signals[signum]
-
-            if handler is None:
-                self.bus.log('Restoring %s handler to SIG_DFL.' % signame)
-                handler = _signal.SIG_DFL
-            else:
-                self.bus.log('Restoring %s handler %r.' % (signame, handler))
-
-            try:
-                our_handler = _signal.signal(signum, handler)
-                if our_handler is None:
-                    self.bus.log('Restored old %s handler %r, but our '
-                                 'handler was not registered.' %
-                                 (signame, handler), level=30)
-            except ValueError:
-                self.bus.log('Unable to restore %s handler %r.' %
-                             (signame, handler), level=40, traceback=True)
-
-    def set_handler(self, signal, listener=None):
-        """Subscribe a handler for the given signal (number or name).
-
-        If the optional 'listener' argument is provided, it will be
-        subscribed as a listener for the given signal's channel.
-
-        If the given signal name or number is not available on the current
-        platform, ValueError is raised.
-        """
-        if isinstance(signal, text_or_bytes):
-            signum = getattr(_signal, signal, None)
-            if signum is None:
-                raise ValueError('No such signal: %r' % signal)
-            signame = signal
-        else:
-            try:
-                signame = self.signals[signal]
-            except KeyError:
-                raise ValueError('No such signal: %r' % signal)
-            signum = signal
-
-        prev = _signal.signal(signum, self._handle_signal)
-        self._previous_handlers[signum] = prev
-
-        if listener is not None:
-            self.bus.log('Listening for %s.' % signame)
-            self.bus.subscribe(signame, listener)
-
-    def _handle_signal(self, signum=None, frame=None):
-        """Python signal handler (self.set_handler subscribes it for you)."""
-        signame = self.signals[signum]
-        self.bus.log('Caught signal %s.' % signame)
-        self.bus.publish(signame)
-
-    def handle_SIGHUP(self):
-        """Restart if daemonized, else exit."""
-        if self._is_daemonized():
-            self.bus.log('SIGHUP caught while daemonized. Restarting.')
-            self.bus.restart()
-        else:
-            # not daemonized (may be foreground or background)
-            self.bus.log('SIGHUP caught but not daemonized. Exiting.')
-            self.bus.exit()
-
-
-try:
-    import pwd
-    import grp
-except ImportError:
-    pwd, grp = None, None
-
-
-class DropPrivileges(SimplePlugin):
-
-    """Drop privileges. uid/gid arguments not available on Windows.
-
-    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):
-        SimplePlugin.__init__(self, bus)
-        self.finalized = False
-        self.uid = uid
-        self.gid = gid
-        self.umask = umask
-
-    @property
-    def uid(self):
-        """The uid under which to run. Availability: Unix."""
-        return self._uid
-
-    @uid.setter
-    def uid(self, val):
-        if val is not None:
-            if pwd is None:
-                self.bus.log('pwd module not available; ignoring uid.',
-                             level=30)
-                val = None
-            elif isinstance(val, text_or_bytes):
-                val = pwd.getpwnam(val)[2]
-        self._uid = val
-
-    @property
-    def gid(self):
-        """The gid under which to run. Availability: Unix."""
-        return self._gid
-
-    @gid.setter
-    def gid(self, val):
-        if val is not None:
-            if grp is None:
-                self.bus.log('grp module not available; ignoring gid.',
-                             level=30)
-                val = None
-            elif isinstance(val, text_or_bytes):
-                val = grp.getgrnam(val)[2]
-        self._gid = val
-
-    @property
-    def umask(self):
-        """The default permission mode for newly created files and directories.
-
-        Usually expressed in octal format, for example, ``0644``.
-        Availability: Unix, Windows.
-        """
-        return self._umask
-
-    @umask.setter
-    def umask(self, val):
-        if val is not None:
-            try:
-                os.umask
-            except AttributeError:
-                self.bus.log('umask function not available; ignoring umask.',
-                             level=30)
-                val = None
-        self._umask = val
-
-    def start(self):
-        # uid/gid
-        def current_ids():
-            """Return the current (uid, gid) if available."""
-            name, group = None, None
-            if pwd:
-                name = pwd.getpwuid(os.getuid())[0]
-            if grp:
-                group = grp.getgrgid(os.getgid())[0]
-            return name, group
-
-        if self.finalized:
-            if not (self.uid is None and self.gid is None):
-                self.bus.log('Already running as uid: %r gid: %r' %
-                             current_ids())
-        else:
-            if self.uid is None and self.gid is None:
-                if pwd or grp:
-                    self.bus.log('uid/gid not set', level=30)
-            else:
-                self.bus.log('Started as uid: %r gid: %r' % current_ids())
-                if self.gid is not None:
-                    os.setgid(self.gid)
-                    os.setgroups([])
-                if self.uid is not None:
-                    os.setuid(self.uid)
-                self.bus.log('Running as uid: %r gid: %r' % current_ids())
-
-        # umask
-        if self.finalized:
-            if self.umask is not None:
-                self.bus.log('umask already set to: %03o' % self.umask)
-        else:
-            if self.umask is None:
-                self.bus.log('umask not set', level=30)
-            else:
-                old_umask = os.umask(self.umask)
-                self.bus.log('umask old: %03o, new: %03o' %
-                             (old_umask, self.umask))
-
-        self.finalized = True
-    # This is slightly higher than the priority for server.start
-    # in order to facilitate the most common use: starting on a low
-    # port (which requires root) and then dropping to another user.
-    start.priority = 77
-
-
-class Daemonizer(SimplePlugin):
-
-    """Daemonize the running script.
-
-    Use this with a Web Site Process Bus via::
-
-        Daemonizer(bus).subscribe()
-
-    When this component finishes, the process is completely decoupled from
-    the parent environment. Please note that when this component is used,
-    the return code from the parent process will still be 0 if a startup
-    error occurs in the forked children. Errors in the initial daemonizing
-    process still return proper exit codes. Therefore, if you use this
-    plugin to daemonize, don't use the return code as an accurate indicator
-    of whether the process fully started. In fact, that return code only
-    indicates if the process successfully finished the first fork.
-    """
-
-    def __init__(self, bus, stdin='/dev/null', stdout='/dev/null',
-                 stderr='/dev/null'):
-        SimplePlugin.__init__(self, bus)
-        self.stdin = stdin
-        self.stdout = stdout
-        self.stderr = stderr
-        self.finalized = False
-
-    def start(self):
-        if self.finalized:
-            self.bus.log('Already deamonized.')
-
-        # forking has issues with threads:
-        # http://www.opengroup.org/onlinepubs/000095399/functions/fork.html
-        # "The general problem with making fork() work in a multi-threaded
-        #  world is what to do with all of the threads..."
-        # So we check for active threads:
-        if threading.activeCount() != 1:
-            self.bus.log('There are %r active threads. '
-                         'Daemonizing now may cause strange failures.' %
-                         threading.enumerate(), level=30)
-
-        self.daemonize(self.stdin, self.stdout, self.stderr, self.bus.log)
-
-        self.finalized = True
-    start.priority = 65
-
-    @staticmethod
-    def daemonize(
-            stdin='/dev/null', stdout='/dev/null', stderr='/dev/null',
-            logger=lambda msg: None):
-        # See http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
-        # (or http://www.faqs.org/faqs/unix-faq/programmer/faq/ section 1.7)
-        # and http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66012
-
-        # Finish up with the current stdout/stderr
-        sys.stdout.flush()
-        sys.stderr.flush()
-
-        error_tmpl = (
-            '{sys.argv[0]}: fork #{n} failed: ({exc.errno}) {exc.strerror}\n'
-        )
-
-        for fork in range(2):
-            msg = ['Forking once.', 'Forking twice.'][fork]
-            try:
-                pid = os.fork()
-                if pid > 0:
-                    # This is the parent; exit.
-                    logger(msg)
-                    os._exit(0)
-            except OSError as exc:
-                # Python raises OSError rather than returning negative numbers.
-                sys.exit(error_tmpl.format(sys=sys, exc=exc, n=fork + 1))
-            if fork == 0:
-                os.setsid()
-
-        os.umask(0)
-
-        si = open(stdin, 'r')
-        so = open(stdout, 'a+')
-        se = open(stderr, 'a+')
-
-        # os.dup2(fd, fd2) will close fd2 if necessary,
-        # so we don't explicitly close stdin/out/err.
-        # See http://docs.python.org/lib/os-fd-ops.html
-        os.dup2(si.fileno(), sys.stdin.fileno())
-        os.dup2(so.fileno(), sys.stdout.fileno())
-        os.dup2(se.fileno(), sys.stderr.fileno())
-
-        logger('Daemonized to PID: %s' % os.getpid())
-
-
-class PIDFile(SimplePlugin):
-
-    """Maintain a PID file via a WSPBus."""
-
-    def __init__(self, bus, pidfile):
-        SimplePlugin.__init__(self, bus)
-        self.pidfile = pidfile
-        self.finalized = False
-
-    def start(self):
-        pid = os.getpid()
-        if self.finalized:
-            self.bus.log('PID %r already written to %r.' % (pid, self.pidfile))
-        else:
-            open(self.pidfile, 'wb').write(ntob('%s\n' % pid, 'utf8'))
-            self.bus.log('PID %r written to %r.' % (pid, self.pidfile))
-            self.finalized = True
-    start.priority = 70
-
-    def exit(self):
-        try:
-            os.remove(self.pidfile)
-            self.bus.log('PID file removed: %r.' % self.pidfile)
-        except (KeyboardInterrupt, SystemExit):
-            raise
-        except Exception:
-            pass
-
-
-class PerpetualTimer(Timer):
-
-    """A responsive subclass of threading.Timer whose run() method repeats.
-
-    Use this timer only when you really need a very interruptible timer;
-    this checks its 'finished' condition up to 20 times a second, which can
-    results in pretty high CPU usage
-    """
-
-    def __init__(self, *args, **kwargs):
-        "Override parent constructor to allow 'bus' to be provided."
-        self.bus = kwargs.pop('bus', None)
-        super(PerpetualTimer, self).__init__(*args, **kwargs)
-
-    def run(self):
-        while True:
-            self.finished.wait(self.interval)
-            if self.finished.isSet():
-                return
-            try:
-                self.function(*self.args, **self.kwargs)
-            except Exception:
-                if self.bus:
-                    self.bus.log(
-                        'Error in perpetual timer thread function %r.' %
-                        self.function, level=40, traceback=True)
-                # Quit on first error to avoid massive logs.
-                raise
-
-
-class BackgroundTask(threading.Thread):
-
-    """A subclass of threading.Thread whose run() method repeats.
-
-    Use this class for most repeating tasks. It uses time.sleep() to wait
-    for each interval, which isn't very responsive; that is, even if you call
-    self.cancel(), you'll have to wait until the sleep() call finishes before
-    the thread stops. To compensate, it defaults to being daemonic, which means
-    it won't delay stopping the whole process.
-    """
-
-    def __init__(self, interval, function, args=[], kwargs={}, bus=None):
-        super(BackgroundTask, self).__init__()
-        self.interval = interval
-        self.function = function
-        self.args = args
-        self.kwargs = kwargs
-        self.running = False
-        self.bus = bus
-
-        # default to daemonic
-        self.daemon = True
-
-    def cancel(self):
-        self.running = False
-
-    def run(self):
-        self.running = True
-        while self.running:
-            time.sleep(self.interval)
-            if not self.running:
-                return
-            try:
-                self.function(*self.args, **self.kwargs)
-            except Exception:
-                if self.bus:
-                    self.bus.log('Error in background task thread function %r.'
-                                 % self.function, level=40, traceback=True)
-                # Quit on first error to avoid massive logs.
-                raise
-
-
-class Monitor(SimplePlugin):
-
-    """WSPBus listener to periodically run a callback in its own thread."""
-
-    callback = None
-    """The function to call at intervals."""
-
-    frequency = 60
-    """The time in seconds between callback runs."""
-
-    thread = None
-    """A :class:`BackgroundTask<cherrypy.process.plugins.BackgroundTask>`
-    thread.
-    """
-
-    def __init__(self, bus, callback, frequency=60, name=None):
-        SimplePlugin.__init__(self, bus)
-        self.callback = callback
-        self.frequency = frequency
-        self.thread = None
-        self.name = name
-
-    def start(self):
-        """Start our callback in its own background thread."""
-        if self.frequency > 0:
-            threadname = self.name or self.__class__.__name__
-            if self.thread is None:
-                self.thread = BackgroundTask(self.frequency, self.callback,
-                                             bus=self.bus)
-                self.thread.setName(threadname)
-                self.thread.start()
-                self.bus.log('Started monitor thread %r.' % threadname)
-            else:
-                self.bus.log('Monitor thread %r already started.' % threadname)
-    start.priority = 70
-
-    def stop(self):
-        """Stop our callback's background task thread."""
-        if self.thread is None:
-            self.bus.log('No thread running for %s.' %
-                         self.name or self.__class__.__name__)
-        else:
-            if self.thread is not threading.currentThread():
-                name = self.thread.getName()
-                self.thread.cancel()
-                if not self.thread.daemon:
-                    self.bus.log('Joining %r' % name)
-                    self.thread.join()
-                self.bus.log('Stopped thread %r.' % name)
-            self.thread = None
-
-    def graceful(self):
-        """Stop the callback's background task thread and restart it."""
-        self.stop()
-        self.start()
-
-
-class Autoreloader(Monitor):
-
-    """Monitor which re-executes the process when files change.
-
-    This :ref:`plugin<plugins>` restarts the process (via :func:`os.execv`)
-    if any of the files it monitors change (or is deleted). By default, the
-    autoreloader monitors all imported modules; you can add to the
-    set by adding to ``autoreload.files``::
-
-        cherrypy.engine.autoreload.files.add(myFile)
-
-    If there are imported files you do *not* wish to monitor, you can
-    adjust the ``match`` attribute, a regular expression. For example,
-    to stop monitoring cherrypy itself::
-
-        cherrypy.engine.autoreload.match = r'^(?!cherrypy).+'
-
-    Like all :class:`Monitor<cherrypy.process.plugins.Monitor>` plugins,
-    the autoreload plugin takes a ``frequency`` argument. The default is
-    1 second; that is, the autoreloader will examine files once each second.
-    """
-
-    files = None
-    """The set of files to poll for modifications."""
-
-    frequency = 1
-    """The interval in seconds at which to poll for modified files."""
-
-    match = '.*'
-    """A regular expression by which to match filenames."""
-
-    def __init__(self, bus, frequency=1, match='.*'):
-        self.mtimes = {}
-        self.files = set()
-        self.match = match
-        Monitor.__init__(self, bus, self.run, frequency)
-
-    def start(self):
-        """Start our own background task thread for self.run."""
-        if self.thread is None:
-            self.mtimes = {}
-        Monitor.start(self)
-    start.priority = 70
-
-    def sysfiles(self):
-        """Return a Set of sys.modules filenames to monitor."""
-        search_mod_names = filter(re.compile(self.match).match, sys.modules)
-        mods = map(sys.modules.get, search_mod_names)
-        return set(filter(None, map(self._file_for_module, mods)))
-
-    @classmethod
-    def _file_for_module(cls, module):
-        """Return the relevant file for the module."""
-        return (
-            cls._archive_for_zip_module(module)
-            or cls._file_for_file_module(module)
-        )
-
-    @staticmethod
-    def _archive_for_zip_module(module):
-        """Return the archive filename for the module if relevant."""
-        try:
-            return module.__loader__.archive
-        except AttributeError:
-            pass
-
-    @classmethod
-    def _file_for_file_module(cls, module):
-        """Return the file for the module."""
-        try:
-            return module.__file__ and cls._make_absolute(module.__file__)
-        except AttributeError:
-            pass
-
-    @staticmethod
-    def _make_absolute(filename):
-        """Ensure filename is absolute to avoid effect of os.chdir."""
-        return filename if os.path.isabs(filename) else (
-            os.path.normpath(os.path.join(_module__file__base, filename))
-        )
-
-    def run(self):
-        """Reload the process if registered files have been modified."""
-        for filename in self.sysfiles() | self.files:
-            if filename:
-                if filename.endswith('.pyc'):
-                    filename = filename[:-1]
-
-                oldtime = self.mtimes.get(filename, 0)
-                if oldtime is None:
-                    # Module with no .py file. Skip it.
-                    continue
-
-                try:
-                    mtime = os.stat(filename).st_mtime
-                except OSError:
-                    # Either a module with no .py file, or it's been deleted.
-                    mtime = None
-
-                if filename not in self.mtimes:
-                    # If a module has no .py file, this will be None.
-                    self.mtimes[filename] = mtime
-                else:
-                    if mtime is None or mtime > oldtime:
-                        # The file has been deleted or modified.
-                        self.bus.log('Restarting because %s changed.' %
-                                     filename)
-                        self.thread.cancel()
-                        self.bus.log('Stopped thread %r.' %
-                                     self.thread.getName())
-                        self.bus.restart()
-                        return
-
-
-class ThreadManager(SimplePlugin):
-
-    """Manager for HTTP request threads.
-
-    If you have control over thread creation and destruction, publish to
-    the 'acquire_thread' and 'release_thread' channels (for each thread).
-    This will register/unregister the current thread and publish to
-    'start_thread' and 'stop_thread' listeners in the bus as needed.
-
-    If threads are created and destroyed by code you do not control
-    (e.g., Apache), then, at the beginning of every HTTP request,
-    publish to 'acquire_thread' only. You should not publish to
-    'release_thread' in this case, since you do not know whether
-    the thread will be re-used or not. The bus will call
-    'stop_thread' listeners for you when it stops.
-    """
-
-    threads = None
-    """A map of {thread ident: index number} pairs."""
-
-    def __init__(self, bus):
-        self.threads = {}
-        SimplePlugin.__init__(self, bus)
-        self.bus.listeners.setdefault('acquire_thread', set())
-        self.bus.listeners.setdefault('start_thread', set())
-        self.bus.listeners.setdefault('release_thread', set())
-        self.bus.listeners.setdefault('stop_thread', set())
-
-    def acquire_thread(self):
-        """Run 'start_thread' listeners for the current thread.
-
-        If the current thread has already been seen, any 'start_thread'
-        listeners will not be run again.
-        """
-        thread_ident = _thread.get_ident()
-        if thread_ident not in self.threads:
-            # We can't just use get_ident as the thread ID
-            # because some platforms reuse thread ID's.
-            i = len(self.threads) + 1
-            self.threads[thread_ident] = i
-            self.bus.publish('start_thread', i)
-
-    def release_thread(self):
-        """Release the current thread and run 'stop_thread' listeners."""
-        thread_ident = _thread.get_ident()
-        i = self.threads.pop(thread_ident, None)
-        if i is not None:
-            self.bus.publish('stop_thread', i)
-
-    def stop(self):
-        """Release all threads and run all 'stop_thread' listeners."""
-        for thread_ident, i in self.threads.items():
-            self.bus.publish('stop_thread', i)
-        self.threads.clear()
-    graceful = stop
diff --git a/libraries/cherrypy/process/servers.py b/libraries/cherrypy/process/servers.py
deleted file mode 100644
index dcb34de6..00000000
--- a/libraries/cherrypy/process/servers.py
+++ /dev/null
@@ -1,416 +0,0 @@
-r"""
-Starting in CherryPy 3.1, cherrypy.server is implemented as an
-:ref:`Engine Plugin<plugins>`. It's an instance of
-:class:`cherrypy._cpserver.Server`, which is a subclass of
-:class:`cherrypy.process.servers.ServerAdapter`. The ``ServerAdapter`` class
-is designed to control other servers, as well.
-
-Multiple servers/ports
-======================
-
-If you need to start more than one HTTP server (to serve on multiple ports, or
-protocols, etc.), you can manually register each one and then start them all
-with engine.start::
-
-    s1 = ServerAdapter(
-        cherrypy.engine,
-        MyWSGIServer(host='0.0.0.0', port=80)
-    )
-    s2 = ServerAdapter(
-        cherrypy.engine,
-        another.HTTPServer(host='127.0.0.1', SSL=True)
-    )
-    s1.subscribe()
-    s2.subscribe()
-    cherrypy.engine.start()
-
-.. index:: SCGI
-
-FastCGI/SCGI
-============
-
-There are also Flup\ **F**\ CGIServer and Flup\ **S**\ CGIServer classes in
-:mod:`cherrypy.process.servers`. To start an fcgi server, for example,
-wrap an instance of it in a ServerAdapter::
-
-    addr = ('0.0.0.0', 4000)
-    f = servers.FlupFCGIServer(application=cherrypy.tree, bindAddress=addr)
-    s = servers.ServerAdapter(cherrypy.engine, httpserver=f, bind_addr=addr)
-    s.subscribe()
-
-The :doc:`cherryd</deployguide/cherryd>` startup script will do the above for
-you via its `-f` flag.
-Note that you need to download and install `flup <http://trac.saddi.com/flup>`_
-yourself, whether you use ``cherryd`` or not.
-
-.. _fastcgi:
-.. index:: FastCGI
-
-FastCGI
--------
-
-A very simple setup lets your cherry run with FastCGI.
-You just need the flup library,
-plus a running Apache server (with ``mod_fastcgi``) or lighttpd server.
-
-CherryPy code
-^^^^^^^^^^^^^
-
-hello.py::
-
-    #!/usr/bin/python
-    import cherrypy
-
-    class HelloWorld:
-        '''Sample request handler class.'''
-        @cherrypy.expose
-        def index(self):
-            return "Hello world!"
-
-    cherrypy.tree.mount(HelloWorld())
-    # CherryPy autoreload must be disabled for the flup server to work
-    cherrypy.config.update({'engine.autoreload.on':False})
-
-Then run :doc:`/deployguide/cherryd` with the '-f' arg::
-
-    cherryd -c <myconfig> -d -f -i hello.py
-
-Apache
-^^^^^^
-
-At the top level in httpd.conf::
-
-    FastCgiIpcDir /tmp
-    FastCgiServer /path/to/cherry.fcgi -idle-timeout 120 -processes 4
-
-And inside the relevant VirtualHost section::
-
-    # FastCGI config
-    AddHandler fastcgi-script .fcgi
-    ScriptAliasMatch (.*$) /path/to/cherry.fcgi$1
-
-Lighttpd
-^^^^^^^^
-
-For `Lighttpd <http://www.lighttpd.net/>`_ you can follow these
-instructions. Within ``lighttpd.conf`` make sure ``mod_fastcgi`` is
-active within ``server.modules``. Then, within your ``$HTTP["host"]``
-directive, configure your fastcgi script like the following::
-
-    $HTTP["url"] =~ "" {
-      fastcgi.server = (
-        "/" => (
-          "script.fcgi" => (
-            "bin-path" => "/path/to/your/script.fcgi",
-            "socket"          => "/tmp/script.sock",
-            "check-local"     => "disable",
-            "disable-time"    => 1,
-            "min-procs"       => 1,
-            "max-procs"       => 1, # adjust as needed
-          ),
-        ),
-      )
-    } # end of $HTTP["url"] =~ "^/"
-
-Please see `Lighttpd FastCGI Docs
-<http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModFastCGI>`_ for
-an explanation of the possible configuration options.
-"""
-
-import os
-import sys
-import time
-import warnings
-import contextlib
-
-import portend
-
-
-class Timeouts:
-    occupied = 5
-    free = 1
-
-
-class ServerAdapter(object):
-
-    """Adapter for an HTTP server.
-
-    If you need to start more than one HTTP server (to serve on multiple
-    ports, or protocols, etc.), you can manually register each one and then
-    start them all with bus.start::
-
-        s1 = ServerAdapter(bus, MyWSGIServer(host='0.0.0.0', port=80))
-        s2 = ServerAdapter(bus, another.HTTPServer(host='127.0.0.1', SSL=True))
-        s1.subscribe()
-        s2.subscribe()
-        bus.start()
-    """
-
-    def __init__(self, bus, httpserver=None, bind_addr=None):
-        self.bus = bus
-        self.httpserver = httpserver
-        self.bind_addr = bind_addr
-        self.interrupt = None
-        self.running = False
-
-    def subscribe(self):
-        self.bus.subscribe('start', self.start)
-        self.bus.subscribe('stop', self.stop)
-
-    def unsubscribe(self):
-        self.bus.unsubscribe('start', self.start)
-        self.bus.unsubscribe('stop', self.stop)
-
-    def start(self):
-        """Start the HTTP server."""
-        if self.running:
-            self.bus.log('Already serving on %s' % self.description)
-            return
-
-        self.interrupt = None
-        if not self.httpserver:
-            raise ValueError('No HTTP server has been created.')
-
-        if not os.environ.get('LISTEN_PID', None):
-            # Start the httpserver in a new thread.
-            if isinstance(self.bind_addr, tuple):
-                portend.free(*self.bind_addr, timeout=Timeouts.free)
-
-        import threading
-        t = threading.Thread(target=self._start_http_thread)
-        t.setName('HTTPServer ' + t.getName())
-        t.start()
-
-        self.wait()
-        self.running = True
-        self.bus.log('Serving on %s' % self.description)
-    start.priority = 75
-
-    @property
-    def description(self):
-        """
-        A description about where this server is bound.
-        """
-        if self.bind_addr is None:
-            on_what = 'unknown interface (dynamic?)'
-        elif isinstance(self.bind_addr, tuple):
-            on_what = self._get_base()
-        else:
-            on_what = 'socket file: %s' % self.bind_addr
-        return on_what
-
-    def _get_base(self):
-        if not self.httpserver:
-            return ''
-        host, port = self.bound_addr
-        if getattr(self.httpserver, 'ssl_adapter', None):
-            scheme = 'https'
-            if port != 443:
-                host += ':%s' % port
-        else:
-            scheme = 'http'
-            if port != 80:
-                host += ':%s' % port
-
-        return '%s://%s' % (scheme, host)
-
-    def _start_http_thread(self):
-        """HTTP servers MUST be running in new threads, so that the
-        main thread persists to receive KeyboardInterrupt's. If an
-        exception is raised in the httpserver's thread then it's
-        trapped here, and the bus (and therefore our httpserver)
-        are shut down.
-        """
-        try:
-            self.httpserver.start()
-        except KeyboardInterrupt:
-            self.bus.log('<Ctrl-C> hit: shutting down HTTP server')
-            self.interrupt = sys.exc_info()[1]
-            self.bus.exit()
-        except SystemExit:
-            self.bus.log('SystemExit raised: shutting down HTTP server')
-            self.interrupt = sys.exc_info()[1]
-            self.bus.exit()
-            raise
-        except Exception:
-            self.interrupt = sys.exc_info()[1]
-            self.bus.log('Error in HTTP server: shutting down',
-                         traceback=True, level=40)
-            self.bus.exit()
-            raise
-
-    def wait(self):
-        """Wait until the HTTP server is ready to receive requests."""
-        while not getattr(self.httpserver, 'ready', False):
-            if self.interrupt:
-                raise self.interrupt
-            time.sleep(.1)
-
-        # bypass check when LISTEN_PID is set
-        if os.environ.get('LISTEN_PID', None):
-            return
-
-        # bypass check when running via socket-activation
-        # (for socket-activation the port will be managed by systemd)
-        if not isinstance(self.bind_addr, tuple):
-            return
-
-        # wait for port to be occupied
-        with _safe_wait(*self.bound_addr):
-            portend.occupied(*self.bound_addr, timeout=Timeouts.occupied)
-
-    @property
-    def bound_addr(self):
-        """
-        The bind address, or if it's an ephemeral port and the
-        socket has been bound, return the actual port bound.
-        """
-        host, port = self.bind_addr
-        if port == 0 and self.httpserver.socket:
-            # Bound to ephemeral port. Get the actual port allocated.
-            port = self.httpserver.socket.getsockname()[1]
-        return host, port
-
-    def stop(self):
-        """Stop the HTTP server."""
-        if self.running:
-            # stop() MUST block until the server is *truly* stopped.
-            self.httpserver.stop()
-            # Wait for the socket to be truly freed.
-            if isinstance(self.bind_addr, tuple):
-                portend.free(*self.bound_addr, timeout=Timeouts.free)
-            self.running = False
-            self.bus.log('HTTP Server %s shut down' % self.httpserver)
-        else:
-            self.bus.log('HTTP Server %s already shut down' % self.httpserver)
-    stop.priority = 25
-
-    def restart(self):
-        """Restart the HTTP server."""
-        self.stop()
-        self.start()
-
-
-class FlupCGIServer(object):
-
-    """Adapter for a flup.server.cgi.WSGIServer."""
-
-    def __init__(self, *args, **kwargs):
-        self.args = args
-        self.kwargs = kwargs
-        self.ready = False
-
-    def start(self):
-        """Start the CGI server."""
-        # We have to instantiate the server class here because its __init__
-        # starts a threadpool. If we do it too early, daemonize won't work.
-        from flup.server.cgi import WSGIServer
-
-        self.cgiserver = WSGIServer(*self.args, **self.kwargs)
-        self.ready = True
-        self.cgiserver.run()
-
-    def stop(self):
-        """Stop the HTTP server."""
-        self.ready = False
-
-
-class FlupFCGIServer(object):
-
-    """Adapter for a flup.server.fcgi.WSGIServer."""
-
-    def __init__(self, *args, **kwargs):
-        if kwargs.get('bindAddress', None) is None:
-            import socket
-            if not hasattr(socket, 'fromfd'):
-                raise ValueError(
-                    'Dynamic FCGI server not available on this platform. '
-                    'You must use a static or external one by providing a '
-                    'legal bindAddress.')
-        self.args = args
-        self.kwargs = kwargs
-        self.ready = False
-
-    def start(self):
-        """Start the FCGI server."""
-        # We have to instantiate the server class here because its __init__
-        # starts a threadpool. If we do it too early, daemonize won't work.
-        from flup.server.fcgi import WSGIServer
-        self.fcgiserver = WSGIServer(*self.args, **self.kwargs)
-        # TODO: report this bug upstream to flup.
-        # If we don't set _oldSIGs on Windows, we get:
-        #   File "C:\Python24\Lib\site-packages\flup\server\threadedserver.py",
-        #   line 108, in run
-        #     self._restoreSignalHandlers()
-        #   File "C:\Python24\Lib\site-packages\flup\server\threadedserver.py",
-        #   line 156, in _restoreSignalHandlers
-        #     for signum,handler in self._oldSIGs:
-        #   AttributeError: 'WSGIServer' object has no attribute '_oldSIGs'
-        self.fcgiserver._installSignalHandlers = lambda: None
-        self.fcgiserver._oldSIGs = []
-        self.ready = True
-        self.fcgiserver.run()
-
-    def stop(self):
-        """Stop the HTTP server."""
-        # Forcibly stop the fcgi server main event loop.
-        self.fcgiserver._keepGoing = False
-        # Force all worker threads to die off.
-        self.fcgiserver._threadPool.maxSpare = (
-            self.fcgiserver._threadPool._idleCount)
-        self.ready = False
-
-
-class FlupSCGIServer(object):
-
-    """Adapter for a flup.server.scgi.WSGIServer."""
-
-    def __init__(self, *args, **kwargs):
-        self.args = args
-        self.kwargs = kwargs
-        self.ready = False
-
-    def start(self):
-        """Start the SCGI server."""
-        # We have to instantiate the server class here because its __init__
-        # starts a threadpool. If we do it too early, daemonize won't work.
-        from flup.server.scgi import WSGIServer
-        self.scgiserver = WSGIServer(*self.args, **self.kwargs)
-        # TODO: report this bug upstream to flup.
-        # If we don't set _oldSIGs on Windows, we get:
-        #   File "C:\Python24\Lib\site-packages\flup\server\threadedserver.py",
-        #   line 108, in run
-        #     self._restoreSignalHandlers()
-        #   File "C:\Python24\Lib\site-packages\flup\server\threadedserver.py",
-        #   line 156, in _restoreSignalHandlers
-        #     for signum,handler in self._oldSIGs:
-        #   AttributeError: 'WSGIServer' object has no attribute '_oldSIGs'
-        self.scgiserver._installSignalHandlers = lambda: None
-        self.scgiserver._oldSIGs = []
-        self.ready = True
-        self.scgiserver.run()
-
-    def stop(self):
-        """Stop the HTTP server."""
-        self.ready = False
-        # Forcibly stop the scgi server main event loop.
-        self.scgiserver._keepGoing = False
-        # Force all worker threads to die off.
-        self.scgiserver._threadPool.maxSpare = 0
-
-
-@contextlib.contextmanager
-def _safe_wait(host, port):
-    """
-    On systems where a loopback interface is not available and the
-    server is bound to all interfaces, it's difficult to determine
-    whether the server is in fact occupying the port. In this case,
-    just issue a warning and move on. See issue #1100.
-    """
-    try:
-        yield
-    except portend.Timeout:
-        if host == portend.client_host(host):
-            raise
-        msg = 'Unable to verify that the server is bound on %r' % port
-        warnings.warn(msg)
diff --git a/libraries/cherrypy/process/win32.py b/libraries/cherrypy/process/win32.py
deleted file mode 100644
index 096b0278..00000000
--- a/libraries/cherrypy/process/win32.py
+++ /dev/null
@@ -1,183 +0,0 @@
-"""Windows service. Requires pywin32."""
-
-import os
-import win32api
-import win32con
-import win32event
-import win32service
-import win32serviceutil
-
-from cherrypy.process import wspbus, plugins
-
-
-class ConsoleCtrlHandler(plugins.SimplePlugin):
-
-    """A WSPBus plugin for handling Win32 console events (like Ctrl-C)."""
-
-    def __init__(self, bus):
-        self.is_set = False
-        plugins.SimplePlugin.__init__(self, bus)
-
-    def start(self):
-        if self.is_set:
-            self.bus.log('Handler for console events already set.', level=40)
-            return
-
-        result = win32api.SetConsoleCtrlHandler(self.handle, 1)
-        if result == 0:
-            self.bus.log('Could not SetConsoleCtrlHandler (error %r)' %
-                         win32api.GetLastError(), level=40)
-        else:
-            self.bus.log('Set handler for console events.', level=40)
-            self.is_set = True
-
-    def stop(self):
-        if not self.is_set:
-            self.bus.log('Handler for console events already off.', level=40)
-            return
-
-        try:
-            result = win32api.SetConsoleCtrlHandler(self.handle, 0)
-        except ValueError:
-            # "ValueError: The object has not been registered"
-            result = 1
-
-        if result == 0:
-            self.bus.log('Could not remove SetConsoleCtrlHandler (error %r)' %
-                         win32api.GetLastError(), level=40)
-        else:
-            self.bus.log('Removed handler for console events.', level=40)
-            self.is_set = False
-
-    def handle(self, event):
-        """Handle console control events (like Ctrl-C)."""
-        if event in (win32con.CTRL_C_EVENT, win32con.CTRL_LOGOFF_EVENT,
-                     win32con.CTRL_BREAK_EVENT, win32con.CTRL_SHUTDOWN_EVENT,
-                     win32con.CTRL_CLOSE_EVENT):
-            self.bus.log('Console event %s: shutting down bus' % event)
-
-            # Remove self immediately so repeated Ctrl-C doesn't re-call it.
-            try:
-                self.stop()
-            except ValueError:
-                pass
-
-            self.bus.exit()
-            # 'First to return True stops the calls'
-            return 1
-        return 0
-
-
-class Win32Bus(wspbus.Bus):
-
-    """A Web Site Process Bus implementation for Win32.
-
-    Instead of time.sleep, this bus blocks using native win32event objects.
-    """
-
-    def __init__(self):
-        self.events = {}
-        wspbus.Bus.__init__(self)
-
-    def _get_state_event(self, state):
-        """Return a win32event for the given state (creating it if needed)."""
-        try:
-            return self.events[state]
-        except KeyError:
-            event = win32event.CreateEvent(None, 0, 0,
-                                           'WSPBus %s Event (pid=%r)' %
-                                           (state.name, os.getpid()))
-            self.events[state] = event
-            return event
-
-    @property
-    def state(self):
-        return self._state
-
-    @state.setter
-    def state(self, value):
-        self._state = value
-        event = self._get_state_event(value)
-        win32event.PulseEvent(event)
-
-    def wait(self, state, interval=0.1, channel=None):
-        """Wait for the given state(s), KeyboardInterrupt or SystemExit.
-
-        Since this class uses native win32event objects, the interval
-        argument is ignored.
-        """
-        if isinstance(state, (tuple, list)):
-            # Don't wait for an event that beat us to the punch ;)
-            if self.state not in state:
-                events = tuple([self._get_state_event(s) for s in state])
-                win32event.WaitForMultipleObjects(
-                    events, 0, win32event.INFINITE)
-        else:
-            # Don't wait for an event that beat us to the punch ;)
-            if self.state != state:
-                event = self._get_state_event(state)
-                win32event.WaitForSingleObject(event, win32event.INFINITE)
-
-
-class _ControlCodes(dict):
-
-    """Control codes used to "signal" a service via ControlService.
-
-    User-defined control codes are in the range 128-255. We generally use
-    the standard Python value for the Linux signal and add 128. Example:
-
-        >>> signal.SIGUSR1
-        10
-        control_codes['graceful'] = 128 + 10
-    """
-
-    def key_for(self, obj):
-        """For the given value, return its corresponding key."""
-        for key, val in self.items():
-            if val is obj:
-                return key
-        raise ValueError('The given object could not be found: %r' % obj)
-
-
-control_codes = _ControlCodes({'graceful': 138})
-
-
-def signal_child(service, command):
-    if command == 'stop':
-        win32serviceutil.StopService(service)
-    elif command == 'restart':
-        win32serviceutil.RestartService(service)
-    else:
-        win32serviceutil.ControlService(service, control_codes[command])
-
-
-class PyWebService(win32serviceutil.ServiceFramework):
-
-    """Python Web Service."""
-
-    _svc_name_ = 'Python Web Service'
-    _svc_display_name_ = 'Python Web Service'
-    _svc_deps_ = None        # sequence of service names on which this depends
-    _exe_name_ = 'pywebsvc'
-    _exe_args_ = None        # Default to no arguments
-
-    # Only exists on Windows 2000 or later, ignored on windows NT
-    _svc_description_ = 'Python Web Service'
-
-    def SvcDoRun(self):
-        from cherrypy import process
-        process.bus.start()
-        process.bus.block()
-
-    def SvcStop(self):
-        from cherrypy import process
-        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
-        process.bus.exit()
-
-    def SvcOther(self, control):
-        from cherrypy import process
-        process.bus.publish(control_codes.key_for(control))
-
-
-if __name__ == '__main__':
-    win32serviceutil.HandleCommandLine(PyWebService)
diff --git a/libraries/cherrypy/process/wspbus.py b/libraries/cherrypy/process/wspbus.py
deleted file mode 100644
index 39ac45bf..00000000
--- a/libraries/cherrypy/process/wspbus.py
+++ /dev/null
@@ -1,590 +0,0 @@
-r"""An implementation of the Web Site Process Bus.
-
-This module is completely standalone, depending only on the stdlib.
-
-Web Site Process Bus
---------------------
-
-A Bus object is used to contain and manage site-wide behavior:
-daemonization, HTTP server start/stop, process reload, signal handling,
-drop privileges, PID file management, logging for all of these,
-and many more.
-
-In addition, a Bus object provides a place for each web framework
-to register code that runs in response to site-wide events (like
-process start and stop), or which controls or otherwise interacts with
-the site-wide components mentioned above. For example, a framework which
-uses file-based templates would add known template filenames to an
-autoreload component.
-
-Ideally, a Bus object will be flexible enough to be useful in a variety
-of invocation scenarios:
-
- 1. The deployer starts a site from the command line via a
-    framework-neutral deployment script; applications from multiple frameworks
-    are mixed in a single site. Command-line arguments and configuration
-    files are used to define site-wide components such as the HTTP server,
-    WSGI component graph, autoreload behavior, signal handling, etc.
- 2. The deployer starts a site via some other process, such as Apache;
-    applications from multiple frameworks are mixed in a single site.
-    Autoreload and signal handling (from Python at least) are disabled.
- 3. The deployer starts a site via a framework-specific mechanism;
-    for example, when running tests, exploring tutorials, or deploying
-    single applications from a single framework. The framework controls
-    which site-wide components are enabled as it sees fit.
-
-The Bus object in this package uses topic-based publish-subscribe
-messaging to accomplish all this. A few topic channels are built in
-('start', 'stop', 'exit', 'graceful', 'log', and 'main'). Frameworks and
-site containers are free to define their own. If a message is sent to a
-channel that has not been defined or has no listeners, there is no effect.
-
-In general, there should only ever be a single Bus object per process.
-Frameworks and site containers share a single Bus object by publishing
-messages and subscribing listeners.
-
-The Bus object works as a finite state machine which models the current
-state of the process. Bus methods move it from one state to another;
-those methods then publish to subscribed listeners on the channel for
-the new state.::
-
-                        O
-                        |
-                        V
-       STOPPING --> STOPPED --> EXITING -> X
-          A   A         |
-          |    \___     |
-          |        \    |
-          |         V   V
-        STARTED <-- STARTING
-
-"""
-
-import atexit
-
-try:
-    import ctypes
-except (ImportError, MemoryError):
-    """Google AppEngine is shipped without ctypes
-
-    :seealso: http://stackoverflow.com/a/6523777/70170
-    """
-    ctypes = None
-
-import operator
-import os
-import sys
-import threading
-import time
-import traceback as _traceback
-import warnings
-import subprocess
-import functools
-
-import six
-
-
-# Here I save the value of os.getcwd(), which, if I am imported early enough,
-# will be the directory from which the startup script was run.  This is needed
-# by _do_execv(), to change back to the original directory before execv()ing a
-# new process.  This is a defense against the application having changed the
-# current working directory (which could make sys.executable "not found" if
-# sys.executable is a relative-path, and/or cause other problems).
-_startup_cwd = os.getcwd()
-
-
-class ChannelFailures(Exception):
-    """Exception raised during errors on Bus.publish()."""
-
-    delimiter = '\n'
-
-    def __init__(self, *args, **kwargs):
-        """Initialize ChannelFailures errors wrapper."""
-        super(ChannelFailures, self).__init__(*args, **kwargs)
-        self._exceptions = list()
-
-    def handle_exception(self):
-        """Append the current exception to self."""
-        self._exceptions.append(sys.exc_info()[1])
-
-    def get_instances(self):
-        """Return a list of seen exception instances."""
-        return self._exceptions[:]
-
-    def __str__(self):
-        """Render the list of errors, which happened in channel."""
-        exception_strings = map(repr, self.get_instances())
-        return self.delimiter.join(exception_strings)
-
-    __repr__ = __str__
-
-    def __bool__(self):
-        """Determine whether any error happened in channel."""
-        return bool(self._exceptions)
-    __nonzero__ = __bool__
-
-# Use a flag to indicate the state of the bus.
-
-
-class _StateEnum(object):
-
-    class State(object):
-        name = None
-
-        def __repr__(self):
-            return 'states.%s' % self.name
-
-    def __setattr__(self, key, value):
-        if isinstance(value, self.State):
-            value.name = key
-        object.__setattr__(self, key, value)
-
-
-states = _StateEnum()
-states.STOPPED = states.State()
-states.STARTING = states.State()
-states.STARTED = states.State()
-states.STOPPING = states.State()
-states.EXITING = states.State()
-
-
-try:
-    import fcntl
-except ImportError:
-    max_files = 0
-else:
-    try:
-        max_files = os.sysconf('SC_OPEN_MAX')
-    except AttributeError:
-        max_files = 1024
-
-
-class Bus(object):
-    """Process state-machine and messenger for HTTP site deployment.
-
-    All listeners for a given channel are guaranteed to be called even
-    if others at the same channel fail. Each failure is logged, but
-    execution proceeds on to the next listener. The only way to stop all
-    processing from inside a listener is to raise SystemExit and stop the
-    whole server.
-    """
-
-    states = states
-    state = states.STOPPED
-    execv = False
-    max_cloexec_files = max_files
-
-    def __init__(self):
-        """Initialize pub/sub bus."""
-        self.execv = False
-        self.state = states.STOPPED
-        channels = 'start', 'stop', 'exit', 'graceful', 'log', 'main'
-        self.listeners = dict(
-            (channel, set())
-            for channel in channels
-        )
-        self._priorities = {}
-
-    def subscribe(self, channel, callback=None, priority=None):
-        """Add the given callback at the given channel (if not present).
-
-        If callback is None, return a partial suitable for decorating
-        the callback.
-        """
-        if callback is None:
-            return functools.partial(
-                self.subscribe,
-                channel,
-                priority=priority,
-            )
-
-        ch_listeners = self.listeners.setdefault(channel, set())
-        ch_listeners.add(callback)
-
-        if priority is None:
-            priority = getattr(callback, 'priority', 50)
-        self._priorities[(channel, callback)] = priority
-
-    def unsubscribe(self, channel, callback):
-        """Discard the given callback (if present)."""
-        listeners = self.listeners.get(channel)
-        if listeners and callback in listeners:
-            listeners.discard(callback)
-            del self._priorities[(channel, callback)]
-
-    def publish(self, channel, *args, **kwargs):
-        """Return output of all subscribers for the given channel."""
-        if channel not in self.listeners:
-            return []
-
-        exc = ChannelFailures()
-        output = []
-
-        raw_items = (
-            (self._priorities[(channel, listener)], listener)
-            for listener in self.listeners[channel]
-        )
-        items = sorted(raw_items, key=operator.itemgetter(0))
-        for priority, listener in items:
-            try:
-                output.append(listener(*args, **kwargs))
-            except KeyboardInterrupt:
-                raise
-            except SystemExit:
-                e = sys.exc_info()[1]
-                # If we have previous errors ensure the exit code is non-zero
-                if exc and e.code == 0:
-                    e.code = 1
-                raise
-            except Exception:
-                exc.handle_exception()
-                if channel == 'log':
-                    # Assume any further messages to 'log' will fail.
-                    pass
-                else:
-                    self.log('Error in %r listener %r' % (channel, listener),
-                             level=40, traceback=True)
-        if exc:
-            raise exc
-        return output
-
-    def _clean_exit(self):
-        """Assert that the Bus is not running in atexit handler callback."""
-        if self.state != states.EXITING:
-            warnings.warn(
-                'The main thread is exiting, but the Bus is in the %r state; '
-                'shutting it down automatically now. You must either call '
-                'bus.block() after start(), or call bus.exit() before the '
-                'main thread exits.' % self.state, RuntimeWarning)
-            self.exit()
-
-    def start(self):
-        """Start all services."""
-        atexit.register(self._clean_exit)
-
-        self.state = states.STARTING
-        self.log('Bus STARTING')
-        try:
-            self.publish('start')
-            self.state = states.STARTED
-            self.log('Bus STARTED')
-        except (KeyboardInterrupt, SystemExit):
-            raise
-        except Exception:
-            self.log('Shutting down due to error in start listener:',
-                     level=40, traceback=True)
-            e_info = sys.exc_info()[1]
-            try:
-                self.exit()
-            except Exception:
-                # Any stop/exit errors will be logged inside publish().
-                pass
-            # Re-raise the original error
-            raise e_info
-
-    def exit(self):
-        """Stop all services and prepare to exit the process."""
-        exitstate = self.state
-        EX_SOFTWARE = 70
-        try:
-            self.stop()
-
-            self.state = states.EXITING
-            self.log('Bus EXITING')
-            self.publish('exit')
-            # This isn't strictly necessary, but it's better than seeing
-            # "Waiting for child threads to terminate..." and then nothing.
-            self.log('Bus EXITED')
-        except Exception:
-            # This method is often called asynchronously (whether thread,
-            # signal handler, console handler, or atexit handler), so we
-            # can't just let exceptions propagate out unhandled.
-            # Assume it's been logged and just die.
-            os._exit(EX_SOFTWARE)
-
-        if exitstate == states.STARTING:
-            # exit() was called before start() finished, possibly due to
-            # Ctrl-C because a start listener got stuck. In this case,
-            # we could get stuck in a loop where Ctrl-C never exits the
-            # process, so we just call os.exit here.
-            os._exit(EX_SOFTWARE)
-
-    def restart(self):
-        """Restart the process (may close connections).
-
-        This method does not restart the process from the calling thread;
-        instead, it stops the bus and asks the main thread to call execv.
-        """
-        self.execv = True
-        self.exit()
-
-    def graceful(self):
-        """Advise all services to reload."""
-        self.log('Bus graceful')
-        self.publish('graceful')
-
-    def block(self, interval=0.1):
-        """Wait for the EXITING state, KeyboardInterrupt or SystemExit.
-
-        This function is intended to be called only by the main thread.
-        After waiting for the EXITING state, it also waits for all threads
-        to terminate, and then calls os.execv if self.execv is True. This
-        design allows another thread to call bus.restart, yet have the main
-        thread perform the actual execv call (required on some platforms).
-        """
-        try:
-            self.wait(states.EXITING, interval=interval, channel='main')
-        except (KeyboardInterrupt, IOError):
-            # The time.sleep call might raise
-            # "IOError: [Errno 4] Interrupted function call" on KBInt.
-            self.log('Keyboard Interrupt: shutting down bus')
-            self.exit()
-        except SystemExit:
-            self.log('SystemExit raised: shutting down bus')
-            self.exit()
-            raise
-
-        # Waiting for ALL child threads to finish is necessary on OS X.
-        # See https://github.com/cherrypy/cherrypy/issues/581.
-        # It's also good to let them all shut down before allowing
-        # the main thread to call atexit handlers.
-        # See https://github.com/cherrypy/cherrypy/issues/751.
-        self.log('Waiting for child threads to terminate...')
-        for t in threading.enumerate():
-            # Validate the we're not trying to join the MainThread
-            # that will cause a deadlock and the case exist when
-            # implemented as a windows service and in any other case
-            # that another thread executes cherrypy.engine.exit()
-            if (
-                    t != threading.currentThread() and
-                    not isinstance(t, threading._MainThread) and
-                    # Note that any dummy (external) threads are
-                    # always daemonic.
-                    not t.daemon
-            ):
-                self.log('Waiting for thread %s.' % t.getName())
-                t.join()
-
-        if self.execv:
-            self._do_execv()
-
-    def wait(self, state, interval=0.1, channel=None):
-        """Poll for the given state(s) at intervals; publish to channel."""
-        if isinstance(state, (tuple, list)):
-            states = state
-        else:
-            states = [state]
-
-        while self.state not in states:
-            time.sleep(interval)
-            self.publish(channel)
-
-    def _do_execv(self):
-        """Re-execute the current process.
-
-        This must be called from the main thread, because certain platforms
-        (OS X) don't allow execv to be called in a child thread very well.
-        """
-        try:
-            args = self._get_true_argv()
-        except NotImplementedError:
-            """It's probably win32 or GAE"""
-            args = [sys.executable] + self._get_interpreter_argv() + sys.argv
-
-        self.log('Re-spawning %s' % ' '.join(args))
-
-        self._extend_pythonpath(os.environ)
-
-        if sys.platform[:4] == 'java':
-            from _systemrestart import SystemRestart
-            raise SystemRestart
-        else:
-            if sys.platform == 'win32':
-                args = ['"%s"' % arg for arg in args]
-
-            os.chdir(_startup_cwd)
-            if self.max_cloexec_files:
-                self._set_cloexec()
-            os.execv(sys.executable, args)
-
-    @staticmethod
-    def _get_interpreter_argv():
-        """Retrieve current Python interpreter's arguments.
-
-        Returns empty tuple in case of frozen mode, uses built-in arguments
-        reproduction function otherwise.
-
-        Frozen mode is possible for the app has been packaged into a binary
-        executable using py2exe. In this case the interpreter's arguments are
-        already built-in into that executable.
-
-        :seealso: https://github.com/cherrypy/cherrypy/issues/1526
-        Ref: https://pythonhosted.org/PyInstaller/runtime-information.html
-        """
-        return ([]
-                if getattr(sys, 'frozen', False)
-                else subprocess._args_from_interpreter_flags())
-
-    @staticmethod
-    def _get_true_argv():
-        """Retrieve all real arguments of the python interpreter.
-
-        ...even those not listed in ``sys.argv``
-
-        :seealso: http://stackoverflow.com/a/28338254/595220
-        :seealso: http://stackoverflow.com/a/6683222/595220
-        :seealso: http://stackoverflow.com/a/28414807/595220
-        """
-        try:
-            char_p = ctypes.c_char_p if six.PY2 else ctypes.c_wchar_p
-
-            argv = ctypes.POINTER(char_p)()
-            argc = ctypes.c_int()
-
-            ctypes.pythonapi.Py_GetArgcArgv(
-                ctypes.byref(argc),
-                ctypes.byref(argv),
-            )
-
-            _argv = argv[:argc.value]
-
-            # The code below is trying to correctly handle special cases.
-            # `-c`'s argument interpreted by Python itself becomes `-c` as
-            # well. Same applies to `-m`. This snippet is trying to survive
-            # at least the case with `-m`
-            # Ref: https://github.com/cherrypy/cherrypy/issues/1545
-            # Ref: python/cpython@418baf9
-            argv_len, is_command, is_module = len(_argv), False, False
-
-            try:
-                m_ind = _argv.index('-m')
-                if m_ind < argv_len - 1 and _argv[m_ind + 1] in ('-c', '-m'):
-                    """
-                    In some older Python versions `-m`'s argument may be
-                    substituted with `-c`, not `-m`
-                    """
-                    is_module = True
-            except (IndexError, ValueError):
-                m_ind = None
-
-            try:
-                c_ind = _argv.index('-c')
-                if c_ind < argv_len - 1 and _argv[c_ind + 1] == '-c':
-                    is_command = True
-            except (IndexError, ValueError):
-                c_ind = None
-
-            if is_module:
-                """It's containing `-m -m` sequence of arguments"""
-                if is_command and c_ind < m_ind:
-                    """There's `-c -c` before `-m`"""
-                    raise RuntimeError(
-                        "Cannot reconstruct command from '-c'. Ref: "
-                        'https://github.com/cherrypy/cherrypy/issues/1545')
-                # Survive module argument here
-                original_module = sys.argv[0]
-                if not os.access(original_module, os.R_OK):
-                    """There's no such module exist"""
-                    raise AttributeError(
-                        "{} doesn't seem to be a module "
-                        'accessible by current user'.format(original_module))
-                del _argv[m_ind:m_ind + 2]  # remove `-m -m`
-                # ... and substitute it with the original module path:
-                _argv.insert(m_ind, original_module)
-            elif is_command:
-                """It's containing just `-c -c` sequence of arguments"""
-                raise RuntimeError(
-                    "Cannot reconstruct command from '-c'. "
-                    'Ref: https://github.com/cherrypy/cherrypy/issues/1545')
-        except AttributeError:
-            """It looks Py_GetArgcArgv is completely absent in some environments
-
-            It is known, that there's no Py_GetArgcArgv in MS Windows and
-            ``ctypes`` module is completely absent in Google AppEngine
-
-            :seealso: https://github.com/cherrypy/cherrypy/issues/1506
-            :seealso: https://github.com/cherrypy/cherrypy/issues/1512
-            :ref: http://bit.ly/2gK6bXK
-            """
-            raise NotImplementedError
-        else:
-            return _argv
-
-    @staticmethod
-    def _extend_pythonpath(env):
-        """Prepend current working dir to PATH environment variable if needed.
-
-        If sys.path[0] is an empty string, the interpreter was likely
-        invoked with -m and the effective path is about to change on
-        re-exec.  Add the current directory to $PYTHONPATH to ensure
-        that the new process sees the same path.
-
-        This issue cannot be addressed in the general case because
-        Python cannot reliably reconstruct the
-        original command line (http://bugs.python.org/issue14208).
-
-        (This idea filched from tornado.autoreload)
-        """
-        path_prefix = '.' + os.pathsep
-        existing_path = env.get('PYTHONPATH', '')
-        needs_patch = (
-            sys.path[0] == '' and
-            not existing_path.startswith(path_prefix)
-        )
-
-        if needs_patch:
-            env['PYTHONPATH'] = path_prefix + existing_path
-
-    def _set_cloexec(self):
-        """Set the CLOEXEC flag on all open files (except stdin/out/err).
-
-        If self.max_cloexec_files is an integer (the default), then on
-        platforms which support it, it represents the max open files setting
-        for the operating system. This function will be called just before
-        the process is restarted via os.execv() to prevent open files
-        from persisting into the new process.
-
-        Set self.max_cloexec_files to 0 to disable this behavior.
-        """
-        for fd in range(3, self.max_cloexec_files):  # skip stdin/out/err
-            try:
-                flags = fcntl.fcntl(fd, fcntl.F_GETFD)
-            except IOError:
-                continue
-            fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
-
-    def stop(self):
-        """Stop all services."""
-        self.state = states.STOPPING
-        self.log('Bus STOPPING')
-        self.publish('stop')
-        self.state = states.STOPPED
-        self.log('Bus STOPPED')
-
-    def start_with_callback(self, func, args=None, kwargs=None):
-        """Start 'func' in a new thread T, then start self (and return T)."""
-        if args is None:
-            args = ()
-        if kwargs is None:
-            kwargs = {}
-        args = (func,) + args
-
-        def _callback(func, *a, **kw):
-            self.wait(states.STARTED)
-            func(*a, **kw)
-        t = threading.Thread(target=_callback, args=args, kwargs=kwargs)
-        t.setName('Bus Callback ' + t.getName())
-        t.start()
-
-        self.start()
-
-        return t
-
-    def log(self, msg='', level=20, traceback=False):
-        """Log the given message. Append the last traceback if requested."""
-        if traceback:
-            msg += '\n' + ''.join(_traceback.format_exception(*sys.exc_info()))
-        self.publish('log', msg, level)
-
-
-bus = Bus()
diff --git a/libraries/cherrypy/scaffold/__init__.py b/libraries/cherrypy/scaffold/__init__.py
deleted file mode 100644
index bcddba2d..00000000
--- a/libraries/cherrypy/scaffold/__init__.py
+++ /dev/null
@@ -1,63 +0,0 @@
-"""<MyProject>, a CherryPy application.
-
-Use this as a base for creating new CherryPy applications. When you want
-to make a new app, copy and paste this folder to some other location
-(maybe site-packages) and rename it to the name of your project,
-then tweak as desired.
-
-Even before any tweaking, this should serve a few demonstration pages.
-Change to this directory and run:
-
-    cherryd -c site.conf
-
-"""
-
-import cherrypy
-from cherrypy import tools, url
-
-import os
-local_dir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-
-@cherrypy.config(**{'tools.log_tracebacks.on': True})
-class Root:
-    """Declaration of the CherryPy app URI structure."""
-
-    @cherrypy.expose
-    def index(self):
-        """Render HTML-template at the root path of the web-app."""
-        return """<html>
-<body>Try some <a href='%s?a=7'>other</a> path,
-or a <a href='%s?n=14'>default</a> path.<br />
-Or, just look at the pretty picture:<br />
-<img src='%s' />
-</body></html>""" % (url('other'), url('else'),
-                     url('files/made_with_cherrypy_small.png'))
-
-    @cherrypy.expose
-    def default(self, *args, **kwargs):
-        """Render catch-all args and kwargs."""
-        return 'args: %s kwargs: %s' % (args, kwargs)
-
-    @cherrypy.expose
-    def other(self, a=2, b='bananas', c=None):
-        """Render number of fruits based on third argument."""
-        cherrypy.response.headers['Content-Type'] = 'text/plain'
-        if c is None:
-            return 'Have %d %s.' % (int(a), b)
-        else:
-            return 'Have %d %s, %s.' % (int(a), b, c)
-
-    files = tools.staticdir.handler(
-        section='/files',
-        dir=os.path.join(local_dir, 'static'),
-        # Ignore .php files, etc.
-                match=r'\.(css|gif|html?|ico|jpe?g|js|png|swf|xml)$',
-    )
-
-
-root = Root()
-
-# Uncomment the following to use your own favicon instead of CP's default.
-# favicon_path = os.path.join(local_dir, "favicon.ico")
-# root.favicon_ico = tools.staticfile.handler(filename=favicon_path)
diff --git a/libraries/cherrypy/scaffold/apache-fcgi.conf b/libraries/cherrypy/scaffold/apache-fcgi.conf
deleted file mode 100644
index 6e4f144c..00000000
--- a/libraries/cherrypy/scaffold/apache-fcgi.conf
+++ /dev/null
@@ -1,22 +0,0 @@
-# Apache2 server conf file for using CherryPy with mod_fcgid.
-
-# This doesn't have to be "C:/", but it has to be a directory somewhere, and
-# MUST match the directory used in the FastCgiExternalServer directive, below.
-DocumentRoot "C:/"
-
-ServerName 127.0.0.1
-Listen 80
-LoadModule fastcgi_module modules/mod_fastcgi.dll
-LoadModule rewrite_module modules/mod_rewrite.so
-
-Options ExecCGI
-SetHandler fastcgi-script
-RewriteEngine On
-# Send requests for any URI to our fastcgi handler.
-RewriteRule ^(.*)$ /fastcgi.pyc [L]
-
-# The FastCgiExternalServer directive defines filename as an external FastCGI application.
-# If filename does not begin with a slash (/) then it is assumed to be relative to the ServerRoot.
-# The filename does not have to exist in the local filesystem. URIs that Apache resolves to this
-# filename will be handled by this external FastCGI application.
-FastCgiExternalServer "C:/fastcgi.pyc" -host 127.0.0.1:8088
diff --git a/libraries/cherrypy/scaffold/example.conf b/libraries/cherrypy/scaffold/example.conf
deleted file mode 100644
index 63250fe3..00000000
--- a/libraries/cherrypy/scaffold/example.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-[/]
-log.error_file: "error.log"
-log.access_file: "access.log"
diff --git a/libraries/cherrypy/scaffold/site.conf b/libraries/cherrypy/scaffold/site.conf
deleted file mode 100644
index 6ed38983..00000000
--- a/libraries/cherrypy/scaffold/site.conf
+++ /dev/null
@@ -1,14 +0,0 @@
-[global]
-# Uncomment this when you're done developing
-#environment: "production"
-
-server.socket_host: "0.0.0.0"
-server.socket_port: 8088
-
-# Uncomment the following lines to run on HTTPS at the same time
-#server.2.socket_host: "0.0.0.0"
-#server.2.socket_port: 8433
-#server.2.ssl_certificate: '../test/test.pem'
-#server.2.ssl_private_key: '../test/test.pem'
-
-tree.myapp: cherrypy.Application(scaffold.root, "/", "example.conf")
diff --git a/libraries/cherrypy/scaffold/static/made_with_cherrypy_small.png b/libraries/cherrypy/scaffold/static/made_with_cherrypy_small.png
deleted file mode 100644
index 724f9d72..00000000
Binary files a/libraries/cherrypy/scaffold/static/made_with_cherrypy_small.png and /dev/null differ
diff --git a/libraries/cherrypy/test/__init__.py b/libraries/cherrypy/test/__init__.py
deleted file mode 100644
index 068382be..00000000
--- a/libraries/cherrypy/test/__init__.py
+++ /dev/null
@@ -1,24 +0,0 @@
-"""
-Regression test suite for CherryPy.
-"""
-
-import os
-import sys
-
-
-def newexit():
-    os._exit(1)
-
-
-def setup():
-    # We want to monkey patch sys.exit so that we can get some
-    # information about where exit is being called.
-    newexit._old = sys.exit
-    sys.exit = newexit
-
-
-def teardown():
-    try:
-        sys.exit = sys.exit._old
-    except AttributeError:
-        sys.exit = sys._exit
diff --git a/libraries/cherrypy/test/_test_decorators.py b/libraries/cherrypy/test/_test_decorators.py
deleted file mode 100644
index 74832e40..00000000
--- a/libraries/cherrypy/test/_test_decorators.py
+++ /dev/null
@@ -1,39 +0,0 @@
-"""Test module for the @-decorator syntax, which is version-specific"""
-
-import cherrypy
-from cherrypy import expose, tools
-
-
-class ExposeExamples(object):
-
-    @expose
-    def no_call(self):
-        return 'Mr E. R. Bradshaw'
-
-    @expose()
-    def call_empty(self):
-        return 'Mrs. B.J. Smegma'
-
-    @expose('call_alias')
-    def nesbitt(self):
-        return 'Mr Nesbitt'
-
-    @expose(['alias1', 'alias2'])
-    def andrews(self):
-        return 'Mr Ken Andrews'
-
-    @expose(alias='alias3')
-    def watson(self):
-        return 'Mr. and Mrs. Watson'
-
-
-class ToolExamples(object):
-
-    @expose
-    # This is here to demonstrate that using the config decorator
-    # does not overwrite other config attributes added by the Tool
-    # decorator (in this case response_headers).
-    @cherrypy.config(**{'response.stream': True})
-    @tools.response_headers(headers=[('Content-Type', 'application/data')])
-    def blah(self):
-        yield b'blah'
diff --git a/libraries/cherrypy/test/_test_states_demo.py b/libraries/cherrypy/test/_test_states_demo.py
deleted file mode 100644
index a49407ba..00000000
--- a/libraries/cherrypy/test/_test_states_demo.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import os
-import sys
-import time
-
-import cherrypy
-
-starttime = time.time()
-
-
-class Root:
-
-    @cherrypy.expose
-    def index(self):
-        return 'Hello World'
-
-    @cherrypy.expose
-    def mtimes(self):
-        return repr(cherrypy.engine.publish('Autoreloader', 'mtimes'))
-
-    @cherrypy.expose
-    def pid(self):
-        return str(os.getpid())
-
-    @cherrypy.expose
-    def start(self):
-        return repr(starttime)
-
-    @cherrypy.expose
-    def exit(self):
-        # This handler might be called before the engine is STARTED if an
-        # HTTP worker thread handles it before the HTTP server returns
-        # control to engine.start. We avoid that race condition here
-        # by waiting for the Bus to be STARTED.
-        cherrypy.engine.wait(state=cherrypy.engine.states.STARTED)
-        cherrypy.engine.exit()
-
-
-@cherrypy.engine.subscribe('start', priority=100)
-def unsub_sig():
-    cherrypy.log('unsubsig: %s' % cherrypy.config.get('unsubsig', False))
-    if cherrypy.config.get('unsubsig', False):
-        cherrypy.log('Unsubscribing the default cherrypy signal handler')
-        cherrypy.engine.signal_handler.unsubscribe()
-    try:
-        from signal import signal, SIGTERM
-    except ImportError:
-        pass
-    else:
-        def old_term_handler(signum=None, frame=None):
-            cherrypy.log('I am an old SIGTERM handler.')
-            sys.exit(0)
-        cherrypy.log('Subscribing the new one.')
-        signal(SIGTERM, old_term_handler)
-
-
-@cherrypy.engine.subscribe('start', priority=6)
-def starterror():
-    if cherrypy.config.get('starterror', False):
-        1 / 0
-
-
-@cherrypy.engine.subscribe('start', priority=6)
-def log_test_case_name():
-    if cherrypy.config.get('test_case_name', False):
-        cherrypy.log('STARTED FROM: %s' %
-                     cherrypy.config.get('test_case_name'))
-
-
-cherrypy.tree.mount(Root(), '/', {'/': {}})
diff --git a/libraries/cherrypy/test/benchmark.py b/libraries/cherrypy/test/benchmark.py
deleted file mode 100644
index 44dfeff1..00000000
--- a/libraries/cherrypy/test/benchmark.py
+++ /dev/null
@@ -1,425 +0,0 @@
-"""CherryPy Benchmark Tool
-
-    Usage:
-        benchmark.py [options]
-
-    --null:        use a null Request object (to bench the HTTP server only)
-    --notests:     start the server but do not run the tests; this allows
-                   you to check the tested pages with a browser
-    --help:        show this help message
-    --cpmodpy:     run tests via apache on 54583 (with the builtin _cpmodpy)
-    --modpython:   run tests via apache on 54583 (with modpython_gateway)
-    --ab=path:     Use the ab script/executable at 'path' (see below)
-    --apache=path: Use the apache script/exe at 'path' (see below)
-
-    To run the benchmarks, the Apache Benchmark tool "ab" must either be on
-    your system path, or specified via the --ab=path option.
-
-    To run the modpython tests, the "apache" executable or script must be
-    on your system path, or provided via the --apache=path option. On some
-    platforms, "apache" may be called "apachectl" or "apache2ctl"--create
-    a symlink to them if needed.
-"""
-
-import getopt
-import os
-import re
-import sys
-import time
-
-import cherrypy
-from cherrypy import _cperror, _cpmodpy
-from cherrypy.lib import httputil
-
-
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-AB_PATH = ''
-APACHE_PATH = 'apache'
-SCRIPT_NAME = '/cpbench/users/rdelon/apps/blog'
-
-__all__ = ['ABSession', 'Root', 'print_report',
-           'run_standard_benchmarks', 'safe_threads',
-           'size_report', 'thread_report',
-           ]
-
-size_cache = {}
-
-
-class Root:
-
-    @cherrypy.expose
-    def index(self):
-        return """<html>
-<head>
-    <title>CherryPy Benchmark</title>
-</head>
-<body>
-    <ul>
-        <li><a href="hello">Hello, world! (14 byte dynamic)</a></li>
-        <li><a href="static/index.html">Static file (14 bytes static)</a></li>
-        <li><form action="sizer">Response of length:
-            <input type='text' name='size' value='10' /></form>
-        </li>
-    </ul>
-</body>
-</html>"""
-
-    @cherrypy.expose
-    def hello(self):
-        return 'Hello, world\r\n'
-
-    @cherrypy.expose
-    def sizer(self, size):
-        resp = size_cache.get(size, None)
-        if resp is None:
-            size_cache[size] = resp = 'X' * int(size)
-        return resp
-
-
-def init():
-
-    cherrypy.config.update({
-        'log.error.file': '',
-        'environment': 'production',
-        'server.socket_host': '127.0.0.1',
-        'server.socket_port': 54583,
-        'server.max_request_header_size': 0,
-        'server.max_request_body_size': 0,
-    })
-
-    # Cheat mode on ;)
-    del cherrypy.config['tools.log_tracebacks.on']
-    del cherrypy.config['tools.log_headers.on']
-    del cherrypy.config['tools.trailing_slash.on']
-
-    appconf = {
-        '/static': {
-            'tools.staticdir.on': True,
-            'tools.staticdir.dir': 'static',
-            'tools.staticdir.root': curdir,
-        },
-    }
-    globals().update(
-        app=cherrypy.tree.mount(Root(), SCRIPT_NAME, appconf),
-    )
-
-
-class NullRequest:
-
-    """A null HTTP request class, returning 200 and an empty body."""
-
-    def __init__(self, local, remote, scheme='http'):
-        pass
-
-    def close(self):
-        pass
-
-    def run(self, method, path, query_string, protocol, headers, rfile):
-        cherrypy.response.status = '200 OK'
-        cherrypy.response.header_list = [('Content-Type', 'text/html'),
-                                         ('Server', 'Null CherryPy'),
-                                         ('Date', httputil.HTTPDate()),
-                                         ('Content-Length', '0'),
-                                         ]
-        cherrypy.response.body = ['']
-        return cherrypy.response
-
-
-class NullResponse:
-    pass
-
-
-class ABSession:
-
-    """A session of 'ab', the Apache HTTP server benchmarking tool.
-
-Example output from ab:
-
-This is ApacheBench, Version 2.0.40-dev <$Revision: 1.121.2.1 $> apache-2.0
-Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
-Copyright (c) 1998-2002 The Apache Software Foundation, http://www.apache.org/
-
-Benchmarking 127.0.0.1 (be patient)
-Completed 100 requests
-Completed 200 requests
-Completed 300 requests
-Completed 400 requests
-Completed 500 requests
-Completed 600 requests
-Completed 700 requests
-Completed 800 requests
-Completed 900 requests
-
-
-Server Software:        CherryPy/3.1beta
-Server Hostname:        127.0.0.1
-Server Port:            54583
-
-Document Path:          /static/index.html
-Document Length:        14 bytes
-
-Concurrency Level:      10
-Time taken for tests:   9.643867 seconds
-Complete requests:      1000
-Failed requests:        0
-Write errors:           0
-Total transferred:      189000 bytes
-HTML transferred:       14000 bytes
-Requests per second:    103.69 [#/sec] (mean)
-Time per request:       96.439 [ms] (mean)
-Time per request:       9.644 [ms] (mean, across all concurrent requests)
-Transfer rate:          19.08 [Kbytes/sec] received
-
-Connection Times (ms)
-              min  mean[+/-sd] median   max
-Connect:        0    0   2.9      0      10
-Processing:    20   94   7.3     90     130
-Waiting:        0   43  28.1     40     100
-Total:         20   95   7.3    100     130
-
-Percentage of the requests served within a certain time (ms)
-  50%    100
-  66%    100
-  75%    100
-  80%    100
-  90%    100
-  95%    100
-  98%    100
-  99%    110
- 100%    130 (longest request)
-Finished 1000 requests
-"""
-
-    parse_patterns = [
-        ('complete_requests', 'Completed',
-         br'^Complete requests:\s*(\d+)'),
-        ('failed_requests', 'Failed',
-         br'^Failed requests:\s*(\d+)'),
-        ('requests_per_second', 'req/sec',
-         br'^Requests per second:\s*([0-9.]+)'),
-        ('time_per_request_concurrent', 'msec/req',
-         br'^Time per request:\s*([0-9.]+).*concurrent requests\)$'),
-        ('transfer_rate', 'KB/sec',
-         br'^Transfer rate:\s*([0-9.]+)')
-    ]
-
-    def __init__(self, path=SCRIPT_NAME + '/hello', requests=1000,
-                 concurrency=10):
-        self.path = path
-        self.requests = requests
-        self.concurrency = concurrency
-
-    def args(self):
-        port = cherrypy.server.socket_port
-        assert self.concurrency > 0
-        assert self.requests > 0
-        # Don't use "localhost".
-        # Cf
-        # http://mail.python.org/pipermail/python-win32/2008-March/007050.html
-        return ('-k -n %s -c %s http://127.0.0.1:%s%s' %
-                (self.requests, self.concurrency, port, self.path))
-
-    def run(self):
-        # Parse output of ab, setting attributes on self
-        try:
-            self.output = _cpmodpy.read_process(AB_PATH or 'ab', self.args())
-        except Exception:
-            print(_cperror.format_exc())
-            raise
-
-        for attr, name, pattern in self.parse_patterns:
-            val = re.search(pattern, self.output, re.MULTILINE)
-            if val:
-                val = val.group(1)
-                setattr(self, attr, val)
-            else:
-                setattr(self, attr, None)
-
-
-safe_threads = (25, 50, 100, 200, 400)
-if sys.platform in ('win32',):
-    # For some reason, ab crashes with > 50 threads on my Win2k laptop.
-    safe_threads = (10, 20, 30, 40, 50)
-
-
-def thread_report(path=SCRIPT_NAME + '/hello', concurrency=safe_threads):
-    sess = ABSession(path)
-    attrs, names, patterns = list(zip(*sess.parse_patterns))
-    avg = dict.fromkeys(attrs, 0.0)
-
-    yield ('threads',) + names
-    for c in concurrency:
-        sess.concurrency = c
-        sess.run()
-        row = [c]
-        for attr in attrs:
-            val = getattr(sess, attr)
-            if val is None:
-                print(sess.output)
-                row = None
-                break
-            val = float(val)
-            avg[attr] += float(val)
-            row.append(val)
-        if row:
-            yield row
-
-    # Add a row of averages.
-    yield ['Average'] + [str(avg[attr] / len(concurrency)) for attr in attrs]
-
-
-def size_report(sizes=(10, 100, 1000, 10000, 100000, 100000000),
-                concurrency=50):
-    sess = ABSession(concurrency=concurrency)
-    attrs, names, patterns = list(zip(*sess.parse_patterns))
-    yield ('bytes',) + names
-    for sz in sizes:
-        sess.path = '%s/sizer?size=%s' % (SCRIPT_NAME, sz)
-        sess.run()
-        yield [sz] + [getattr(sess, attr) for attr in attrs]
-
-
-def print_report(rows):
-    for row in rows:
-        print('')
-        for val in row:
-            sys.stdout.write(str(val).rjust(10) + ' | ')
-    print('')
-
-
-def run_standard_benchmarks():
-    print('')
-    print('Client Thread Report (1000 requests, 14 byte response body, '
-          '%s server threads):' % cherrypy.server.thread_pool)
-    print_report(thread_report())
-
-    print('')
-    print('Client Thread Report (1000 requests, 14 bytes via staticdir, '
-          '%s server threads):' % cherrypy.server.thread_pool)
-    print_report(thread_report('%s/static/index.html' % SCRIPT_NAME))
-
-    print('')
-    print('Size Report (1000 requests, 50 client threads, '
-          '%s server threads):' % cherrypy.server.thread_pool)
-    print_report(size_report())
-
-
-#                         modpython and other WSGI                         #
-
-def startup_modpython(req=None):
-    """Start the CherryPy app server in 'serverless' mode (for modpython/WSGI).
-    """
-    if cherrypy.engine.state == cherrypy._cpengine.STOPPED:
-        if req:
-            if 'nullreq' in req.get_options():
-                cherrypy.engine.request_class = NullRequest
-                cherrypy.engine.response_class = NullResponse
-            ab_opt = req.get_options().get('ab', '')
-            if ab_opt:
-                global AB_PATH
-                AB_PATH = ab_opt
-        cherrypy.engine.start()
-    if cherrypy.engine.state == cherrypy._cpengine.STARTING:
-        cherrypy.engine.wait()
-    return 0  # apache.OK
-
-
-def run_modpython(use_wsgi=False):
-    print('Starting mod_python...')
-    pyopts = []
-
-    # Pass the null and ab=path options through Apache
-    if '--null' in opts:
-        pyopts.append(('nullreq', ''))
-
-    if '--ab' in opts:
-        pyopts.append(('ab', opts['--ab']))
-
-    s = _cpmodpy.ModPythonServer
-    if use_wsgi:
-        pyopts.append(('wsgi.application', 'cherrypy::tree'))
-        pyopts.append(
-            ('wsgi.startup', 'cherrypy.test.benchmark::startup_modpython'))
-        handler = 'modpython_gateway::handler'
-        s = s(port=54583, opts=pyopts,
-              apache_path=APACHE_PATH, handler=handler)
-    else:
-        pyopts.append(
-            ('cherrypy.setup', 'cherrypy.test.benchmark::startup_modpython'))
-        s = s(port=54583, opts=pyopts, apache_path=APACHE_PATH)
-
-    try:
-        s.start()
-        run()
-    finally:
-        s.stop()
-
-
-if __name__ == '__main__':
-    init()
-
-    longopts = ['cpmodpy', 'modpython', 'null', 'notests',
-                'help', 'ab=', 'apache=']
-    try:
-        switches, args = getopt.getopt(sys.argv[1:], '', longopts)
-        opts = dict(switches)
-    except getopt.GetoptError:
-        print(__doc__)
-        sys.exit(2)
-
-    if '--help' in opts:
-        print(__doc__)
-        sys.exit(0)
-
-    if '--ab' in opts:
-        AB_PATH = opts['--ab']
-
-    if '--notests' in opts:
-        # Return without stopping the server, so that the pages
-        # can be tested from a standard web browser.
-        def run():
-            port = cherrypy.server.socket_port
-            print('You may now open http://127.0.0.1:%s%s/' %
-                  (port, SCRIPT_NAME))
-
-            if '--null' in opts:
-                print('Using null Request object')
-    else:
-        def run():
-            end = time.time() - start
-            print('Started in %s seconds' % end)
-            if '--null' in opts:
-                print('\nUsing null Request object')
-            try:
-                try:
-                    run_standard_benchmarks()
-                except Exception:
-                    print(_cperror.format_exc())
-                    raise
-            finally:
-                cherrypy.engine.exit()
-
-    print('Starting CherryPy app server...')
-
-    class NullWriter(object):
-
-        """Suppresses the printing of socket errors."""
-
-        def write(self, data):
-            pass
-    sys.stderr = NullWriter()
-
-    start = time.time()
-
-    if '--cpmodpy' in opts:
-        run_modpython()
-    elif '--modpython' in opts:
-        run_modpython(use_wsgi=True)
-    else:
-        if '--null' in opts:
-            cherrypy.server.request_class = NullRequest
-            cherrypy.server.response_class = NullResponse
-
-        cherrypy.engine.start_with_callback(run)
-        cherrypy.engine.block()
diff --git a/libraries/cherrypy/test/checkerdemo.py b/libraries/cherrypy/test/checkerdemo.py
deleted file mode 100644
index 3438bd0c..00000000
--- a/libraries/cherrypy/test/checkerdemo.py
+++ /dev/null
@@ -1,49 +0,0 @@
-"""Demonstration app for cherrypy.checker.
-
-This application is intentionally broken and badly designed.
-To demonstrate the output of the CherryPy Checker, simply execute
-this module.
-"""
-
-import os
-import cherrypy
-thisdir = os.path.dirname(os.path.abspath(__file__))
-
-
-class Root:
-    pass
-
-
-if __name__ == '__main__':
-    conf = {'/base': {'tools.staticdir.root': thisdir,
-                      # Obsolete key.
-                      'throw_errors': True,
-                      },
-            # This entry should be OK.
-            '/base/static': {'tools.staticdir.on': True,
-                             'tools.staticdir.dir': 'static'},
-            # Warn on missing folder.
-            '/base/js': {'tools.staticdir.on': True,
-                         'tools.staticdir.dir': 'js'},
-            # Warn on dir with an abs path even though we provide root.
-            '/base/static2': {'tools.staticdir.on': True,
-                              'tools.staticdir.dir': '/static'},
-            # Warn on dir with a relative path with no root.
-            '/static3': {'tools.staticdir.on': True,
-                         'tools.staticdir.dir': 'static'},
-            # Warn on unknown namespace
-            '/unknown': {'toobles.gzip.on': True},
-            # Warn special on cherrypy.<known ns>.*
-            '/cpknown': {'cherrypy.tools.encode.on': True},
-            # Warn on mismatched types
-            '/conftype': {'request.show_tracebacks': 14},
-            # Warn on unknown tool.
-            '/web': {'tools.unknown.on': True},
-            # Warn on server.* in app config.
-            '/app1': {'server.socket_host': '0.0.0.0'},
-            # Warn on 'localhost'
-            'global': {'server.socket_host': 'localhost'},
-            # Warn on '[name]'
-            '[/extra_brackets]': {},
-            }
-    cherrypy.quickstart(Root(), config=conf)
diff --git a/libraries/cherrypy/test/fastcgi.conf b/libraries/cherrypy/test/fastcgi.conf
deleted file mode 100644
index e5c5163c..00000000
--- a/libraries/cherrypy/test/fastcgi.conf
+++ /dev/null
@@ -1,18 +0,0 @@
-
-# Apache2 server conf file for testing CherryPy with mod_fastcgi.
-# fumanchu: I had to hard-code paths due to crazy Debian layouts :(
-ServerRoot /usr/lib/apache2
-User #1000
-ErrorLog /usr/lib/python2.5/site-packages/cproot/trunk/cherrypy/test/mod_fastcgi.error.log
-
-DocumentRoot "/usr/lib/python2.5/site-packages/cproot/trunk/cherrypy/test"
-ServerName 127.0.0.1
-Listen 8080
-LoadModule fastcgi_module modules/mod_fastcgi.so
-LoadModule rewrite_module modules/mod_rewrite.so
-
-Options +ExecCGI
-SetHandler fastcgi-script
-RewriteEngine On
-RewriteRule ^(.*)$ /fastcgi.pyc [L]
-FastCgiExternalServer "/usr/lib/python2.5/site-packages/cproot/trunk/cherrypy/test/fastcgi.pyc" -host 127.0.0.1:4000
diff --git a/libraries/cherrypy/test/fcgi.conf b/libraries/cherrypy/test/fcgi.conf
deleted file mode 100644
index 3062eb35..00000000
--- a/libraries/cherrypy/test/fcgi.conf
+++ /dev/null
@@ -1,14 +0,0 @@
-
-# Apache2 server conf file for testing CherryPy with mod_fcgid.
-
-DocumentRoot "/usr/lib/python2.6/site-packages/cproot/trunk/cherrypy/test"
-ServerName 127.0.0.1
-Listen 8080
-LoadModule fastcgi_module modules/mod_fastcgi.dll
-LoadModule rewrite_module modules/mod_rewrite.so
-
-Options ExecCGI
-SetHandler fastcgi-script
-RewriteEngine On
-RewriteRule ^(.*)$ /fastcgi.pyc [L]
-FastCgiExternalServer "/usr/lib/python2.6/site-packages/cproot/trunk/cherrypy/test/fastcgi.pyc" -host 127.0.0.1:4000
diff --git a/libraries/cherrypy/test/helper.py b/libraries/cherrypy/test/helper.py
deleted file mode 100644
index 01c5a0c0..00000000
--- a/libraries/cherrypy/test/helper.py
+++ /dev/null
@@ -1,542 +0,0 @@
-"""A library of helper functions for the CherryPy test suite."""
-
-import datetime
-import io
-import logging
-import os
-import re
-import subprocess
-import sys
-import time
-import unittest
-import warnings
-
-import portend
-import pytest
-import six
-
-from cheroot.test import webtest
-
-import cherrypy
-from cherrypy._cpcompat import text_or_bytes, HTTPSConnection, ntob
-from cherrypy.lib import httputil
-from cherrypy.lib import gctools
-
-log = logging.getLogger(__name__)
-thisdir = os.path.abspath(os.path.dirname(__file__))
-serverpem = os.path.join(os.getcwd(), thisdir, 'test.pem')
-
-
-class Supervisor(object):
-
-    """Base class for modeling and controlling servers during testing."""
-
-    def __init__(self, **kwargs):
-        for k, v in kwargs.items():
-            if k == 'port':
-                setattr(self, k, int(v))
-            setattr(self, k, v)
-
-
-def log_to_stderr(msg, level):
-    return sys.stderr.write(msg + os.linesep)
-
-
-class LocalSupervisor(Supervisor):
-
-    """Base class for modeling/controlling servers which run in the same
-    process.
-
-    When the server side runs in a different process, start/stop can dump all
-    state between each test module easily. When the server side runs in the
-    same process as the client, however, we have to do a bit more work to
-    ensure config and mounted apps are reset between tests.
-    """
-
-    using_apache = False
-    using_wsgi = False
-
-    def __init__(self, **kwargs):
-        for k, v in kwargs.items():
-            setattr(self, k, v)
-
-        cherrypy.server.httpserver = self.httpserver_class
-
-        # This is perhaps the wrong place for this call but this is the only
-        # place that i've found so far that I KNOW is early enough to set this.
-        cherrypy.config.update({'log.screen': False})
-        engine = cherrypy.engine
-        if hasattr(engine, 'signal_handler'):
-            engine.signal_handler.subscribe()
-        if hasattr(engine, 'console_control_handler'):
-            engine.console_control_handler.subscribe()
-
-    def start(self, modulename=None):
-        """Load and start the HTTP server."""
-        if modulename:
-            # Unhook httpserver so cherrypy.server.start() creates a new
-            # one (with config from setup_server, if declared).
-            cherrypy.server.httpserver = None
-
-        cherrypy.engine.start()
-
-        self.sync_apps()
-
-    def sync_apps(self):
-        """Tell the server about any apps which the setup functions mounted."""
-        pass
-
-    def stop(self):
-        td = getattr(self, 'teardown', None)
-        if td:
-            td()
-
-        cherrypy.engine.exit()
-
-        servers_copy = list(six.iteritems(getattr(cherrypy, 'servers', {})))
-        for name, server in servers_copy:
-            server.unsubscribe()
-            del cherrypy.servers[name]
-
-
-class NativeServerSupervisor(LocalSupervisor):
-
-    """Server supervisor for the builtin HTTP server."""
-
-    httpserver_class = 'cherrypy._cpnative_server.CPHTTPServer'
-    using_apache = False
-    using_wsgi = False
-
-    def __str__(self):
-        return 'Builtin HTTP Server on %s:%s' % (self.host, self.port)
-
-
-class LocalWSGISupervisor(LocalSupervisor):
-
-    """Server supervisor for the builtin WSGI server."""
-
-    httpserver_class = 'cherrypy._cpwsgi_server.CPWSGIServer'
-    using_apache = False
-    using_wsgi = True
-
-    def __str__(self):
-        return 'Builtin WSGI Server on %s:%s' % (self.host, self.port)
-
-    def sync_apps(self):
-        """Hook a new WSGI app into the origin server."""
-        cherrypy.server.httpserver.wsgi_app = self.get_app()
-
-    def get_app(self, app=None):
-        """Obtain a new (decorated) WSGI app to hook into the origin server."""
-        if app is None:
-            app = cherrypy.tree
-
-        if self.validate:
-            try:
-                from wsgiref import validate
-            except ImportError:
-                warnings.warn(
-                    'Error importing wsgiref. The validator will not run.')
-            else:
-                # wraps the app in the validator
-                app = validate.validator(app)
-
-        return app
-
-
-def get_cpmodpy_supervisor(**options):
-    from cherrypy.test import modpy
-    sup = modpy.ModPythonSupervisor(**options)
-    sup.template = modpy.conf_cpmodpy
-    return sup
-
-
-def get_modpygw_supervisor(**options):
-    from cherrypy.test import modpy
-    sup = modpy.ModPythonSupervisor(**options)
-    sup.template = modpy.conf_modpython_gateway
-    sup.using_wsgi = True
-    return sup
-
-
-def get_modwsgi_supervisor(**options):
-    from cherrypy.test import modwsgi
-    return modwsgi.ModWSGISupervisor(**options)
-
-
-def get_modfcgid_supervisor(**options):
-    from cherrypy.test import modfcgid
-    return modfcgid.ModFCGISupervisor(**options)
-
-
-def get_modfastcgi_supervisor(**options):
-    from cherrypy.test import modfastcgi
-    return modfastcgi.ModFCGISupervisor(**options)
-
-
-def get_wsgi_u_supervisor(**options):
-    cherrypy.server.wsgi_version = ('u', 0)
-    return LocalWSGISupervisor(**options)
-
-
-class CPWebCase(webtest.WebCase):
-
-    script_name = ''
-    scheme = 'http'
-
-    available_servers = {'wsgi': LocalWSGISupervisor,
-                         'wsgi_u': get_wsgi_u_supervisor,
-                         'native': NativeServerSupervisor,
-                         'cpmodpy': get_cpmodpy_supervisor,
-                         'modpygw': get_modpygw_supervisor,
-                         'modwsgi': get_modwsgi_supervisor,
-                         'modfcgid': get_modfcgid_supervisor,
-                         'modfastcgi': get_modfastcgi_supervisor,
-                         }
-    default_server = 'wsgi'
-
-    @classmethod
-    def _setup_server(cls, supervisor, conf):
-        v = sys.version.split()[0]
-        log.info('Python version used to run this test script: %s' % v)
-        log.info('CherryPy version: %s' % cherrypy.__version__)
-        if supervisor.scheme == 'https':
-            ssl = ' (ssl)'
-        else:
-            ssl = ''
-        log.info('HTTP server version: %s%s' % (supervisor.protocol, ssl))
-        log.info('PID: %s' % os.getpid())
-
-        cherrypy.server.using_apache = supervisor.using_apache
-        cherrypy.server.using_wsgi = supervisor.using_wsgi
-
-        if sys.platform[:4] == 'java':
-            cherrypy.config.update({'server.nodelay': False})
-
-        if isinstance(conf, text_or_bytes):
-            parser = cherrypy.lib.reprconf.Parser()
-            conf = parser.dict_from_file(conf).get('global', {})
-        else:
-            conf = conf or {}
-        baseconf = conf.copy()
-        baseconf.update({'server.socket_host': supervisor.host,
-                         'server.socket_port': supervisor.port,
-                         'server.protocol_version': supervisor.protocol,
-                         'environment': 'test_suite',
-                         })
-        if supervisor.scheme == 'https':
-            # baseconf['server.ssl_module'] = 'builtin'
-            baseconf['server.ssl_certificate'] = serverpem
-            baseconf['server.ssl_private_key'] = serverpem
-
-        # helper must be imported lazily so the coverage tool
-        # can run against module-level statements within cherrypy.
-        # Also, we have to do "from cherrypy.test import helper",
-        # exactly like each test module does, because a relative import
-        # would stick a second instance of webtest in sys.modules,
-        # and we wouldn't be able to globally override the port anymore.
-        if supervisor.scheme == 'https':
-            webtest.WebCase.HTTP_CONN = HTTPSConnection
-        return baseconf
-
-    @classmethod
-    def setup_class(cls):
-        ''
-        # Creates a server
-        conf = {
-            'scheme': 'http',
-            'protocol': 'HTTP/1.1',
-            'port': 54583,
-            'host': '127.0.0.1',
-            'validate': False,
-            'server': 'wsgi',
-        }
-        supervisor_factory = cls.available_servers.get(
-            conf.get('server', 'wsgi'))
-        if supervisor_factory is None:
-            raise RuntimeError('Unknown server in config: %s' % conf['server'])
-        supervisor = supervisor_factory(**conf)
-
-        # Copied from "run_test_suite"
-        cherrypy.config.reset()
-        baseconf = cls._setup_server(supervisor, conf)
-        cherrypy.config.update(baseconf)
-        setup_client()
-
-        if hasattr(cls, 'setup_server'):
-            # Clear the cherrypy tree and clear the wsgi server so that
-            # it can be updated with the new root
-            cherrypy.tree = cherrypy._cptree.Tree()
-            cherrypy.server.httpserver = None
-            cls.setup_server()
-            # Add a resource for verifying there are no refleaks
-            # to *every* test class.
-            cherrypy.tree.mount(gctools.GCRoot(), '/gc')
-            cls.do_gc_test = True
-            supervisor.start(cls.__module__)
-
-        cls.supervisor = supervisor
-
-    @classmethod
-    def teardown_class(cls):
-        ''
-        if hasattr(cls, 'setup_server'):
-            cls.supervisor.stop()
-
-    do_gc_test = False
-
-    def test_gc(self):
-        if not self.do_gc_test:
-            return
-
-        self.getPage('/gc/stats')
-        try:
-            self.assertBody('Statistics:')
-        except Exception:
-            'Failures occur intermittently. See #1420'
-
-    def prefix(self):
-        return self.script_name.rstrip('/')
-
-    def base(self):
-        if ((self.scheme == 'http' and self.PORT == 80) or
-                (self.scheme == 'https' and self.PORT == 443)):
-            port = ''
-        else:
-            port = ':%s' % self.PORT
-
-        return '%s://%s%s%s' % (self.scheme, self.HOST, port,
-                                self.script_name.rstrip('/'))
-
-    def exit(self):
-        sys.exit()
-
-    def getPage(self, url, headers=None, method='GET', body=None,
-                protocol=None, raise_subcls=None):
-        """Open the url. Return status, headers, body.
-
-        `raise_subcls` must be a tuple with the exceptions classes
-        or a single exception class that are not going to be considered
-        a socket.error regardless that they were are subclass of a
-        socket.error and therefore not considered for a connection retry.
-        """
-        if self.script_name:
-            url = httputil.urljoin(self.script_name, url)
-        return webtest.WebCase.getPage(self, url, headers, method, body,
-                                       protocol, raise_subcls)
-
-    def skip(self, msg='skipped '):
-        pytest.skip(msg)
-
-    def assertErrorPage(self, status, message=None, pattern=''):
-        """Compare the response body with a built in error page.
-
-        The function will optionally look for the regexp pattern,
-        within the exception embedded in the error page."""
-
-        # This will never contain a traceback
-        page = cherrypy._cperror.get_error_page(status, message=message)
-
-        # First, test the response body without checking the traceback.
-        # Stick a match-all group (.*) in to grab the traceback.
-        def esc(text):
-            return re.escape(ntob(text))
-        epage = re.escape(page)
-        epage = epage.replace(
-            esc('<pre id="traceback"></pre>'),
-            esc('<pre id="traceback">') + b'(.*)' + esc('</pre>'))
-        m = re.match(epage, self.body, re.DOTALL)
-        if not m:
-            self._handlewebError(
-                'Error page does not match; expected:\n' + page)
-            return
-
-        # Now test the pattern against the traceback
-        if pattern is None:
-            # Special-case None to mean that there should be *no* traceback.
-            if m and m.group(1):
-                self._handlewebError('Error page contains traceback')
-        else:
-            if (m is None) or (
-                not re.search(ntob(re.escape(pattern), self.encoding),
-                              m.group(1))):
-                msg = 'Error page does not contain %s in traceback'
-                self._handlewebError(msg % repr(pattern))
-
-    date_tolerance = 2
-
-    def assertEqualDates(self, dt1, dt2, seconds=None):
-        """Assert abs(dt1 - dt2) is within Y seconds."""
-        if seconds is None:
-            seconds = self.date_tolerance
-
-        if dt1 > dt2:
-            diff = dt1 - dt2
-        else:
-            diff = dt2 - dt1
-        if not diff < datetime.timedelta(seconds=seconds):
-            raise AssertionError('%r and %r are not within %r seconds.' %
-                                 (dt1, dt2, seconds))
-
-
-def _test_method_sorter(_, x, y):
-    """Monkeypatch the test sorter to always run test_gc last in each suite."""
-    if x == 'test_gc':
-        return 1
-    if y == 'test_gc':
-        return -1
-    if x > y:
-        return 1
-    if x < y:
-        return -1
-    return 0
-
-
-unittest.TestLoader.sortTestMethodsUsing = _test_method_sorter
-
-
-def setup_client():
-    """Set up the WebCase classes to match the server's socket settings."""
-    webtest.WebCase.PORT = cherrypy.server.socket_port
-    webtest.WebCase.HOST = cherrypy.server.socket_host
-    if cherrypy.server.ssl_certificate:
-        CPWebCase.scheme = 'https'
-
-# --------------------------- Spawning helpers --------------------------- #
-
-
-class CPProcess(object):
-
-    pid_file = os.path.join(thisdir, 'test.pid')
-    config_file = os.path.join(thisdir, 'test.conf')
-    config_template = """[global]
-server.socket_host: '%(host)s'
-server.socket_port: %(port)s
-checker.on: False
-log.screen: False
-log.error_file: r'%(error_log)s'
-log.access_file: r'%(access_log)s'
-%(ssl)s
-%(extra)s
-"""
-    error_log = os.path.join(thisdir, 'test.error.log')
-    access_log = os.path.join(thisdir, 'test.access.log')
-
-    def __init__(self, wait=False, daemonize=False, ssl=False,
-                 socket_host=None, socket_port=None):
-        self.wait = wait
-        self.daemonize = daemonize
-        self.ssl = ssl
-        self.host = socket_host or cherrypy.server.socket_host
-        self.port = socket_port or cherrypy.server.socket_port
-
-    def write_conf(self, extra=''):
-        if self.ssl:
-            serverpem = os.path.join(thisdir, 'test.pem')
-            ssl = """
-server.ssl_certificate: r'%s'
-server.ssl_private_key: r'%s'
-""" % (serverpem, serverpem)
-        else:
-            ssl = ''
-
-        conf = self.config_template % {
-            'host': self.host,
-            'port': self.port,
-            'error_log': self.error_log,
-            'access_log': self.access_log,
-            'ssl': ssl,
-            'extra': extra,
-        }
-        with io.open(self.config_file, 'w', encoding='utf-8') as f:
-            f.write(six.text_type(conf))
-
-    def start(self, imports=None):
-        """Start cherryd in a subprocess."""
-        portend.free(self.host, self.port, timeout=1)
-
-        args = [
-            '-m',
-            'cherrypy',
-            '-c', self.config_file,
-            '-p', self.pid_file,
-        ]
-        r"""
-        Command for running cherryd server with autoreload enabled
-
-        Using
-
-        ```
-        ['-c',
-         "__requires__ = 'CherryPy'; \
-         import pkg_resources, re, sys; \
-         sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]); \
-         sys.exit(\
-            pkg_resources.load_entry_point(\
-                'CherryPy', 'console_scripts', 'cherryd')())"]
-        ```
-
-        doesn't work as it's impossible to reconstruct the `-c`'s contents.
-        Ref: https://github.com/cherrypy/cherrypy/issues/1545
-        """
-
-        if not isinstance(imports, (list, tuple)):
-            imports = [imports]
-        for i in imports:
-            if i:
-                args.append('-i')
-                args.append(i)
-
-        if self.daemonize:
-            args.append('-d')
-
-        env = os.environ.copy()
-        # Make sure we import the cherrypy package in which this module is
-        # defined.
-        grandparentdir = os.path.abspath(os.path.join(thisdir, '..', '..'))
-        if env.get('PYTHONPATH', ''):
-            env['PYTHONPATH'] = os.pathsep.join(
-                (grandparentdir, env['PYTHONPATH']))
-        else:
-            env['PYTHONPATH'] = grandparentdir
-        self._proc = subprocess.Popen([sys.executable] + args, env=env)
-        if self.wait:
-            self.exit_code = self._proc.wait()
-        else:
-            portend.occupied(self.host, self.port, timeout=5)
-
-        # Give the engine a wee bit more time to finish STARTING
-        if self.daemonize:
-            time.sleep(2)
-        else:
-            time.sleep(1)
-
-    def get_pid(self):
-        if self.daemonize:
-            return int(open(self.pid_file, 'rb').read())
-        return self._proc.pid
-
-    def join(self):
-        """Wait for the process to exit."""
-        if self.daemonize:
-            return self._join_daemon()
-        self._proc.wait()
-
-    def _join_daemon(self):
-        try:
-            try:
-                # Mac, UNIX
-                os.wait()
-            except AttributeError:
-                # Windows
-                try:
-                    pid = self.get_pid()
-                except IOError:
-                    # Assume the subprocess deleted the pidfile on shutdown.
-                    pass
-                else:
-                    os.waitpid(pid, 0)
-        except OSError:
-            x = sys.exc_info()[1]
-            if x.args != (10, 'No child processes'):
-                raise
diff --git a/libraries/cherrypy/test/logtest.py b/libraries/cherrypy/test/logtest.py
deleted file mode 100644
index ed8f1540..00000000
--- a/libraries/cherrypy/test/logtest.py
+++ /dev/null
@@ -1,228 +0,0 @@
-"""logtest, a unittest.TestCase helper for testing log output."""
-
-import sys
-import time
-from uuid import UUID
-
-import six
-
-from cherrypy._cpcompat import text_or_bytes, ntob
-
-
-try:
-    # On Windows, msvcrt.getch reads a single char without output.
-    import msvcrt
-
-    def getchar():
-        return msvcrt.getch()
-except ImportError:
-    # Unix getchr
-    import tty
-    import termios
-
-    def getchar():
-        fd = sys.stdin.fileno()
-        old_settings = termios.tcgetattr(fd)
-        try:
-            tty.setraw(sys.stdin.fileno())
-            ch = sys.stdin.read(1)
-        finally:
-            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
-        return ch
-
-
-class LogCase(object):
-
-    """unittest.TestCase mixin for testing log messages.
-
-    logfile: a filename for the desired log. Yes, I know modes are evil,
-        but it makes the test functions so much cleaner to set this once.
-
-    lastmarker: the last marker in the log. This can be used to search for
-        messages since the last marker.
-
-    markerPrefix: a string with which to prefix log markers. This should be
-        unique enough from normal log output to use for marker identification.
-    """
-
-    logfile = None
-    lastmarker = None
-    markerPrefix = b'test suite marker: '
-
-    def _handleLogError(self, msg, data, marker, pattern):
-        print('')
-        print('    ERROR: %s' % msg)
-
-        if not self.interactive:
-            raise self.failureException(msg)
-
-        p = ('    Show: '
-             '[L]og [M]arker [P]attern; '
-             '[I]gnore, [R]aise, or sys.e[X]it >> ')
-        sys.stdout.write(p + ' ')
-        # ARGH
-        sys.stdout.flush()
-        while True:
-            i = getchar().upper()
-            if i not in 'MPLIRX':
-                continue
-            print(i.upper())  # Also prints new line
-            if i == 'L':
-                for x, line in enumerate(data):
-                    if (x + 1) % self.console_height == 0:
-                        # The \r and comma should make the next line overwrite
-                        sys.stdout.write('<-- More -->\r ')
-                        m = getchar().lower()
-                        # Erase our "More" prompt
-                        sys.stdout.write('            \r ')
-                        if m == 'q':
-                            break
-                    print(line.rstrip())
-            elif i == 'M':
-                print(repr(marker or self.lastmarker))
-            elif i == 'P':
-                print(repr(pattern))
-            elif i == 'I':
-                # return without raising the normal exception
-                return
-            elif i == 'R':
-                raise self.failureException(msg)
-            elif i == 'X':
-                self.exit()
-            sys.stdout.write(p + ' ')
-
-    def exit(self):
-        sys.exit()
-
-    def emptyLog(self):
-        """Overwrite self.logfile with 0 bytes."""
-        open(self.logfile, 'wb').write('')
-
-    def markLog(self, key=None):
-        """Insert a marker line into the log and set self.lastmarker."""
-        if key is None:
-            key = str(time.time())
-        self.lastmarker = key
-
-        open(self.logfile, 'ab+').write(
-            ntob('%s%s\n' % (self.markerPrefix, key), 'utf-8'))
-
-    def _read_marked_region(self, marker=None):
-        """Return lines from self.logfile in the marked region.
-
-        If marker is None, self.lastmarker is used. If the log hasn't
-        been marked (using self.markLog), the entire log will be returned.
-        """
-# Give the logger time to finish writing?
-# time.sleep(0.5)
-
-        logfile = self.logfile
-        marker = marker or self.lastmarker
-        if marker is None:
-            return open(logfile, 'rb').readlines()
-
-        if isinstance(marker, six.text_type):
-            marker = marker.encode('utf-8')
-        data = []
-        in_region = False
-        for line in open(logfile, 'rb'):
-            if in_region:
-                if line.startswith(self.markerPrefix) and marker not in line:
-                    break
-                else:
-                    data.append(line)
-            elif marker in line:
-                in_region = True
-        return data
-
-    def assertInLog(self, line, marker=None):
-        """Fail if the given (partial) line is not in the log.
-
-        The log will be searched from the given marker to the next marker.
-        If marker is None, self.lastmarker is used. If the log hasn't
-        been marked (using self.markLog), the entire log will be searched.
-        """
-        data = self._read_marked_region(marker)
-        for logline in data:
-            if line in logline:
-                return
-        msg = '%r not found in log' % line
-        self._handleLogError(msg, data, marker, line)
-
-    def assertNotInLog(self, line, marker=None):
-        """Fail if the given (partial) line is in the log.
-
-        The log will be searched from the given marker to the next marker.
-        If marker is None, self.lastmarker is used. If the log hasn't
-        been marked (using self.markLog), the entire log will be searched.
-        """
-        data = self._read_marked_region(marker)
-        for logline in data:
-            if line in logline:
-                msg = '%r found in log' % line
-                self._handleLogError(msg, data, marker, line)
-
-    def assertValidUUIDv4(self, marker=None):
-        """Fail if the given UUIDv4 is not valid.
-
-        The log will be searched from the given marker to the next marker.
-        If marker is None, self.lastmarker is used. If the log hasn't
-        been marked (using self.markLog), the entire log will be searched.
-        """
-        data = self._read_marked_region(marker)
-        data = [
-            chunk.decode('utf-8').rstrip('\n').rstrip('\r')
-            for chunk in data
-        ]
-        for log_chunk in data:
-            try:
-                uuid_log = data[-1]
-                uuid_obj = UUID(uuid_log, version=4)
-            except (TypeError, ValueError):
-                pass  # it might be in other chunk
-            else:
-                if str(uuid_obj) == uuid_log:
-                    return
-                msg = '%r is not a valid UUIDv4' % uuid_log
-                self._handleLogError(msg, data, marker, log_chunk)
-
-        msg = 'UUIDv4 not found in log'
-        self._handleLogError(msg, data, marker, log_chunk)
-
-    def assertLog(self, sliceargs, lines, marker=None):
-        """Fail if log.readlines()[sliceargs] is not contained in 'lines'.
-
-        The log will be searched from the given marker to the next marker.
-        If marker is None, self.lastmarker is used. If the log hasn't
-        been marked (using self.markLog), the entire log will be searched.
-        """
-        data = self._read_marked_region(marker)
-        if isinstance(sliceargs, int):
-            # Single arg. Use __getitem__ and allow lines to be str or list.
-            if isinstance(lines, (tuple, list)):
-                lines = lines[0]
-            if isinstance(lines, six.text_type):
-                lines = lines.encode('utf-8')
-            if lines not in data[sliceargs]:
-                msg = '%r not found on log line %r' % (lines, sliceargs)
-                self._handleLogError(
-                    msg,
-                    [data[sliceargs], '--EXTRA CONTEXT--'] + data[
-                        sliceargs + 1:sliceargs + 6],
-                    marker,
-                    lines)
-        else:
-            # Multiple args. Use __getslice__ and require lines to be list.
-            if isinstance(lines, tuple):
-                lines = list(lines)
-            elif isinstance(lines, text_or_bytes):
-                raise TypeError("The 'lines' arg must be a list when "
-                                "'sliceargs' is a tuple.")
-
-            start, stop = sliceargs
-            for line, logline in zip(lines, data[start:stop]):
-                if isinstance(line, six.text_type):
-                    line = line.encode('utf-8')
-                if line not in logline:
-                    msg = '%r not found in log' % line
-                    self._handleLogError(msg, data[start:stop], marker, line)
diff --git a/libraries/cherrypy/test/modfastcgi.py b/libraries/cherrypy/test/modfastcgi.py
deleted file mode 100644
index 79ec3d18..00000000
--- a/libraries/cherrypy/test/modfastcgi.py
+++ /dev/null
@@ -1,136 +0,0 @@
-"""Wrapper for mod_fastcgi, for use as a CherryPy HTTP server when testing.
-
-To autostart fastcgi, the "apache" executable or script must be
-on your system path, or you must override the global APACHE_PATH.
-On some platforms, "apache" may be called "apachectl", "apache2ctl",
-or "httpd"--create a symlink to them if needed.
-
-You'll also need the WSGIServer from flup.servers.
-See http://projects.amor.org/misc/wiki/ModPythonGateway
-
-
-KNOWN BUGS
-==========
-
-1. Apache processes Range headers automatically; CherryPy's truncated
-    output is then truncated again by Apache. See test_core.testRanges.
-    This was worked around in http://www.cherrypy.org/changeset/1319.
-2. Apache does not allow custom HTTP methods like CONNECT as per the spec.
-    See test_core.testHTTPMethods.
-3. Max request header and body settings do not work with Apache.
-4. Apache replaces status "reason phrases" automatically. For example,
-    CherryPy may set "304 Not modified" but Apache will write out
-    "304 Not Modified" (capital "M").
-5. Apache does not allow custom error codes as per the spec.
-6. Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
-    Request-URI too early.
-7. mod_python will not read request bodies which use the "chunked"
-    transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block
-    instead of REQUEST_CHUNKED_DECHUNK, see Apache2's http_protocol.c and
-    mod_python's requestobject.c).
-8. Apache will output a "Content-Length: 0" response header even if there's
-    no response entity body. This isn't really a bug; it just differs from
-    the CherryPy default.
-"""
-
-import os
-import re
-
-import cherrypy
-from cherrypy.process import servers
-from cherrypy.test import helper
-
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-
-def read_process(cmd, args=''):
-    pipein, pipeout = os.popen4('%s %s' % (cmd, args))
-    try:
-        firstline = pipeout.readline()
-        if (re.search(r'(not recognized|No such file|not found)', firstline,
-                      re.IGNORECASE)):
-            raise IOError('%s must be on your system path.' % cmd)
-        output = firstline + pipeout.read()
-    finally:
-        pipeout.close()
-    return output
-
-
-APACHE_PATH = 'apache2ctl'
-CONF_PATH = 'fastcgi.conf'
-
-conf_fastcgi = """
-# Apache2 server conf file for testing CherryPy with mod_fastcgi.
-# fumanchu: I had to hard-code paths due to crazy Debian layouts :(
-ServerRoot /usr/lib/apache2
-User #1000
-ErrorLog %(root)s/mod_fastcgi.error.log
-
-DocumentRoot "%(root)s"
-ServerName 127.0.0.1
-Listen %(port)s
-LoadModule fastcgi_module modules/mod_fastcgi.so
-LoadModule rewrite_module modules/mod_rewrite.so
-
-Options +ExecCGI
-SetHandler fastcgi-script
-RewriteEngine On
-RewriteRule ^(.*)$ /fastcgi.pyc [L]
-FastCgiExternalServer "%(server)s" -host 127.0.0.1:4000
-"""
-
-
-def erase_script_name(environ, start_response):
-    environ['SCRIPT_NAME'] = ''
-    return cherrypy.tree(environ, start_response)
-
-
-class ModFCGISupervisor(helper.LocalWSGISupervisor):
-
-    httpserver_class = 'cherrypy.process.servers.FlupFCGIServer'
-    using_apache = True
-    using_wsgi = True
-    template = conf_fastcgi
-
-    def __str__(self):
-        return 'FCGI Server on %s:%s' % (self.host, self.port)
-
-    def start(self, modulename):
-        cherrypy.server.httpserver = servers.FlupFCGIServer(
-            application=erase_script_name, bindAddress=('127.0.0.1', 4000))
-        cherrypy.server.httpserver.bind_addr = ('127.0.0.1', 4000)
-        cherrypy.server.socket_port = 4000
-        # For FCGI, we both start apache...
-        self.start_apache()
-        # ...and our local server
-        cherrypy.engine.start()
-        self.sync_apps()
-
-    def start_apache(self):
-        fcgiconf = CONF_PATH
-        if not os.path.isabs(fcgiconf):
-            fcgiconf = os.path.join(curdir, fcgiconf)
-
-        # Write the Apache conf file.
-        f = open(fcgiconf, 'wb')
-        try:
-            server = repr(os.path.join(curdir, 'fastcgi.pyc'))[1:-1]
-            output = self.template % {'port': self.port, 'root': curdir,
-                                      'server': server}
-            output = output.replace('\r\n', '\n')
-            f.write(output)
-        finally:
-            f.close()
-
-        result = read_process(APACHE_PATH, '-k start -f %s' % fcgiconf)
-        if result:
-            print(result)
-
-    def stop(self):
-        """Gracefully shutdown a server that is serving forever."""
-        read_process(APACHE_PATH, '-k stop')
-        helper.LocalWSGISupervisor.stop(self)
-
-    def sync_apps(self):
-        cherrypy.server.httpserver.fcgiserver.application = self.get_app(
-            erase_script_name)
diff --git a/libraries/cherrypy/test/modfcgid.py b/libraries/cherrypy/test/modfcgid.py
deleted file mode 100644
index d101bd67..00000000
--- a/libraries/cherrypy/test/modfcgid.py
+++ /dev/null
@@ -1,124 +0,0 @@
-"""Wrapper for mod_fcgid, for use as a CherryPy HTTP server when testing.
-
-To autostart fcgid, the "apache" executable or script must be
-on your system path, or you must override the global APACHE_PATH.
-On some platforms, "apache" may be called "apachectl", "apache2ctl",
-or "httpd"--create a symlink to them if needed.
-
-You'll also need the WSGIServer from flup.servers.
-See http://projects.amor.org/misc/wiki/ModPythonGateway
-
-
-KNOWN BUGS
-==========
-
-1. Apache processes Range headers automatically; CherryPy's truncated
-    output is then truncated again by Apache. See test_core.testRanges.
-    This was worked around in http://www.cherrypy.org/changeset/1319.
-2. Apache does not allow custom HTTP methods like CONNECT as per the spec.
-    See test_core.testHTTPMethods.
-3. Max request header and body settings do not work with Apache.
-4. Apache replaces status "reason phrases" automatically. For example,
-    CherryPy may set "304 Not modified" but Apache will write out
-    "304 Not Modified" (capital "M").
-5. Apache does not allow custom error codes as per the spec.
-6. Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
-    Request-URI too early.
-7. mod_python will not read request bodies which use the "chunked"
-    transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block
-    instead of REQUEST_CHUNKED_DECHUNK, see Apache2's http_protocol.c and
-    mod_python's requestobject.c).
-8. Apache will output a "Content-Length: 0" response header even if there's
-    no response entity body. This isn't really a bug; it just differs from
-    the CherryPy default.
-"""
-
-import os
-import re
-
-import cherrypy
-from cherrypy._cpcompat import ntob
-from cherrypy.process import servers
-from cherrypy.test import helper
-
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-
-def read_process(cmd, args=''):
-    pipein, pipeout = os.popen4('%s %s' % (cmd, args))
-    try:
-        firstline = pipeout.readline()
-        if (re.search(r'(not recognized|No such file|not found)', firstline,
-                      re.IGNORECASE)):
-            raise IOError('%s must be on your system path.' % cmd)
-        output = firstline + pipeout.read()
-    finally:
-        pipeout.close()
-    return output
-
-
-APACHE_PATH = 'httpd'
-CONF_PATH = 'fcgi.conf'
-
-conf_fcgid = """
-# Apache2 server conf file for testing CherryPy with mod_fcgid.
-
-DocumentRoot "%(root)s"
-ServerName 127.0.0.1
-Listen %(port)s
-LoadModule fastcgi_module modules/mod_fastcgi.dll
-LoadModule rewrite_module modules/mod_rewrite.so
-
-Options ExecCGI
-SetHandler fastcgi-script
-RewriteEngine On
-RewriteRule ^(.*)$ /fastcgi.pyc [L]
-FastCgiExternalServer "%(server)s" -host 127.0.0.1:4000
-"""
-
-
-class ModFCGISupervisor(helper.LocalSupervisor):
-
-    using_apache = True
-    using_wsgi = True
-    template = conf_fcgid
-
-    def __str__(self):
-        return 'FCGI Server on %s:%s' % (self.host, self.port)
-
-    def start(self, modulename):
-        cherrypy.server.httpserver = servers.FlupFCGIServer(
-            application=cherrypy.tree, bindAddress=('127.0.0.1', 4000))
-        cherrypy.server.httpserver.bind_addr = ('127.0.0.1', 4000)
-        # For FCGI, we both start apache...
-        self.start_apache()
-        # ...and our local server
-        helper.LocalServer.start(self, modulename)
-
-    def start_apache(self):
-        fcgiconf = CONF_PATH
-        if not os.path.isabs(fcgiconf):
-            fcgiconf = os.path.join(curdir, fcgiconf)
-
-        # Write the Apache conf file.
-        f = open(fcgiconf, 'wb')
-        try:
-            server = repr(os.path.join(curdir, 'fastcgi.pyc'))[1:-1]
-            output = self.template % {'port': self.port, 'root': curdir,
-                                      'server': server}
-            output = ntob(output.replace('\r\n', '\n'))
-            f.write(output)
-        finally:
-            f.close()
-
-        result = read_process(APACHE_PATH, '-k start -f %s' % fcgiconf)
-        if result:
-            print(result)
-
-    def stop(self):
-        """Gracefully shutdown a server that is serving forever."""
-        read_process(APACHE_PATH, '-k stop')
-        helper.LocalServer.stop(self)
-
-    def sync_apps(self):
-        cherrypy.server.httpserver.fcgiserver.application = self.get_app()
diff --git a/libraries/cherrypy/test/modpy.py b/libraries/cherrypy/test/modpy.py
deleted file mode 100644
index 7c288d2c..00000000
--- a/libraries/cherrypy/test/modpy.py
+++ /dev/null
@@ -1,164 +0,0 @@
-"""Wrapper for mod_python, for use as a CherryPy HTTP server when testing.
-
-To autostart modpython, the "apache" executable or script must be
-on your system path, or you must override the global APACHE_PATH.
-On some platforms, "apache" may be called "apachectl" or "apache2ctl"--
-create a symlink to them if needed.
-
-If you wish to test the WSGI interface instead of our _cpmodpy interface,
-you also need the 'modpython_gateway' module at:
-http://projects.amor.org/misc/wiki/ModPythonGateway
-
-
-KNOWN BUGS
-==========
-
-1. Apache processes Range headers automatically; CherryPy's truncated
-    output is then truncated again by Apache. See test_core.testRanges.
-    This was worked around in http://www.cherrypy.org/changeset/1319.
-2. Apache does not allow custom HTTP methods like CONNECT as per the spec.
-    See test_core.testHTTPMethods.
-3. Max request header and body settings do not work with Apache.
-4. Apache replaces status "reason phrases" automatically. For example,
-    CherryPy may set "304 Not modified" but Apache will write out
-    "304 Not Modified" (capital "M").
-5. Apache does not allow custom error codes as per the spec.
-6. Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
-    Request-URI too early.
-7. mod_python will not read request bodies which use the "chunked"
-    transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block
-    instead of REQUEST_CHUNKED_DECHUNK, see Apache2's http_protocol.c and
-    mod_python's requestobject.c).
-8. Apache will output a "Content-Length: 0" response header even if there's
-    no response entity body. This isn't really a bug; it just differs from
-    the CherryPy default.
-"""
-
-import os
-import re
-
-import cherrypy
-from cherrypy.test import helper
-
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-
-def read_process(cmd, args=''):
-    pipein, pipeout = os.popen4('%s %s' % (cmd, args))
-    try:
-        firstline = pipeout.readline()
-        if (re.search(r'(not recognized|No such file|not found)', firstline,
-                      re.IGNORECASE)):
-            raise IOError('%s must be on your system path.' % cmd)
-        output = firstline + pipeout.read()
-    finally:
-        pipeout.close()
-    return output
-
-
-APACHE_PATH = 'httpd'
-CONF_PATH = 'test_mp.conf'
-
-conf_modpython_gateway = """
-# Apache2 server conf file for testing CherryPy with modpython_gateway.
-
-ServerName 127.0.0.1
-DocumentRoot "/"
-Listen %(port)s
-LoadModule python_module modules/mod_python.so
-
-SetHandler python-program
-PythonFixupHandler cherrypy.test.modpy::wsgisetup
-PythonOption testmod %(modulename)s
-PythonHandler modpython_gateway::handler
-PythonOption wsgi.application cherrypy::tree
-PythonOption socket_host %(host)s
-PythonDebug On
-"""
-
-conf_cpmodpy = """
-# Apache2 server conf file for testing CherryPy with _cpmodpy.
-
-ServerName 127.0.0.1
-DocumentRoot "/"
-Listen %(port)s
-LoadModule python_module modules/mod_python.so
-
-SetHandler python-program
-PythonFixupHandler cherrypy.test.modpy::cpmodpysetup
-PythonHandler cherrypy._cpmodpy::handler
-PythonOption cherrypy.setup cherrypy.test.%(modulename)s::setup_server
-PythonOption socket_host %(host)s
-PythonDebug On
-"""
-
-
-class ModPythonSupervisor(helper.Supervisor):
-
-    using_apache = True
-    using_wsgi = False
-    template = None
-
-    def __str__(self):
-        return 'ModPython Server on %s:%s' % (self.host, self.port)
-
-    def start(self, modulename):
-        mpconf = CONF_PATH
-        if not os.path.isabs(mpconf):
-            mpconf = os.path.join(curdir, mpconf)
-
-        f = open(mpconf, 'wb')
-        try:
-            f.write(self.template %
-                    {'port': self.port, 'modulename': modulename,
-                     'host': self.host})
-        finally:
-            f.close()
-
-        result = read_process(APACHE_PATH, '-k start -f %s' % mpconf)
-        if result:
-            print(result)
-
-    def stop(self):
-        """Gracefully shutdown a server that is serving forever."""
-        read_process(APACHE_PATH, '-k stop')
-
-
-loaded = False
-
-
-def wsgisetup(req):
-    global loaded
-    if not loaded:
-        loaded = True
-        options = req.get_options()
-
-        cherrypy.config.update({
-            'log.error_file': os.path.join(curdir, 'test.log'),
-            'environment': 'test_suite',
-            'server.socket_host': options['socket_host'],
-        })
-
-        modname = options['testmod']
-        mod = __import__(modname, globals(), locals(), [''])
-        mod.setup_server()
-
-        cherrypy.server.unsubscribe()
-        cherrypy.engine.start()
-    from mod_python import apache
-    return apache.OK
-
-
-def cpmodpysetup(req):
-    global loaded
-    if not loaded:
-        loaded = True
-        options = req.get_options()
-
-        cherrypy.config.update({
-            'log.error_file': os.path.join(curdir, 'test.log'),
-            'environment': 'test_suite',
-            'server.socket_host': options['socket_host'],
-        })
-    from mod_python import apache
-    return apache.OK
diff --git a/libraries/cherrypy/test/modwsgi.py b/libraries/cherrypy/test/modwsgi.py
deleted file mode 100644
index f558e223..00000000
--- a/libraries/cherrypy/test/modwsgi.py
+++ /dev/null
@@ -1,154 +0,0 @@
-"""Wrapper for mod_wsgi, for use as a CherryPy HTTP server.
-
-To autostart modwsgi, the "apache" executable or script must be
-on your system path, or you must override the global APACHE_PATH.
-On some platforms, "apache" may be called "apachectl" or "apache2ctl"--
-create a symlink to them if needed.
-
-
-KNOWN BUGS
-==========
-
-##1. Apache processes Range headers automatically; CherryPy's truncated
-##    output is then truncated again by Apache. See test_core.testRanges.
-##    This was worked around in http://www.cherrypy.org/changeset/1319.
-2. Apache does not allow custom HTTP methods like CONNECT as per the spec.
-    See test_core.testHTTPMethods.
-3. Max request header and body settings do not work with Apache.
-##4. Apache replaces status "reason phrases" automatically. For example,
-##    CherryPy may set "304 Not modified" but Apache will write out
-##    "304 Not Modified" (capital "M").
-##5. Apache does not allow custom error codes as per the spec.
-##6. Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
-##    Request-URI too early.
-7. mod_wsgi will not read request bodies which use the "chunked"
-    transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block
-    instead of REQUEST_CHUNKED_DECHUNK, see Apache2's http_protocol.c and
-    mod_python's requestobject.c).
-8. When responding with 204 No Content, mod_wsgi adds a Content-Length
-    header for you.
-9. When an error is raised, mod_wsgi has no facility for printing a
-    traceback as the response content (it's sent to the Apache log instead).
-10. Startup and shutdown of Apache when running mod_wsgi seems slow.
-"""
-
-import os
-import re
-import sys
-import time
-
-import portend
-
-from cheroot.test import webtest
-
-import cherrypy
-from cherrypy.test import helper
-
-curdir = os.path.abspath(os.path.dirname(__file__))
-
-
-def read_process(cmd, args=''):
-    pipein, pipeout = os.popen4('%s %s' % (cmd, args))
-    try:
-        firstline = pipeout.readline()
-        if (re.search(r'(not recognized|No such file|not found)', firstline,
-                      re.IGNORECASE)):
-            raise IOError('%s must be on your system path.' % cmd)
-        output = firstline + pipeout.read()
-    finally:
-        pipeout.close()
-    return output
-
-
-if sys.platform == 'win32':
-    APACHE_PATH = 'httpd'
-else:
-    APACHE_PATH = 'apache'
-
-CONF_PATH = 'test_mw.conf'
-
-conf_modwsgi = r"""
-# Apache2 server conf file for testing CherryPy with modpython_gateway.
-
-ServerName 127.0.0.1
-DocumentRoot "/"
-Listen %(port)s
-
-AllowEncodedSlashes On
-LoadModule rewrite_module modules/mod_rewrite.so
-RewriteEngine on
-RewriteMap escaping int:escape
-
-LoadModule log_config_module modules/mod_log_config.so
-LogFormat "%%h %%l %%u %%t \"%%r\" %%>s %%b \"%%{Referer}i\" \"%%{User-agent}i\"" combined
-CustomLog "%(curdir)s/apache.access.log" combined
-ErrorLog "%(curdir)s/apache.error.log"
-LogLevel debug
-
-LoadModule wsgi_module modules/mod_wsgi.so
-LoadModule env_module modules/mod_env.so
-
-WSGIScriptAlias / "%(curdir)s/modwsgi.py"
-SetEnv testmod %(testmod)s
-""" # noqa E501
-
-
-class ModWSGISupervisor(helper.Supervisor):
-
-    """Server Controller for ModWSGI and CherryPy."""
-
-    using_apache = True
-    using_wsgi = True
-    template = conf_modwsgi
-
-    def __str__(self):
-        return 'ModWSGI Server on %s:%s' % (self.host, self.port)
-
-    def start(self, modulename):
-        mpconf = CONF_PATH
-        if not os.path.isabs(mpconf):
-            mpconf = os.path.join(curdir, mpconf)
-
-        f = open(mpconf, 'wb')
-        try:
-            output = (self.template %
-                      {'port': self.port, 'testmod': modulename,
-                       'curdir': curdir})
-            f.write(output)
-        finally:
-            f.close()
-
-        result = read_process(APACHE_PATH, '-k start -f %s' % mpconf)
-        if result:
-            print(result)
-
-        # Make a request so mod_wsgi starts up our app.
-        # If we don't, concurrent initial requests will 404.
-        portend.occupied('127.0.0.1', self.port, timeout=5)
-        webtest.openURL('/ihopetheresnodefault', port=self.port)
-        time.sleep(1)
-
-    def stop(self):
-        """Gracefully shutdown a server that is serving forever."""
-        read_process(APACHE_PATH, '-k stop')
-
-
-loaded = False
-
-
-def application(environ, start_response):
-    global loaded
-    if not loaded:
-        loaded = True
-        modname = 'cherrypy.test.' + environ['testmod']
-        mod = __import__(modname, globals(), locals(), [''])
-        mod.setup_server()
-
-        cherrypy.config.update({
-            'log.error_file': os.path.join(curdir, 'test.error.log'),
-            'log.access_file': os.path.join(curdir, 'test.access.log'),
-            'environment': 'test_suite',
-            'engine.SIGHUP': None,
-            'engine.SIGTERM': None,
-        })
-    return cherrypy.tree(environ, start_response)
diff --git a/libraries/cherrypy/test/sessiondemo.py b/libraries/cherrypy/test/sessiondemo.py
deleted file mode 100644
index 8226c1b9..00000000
--- a/libraries/cherrypy/test/sessiondemo.py
+++ /dev/null
@@ -1,161 +0,0 @@
-#!/usr/bin/python
-"""A session demonstration app."""
-
-import calendar
-from datetime import datetime
-import sys
-
-import six
-
-import cherrypy
-from cherrypy.lib import sessions
-
-
-page = """
-<html>
-<head>
-<style type='text/css'>
-table { border-collapse: collapse; border: 1px solid #663333; }
-th { text-align: right; background-color: #663333; color: white; padding: 0.5em; }
-td { white-space: pre-wrap; font-family: monospace; padding: 0.5em;
-     border: 1px solid #663333; }
-.warn { font-family: serif; color: #990000; }
-</style>
-<script type="text/javascript">
-<!--
-function twodigit(d) { return d < 10 ? "0" + d : d; }
-function formattime(t) {
-    var month = t.getUTCMonth() + 1;
-    var day = t.getUTCDate();
-    var year = t.getUTCFullYear();
-    var hours = t.getUTCHours();
-    var minutes = t.getUTCMinutes();
-    return (year + "/" + twodigit(month) + "/" + twodigit(day) + " " +
-            hours + ":" + twodigit(minutes) + " UTC");
-}
-
-function interval(s) {
-    // Return the given interval (in seconds) as an English phrase
-    var seconds = s %% 60;
-    s = Math.floor(s / 60);
-    var minutes = s %% 60;
-    s = Math.floor(s / 60);
-    var hours = s %% 24;
-    var v = twodigit(hours) + ":" + twodigit(minutes) + ":" + twodigit(seconds);
-    var days = Math.floor(s / 24);
-    if (days != 0) v = days + ' days, ' + v;
-    return v;
-}
-
-var fudge_seconds = 5;
-
-function init() {
-    // Set the content of the 'btime' cell.
-    var currentTime = new Date();
-    var bunixtime = Math.floor(currentTime.getTime() / 1000);
-
-    var v = formattime(currentTime);
-    v += " (Unix time: " + bunixtime + ")";
-
-    var diff = Math.abs(%(serverunixtime)s - bunixtime);
-    if (diff > fudge_seconds) v += "<p class='warn'>Browser and Server times disagree.</p>";
-
-    document.getElementById('btime').innerHTML = v;
-
-    // Warn if response cookie expires is not close to one hour in the future.
-    // Yes, we want this to happen when wit hit the 'Expire' link, too.
-    var expires = Date.parse("%(expires)s") / 1000;
-    var onehour = (60 * 60);
-    if (Math.abs(expires - (bunixtime + onehour)) > fudge_seconds) {
-        diff = Math.floor(expires - bunixtime);
-        if (expires > (bunixtime + onehour)) {
-            var msg = "Response cookie 'expires' date is " + interval(diff) + " in the future.";
-        } else {
-            var msg = "Response cookie 'expires' date is " + interval(0 - diff) + " in the past.";
-        }
-        document.getElementById('respcookiewarn').innerHTML = msg;
-    }
-}
-//-->
-</script>
-</head>
-
-<body onload='init()'>
-<h2>Session Demo</h2>
-<p>Reload this page. The session ID should not change from one reload to the next</p>
-<p><a href='../'>Index</a> | <a href='expire'>Expire</a> | <a href='regen'>Regenerate</a></p>
-<table>
-    <tr><th>Session ID:</th><td>%(sessionid)s<p class='warn'>%(changemsg)s</p></td></tr>
-    <tr><th>Request Cookie</th><td>%(reqcookie)s</td></tr>
-    <tr><th>Response Cookie</th><td>%(respcookie)s<p id='respcookiewarn' class='warn'></p></td></tr>
-    <tr><th>Session Data</th><td>%(sessiondata)s</td></tr>
-    <tr><th>Server Time</th><td id='stime'>%(servertime)s (Unix time: %(serverunixtime)s)</td></tr>
-    <tr><th>Browser Time</th><td id='btime'>&nbsp;</td></tr>
-    <tr><th>Cherrypy Version:</th><td>%(cpversion)s</td></tr>
-    <tr><th>Python Version:</th><td>%(pyversion)s</td></tr>
-</table>
-</body></html>
-"""  # noqa E501
-
-
-class Root(object):
-
-    def page(self):
-        changemsg = []
-        if cherrypy.session.id != cherrypy.session.originalid:
-            if cherrypy.session.originalid is None:
-                changemsg.append(
-                    'Created new session because no session id was given.')
-            if cherrypy.session.missing:
-                changemsg.append(
-                    'Created new session due to missing '
-                    '(expired or malicious) session.')
-            if cherrypy.session.regenerated:
-                changemsg.append('Application generated a new session.')
-
-        try:
-            expires = cherrypy.response.cookie['session_id']['expires']
-        except KeyError:
-            expires = ''
-
-        return page % {
-            'sessionid': cherrypy.session.id,
-            'changemsg': '<br>'.join(changemsg),
-            'respcookie': cherrypy.response.cookie.output(),
-            'reqcookie': cherrypy.request.cookie.output(),
-            'sessiondata': list(six.iteritems(cherrypy.session)),
-            'servertime': (
-                datetime.utcnow().strftime('%Y/%m/%d %H:%M') + ' UTC'
-            ),
-            'serverunixtime': calendar.timegm(datetime.utcnow().timetuple()),
-            'cpversion': cherrypy.__version__,
-            'pyversion': sys.version,
-            'expires': expires,
-        }
-
-    @cherrypy.expose
-    def index(self):
-        # Must modify data or the session will not be saved.
-        cherrypy.session['color'] = 'green'
-        return self.page()
-
-    @cherrypy.expose
-    def expire(self):
-        sessions.expire()
-        return self.page()
-
-    @cherrypy.expose
-    def regen(self):
-        cherrypy.session.regenerate()
-        # Must modify data or the session will not be saved.
-        cherrypy.session['color'] = 'yellow'
-        return self.page()
-
-
-if __name__ == '__main__':
-    cherrypy.config.update({
-        # 'environment': 'production',
-        'log.screen': True,
-        'tools.sessions.on': True,
-    })
-    cherrypy.quickstart(Root())
diff --git a/libraries/cherrypy/test/static/404.html b/libraries/cherrypy/test/static/404.html
deleted file mode 100644
index 01b17b09..00000000
--- a/libraries/cherrypy/test/static/404.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
- <body>
-  <h1>I couldn't find that thing you were looking for!</h1>
- </body>
-</html>
diff --git a/libraries/cherrypy/test/static/dirback.jpg b/libraries/cherrypy/test/static/dirback.jpg
deleted file mode 100644
index 80403dc2..00000000
Binary files a/libraries/cherrypy/test/static/dirback.jpg and /dev/null differ
diff --git a/libraries/cherrypy/test/static/index.html b/libraries/cherrypy/test/static/index.html
deleted file mode 100644
index a5c19667..00000000
--- a/libraries/cherrypy/test/static/index.html
+++ /dev/null
@@ -1 +0,0 @@
-Hello, world
diff --git a/libraries/cherrypy/test/style.css b/libraries/cherrypy/test/style.css
deleted file mode 100644
index b266e93d..00000000
--- a/libraries/cherrypy/test/style.css
+++ /dev/null
@@ -1 +0,0 @@
-Dummy stylesheet
diff --git a/libraries/cherrypy/test/test.pem b/libraries/cherrypy/test/test.pem
deleted file mode 100644
index 47a47042..00000000
--- a/libraries/cherrypy/test/test.pem
+++ /dev/null
@@ -1,38 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQDBKo554mzIMY+AByUNpaUOP9bJnQ7ZLQe9XgHwoLJR4VzpyZZZ
-R9L4WtImEew05FY3Izerfm3MN3+MC0tJ6yQU9sOiU3vBW6RrLIMlfKsnRwBRZ0Kn
-da+O6xldVSosu8Ev3z9VZ94iC/ZgKzrH7Mjj/U8/MQO7RBS/LAqee8bFNQIDAQAB
-AoGAWOCF0ZrWxn3XMucWq2LNwPKqlvVGwbIwX3cDmX22zmnM4Fy6arXbYh4XlyCj
-9+ofqRrxIFz5k/7tFriTmZ0xag5+Jdx+Kwg0/twiP7XCNKipFogwe1Hznw8OFAoT
-enKBdj2+/n2o0Bvo/tDB59m9L/538d46JGQUmJlzMyqYikECQQDyoq+8CtMNvE18
-8VgHcR/KtApxWAjj4HpaHYL637ATjThetUZkW92mgDgowyplthusxdNqhHWyv7E8
-tWNdYErZAkEAy85ShTR0M5aWmrE7o0r0SpWInAkNBH9aXQRRARFYsdBtNfRu6I0i
-0lvU9wiu3eF57FMEC86yViZ5UBnQfTu7vQJAVesj/Zt7pwaCDfdMa740OsxMUlyR
-MVhhGx4OLpYdPJ8qUecxGQKq13XZ7R1HGyNEY4bd2X80Smq08UFuATfC6QJAH8UB
-yBHtKz2GLIcELOg6PIYizW/7v3+6rlVF60yw7sb2vzpjL40QqIn4IKoR2DSVtOkb
-8FtAIX3N21aq0VrGYQJBAIPiaEc2AZ8Bq2GC4F3wOz/BxJ/izvnkiotR12QK4fh5
-yjZMhTjWCas5zwHR5PDjlD88AWGDMsZ1PicD4348xJQ=
------END RSA PRIVATE KEY-----
------BEGIN CERTIFICATE-----
-MIIDxTCCAy6gAwIBAgIJAI18BD7eQxlGMA0GCSqGSIb3DQEBBAUAMIGeMQswCQYD
-VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAGA1UEBxMJU2FuIERpZWdv
-MRkwFwYDVQQKExBDaGVycnlQeSBQcm9qZWN0MREwDwYDVQQLEwhkZXYtdGVzdDEW
-MBQGA1UEAxMNQ2hlcnJ5UHkgVGVhbTEgMB4GCSqGSIb3DQEJARYRcmVtaUBjaGVy
-cnlweS5vcmcwHhcNMDYwOTA5MTkyMDIwWhcNMzQwMTI0MTkyMDIwWjCBnjELMAkG
-A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVNhbiBEaWVn
-bzEZMBcGA1UEChMQQ2hlcnJ5UHkgUHJvamVjdDERMA8GA1UECxMIZGV2LXRlc3Qx
-FjAUBgNVBAMTDUNoZXJyeVB5IFRlYW0xIDAeBgkqhkiG9w0BCQEWEXJlbWlAY2hl
-cnJ5cHkub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBKo554mzIMY+A
-ByUNpaUOP9bJnQ7ZLQe9XgHwoLJR4VzpyZZZR9L4WtImEew05FY3Izerfm3MN3+M
-C0tJ6yQU9sOiU3vBW6RrLIMlfKsnRwBRZ0Knda+O6xldVSosu8Ev3z9VZ94iC/Zg
-KzrH7Mjj/U8/MQO7RBS/LAqee8bFNQIDAQABo4IBBzCCAQMwHQYDVR0OBBYEFDIQ
-2feb71tVZCWpU0qJ/Tw+wdtoMIHTBgNVHSMEgcswgciAFDIQ2feb71tVZCWpU0qJ
-/Tw+wdtooYGkpIGhMIGeMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5p
-YTESMBAGA1UEBxMJU2FuIERpZWdvMRkwFwYDVQQKExBDaGVycnlQeSBQcm9qZWN0
-MREwDwYDVQQLEwhkZXYtdGVzdDEWMBQGA1UEAxMNQ2hlcnJ5UHkgVGVhbTEgMB4G
-CSqGSIb3DQEJARYRcmVtaUBjaGVycnlweS5vcmeCCQCNfAQ+3kMZRjAMBgNVHRME
-BTADAQH/MA0GCSqGSIb3DQEBBAUAA4GBAL7AAQz7IePV48ZTAFHKr88ntPALsL5S
-8vHCZPNMevNkLTj3DYUw2BcnENxMjm1kou2F2BkvheBPNZKIhc6z4hAml3ed1xa2
-D7w6e6OTcstdK/+KrPDDHeOP1dhMWNs2JE1bNlfF1LiXzYKSXpe88eCKjCXsCT/T
-NluCaWQys3MS
------END CERTIFICATE-----
diff --git a/libraries/cherrypy/test/test_auth_basic.py b/libraries/cherrypy/test/test_auth_basic.py
deleted file mode 100644
index d7e69a9b..00000000
--- a/libraries/cherrypy/test/test_auth_basic.py
+++ /dev/null
@@ -1,135 +0,0 @@
-# This file is part of CherryPy <http://www.cherrypy.org/>
-# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:expandtab:fileencoding=utf-8
-
-from hashlib import md5
-
-import cherrypy
-from cherrypy._cpcompat import ntob
-from cherrypy.lib import auth_basic
-from cherrypy.test import helper
-
-
-class BasicAuthTest(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-        class Root:
-
-            @cherrypy.expose
-            def index(self):
-                return 'This is public.'
-
-        class BasicProtected:
-
-            @cherrypy.expose
-            def index(self):
-                return "Hello %s, you've been authorized." % (
-                    cherrypy.request.login)
-
-        class BasicProtected2:
-
-            @cherrypy.expose
-            def index(self):
-                return "Hello %s, you've been authorized." % (
-                    cherrypy.request.login)
-
-        class BasicProtected2_u:
-
-            @cherrypy.expose
-            def index(self):
-                return "Hello %s, you've been authorized." % (
-                    cherrypy.request.login)
-
-        userpassdict = {'xuser': 'xpassword'}
-        userhashdict = {'xuser': md5(b'xpassword').hexdigest()}
-        userhashdict_u = {'xюзер': md5(ntob('їжа', 'utf-8')).hexdigest()}
-
-        def checkpasshash(realm, user, password):
-            p = userhashdict.get(user)
-            return p and p == md5(ntob(password)).hexdigest() or False
-
-        def checkpasshash_u(realm, user, password):
-            p = userhashdict_u.get(user)
-            return p and p == md5(ntob(password, 'utf-8')).hexdigest() or False
-
-        basic_checkpassword_dict = auth_basic.checkpassword_dict(userpassdict)
-        conf = {
-            '/basic': {
-                'tools.auth_basic.on': True,
-                'tools.auth_basic.realm': 'wonderland',
-                'tools.auth_basic.checkpassword': basic_checkpassword_dict
-            },
-            '/basic2': {
-                'tools.auth_basic.on': True,
-                'tools.auth_basic.realm': 'wonderland',
-                'tools.auth_basic.checkpassword': checkpasshash,
-                'tools.auth_basic.accept_charset': 'ISO-8859-1',
-            },
-            '/basic2_u': {
-                'tools.auth_basic.on': True,
-                'tools.auth_basic.realm': 'wonderland',
-                'tools.auth_basic.checkpassword': checkpasshash_u,
-                'tools.auth_basic.accept_charset': 'UTF-8',
-            },
-        }
-
-        root = Root()
-        root.basic = BasicProtected()
-        root.basic2 = BasicProtected2()
-        root.basic2_u = BasicProtected2_u()
-        cherrypy.tree.mount(root, config=conf)
-
-    def testPublic(self):
-        self.getPage('/')
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html;charset=utf-8')
-        self.assertBody('This is public.')
-
-    def testBasic(self):
-        self.getPage('/basic/')
-        self.assertStatus(401)
-        self.assertHeader(
-            'WWW-Authenticate',
-            'Basic realm="wonderland", charset="UTF-8"'
-        )
-
-        self.getPage('/basic/',
-                     [('Authorization', 'Basic eHVzZXI6eHBhc3N3b3JX')])
-        self.assertStatus(401)
-
-        self.getPage('/basic/',
-                     [('Authorization', 'Basic eHVzZXI6eHBhc3N3b3Jk')])
-        self.assertStatus('200 OK')
-        self.assertBody("Hello xuser, you've been authorized.")
-
-    def testBasic2(self):
-        self.getPage('/basic2/')
-        self.assertStatus(401)
-        self.assertHeader('WWW-Authenticate', 'Basic realm="wonderland"')
-
-        self.getPage('/basic2/',
-                     [('Authorization', 'Basic eHVzZXI6eHBhc3N3b3JX')])
-        self.assertStatus(401)
-
-        self.getPage('/basic2/',
-                     [('Authorization', 'Basic eHVzZXI6eHBhc3N3b3Jk')])
-        self.assertStatus('200 OK')
-        self.assertBody("Hello xuser, you've been authorized.")
-
-    def testBasic2_u(self):
-        self.getPage('/basic2_u/')
-        self.assertStatus(401)
-        self.assertHeader(
-            'WWW-Authenticate',
-            'Basic realm="wonderland", charset="UTF-8"'
-        )
-
-        self.getPage('/basic2_u/',
-                     [('Authorization', 'Basic eNGO0LfQtdGAOtGX0LbRgw==')])
-        self.assertStatus(401)
-
-        self.getPage('/basic2_u/',
-                     [('Authorization', 'Basic eNGO0LfQtdGAOtGX0LbQsA==')])
-        self.assertStatus('200 OK')
-        self.assertBody("Hello xюзер, you've been authorized.")
diff --git a/libraries/cherrypy/test/test_auth_digest.py b/libraries/cherrypy/test/test_auth_digest.py
deleted file mode 100644
index 512e39a5..00000000
--- a/libraries/cherrypy/test/test_auth_digest.py
+++ /dev/null
@@ -1,134 +0,0 @@
-# This file is part of CherryPy <http://www.cherrypy.org/>
-# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:expandtab:fileencoding=utf-8
-
-import six
-
-
-import cherrypy
-from cherrypy.lib import auth_digest
-from cherrypy._cpcompat import ntob
-
-from cherrypy.test import helper
-
-
-def _fetch_users():
-    return {'test': 'test', '☃йюзер': 'їпароль'}
-
-
-get_ha1 = cherrypy.lib.auth_digest.get_ha1_dict_plain(_fetch_users())
-
-
-class DigestAuthTest(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-        class Root:
-
-            @cherrypy.expose
-            def index(self):
-                return 'This is public.'
-
-        class DigestProtected:
-
-            @cherrypy.expose
-            def index(self, *args, **kwargs):
-                return "Hello %s, you've been authorized." % (
-                    cherrypy.request.login)
-
-        conf = {'/digest': {'tools.auth_digest.on': True,
-                            'tools.auth_digest.realm': 'localhost',
-                            'tools.auth_digest.get_ha1': get_ha1,
-                            'tools.auth_digest.key': 'a565c27146791cfb',
-                            'tools.auth_digest.debug': True,
-                            'tools.auth_digest.accept_charset': 'UTF-8'}}
-
-        root = Root()
-        root.digest = DigestProtected()
-        cherrypy.tree.mount(root, config=conf)
-
-    def testPublic(self):
-        self.getPage('/')
-        assert self.status == '200 OK'
-        self.assertHeader('Content-Type', 'text/html;charset=utf-8')
-        assert self.body == b'This is public.'
-
-    def _test_parametric_digest(self, username, realm):
-        test_uri = '/digest/?@/=%2F%40&%f0%9f%99%88=path'
-
-        self.getPage(test_uri)
-        assert self.status_code == 401
-
-        msg = 'Digest authentification scheme was not found'
-        www_auth_digest = tuple(filter(
-            lambda kv: kv[0].lower() == 'www-authenticate'
-            and kv[1].startswith('Digest '),
-            self.headers,
-        ))
-        assert len(www_auth_digest) == 1, msg
-
-        items = www_auth_digest[0][-1][7:].split(', ')
-        tokens = {}
-        for item in items:
-            key, value = item.split('=')
-            tokens[key.lower()] = value
-
-        assert tokens['realm'] == '"localhost"'
-        assert tokens['algorithm'] == '"MD5"'
-        assert tokens['qop'] == '"auth"'
-        assert tokens['charset'] == '"UTF-8"'
-
-        nonce = tokens['nonce'].strip('"')
-
-        # Test user agent response with a wrong value for 'realm'
-        base_auth = ('Digest username="%s", '
-                     'realm="%s", '
-                     'nonce="%s", '
-                     'uri="%s", '
-                     'algorithm=MD5, '
-                     'response="%s", '
-                     'qop=auth, '
-                     'nc=%s, '
-                     'cnonce="1522e61005789929"')
-
-        encoded_user = username
-        if six.PY3:
-            encoded_user = encoded_user.encode('utf-8')
-        encoded_user = encoded_user.decode('latin1')
-        auth_header = base_auth % (
-            encoded_user, realm, nonce, test_uri,
-            '11111111111111111111111111111111', '00000001',
-        )
-        auth = auth_digest.HttpDigestAuthorization(auth_header, 'GET')
-        # calculate the response digest
-        ha1 = get_ha1(auth.realm, auth.username)
-        response = auth.request_digest(ha1)
-        auth_header = base_auth % (
-            encoded_user, realm, nonce, test_uri,
-            response, '00000001',
-        )
-        self.getPage(test_uri, [('Authorization', auth_header)])
-
-    def test_wrong_realm(self):
-        # send response with correct response digest, but wrong realm
-        self._test_parametric_digest(username='test', realm='wrong realm')
-        assert self.status_code == 401
-
-    def test_ascii_user(self):
-        self._test_parametric_digest(username='test', realm='localhost')
-        assert self.status == '200 OK'
-        assert self.body == b"Hello test, you've been authorized."
-
-    def test_unicode_user(self):
-        self._test_parametric_digest(username='☃йюзер', realm='localhost')
-        assert self.status == '200 OK'
-        assert self.body == ntob(
-            "Hello ☃йюзер, you've been authorized.", 'utf-8',
-        )
-
-    def test_wrong_scheme(self):
-        basic_auth = {
-            'Authorization': 'Basic foo:bar',
-        }
-        self.getPage('/digest/', headers=list(basic_auth.items()))
-        assert self.status_code == 401
diff --git a/libraries/cherrypy/test/test_bus.py b/libraries/cherrypy/test/test_bus.py
deleted file mode 100644
index 6026b47e..00000000
--- a/libraries/cherrypy/test/test_bus.py
+++ /dev/null
@@ -1,274 +0,0 @@
-import threading
-import time
-import unittest
-
-from cherrypy.process import wspbus
-
-
-msg = 'Listener %d on channel %s: %s.'
-
-
-class PublishSubscribeTests(unittest.TestCase):
-
-    def get_listener(self, channel, index):
-        def listener(arg=None):
-            self.responses.append(msg % (index, channel, arg))
-        return listener
-
-    def test_builtin_channels(self):
-        b = wspbus.Bus()
-
-        self.responses, expected = [], []
-
-        for channel in b.listeners:
-            for index, priority in enumerate([100, 50, 0, 51]):
-                b.subscribe(channel,
-                            self.get_listener(channel, index), priority)
-
-        for channel in b.listeners:
-            b.publish(channel)
-            expected.extend([msg % (i, channel, None) for i in (2, 1, 3, 0)])
-            b.publish(channel, arg=79347)
-            expected.extend([msg % (i, channel, 79347) for i in (2, 1, 3, 0)])
-
-        self.assertEqual(self.responses, expected)
-
-    def test_custom_channels(self):
-        b = wspbus.Bus()
-
-        self.responses, expected = [], []
-
-        custom_listeners = ('hugh', 'louis', 'dewey')
-        for channel in custom_listeners:
-            for index, priority in enumerate([None, 10, 60, 40]):
-                b.subscribe(channel,
-                            self.get_listener(channel, index), priority)
-
-        for channel in custom_listeners:
-            b.publish(channel, 'ah so')
-            expected.extend([msg % (i, channel, 'ah so')
-                            for i in (1, 3, 0, 2)])
-            b.publish(channel)
-            expected.extend([msg % (i, channel, None) for i in (1, 3, 0, 2)])
-
-        self.assertEqual(self.responses, expected)
-
-    def test_listener_errors(self):
-        b = wspbus.Bus()
-
-        self.responses, expected = [], []
-        channels = [c for c in b.listeners if c != 'log']
-
-        for channel in channels:
-            b.subscribe(channel, self.get_listener(channel, 1))
-            # This will break since the lambda takes no args.
-            b.subscribe(channel, lambda: None, priority=20)
-
-        for channel in channels:
-            self.assertRaises(wspbus.ChannelFailures, b.publish, channel, 123)
-            expected.append(msg % (1, channel, 123))
-
-        self.assertEqual(self.responses, expected)
-
-
-class BusMethodTests(unittest.TestCase):
-
-    def log(self, bus):
-        self._log_entries = []
-
-        def logit(msg, level):
-            self._log_entries.append(msg)
-        bus.subscribe('log', logit)
-
-    def assertLog(self, entries):
-        self.assertEqual(self._log_entries, entries)
-
-    def get_listener(self, channel, index):
-        def listener(arg=None):
-            self.responses.append(msg % (index, channel, arg))
-        return listener
-
-    def test_start(self):
-        b = wspbus.Bus()
-        self.log(b)
-
-        self.responses = []
-        num = 3
-        for index in range(num):
-            b.subscribe('start', self.get_listener('start', index))
-
-        b.start()
-        try:
-            # The start method MUST call all 'start' listeners.
-            self.assertEqual(
-                set(self.responses),
-                set([msg % (i, 'start', None) for i in range(num)]))
-            # The start method MUST move the state to STARTED
-            # (or EXITING, if errors occur)
-            self.assertEqual(b.state, b.states.STARTED)
-            # The start method MUST log its states.
-            self.assertLog(['Bus STARTING', 'Bus STARTED'])
-        finally:
-            # Exit so the atexit handler doesn't complain.
-            b.exit()
-
-    def test_stop(self):
-        b = wspbus.Bus()
-        self.log(b)
-
-        self.responses = []
-        num = 3
-        for index in range(num):
-            b.subscribe('stop', self.get_listener('stop', index))
-
-        b.stop()
-
-        # The stop method MUST call all 'stop' listeners.
-        self.assertEqual(set(self.responses),
-                         set([msg % (i, 'stop', None) for i in range(num)]))
-        # The stop method MUST move the state to STOPPED
-        self.assertEqual(b.state, b.states.STOPPED)
-        # The stop method MUST log its states.
-        self.assertLog(['Bus STOPPING', 'Bus STOPPED'])
-
-    def test_graceful(self):
-        b = wspbus.Bus()
-        self.log(b)
-
-        self.responses = []
-        num = 3
-        for index in range(num):
-            b.subscribe('graceful', self.get_listener('graceful', index))
-
-        b.graceful()
-
-        # The graceful method MUST call all 'graceful' listeners.
-        self.assertEqual(
-            set(self.responses),
-            set([msg % (i, 'graceful', None) for i in range(num)]))
-        # The graceful method MUST log its states.
-        self.assertLog(['Bus graceful'])
-
-    def test_exit(self):
-        b = wspbus.Bus()
-        self.log(b)
-
-        self.responses = []
-        num = 3
-        for index in range(num):
-            b.subscribe('stop', self.get_listener('stop', index))
-            b.subscribe('exit', self.get_listener('exit', index))
-
-        b.exit()
-
-        # The exit method MUST call all 'stop' listeners,
-        # and then all 'exit' listeners.
-        self.assertEqual(set(self.responses),
-                         set([msg % (i, 'stop', None) for i in range(num)] +
-                             [msg % (i, 'exit', None) for i in range(num)]))
-        # The exit method MUST move the state to EXITING
-        self.assertEqual(b.state, b.states.EXITING)
-        # The exit method MUST log its states.
-        self.assertLog(
-            ['Bus STOPPING', 'Bus STOPPED', 'Bus EXITING', 'Bus EXITED'])
-
-    def test_wait(self):
-        b = wspbus.Bus()
-
-        def f(method):
-            time.sleep(0.2)
-            getattr(b, method)()
-
-        for method, states in [('start', [b.states.STARTED]),
-                               ('stop', [b.states.STOPPED]),
-                               ('start',
-                                [b.states.STARTING, b.states.STARTED]),
-                               ('exit', [b.states.EXITING]),
-                               ]:
-            threading.Thread(target=f, args=(method,)).start()
-            b.wait(states)
-
-            # The wait method MUST wait for the given state(s).
-            if b.state not in states:
-                self.fail('State %r not in %r' % (b.state, states))
-
-    def test_block(self):
-        b = wspbus.Bus()
-        self.log(b)
-
-        def f():
-            time.sleep(0.2)
-            b.exit()
-
-        def g():
-            time.sleep(0.4)
-        threading.Thread(target=f).start()
-        threading.Thread(target=g).start()
-        threads = [t for t in threading.enumerate() if not t.daemon]
-        self.assertEqual(len(threads), 3)
-
-        b.block()
-
-        # The block method MUST wait for the EXITING state.
-        self.assertEqual(b.state, b.states.EXITING)
-        # The block method MUST wait for ALL non-main, non-daemon threads to
-        # finish.
-        threads = [t for t in threading.enumerate() if not t.daemon]
-        self.assertEqual(len(threads), 1)
-        # The last message will mention an indeterminable thread name; ignore
-        # it
-        self.assertEqual(self._log_entries[:-1],
-                         ['Bus STOPPING', 'Bus STOPPED',
-                          'Bus EXITING', 'Bus EXITED',
-                          'Waiting for child threads to terminate...'])
-
-    def test_start_with_callback(self):
-        b = wspbus.Bus()
-        self.log(b)
-        try:
-            events = []
-
-            def f(*args, **kwargs):
-                events.append(('f', args, kwargs))
-
-            def g():
-                events.append('g')
-            b.subscribe('start', g)
-            b.start_with_callback(f, (1, 3, 5), {'foo': 'bar'})
-            # Give wait() time to run f()
-            time.sleep(0.2)
-
-            # The callback method MUST wait for the STARTED state.
-            self.assertEqual(b.state, b.states.STARTED)
-            # The callback method MUST run after all start methods.
-            self.assertEqual(events, ['g', ('f', (1, 3, 5), {'foo': 'bar'})])
-        finally:
-            b.exit()
-
-    def test_log(self):
-        b = wspbus.Bus()
-        self.log(b)
-        self.assertLog([])
-
-        # Try a normal message.
-        expected = []
-        for msg in ["O mah darlin'"] * 3 + ['Clementiiiiiiiine']:
-            b.log(msg)
-            expected.append(msg)
-            self.assertLog(expected)
-
-        # Try an error message
-        try:
-            foo
-        except NameError:
-            b.log('You are lost and gone forever', traceback=True)
-            lastmsg = self._log_entries[-1]
-            if 'Traceback' not in lastmsg or 'NameError' not in lastmsg:
-                self.fail('Last log message %r did not contain '
-                          'the expected traceback.' % lastmsg)
-        else:
-            self.fail('NameError was not raised as expected.')
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/libraries/cherrypy/test/test_caching.py b/libraries/cherrypy/test/test_caching.py
deleted file mode 100644
index 1a6ed4f2..00000000
--- a/libraries/cherrypy/test/test_caching.py
+++ /dev/null
@@ -1,392 +0,0 @@
-import datetime
-from itertools import count
-import os
-import threading
-import time
-
-from six.moves import range
-from six.moves import urllib
-
-import pytest
-
-import cherrypy
-from cherrypy.lib import httputil
-
-from cherrypy.test import helper
-
-
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-gif_bytes = (
-    b'GIF89a\x01\x00\x01\x00\x82\x00\x01\x99"\x1e\x00\x00\x00\x00\x00'
-    b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
-    b'\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x02\x03\x02\x08\t\x00;'
-)
-
-
-class CacheTest(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-
-        @cherrypy.config(**{'tools.caching.on': True})
-        class Root:
-
-            def __init__(self):
-                self.counter = 0
-                self.control_counter = 0
-                self.longlock = threading.Lock()
-
-            @cherrypy.expose
-            def index(self):
-                self.counter += 1
-                msg = 'visit #%s' % self.counter
-                return msg
-
-            @cherrypy.expose
-            def control(self):
-                self.control_counter += 1
-                return 'visit #%s' % self.control_counter
-
-            @cherrypy.expose
-            def a_gif(self):
-                cherrypy.response.headers[
-                    'Last-Modified'] = httputil.HTTPDate()
-                return gif_bytes
-
-            @cherrypy.expose
-            def long_process(self, seconds='1'):
-                try:
-                    self.longlock.acquire()
-                    time.sleep(float(seconds))
-                finally:
-                    self.longlock.release()
-                return 'success!'
-
-            @cherrypy.expose
-            def clear_cache(self, path):
-                cherrypy._cache.store[cherrypy.request.base + path].clear()
-
-        @cherrypy.config(**{
-            'tools.caching.on': True,
-            'tools.response_headers.on': True,
-            'tools.response_headers.headers': [
-                ('Vary', 'Our-Varying-Header')
-            ],
-        })
-        class VaryHeaderCachingServer(object):
-
-            def __init__(self):
-                self.counter = count(1)
-
-            @cherrypy.expose
-            def index(self):
-                return 'visit #%s' % next(self.counter)
-
-        @cherrypy.config(**{
-            'tools.expires.on': True,
-            'tools.expires.secs': 60,
-            'tools.staticdir.on': True,
-            'tools.staticdir.dir': 'static',
-            'tools.staticdir.root': curdir,
-        })
-        class UnCached(object):
-
-            @cherrypy.expose
-            @cherrypy.config(**{'tools.expires.secs': 0})
-            def force(self):
-                cherrypy.response.headers['Etag'] = 'bibbitybobbityboo'
-                self._cp_config['tools.expires.force'] = True
-                self._cp_config['tools.expires.secs'] = 0
-                return 'being forceful'
-
-            @cherrypy.expose
-            def dynamic(self):
-                cherrypy.response.headers['Etag'] = 'bibbitybobbityboo'
-                cherrypy.response.headers['Cache-Control'] = 'private'
-                return 'D-d-d-dynamic!'
-
-            @cherrypy.expose
-            def cacheable(self):
-                cherrypy.response.headers['Etag'] = 'bibbitybobbityboo'
-                return "Hi, I'm cacheable."
-
-            @cherrypy.expose
-            @cherrypy.config(**{'tools.expires.secs': 86400})
-            def specific(self):
-                cherrypy.response.headers[
-                    'Etag'] = 'need_this_to_make_me_cacheable'
-                return 'I am being specific'
-
-            class Foo(object):
-                pass
-
-            @cherrypy.expose
-            @cherrypy.config(**{'tools.expires.secs': Foo()})
-            def wrongtype(self):
-                cherrypy.response.headers[
-                    'Etag'] = 'need_this_to_make_me_cacheable'
-                return 'Woops'
-
-        @cherrypy.config(**{
-            'tools.gzip.mime_types': ['text/*', 'image/*'],
-            'tools.caching.on': True,
-            'tools.staticdir.on': True,
-            'tools.staticdir.dir': 'static',
-            'tools.staticdir.root': curdir
-        })
-        class GzipStaticCache(object):
-            pass
-
-        cherrypy.tree.mount(Root())
-        cherrypy.tree.mount(UnCached(), '/expires')
-        cherrypy.tree.mount(VaryHeaderCachingServer(), '/varying_headers')
-        cherrypy.tree.mount(GzipStaticCache(), '/gzip_static_cache')
-        cherrypy.config.update({'tools.gzip.on': True})
-
-    def testCaching(self):
-        elapsed = 0.0
-        for trial in range(10):
-            self.getPage('/')
-            # The response should be the same every time,
-            # except for the Age response header.
-            self.assertBody('visit #1')
-            if trial != 0:
-                age = int(self.assertHeader('Age'))
-                self.assert_(age >= elapsed)
-                elapsed = age
-
-        # POST, PUT, DELETE should not be cached.
-        self.getPage('/', method='POST')
-        self.assertBody('visit #2')
-        # Because gzip is turned on, the Vary header should always Vary for
-        # content-encoding
-        self.assertHeader('Vary', 'Accept-Encoding')
-        # The previous request should have invalidated the cache,
-        # so this request will recalc the response.
-        self.getPage('/', method='GET')
-        self.assertBody('visit #3')
-        # ...but this request should get the cached copy.
-        self.getPage('/', method='GET')
-        self.assertBody('visit #3')
-        self.getPage('/', method='DELETE')
-        self.assertBody('visit #4')
-
-        # The previous request should have invalidated the cache,
-        # so this request will recalc the response.
-        self.getPage('/', method='GET', headers=[('Accept-Encoding', 'gzip')])
-        self.assertHeader('Content-Encoding', 'gzip')
-        self.assertHeader('Vary')
-        self.assertEqual(
-            cherrypy.lib.encoding.decompress(self.body), b'visit #5')
-
-        # Now check that a second request gets the gzip header and gzipped body
-        # This also tests a bug in 3.0 to 3.0.2 whereby the cached, gzipped
-        # response body was being gzipped a second time.
-        self.getPage('/', method='GET', headers=[('Accept-Encoding', 'gzip')])
-        self.assertHeader('Content-Encoding', 'gzip')
-        self.assertEqual(
-            cherrypy.lib.encoding.decompress(self.body), b'visit #5')
-
-        # Now check that a third request that doesn't accept gzip
-        # skips the cache (because the 'Vary' header denies it).
-        self.getPage('/', method='GET')
-        self.assertNoHeader('Content-Encoding')
-        self.assertBody('visit #6')
-
-    def testVaryHeader(self):
-        self.getPage('/varying_headers/')
-        self.assertStatus('200 OK')
-        self.assertHeaderItemValue('Vary', 'Our-Varying-Header')
-        self.assertBody('visit #1')
-
-        # Now check that different 'Vary'-fields don't evict each other.
-        # This test creates 2 requests with different 'Our-Varying-Header'
-        # and then tests if the first one still exists.
-        self.getPage('/varying_headers/',
-                     headers=[('Our-Varying-Header', 'request 2')])
-        self.assertStatus('200 OK')
-        self.assertBody('visit #2')
-
-        self.getPage('/varying_headers/',
-                     headers=[('Our-Varying-Header', 'request 2')])
-        self.assertStatus('200 OK')
-        self.assertBody('visit #2')
-
-        self.getPage('/varying_headers/')
-        self.assertStatus('200 OK')
-        self.assertBody('visit #1')
-
-    def testExpiresTool(self):
-        # test setting an expires header
-        self.getPage('/expires/specific')
-        self.assertStatus('200 OK')
-        self.assertHeader('Expires')
-
-        # test exceptions for bad time values
-        self.getPage('/expires/wrongtype')
-        self.assertStatus(500)
-        self.assertInBody('TypeError')
-
-        # static content should not have "cache prevention" headers
-        self.getPage('/expires/index.html')
-        self.assertStatus('200 OK')
-        self.assertNoHeader('Pragma')
-        self.assertNoHeader('Cache-Control')
-        self.assertHeader('Expires')
-
-        # dynamic content that sets indicators should not have
-        # "cache prevention" headers
-        self.getPage('/expires/cacheable')
-        self.assertStatus('200 OK')
-        self.assertNoHeader('Pragma')
-        self.assertNoHeader('Cache-Control')
-        self.assertHeader('Expires')
-
-        self.getPage('/expires/dynamic')
-        self.assertBody('D-d-d-dynamic!')
-        # the Cache-Control header should be untouched
-        self.assertHeader('Cache-Control', 'private')
-        self.assertHeader('Expires')
-
-        # configure the tool to ignore indicators and replace existing headers
-        self.getPage('/expires/force')
-        self.assertStatus('200 OK')
-        # This also gives us a chance to test 0 expiry with no other headers
-        self.assertHeader('Pragma', 'no-cache')
-        if cherrypy.server.protocol_version == 'HTTP/1.1':
-            self.assertHeader('Cache-Control', 'no-cache, must-revalidate')
-        self.assertHeader('Expires', 'Sun, 28 Jan 2007 00:00:00 GMT')
-
-        # static content should now have "cache prevention" headers
-        self.getPage('/expires/index.html')
-        self.assertStatus('200 OK')
-        self.assertHeader('Pragma', 'no-cache')
-        if cherrypy.server.protocol_version == 'HTTP/1.1':
-            self.assertHeader('Cache-Control', 'no-cache, must-revalidate')
-        self.assertHeader('Expires', 'Sun, 28 Jan 2007 00:00:00 GMT')
-
-        # the cacheable handler should now have "cache prevention" headers
-        self.getPage('/expires/cacheable')
-        self.assertStatus('200 OK')
-        self.assertHeader('Pragma', 'no-cache')
-        if cherrypy.server.protocol_version == 'HTTP/1.1':
-            self.assertHeader('Cache-Control', 'no-cache, must-revalidate')
-        self.assertHeader('Expires', 'Sun, 28 Jan 2007 00:00:00 GMT')
-
-        self.getPage('/expires/dynamic')
-        self.assertBody('D-d-d-dynamic!')
-        # dynamic sets Cache-Control to private but it should  be
-        # overwritten here ...
-        self.assertHeader('Pragma', 'no-cache')
-        if cherrypy.server.protocol_version == 'HTTP/1.1':
-            self.assertHeader('Cache-Control', 'no-cache, must-revalidate')
-        self.assertHeader('Expires', 'Sun, 28 Jan 2007 00:00:00 GMT')
-
-    def _assert_resp_len_and_enc_for_gzip(self, uri):
-        """
-        Test that after querying gzipped content it's remains valid in
-        cache and available non-gzipped as well.
-        """
-        ACCEPT_GZIP_HEADERS = [('Accept-Encoding', 'gzip')]
-        content_len = None
-
-        for _ in range(3):
-            self.getPage(uri, method='GET', headers=ACCEPT_GZIP_HEADERS)
-
-            if content_len is not None:
-                # all requests should get the same length
-                self.assertHeader('Content-Length', content_len)
-                self.assertHeader('Content-Encoding', 'gzip')
-
-            content_len = dict(self.headers)['Content-Length']
-
-        # check that we can still get non-gzipped version
-        self.getPage(uri, method='GET')
-        self.assertNoHeader('Content-Encoding')
-        # non-gzipped version should have a different content length
-        self.assertNoHeaderItemValue('Content-Length', content_len)
-
-    def testGzipStaticCache(self):
-        """Test that cache and gzip tools play well together when both enabled.
-
-        Ref GitHub issue #1190.
-        """
-        GZIP_STATIC_CACHE_TMPL = '/gzip_static_cache/{}'
-        resource_files = ('index.html', 'dirback.jpg')
-
-        for f in resource_files:
-            uri = GZIP_STATIC_CACHE_TMPL.format(f)
-            self._assert_resp_len_and_enc_for_gzip(uri)
-
-    def testLastModified(self):
-        self.getPage('/a.gif')
-        self.assertStatus(200)
-        self.assertBody(gif_bytes)
-        lm1 = self.assertHeader('Last-Modified')
-
-        # this request should get the cached copy.
-        self.getPage('/a.gif')
-        self.assertStatus(200)
-        self.assertBody(gif_bytes)
-        self.assertHeader('Age')
-        lm2 = self.assertHeader('Last-Modified')
-        self.assertEqual(lm1, lm2)
-
-        # this request should match the cached copy, but raise 304.
-        self.getPage('/a.gif', [('If-Modified-Since', lm1)])
-        self.assertStatus(304)
-        self.assertNoHeader('Last-Modified')
-        if not getattr(cherrypy.server, 'using_apache', False):
-            self.assertHeader('Age')
-
-    @pytest.mark.xfail(reason='#1536')
-    def test_antistampede(self):
-        SECONDS = 4
-        slow_url = '/long_process?seconds={SECONDS}'.format(**locals())
-        # We MUST make an initial synchronous request in order to create the
-        # AntiStampedeCache object, and populate its selecting_headers,
-        # before the actual stampede.
-        self.getPage(slow_url)
-        self.assertBody('success!')
-        path = urllib.parse.quote(slow_url, safe='')
-        self.getPage('/clear_cache?path=' + path)
-        self.assertStatus(200)
-
-        start = datetime.datetime.now()
-
-        def run():
-            self.getPage(slow_url)
-            # The response should be the same every time
-            self.assertBody('success!')
-        ts = [threading.Thread(target=run) for i in range(100)]
-        for t in ts:
-            t.start()
-        for t in ts:
-            t.join()
-        finish = datetime.datetime.now()
-        # Allow for overhead, two seconds for slow hosts
-        allowance = SECONDS + 2
-        self.assertEqualDates(start, finish, seconds=allowance)
-
-    def test_cache_control(self):
-        self.getPage('/control')
-        self.assertBody('visit #1')
-        self.getPage('/control')
-        self.assertBody('visit #1')
-
-        self.getPage('/control', headers=[('Cache-Control', 'no-cache')])
-        self.assertBody('visit #2')
-        self.getPage('/control')
-        self.assertBody('visit #2')
-
-        self.getPage('/control', headers=[('Pragma', 'no-cache')])
-        self.assertBody('visit #3')
-        self.getPage('/control')
-        self.assertBody('visit #3')
-
-        time.sleep(1)
-        self.getPage('/control', headers=[('Cache-Control', 'max-age=0')])
-        self.assertBody('visit #4')
-        self.getPage('/control')
-        self.assertBody('visit #4')
diff --git a/libraries/cherrypy/test/test_compat.py b/libraries/cherrypy/test/test_compat.py
deleted file mode 100644
index 44a9fa31..00000000
--- a/libraries/cherrypy/test/test_compat.py
+++ /dev/null
@@ -1,34 +0,0 @@
-"""Test Python 2/3 compatibility module."""
-from __future__ import unicode_literals
-
-import unittest
-
-import pytest
-import six
-
-from cherrypy import _cpcompat as compat
-
-
-class StringTester(unittest.TestCase):
-    """Tests for string conversion."""
-
-    @pytest.mark.skipif(six.PY3, reason='Only useful on Python 2')
-    def test_ntob_non_native(self):
-        """ntob should raise an Exception on unicode.
-
-        (Python 2 only)
-
-        See #1132 for discussion.
-        """
-        self.assertRaises(TypeError, compat.ntob, 'fight')
-
-
-class EscapeTester(unittest.TestCase):
-    """Class to test escape_html function from _cpcompat."""
-
-    def test_escape_quote(self):
-        """test_escape_quote - Verify the output for &<>"' chars."""
-        self.assertEqual(
-            """xx&amp;&lt;&gt;"aa'""",
-            compat.escape_html("""xx&<>"aa'"""),
-        )
diff --git a/libraries/cherrypy/test/test_config.py b/libraries/cherrypy/test/test_config.py
deleted file mode 100644
index be17df90..00000000
--- a/libraries/cherrypy/test/test_config.py
+++ /dev/null
@@ -1,303 +0,0 @@
-"""Tests for the CherryPy configuration system."""
-
-import io
-import os
-import sys
-import unittest
-
-import six
-
-import cherrypy
-
-from cherrypy.test import helper
-
-
-localDir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-
-def StringIOFromNative(x):
-    return io.StringIO(six.text_type(x))
-
-
-def setup_server():
-
-    @cherrypy.config(foo='this', bar='that')
-    class Root:
-
-        def __init__(self):
-            cherrypy.config.namespaces['db'] = self.db_namespace
-
-        def db_namespace(self, k, v):
-            if k == 'scheme':
-                self.db = v
-
-        @cherrypy.expose(alias=('global_', 'xyz'))
-        def index(self, key):
-            return cherrypy.request.config.get(key, 'None')
-
-        @cherrypy.expose
-        def repr(self, key):
-            return repr(cherrypy.request.config.get(key, None))
-
-        @cherrypy.expose
-        def dbscheme(self):
-            return self.db
-
-        @cherrypy.expose
-        @cherrypy.config(**{'request.body.attempt_charsets': ['utf-16']})
-        def plain(self, x):
-            return x
-
-        favicon_ico = cherrypy.tools.staticfile.handler(
-            filename=os.path.join(localDir, '../favicon.ico'))
-
-    @cherrypy.config(foo='this2', baz='that2')
-    class Foo:
-
-        @cherrypy.expose
-        def index(self, key):
-            return cherrypy.request.config.get(key, 'None')
-        nex = index
-
-        @cherrypy.expose
-        @cherrypy.config(**{'response.headers.X-silly': 'sillyval'})
-        def silly(self):
-            return 'Hello world'
-
-        # Test the expose and config decorators
-        @cherrypy.config(foo='this3', **{'bax': 'this4'})
-        @cherrypy.expose
-        def bar(self, key):
-            return repr(cherrypy.request.config.get(key, None))
-
-    class Another:
-
-        @cherrypy.expose
-        def index(self, key):
-            return str(cherrypy.request.config.get(key, 'None'))
-
-    def raw_namespace(key, value):
-        if key == 'input.map':
-            handler = cherrypy.request.handler
-
-            def wrapper():
-                params = cherrypy.request.params
-                for name, coercer in list(value.items()):
-                    try:
-                        params[name] = coercer(params[name])
-                    except KeyError:
-                        pass
-                return handler()
-            cherrypy.request.handler = wrapper
-        elif key == 'output':
-            handler = cherrypy.request.handler
-
-            def wrapper():
-                # 'value' is a type (like int or str).
-                return value(handler())
-            cherrypy.request.handler = wrapper
-
-    @cherrypy.config(**{'raw.output': repr})
-    class Raw:
-
-        @cherrypy.expose
-        @cherrypy.config(**{'raw.input.map': {'num': int}})
-        def incr(self, num):
-            return num + 1
-
-    if not six.PY3:
-        thing3 = "thing3: unicode('test', errors='ignore')"
-    else:
-        thing3 = ''
-
-    ioconf = StringIOFromNative("""
-[/]
-neg: -1234
-filename: os.path.join(sys.prefix, "hello.py")
-thing1: cherrypy.lib.httputil.response_codes[404]
-thing2: __import__('cherrypy.tutorial', globals(), locals(), ['']).thing2
-%s
-complex: 3+2j
-mul: 6*3
-ones: "11"
-twos: "22"
-stradd: %%(ones)s + %%(twos)s + "33"
-
-[/favicon.ico]
-tools.staticfile.filename = %r
-""" % (thing3, os.path.join(localDir, 'static/dirback.jpg')))
-
-    root = Root()
-    root.foo = Foo()
-    root.raw = Raw()
-    app = cherrypy.tree.mount(root, config=ioconf)
-    app.request_class.namespaces['raw'] = raw_namespace
-
-    cherrypy.tree.mount(Another(), '/another')
-    cherrypy.config.update({'luxuryyacht': 'throatwobblermangrove',
-                            'db.scheme': r'sqlite///memory',
-                            })
-
-
-#                             Client-side code                             #
-
-
-class ConfigTests(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def testConfig(self):
-        tests = [
-            ('/', 'nex', 'None'),
-            ('/', 'foo', 'this'),
-            ('/', 'bar', 'that'),
-            ('/xyz', 'foo', 'this'),
-            ('/foo/', 'foo', 'this2'),
-            ('/foo/', 'bar', 'that'),
-            ('/foo/', 'bax', 'None'),
-            ('/foo/bar', 'baz', "'that2'"),
-            ('/foo/nex', 'baz', 'that2'),
-            # If 'foo' == 'this', then the mount point '/another' leaks into
-            # '/'.
-            ('/another/', 'foo', 'None'),
-        ]
-        for path, key, expected in tests:
-            self.getPage(path + '?key=' + key)
-            self.assertBody(expected)
-
-        expectedconf = {
-            # From CP defaults
-            'tools.log_headers.on': False,
-            'tools.log_tracebacks.on': True,
-            'request.show_tracebacks': True,
-            'log.screen': False,
-            'environment': 'test_suite',
-            'engine.autoreload.on': False,
-            # From global config
-            'luxuryyacht': 'throatwobblermangrove',
-            # From Root._cp_config
-            'bar': 'that',
-            # From Foo._cp_config
-            'baz': 'that2',
-            # From Foo.bar._cp_config
-            'foo': 'this3',
-            'bax': 'this4',
-        }
-        for key, expected in expectedconf.items():
-            self.getPage('/foo/bar?key=' + key)
-            self.assertBody(repr(expected))
-
-    def testUnrepr(self):
-        self.getPage('/repr?key=neg')
-        self.assertBody('-1234')
-
-        self.getPage('/repr?key=filename')
-        self.assertBody(repr(os.path.join(sys.prefix, 'hello.py')))
-
-        self.getPage('/repr?key=thing1')
-        self.assertBody(repr(cherrypy.lib.httputil.response_codes[404]))
-
-        if not getattr(cherrypy.server, 'using_apache', False):
-            # The object ID's won't match up when using Apache, since the
-            # server and client are running in different processes.
-            self.getPage('/repr?key=thing2')
-            from cherrypy.tutorial import thing2
-            self.assertBody(repr(thing2))
-
-        if not six.PY3:
-            self.getPage('/repr?key=thing3')
-            self.assertBody(repr(six.text_type('test')))
-
-        self.getPage('/repr?key=complex')
-        self.assertBody('(3+2j)')
-
-        self.getPage('/repr?key=mul')
-        self.assertBody('18')
-
-        self.getPage('/repr?key=stradd')
-        self.assertBody(repr('112233'))
-
-    def testRespNamespaces(self):
-        self.getPage('/foo/silly')
-        self.assertHeader('X-silly', 'sillyval')
-        self.assertBody('Hello world')
-
-    def testCustomNamespaces(self):
-        self.getPage('/raw/incr?num=12')
-        self.assertBody('13')
-
-        self.getPage('/dbscheme')
-        self.assertBody(r'sqlite///memory')
-
-    def testHandlerToolConfigOverride(self):
-        # Assert that config overrides tool constructor args. Above, we set
-        # the favicon in the page handler to be '../favicon.ico',
-        # but then overrode it in config to be './static/dirback.jpg'.
-        self.getPage('/favicon.ico')
-        self.assertBody(open(os.path.join(localDir, 'static/dirback.jpg'),
-                             'rb').read())
-
-    def test_request_body_namespace(self):
-        self.getPage('/plain', method='POST', headers=[
-            ('Content-Type', 'application/x-www-form-urlencoded'),
-            ('Content-Length', '13')],
-            body=b'\xff\xfex\x00=\xff\xfea\x00b\x00c\x00')
-        self.assertBody('abc')
-
-
-class VariableSubstitutionTests(unittest.TestCase):
-    setup_server = staticmethod(setup_server)
-
-    def test_config(self):
-        from textwrap import dedent
-
-        # variable substitution with [DEFAULT]
-        conf = dedent("""
-        [DEFAULT]
-        dir = "/some/dir"
-        my.dir = %(dir)s + "/sub"
-
-        [my]
-        my.dir = %(dir)s + "/my/dir"
-        my.dir2 = %(my.dir)s + '/dir2'
-
-        """)
-
-        fp = StringIOFromNative(conf)
-
-        cherrypy.config.update(fp)
-        self.assertEqual(cherrypy.config['my']['my.dir'], '/some/dir/my/dir')
-        self.assertEqual(cherrypy.config['my']
-                         ['my.dir2'], '/some/dir/my/dir/dir2')
-
-
-class CallablesInConfigTest(unittest.TestCase):
-    setup_server = staticmethod(setup_server)
-
-    def test_call_with_literal_dict(self):
-        from textwrap import dedent
-        conf = dedent("""
-        [my]
-        value = dict(**{'foo': 'bar'})
-        """)
-        fp = StringIOFromNative(conf)
-        cherrypy.config.update(fp)
-        self.assertEqual(cherrypy.config['my']['value'], {'foo': 'bar'})
-
-    def test_call_with_kwargs(self):
-        from textwrap import dedent
-        conf = dedent("""
-        [my]
-        value = dict(foo="buzz", **cherrypy._test_dict)
-        """)
-        test_dict = {
-            'foo': 'bar',
-            'bar': 'foo',
-            'fizz': 'buzz'
-        }
-        cherrypy._test_dict = test_dict
-        fp = StringIOFromNative(conf)
-        cherrypy.config.update(fp)
-        test_dict['foo'] = 'buzz'
-        self.assertEqual(cherrypy.config['my']['value']['foo'], 'buzz')
-        self.assertEqual(cherrypy.config['my']['value'], test_dict)
-        del cherrypy._test_dict
diff --git a/libraries/cherrypy/test/test_config_server.py b/libraries/cherrypy/test/test_config_server.py
deleted file mode 100644
index 7b183530..00000000
--- a/libraries/cherrypy/test/test_config_server.py
+++ /dev/null
@@ -1,126 +0,0 @@
-"""Tests for the CherryPy configuration system."""
-
-import os
-
-import cherrypy
-from cherrypy.test import helper
-
-
-localDir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-
-#                             Client-side code                             #
-
-
-class ServerConfigTests(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-
-        class Root:
-
-            @cherrypy.expose
-            def index(self):
-                return cherrypy.request.wsgi_environ['SERVER_PORT']
-
-            @cherrypy.expose
-            def upload(self, file):
-                return 'Size: %s' % len(file.file.read())
-
-            @cherrypy.expose
-            @cherrypy.config(**{'request.body.maxbytes': 100})
-            def tinyupload(self):
-                return cherrypy.request.body.read()
-
-        cherrypy.tree.mount(Root())
-
-        cherrypy.config.update({
-            'server.socket_host': '0.0.0.0',
-            'server.socket_port': 9876,
-            'server.max_request_body_size': 200,
-            'server.max_request_header_size': 500,
-            'server.socket_timeout': 0.5,
-
-            # Test explicit server.instance
-            'server.2.instance': 'cherrypy._cpwsgi_server.CPWSGIServer',
-            'server.2.socket_port': 9877,
-
-            # Test non-numeric <servername>
-            # Also test default server.instance = builtin server
-            'server.yetanother.socket_port': 9878,
-        })
-
-    PORT = 9876
-
-    def testBasicConfig(self):
-        self.getPage('/')
-        self.assertBody(str(self.PORT))
-
-    def testAdditionalServers(self):
-        if self.scheme == 'https':
-            return self.skip('not available under ssl')
-        self.PORT = 9877
-        self.getPage('/')
-        self.assertBody(str(self.PORT))
-        self.PORT = 9878
-        self.getPage('/')
-        self.assertBody(str(self.PORT))
-
-    def testMaxRequestSizePerHandler(self):
-        if getattr(cherrypy.server, 'using_apache', False):
-            return self.skip('skipped due to known Apache differences... ')
-
-        self.getPage('/tinyupload', method='POST',
-                     headers=[('Content-Type', 'text/plain'),
-                              ('Content-Length', '100')],
-                     body='x' * 100)
-        self.assertStatus(200)
-        self.assertBody('x' * 100)
-
-        self.getPage('/tinyupload', method='POST',
-                     headers=[('Content-Type', 'text/plain'),
-                              ('Content-Length', '101')],
-                     body='x' * 101)
-        self.assertStatus(413)
-
-    def testMaxRequestSize(self):
-        if getattr(cherrypy.server, 'using_apache', False):
-            return self.skip('skipped due to known Apache differences... ')
-
-        for size in (500, 5000, 50000):
-            self.getPage('/', headers=[('From', 'x' * 500)])
-            self.assertStatus(413)
-
-        # Test for https://github.com/cherrypy/cherrypy/issues/421
-        # (Incorrect border condition in readline of SizeCheckWrapper).
-        # This hangs in rev 891 and earlier.
-        lines256 = 'x' * 248
-        self.getPage('/',
-                     headers=[('Host', '%s:%s' % (self.HOST, self.PORT)),
-                              ('From', lines256)])
-
-        # Test upload
-        cd = (
-            'Content-Disposition: form-data; '
-            'name="file"; '
-            'filename="hello.txt"'
-        )
-        body = '\r\n'.join([
-            '--x',
-            cd,
-            'Content-Type: text/plain',
-            '',
-            '%s',
-            '--x--'])
-        partlen = 200 - len(body)
-        b = body % ('x' * partlen)
-        h = [('Content-type', 'multipart/form-data; boundary=x'),
-             ('Content-Length', '%s' % len(b))]
-        self.getPage('/upload', h, 'POST', b)
-        self.assertBody('Size: %d' % partlen)
-
-        b = body % ('x' * 200)
-        h = [('Content-type', 'multipart/form-data; boundary=x'),
-             ('Content-Length', '%s' % len(b))]
-        self.getPage('/upload', h, 'POST', b)
-        self.assertStatus(413)
diff --git a/libraries/cherrypy/test/test_conn.py b/libraries/cherrypy/test/test_conn.py
deleted file mode 100644
index 7d60c6fb..00000000
--- a/libraries/cherrypy/test/test_conn.py
+++ /dev/null
@@ -1,873 +0,0 @@
-"""Tests for TCP connection handling, including proper and timely close."""
-
-import errno
-import socket
-import sys
-import time
-
-import six
-from six.moves import urllib
-from six.moves.http_client import BadStatusLine, HTTPConnection, NotConnected
-
-import pytest
-
-from cheroot.test import webtest
-
-import cherrypy
-from cherrypy._cpcompat import HTTPSConnection, ntob, tonative
-from cherrypy.test import helper
-
-
-timeout = 1
-pov = 'pPeErRsSiIsStTeEnNcCeE oOfF vViIsSiIoOnN'
-
-
-def setup_server():
-
-    def raise500():
-        raise cherrypy.HTTPError(500)
-
-    class Root:
-
-        @cherrypy.expose
-        def index(self):
-            return pov
-        page1 = index
-        page2 = index
-        page3 = index
-
-        @cherrypy.expose
-        def hello(self):
-            return 'Hello, world!'
-
-        @cherrypy.expose
-        def timeout(self, t):
-            return str(cherrypy.server.httpserver.timeout)
-
-        @cherrypy.expose
-        @cherrypy.config(**{'response.stream': True})
-        def stream(self, set_cl=False):
-            if set_cl:
-                cherrypy.response.headers['Content-Length'] = 10
-
-            def content():
-                for x in range(10):
-                    yield str(x)
-
-            return content()
-
-        @cherrypy.expose
-        def error(self, code=500):
-            raise cherrypy.HTTPError(code)
-
-        @cherrypy.expose
-        def upload(self):
-            if not cherrypy.request.method == 'POST':
-                raise AssertionError("'POST' != request.method %r" %
-                                     cherrypy.request.method)
-            return "thanks for '%s'" % cherrypy.request.body.read()
-
-        @cherrypy.expose
-        def custom(self, response_code):
-            cherrypy.response.status = response_code
-            return 'Code = %s' % response_code
-
-        @cherrypy.expose
-        @cherrypy.config(**{'hooks.on_start_resource': raise500})
-        def err_before_read(self):
-            return 'ok'
-
-        @cherrypy.expose
-        def one_megabyte_of_a(self):
-            return ['a' * 1024] * 1024
-
-        @cherrypy.expose
-        # Turn off the encoding tool so it doens't collapse
-        # our response body and reclaculate the Content-Length.
-        @cherrypy.config(**{'tools.encode.on': False})
-        def custom_cl(self, body, cl):
-            cherrypy.response.headers['Content-Length'] = cl
-            if not isinstance(body, list):
-                body = [body]
-            newbody = []
-            for chunk in body:
-                if isinstance(chunk, six.text_type):
-                    chunk = chunk.encode('ISO-8859-1')
-                newbody.append(chunk)
-            return newbody
-
-    cherrypy.tree.mount(Root())
-    cherrypy.config.update({
-        'server.max_request_body_size': 1001,
-        'server.socket_timeout': timeout,
-    })
-
-
-class ConnectionCloseTests(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def test_HTTP11(self):
-        if cherrypy.server.protocol_version != 'HTTP/1.1':
-            return self.skip()
-
-        self.PROTOCOL = 'HTTP/1.1'
-
-        self.persistent = True
-
-        # Make the first request and assert there's no "Connection: close".
-        self.getPage('/')
-        self.assertStatus('200 OK')
-        self.assertBody(pov)
-        self.assertNoHeader('Connection')
-
-        # Make another request on the same connection.
-        self.getPage('/page1')
-        self.assertStatus('200 OK')
-        self.assertBody(pov)
-        self.assertNoHeader('Connection')
-
-        # Test client-side close.
-        self.getPage('/page2', headers=[('Connection', 'close')])
-        self.assertStatus('200 OK')
-        self.assertBody(pov)
-        self.assertHeader('Connection', 'close')
-
-        # Make another request on the same connection, which should error.
-        self.assertRaises(NotConnected, self.getPage, '/')
-
-    def test_Streaming_no_len(self):
-        try:
-            self._streaming(set_cl=False)
-        finally:
-            try:
-                self.HTTP_CONN.close()
-            except (TypeError, AttributeError):
-                pass
-
-    def test_Streaming_with_len(self):
-        try:
-            self._streaming(set_cl=True)
-        finally:
-            try:
-                self.HTTP_CONN.close()
-            except (TypeError, AttributeError):
-                pass
-
-    def _streaming(self, set_cl):
-        if cherrypy.server.protocol_version == 'HTTP/1.1':
-            self.PROTOCOL = 'HTTP/1.1'
-
-            self.persistent = True
-
-            # Make the first request and assert there's no "Connection: close".
-            self.getPage('/')
-            self.assertStatus('200 OK')
-            self.assertBody(pov)
-            self.assertNoHeader('Connection')
-
-            # Make another, streamed request on the same connection.
-            if set_cl:
-                # When a Content-Length is provided, the content should stream
-                # without closing the connection.
-                self.getPage('/stream?set_cl=Yes')
-                self.assertHeader('Content-Length')
-                self.assertNoHeader('Connection', 'close')
-                self.assertNoHeader('Transfer-Encoding')
-
-                self.assertStatus('200 OK')
-                self.assertBody('0123456789')
-            else:
-                # When no Content-Length response header is provided,
-                # streamed output will either close the connection, or use
-                # chunked encoding, to determine transfer-length.
-                self.getPage('/stream')
-                self.assertNoHeader('Content-Length')
-                self.assertStatus('200 OK')
-                self.assertBody('0123456789')
-
-                chunked_response = False
-                for k, v in self.headers:
-                    if k.lower() == 'transfer-encoding':
-                        if str(v) == 'chunked':
-                            chunked_response = True
-
-                if chunked_response:
-                    self.assertNoHeader('Connection', 'close')
-                else:
-                    self.assertHeader('Connection', 'close')
-
-                    # Make another request on the same connection, which should
-                    # error.
-                    self.assertRaises(NotConnected, self.getPage, '/')
-
-                # Try HEAD. See
-                # https://github.com/cherrypy/cherrypy/issues/864.
-                self.getPage('/stream', method='HEAD')
-                self.assertStatus('200 OK')
-                self.assertBody('')
-                self.assertNoHeader('Transfer-Encoding')
-        else:
-            self.PROTOCOL = 'HTTP/1.0'
-
-            self.persistent = True
-
-            # Make the first request and assert Keep-Alive.
-            self.getPage('/', headers=[('Connection', 'Keep-Alive')])
-            self.assertStatus('200 OK')
-            self.assertBody(pov)
-            self.assertHeader('Connection', 'Keep-Alive')
-
-            # Make another, streamed request on the same connection.
-            if set_cl:
-                # When a Content-Length is provided, the content should
-                # stream without closing the connection.
-                self.getPage('/stream?set_cl=Yes',
-                             headers=[('Connection', 'Keep-Alive')])
-                self.assertHeader('Content-Length')
-                self.assertHeader('Connection', 'Keep-Alive')
-                self.assertNoHeader('Transfer-Encoding')
-                self.assertStatus('200 OK')
-                self.assertBody('0123456789')
-            else:
-                # When a Content-Length is not provided,
-                # the server should close the connection.
-                self.getPage('/stream', headers=[('Connection', 'Keep-Alive')])
-                self.assertStatus('200 OK')
-                self.assertBody('0123456789')
-
-                self.assertNoHeader('Content-Length')
-                self.assertNoHeader('Connection', 'Keep-Alive')
-                self.assertNoHeader('Transfer-Encoding')
-
-                # Make another request on the same connection, which should
-                # error.
-                self.assertRaises(NotConnected, self.getPage, '/')
-
-    def test_HTTP10_KeepAlive(self):
-        self.PROTOCOL = 'HTTP/1.0'
-        if self.scheme == 'https':
-            self.HTTP_CONN = HTTPSConnection
-        else:
-            self.HTTP_CONN = HTTPConnection
-
-        # Test a normal HTTP/1.0 request.
-        self.getPage('/page2')
-        self.assertStatus('200 OK')
-        self.assertBody(pov)
-        # Apache, for example, may emit a Connection header even for HTTP/1.0
-        # self.assertNoHeader("Connection")
-
-        # Test a keep-alive HTTP/1.0 request.
-        self.persistent = True
-
-        self.getPage('/page3', headers=[('Connection', 'Keep-Alive')])
-        self.assertStatus('200 OK')
-        self.assertBody(pov)
-        self.assertHeader('Connection', 'Keep-Alive')
-
-        # Remove the keep-alive header again.
-        self.getPage('/page3')
-        self.assertStatus('200 OK')
-        self.assertBody(pov)
-        # Apache, for example, may emit a Connection header even for HTTP/1.0
-        # self.assertNoHeader("Connection")
-
-
-class PipelineTests(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def test_HTTP11_Timeout(self):
-        # If we timeout without sending any data,
-        # the server will close the conn with a 408.
-        if cherrypy.server.protocol_version != 'HTTP/1.1':
-            return self.skip()
-
-        self.PROTOCOL = 'HTTP/1.1'
-
-        # Connect but send nothing.
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.auto_open = False
-        conn.connect()
-
-        # Wait for our socket timeout
-        time.sleep(timeout * 2)
-
-        # The request should have returned 408 already.
-        response = conn.response_class(conn.sock, method='GET')
-        response.begin()
-        self.assertEqual(response.status, 408)
-        conn.close()
-
-        # Connect but send half the headers only.
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.auto_open = False
-        conn.connect()
-        conn.send(b'GET /hello HTTP/1.1')
-        conn.send(('Host: %s' % self.HOST).encode('ascii'))
-
-        # Wait for our socket timeout
-        time.sleep(timeout * 2)
-
-        # The conn should have already sent 408.
-        response = conn.response_class(conn.sock, method='GET')
-        response.begin()
-        self.assertEqual(response.status, 408)
-        conn.close()
-
-    def test_HTTP11_Timeout_after_request(self):
-        # If we timeout after at least one request has succeeded,
-        # the server will close the conn without 408.
-        if cherrypy.server.protocol_version != 'HTTP/1.1':
-            return self.skip()
-
-        self.PROTOCOL = 'HTTP/1.1'
-
-        # Make an initial request
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.putrequest('GET', '/timeout?t=%s' % timeout, skip_host=True)
-        conn.putheader('Host', self.HOST)
-        conn.endheaders()
-        response = conn.response_class(conn.sock, method='GET')
-        response.begin()
-        self.assertEqual(response.status, 200)
-        self.body = response.read()
-        self.assertBody(str(timeout))
-
-        # Make a second request on the same socket
-        conn._output(b'GET /hello HTTP/1.1')
-        conn._output(ntob('Host: %s' % self.HOST, 'ascii'))
-        conn._send_output()
-        response = conn.response_class(conn.sock, method='GET')
-        response.begin()
-        self.assertEqual(response.status, 200)
-        self.body = response.read()
-        self.assertBody('Hello, world!')
-
-        # Wait for our socket timeout
-        time.sleep(timeout * 2)
-
-        # Make another request on the same socket, which should error
-        conn._output(b'GET /hello HTTP/1.1')
-        conn._output(ntob('Host: %s' % self.HOST, 'ascii'))
-        conn._send_output()
-        response = conn.response_class(conn.sock, method='GET')
-        try:
-            response.begin()
-        except Exception:
-            if not isinstance(sys.exc_info()[1],
-                              (socket.error, BadStatusLine)):
-                self.fail("Writing to timed out socket didn't fail"
-                          ' as it should have: %s' % sys.exc_info()[1])
-        else:
-            if response.status != 408:
-                self.fail("Writing to timed out socket didn't fail"
-                          ' as it should have: %s' %
-                          response.read())
-
-        conn.close()
-
-        # Make another request on a new socket, which should work
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.putrequest('GET', '/', skip_host=True)
-        conn.putheader('Host', self.HOST)
-        conn.endheaders()
-        response = conn.response_class(conn.sock, method='GET')
-        response.begin()
-        self.assertEqual(response.status, 200)
-        self.body = response.read()
-        self.assertBody(pov)
-
-        # Make another request on the same socket,
-        # but timeout on the headers
-        conn.send(b'GET /hello HTTP/1.1')
-        # Wait for our socket timeout
-        time.sleep(timeout * 2)
-        response = conn.response_class(conn.sock, method='GET')
-        try:
-            response.begin()
-        except Exception:
-            if not isinstance(sys.exc_info()[1],
-                              (socket.error, BadStatusLine)):
-                self.fail("Writing to timed out socket didn't fail"
-                          ' as it should have: %s' % sys.exc_info()[1])
-        else:
-            self.fail("Writing to timed out socket didn't fail"
-                      ' as it should have: %s' %
-                      response.read())
-
-        conn.close()
-
-        # Retry the request on a new connection, which should work
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.putrequest('GET', '/', skip_host=True)
-        conn.putheader('Host', self.HOST)
-        conn.endheaders()
-        response = conn.response_class(conn.sock, method='GET')
-        response.begin()
-        self.assertEqual(response.status, 200)
-        self.body = response.read()
-        self.assertBody(pov)
-        conn.close()
-
-    def test_HTTP11_pipelining(self):
-        if cherrypy.server.protocol_version != 'HTTP/1.1':
-            return self.skip()
-
-        self.PROTOCOL = 'HTTP/1.1'
-
-        # Test pipelining. httplib doesn't support this directly.
-        self.persistent = True
-        conn = self.HTTP_CONN
-
-        # Put request 1
-        conn.putrequest('GET', '/hello', skip_host=True)
-        conn.putheader('Host', self.HOST)
-        conn.endheaders()
-
-        for trial in range(5):
-            # Put next request
-            conn._output(b'GET /hello HTTP/1.1')
-            conn._output(ntob('Host: %s' % self.HOST, 'ascii'))
-            conn._send_output()
-
-            # Retrieve previous response
-            response = conn.response_class(conn.sock, method='GET')
-            # there is a bug in python3 regarding the buffering of
-            # ``conn.sock``. Until that bug get's fixed we will
-            # monkey patch the ``response`` instance.
-            # https://bugs.python.org/issue23377
-            if six.PY3:
-                response.fp = conn.sock.makefile('rb', 0)
-            response.begin()
-            body = response.read(13)
-            self.assertEqual(response.status, 200)
-            self.assertEqual(body, b'Hello, world!')
-
-        # Retrieve final response
-        response = conn.response_class(conn.sock, method='GET')
-        response.begin()
-        body = response.read()
-        self.assertEqual(response.status, 200)
-        self.assertEqual(body, b'Hello, world!')
-
-        conn.close()
-
-    def test_100_Continue(self):
-        if cherrypy.server.protocol_version != 'HTTP/1.1':
-            return self.skip()
-
-        self.PROTOCOL = 'HTTP/1.1'
-
-        self.persistent = True
-        conn = self.HTTP_CONN
-
-        # Try a page without an Expect request header first.
-        # Note that httplib's response.begin automatically ignores
-        # 100 Continue responses, so we must manually check for it.
-        try:
-            conn.putrequest('POST', '/upload', skip_host=True)
-            conn.putheader('Host', self.HOST)
-            conn.putheader('Content-Type', 'text/plain')
-            conn.putheader('Content-Length', '4')
-            conn.endheaders()
-            conn.send(ntob("d'oh"))
-            response = conn.response_class(conn.sock, method='POST')
-            version, status, reason = response._read_status()
-            self.assertNotEqual(status, 100)
-        finally:
-            conn.close()
-
-        # Now try a page with an Expect header...
-        try:
-            conn.connect()
-            conn.putrequest('POST', '/upload', skip_host=True)
-            conn.putheader('Host', self.HOST)
-            conn.putheader('Content-Type', 'text/plain')
-            conn.putheader('Content-Length', '17')
-            conn.putheader('Expect', '100-continue')
-            conn.endheaders()
-            response = conn.response_class(conn.sock, method='POST')
-
-            # ...assert and then skip the 100 response
-            version, status, reason = response._read_status()
-            self.assertEqual(status, 100)
-            while True:
-                line = response.fp.readline().strip()
-                if line:
-                    self.fail(
-                        '100 Continue should not output any headers. Got %r' %
-                        line)
-                else:
-                    break
-
-            # ...send the body
-            body = b'I am a small file'
-            conn.send(body)
-
-            # ...get the final response
-            response.begin()
-            self.status, self.headers, self.body = webtest.shb(response)
-            self.assertStatus(200)
-            self.assertBody("thanks for '%s'" % body)
-        finally:
-            conn.close()
-
-
-class ConnectionTests(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def test_readall_or_close(self):
-        if cherrypy.server.protocol_version != 'HTTP/1.1':
-            return self.skip()
-
-        self.PROTOCOL = 'HTTP/1.1'
-
-        if self.scheme == 'https':
-            self.HTTP_CONN = HTTPSConnection
-        else:
-            self.HTTP_CONN = HTTPConnection
-
-        # Test a max of 0 (the default) and then reset to what it was above.
-        old_max = cherrypy.server.max_request_body_size
-        for new_max in (0, old_max):
-            cherrypy.server.max_request_body_size = new_max
-
-            self.persistent = True
-            conn = self.HTTP_CONN
-
-            # Get a POST page with an error
-            conn.putrequest('POST', '/err_before_read', skip_host=True)
-            conn.putheader('Host', self.HOST)
-            conn.putheader('Content-Type', 'text/plain')
-            conn.putheader('Content-Length', '1000')
-            conn.putheader('Expect', '100-continue')
-            conn.endheaders()
-            response = conn.response_class(conn.sock, method='POST')
-
-            # ...assert and then skip the 100 response
-            version, status, reason = response._read_status()
-            self.assertEqual(status, 100)
-            while True:
-                skip = response.fp.readline().strip()
-                if not skip:
-                    break
-
-            # ...send the body
-            conn.send(ntob('x' * 1000))
-
-            # ...get the final response
-            response.begin()
-            self.status, self.headers, self.body = webtest.shb(response)
-            self.assertStatus(500)
-
-            # Now try a working page with an Expect header...
-            conn._output(b'POST /upload HTTP/1.1')
-            conn._output(ntob('Host: %s' % self.HOST, 'ascii'))
-            conn._output(b'Content-Type: text/plain')
-            conn._output(b'Content-Length: 17')
-            conn._output(b'Expect: 100-continue')
-            conn._send_output()
-            response = conn.response_class(conn.sock, method='POST')
-
-            # ...assert and then skip the 100 response
-            version, status, reason = response._read_status()
-            self.assertEqual(status, 100)
-            while True:
-                skip = response.fp.readline().strip()
-                if not skip:
-                    break
-
-            # ...send the body
-            body = b'I am a small file'
-            conn.send(body)
-
-            # ...get the final response
-            response.begin()
-            self.status, self.headers, self.body = webtest.shb(response)
-            self.assertStatus(200)
-            self.assertBody("thanks for '%s'" % body)
-            conn.close()
-
-    def test_No_Message_Body(self):
-        if cherrypy.server.protocol_version != 'HTTP/1.1':
-            return self.skip()
-
-        self.PROTOCOL = 'HTTP/1.1'
-
-        # Set our HTTP_CONN to an instance so it persists between requests.
-        self.persistent = True
-
-        # Make the first request and assert there's no "Connection: close".
-        self.getPage('/')
-        self.assertStatus('200 OK')
-        self.assertBody(pov)
-        self.assertNoHeader('Connection')
-
-        # Make a 204 request on the same connection.
-        self.getPage('/custom/204')
-        self.assertStatus(204)
-        self.assertNoHeader('Content-Length')
-        self.assertBody('')
-        self.assertNoHeader('Connection')
-
-        # Make a 304 request on the same connection.
-        self.getPage('/custom/304')
-        self.assertStatus(304)
-        self.assertNoHeader('Content-Length')
-        self.assertBody('')
-        self.assertNoHeader('Connection')
-
-    def test_Chunked_Encoding(self):
-        if cherrypy.server.protocol_version != 'HTTP/1.1':
-            return self.skip()
-
-        if (hasattr(self, 'harness') and
-                'modpython' in self.harness.__class__.__name__.lower()):
-            # mod_python forbids chunked encoding
-            return self.skip()
-
-        self.PROTOCOL = 'HTTP/1.1'
-
-        # Set our HTTP_CONN to an instance so it persists between requests.
-        self.persistent = True
-        conn = self.HTTP_CONN
-
-        # Try a normal chunked request (with extensions)
-        body = ntob('8;key=value\r\nxx\r\nxxxx\r\n5\r\nyyyyy\r\n0\r\n'
-                    'Content-Type: application/json\r\n'
-                    '\r\n')
-        conn.putrequest('POST', '/upload', skip_host=True)
-        conn.putheader('Host', self.HOST)
-        conn.putheader('Transfer-Encoding', 'chunked')
-        conn.putheader('Trailer', 'Content-Type')
-        # Note that this is somewhat malformed:
-        # we shouldn't be sending Content-Length.
-        # RFC 2616 says the server should ignore it.
-        conn.putheader('Content-Length', '3')
-        conn.endheaders()
-        conn.send(body)
-        response = conn.getresponse()
-        self.status, self.headers, self.body = webtest.shb(response)
-        self.assertStatus('200 OK')
-        self.assertBody("thanks for '%s'" % b'xx\r\nxxxxyyyyy')
-
-        # Try a chunked request that exceeds server.max_request_body_size.
-        # Note that the delimiters and trailer are included.
-        body = ntob('3e3\r\n' + ('x' * 995) + '\r\n0\r\n\r\n')
-        conn.putrequest('POST', '/upload', skip_host=True)
-        conn.putheader('Host', self.HOST)
-        conn.putheader('Transfer-Encoding', 'chunked')
-        conn.putheader('Content-Type', 'text/plain')
-        # Chunked requests don't need a content-length
-        # #        conn.putheader("Content-Length", len(body))
-        conn.endheaders()
-        conn.send(body)
-        response = conn.getresponse()
-        self.status, self.headers, self.body = webtest.shb(response)
-        self.assertStatus(413)
-        conn.close()
-
-    def test_Content_Length_in(self):
-        # Try a non-chunked request where Content-Length exceeds
-        # server.max_request_body_size. Assert error before body send.
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.putrequest('POST', '/upload', skip_host=True)
-        conn.putheader('Host', self.HOST)
-        conn.putheader('Content-Type', 'text/plain')
-        conn.putheader('Content-Length', '9999')
-        conn.endheaders()
-        response = conn.getresponse()
-        self.status, self.headers, self.body = webtest.shb(response)
-        self.assertStatus(413)
-        self.assertBody('The entity sent with the request exceeds '
-                        'the maximum allowed bytes.')
-        conn.close()
-
-    def test_Content_Length_out_preheaders(self):
-        # Try a non-chunked response where Content-Length is less than
-        # the actual bytes in the response body.
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.putrequest('GET', '/custom_cl?body=I+have+too+many+bytes&cl=5',
-                        skip_host=True)
-        conn.putheader('Host', self.HOST)
-        conn.endheaders()
-        response = conn.getresponse()
-        self.status, self.headers, self.body = webtest.shb(response)
-        self.assertStatus(500)
-        self.assertBody(
-            'The requested resource returned more bytes than the '
-            'declared Content-Length.')
-        conn.close()
-
-    def test_Content_Length_out_postheaders(self):
-        # Try a non-chunked response where Content-Length is less than
-        # the actual bytes in the response body.
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.putrequest(
-            'GET', '/custom_cl?body=I+too&body=+have+too+many&cl=5',
-            skip_host=True)
-        conn.putheader('Host', self.HOST)
-        conn.endheaders()
-        response = conn.getresponse()
-        self.status, self.headers, self.body = webtest.shb(response)
-        self.assertStatus(200)
-        self.assertBody('I too')
-        conn.close()
-
-    def test_598(self):
-        tmpl = '{scheme}://{host}:{port}/one_megabyte_of_a/'
-        url = tmpl.format(
-            scheme=self.scheme,
-            host=self.HOST,
-            port=self.PORT,
-        )
-        remote_data_conn = urllib.request.urlopen(url)
-        buf = remote_data_conn.read(512)
-        time.sleep(timeout * 0.6)
-        remaining = (1024 * 1024) - 512
-        while remaining:
-            data = remote_data_conn.read(remaining)
-            if not data:
-                break
-            else:
-                buf += data
-            remaining -= len(data)
-
-        self.assertEqual(len(buf), 1024 * 1024)
-        self.assertEqual(buf, ntob('a' * 1024 * 1024))
-        self.assertEqual(remaining, 0)
-        remote_data_conn.close()
-
-
-def setup_upload_server():
-
-    class Root:
-        @cherrypy.expose
-        def upload(self):
-            if not cherrypy.request.method == 'POST':
-                raise AssertionError("'POST' != request.method %r" %
-                                     cherrypy.request.method)
-            return "thanks for '%s'" % tonative(cherrypy.request.body.read())
-
-    cherrypy.tree.mount(Root())
-    cherrypy.config.update({
-        'server.max_request_body_size': 1001,
-        'server.socket_timeout': 10,
-        'server.accepted_queue_size': 5,
-        'server.accepted_queue_timeout': 0.1,
-    })
-
-
-reset_names = 'ECONNRESET', 'WSAECONNRESET'
-socket_reset_errors = [
-    getattr(errno, name)
-    for name in reset_names
-    if hasattr(errno, name)
-]
-'reset error numbers available on this platform'
-
-socket_reset_errors += [
-    # Python 3.5 raises an http.client.RemoteDisconnected
-    # with this message
-    'Remote end closed connection without response',
-]
-
-
-class LimitedRequestQueueTests(helper.CPWebCase):
-    setup_server = staticmethod(setup_upload_server)
-
-    @pytest.mark.xfail(reason='#1535')
-    def test_queue_full(self):
-        conns = []
-        overflow_conn = None
-
-        try:
-            # Make 15 initial requests and leave them open, which should use
-            # all of wsgiserver's WorkerThreads and fill its Queue.
-            for i in range(15):
-                conn = self.HTTP_CONN(self.HOST, self.PORT)
-                conn.putrequest('POST', '/upload', skip_host=True)
-                conn.putheader('Host', self.HOST)
-                conn.putheader('Content-Type', 'text/plain')
-                conn.putheader('Content-Length', '4')
-                conn.endheaders()
-                conns.append(conn)
-
-            # Now try a 16th conn, which should be closed by the
-            # server immediately.
-            overflow_conn = self.HTTP_CONN(self.HOST, self.PORT)
-            # Manually connect since httplib won't let us set a timeout
-            for res in socket.getaddrinfo(self.HOST, self.PORT, 0,
-                                          socket.SOCK_STREAM):
-                af, socktype, proto, canonname, sa = res
-                overflow_conn.sock = socket.socket(af, socktype, proto)
-                overflow_conn.sock.settimeout(5)
-                overflow_conn.sock.connect(sa)
-                break
-
-            overflow_conn.putrequest('GET', '/', skip_host=True)
-            overflow_conn.putheader('Host', self.HOST)
-            overflow_conn.endheaders()
-            response = overflow_conn.response_class(
-                overflow_conn.sock,
-                method='GET',
-            )
-            try:
-                response.begin()
-            except socket.error as exc:
-                if exc.args[0] in socket_reset_errors:
-                    pass  # Expected.
-                else:
-                    tmpl = (
-                        'Overflow conn did not get RST. '
-                        'Got {exc.args!r} instead'
-                    )
-                    raise AssertionError(tmpl.format(**locals()))
-            except BadStatusLine:
-                # This is a special case in OS X. Linux and Windows will
-                # RST correctly.
-                assert sys.platform == 'darwin'
-            else:
-                raise AssertionError('Overflow conn did not get RST ')
-        finally:
-            for conn in conns:
-                conn.send(b'done')
-                response = conn.response_class(conn.sock, method='POST')
-                response.begin()
-                self.body = response.read()
-                self.assertBody("thanks for 'done'")
-                self.assertEqual(response.status, 200)
-                conn.close()
-            if overflow_conn:
-                overflow_conn.close()
-
-
-class BadRequestTests(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def test_No_CRLF(self):
-        self.persistent = True
-
-        conn = self.HTTP_CONN
-        conn.send(b'GET /hello HTTP/1.1\n\n')
-        response = conn.response_class(conn.sock, method='GET')
-        response.begin()
-        self.body = response.read()
-        self.assertBody('HTTP requires CRLF terminators')
-        conn.close()
-
-        conn.connect()
-        conn.send(b'GET /hello HTTP/1.1\r\n\n')
-        response = conn.response_class(conn.sock, method='GET')
-        response.begin()
-        self.body = response.read()
-        self.assertBody('HTTP requires CRLF terminators')
-        conn.close()
diff --git a/libraries/cherrypy/test/test_core.py b/libraries/cherrypy/test/test_core.py
deleted file mode 100644
index 9834c1f3..00000000
--- a/libraries/cherrypy/test/test_core.py
+++ /dev/null
@@ -1,823 +0,0 @@
-# coding: utf-8
-
-"""Basic tests for the CherryPy core: request handling."""
-
-import os
-import sys
-import types
-
-import six
-
-import cherrypy
-from cherrypy._cpcompat import ntou
-from cherrypy import _cptools, tools
-from cherrypy.lib import httputil, static
-
-from cherrypy.test._test_decorators import ExposeExamples
-from cherrypy.test import helper
-
-
-localDir = os.path.dirname(__file__)
-favicon_path = os.path.join(os.getcwd(), localDir, '../favicon.ico')
-
-#                             Client-side code                             #
-
-
-class CoreRequestHandlingTest(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-        class Root:
-
-            @cherrypy.expose
-            def index(self):
-                return 'hello'
-
-            favicon_ico = tools.staticfile.handler(filename=favicon_path)
-
-            @cherrypy.expose
-            def defct(self, newct):
-                newct = 'text/%s' % newct
-                cherrypy.config.update({'tools.response_headers.on': True,
-                                        'tools.response_headers.headers':
-                                        [('Content-Type', newct)]})
-
-            @cherrypy.expose
-            def baseurl(self, path_info, relative=None):
-                return cherrypy.url(path_info, relative=bool(relative))
-
-        root = Root()
-        root.expose_dec = ExposeExamples()
-
-        class TestType(type):
-
-            """Metaclass which automatically exposes all functions in each
-            subclass, and adds an instance of the subclass as an attribute
-            of root.
-            """
-            def __init__(cls, name, bases, dct):
-                type.__init__(cls, name, bases, dct)
-                for value in six.itervalues(dct):
-                    if isinstance(value, types.FunctionType):
-                        value.exposed = True
-                setattr(root, name.lower(), cls())
-        Test = TestType('Test', (object, ), {})
-
-        @cherrypy.config(**{'tools.trailing_slash.on': False})
-        class URL(Test):
-
-            def index(self, path_info, relative=None):
-                if relative != 'server':
-                    relative = bool(relative)
-                return cherrypy.url(path_info, relative=relative)
-
-            def leaf(self, path_info, relative=None):
-                if relative != 'server':
-                    relative = bool(relative)
-                return cherrypy.url(path_info, relative=relative)
-
-            def qs(self, qs):
-                return cherrypy.url(qs=qs)
-
-        def log_status():
-            Status.statuses.append(cherrypy.response.status)
-        cherrypy.tools.log_status = cherrypy.Tool(
-            'on_end_resource', log_status)
-
-        class Status(Test):
-
-            def index(self):
-                return 'normal'
-
-            def blank(self):
-                cherrypy.response.status = ''
-
-            # According to RFC 2616, new status codes are OK as long as they
-            # are between 100 and 599.
-
-            # Here is an illegal code...
-            def illegal(self):
-                cherrypy.response.status = 781
-                return 'oops'
-
-            # ...and here is an unknown but legal code.
-            def unknown(self):
-                cherrypy.response.status = '431 My custom error'
-                return 'funky'
-
-            # Non-numeric code
-            def bad(self):
-                cherrypy.response.status = 'error'
-                return 'bad news'
-
-            statuses = []
-
-            @cherrypy.config(**{'tools.log_status.on': True})
-            def on_end_resource_stage(self):
-                return repr(self.statuses)
-
-        class Redirect(Test):
-
-            @cherrypy.config(**{
-                'tools.err_redirect.on': True,
-                'tools.err_redirect.url': '/errpage',
-                'tools.err_redirect.internal': False,
-            })
-            class Error:
-                @cherrypy.expose
-                def index(self):
-                    raise NameError('redirect_test')
-
-            error = Error()
-
-            def index(self):
-                return 'child'
-
-            def custom(self, url, code):
-                raise cherrypy.HTTPRedirect(url, code)
-
-            @cherrypy.config(**{'tools.trailing_slash.extra': True})
-            def by_code(self, code):
-                raise cherrypy.HTTPRedirect('somewhere%20else', code)
-
-            def nomodify(self):
-                raise cherrypy.HTTPRedirect('', 304)
-
-            def proxy(self):
-                raise cherrypy.HTTPRedirect('proxy', 305)
-
-            def stringify(self):
-                return str(cherrypy.HTTPRedirect('/'))
-
-            def fragment(self, frag):
-                raise cherrypy.HTTPRedirect('/some/url#%s' % frag)
-
-            def url_with_quote(self):
-                raise cherrypy.HTTPRedirect("/some\"url/that'we/want")
-
-            def url_with_xss(self):
-                raise cherrypy.HTTPRedirect(
-                    "/some<script>alert(1);</script>url/that'we/want")
-
-            def url_with_unicode(self):
-                raise cherrypy.HTTPRedirect(ntou('тест', 'utf-8'))
-
-        def login_redir():
-            if not getattr(cherrypy.request, 'login', None):
-                raise cherrypy.InternalRedirect('/internalredirect/login')
-        tools.login_redir = _cptools.Tool('before_handler', login_redir)
-
-        def redir_custom():
-            raise cherrypy.InternalRedirect('/internalredirect/custom_err')
-
-        class InternalRedirect(Test):
-
-            def index(self):
-                raise cherrypy.InternalRedirect('/')
-
-            @cherrypy.expose
-            @cherrypy.config(**{'hooks.before_error_response': redir_custom})
-            def choke(self):
-                return 3 / 0
-
-            def relative(self, a, b):
-                raise cherrypy.InternalRedirect('cousin?t=6')
-
-            def cousin(self, t):
-                assert cherrypy.request.prev.closed
-                return cherrypy.request.prev.query_string
-
-            def petshop(self, user_id):
-                if user_id == 'parrot':
-                    # Trade it for a slug when redirecting
-                    raise cherrypy.InternalRedirect(
-                        '/image/getImagesByUser?user_id=slug')
-                elif user_id == 'terrier':
-                    # Trade it for a fish when redirecting
-                    raise cherrypy.InternalRedirect(
-                        '/image/getImagesByUser?user_id=fish')
-                else:
-                    # This should pass the user_id through to getImagesByUser
-                    raise cherrypy.InternalRedirect(
-                        '/image/getImagesByUser?user_id=%s' % str(user_id))
-
-            # We support Python 2.3, but the @-deco syntax would look like
-            # this:
-            # @tools.login_redir()
-            def secure(self):
-                return 'Welcome!'
-            secure = tools.login_redir()(secure)
-            # Since calling the tool returns the same function you pass in,
-            # you could skip binding the return value, and just write:
-            # tools.login_redir()(secure)
-
-            def login(self):
-                return 'Please log in'
-
-            def custom_err(self):
-                return 'Something went horribly wrong.'
-
-            @cherrypy.config(**{'hooks.before_request_body': redir_custom})
-            def early_ir(self, arg):
-                return 'whatever'
-
-        class Image(Test):
-
-            def getImagesByUser(self, user_id):
-                return '0 images for %s' % user_id
-
-        class Flatten(Test):
-
-            def as_string(self):
-                return 'content'
-
-            def as_list(self):
-                return ['con', 'tent']
-
-            def as_yield(self):
-                yield b'content'
-
-            @cherrypy.config(**{'tools.flatten.on': True})
-            def as_dblyield(self):
-                yield self.as_yield()
-
-            def as_refyield(self):
-                for chunk in self.as_yield():
-                    yield chunk
-
-        class Ranges(Test):
-
-            def get_ranges(self, bytes):
-                return repr(httputil.get_ranges('bytes=%s' % bytes, 8))
-
-            def slice_file(self):
-                path = os.path.join(os.getcwd(), os.path.dirname(__file__))
-                return static.serve_file(
-                    os.path.join(path, 'static/index.html'))
-
-        class Cookies(Test):
-
-            def single(self, name):
-                cookie = cherrypy.request.cookie[name]
-                # Python2's SimpleCookie.__setitem__ won't take unicode keys.
-                cherrypy.response.cookie[str(name)] = cookie.value
-
-            def multiple(self, names):
-                list(map(self.single, names))
-
-        def append_headers(header_list, debug=False):
-            if debug:
-                cherrypy.log(
-                    'Extending response headers with %s' % repr(header_list),
-                    'TOOLS.APPEND_HEADERS')
-            cherrypy.serving.response.header_list.extend(header_list)
-        cherrypy.tools.append_headers = cherrypy.Tool(
-            'on_end_resource', append_headers)
-
-        class MultiHeader(Test):
-
-            def header_list(self):
-                pass
-            header_list = cherrypy.tools.append_headers(header_list=[
-                (b'WWW-Authenticate', b'Negotiate'),
-                (b'WWW-Authenticate', b'Basic realm="foo"'),
-            ])(header_list)
-
-            def commas(self):
-                cherrypy.response.headers[
-                    'WWW-Authenticate'] = 'Negotiate,Basic realm="foo"'
-
-        cherrypy.tree.mount(root)
-
-    def testStatus(self):
-        self.getPage('/status/')
-        self.assertBody('normal')
-        self.assertStatus(200)
-
-        self.getPage('/status/blank')
-        self.assertBody('')
-        self.assertStatus(200)
-
-        self.getPage('/status/illegal')
-        self.assertStatus(500)
-        msg = 'Illegal response status from server (781 is out of range).'
-        self.assertErrorPage(500, msg)
-
-        if not getattr(cherrypy.server, 'using_apache', False):
-            self.getPage('/status/unknown')
-            self.assertBody('funky')
-            self.assertStatus(431)
-
-        self.getPage('/status/bad')
-        self.assertStatus(500)
-        msg = "Illegal response status from server ('error' is non-numeric)."
-        self.assertErrorPage(500, msg)
-
-    def test_on_end_resource_status(self):
-        self.getPage('/status/on_end_resource_stage')
-        self.assertBody('[]')
-        self.getPage('/status/on_end_resource_stage')
-        self.assertBody(repr(['200 OK']))
-
-    def testSlashes(self):
-        # Test that requests for index methods without a trailing slash
-        # get redirected to the same URI path with a trailing slash.
-        # Make sure GET params are preserved.
-        self.getPage('/redirect?id=3')
-        self.assertStatus(301)
-        self.assertMatchesBody(
-            '<a href=([\'"])%s/redirect/[?]id=3\\1>'
-            '%s/redirect/[?]id=3</a>' % (self.base(), self.base())
-        )
-
-        if self.prefix():
-            # Corner case: the "trailing slash" redirect could be tricky if
-            # we're using a virtual root and the URI is "/vroot" (no slash).
-            self.getPage('')
-            self.assertStatus(301)
-            self.assertMatchesBody("<a href=(['\"])%s/\\1>%s/</a>" %
-                                   (self.base(), self.base()))
-
-        # Test that requests for NON-index methods WITH a trailing slash
-        # get redirected to the same URI path WITHOUT a trailing slash.
-        # Make sure GET params are preserved.
-        self.getPage('/redirect/by_code/?code=307')
-        self.assertStatus(301)
-        self.assertMatchesBody(
-            "<a href=(['\"])%s/redirect/by_code[?]code=307\\1>"
-            '%s/redirect/by_code[?]code=307</a>'
-            % (self.base(), self.base())
-        )
-
-        # If the trailing_slash tool is off, CP should just continue
-        # as if the slashes were correct. But it needs some help
-        # inside cherrypy.url to form correct output.
-        self.getPage('/url?path_info=page1')
-        self.assertBody('%s/url/page1' % self.base())
-        self.getPage('/url/leaf/?path_info=page1')
-        self.assertBody('%s/url/page1' % self.base())
-
-    def testRedirect(self):
-        self.getPage('/redirect/')
-        self.assertBody('child')
-        self.assertStatus(200)
-
-        self.getPage('/redirect/by_code?code=300')
-        self.assertMatchesBody(
-            r"<a href=(['\"])(.*)somewhere%20else\1>\2somewhere%20else</a>")
-        self.assertStatus(300)
-
-        self.getPage('/redirect/by_code?code=301')
-        self.assertMatchesBody(
-            r"<a href=(['\"])(.*)somewhere%20else\1>\2somewhere%20else</a>")
-        self.assertStatus(301)
-
-        self.getPage('/redirect/by_code?code=302')
-        self.assertMatchesBody(
-            r"<a href=(['\"])(.*)somewhere%20else\1>\2somewhere%20else</a>")
-        self.assertStatus(302)
-
-        self.getPage('/redirect/by_code?code=303')
-        self.assertMatchesBody(
-            r"<a href=(['\"])(.*)somewhere%20else\1>\2somewhere%20else</a>")
-        self.assertStatus(303)
-
-        self.getPage('/redirect/by_code?code=307')
-        self.assertMatchesBody(
-            r"<a href=(['\"])(.*)somewhere%20else\1>\2somewhere%20else</a>")
-        self.assertStatus(307)
-
-        self.getPage('/redirect/nomodify')
-        self.assertBody('')
-        self.assertStatus(304)
-
-        self.getPage('/redirect/proxy')
-        self.assertBody('')
-        self.assertStatus(305)
-
-        # HTTPRedirect on error
-        self.getPage('/redirect/error/')
-        self.assertStatus(('302 Found', '303 See Other'))
-        self.assertInBody('/errpage')
-
-        # Make sure str(HTTPRedirect()) works.
-        self.getPage('/redirect/stringify', protocol='HTTP/1.0')
-        self.assertStatus(200)
-        self.assertBody("(['%s/'], 302)" % self.base())
-        if cherrypy.server.protocol_version == 'HTTP/1.1':
-            self.getPage('/redirect/stringify', protocol='HTTP/1.1')
-            self.assertStatus(200)
-            self.assertBody("(['%s/'], 303)" % self.base())
-
-        # check that #fragments are handled properly
-        # http://skrb.org/ietf/http_errata.html#location-fragments
-        frag = 'foo'
-        self.getPage('/redirect/fragment/%s' % frag)
-        self.assertMatchesBody(
-            r"<a href=(['\"])(.*)\/some\/url\#%s\1>\2\/some\/url\#%s</a>" % (
-                frag, frag))
-        loc = self.assertHeader('Location')
-        assert loc.endswith('#%s' % frag)
-        self.assertStatus(('302 Found', '303 See Other'))
-
-        # check injection protection
-        # See https://github.com/cherrypy/cherrypy/issues/1003
-        self.getPage(
-            '/redirect/custom?'
-            'code=303&url=/foobar/%0d%0aSet-Cookie:%20somecookie=someval')
-        self.assertStatus(303)
-        loc = self.assertHeader('Location')
-        assert 'Set-Cookie' in loc
-        self.assertNoHeader('Set-Cookie')
-
-        def assertValidXHTML():
-            from xml.etree import ElementTree
-            try:
-                ElementTree.fromstring(
-                    '<html><body>%s</body></html>' % self.body,
-                )
-            except ElementTree.ParseError:
-                self._handlewebError(
-                    'automatically generated redirect did not '
-                    'generate well-formed html',
-                )
-
-        # check redirects to URLs generated valid HTML - we check this
-        # by seeing if it appears as valid XHTML.
-        self.getPage('/redirect/by_code?code=303')
-        self.assertStatus(303)
-        assertValidXHTML()
-
-        # do the same with a url containing quote characters.
-        self.getPage('/redirect/url_with_quote')
-        self.assertStatus(303)
-        assertValidXHTML()
-
-    def test_redirect_with_xss(self):
-        """A redirect to a URL with HTML injected should result
-        in page contents escaped."""
-        self.getPage('/redirect/url_with_xss')
-        self.assertStatus(303)
-        assert b'<script>' not in self.body
-        assert b'&lt;script&gt;' in self.body
-
-    def test_redirect_with_unicode(self):
-        """
-        A redirect to a URL with Unicode should return a Location
-        header containing that Unicode URL.
-        """
-        # test disabled due to #1440
-        return
-        self.getPage('/redirect/url_with_unicode')
-        self.assertStatus(303)
-        loc = self.assertHeader('Location')
-        assert ntou('тест', encoding='utf-8') in loc
-
-    def test_InternalRedirect(self):
-        # InternalRedirect
-        self.getPage('/internalredirect/')
-        self.assertBody('hello')
-        self.assertStatus(200)
-
-        # Test passthrough
-        self.getPage(
-            '/internalredirect/petshop?user_id=Sir-not-appearing-in-this-film')
-        self.assertBody('0 images for Sir-not-appearing-in-this-film')
-        self.assertStatus(200)
-
-        # Test args
-        self.getPage('/internalredirect/petshop?user_id=parrot')
-        self.assertBody('0 images for slug')
-        self.assertStatus(200)
-
-        # Test POST
-        self.getPage('/internalredirect/petshop', method='POST',
-                     body='user_id=terrier')
-        self.assertBody('0 images for fish')
-        self.assertStatus(200)
-
-        # Test ir before body read
-        self.getPage('/internalredirect/early_ir', method='POST',
-                     body='arg=aha!')
-        self.assertBody('Something went horribly wrong.')
-        self.assertStatus(200)
-
-        self.getPage('/internalredirect/secure')
-        self.assertBody('Please log in')
-        self.assertStatus(200)
-
-        # Relative path in InternalRedirect.
-        # Also tests request.prev.
-        self.getPage('/internalredirect/relative?a=3&b=5')
-        self.assertBody('a=3&b=5')
-        self.assertStatus(200)
-
-        # InternalRedirect on error
-        self.getPage('/internalredirect/choke')
-        self.assertStatus(200)
-        self.assertBody('Something went horribly wrong.')
-
-    def testFlatten(self):
-        for url in ['/flatten/as_string', '/flatten/as_list',
-                    '/flatten/as_yield', '/flatten/as_dblyield',
-                    '/flatten/as_refyield']:
-            self.getPage(url)
-            self.assertBody('content')
-
-    def testRanges(self):
-        self.getPage('/ranges/get_ranges?bytes=3-6')
-        self.assertBody('[(3, 7)]')
-
-        # Test multiple ranges and a suffix-byte-range-spec, for good measure.
-        self.getPage('/ranges/get_ranges?bytes=2-4,-1')
-        self.assertBody('[(2, 5), (7, 8)]')
-
-        # Test a suffix-byte-range longer than the content
-        # length. Note that in this test, the content length
-        # is 8 bytes.
-        self.getPage('/ranges/get_ranges?bytes=-100')
-        self.assertBody('[(0, 8)]')
-
-        # Get a partial file.
-        if cherrypy.server.protocol_version == 'HTTP/1.1':
-            self.getPage('/ranges/slice_file', [('Range', 'bytes=2-5')])
-            self.assertStatus(206)
-            self.assertHeader('Content-Type', 'text/html;charset=utf-8')
-            self.assertHeader('Content-Range', 'bytes 2-5/14')
-            self.assertBody('llo,')
-
-            # What happens with overlapping ranges (and out of order, too)?
-            self.getPage('/ranges/slice_file', [('Range', 'bytes=4-6,2-5')])
-            self.assertStatus(206)
-            ct = self.assertHeader('Content-Type')
-            expected_type = 'multipart/byteranges; boundary='
-            self.assert_(ct.startswith(expected_type))
-            boundary = ct[len(expected_type):]
-            expected_body = ('\r\n--%s\r\n'
-                             'Content-type: text/html\r\n'
-                             'Content-range: bytes 4-6/14\r\n'
-                             '\r\n'
-                             'o, \r\n'
-                             '--%s\r\n'
-                             'Content-type: text/html\r\n'
-                             'Content-range: bytes 2-5/14\r\n'
-                             '\r\n'
-                             'llo,\r\n'
-                             '--%s--\r\n' % (boundary, boundary, boundary))
-            self.assertBody(expected_body)
-            self.assertHeader('Content-Length')
-
-            # Test "416 Requested Range Not Satisfiable"
-            self.getPage('/ranges/slice_file', [('Range', 'bytes=2300-2900')])
-            self.assertStatus(416)
-            # "When this status code is returned for a byte-range request,
-            # the response SHOULD include a Content-Range entity-header
-            # field specifying the current length of the selected resource"
-            self.assertHeader('Content-Range', 'bytes */14')
-        elif cherrypy.server.protocol_version == 'HTTP/1.0':
-            # Test Range behavior with HTTP/1.0 request
-            self.getPage('/ranges/slice_file', [('Range', 'bytes=2-5')])
-            self.assertStatus(200)
-            self.assertBody('Hello, world\r\n')
-
-    def testFavicon(self):
-        # favicon.ico is served by staticfile.
-        icofilename = os.path.join(localDir, '../favicon.ico')
-        icofile = open(icofilename, 'rb')
-        data = icofile.read()
-        icofile.close()
-
-        self.getPage('/favicon.ico')
-        self.assertBody(data)
-
-    def skip_if_bad_cookies(self):
-        """
-        cookies module fails to reject invalid cookies
-        https://github.com/cherrypy/cherrypy/issues/1405
-        """
-        cookies = sys.modules.get('http.cookies')
-        _is_legal_key = getattr(cookies, '_is_legal_key', lambda x: False)
-        if not _is_legal_key(','):
-            return
-        issue = 'http://bugs.python.org/issue26302'
-        tmpl = 'Broken cookies module ({issue})'
-        self.skip(tmpl.format(**locals()))
-
-    def testCookies(self):
-        self.skip_if_bad_cookies()
-
-        self.getPage('/cookies/single?name=First',
-                     [('Cookie', 'First=Dinsdale;')])
-        self.assertHeader('Set-Cookie', 'First=Dinsdale')
-
-        self.getPage('/cookies/multiple?names=First&names=Last',
-                     [('Cookie', 'First=Dinsdale; Last=Piranha;'),
-                      ])
-        self.assertHeader('Set-Cookie', 'First=Dinsdale')
-        self.assertHeader('Set-Cookie', 'Last=Piranha')
-
-        self.getPage('/cookies/single?name=Something-With%2CComma',
-                     [('Cookie', 'Something-With,Comma=some-value')])
-        self.assertStatus(400)
-
-    def testDefaultContentType(self):
-        self.getPage('/')
-        self.assertHeader('Content-Type', 'text/html;charset=utf-8')
-        self.getPage('/defct/plain')
-        self.getPage('/')
-        self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
-        self.getPage('/defct/html')
-
-    def test_multiple_headers(self):
-        self.getPage('/multiheader/header_list')
-        self.assertEqual(
-            [(k, v) for k, v in self.headers if k == 'WWW-Authenticate'],
-            [('WWW-Authenticate', 'Negotiate'),
-             ('WWW-Authenticate', 'Basic realm="foo"'),
-             ])
-        self.getPage('/multiheader/commas')
-        self.assertHeader('WWW-Authenticate', 'Negotiate,Basic realm="foo"')
-
-    def test_cherrypy_url(self):
-        # Input relative to current
-        self.getPage('/url/leaf?path_info=page1')
-        self.assertBody('%s/url/page1' % self.base())
-        self.getPage('/url/?path_info=page1')
-        self.assertBody('%s/url/page1' % self.base())
-        # Other host header
-        host = 'www.mydomain.example'
-        self.getPage('/url/leaf?path_info=page1',
-                     headers=[('Host', host)])
-        self.assertBody('%s://%s/url/page1' % (self.scheme, host))
-
-        # Input is 'absolute'; that is, relative to script_name
-        self.getPage('/url/leaf?path_info=/page1')
-        self.assertBody('%s/page1' % self.base())
-        self.getPage('/url/?path_info=/page1')
-        self.assertBody('%s/page1' % self.base())
-
-        # Single dots
-        self.getPage('/url/leaf?path_info=./page1')
-        self.assertBody('%s/url/page1' % self.base())
-        self.getPage('/url/leaf?path_info=other/./page1')
-        self.assertBody('%s/url/other/page1' % self.base())
-        self.getPage('/url/?path_info=/other/./page1')
-        self.assertBody('%s/other/page1' % self.base())
-        self.getPage('/url/?path_info=/other/././././page1')
-        self.assertBody('%s/other/page1' % self.base())
-
-        # Double dots
-        self.getPage('/url/leaf?path_info=../page1')
-        self.assertBody('%s/page1' % self.base())
-        self.getPage('/url/leaf?path_info=other/../page1')
-        self.assertBody('%s/url/page1' % self.base())
-        self.getPage('/url/leaf?path_info=/other/../page1')
-        self.assertBody('%s/page1' % self.base())
-        self.getPage('/url/leaf?path_info=/other/../../../page1')
-        self.assertBody('%s/page1' % self.base())
-        self.getPage('/url/leaf?path_info=/other/../../../../../page1')
-        self.assertBody('%s/page1' % self.base())
-
-        # qs param is not normalized as a path
-        self.getPage('/url/qs?qs=/other')
-        self.assertBody('%s/url/qs?/other' % self.base())
-        self.getPage('/url/qs?qs=/other/../page1')
-        self.assertBody('%s/url/qs?/other/../page1' % self.base())
-        self.getPage('/url/qs?qs=../page1')
-        self.assertBody('%s/url/qs?../page1' % self.base())
-        self.getPage('/url/qs?qs=../../page1')
-        self.assertBody('%s/url/qs?../../page1' % self.base())
-
-        # Output relative to current path or script_name
-        self.getPage('/url/?path_info=page1&relative=True')
-        self.assertBody('page1')
-        self.getPage('/url/leaf?path_info=/page1&relative=True')
-        self.assertBody('../page1')
-        self.getPage('/url/leaf?path_info=page1&relative=True')
-        self.assertBody('page1')
-        self.getPage('/url/leaf?path_info=leaf/page1&relative=True')
-        self.assertBody('leaf/page1')
-        self.getPage('/url/leaf?path_info=../page1&relative=True')
-        self.assertBody('../page1')
-        self.getPage('/url/?path_info=other/../page1&relative=True')
-        self.assertBody('page1')
-
-        # Output relative to /
-        self.getPage('/baseurl?path_info=ab&relative=True')
-        self.assertBody('ab')
-        # Output relative to /
-        self.getPage('/baseurl?path_info=/ab&relative=True')
-        self.assertBody('ab')
-
-        # absolute-path references ("server-relative")
-        # Input relative to current
-        self.getPage('/url/leaf?path_info=page1&relative=server')
-        self.assertBody('/url/page1')
-        self.getPage('/url/?path_info=page1&relative=server')
-        self.assertBody('/url/page1')
-        # Input is 'absolute'; that is, relative to script_name
-        self.getPage('/url/leaf?path_info=/page1&relative=server')
-        self.assertBody('/page1')
-        self.getPage('/url/?path_info=/page1&relative=server')
-        self.assertBody('/page1')
-
-    def test_expose_decorator(self):
-        # Test @expose
-        self.getPage('/expose_dec/no_call')
-        self.assertStatus(200)
-        self.assertBody('Mr E. R. Bradshaw')
-
-        # Test @expose()
-        self.getPage('/expose_dec/call_empty')
-        self.assertStatus(200)
-        self.assertBody('Mrs. B.J. Smegma')
-
-        # Test @expose("alias")
-        self.getPage('/expose_dec/call_alias')
-        self.assertStatus(200)
-        self.assertBody('Mr Nesbitt')
-        # Does the original name work?
-        self.getPage('/expose_dec/nesbitt')
-        self.assertStatus(200)
-        self.assertBody('Mr Nesbitt')
-
-        # Test @expose(["alias1", "alias2"])
-        self.getPage('/expose_dec/alias1')
-        self.assertStatus(200)
-        self.assertBody('Mr Ken Andrews')
-        self.getPage('/expose_dec/alias2')
-        self.assertStatus(200)
-        self.assertBody('Mr Ken Andrews')
-        # Does the original name work?
-        self.getPage('/expose_dec/andrews')
-        self.assertStatus(200)
-        self.assertBody('Mr Ken Andrews')
-
-        # Test @expose(alias="alias")
-        self.getPage('/expose_dec/alias3')
-        self.assertStatus(200)
-        self.assertBody('Mr. and Mrs. Watson')
-
-
-class ErrorTests(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-        def break_header():
-            # Add a header after finalize that is invalid
-            cherrypy.serving.response.header_list.append((2, 3))
-        cherrypy.tools.break_header = cherrypy.Tool(
-            'on_end_resource', break_header)
-
-        class Root:
-
-            @cherrypy.expose
-            def index(self):
-                return 'hello'
-
-            @cherrypy.config(**{'tools.break_header.on': True})
-            def start_response_error(self):
-                return 'salud!'
-
-            @cherrypy.expose
-            def stat(self, path):
-                with cherrypy.HTTPError.handle(OSError, 404):
-                    os.stat(path)
-
-        root = Root()
-
-        cherrypy.tree.mount(root)
-
-    def test_start_response_error(self):
-        self.getPage('/start_response_error')
-        self.assertStatus(500)
-        self.assertInBody(
-            'TypeError: response.header_list key 2 is not a byte string.')
-
-    def test_contextmanager(self):
-        self.getPage('/stat/missing')
-        self.assertStatus(404)
-        body_text = self.body.decode('utf-8')
-        assert (
-            'No such file or directory' in body_text or
-            'cannot find the file specified' in body_text
-        )
-
-
-class TestBinding:
-    def test_bind_ephemeral_port(self):
-        """
-        A server configured to bind to port 0 will bind to an ephemeral
-        port and indicate that port number on startup.
-        """
-        cherrypy.config.reset()
-        bind_ephemeral_conf = {
-            'server.socket_port': 0,
-        }
-        cherrypy.config.update(bind_ephemeral_conf)
-        cherrypy.engine.start()
-        assert cherrypy.server.bound_addr != cherrypy.server.bind_addr
-        _host, port = cherrypy.server.bound_addr
-        assert port > 0
-        cherrypy.engine.stop()
-        assert cherrypy.server.bind_addr == cherrypy.server.bound_addr
diff --git a/libraries/cherrypy/test/test_dynamicobjectmapping.py b/libraries/cherrypy/test/test_dynamicobjectmapping.py
deleted file mode 100644
index 725a3ce0..00000000
--- a/libraries/cherrypy/test/test_dynamicobjectmapping.py
+++ /dev/null
@@ -1,424 +0,0 @@
-import six
-
-import cherrypy
-from cherrypy.test import helper
-
-script_names = ['', '/foo', '/users/fred/blog', '/corp/blog']
-
-
-def setup_server():
-    class SubSubRoot:
-
-        @cherrypy.expose
-        def index(self):
-            return 'SubSubRoot index'
-
-        @cherrypy.expose
-        def default(self, *args):
-            return 'SubSubRoot default'
-
-        @cherrypy.expose
-        def handler(self):
-            return 'SubSubRoot handler'
-
-        @cherrypy.expose
-        def dispatch(self):
-            return 'SubSubRoot dispatch'
-
-    subsubnodes = {
-        '1': SubSubRoot(),
-        '2': SubSubRoot(),
-    }
-
-    class SubRoot:
-
-        @cherrypy.expose
-        def index(self):
-            return 'SubRoot index'
-
-        @cherrypy.expose
-        def default(self, *args):
-            return 'SubRoot %s' % (args,)
-
-        @cherrypy.expose
-        def handler(self):
-            return 'SubRoot handler'
-
-        def _cp_dispatch(self, vpath):
-            return subsubnodes.get(vpath[0], None)
-
-    subnodes = {
-        '1': SubRoot(),
-        '2': SubRoot(),
-    }
-
-    class Root:
-
-        @cherrypy.expose
-        def index(self):
-            return 'index'
-
-        @cherrypy.expose
-        def default(self, *args):
-            return 'default %s' % (args,)
-
-        @cherrypy.expose
-        def handler(self):
-            return 'handler'
-
-        def _cp_dispatch(self, vpath):
-            return subnodes.get(vpath[0])
-
-    # -------------------------------------------------------------------------
-    # DynamicNodeAndMethodDispatcher example.
-    # This example exposes a fairly naive HTTP api
-    class User(object):
-
-        def __init__(self, id, name):
-            self.id = id
-            self.name = name
-
-        def __unicode__(self):
-            return six.text_type(self.name)
-
-        def __str__(self):
-            return str(self.name)
-
-    user_lookup = {
-        1: User(1, 'foo'),
-        2: User(2, 'bar'),
-    }
-
-    def make_user(name, id=None):
-        if not id:
-            id = max(*list(user_lookup.keys())) + 1
-        user_lookup[id] = User(id, name)
-        return id
-
-    @cherrypy.expose
-    class UserContainerNode(object):
-
-        def POST(self, name):
-            """
-            Allow the creation of a new Object
-            """
-            return 'POST %d' % make_user(name)
-
-        def GET(self):
-            return six.text_type(sorted(user_lookup.keys()))
-
-        def dynamic_dispatch(self, vpath):
-            try:
-                id = int(vpath[0])
-            except (ValueError, IndexError):
-                return None
-            return UserInstanceNode(id)
-
-    @cherrypy.expose
-    class UserInstanceNode(object):
-
-        def __init__(self, id):
-            self.id = id
-            self.user = user_lookup.get(id, None)
-
-            # For all but PUT methods there MUST be a valid user identified
-            # by self.id
-            if not self.user and cherrypy.request.method != 'PUT':
-                raise cherrypy.HTTPError(404)
-
-        def GET(self, *args, **kwargs):
-            """
-            Return the appropriate representation of the instance.
-            """
-            return six.text_type(self.user)
-
-        def POST(self, name):
-            """
-            Update the fields of the user instance.
-            """
-            self.user.name = name
-            return 'POST %d' % self.user.id
-
-        def PUT(self, name):
-            """
-            Create a new user with the specified id, or edit it if it already
-            exists
-            """
-            if self.user:
-                # Edit the current user
-                self.user.name = name
-                return 'PUT %d' % self.user.id
-            else:
-                # Make a new user with said attributes.
-                return 'PUT %d' % make_user(name, self.id)
-
-        def DELETE(self):
-            """
-            Delete the user specified at the id.
-            """
-            id = self.user.id
-            del user_lookup[self.user.id]
-            del self.user
-            return 'DELETE %d' % id
-
-    class ABHandler:
-
-        class CustomDispatch:
-
-            @cherrypy.expose
-            def index(self, a, b):
-                return 'custom'
-
-        def _cp_dispatch(self, vpath):
-            """Make sure that if we don't pop anything from vpath,
-            processing still works.
-            """
-            return self.CustomDispatch()
-
-        @cherrypy.expose
-        def index(self, a, b=None):
-            body = ['a:' + str(a)]
-            if b is not None:
-                body.append(',b:' + str(b))
-            return ''.join(body)
-
-        @cherrypy.expose
-        def delete(self, a, b):
-            return 'deleting ' + str(a) + ' and ' + str(b)
-
-    class IndexOnly:
-
-        def _cp_dispatch(self, vpath):
-            """Make sure that popping ALL of vpath still shows the index
-            handler.
-            """
-            while vpath:
-                vpath.pop()
-            return self
-
-        @cherrypy.expose
-        def index(self):
-            return 'IndexOnly index'
-
-    class DecoratedPopArgs:
-
-        """Test _cp_dispatch with @cherrypy.popargs."""
-
-        @cherrypy.expose
-        def index(self):
-            return 'no params'
-
-        @cherrypy.expose
-        def hi(self):
-            return "hi was not interpreted as 'a' param"
-    DecoratedPopArgs = cherrypy.popargs(
-        'a', 'b', handler=ABHandler())(DecoratedPopArgs)
-
-    class NonDecoratedPopArgs:
-
-        """Test _cp_dispatch = cherrypy.popargs()"""
-
-        _cp_dispatch = cherrypy.popargs('a')
-
-        @cherrypy.expose
-        def index(self, a):
-            return 'index: ' + str(a)
-
-    class ParameterizedHandler:
-
-        """Special handler created for each request"""
-
-        def __init__(self, a):
-            self.a = a
-
-        @cherrypy.expose
-        def index(self):
-            if 'a' in cherrypy.request.params:
-                raise Exception(
-                    'Parameterized handler argument ended up in '
-                    'request.params')
-            return self.a
-
-    class ParameterizedPopArgs:
-
-        """Test cherrypy.popargs() with a function call handler"""
-    ParameterizedPopArgs = cherrypy.popargs(
-        'a', handler=ParameterizedHandler)(ParameterizedPopArgs)
-
-    Root.decorated = DecoratedPopArgs()
-    Root.undecorated = NonDecoratedPopArgs()
-    Root.index_only = IndexOnly()
-    Root.parameter_test = ParameterizedPopArgs()
-
-    Root.users = UserContainerNode()
-
-    md = cherrypy.dispatch.MethodDispatcher('dynamic_dispatch')
-    for url in script_names:
-        conf = {
-            '/': {
-                'user': (url or '/').split('/')[-2],
-            },
-            '/users': {
-                'request.dispatch': md
-            },
-        }
-        cherrypy.tree.mount(Root(), url, conf)
-
-
-class DynamicObjectMappingTest(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def testObjectMapping(self):
-        for url in script_names:
-            self.script_name = url
-
-            self.getPage('/')
-            self.assertBody('index')
-
-            self.getPage('/handler')
-            self.assertBody('handler')
-
-            # Dynamic dispatch will succeed here for the subnodes
-            # so the subroot gets called
-            self.getPage('/1/')
-            self.assertBody('SubRoot index')
-
-            self.getPage('/2/')
-            self.assertBody('SubRoot index')
-
-            self.getPage('/1/handler')
-            self.assertBody('SubRoot handler')
-
-            self.getPage('/2/handler')
-            self.assertBody('SubRoot handler')
-
-            # Dynamic dispatch will fail here for the subnodes
-            # so the default gets called
-            self.getPage('/asdf/')
-            self.assertBody("default ('asdf',)")
-
-            self.getPage('/asdf/asdf')
-            self.assertBody("default ('asdf', 'asdf')")
-
-            self.getPage('/asdf/handler')
-            self.assertBody("default ('asdf', 'handler')")
-
-            # Dynamic dispatch will succeed here for the subsubnodes
-            # so the subsubroot gets called
-            self.getPage('/1/1/')
-            self.assertBody('SubSubRoot index')
-
-            self.getPage('/2/2/')
-            self.assertBody('SubSubRoot index')
-
-            self.getPage('/1/1/handler')
-            self.assertBody('SubSubRoot handler')
-
-            self.getPage('/2/2/handler')
-            self.assertBody('SubSubRoot handler')
-
-            self.getPage('/2/2/dispatch')
-            self.assertBody('SubSubRoot dispatch')
-
-            # The exposed dispatch will not be called as a dispatch
-            # method.
-            self.getPage('/2/2/foo/foo')
-            self.assertBody('SubSubRoot default')
-
-            # Dynamic dispatch will fail here for the subsubnodes
-            # so the SubRoot gets called
-            self.getPage('/1/asdf/')
-            self.assertBody("SubRoot ('asdf',)")
-
-            self.getPage('/1/asdf/asdf')
-            self.assertBody("SubRoot ('asdf', 'asdf')")
-
-            self.getPage('/1/asdf/handler')
-            self.assertBody("SubRoot ('asdf', 'handler')")
-
-    def testMethodDispatch(self):
-        # GET acts like a container
-        self.getPage('/users')
-        self.assertBody('[1, 2]')
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-
-        # POST to the container URI allows creation
-        self.getPage('/users', method='POST', body='name=baz')
-        self.assertBody('POST 3')
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-
-        # POST to a specific instanct URI results in a 404
-        # as the resource does not exit.
-        self.getPage('/users/5', method='POST', body='name=baz')
-        self.assertStatus(404)
-
-        # PUT to a specific instanct URI results in creation
-        self.getPage('/users/5', method='PUT', body='name=boris')
-        self.assertBody('PUT 5')
-        self.assertHeader('Allow', 'DELETE, GET, HEAD, POST, PUT')
-
-        # GET acts like a container
-        self.getPage('/users')
-        self.assertBody('[1, 2, 3, 5]')
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-
-        test_cases = (
-            (1, 'foo', 'fooupdated', 'DELETE, GET, HEAD, POST, PUT'),
-            (2, 'bar', 'barupdated', 'DELETE, GET, HEAD, POST, PUT'),
-            (3, 'baz', 'bazupdated', 'DELETE, GET, HEAD, POST, PUT'),
-            (5, 'boris', 'borisupdated', 'DELETE, GET, HEAD, POST, PUT'),
-        )
-        for id, name, updatedname, headers in test_cases:
-            self.getPage('/users/%d' % id)
-            self.assertBody(name)
-            self.assertHeader('Allow', headers)
-
-            # Make sure POSTs update already existings resources
-            self.getPage('/users/%d' %
-                         id, method='POST', body='name=%s' % updatedname)
-            self.assertBody('POST %d' % id)
-            self.assertHeader('Allow', headers)
-
-            # Make sure PUTs Update already existing resources.
-            self.getPage('/users/%d' %
-                         id, method='PUT', body='name=%s' % updatedname)
-            self.assertBody('PUT %d' % id)
-            self.assertHeader('Allow', headers)
-
-            # Make sure DELETES Remove already existing resources.
-            self.getPage('/users/%d' % id, method='DELETE')
-            self.assertBody('DELETE %d' % id)
-            self.assertHeader('Allow', headers)
-
-        # GET acts like a container
-        self.getPage('/users')
-        self.assertBody('[]')
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-
-    def testVpathDispatch(self):
-        self.getPage('/decorated/')
-        self.assertBody('no params')
-
-        self.getPage('/decorated/hi')
-        self.assertBody("hi was not interpreted as 'a' param")
-
-        self.getPage('/decorated/yo/')
-        self.assertBody('a:yo')
-
-        self.getPage('/decorated/yo/there/')
-        self.assertBody('a:yo,b:there')
-
-        self.getPage('/decorated/yo/there/delete')
-        self.assertBody('deleting yo and there')
-
-        self.getPage('/decorated/yo/there/handled_by_dispatch/')
-        self.assertBody('custom')
-
-        self.getPage('/undecorated/blah/')
-        self.assertBody('index: blah')
-
-        self.getPage('/index_only/a/b/c/d/e/f/g/')
-        self.assertBody('IndexOnly index')
-
-        self.getPage('/parameter_test/argument2/')
-        self.assertBody('argument2')
diff --git a/libraries/cherrypy/test/test_encoding.py b/libraries/cherrypy/test/test_encoding.py
deleted file mode 100644
index ab24ab93..00000000
--- a/libraries/cherrypy/test/test_encoding.py
+++ /dev/null
@@ -1,426 +0,0 @@
-# coding: utf-8
-
-import gzip
-import io
-from unittest import mock
-
-from six.moves.http_client import IncompleteRead
-from six.moves.urllib.parse import quote as url_quote
-
-import cherrypy
-from cherrypy._cpcompat import ntob, ntou
-
-from cherrypy.test import helper
-
-
-europoundUnicode = ntou('£', encoding='utf-8')
-sing = ntou('毛泽东: Sing, Little Birdie?', encoding='utf-8')
-
-sing8 = sing.encode('utf-8')
-sing16 = sing.encode('utf-16')
-
-
-class EncodingTests(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-        class Root:
-
-            @cherrypy.expose
-            def index(self, param):
-                assert param == europoundUnicode, '%r != %r' % (
-                    param, europoundUnicode)
-                yield europoundUnicode
-
-            @cherrypy.expose
-            def mao_zedong(self):
-                return sing
-
-            @cherrypy.expose
-            @cherrypy.config(**{'tools.encode.encoding': 'utf-8'})
-            def utf8(self):
-                return sing8
-
-            @cherrypy.expose
-            def cookies_and_headers(self):
-                # if the headers have non-ascii characters and a cookie has
-                #  any part which is unicode (even ascii), the response
-                #  should not fail.
-                cherrypy.response.cookie['candy'] = 'bar'
-                cherrypy.response.cookie['candy']['domain'] = 'cherrypy.org'
-                cherrypy.response.headers[
-                    'Some-Header'] = 'My d\xc3\xb6g has fleas'
-                return 'Any content'
-
-            @cherrypy.expose
-            def reqparams(self, *args, **kwargs):
-                return b', '.join(
-                    [': '.join((k, v)).encode('utf8')
-                     for k, v in sorted(cherrypy.request.params.items())]
-                )
-
-            @cherrypy.expose
-            @cherrypy.config(**{
-                'tools.encode.text_only': False,
-                'tools.encode.add_charset': True,
-            })
-            def nontext(self, *args, **kwargs):
-                cherrypy.response.headers[
-                    'Content-Type'] = 'application/binary'
-                return '\x00\x01\x02\x03'
-
-        class GZIP:
-
-            @cherrypy.expose
-            def index(self):
-                yield 'Hello, world'
-
-            @cherrypy.expose
-            # Turn encoding off so the gzip tool is the one doing the collapse.
-            @cherrypy.config(**{'tools.encode.on': False})
-            def noshow(self):
-                # Test for ticket #147, where yield showed no exceptions
-                # (content-encoding was still gzip even though traceback
-                # wasn't zipped).
-                raise IndexError()
-                yield 'Here be dragons'
-
-            @cherrypy.expose
-            @cherrypy.config(**{'response.stream': True})
-            def noshow_stream(self):
-                # Test for ticket #147, where yield showed no exceptions
-                # (content-encoding was still gzip even though traceback
-                # wasn't zipped).
-                raise IndexError()
-                yield 'Here be dragons'
-
-        class Decode:
-
-            @cherrypy.expose
-            @cherrypy.config(**{
-                'tools.decode.on': True,
-                'tools.decode.default_encoding': ['utf-16'],
-            })
-            def extra_charset(self, *args, **kwargs):
-                return ', '.join([': '.join((k, v))
-                                  for k, v in cherrypy.request.params.items()])
-
-            @cherrypy.expose
-            @cherrypy.config(**{
-                'tools.decode.on': True,
-                'tools.decode.encoding': 'utf-16',
-            })
-            def force_charset(self, *args, **kwargs):
-                return ', '.join([': '.join((k, v))
-                                  for k, v in cherrypy.request.params.items()])
-
-        root = Root()
-        root.gzip = GZIP()
-        root.decode = Decode()
-        cherrypy.tree.mount(root, config={'/gzip': {'tools.gzip.on': True}})
-
-    def test_query_string_decoding(self):
-        URI_TMPL = '/reqparams?q={q}'
-
-        europoundUtf8_2_bytes = europoundUnicode.encode('utf-8')
-        europoundUtf8_2nd_byte = europoundUtf8_2_bytes[1:2]
-
-        # Encoded utf8 query strings MUST be parsed correctly.
-        # Here, q is the POUND SIGN U+00A3 encoded in utf8 and then %HEX
-        self.getPage(URI_TMPL.format(q=url_quote(europoundUtf8_2_bytes)))
-        # The return value will be encoded as utf8.
-        self.assertBody(b'q: ' + europoundUtf8_2_bytes)
-
-        # Query strings that are incorrectly encoded MUST raise 404.
-        # Here, q is the second byte of POUND SIGN U+A3 encoded in utf8
-        # and then %HEX
-        # TODO: check whether this shouldn't raise 400 Bad Request instead
-        self.getPage(URI_TMPL.format(q=url_quote(europoundUtf8_2nd_byte)))
-        self.assertStatus(404)
-        self.assertErrorPage(
-            404,
-            'The given query string could not be processed. Query '
-            "strings for this resource must be encoded with 'utf8'.")
-
-    def test_urlencoded_decoding(self):
-        # Test the decoding of an application/x-www-form-urlencoded entity.
-        europoundUtf8 = europoundUnicode.encode('utf-8')
-        body = b'param=' + europoundUtf8
-        self.getPage('/',
-                     method='POST',
-                     headers=[
-                         ('Content-Type', 'application/x-www-form-urlencoded'),
-                         ('Content-Length', str(len(body))),
-                     ],
-                     body=body),
-        self.assertBody(europoundUtf8)
-
-        # Encoded utf8 entities MUST be parsed and decoded correctly.
-        # Here, q is the POUND SIGN U+00A3 encoded in utf8
-        body = b'q=\xc2\xa3'
-        self.getPage('/reqparams', method='POST',
-                     headers=[(
-                         'Content-Type', 'application/x-www-form-urlencoded'),
-                         ('Content-Length', str(len(body))),
-                     ],
-                     body=body),
-        self.assertBody(b'q: \xc2\xa3')
-
-        # ...and in utf16, which is not in the default attempt_charsets list:
-        body = b'\xff\xfeq\x00=\xff\xfe\xa3\x00'
-        self.getPage('/reqparams',
-                     method='POST',
-                     headers=[
-                         ('Content-Type',
-                          'application/x-www-form-urlencoded;charset=utf-16'),
-                         ('Content-Length', str(len(body))),
-                     ],
-                     body=body),
-        self.assertBody(b'q: \xc2\xa3')
-
-        # Entities that are incorrectly encoded MUST raise 400.
-        # Here, q is the POUND SIGN U+00A3 encoded in utf16, but
-        # the Content-Type incorrectly labels it utf-8.
-        body = b'\xff\xfeq\x00=\xff\xfe\xa3\x00'
-        self.getPage('/reqparams',
-                     method='POST',
-                     headers=[
-                         ('Content-Type',
-                          'application/x-www-form-urlencoded;charset=utf-8'),
-                         ('Content-Length', str(len(body))),
-                     ],
-                     body=body),
-        self.assertStatus(400)
-        self.assertErrorPage(
-            400,
-            'The request entity could not be decoded. The following charsets '
-            "were attempted: ['utf-8']")
-
-    def test_decode_tool(self):
-        # An extra charset should be tried first, and succeed if it matches.
-        # Here, we add utf-16 as a charset and pass a utf-16 body.
-        body = b'\xff\xfeq\x00=\xff\xfe\xa3\x00'
-        self.getPage('/decode/extra_charset', method='POST',
-                     headers=[(
-                         'Content-Type', 'application/x-www-form-urlencoded'),
-                         ('Content-Length', str(len(body))),
-                     ],
-                     body=body),
-        self.assertBody(b'q: \xc2\xa3')
-
-        # An extra charset should be tried first, and continue to other default
-        # charsets if it doesn't match.
-        # Here, we add utf-16 as a charset but still pass a utf-8 body.
-        body = b'q=\xc2\xa3'
-        self.getPage('/decode/extra_charset', method='POST',
-                     headers=[(
-                         'Content-Type', 'application/x-www-form-urlencoded'),
-                         ('Content-Length', str(len(body))),
-                     ],
-                     body=body),
-        self.assertBody(b'q: \xc2\xa3')
-
-        # An extra charset should error if force is True and it doesn't match.
-        # Here, we force utf-16 as a charset but still pass a utf-8 body.
-        body = b'q=\xc2\xa3'
-        self.getPage('/decode/force_charset', method='POST',
-                     headers=[(
-                         'Content-Type', 'application/x-www-form-urlencoded'),
-                         ('Content-Length', str(len(body))),
-                     ],
-                     body=body),
-        self.assertErrorPage(
-            400,
-            'The request entity could not be decoded. The following charsets '
-            "were attempted: ['utf-16']")
-
-    def test_multipart_decoding(self):
-        # Test the decoding of a multipart entity when the charset (utf16) is
-        # explicitly given.
-        body = ntob('\r\n'.join([
-            '--X',
-            'Content-Type: text/plain;charset=utf-16',
-            'Content-Disposition: form-data; name="text"',
-            '',
-            '\xff\xfea\x00b\x00\x1c c\x00',
-            '--X',
-            'Content-Type: text/plain;charset=utf-16',
-            'Content-Disposition: form-data; name="submit"',
-            '',
-            '\xff\xfeC\x00r\x00e\x00a\x00t\x00e\x00',
-            '--X--'
-        ]))
-        self.getPage('/reqparams', method='POST',
-                     headers=[(
-                         'Content-Type', 'multipart/form-data;boundary=X'),
-                         ('Content-Length', str(len(body))),
-                     ],
-                     body=body),
-        self.assertBody(b'submit: Create, text: ab\xe2\x80\x9cc')
-
-    @mock.patch('cherrypy._cpreqbody.Part.maxrambytes', 1)
-    def test_multipart_decoding_bigger_maxrambytes(self):
-        """
-        Decoding of a multipart entity should also pass when
-        the entity is bigger than maxrambytes. See ticket #1352.
-        """
-        self.test_multipart_decoding()
-
-    def test_multipart_decoding_no_charset(self):
-        # Test the decoding of a multipart entity when the charset (utf8) is
-        # NOT explicitly given, but is in the list of charsets to attempt.
-        body = ntob('\r\n'.join([
-            '--X',
-            'Content-Disposition: form-data; name="text"',
-            '',
-            '\xe2\x80\x9c',
-            '--X',
-            'Content-Disposition: form-data; name="submit"',
-            '',
-            'Create',
-            '--X--'
-        ]))
-        self.getPage('/reqparams', method='POST',
-                     headers=[(
-                         'Content-Type', 'multipart/form-data;boundary=X'),
-                         ('Content-Length', str(len(body))),
-                     ],
-                     body=body),
-        self.assertBody(b'submit: Create, text: \xe2\x80\x9c')
-
-    def test_multipart_decoding_no_successful_charset(self):
-        # Test the decoding of a multipart entity when the charset (utf16) is
-        # NOT explicitly given, and is NOT in the list of charsets to attempt.
-        body = ntob('\r\n'.join([
-            '--X',
-            'Content-Disposition: form-data; name="text"',
-            '',
-            '\xff\xfea\x00b\x00\x1c c\x00',
-            '--X',
-            'Content-Disposition: form-data; name="submit"',
-            '',
-            '\xff\xfeC\x00r\x00e\x00a\x00t\x00e\x00',
-            '--X--'
-        ]))
-        self.getPage('/reqparams', method='POST',
-                     headers=[(
-                         'Content-Type', 'multipart/form-data;boundary=X'),
-                         ('Content-Length', str(len(body))),
-                     ],
-                     body=body),
-        self.assertStatus(400)
-        self.assertErrorPage(
-            400,
-            'The request entity could not be decoded. The following charsets '
-            "were attempted: ['us-ascii', 'utf-8']")
-
-    def test_nontext(self):
-        self.getPage('/nontext')
-        self.assertHeader('Content-Type', 'application/binary;charset=utf-8')
-        self.assertBody('\x00\x01\x02\x03')
-
-    def testEncoding(self):
-        # Default encoding should be utf-8
-        self.getPage('/mao_zedong')
-        self.assertBody(sing8)
-
-        # Ask for utf-16.
-        self.getPage('/mao_zedong', [('Accept-Charset', 'utf-16')])
-        self.assertHeader('Content-Type', 'text/html;charset=utf-16')
-        self.assertBody(sing16)
-
-        # Ask for multiple encodings. ISO-8859-1 should fail, and utf-16
-        # should be produced.
-        self.getPage('/mao_zedong', [('Accept-Charset',
-                                      'iso-8859-1;q=1, utf-16;q=0.5')])
-        self.assertBody(sing16)
-
-        # The "*" value should default to our default_encoding, utf-8
-        self.getPage('/mao_zedong', [('Accept-Charset', '*;q=1, utf-7;q=.2')])
-        self.assertBody(sing8)
-
-        # Only allow iso-8859-1, which should fail and raise 406.
-        self.getPage('/mao_zedong', [('Accept-Charset', 'iso-8859-1, *;q=0')])
-        self.assertStatus('406 Not Acceptable')
-        self.assertInBody('Your client sent this Accept-Charset header: '
-                          'iso-8859-1, *;q=0. We tried these charsets: '
-                          'iso-8859-1.')
-
-        # Ask for x-mac-ce, which should be unknown. See ticket #569.
-        self.getPage('/mao_zedong', [('Accept-Charset',
-                                      'us-ascii, ISO-8859-1, x-mac-ce')])
-        self.assertStatus('406 Not Acceptable')
-        self.assertInBody('Your client sent this Accept-Charset header: '
-                          'us-ascii, ISO-8859-1, x-mac-ce. We tried these '
-                          'charsets: ISO-8859-1, us-ascii, x-mac-ce.')
-
-        # Test the 'encoding' arg to encode.
-        self.getPage('/utf8')
-        self.assertBody(sing8)
-        self.getPage('/utf8', [('Accept-Charset', 'us-ascii, ISO-8859-1')])
-        self.assertStatus('406 Not Acceptable')
-
-        # Test malformed quality value, which should raise 400.
-        self.getPage('/mao_zedong', [('Accept-Charset',
-                                      'ISO-8859-1,utf-8;q=0.7,*;q=0.7)')])
-        self.assertStatus('400 Bad Request')
-
-    def testGzip(self):
-        zbuf = io.BytesIO()
-        zfile = gzip.GzipFile(mode='wb', fileobj=zbuf, compresslevel=9)
-        zfile.write(b'Hello, world')
-        zfile.close()
-
-        self.getPage('/gzip/', headers=[('Accept-Encoding', 'gzip')])
-        self.assertInBody(zbuf.getvalue()[:3])
-        self.assertHeader('Vary', 'Accept-Encoding')
-        self.assertHeader('Content-Encoding', 'gzip')
-
-        # Test when gzip is denied.
-        self.getPage('/gzip/', headers=[('Accept-Encoding', 'identity')])
-        self.assertHeader('Vary', 'Accept-Encoding')
-        self.assertNoHeader('Content-Encoding')
-        self.assertBody('Hello, world')
-
-        self.getPage('/gzip/', headers=[('Accept-Encoding', 'gzip;q=0')])
-        self.assertHeader('Vary', 'Accept-Encoding')
-        self.assertNoHeader('Content-Encoding')
-        self.assertBody('Hello, world')
-
-        # Test that trailing comma doesn't cause IndexError
-        # Ref: https://github.com/cherrypy/cherrypy/issues/988
-        self.getPage('/gzip/', headers=[('Accept-Encoding', 'gzip,deflate,')])
-        self.assertStatus(200)
-        self.assertNotInBody('IndexError')
-
-        self.getPage('/gzip/', headers=[('Accept-Encoding', '*;q=0')])
-        self.assertStatus(406)
-        self.assertNoHeader('Content-Encoding')
-        self.assertErrorPage(406, 'identity, gzip')
-
-        # Test for ticket #147
-        self.getPage('/gzip/noshow', headers=[('Accept-Encoding', 'gzip')])
-        self.assertNoHeader('Content-Encoding')
-        self.assertStatus(500)
-        self.assertErrorPage(500, pattern='IndexError\n')
-
-        # In this case, there's nothing we can do to deliver a
-        # readable page, since 1) the gzip header is already set,
-        # and 2) we may have already written some of the body.
-        # The fix is to never stream yields when using gzip.
-        if (cherrypy.server.protocol_version == 'HTTP/1.0' or
-                getattr(cherrypy.server, 'using_apache', False)):
-            self.getPage('/gzip/noshow_stream',
-                         headers=[('Accept-Encoding', 'gzip')])
-            self.assertHeader('Content-Encoding', 'gzip')
-            self.assertInBody('\x1f\x8b\x08\x00')
-        else:
-            # The wsgiserver will simply stop sending data, and the HTTP client
-            # will error due to an incomplete chunk-encoded stream.
-            self.assertRaises((ValueError, IncompleteRead), self.getPage,
-                              '/gzip/noshow_stream',
-                              headers=[('Accept-Encoding', 'gzip')])
-
-    def test_UnicodeHeaders(self):
-        self.getPage('/cookies_and_headers')
-        self.assertBody('Any content')
diff --git a/libraries/cherrypy/test/test_etags.py b/libraries/cherrypy/test/test_etags.py
deleted file mode 100644
index 293eb866..00000000
--- a/libraries/cherrypy/test/test_etags.py
+++ /dev/null
@@ -1,84 +0,0 @@
-import cherrypy
-from cherrypy._cpcompat import ntou
-from cherrypy.test import helper
-
-
-class ETagTest(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-        class Root:
-
-            @cherrypy.expose
-            def resource(self):
-                return 'Oh wah ta goo Siam.'
-
-            @cherrypy.expose
-            def fail(self, code):
-                code = int(code)
-                if 300 <= code <= 399:
-                    raise cherrypy.HTTPRedirect([], code)
-                else:
-                    raise cherrypy.HTTPError(code)
-
-            @cherrypy.expose
-            # In Python 3, tools.encode is on by default
-            @cherrypy.config(**{'tools.encode.on': True})
-            def unicoded(self):
-                return ntou('I am a \u1ee4nicode string.', 'escape')
-
-        conf = {'/': {'tools.etags.on': True,
-                      'tools.etags.autotags': True,
-                      }}
-        cherrypy.tree.mount(Root(), config=conf)
-
-    def test_etags(self):
-        self.getPage('/resource')
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html;charset=utf-8')
-        self.assertBody('Oh wah ta goo Siam.')
-        etag = self.assertHeader('ETag')
-
-        # Test If-Match (both valid and invalid)
-        self.getPage('/resource', headers=[('If-Match', etag)])
-        self.assertStatus('200 OK')
-        self.getPage('/resource', headers=[('If-Match', '*')])
-        self.assertStatus('200 OK')
-        self.getPage('/resource', headers=[('If-Match', '*')], method='POST')
-        self.assertStatus('200 OK')
-        self.getPage('/resource', headers=[('If-Match', 'a bogus tag')])
-        self.assertStatus('412 Precondition Failed')
-
-        # Test If-None-Match (both valid and invalid)
-        self.getPage('/resource', headers=[('If-None-Match', etag)])
-        self.assertStatus(304)
-        self.getPage('/resource', method='POST',
-                     headers=[('If-None-Match', etag)])
-        self.assertStatus('412 Precondition Failed')
-        self.getPage('/resource', headers=[('If-None-Match', '*')])
-        self.assertStatus(304)
-        self.getPage('/resource', headers=[('If-None-Match', 'a bogus tag')])
-        self.assertStatus('200 OK')
-
-    def test_errors(self):
-        self.getPage('/resource')
-        self.assertStatus(200)
-        etag = self.assertHeader('ETag')
-
-        # Test raising errors in page handler
-        self.getPage('/fail/412', headers=[('If-Match', etag)])
-        self.assertStatus(412)
-        self.getPage('/fail/304', headers=[('If-Match', etag)])
-        self.assertStatus(304)
-        self.getPage('/fail/412', headers=[('If-None-Match', '*')])
-        self.assertStatus(412)
-        self.getPage('/fail/304', headers=[('If-None-Match', '*')])
-        self.assertStatus(304)
-
-    def test_unicode_body(self):
-        self.getPage('/unicoded')
-        self.assertStatus(200)
-        etag1 = self.assertHeader('ETag')
-        self.getPage('/unicoded', headers=[('If-Match', etag1)])
-        self.assertStatus(200)
-        self.assertHeader('ETag', etag1)
diff --git a/libraries/cherrypy/test/test_http.py b/libraries/cherrypy/test/test_http.py
deleted file mode 100644
index 0899d4d0..00000000
--- a/libraries/cherrypy/test/test_http.py
+++ /dev/null
@@ -1,307 +0,0 @@
-# coding: utf-8
-"""Tests for managing HTTP issues (malformed requests, etc)."""
-
-import errno
-import mimetypes
-import socket
-import sys
-from unittest import mock
-
-import six
-from six.moves.http_client import HTTPConnection
-from six.moves import urllib
-
-import cherrypy
-from cherrypy._cpcompat import HTTPSConnection, quote
-
-from cherrypy.test import helper
-
-
-def is_ascii(text):
-    """
-    Return True if the text encodes as ascii.
-    """
-    try:
-        text.encode('ascii')
-        return True
-    except Exception:
-        pass
-    return False
-
-
-def encode_filename(filename):
-    """
-    Given a filename to be used in a multipart/form-data,
-    encode the name. Return the key and encoded filename.
-    """
-    if is_ascii(filename):
-        return 'filename', '"{filename}"'.format(**locals())
-    encoded = quote(filename, encoding='utf-8')
-    return 'filename*', "'".join((
-        'UTF-8',
-        '',  # lang
-        encoded,
-    ))
-
-
-def encode_multipart_formdata(files):
-    """Return (content_type, body) ready for httplib.HTTP instance.
-
-    files: a sequence of (name, filename, value) tuples for multipart uploads.
-    filename can be a string or a tuple ('filename string', 'encoding')
-    """
-    BOUNDARY = '________ThIs_Is_tHe_bouNdaRY_$'
-    L = []
-    for key, filename, value in files:
-        L.append('--' + BOUNDARY)
-
-        fn_key, encoded = encode_filename(filename)
-        tmpl = \
-            'Content-Disposition: form-data; name="{key}"; {fn_key}={encoded}'
-        L.append(tmpl.format(**locals()))
-        ct = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
-        L.append('Content-Type: %s' % ct)
-        L.append('')
-        L.append(value)
-    L.append('--' + BOUNDARY + '--')
-    L.append('')
-    body = '\r\n'.join(L)
-    content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
-    return content_type, body
-
-
-class HTTPTests(helper.CPWebCase):
-
-    def make_connection(self):
-        if self.scheme == 'https':
-            return HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
-        else:
-            return HTTPConnection('%s:%s' % (self.interface(), self.PORT))
-
-    @staticmethod
-    def setup_server():
-        class Root:
-
-            @cherrypy.expose
-            def index(self, *args, **kwargs):
-                return 'Hello world!'
-
-            @cherrypy.expose
-            @cherrypy.config(**{'request.process_request_body': False})
-            def no_body(self, *args, **kwargs):
-                return 'Hello world!'
-
-            @cherrypy.expose
-            def post_multipart(self, file):
-                """Return a summary ("a * 65536\nb * 65536") of the uploaded
-                file.
-                """
-                contents = file.file.read()
-                summary = []
-                curchar = None
-                count = 0
-                for c in contents:
-                    if c == curchar:
-                        count += 1
-                    else:
-                        if count:
-                            if six.PY3:
-                                curchar = chr(curchar)
-                            summary.append('%s * %d' % (curchar, count))
-                        count = 1
-                        curchar = c
-                if count:
-                    if six.PY3:
-                        curchar = chr(curchar)
-                    summary.append('%s * %d' % (curchar, count))
-                return ', '.join(summary)
-
-            @cherrypy.expose
-            def post_filename(self, myfile):
-                '''Return the name of the file which was uploaded.'''
-                return myfile.filename
-
-        cherrypy.tree.mount(Root())
-        cherrypy.config.update({'server.max_request_body_size': 30000000})
-
-    def test_no_content_length(self):
-        # "The presence of a message-body in a request is signaled by the
-        # inclusion of a Content-Length or Transfer-Encoding header field in
-        # the request's message-headers."
-        #
-        # Send a message with neither header and no body. Even though
-        # the request is of method POST, this should be OK because we set
-        # request.process_request_body to False for our handler.
-        c = self.make_connection()
-        c.request('POST', '/no_body')
-        response = c.getresponse()
-        self.body = response.fp.read()
-        self.status = str(response.status)
-        self.assertStatus(200)
-        self.assertBody(b'Hello world!')
-
-        # Now send a message that has no Content-Length, but does send a body.
-        # Verify that CP times out the socket and responds
-        # with 411 Length Required.
-        if self.scheme == 'https':
-            c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
-        else:
-            c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
-
-        # `_get_content_length` is needed for Python 3.6+
-        with mock.patch.object(
-                c,
-                '_get_content_length',
-                lambda body, method: None,
-                create=True):
-            # `_set_content_length` is needed for Python 2.7-3.5
-            with mock.patch.object(c, '_set_content_length', create=True):
-                c.request('POST', '/')
-
-        response = c.getresponse()
-        self.body = response.fp.read()
-        self.status = str(response.status)
-        self.assertStatus(411)
-
-    def test_post_multipart(self):
-        alphabet = 'abcdefghijklmnopqrstuvwxyz'
-        # generate file contents for a large post
-        contents = ''.join([c * 65536 for c in alphabet])
-
-        # encode as multipart form data
-        files = [('file', 'file.txt', contents)]
-        content_type, body = encode_multipart_formdata(files)
-        body = body.encode('Latin-1')
-
-        # post file
-        c = self.make_connection()
-        c.putrequest('POST', '/post_multipart')
-        c.putheader('Content-Type', content_type)
-        c.putheader('Content-Length', str(len(body)))
-        c.endheaders()
-        c.send(body)
-
-        response = c.getresponse()
-        self.body = response.fp.read()
-        self.status = str(response.status)
-        self.assertStatus(200)
-        parts = ['%s * 65536' % ch for ch in alphabet]
-        self.assertBody(', '.join(parts))
-
-    def test_post_filename_with_special_characters(self):
-        '''Testing that we can handle filenames with special characters. This
-        was reported as a bug in:
-           https://github.com/cherrypy/cherrypy/issues/1146/
-           https://github.com/cherrypy/cherrypy/issues/1397/
-           https://github.com/cherrypy/cherrypy/issues/1694/
-        '''
-        # We'll upload a bunch of files with differing names.
-        fnames = [
-            'boop.csv', 'foo, bar.csv', 'bar, xxxx.csv', 'file"name.csv',
-            'file;name.csv', 'file; name.csv', u'test_łóąä.txt',
-        ]
-        for fname in fnames:
-            files = [('myfile', fname, 'yunyeenyunyue')]
-            content_type, body = encode_multipart_formdata(files)
-            body = body.encode('Latin-1')
-
-            # post file
-            c = self.make_connection()
-            c.putrequest('POST', '/post_filename')
-            c.putheader('Content-Type', content_type)
-            c.putheader('Content-Length', str(len(body)))
-            c.endheaders()
-            c.send(body)
-
-            response = c.getresponse()
-            self.body = response.fp.read()
-            self.status = str(response.status)
-            self.assertStatus(200)
-            self.assertBody(fname)
-
-    def test_malformed_request_line(self):
-        if getattr(cherrypy.server, 'using_apache', False):
-            return self.skip('skipped due to known Apache differences...')
-
-        # Test missing version in Request-Line
-        c = self.make_connection()
-        c._output(b'geT /')
-        c._send_output()
-        if hasattr(c, 'strict'):
-            response = c.response_class(c.sock, strict=c.strict, method='GET')
-        else:
-            # Python 3.2 removed the 'strict' feature, saying:
-            # "http.client now always assumes HTTP/1.x compliant servers."
-            response = c.response_class(c.sock, method='GET')
-        response.begin()
-        self.assertEqual(response.status, 400)
-        self.assertEqual(response.fp.read(22), b'Malformed Request-Line')
-        c.close()
-
-    def test_request_line_split_issue_1220(self):
-        params = {
-            'intervenant-entreprise-evenement_classaction':
-                'evenement-mailremerciements',
-            '_path': 'intervenant-entreprise-evenement',
-            'intervenant-entreprise-evenement_action-id': 19404,
-            'intervenant-entreprise-evenement_id': 19404,
-            'intervenant-entreprise_id': 28092,
-        }
-        Request_URI = '/index?' + urllib.parse.urlencode(params)
-        self.assertEqual(len('GET %s HTTP/1.1\r\n' % Request_URI), 256)
-        self.getPage(Request_URI)
-        self.assertBody('Hello world!')
-
-    def test_malformed_header(self):
-        c = self.make_connection()
-        c.putrequest('GET', '/')
-        c.putheader('Content-Type', 'text/plain')
-        # See https://github.com/cherrypy/cherrypy/issues/941
-        c._output(b're, 1.2.3.4#015#012')
-        c.endheaders()
-
-        response = c.getresponse()
-        self.status = str(response.status)
-        self.assertStatus(400)
-        self.body = response.fp.read(20)
-        self.assertBody('Illegal header line.')
-
-    def test_http_over_https(self):
-        if self.scheme != 'https':
-            return self.skip('skipped (not running HTTPS)... ')
-
-        # Try connecting without SSL.
-        conn = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
-        conn.putrequest('GET', '/', skip_host=True)
-        conn.putheader('Host', self.HOST)
-        conn.endheaders()
-        response = conn.response_class(conn.sock, method='GET')
-        try:
-            response.begin()
-            self.assertEqual(response.status, 400)
-            self.body = response.read()
-            self.assertBody('The client sent a plain HTTP request, but this '
-                            'server only speaks HTTPS on this port.')
-        except socket.error:
-            e = sys.exc_info()[1]
-            # "Connection reset by peer" is also acceptable.
-            if e.errno != errno.ECONNRESET:
-                raise
-
-    def test_garbage_in(self):
-        # Connect without SSL regardless of server.scheme
-        c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
-        c._output(b'gjkgjklsgjklsgjkljklsg')
-        c._send_output()
-        response = c.response_class(c.sock, method='GET')
-        try:
-            response.begin()
-            self.assertEqual(response.status, 400)
-            self.assertEqual(response.fp.read(22),
-                             b'Malformed Request-Line')
-            c.close()
-        except socket.error:
-            e = sys.exc_info()[1]
-            # "Connection reset by peer" is also acceptable.
-            if e.errno != errno.ECONNRESET:
-                raise
diff --git a/libraries/cherrypy/test/test_httputil.py b/libraries/cherrypy/test/test_httputil.py
deleted file mode 100644
index 656b8a3d..00000000
--- a/libraries/cherrypy/test/test_httputil.py
+++ /dev/null
@@ -1,80 +0,0 @@
-"""Test helpers from ``cherrypy.lib.httputil`` module."""
-import pytest
-from six.moves import http_client
-
-from cherrypy.lib import httputil
-
-
-@pytest.mark.parametrize(
-    'script_name,path_info,expected_url',
-    [
-        ('/sn/', '/pi/', '/sn/pi/'),
-        ('/sn/', '/pi', '/sn/pi'),
-        ('/sn/', '/', '/sn/'),
-        ('/sn/', '', '/sn/'),
-        ('/sn', '/pi/', '/sn/pi/'),
-        ('/sn', '/pi', '/sn/pi'),
-        ('/sn', '/', '/sn/'),
-        ('/sn', '', '/sn'),
-        ('/', '/pi/', '/pi/'),
-        ('/', '/pi', '/pi'),
-        ('/', '/', '/'),
-        ('/', '', '/'),
-        ('', '/pi/', '/pi/'),
-        ('', '/pi', '/pi'),
-        ('', '/', '/'),
-        ('', '', '/'),
-    ]
-)
-def test_urljoin(script_name, path_info, expected_url):
-    """Test all slash+atom combinations for SCRIPT_NAME and PATH_INFO."""
-    actual_url = httputil.urljoin(script_name, path_info)
-    assert actual_url == expected_url
-
-
-EXPECTED_200 = (200, 'OK', 'Request fulfilled, document follows')
-EXPECTED_500 = (
-    500,
-    'Internal Server Error',
-    'The server encountered an unexpected condition which '
-    'prevented it from fulfilling the request.',
-)
-EXPECTED_404 = (404, 'Not Found', 'Nothing matches the given URI')
-EXPECTED_444 = (444, 'Non-existent reason', '')
-
-
-@pytest.mark.parametrize(
-    'status,expected_status',
-    [
-        (None, EXPECTED_200),
-        (200, EXPECTED_200),
-        ('500', EXPECTED_500),
-        (http_client.NOT_FOUND, EXPECTED_404),
-        ('444 Non-existent reason', EXPECTED_444),
-    ]
-)
-def test_valid_status(status, expected_status):
-    """Check valid int, string and http_client-constants
-    statuses processing."""
-    assert httputil.valid_status(status) == expected_status
-
-
-@pytest.mark.parametrize(
-    'status_code,error_msg',
-    [
-        ('hey', "Illegal response status from server ('hey' is non-numeric)."),
-        (
-            {'hey': 'hi'},
-            'Illegal response status from server '
-            "({'hey': 'hi'} is non-numeric).",
-        ),
-        (1, 'Illegal response status from server (1 is out of range).'),
-        (600, 'Illegal response status from server (600 is out of range).'),
-    ]
-)
-def test_invalid_status(status_code, error_msg):
-    """Check that invalid status cause certain errors."""
-    with pytest.raises(ValueError) as excinfo:
-        httputil.valid_status(status_code)
-
-    assert error_msg in str(excinfo)
diff --git a/libraries/cherrypy/test/test_iterator.py b/libraries/cherrypy/test/test_iterator.py
deleted file mode 100644
index 92f08e7c..00000000
--- a/libraries/cherrypy/test/test_iterator.py
+++ /dev/null
@@ -1,196 +0,0 @@
-import six
-
-import cherrypy
-from cherrypy.test import helper
-
-
-class IteratorBase(object):
-
-    created = 0
-    datachunk = 'butternut squash' * 256
-
-    @classmethod
-    def incr(cls):
-        cls.created += 1
-
-    @classmethod
-    def decr(cls):
-        cls.created -= 1
-
-
-class OurGenerator(IteratorBase):
-
-    def __iter__(self):
-        self.incr()
-        try:
-            for i in range(1024):
-                yield self.datachunk
-        finally:
-            self.decr()
-
-
-class OurIterator(IteratorBase):
-
-    started = False
-    closed_off = False
-    count = 0
-
-    def increment(self):
-        self.incr()
-
-    def decrement(self):
-        if not self.closed_off:
-            self.closed_off = True
-            self.decr()
-
-    def __iter__(self):
-        return self
-
-    def __next__(self):
-        if not self.started:
-            self.started = True
-            self.increment()
-        self.count += 1
-        if self.count > 1024:
-            raise StopIteration
-        return self.datachunk
-
-    next = __next__
-
-    def __del__(self):
-        self.decrement()
-
-
-class OurClosableIterator(OurIterator):
-
-    def close(self):
-        self.decrement()
-
-
-class OurNotClosableIterator(OurIterator):
-
-    # We can't close something which requires an additional argument.
-    def close(self, somearg):
-        self.decrement()
-
-
-class OurUnclosableIterator(OurIterator):
-    close = 'close'  # not callable!
-
-
-class IteratorTest(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-
-        class Root(object):
-
-            @cherrypy.expose
-            def count(self, clsname):
-                cherrypy.response.headers['Content-Type'] = 'text/plain'
-                return six.text_type(globals()[clsname].created)
-
-            @cherrypy.expose
-            def getall(self, clsname):
-                cherrypy.response.headers['Content-Type'] = 'text/plain'
-                return globals()[clsname]()
-
-            @cherrypy.expose
-            @cherrypy.config(**{'response.stream': True})
-            def stream(self, clsname):
-                return self.getall(clsname)
-
-        cherrypy.tree.mount(Root())
-
-    def test_iterator(self):
-        try:
-            self._test_iterator()
-        except Exception:
-            'Test fails intermittently. See #1419'
-
-    def _test_iterator(self):
-        if cherrypy.server.protocol_version != 'HTTP/1.1':
-            return self.skip()
-
-        self.PROTOCOL = 'HTTP/1.1'
-
-        # Check the counts of all the classes, they should be zero.
-        closables = ['OurClosableIterator', 'OurGenerator']
-        unclosables = ['OurUnclosableIterator', 'OurNotClosableIterator']
-        all_classes = closables + unclosables
-
-        import random
-        random.shuffle(all_classes)
-
-        for clsname in all_classes:
-            self.getPage('/count/' + clsname)
-            self.assertStatus(200)
-            self.assertBody('0')
-
-        # We should also be able to read the entire content body
-        # successfully, though we don't need to, we just want to
-        # check the header.
-        for clsname in all_classes:
-            itr_conn = self.get_conn()
-            itr_conn.putrequest('GET', '/getall/' + clsname)
-            itr_conn.endheaders()
-            response = itr_conn.getresponse()
-            self.assertEqual(response.status, 200)
-            headers = response.getheaders()
-            for header_name, header_value in headers:
-                if header_name.lower() == 'content-length':
-                    expected = six.text_type(1024 * 16 * 256)
-                    assert header_value == expected, header_value
-                    break
-            else:
-                raise AssertionError('No Content-Length header found')
-
-            # As the response should be fully consumed by CherryPy
-            # before sending back, the count should still be at zero
-            # by the time the response has been sent.
-            self.getPage('/count/' + clsname)
-            self.assertStatus(200)
-            self.assertBody('0')
-
-        # Now we do the same check with streaming - some classes will
-        # be automatically closed, while others cannot.
-        stream_counts = {}
-        for clsname in all_classes:
-            itr_conn = self.get_conn()
-            itr_conn.putrequest('GET', '/stream/' + clsname)
-            itr_conn.endheaders()
-            response = itr_conn.getresponse()
-            self.assertEqual(response.status, 200)
-            response.fp.read(65536)
-
-            # Let's check the count - this should always be one.
-            self.getPage('/count/' + clsname)
-            self.assertBody('1')
-
-            # Now if we close the connection, the count should go back
-            # to zero.
-            itr_conn.close()
-            self.getPage('/count/' + clsname)
-
-            # If this is a response which should be easily closed, then
-            # we will test to see if the value has gone back down to
-            # zero.
-            if clsname in closables:
-
-                # Sometimes we try to get the answer too quickly - we
-                # will wait for 100 ms before asking again if we didn't
-                # get the answer we wanted.
-                if self.body != '0':
-                    import time
-                    time.sleep(0.1)
-                    self.getPage('/count/' + clsname)
-
-            stream_counts[clsname] = int(self.body)
-
-        # Check that we closed off the classes which should provide
-        # easy mechanisms for doing so.
-        for clsname in closables:
-            assert stream_counts[clsname] == 0, (
-                'did not close off stream response correctly, expected '
-                'count of zero for %s: %s' % (clsname, stream_counts)
-            )
diff --git a/libraries/cherrypy/test/test_json.py b/libraries/cherrypy/test/test_json.py
deleted file mode 100644
index 1585f6e6..00000000
--- a/libraries/cherrypy/test/test_json.py
+++ /dev/null
@@ -1,102 +0,0 @@
-import cherrypy
-from cherrypy.test import helper
-
-from cherrypy._cpcompat import json
-
-
-json_out = cherrypy.config(**{'tools.json_out.on': True})
-json_in = cherrypy.config(**{'tools.json_in.on': True})
-
-
-class JsonTest(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-        class Root(object):
-
-            @cherrypy.expose
-            def plain(self):
-                return 'hello'
-
-            @cherrypy.expose
-            @json_out
-            def json_string(self):
-                return 'hello'
-
-            @cherrypy.expose
-            @json_out
-            def json_list(self):
-                return ['a', 'b', 42]
-
-            @cherrypy.expose
-            @json_out
-            def json_dict(self):
-                return {'answer': 42}
-
-            @cherrypy.expose
-            @json_in
-            def json_post(self):
-                if cherrypy.request.json == [13, 'c']:
-                    return 'ok'
-                else:
-                    return 'nok'
-
-            @cherrypy.expose
-            @json_out
-            @cherrypy.config(**{'tools.caching.on': True})
-            def json_cached(self):
-                return 'hello there'
-
-        root = Root()
-        cherrypy.tree.mount(root)
-
-    def test_json_output(self):
-        if json is None:
-            self.skip('json not found ')
-            return
-
-        self.getPage('/plain')
-        self.assertBody('hello')
-
-        self.getPage('/json_string')
-        self.assertBody('"hello"')
-
-        self.getPage('/json_list')
-        self.assertBody('["a", "b", 42]')
-
-        self.getPage('/json_dict')
-        self.assertBody('{"answer": 42}')
-
-    def test_json_input(self):
-        if json is None:
-            self.skip('json not found ')
-            return
-
-        body = '[13, "c"]'
-        headers = [('Content-Type', 'application/json'),
-                   ('Content-Length', str(len(body)))]
-        self.getPage('/json_post', method='POST', headers=headers, body=body)
-        self.assertBody('ok')
-
-        body = '[13, "c"]'
-        headers = [('Content-Type', 'text/plain'),
-                   ('Content-Length', str(len(body)))]
-        self.getPage('/json_post', method='POST', headers=headers, body=body)
-        self.assertStatus(415, 'Expected an application/json content type')
-
-        body = '[13, -]'
-        headers = [('Content-Type', 'application/json'),
-                   ('Content-Length', str(len(body)))]
-        self.getPage('/json_post', method='POST', headers=headers, body=body)
-        self.assertStatus(400, 'Invalid JSON document')
-
-    def test_cached(self):
-        if json is None:
-            self.skip('json not found ')
-            return
-
-        self.getPage('/json_cached')
-        self.assertStatus(200, '"hello"')
-
-        self.getPage('/json_cached')  # 2'nd time to hit cache
-        self.assertStatus(200, '"hello"')
diff --git a/libraries/cherrypy/test/test_logging.py b/libraries/cherrypy/test/test_logging.py
deleted file mode 100644
index c4948c20..00000000
--- a/libraries/cherrypy/test/test_logging.py
+++ /dev/null
@@ -1,209 +0,0 @@
-"""Basic tests for the CherryPy core: request handling."""
-
-import os
-from unittest import mock
-
-import six
-
-import cherrypy
-from cherrypy._cpcompat import ntou
-from cherrypy.test import helper, logtest
-
-localDir = os.path.dirname(__file__)
-access_log = os.path.join(localDir, 'access.log')
-error_log = os.path.join(localDir, 'error.log')
-
-# Some unicode strings.
-tartaros = ntou('\u03a4\u1f71\u03c1\u03c4\u03b1\u03c1\u03bf\u03c2', 'escape')
-erebos = ntou('\u0388\u03c1\u03b5\u03b2\u03bf\u03c2.com', 'escape')
-
-
-def setup_server():
-    class Root:
-
-        @cherrypy.expose
-        def index(self):
-            return 'hello'
-
-        @cherrypy.expose
-        def uni_code(self):
-            cherrypy.request.login = tartaros
-            cherrypy.request.remote.name = erebos
-
-        @cherrypy.expose
-        def slashes(self):
-            cherrypy.request.request_line = r'GET /slashed\path HTTP/1.1'
-
-        @cherrypy.expose
-        def whitespace(self):
-            # User-Agent = "User-Agent" ":" 1*( product | comment )
-            # comment    = "(" *( ctext | quoted-pair | comment ) ")"
-            # ctext      = <any TEXT excluding "(" and ")">
-            # TEXT       = <any OCTET except CTLs, but including LWS>
-            # LWS        = [CRLF] 1*( SP | HT )
-            cherrypy.request.headers['User-Agent'] = 'Browzuh (1.0\r\n\t\t.3)'
-
-        @cherrypy.expose
-        def as_string(self):
-            return 'content'
-
-        @cherrypy.expose
-        def as_yield(self):
-            yield 'content'
-
-        @cherrypy.expose
-        @cherrypy.config(**{'tools.log_tracebacks.on': True})
-        def error(self):
-            raise ValueError()
-
-    root = Root()
-
-    cherrypy.config.update({
-        'log.error_file': error_log,
-        'log.access_file': access_log,
-    })
-    cherrypy.tree.mount(root)
-
-
-class AccessLogTests(helper.CPWebCase, logtest.LogCase):
-    setup_server = staticmethod(setup_server)
-
-    logfile = access_log
-
-    def testNormalReturn(self):
-        self.markLog()
-        self.getPage('/as_string',
-                     headers=[('Referer', 'http://www.cherrypy.org/'),
-                              ('User-Agent', 'Mozilla/5.0')])
-        self.assertBody('content')
-        self.assertStatus(200)
-
-        intro = '%s - - [' % self.interface()
-
-        self.assertLog(-1, intro)
-
-        if [k for k, v in self.headers if k.lower() == 'content-length']:
-            self.assertLog(-1, '] "GET %s/as_string HTTP/1.1" 200 7 '
-                           '"http://www.cherrypy.org/" "Mozilla/5.0"'
-                           % self.prefix())
-        else:
-            self.assertLog(-1, '] "GET %s/as_string HTTP/1.1" 200 - '
-                           '"http://www.cherrypy.org/" "Mozilla/5.0"'
-                           % self.prefix())
-
-    def testNormalYield(self):
-        self.markLog()
-        self.getPage('/as_yield')
-        self.assertBody('content')
-        self.assertStatus(200)
-
-        intro = '%s - - [' % self.interface()
-
-        self.assertLog(-1, intro)
-        if [k for k, v in self.headers if k.lower() == 'content-length']:
-            self.assertLog(-1, '] "GET %s/as_yield HTTP/1.1" 200 7 "" ""' %
-                           self.prefix())
-        else:
-            self.assertLog(-1, '] "GET %s/as_yield HTTP/1.1" 200 - "" ""'
-                           % self.prefix())
-
-    @mock.patch(
-        'cherrypy._cplogging.LogManager.access_log_format',
-        '{h} {l} {u} {t} "{r}" {s} {b} "{f}" "{a}" {o}'
-        if six.PY3 else
-        '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(o)s'
-    )
-    def testCustomLogFormat(self):
-        """Test a customized access_log_format string, which is a
-        feature of _cplogging.LogManager.access()."""
-        self.markLog()
-        self.getPage('/as_string', headers=[('Referer', 'REFERER'),
-                                            ('User-Agent', 'USERAGENT'),
-                                            ('Host', 'HOST')])
-        self.assertLog(-1, '%s - - [' % self.interface())
-        self.assertLog(-1, '] "GET /as_string HTTP/1.1" '
-                           '200 7 "REFERER" "USERAGENT" HOST')
-
-    @mock.patch(
-        'cherrypy._cplogging.LogManager.access_log_format',
-        '{h} {l} {u} {z} "{r}" {s} {b} "{f}" "{a}" {o}'
-        if six.PY3 else
-        '%(h)s %(l)s %(u)s %(z)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(o)s'
-    )
-    def testTimezLogFormat(self):
-        """Test a customized access_log_format string, which is a
-        feature of _cplogging.LogManager.access()."""
-        self.markLog()
-
-        expected_time = str(cherrypy._cplogging.LazyRfc3339UtcTime())
-        with mock.patch(
-                'cherrypy._cplogging.LazyRfc3339UtcTime',
-                lambda: expected_time):
-            self.getPage('/as_string', headers=[('Referer', 'REFERER'),
-                                                ('User-Agent', 'USERAGENT'),
-                                                ('Host', 'HOST')])
-
-        self.assertLog(-1, '%s - - ' % self.interface())
-        self.assertLog(-1, expected_time)
-        self.assertLog(-1, ' "GET /as_string HTTP/1.1" '
-                           '200 7 "REFERER" "USERAGENT" HOST')
-
-    @mock.patch(
-        'cherrypy._cplogging.LogManager.access_log_format',
-        '{i}' if six.PY3 else '%(i)s'
-    )
-    def testUUIDv4ParameterLogFormat(self):
-        """Test rendering of UUID4 within access log."""
-        self.markLog()
-        self.getPage('/as_string')
-        self.assertValidUUIDv4()
-
-    def testEscapedOutput(self):
-        # Test unicode in access log pieces.
-        self.markLog()
-        self.getPage('/uni_code')
-        self.assertStatus(200)
-        if six.PY3:
-            # The repr of a bytestring in six.PY3 includes a b'' prefix
-            self.assertLog(-1, repr(tartaros.encode('utf8'))[2:-1])
-        else:
-            self.assertLog(-1, repr(tartaros.encode('utf8'))[1:-1])
-        # Test the erebos value. Included inline for your enlightenment.
-        # Note the 'r' prefix--those backslashes are literals.
-        self.assertLog(-1, r'\xce\x88\xcf\x81\xce\xb5\xce\xb2\xce\xbf\xcf\x82')
-
-        # Test backslashes in output.
-        self.markLog()
-        self.getPage('/slashes')
-        self.assertStatus(200)
-        if six.PY3:
-            self.assertLog(-1, b'"GET /slashed\\path HTTP/1.1"')
-        else:
-            self.assertLog(-1, r'"GET /slashed\\path HTTP/1.1"')
-
-        # Test whitespace in output.
-        self.markLog()
-        self.getPage('/whitespace')
-        self.assertStatus(200)
-        # Again, note the 'r' prefix.
-        self.assertLog(-1, r'"Browzuh (1.0\r\n\t\t.3)"')
-
-
-class ErrorLogTests(helper.CPWebCase, logtest.LogCase):
-    setup_server = staticmethod(setup_server)
-
-    logfile = error_log
-
-    def testTracebacks(self):
-        # Test that tracebacks get written to the error log.
-        self.markLog()
-        ignore = helper.webtest.ignored_exceptions
-        ignore.append(ValueError)
-        try:
-            self.getPage('/error')
-            self.assertInBody('raise ValueError()')
-            self.assertLog(0, 'HTTP')
-            self.assertLog(1, 'Traceback (most recent call last):')
-            self.assertLog(-2, 'raise ValueError()')
-        finally:
-            ignore.pop()
diff --git a/libraries/cherrypy/test/test_mime.py b/libraries/cherrypy/test/test_mime.py
deleted file mode 100644
index ef35d10e..00000000
--- a/libraries/cherrypy/test/test_mime.py
+++ /dev/null
@@ -1,134 +0,0 @@
-"""Tests for various MIME issues, including the safe_multipart Tool."""
-
-import cherrypy
-from cherrypy._cpcompat import ntou
-from cherrypy.test import helper
-
-
-def setup_server():
-
-    class Root:
-
-        @cherrypy.expose
-        def multipart(self, parts):
-            return repr(parts)
-
-        @cherrypy.expose
-        def multipart_form_data(self, **kwargs):
-            return repr(list(sorted(kwargs.items())))
-
-        @cherrypy.expose
-        def flashupload(self, Filedata, Upload, Filename):
-            return ('Upload: %s, Filename: %s, Filedata: %r' %
-                    (Upload, Filename, Filedata.file.read()))
-
-    cherrypy.config.update({'server.max_request_body_size': 0})
-    cherrypy.tree.mount(Root())
-
-
-#                             Client-side code                             #
-
-
-class MultipartTest(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def test_multipart(self):
-        text_part = ntou('This is the text version')
-        html_part = ntou(
-            """<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-<head>
- <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type">
-</head>
-<body bgcolor="#ffffff" text="#000000">
-
-This is the <strong>HTML</strong> version
-</body>
-</html>
-""")
-        body = '\r\n'.join([
-            '--123456789',
-            "Content-Type: text/plain; charset='ISO-8859-1'",
-            'Content-Transfer-Encoding: 7bit',
-            '',
-            text_part,
-            '--123456789',
-            "Content-Type: text/html; charset='ISO-8859-1'",
-            '',
-            html_part,
-            '--123456789--'])
-        headers = [
-            ('Content-Type', 'multipart/mixed; boundary=123456789'),
-            ('Content-Length', str(len(body))),
-        ]
-        self.getPage('/multipart', headers, 'POST', body)
-        self.assertBody(repr([text_part, html_part]))
-
-    def test_multipart_form_data(self):
-        body = '\r\n'.join([
-            '--X',
-            'Content-Disposition: form-data; name="foo"',
-            '',
-            'bar',
-            '--X',
-            # Test a param with more than one value.
-            # See
-            # https://github.com/cherrypy/cherrypy/issues/1028
-            'Content-Disposition: form-data; name="baz"',
-            '',
-            '111',
-            '--X',
-            'Content-Disposition: form-data; name="baz"',
-            '',
-            '333',
-            '--X--'
-        ])
-        self.getPage('/multipart_form_data', method='POST',
-                     headers=[(
-                         'Content-Type', 'multipart/form-data;boundary=X'),
-                         ('Content-Length', str(len(body))),
-                     ],
-                     body=body),
-        self.assertBody(
-            repr([('baz', [ntou('111'), ntou('333')]), ('foo', ntou('bar'))]))
-
-
-class SafeMultipartHandlingTest(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def test_Flash_Upload(self):
-        headers = [
-            ('Accept', 'text/*'),
-            ('Content-Type', 'multipart/form-data; '
-             'boundary=----------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6'),
-            ('User-Agent', 'Shockwave Flash'),
-            ('Host', 'www.example.com:54583'),
-            ('Content-Length', '499'),
-            ('Connection', 'Keep-Alive'),
-            ('Cache-Control', 'no-cache'),
-        ]
-        filedata = (b'<?xml version="1.0" encoding="UTF-8"?>\r\n'
-                    b'<projectDescription>\r\n'
-                    b'</projectDescription>\r\n')
-        body = (
-            b'------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n'
-            b'Content-Disposition: form-data; name="Filename"\r\n'
-            b'\r\n'
-            b'.project\r\n'
-            b'------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n'
-            b'Content-Disposition: form-data; '
-            b'name="Filedata"; filename=".project"\r\n'
-            b'Content-Type: application/octet-stream\r\n'
-            b'\r\n' +
-            filedata +
-            b'\r\n'
-            b'------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n'
-            b'Content-Disposition: form-data; name="Upload"\r\n'
-            b'\r\n'
-            b'Submit Query\r\n'
-            # Flash apps omit the trailing \r\n on the last line:
-            b'------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6--'
-        )
-        self.getPage('/flashupload', headers, 'POST', body)
-        self.assertBody('Upload: Submit Query, Filename: .project, '
-                        'Filedata: %r' % filedata)
diff --git a/libraries/cherrypy/test/test_misc_tools.py b/libraries/cherrypy/test/test_misc_tools.py
deleted file mode 100644
index fb85b8f8..00000000
--- a/libraries/cherrypy/test/test_misc_tools.py
+++ /dev/null
@@ -1,210 +0,0 @@
-import os
-
-import cherrypy
-from cherrypy import tools
-from cherrypy.test import helper
-
-
-localDir = os.path.dirname(__file__)
-logfile = os.path.join(localDir, 'test_misc_tools.log')
-
-
-def setup_server():
-    class Root:
-
-        @cherrypy.expose
-        def index(self):
-            yield 'Hello, world'
-        h = [('Content-Language', 'en-GB'), ('Content-Type', 'text/plain')]
-        tools.response_headers(headers=h)(index)
-
-        @cherrypy.expose
-        @cherrypy.config(**{
-            'tools.response_headers.on': True,
-            'tools.response_headers.headers': [
-                ('Content-Language', 'fr'),
-                ('Content-Type', 'text/plain'),
-            ],
-            'tools.log_hooks.on': True,
-        })
-        def other(self):
-            return 'salut'
-
-    @cherrypy.config(**{'tools.accept.on': True})
-    class Accept:
-
-        @cherrypy.expose
-        def index(self):
-            return '<a href="feed">Atom feed</a>'
-
-        @cherrypy.expose
-        @tools.accept(media='application/atom+xml')
-        def feed(self):
-            return """<?xml version="1.0" encoding="utf-8"?>
-<feed xmlns="http://www.w3.org/2005/Atom">
-    <title>Unknown Blog</title>
-</feed>"""
-
-        @cherrypy.expose
-        def select(self):
-            # We could also write this: mtype = cherrypy.lib.accept.accept(...)
-            mtype = tools.accept.callable(['text/html', 'text/plain'])
-            if mtype == 'text/html':
-                return '<h2>Page Title</h2>'
-            else:
-                return 'PAGE TITLE'
-
-    class Referer:
-
-        @cherrypy.expose
-        def accept(self):
-            return 'Accepted!'
-        reject = accept
-
-    class AutoVary:
-
-        @cherrypy.expose
-        def index(self):
-            # Read a header directly with 'get'
-            cherrypy.request.headers.get('Accept-Encoding')
-            # Read a header directly with '__getitem__'
-            cherrypy.request.headers['Host']
-            # Read a header directly with '__contains__'
-            'If-Modified-Since' in cherrypy.request.headers
-            # Read a header directly
-            'Range' in cherrypy.request.headers
-            # Call a lib function
-            tools.accept.callable(['text/html', 'text/plain'])
-            return 'Hello, world!'
-
-    conf = {'/referer': {'tools.referer.on': True,
-                         'tools.referer.pattern': r'http://[^/]*example\.com',
-                         },
-            '/referer/reject': {'tools.referer.accept': False,
-                                'tools.referer.accept_missing': True,
-                                },
-            '/autovary': {'tools.autovary.on': True},
-            }
-
-    root = Root()
-    root.referer = Referer()
-    root.accept = Accept()
-    root.autovary = AutoVary()
-    cherrypy.tree.mount(root, config=conf)
-    cherrypy.config.update({'log.error_file': logfile})
-
-
-class ResponseHeadersTest(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def testResponseHeadersDecorator(self):
-        self.getPage('/')
-        self.assertHeader('Content-Language', 'en-GB')
-        self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
-
-    def testResponseHeaders(self):
-        self.getPage('/other')
-        self.assertHeader('Content-Language', 'fr')
-        self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
-
-
-class RefererTest(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def testReferer(self):
-        self.getPage('/referer/accept')
-        self.assertErrorPage(403, 'Forbidden Referer header.')
-
-        self.getPage('/referer/accept',
-                     headers=[('Referer', 'http://www.example.com/')])
-        self.assertStatus(200)
-        self.assertBody('Accepted!')
-
-        # Reject
-        self.getPage('/referer/reject')
-        self.assertStatus(200)
-        self.assertBody('Accepted!')
-
-        self.getPage('/referer/reject',
-                     headers=[('Referer', 'http://www.example.com/')])
-        self.assertErrorPage(403, 'Forbidden Referer header.')
-
-
-class AcceptTest(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def test_Accept_Tool(self):
-        # Test with no header provided
-        self.getPage('/accept/feed')
-        self.assertStatus(200)
-        self.assertInBody('<title>Unknown Blog</title>')
-
-        # Specify exact media type
-        self.getPage('/accept/feed',
-                     headers=[('Accept', 'application/atom+xml')])
-        self.assertStatus(200)
-        self.assertInBody('<title>Unknown Blog</title>')
-
-        # Specify matching media range
-        self.getPage('/accept/feed', headers=[('Accept', 'application/*')])
-        self.assertStatus(200)
-        self.assertInBody('<title>Unknown Blog</title>')
-
-        # Specify all media ranges
-        self.getPage('/accept/feed', headers=[('Accept', '*/*')])
-        self.assertStatus(200)
-        self.assertInBody('<title>Unknown Blog</title>')
-
-        # Specify unacceptable media types
-        self.getPage('/accept/feed', headers=[('Accept', 'text/html')])
-        self.assertErrorPage(406,
-                             'Your client sent this Accept header: text/html. '
-                             'But this resource only emits these media types: '
-                             'application/atom+xml.')
-
-        # Test resource where tool is 'on' but media is None (not set).
-        self.getPage('/accept/')
-        self.assertStatus(200)
-        self.assertBody('<a href="feed">Atom feed</a>')
-
-    def test_accept_selection(self):
-        # Try both our expected media types
-        self.getPage('/accept/select', [('Accept', 'text/html')])
-        self.assertStatus(200)
-        self.assertBody('<h2>Page Title</h2>')
-        self.getPage('/accept/select', [('Accept', 'text/plain')])
-        self.assertStatus(200)
-        self.assertBody('PAGE TITLE')
-        self.getPage('/accept/select',
-                     [('Accept', 'text/plain, text/*;q=0.5')])
-        self.assertStatus(200)
-        self.assertBody('PAGE TITLE')
-
-        # text/* and */* should prefer text/html since it comes first
-        # in our 'media' argument to tools.accept
-        self.getPage('/accept/select', [('Accept', 'text/*')])
-        self.assertStatus(200)
-        self.assertBody('<h2>Page Title</h2>')
-        self.getPage('/accept/select', [('Accept', '*/*')])
-        self.assertStatus(200)
-        self.assertBody('<h2>Page Title</h2>')
-
-        # Try unacceptable media types
-        self.getPage('/accept/select', [('Accept', 'application/xml')])
-        self.assertErrorPage(
-            406,
-            'Your client sent this Accept header: application/xml. '
-            'But this resource only emits these media types: '
-            'text/html, text/plain.')
-
-
-class AutoVaryTest(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def testAutoVary(self):
-        self.getPage('/autovary/')
-        self.assertHeader(
-            'Vary',
-            'Accept, Accept-Charset, Accept-Encoding, '
-            'Host, If-Modified-Since, Range'
-        )
diff --git a/libraries/cherrypy/test/test_native.py b/libraries/cherrypy/test/test_native.py
deleted file mode 100644
index caebc3f4..00000000
--- a/libraries/cherrypy/test/test_native.py
+++ /dev/null
@@ -1,35 +0,0 @@
-"""Test the native server."""
-
-import pytest
-from requests_toolbelt import sessions
-
-import cherrypy._cpnative_server
-
-
-pytestmark = pytest.mark.skipif(
-    'sys.platform == "win32"',
-    reason='tests fail on Windows',
-)
-
-
-@pytest.fixture
-def cp_native_server(request):
-    """A native server."""
-    class Root(object):
-        @cherrypy.expose
-        def index(self):
-            return 'Hello World!'
-
-    cls = cherrypy._cpnative_server.CPHTTPServer
-    cherrypy.server.httpserver = cls(cherrypy.server)
-
-    cherrypy.tree.mount(Root(), '/')
-    cherrypy.engine.start()
-    request.addfinalizer(cherrypy.engine.stop)
-    url = 'http://localhost:{cherrypy.server.socket_port}'.format(**globals())
-    return sessions.BaseUrlSession(url)
-
-
-def test_basic_request(cp_native_server):
-    """A request to a native server should succeed."""
-    cp_native_server.get('/')
diff --git a/libraries/cherrypy/test/test_objectmapping.py b/libraries/cherrypy/test/test_objectmapping.py
deleted file mode 100644
index 98402b8b..00000000
--- a/libraries/cherrypy/test/test_objectmapping.py
+++ /dev/null
@@ -1,430 +0,0 @@
-import sys
-import cherrypy
-from cherrypy._cpcompat import ntou
-from cherrypy._cptree import Application
-from cherrypy.test import helper
-
-script_names = ['', '/foo', '/users/fred/blog', '/corp/blog']
-
-
-class ObjectMappingTest(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-        class Root:
-
-            @cherrypy.expose
-            def index(self, name='world'):
-                return name
-
-            @cherrypy.expose
-            def foobar(self):
-                return 'bar'
-
-            @cherrypy.expose
-            def default(self, *params, **kwargs):
-                return 'default:' + repr(params)
-
-            @cherrypy.expose
-            def other(self):
-                return 'other'
-
-            @cherrypy.expose
-            def extra(self, *p):
-                return repr(p)
-
-            @cherrypy.expose
-            def redirect(self):
-                raise cherrypy.HTTPRedirect('dir1/', 302)
-
-            def notExposed(self):
-                return 'not exposed'
-
-            @cherrypy.expose
-            def confvalue(self):
-                return cherrypy.request.config.get('user')
-
-            @cherrypy.expose
-            def redirect_via_url(self, path):
-                raise cherrypy.HTTPRedirect(cherrypy.url(path))
-
-            @cherrypy.expose
-            def translate_html(self):
-                return 'OK'
-
-        @cherrypy.expose
-        def mapped_func(self, ID=None):
-            return 'ID is %s' % ID
-        setattr(Root, 'Von B\xfclow', mapped_func)
-
-        class Exposing:
-
-            @cherrypy.expose
-            def base(self):
-                return 'expose works!'
-            cherrypy.expose(base, '1')
-            cherrypy.expose(base, '2')
-
-        class ExposingNewStyle(object):
-
-            @cherrypy.expose
-            def base(self):
-                return 'expose works!'
-            cherrypy.expose(base, '1')
-            cherrypy.expose(base, '2')
-
-        class Dir1:
-
-            @cherrypy.expose
-            def index(self):
-                return 'index for dir1'
-
-            @cherrypy.expose
-            @cherrypy.config(**{'tools.trailing_slash.extra': True})
-            def myMethod(self):
-                return 'myMethod from dir1, path_info is:' + repr(
-                    cherrypy.request.path_info)
-
-            @cherrypy.expose
-            def default(self, *params):
-                return 'default for dir1, param is:' + repr(params)
-
-        class Dir2:
-
-            @cherrypy.expose
-            def index(self):
-                return 'index for dir2, path is:' + cherrypy.request.path_info
-
-            @cherrypy.expose
-            def script_name(self):
-                return cherrypy.tree.script_name()
-
-            @cherrypy.expose
-            def cherrypy_url(self):
-                return cherrypy.url('/extra')
-
-            @cherrypy.expose
-            def posparam(self, *vpath):
-                return '/'.join(vpath)
-
-        class Dir3:
-
-            def default(self):
-                return 'default for dir3, not exposed'
-
-        class Dir4:
-
-            def index(self):
-                return 'index for dir4, not exposed'
-
-        class DefNoIndex:
-
-            @cherrypy.expose
-            def default(self, *args):
-                raise cherrypy.HTTPRedirect('contact')
-
-        # MethodDispatcher code
-        @cherrypy.expose
-        class ByMethod:
-
-            def __init__(self, *things):
-                self.things = list(things)
-
-            def GET(self):
-                return repr(self.things)
-
-            def POST(self, thing):
-                self.things.append(thing)
-
-        class Collection:
-            default = ByMethod('a', 'bit')
-
-        Root.exposing = Exposing()
-        Root.exposingnew = ExposingNewStyle()
-        Root.dir1 = Dir1()
-        Root.dir1.dir2 = Dir2()
-        Root.dir1.dir2.dir3 = Dir3()
-        Root.dir1.dir2.dir3.dir4 = Dir4()
-        Root.defnoindex = DefNoIndex()
-        Root.bymethod = ByMethod('another')
-        Root.collection = Collection()
-
-        d = cherrypy.dispatch.MethodDispatcher()
-        for url in script_names:
-            conf = {'/': {'user': (url or '/').split('/')[-2]},
-                    '/bymethod': {'request.dispatch': d},
-                    '/collection': {'request.dispatch': d},
-                    }
-            cherrypy.tree.mount(Root(), url, conf)
-
-        class Isolated:
-
-            @cherrypy.expose
-            def index(self):
-                return 'made it!'
-
-        cherrypy.tree.mount(Isolated(), '/isolated')
-
-        @cherrypy.expose
-        class AnotherApp:
-
-            def GET(self):
-                return 'milk'
-
-        cherrypy.tree.mount(AnotherApp(), '/app',
-                            {'/': {'request.dispatch': d}})
-
-    def testObjectMapping(self):
-        for url in script_names:
-            self.script_name = url
-
-            self.getPage('/')
-            self.assertBody('world')
-
-            self.getPage('/dir1/myMethod')
-            self.assertBody(
-                "myMethod from dir1, path_info is:'/dir1/myMethod'")
-
-            self.getPage('/this/method/does/not/exist')
-            self.assertBody(
-                "default:('this', 'method', 'does', 'not', 'exist')")
-
-            self.getPage('/extra/too/much')
-            self.assertBody("('too', 'much')")
-
-            self.getPage('/other')
-            self.assertBody('other')
-
-            self.getPage('/notExposed')
-            self.assertBody("default:('notExposed',)")
-
-            self.getPage('/dir1/dir2/')
-            self.assertBody('index for dir2, path is:/dir1/dir2/')
-
-            # Test omitted trailing slash (should be redirected by default).
-            self.getPage('/dir1/dir2')
-            self.assertStatus(301)
-            self.assertHeader('Location', '%s/dir1/dir2/' % self.base())
-
-            # Test extra trailing slash (should be redirected if configured).
-            self.getPage('/dir1/myMethod/')
-            self.assertStatus(301)
-            self.assertHeader('Location', '%s/dir1/myMethod' % self.base())
-
-            # Test that default method must be exposed in order to match.
-            self.getPage('/dir1/dir2/dir3/dir4/index')
-            self.assertBody(
-                "default for dir1, param is:('dir2', 'dir3', 'dir4', 'index')")
-
-            # Test *vpath when default() is defined but not index()
-            # This also tests HTTPRedirect with default.
-            self.getPage('/defnoindex')
-            self.assertStatus((302, 303))
-            self.assertHeader('Location', '%s/contact' % self.base())
-            self.getPage('/defnoindex/')
-            self.assertStatus((302, 303))
-            self.assertHeader('Location', '%s/defnoindex/contact' %
-                              self.base())
-            self.getPage('/defnoindex/page')
-            self.assertStatus((302, 303))
-            self.assertHeader('Location', '%s/defnoindex/contact' %
-                              self.base())
-
-            self.getPage('/redirect')
-            self.assertStatus('302 Found')
-            self.assertHeader('Location', '%s/dir1/' % self.base())
-
-            if not getattr(cherrypy.server, 'using_apache', False):
-                # Test that we can use URL's which aren't all valid Python
-                # identifiers
-                # This should also test the %XX-unquoting of URL's.
-                self.getPage('/Von%20B%fclow?ID=14')
-                self.assertBody('ID is 14')
-
-                # Test that %2F in the path doesn't get unquoted too early;
-                # that is, it should not be used to separate path components.
-                # See ticket #393.
-                self.getPage('/page%2Fname')
-                self.assertBody("default:('page/name',)")
-
-            self.getPage('/dir1/dir2/script_name')
-            self.assertBody(url)
-            self.getPage('/dir1/dir2/cherrypy_url')
-            self.assertBody('%s/extra' % self.base())
-
-            # Test that configs don't overwrite each other from different apps
-            self.getPage('/confvalue')
-            self.assertBody((url or '/').split('/')[-2])
-
-        self.script_name = ''
-
-        # Test absoluteURI's in the Request-Line
-        self.getPage('http://%s:%s/' % (self.interface(), self.PORT))
-        self.assertBody('world')
-
-        self.getPage('http://%s:%s/abs/?service=http://192.168.0.1/x/y/z' %
-                     (self.interface(), self.PORT))
-        self.assertBody("default:('abs',)")
-
-        self.getPage('/rel/?service=http://192.168.120.121:8000/x/y/z')
-        self.assertBody("default:('rel',)")
-
-        # Test that the "isolated" app doesn't leak url's into the root app.
-        # If it did leak, Root.default() would answer with
-        #   "default:('isolated', 'doesnt', 'exist')".
-        self.getPage('/isolated/')
-        self.assertStatus('200 OK')
-        self.assertBody('made it!')
-        self.getPage('/isolated/doesnt/exist')
-        self.assertStatus('404 Not Found')
-
-        # Make sure /foobar maps to Root.foobar and not to the app
-        # mounted at /foo. See
-        # https://github.com/cherrypy/cherrypy/issues/573
-        self.getPage('/foobar')
-        self.assertBody('bar')
-
-    def test_translate(self):
-        self.getPage('/translate_html')
-        self.assertStatus('200 OK')
-        self.assertBody('OK')
-
-        self.getPage('/translate.html')
-        self.assertStatus('200 OK')
-        self.assertBody('OK')
-
-        self.getPage('/translate-html')
-        self.assertStatus('200 OK')
-        self.assertBody('OK')
-
-    def test_redir_using_url(self):
-        for url in script_names:
-            self.script_name = url
-
-            # Test the absolute path to the parent (leading slash)
-            self.getPage('/redirect_via_url?path=./')
-            self.assertStatus(('302 Found', '303 See Other'))
-            self.assertHeader('Location', '%s/' % self.base())
-
-            # Test the relative path to the parent (no leading slash)
-            self.getPage('/redirect_via_url?path=./')
-            self.assertStatus(('302 Found', '303 See Other'))
-            self.assertHeader('Location', '%s/' % self.base())
-
-            # Test the absolute path to the parent (leading slash)
-            self.getPage('/redirect_via_url/?path=./')
-            self.assertStatus(('302 Found', '303 See Other'))
-            self.assertHeader('Location', '%s/' % self.base())
-
-            # Test the relative path to the parent (no leading slash)
-            self.getPage('/redirect_via_url/?path=./')
-            self.assertStatus(('302 Found', '303 See Other'))
-            self.assertHeader('Location', '%s/' % self.base())
-
-    def testPositionalParams(self):
-        self.getPage('/dir1/dir2/posparam/18/24/hut/hike')
-        self.assertBody('18/24/hut/hike')
-
-        # intermediate index methods should not receive posparams;
-        # only the "final" index method should do so.
-        self.getPage('/dir1/dir2/5/3/sir')
-        self.assertBody("default for dir1, param is:('dir2', '5', '3', 'sir')")
-
-        # test that extra positional args raises an 404 Not Found
-        # See https://github.com/cherrypy/cherrypy/issues/733.
-        self.getPage('/dir1/dir2/script_name/extra/stuff')
-        self.assertStatus(404)
-
-    def testExpose(self):
-        # Test the cherrypy.expose function/decorator
-        self.getPage('/exposing/base')
-        self.assertBody('expose works!')
-
-        self.getPage('/exposing/1')
-        self.assertBody('expose works!')
-
-        self.getPage('/exposing/2')
-        self.assertBody('expose works!')
-
-        self.getPage('/exposingnew/base')
-        self.assertBody('expose works!')
-
-        self.getPage('/exposingnew/1')
-        self.assertBody('expose works!')
-
-        self.getPage('/exposingnew/2')
-        self.assertBody('expose works!')
-
-    def testMethodDispatch(self):
-        self.getPage('/bymethod')
-        self.assertBody("['another']")
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-
-        self.getPage('/bymethod', method='HEAD')
-        self.assertBody('')
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-
-        self.getPage('/bymethod', method='POST', body='thing=one')
-        self.assertBody('')
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-
-        self.getPage('/bymethod')
-        self.assertBody(repr(['another', ntou('one')]))
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-
-        self.getPage('/bymethod', method='PUT')
-        self.assertErrorPage(405)
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-
-        # Test default with posparams
-        self.getPage('/collection/silly', method='POST')
-        self.getPage('/collection', method='GET')
-        self.assertBody("['a', 'bit', 'silly']")
-
-        # Test custom dispatcher set on app root (see #737).
-        self.getPage('/app')
-        self.assertBody('milk')
-
-    def testTreeMounting(self):
-        class Root(object):
-
-            @cherrypy.expose
-            def hello(self):
-                return 'Hello world!'
-
-        # When mounting an application instance,
-        # we can't specify a different script name in the call to mount.
-        a = Application(Root(), '/somewhere')
-        self.assertRaises(ValueError, cherrypy.tree.mount, a, '/somewhereelse')
-
-        # When mounting an application instance...
-        a = Application(Root(), '/somewhere')
-        # ...we MUST allow in identical script name in the call to mount...
-        cherrypy.tree.mount(a, '/somewhere')
-        self.getPage('/somewhere/hello')
-        self.assertStatus(200)
-        # ...and MUST allow a missing script_name.
-        del cherrypy.tree.apps['/somewhere']
-        cherrypy.tree.mount(a)
-        self.getPage('/somewhere/hello')
-        self.assertStatus(200)
-
-        # In addition, we MUST be able to create an Application using
-        # script_name == None for access to the wsgi_environ.
-        a = Application(Root(), script_name=None)
-        # However, this does not apply to tree.mount
-        self.assertRaises(TypeError, cherrypy.tree.mount, a, None)
-
-    def testKeywords(self):
-        if sys.version_info < (3,):
-            return self.skip('skipped (Python 3 only)')
-        exec("""class Root(object):
-    @cherrypy.expose
-    def hello(self, *, name='world'):
-        return 'Hello %s!' % name
-cherrypy.tree.mount(Application(Root(), '/keywords'))""")
-
-        self.getPage('/keywords/hello')
-        self.assertStatus(200)
-        self.getPage('/keywords/hello/extra')
-        self.assertStatus(404)
diff --git a/libraries/cherrypy/test/test_params.py b/libraries/cherrypy/test/test_params.py
deleted file mode 100644
index 73b4cb4c..00000000
--- a/libraries/cherrypy/test/test_params.py
+++ /dev/null
@@ -1,61 +0,0 @@
-import sys
-import textwrap
-
-import cherrypy
-from cherrypy.test import helper
-
-
-class ParamsTest(helper.CPWebCase):
-    @staticmethod
-    def setup_server():
-        class Root:
-            @cherrypy.expose
-            @cherrypy.tools.json_out()
-            @cherrypy.tools.params()
-            def resource(self, limit=None, sort=None):
-                return type(limit).__name__
-            # for testing on Py 2
-            resource.__annotations__ = {'limit': int}
-        conf = {'/': {'tools.params.on': True}}
-        cherrypy.tree.mount(Root(), config=conf)
-
-    def test_pass(self):
-        self.getPage('/resource')
-        self.assertStatus(200)
-        self.assertBody('"NoneType"')
-
-        self.getPage('/resource?limit=0')
-        self.assertStatus(200)
-        self.assertBody('"int"')
-
-    def test_error(self):
-        self.getPage('/resource?limit=')
-        self.assertStatus(400)
-        self.assertInBody('invalid literal for int')
-
-        cherrypy.config['tools.params.error'] = 422
-        self.getPage('/resource?limit=')
-        self.assertStatus(422)
-        self.assertInBody('invalid literal for int')
-
-        cherrypy.config['tools.params.exception'] = TypeError
-        self.getPage('/resource?limit=')
-        self.assertStatus(500)
-
-    def test_syntax(self):
-        if sys.version_info < (3,):
-            return self.skip('skipped (Python 3 only)')
-        code = textwrap.dedent("""
-            class Root:
-                @cherrypy.expose
-                @cherrypy.tools.params()
-                def resource(self, limit: int):
-                    return type(limit).__name__
-            conf = {'/': {'tools.params.on': True}}
-            cherrypy.tree.mount(Root(), config=conf)
-            """)
-        exec(code)
-
-        self.getPage('/resource?limit=0')
-        self.assertStatus(200)
-        self.assertBody('int')
diff --git a/libraries/cherrypy/test/test_plugins.py b/libraries/cherrypy/test/test_plugins.py
deleted file mode 100644
index 4d3aa6b1..00000000
--- a/libraries/cherrypy/test/test_plugins.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from cherrypy.process import plugins
-
-
-__metaclass__ = type
-
-
-class TestAutoreloader:
-    def test_file_for_file_module_when_None(self):
-        """No error when module.__file__ is None.
-        """
-        class test_module:
-            __file__ = None
-
-        assert plugins.Autoreloader._file_for_file_module(test_module) is None
diff --git a/libraries/cherrypy/test/test_proxy.py b/libraries/cherrypy/test/test_proxy.py
deleted file mode 100644
index 4d34440a..00000000
--- a/libraries/cherrypy/test/test_proxy.py
+++ /dev/null
@@ -1,154 +0,0 @@
-import cherrypy
-from cherrypy.test import helper
-
-script_names = ['', '/path/to/myapp']
-
-
-class ProxyTest(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-
-        # Set up site
-        cherrypy.config.update({
-            'tools.proxy.on': True,
-            'tools.proxy.base': 'www.mydomain.test',
-        })
-
-        # Set up application
-
-        class Root:
-
-            def __init__(self, sn):
-                # Calculate a URL outside of any requests.
-                self.thisnewpage = cherrypy.url(
-                    '/this/new/page', script_name=sn)
-
-            @cherrypy.expose
-            def pageurl(self):
-                return self.thisnewpage
-
-            @cherrypy.expose
-            def index(self):
-                raise cherrypy.HTTPRedirect('dummy')
-
-            @cherrypy.expose
-            def remoteip(self):
-                return cherrypy.request.remote.ip
-
-            @cherrypy.expose
-            @cherrypy.config(**{
-                'tools.proxy.local': 'X-Host',
-                'tools.trailing_slash.extra': True,
-            })
-            def xhost(self):
-                raise cherrypy.HTTPRedirect('blah')
-
-            @cherrypy.expose
-            def base(self):
-                return cherrypy.request.base
-
-            @cherrypy.expose
-            @cherrypy.config(**{'tools.proxy.scheme': 'X-Forwarded-Ssl'})
-            def ssl(self):
-                return cherrypy.request.base
-
-            @cherrypy.expose
-            def newurl(self):
-                return ("Browse to <a href='%s'>this page</a>."
-                        % cherrypy.url('/this/new/page'))
-
-            @cherrypy.expose
-            @cherrypy.config(**{
-                'tools.proxy.base': None,
-            })
-            def base_no_base(self):
-                return cherrypy.request.base
-
-        for sn in script_names:
-            cherrypy.tree.mount(Root(sn), sn)
-
-    def testProxy(self):
-        self.getPage('/')
-        self.assertHeader('Location',
-                          '%s://www.mydomain.test%s/dummy' %
-                          (self.scheme, self.prefix()))
-
-        # Test X-Forwarded-Host (Apache 1.3.33+ and Apache 2)
-        self.getPage(
-            '/', headers=[('X-Forwarded-Host', 'http://www.example.test')])
-        self.assertHeader('Location', 'http://www.example.test/dummy')
-        self.getPage('/', headers=[('X-Forwarded-Host', 'www.example.test')])
-        self.assertHeader('Location', '%s://www.example.test/dummy' %
-                          self.scheme)
-        # Test multiple X-Forwarded-Host headers
-        self.getPage('/', headers=[
-            ('X-Forwarded-Host', 'http://www.example.test, www.cherrypy.test'),
-        ])
-        self.assertHeader('Location', 'http://www.example.test/dummy')
-
-        # Test X-Forwarded-For (Apache2)
-        self.getPage('/remoteip',
-                     headers=[('X-Forwarded-For', '192.168.0.20')])
-        self.assertBody('192.168.0.20')
-        # Fix bug #1268
-        self.getPage('/remoteip',
-                     headers=[
-                         ('X-Forwarded-For', '67.15.36.43, 192.168.0.20')
-                     ])
-        self.assertBody('67.15.36.43')
-
-        # Test X-Host (lighttpd; see https://trac.lighttpd.net/trac/ticket/418)
-        self.getPage('/xhost', headers=[('X-Host', 'www.example.test')])
-        self.assertHeader('Location', '%s://www.example.test/blah' %
-                          self.scheme)
-
-        # Test X-Forwarded-Proto (lighttpd)
-        self.getPage('/base', headers=[('X-Forwarded-Proto', 'https')])
-        self.assertBody('https://www.mydomain.test')
-
-        # Test X-Forwarded-Ssl (webfaction?)
-        self.getPage('/ssl', headers=[('X-Forwarded-Ssl', 'on')])
-        self.assertBody('https://www.mydomain.test')
-
-        # Test cherrypy.url()
-        for sn in script_names:
-            # Test the value inside requests
-            self.getPage(sn + '/newurl')
-            self.assertBody(
-                "Browse to <a href='%s://www.mydomain.test" % self.scheme +
-                sn + "/this/new/page'>this page</a>.")
-            self.getPage(sn + '/newurl', headers=[('X-Forwarded-Host',
-                                                   'http://www.example.test')])
-            self.assertBody("Browse to <a href='http://www.example.test" +
-                            sn + "/this/new/page'>this page</a>.")
-
-            # Test the value outside requests
-            port = ''
-            if self.scheme == 'http' and self.PORT != 80:
-                port = ':%s' % self.PORT
-            elif self.scheme == 'https' and self.PORT != 443:
-                port = ':%s' % self.PORT
-            host = self.HOST
-            if host in ('0.0.0.0', '::'):
-                import socket
-                host = socket.gethostname()
-            expected = ('%s://%s%s%s/this/new/page'
-                        % (self.scheme, host, port, sn))
-            self.getPage(sn + '/pageurl')
-            self.assertBody(expected)
-
-        # Test trailing slash (see
-        # https://github.com/cherrypy/cherrypy/issues/562).
-        self.getPage('/xhost/', headers=[('X-Host', 'www.example.test')])
-        self.assertHeader('Location', '%s://www.example.test/xhost'
-                          % self.scheme)
-
-    def test_no_base_port_in_host(self):
-        """
-        If no base is indicated, and the host header is used to resolve
-        the base, it should rely on the host header for the port also.
-        """
-        headers = {'Host': 'localhost:8080'}.items()
-        self.getPage('/base_no_base', headers=headers)
-        self.assertBody('http://localhost:8080')
diff --git a/libraries/cherrypy/test/test_refleaks.py b/libraries/cherrypy/test/test_refleaks.py
deleted file mode 100644
index c2fe9e66..00000000
--- a/libraries/cherrypy/test/test_refleaks.py
+++ /dev/null
@@ -1,66 +0,0 @@
-"""Tests for refleaks."""
-
-import itertools
-import platform
-import threading
-
-from six.moves.http_client import HTTPConnection
-
-import cherrypy
-from cherrypy._cpcompat import HTTPSConnection
-from cherrypy.test import helper
-
-
-data = object()
-
-
-class ReferenceTests(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-
-        class Root:
-
-            @cherrypy.expose
-            def index(self, *args, **kwargs):
-                cherrypy.request.thing = data
-                return 'Hello world!'
-
-        cherrypy.tree.mount(Root())
-
-    def test_threadlocal_garbage(self):
-        if platform.system() == 'Darwin':
-            self.skip('queue issues; see #1474')
-        success = itertools.count()
-
-        def getpage():
-            host = '%s:%s' % (self.interface(), self.PORT)
-            if self.scheme == 'https':
-                c = HTTPSConnection(host)
-            else:
-                c = HTTPConnection(host)
-            try:
-                c.putrequest('GET', '/')
-                c.endheaders()
-                response = c.getresponse()
-                body = response.read()
-                self.assertEqual(response.status, 200)
-                self.assertEqual(body, b'Hello world!')
-            finally:
-                c.close()
-            next(success)
-
-        ITERATIONS = 25
-
-        ts = [
-            threading.Thread(target=getpage)
-            for _ in range(ITERATIONS)
-        ]
-
-        for t in ts:
-            t.start()
-
-        for t in ts:
-            t.join()
-
-        self.assertEqual(next(success), ITERATIONS)
diff --git a/libraries/cherrypy/test/test_request_obj.py b/libraries/cherrypy/test/test_request_obj.py
deleted file mode 100644
index 6b93e13d..00000000
--- a/libraries/cherrypy/test/test_request_obj.py
+++ /dev/null
@@ -1,932 +0,0 @@
-"""Basic tests for the cherrypy.Request object."""
-
-from functools import wraps
-import os
-import sys
-import types
-import uuid
-
-import six
-from six.moves.http_client import IncompleteRead
-
-import cherrypy
-from cherrypy._cpcompat import ntou
-from cherrypy.lib import httputil
-from cherrypy.test import helper
-
-localDir = os.path.dirname(__file__)
-
-defined_http_methods = ('OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE',
-                        'TRACE', 'PROPFIND', 'PATCH')
-
-
-#                             Client-side code                             #
-
-
-class RequestObjectTests(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-        class Root:
-
-            @cherrypy.expose
-            def index(self):
-                return 'hello'
-
-            @cherrypy.expose
-            def scheme(self):
-                return cherrypy.request.scheme
-
-            @cherrypy.expose
-            def created_example_com_3128(self):
-                """Handle CONNECT method."""
-                cherrypy.response.status = 204
-
-            @cherrypy.expose
-            def body_example_com_3128(self):
-                """Handle CONNECT method."""
-                return (
-                    cherrypy.request.method
-                    + 'ed to '
-                    + cherrypy.request.path_info
-                )
-
-            @cherrypy.expose
-            def request_uuid4(self):
-                return [
-                    str(cherrypy.request.unique_id),
-                    ' ',
-                    str(cherrypy.request.unique_id),
-                ]
-
-        root = Root()
-
-        class TestType(type):
-            """Metaclass which automatically exposes all functions in each
-            subclass, and adds an instance of the subclass as an attribute
-            of root.
-            """
-            def __init__(cls, name, bases, dct):
-                type.__init__(cls, name, bases, dct)
-                for value in dct.values():
-                    if isinstance(value, types.FunctionType):
-                        value.exposed = True
-                setattr(root, name.lower(), cls())
-        Test = TestType('Test', (object,), {})
-
-        class PathInfo(Test):
-
-            def default(self, *args):
-                return cherrypy.request.path_info
-
-        class Params(Test):
-
-            def index(self, thing):
-                return repr(thing)
-
-            def ismap(self, x, y):
-                return 'Coordinates: %s, %s' % (x, y)
-
-            @cherrypy.config(**{'request.query_string_encoding': 'latin1'})
-            def default(self, *args, **kwargs):
-                return 'args: %s kwargs: %s' % (args, sorted(kwargs.items()))
-
-        @cherrypy.expose
-        class ParamErrorsCallable(object):
-
-            def __call__(self):
-                return 'data'
-
-        def handler_dec(f):
-            @wraps(f)
-            def wrapper(handler, *args, **kwargs):
-                return f(handler, *args, **kwargs)
-            return wrapper
-
-        class ParamErrors(Test):
-
-            @cherrypy.expose
-            def one_positional(self, param1):
-                return 'data'
-
-            @cherrypy.expose
-            def one_positional_args(self, param1, *args):
-                return 'data'
-
-            @cherrypy.expose
-            def one_positional_args_kwargs(self, param1, *args, **kwargs):
-                return 'data'
-
-            @cherrypy.expose
-            def one_positional_kwargs(self, param1, **kwargs):
-                return 'data'
-
-            @cherrypy.expose
-            def no_positional(self):
-                return 'data'
-
-            @cherrypy.expose
-            def no_positional_args(self, *args):
-                return 'data'
-
-            @cherrypy.expose
-            def no_positional_args_kwargs(self, *args, **kwargs):
-                return 'data'
-
-            @cherrypy.expose
-            def no_positional_kwargs(self, **kwargs):
-                return 'data'
-
-            callable_object = ParamErrorsCallable()
-
-            @cherrypy.expose
-            def raise_type_error(self, **kwargs):
-                raise TypeError('Client Error')
-
-            @cherrypy.expose
-            def raise_type_error_with_default_param(self, x, y=None):
-                return '%d' % 'a'  # throw an exception
-
-            @cherrypy.expose
-            @handler_dec
-            def raise_type_error_decorated(self, *args, **kwargs):
-                raise TypeError('Client Error')
-
-        def callable_error_page(status, **kwargs):
-            return "Error %s - Well, I'm very sorry but you haven't paid!" % (
-                status)
-
-        @cherrypy.config(**{'tools.log_tracebacks.on': True})
-        class Error(Test):
-
-            def reason_phrase(self):
-                raise cherrypy.HTTPError("410 Gone fishin'")
-
-            @cherrypy.config(**{
-                'error_page.404': os.path.join(localDir, 'static/index.html'),
-                'error_page.401': callable_error_page,
-            })
-            def custom(self, err='404'):
-                raise cherrypy.HTTPError(
-                    int(err), 'No, <b>really</b>, not found!')
-
-            @cherrypy.config(**{
-                'error_page.default': callable_error_page,
-            })
-            def custom_default(self):
-                return 1 + 'a'  # raise an unexpected error
-
-            @cherrypy.config(**{'error_page.404': 'nonexistent.html'})
-            def noexist(self):
-                raise cherrypy.HTTPError(404, 'No, <b>really</b>, not found!')
-
-            def page_method(self):
-                raise ValueError()
-
-            def page_yield(self):
-                yield 'howdy'
-                raise ValueError()
-
-            @cherrypy.config(**{'response.stream': True})
-            def page_streamed(self):
-                yield 'word up'
-                raise ValueError()
-                yield 'very oops'
-
-            @cherrypy.config(**{'request.show_tracebacks': False})
-            def cause_err_in_finalize(self):
-                # Since status must start with an int, this should error.
-                cherrypy.response.status = 'ZOO OK'
-
-            @cherrypy.config(**{'request.throw_errors': True})
-            def rethrow(self):
-                """Test that an error raised here will be thrown out to
-                the server.
-                """
-                raise ValueError()
-
-        class Expect(Test):
-
-            def expectation_failed(self):
-                expect = cherrypy.request.headers.elements('Expect')
-                if expect and expect[0].value != '100-continue':
-                    raise cherrypy.HTTPError(400)
-                raise cherrypy.HTTPError(417, 'Expectation Failed')
-
-        class Headers(Test):
-
-            def default(self, headername):
-                """Spit back out the value for the requested header."""
-                return cherrypy.request.headers[headername]
-
-            def doubledheaders(self):
-                # From https://github.com/cherrypy/cherrypy/issues/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."
-
-                # Set the most common headers
-                hMap = cherrypy.response.headers
-                hMap['content-type'] = 'text/html'
-                hMap['content-length'] = 18
-                hMap['server'] = 'CherryPy headertest'
-                hMap['location'] = ('%s://%s:%s/headers/'
-                                    % (cherrypy.request.local.ip,
-                                       cherrypy.request.local.port,
-                                       cherrypy.request.scheme))
-
-                # Set a rare header for fun
-                hMap['Expires'] = 'Thu, 01 Dec 2194 16:00:00 GMT'
-
-                return 'double header test'
-
-            def ifmatch(self):
-                val = cherrypy.request.headers['If-Match']
-                assert isinstance(val, six.text_type)
-                cherrypy.response.headers['ETag'] = val
-                return val
-
-        class HeaderElements(Test):
-
-            def get_elements(self, headername):
-                e = cherrypy.request.headers.elements(headername)
-                return '\n'.join([six.text_type(x) for x in e])
-
-        class Method(Test):
-
-            def index(self):
-                m = cherrypy.request.method
-                if m in defined_http_methods or m == 'CONNECT':
-                    return m
-
-                if m == 'LINK':
-                    raise cherrypy.HTTPError(405)
-                else:
-                    raise cherrypy.HTTPError(501)
-
-            def parameterized(self, data):
-                return data
-
-            def request_body(self):
-                # This should be a file object (temp file),
-                # which CP will just pipe back out if we tell it to.
-                return cherrypy.request.body
-
-            def reachable(self):
-                return 'success'
-
-        class Divorce(Test):
-
-            """HTTP Method handlers shouldn't collide with normal method names.
-            For example, a GET-handler shouldn't collide with a method named
-            'get'.
-
-            If you build HTTP method dispatching into CherryPy, rewrite this
-            class to use your new dispatch mechanism and make sure that:
-                "GET /divorce HTTP/1.1" maps to divorce.index() and
-                "GET /divorce/get?ID=13 HTTP/1.1" maps to divorce.get()
-            """
-
-            documents = {}
-
-            @cherrypy.expose
-            def index(self):
-                yield '<h1>Choose your document</h1>\n'
-                yield '<ul>\n'
-                for id, contents in self.documents.items():
-                    yield (
-                        "    <li><a href='/divorce/get?ID=%s'>%s</a>:"
-                        ' %s</li>\n' % (id, id, contents))
-                yield '</ul>'
-
-            @cherrypy.expose
-            def get(self, ID):
-                return ('Divorce document %s: %s' %
-                        (ID, self.documents.get(ID, 'empty')))
-
-        class ThreadLocal(Test):
-
-            def index(self):
-                existing = repr(getattr(cherrypy.request, 'asdf', None))
-                cherrypy.request.asdf = 'rassfrassin'
-                return existing
-
-        appconf = {
-            '/method': {
-                'request.methods_with_bodies': ('POST', 'PUT', 'PROPFIND',
-                                                'PATCH')
-            },
-        }
-        cherrypy.tree.mount(root, config=appconf)
-
-    def test_scheme(self):
-        self.getPage('/scheme')
-        self.assertBody(self.scheme)
-
-    def test_per_request_uuid4(self):
-        self.getPage('/request_uuid4')
-        first_uuid4, _, second_uuid4 = self.body.decode().partition(' ')
-        assert (
-            uuid.UUID(first_uuid4, version=4)
-            == uuid.UUID(second_uuid4, version=4)
-        )
-
-        self.getPage('/request_uuid4')
-        third_uuid4, _, _ = self.body.decode().partition(' ')
-        assert (
-            uuid.UUID(first_uuid4, version=4)
-            != uuid.UUID(third_uuid4, version=4)
-        )
-
-    def testRelativeURIPathInfo(self):
-        self.getPage('/pathinfo/foo/bar')
-        self.assertBody('/pathinfo/foo/bar')
-
-    def testAbsoluteURIPathInfo(self):
-        # http://cherrypy.org/ticket/1061
-        self.getPage('http://localhost/pathinfo/foo/bar')
-        self.assertBody('/pathinfo/foo/bar')
-
-    def testParams(self):
-        self.getPage('/params/?thing=a')
-        self.assertBody(repr(ntou('a')))
-
-        self.getPage('/params/?thing=a&thing=b&thing=c')
-        self.assertBody(repr([ntou('a'), ntou('b'), ntou('c')]))
-
-        # Test friendly error message when given params are not accepted.
-        cherrypy.config.update({'request.show_mismatched_params': True})
-        self.getPage('/params/?notathing=meeting')
-        self.assertInBody('Missing parameters: thing')
-        self.getPage('/params/?thing=meeting&notathing=meeting')
-        self.assertInBody('Unexpected query string parameters: notathing')
-
-        # Test ability to turn off friendly error messages
-        cherrypy.config.update({'request.show_mismatched_params': False})
-        self.getPage('/params/?notathing=meeting')
-        self.assertInBody('Not Found')
-        self.getPage('/params/?thing=meeting&notathing=meeting')
-        self.assertInBody('Not Found')
-
-        # Test "% HEX HEX"-encoded URL, param keys, and values
-        self.getPage('/params/%d4%20%e3/cheese?Gruy%E8re=Bulgn%e9ville')
-        self.assertBody('args: %s kwargs: %s' %
-                        (('\xd4 \xe3', 'cheese'),
-                         [('Gruy\xe8re', ntou('Bulgn\xe9ville'))]))
-
-        # Make sure that encoded = and & get parsed correctly
-        self.getPage(
-            '/params/code?url=http%3A//cherrypy.org/index%3Fa%3D1%26b%3D2')
-        self.assertBody('args: %s kwargs: %s' %
-                        (('code',),
-                         [('url', ntou('http://cherrypy.org/index?a=1&b=2'))]))
-
-        # Test coordinates sent by <img ismap>
-        self.getPage('/params/ismap?223,114')
-        self.assertBody('Coordinates: 223, 114')
-
-        # Test "name[key]" dict-like params
-        self.getPage('/params/dictlike?a[1]=1&a[2]=2&b=foo&b[bar]=baz')
-        self.assertBody('args: %s kwargs: %s' %
-                        (('dictlike',),
-                         [('a[1]', ntou('1')), ('a[2]', ntou('2')),
-                          ('b', ntou('foo')), ('b[bar]', ntou('baz'))]))
-
-    def testParamErrors(self):
-
-        # test that all of the handlers work when given
-        # the correct parameters in order to ensure that the
-        # errors below aren't coming from some other source.
-        for uri in (
-                '/paramerrors/one_positional?param1=foo',
-                '/paramerrors/one_positional_args?param1=foo',
-                '/paramerrors/one_positional_args/foo',
-                '/paramerrors/one_positional_args/foo/bar/baz',
-                '/paramerrors/one_positional_args_kwargs?'
-                'param1=foo&param2=bar',
-                '/paramerrors/one_positional_args_kwargs/foo?'
-                'param2=bar&param3=baz',
-                '/paramerrors/one_positional_args_kwargs/foo/bar/baz?'
-                'param2=bar&param3=baz',
-                '/paramerrors/one_positional_kwargs?'
-                'param1=foo&param2=bar&param3=baz',
-                '/paramerrors/one_positional_kwargs/foo?'
-                'param4=foo&param2=bar&param3=baz',
-                '/paramerrors/no_positional',
-                '/paramerrors/no_positional_args/foo',
-                '/paramerrors/no_positional_args/foo/bar/baz',
-                '/paramerrors/no_positional_args_kwargs?param1=foo&param2=bar',
-                '/paramerrors/no_positional_args_kwargs/foo?param2=bar',
-                '/paramerrors/no_positional_args_kwargs/foo/bar/baz?'
-                'param2=bar&param3=baz',
-                '/paramerrors/no_positional_kwargs?param1=foo&param2=bar',
-                '/paramerrors/callable_object',
-        ):
-            self.getPage(uri)
-            self.assertStatus(200)
-
-        error_msgs = [
-            'Missing parameters',
-            'Nothing matches the given URI',
-            'Multiple values for parameters',
-            'Unexpected query string parameters',
-            'Unexpected body parameters',
-            'Invalid path in Request-URI',
-            'Illegal #fragment in Request-URI',
-        ]
-
-        # uri should be tested for valid absolute path, the status must be 400.
-        for uri, error_idx in (
-            ('invalid/path/without/leading/slash', 5),
-            ('/valid/path#invalid=fragment', 6),
-        ):
-            self.getPage(uri)
-            self.assertStatus(400)
-            self.assertInBody(error_msgs[error_idx])
-
-        # query string parameters are part of the URI, so if they are wrong
-        # for a particular handler, the status MUST be a 404.
-        for uri, msg in (
-            ('/paramerrors/one_positional', error_msgs[0]),
-            ('/paramerrors/one_positional?foo=foo', error_msgs[0]),
-            ('/paramerrors/one_positional/foo/bar/baz', error_msgs[1]),
-            ('/paramerrors/one_positional/foo?param1=foo', error_msgs[2]),
-            ('/paramerrors/one_positional/foo?param1=foo&param2=foo',
-             error_msgs[2]),
-            ('/paramerrors/one_positional_args/foo?param1=foo&param2=foo',
-             error_msgs[2]),
-            ('/paramerrors/one_positional_args/foo/bar/baz?param2=foo',
-             error_msgs[3]),
-            ('/paramerrors/one_positional_args_kwargs/foo/bar/baz?'
-             'param1=bar&param3=baz',
-             error_msgs[2]),
-            ('/paramerrors/one_positional_kwargs/foo?'
-             'param1=foo&param2=bar&param3=baz',
-             error_msgs[2]),
-            ('/paramerrors/no_positional/boo', error_msgs[1]),
-            ('/paramerrors/no_positional?param1=foo', error_msgs[3]),
-            ('/paramerrors/no_positional_args/boo?param1=foo', error_msgs[3]),
-            ('/paramerrors/no_positional_kwargs/boo?param1=foo',
-             error_msgs[1]),
-            ('/paramerrors/callable_object?param1=foo', error_msgs[3]),
-            ('/paramerrors/callable_object/boo', error_msgs[1]),
-        ):
-            for show_mismatched_params in (True, False):
-                cherrypy.config.update(
-                    {'request.show_mismatched_params': show_mismatched_params})
-                self.getPage(uri)
-                self.assertStatus(404)
-                if show_mismatched_params:
-                    self.assertInBody(msg)
-                else:
-                    self.assertInBody('Not Found')
-
-        # if body parameters are wrong, a 400 must be returned.
-        for uri, body, msg in (
-                ('/paramerrors/one_positional/foo',
-                 'param1=foo', error_msgs[2]),
-                ('/paramerrors/one_positional/foo',
-                 'param1=foo&param2=foo', error_msgs[2]),
-                ('/paramerrors/one_positional_args/foo',
-                 'param1=foo&param2=foo', error_msgs[2]),
-                ('/paramerrors/one_positional_args/foo/bar/baz',
-                 'param2=foo', error_msgs[4]),
-                ('/paramerrors/one_positional_args_kwargs/foo/bar/baz',
-                 'param1=bar&param3=baz', error_msgs[2]),
-                ('/paramerrors/one_positional_kwargs/foo',
-                 'param1=foo&param2=bar&param3=baz', error_msgs[2]),
-                ('/paramerrors/no_positional', 'param1=foo', error_msgs[4]),
-                ('/paramerrors/no_positional_args/boo',
-                 'param1=foo', error_msgs[4]),
-                ('/paramerrors/callable_object', 'param1=foo', error_msgs[4]),
-        ):
-            for show_mismatched_params in (True, False):
-                cherrypy.config.update(
-                    {'request.show_mismatched_params': show_mismatched_params})
-                self.getPage(uri, method='POST', body=body)
-                self.assertStatus(400)
-                if show_mismatched_params:
-                    self.assertInBody(msg)
-                else:
-                    self.assertInBody('400 Bad')
-
-        # even if body parameters are wrong, if we get the uri wrong, then
-        # it's a 404
-        for uri, body, msg in (
-                ('/paramerrors/one_positional?param2=foo',
-                 'param1=foo', error_msgs[3]),
-                ('/paramerrors/one_positional/foo/bar',
-                 'param2=foo', error_msgs[1]),
-                ('/paramerrors/one_positional_args/foo/bar?param2=foo',
-                 'param3=foo', error_msgs[3]),
-                ('/paramerrors/one_positional_kwargs/foo/bar',
-                 'param2=bar&param3=baz', error_msgs[1]),
-                ('/paramerrors/no_positional?param1=foo',
-                 'param2=foo', error_msgs[3]),
-                ('/paramerrors/no_positional_args/boo?param2=foo',
-                 'param1=foo', error_msgs[3]),
-                ('/paramerrors/callable_object?param2=bar',
-                 'param1=foo', error_msgs[3]),
-        ):
-            for show_mismatched_params in (True, False):
-                cherrypy.config.update(
-                    {'request.show_mismatched_params': show_mismatched_params})
-                self.getPage(uri, method='POST', body=body)
-                self.assertStatus(404)
-                if show_mismatched_params:
-                    self.assertInBody(msg)
-                else:
-                    self.assertInBody('Not Found')
-
-        # In the case that a handler raises a TypeError we should
-        # let that type error through.
-        for uri in (
-                '/paramerrors/raise_type_error',
-                '/paramerrors/raise_type_error_with_default_param?x=0',
-                '/paramerrors/raise_type_error_with_default_param?x=0&y=0',
-                '/paramerrors/raise_type_error_decorated',
-        ):
-            self.getPage(uri, method='GET')
-            self.assertStatus(500)
-            self.assertTrue('Client Error', self.body)
-
-    def testErrorHandling(self):
-        self.getPage('/error/missing')
-        self.assertStatus(404)
-        self.assertErrorPage(404, "The path '/error/missing' was not found.")
-
-        ignore = helper.webtest.ignored_exceptions
-        ignore.append(ValueError)
-        try:
-            valerr = '\n    raise ValueError()\nValueError'
-            self.getPage('/error/page_method')
-            self.assertErrorPage(500, pattern=valerr)
-
-            self.getPage('/error/page_yield')
-            self.assertErrorPage(500, pattern=valerr)
-
-            if (cherrypy.server.protocol_version == 'HTTP/1.0' or
-                    getattr(cherrypy.server, 'using_apache', False)):
-                self.getPage('/error/page_streamed')
-                # Because this error is raised after the response body has
-                # started, the status should not change to an error status.
-                self.assertStatus(200)
-                self.assertBody('word up')
-            else:
-                # Under HTTP/1.1, the chunked transfer-coding is used.
-                # The HTTP client will choke when the output is incomplete.
-                self.assertRaises((ValueError, IncompleteRead), self.getPage,
-                                  '/error/page_streamed')
-
-            # No traceback should be present
-            self.getPage('/error/cause_err_in_finalize')
-            msg = "Illegal response status from server ('ZOO' is non-numeric)."
-            self.assertErrorPage(500, msg, None)
-        finally:
-            ignore.pop()
-
-        # Test HTTPError with a reason-phrase in the status arg.
-        self.getPage('/error/reason_phrase')
-        self.assertStatus("410 Gone fishin'")
-
-        # Test custom error page for a specific error.
-        self.getPage('/error/custom')
-        self.assertStatus(404)
-        self.assertBody('Hello, world\r\n' + (' ' * 499))
-
-        # Test custom error page for a specific error.
-        self.getPage('/error/custom?err=401')
-        self.assertStatus(401)
-        self.assertBody(
-            'Error 401 Unauthorized - '
-            "Well, I'm very sorry but you haven't paid!")
-
-        # Test default custom error page.
-        self.getPage('/error/custom_default')
-        self.assertStatus(500)
-        self.assertBody(
-            'Error 500 Internal Server Error - '
-            "Well, I'm very sorry but you haven't paid!".ljust(513))
-
-        # Test error in custom error page (ticket #305).
-        # Note that the message is escaped for HTML (ticket #310).
-        self.getPage('/error/noexist')
-        self.assertStatus(404)
-        if sys.version_info >= (3, 3):
-            exc_name = 'FileNotFoundError'
-        else:
-            exc_name = 'IOError'
-        msg = ('No, &lt;b&gt;really&lt;/b&gt;, not found!<br />'
-               'In addition, the custom error page failed:\n<br />'
-               '%s: [Errno 2] '
-               "No such file or directory: 'nonexistent.html'") % (exc_name,)
-        self.assertInBody(msg)
-
-        if getattr(cherrypy.server, 'using_apache', False):
-            pass
-        else:
-            # Test throw_errors (ticket #186).
-            self.getPage('/error/rethrow')
-            self.assertInBody('raise ValueError()')
-
-    def testExpect(self):
-        e = ('Expect', '100-continue')
-        self.getPage('/headerelements/get_elements?headername=Expect', [e])
-        self.assertBody('100-continue')
-
-        self.getPage('/expect/expectation_failed', [e])
-        self.assertStatus(417)
-
-    def testHeaderElements(self):
-        # Accept-* header elements should be sorted, with most preferred first.
-        h = [('Accept', 'audio/*; q=0.2, audio/basic')]
-        self.getPage('/headerelements/get_elements?headername=Accept', h)
-        self.assertStatus(200)
-        self.assertBody('audio/basic\n'
-                        'audio/*;q=0.2')
-
-        h = [
-            ('Accept',
-             'text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c')
-        ]
-        self.getPage('/headerelements/get_elements?headername=Accept', h)
-        self.assertStatus(200)
-        self.assertBody('text/x-c\n'
-                        'text/html\n'
-                        'text/x-dvi;q=0.8\n'
-                        'text/plain;q=0.5')
-
-        # Test that more specific media ranges get priority.
-        h = [('Accept', 'text/*, text/html, text/html;level=1, */*')]
-        self.getPage('/headerelements/get_elements?headername=Accept', h)
-        self.assertStatus(200)
-        self.assertBody('text/html;level=1\n'
-                        'text/html\n'
-                        'text/*\n'
-                        '*/*')
-
-        # Test Accept-Charset
-        h = [('Accept-Charset', 'iso-8859-5, unicode-1-1;q=0.8')]
-        self.getPage(
-            '/headerelements/get_elements?headername=Accept-Charset', h)
-        self.assertStatus('200 OK')
-        self.assertBody('iso-8859-5\n'
-                        'unicode-1-1;q=0.8')
-
-        # Test Accept-Encoding
-        h = [('Accept-Encoding', 'gzip;q=1.0, identity; q=0.5, *;q=0')]
-        self.getPage(
-            '/headerelements/get_elements?headername=Accept-Encoding', h)
-        self.assertStatus('200 OK')
-        self.assertBody('gzip;q=1.0\n'
-                        'identity;q=0.5\n'
-                        '*;q=0')
-
-        # Test Accept-Language
-        h = [('Accept-Language', 'da, en-gb;q=0.8, en;q=0.7')]
-        self.getPage(
-            '/headerelements/get_elements?headername=Accept-Language', h)
-        self.assertStatus('200 OK')
-        self.assertBody('da\n'
-                        'en-gb;q=0.8\n'
-                        'en;q=0.7')
-
-        # Test malformed header parsing. See
-        # https://github.com/cherrypy/cherrypy/issues/763.
-        self.getPage('/headerelements/get_elements?headername=Content-Type',
-                     # Note the illegal trailing ";"
-                     headers=[('Content-Type', 'text/html; charset=utf-8;')])
-        self.assertStatus(200)
-        self.assertBody('text/html;charset=utf-8')
-
-    def test_repeated_headers(self):
-        # Test that two request headers are collapsed into one.
-        # See https://github.com/cherrypy/cherrypy/issues/542.
-        self.getPage('/headers/Accept-Charset',
-                     headers=[('Accept-Charset', 'iso-8859-5'),
-                              ('Accept-Charset', 'unicode-1-1;q=0.8')])
-        self.assertBody('iso-8859-5, unicode-1-1;q=0.8')
-
-        # Tests that each header only appears once, regardless of case.
-        self.getPage('/headers/doubledheaders')
-        self.assertBody('double header test')
-        hnames = [name.title() for name, val in self.headers]
-        for key in ['Content-Length', 'Content-Type', 'Date',
-                    'Expires', 'Location', 'Server']:
-            self.assertEqual(hnames.count(key), 1, self.headers)
-
-    def test_encoded_headers(self):
-        # First, make sure the innards work like expected.
-        self.assertEqual(
-            httputil.decode_TEXT(ntou('=?utf-8?q?f=C3=BCr?=')), ntou('f\xfcr'))
-
-        if cherrypy.server.protocol_version == 'HTTP/1.1':
-            # Test RFC-2047-encoded request and response header values
-            u = ntou('\u212bngstr\xf6m', 'escape')
-            c = ntou('=E2=84=ABngstr=C3=B6m')
-            self.getPage('/headers/ifmatch',
-                         [('If-Match', ntou('=?utf-8?q?%s?=') % c)])
-            # The body should be utf-8 encoded.
-            self.assertBody(b'\xe2\x84\xabngstr\xc3\xb6m')
-            # But the Etag header should be RFC-2047 encoded (binary)
-            self.assertHeader('ETag', ntou('=?utf-8?b?4oSrbmdzdHLDtm0=?='))
-
-            # Test a *LONG* RFC-2047-encoded request and response header value
-            self.getPage('/headers/ifmatch',
-                         [('If-Match', ntou('=?utf-8?q?%s?=') % (c * 10))])
-            self.assertBody(b'\xe2\x84\xabngstr\xc3\xb6m' * 10)
-            # Note: this is different output for Python3, but it decodes fine.
-            etag = self.assertHeader(
-                'ETag',
-                '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
-                '4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
-                '4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
-                '4oSrbmdzdHLDtm0=?=')
-            self.assertEqual(httputil.decode_TEXT(etag), u * 10)
-
-    def test_header_presence(self):
-        # If we don't pass a Content-Type header, it should not be present
-        # in cherrypy.request.headers
-        self.getPage('/headers/Content-Type',
-                     headers=[])
-        self.assertStatus(500)
-
-        # If Content-Type is present in the request, it should be present in
-        # cherrypy.request.headers
-        self.getPage('/headers/Content-Type',
-                     headers=[('Content-type', 'application/json')])
-        self.assertBody('application/json')
-
-    def test_basic_HTTPMethods(self):
-        helper.webtest.methods_with_bodies = ('POST', 'PUT', 'PROPFIND',
-                                              'PATCH')
-
-        # Test that all defined HTTP methods work.
-        for m in defined_http_methods:
-            self.getPage('/method/', method=m)
-
-            # HEAD requests should not return any body.
-            if m == 'HEAD':
-                self.assertBody('')
-            elif m == 'TRACE':
-                # Some HTTP servers (like modpy) have their own TRACE support
-                self.assertEqual(self.body[:5], b'TRACE')
-            else:
-                self.assertBody(m)
-
-        # test of PATCH requests
-        # Request a PATCH method with a form-urlencoded body
-        self.getPage('/method/parameterized', method='PATCH',
-                     body='data=on+top+of+other+things')
-        self.assertBody('on top of other things')
-
-        # Request a PATCH method with a file body
-        b = 'one thing on top of another'
-        h = [('Content-Type', 'text/plain'),
-             ('Content-Length', str(len(b)))]
-        self.getPage('/method/request_body', headers=h, method='PATCH', body=b)
-        self.assertStatus(200)
-        self.assertBody(b)
-
-        # Request a PATCH method with a file body but no Content-Type.
-        # See https://github.com/cherrypy/cherrypy/issues/790.
-        b = b'one thing on top of another'
-        self.persistent = True
-        try:
-            conn = self.HTTP_CONN
-            conn.putrequest('PATCH', '/method/request_body', skip_host=True)
-            conn.putheader('Host', self.HOST)
-            conn.putheader('Content-Length', str(len(b)))
-            conn.endheaders()
-            conn.send(b)
-            response = conn.response_class(conn.sock, method='PATCH')
-            response.begin()
-            self.assertEqual(response.status, 200)
-            self.body = response.read()
-            self.assertBody(b)
-        finally:
-            self.persistent = False
-
-        # Request a PATCH method with no body whatsoever (not an empty one).
-        # See https://github.com/cherrypy/cherrypy/issues/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='PATCH')
-        self.assertStatus(411)
-
-        # HTTP PUT tests
-        # Request a PUT method with a form-urlencoded body
-        self.getPage('/method/parameterized', method='PUT',
-                     body='data=on+top+of+other+things')
-        self.assertBody('on top of other things')
-
-        # Request a PUT method with a file body
-        b = 'one thing on top of another'
-        h = [('Content-Type', 'text/plain'),
-             ('Content-Length', str(len(b)))]
-        self.getPage('/method/request_body', headers=h, method='PUT', body=b)
-        self.assertStatus(200)
-        self.assertBody(b)
-
-        # Request a PUT method with a file body but no Content-Type.
-        # See https://github.com/cherrypy/cherrypy/issues/790.
-        b = b'one thing on top of another'
-        self.persistent = True
-        try:
-            conn = self.HTTP_CONN
-            conn.putrequest('PUT', '/method/request_body', skip_host=True)
-            conn.putheader('Host', self.HOST)
-            conn.putheader('Content-Length', str(len(b)))
-            conn.endheaders()
-            conn.send(b)
-            response = conn.response_class(conn.sock, method='PUT')
-            response.begin()
-            self.assertEqual(response.status, 200)
-            self.body = response.read()
-            self.assertBody(b)
-        finally:
-            self.persistent = False
-
-        # Request a PUT method with no body whatsoever (not an empty one).
-        # See https://github.com/cherrypy/cherrypy/issues/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')
-        self.assertStatus(411)
-
-        # Request a custom method with a request body
-        b = ('<?xml version="1.0" encoding="utf-8" ?>\n\n'
-             '<propfind xmlns="DAV:"><prop><getlastmodified/>'
-             '</prop></propfind>')
-        h = [('Content-Type', 'text/xml'),
-             ('Content-Length', str(len(b)))]
-        self.getPage('/method/request_body', headers=h,
-                     method='PROPFIND', body=b)
-        self.assertStatus(200)
-        self.assertBody(b)
-
-        # Request a disallowed method
-        self.getPage('/method/', method='LINK')
-        self.assertStatus(405)
-
-        # Request an unknown method
-        self.getPage('/method/', method='SEARCH')
-        self.assertStatus(501)
-
-        # For method dispatchers: make sure that an HTTP method doesn't
-        # collide with a virtual path atom. If you build HTTP-method
-        # dispatching into the core, rewrite these handlers to use
-        # your dispatch idioms.
-        self.getPage('/divorce/get?ID=13')
-        self.assertBody('Divorce document 13: empty')
-        self.assertStatus(200)
-        self.getPage('/divorce/', method='GET')
-        self.assertBody('<h1>Choose your document</h1>\n<ul>\n</ul>')
-        self.assertStatus(200)
-
-    def test_CONNECT_method(self):
-        self.persistent = True
-        try:
-            conn = self.HTTP_CONN
-            conn.request('CONNECT', 'created.example.com:3128')
-            response = conn.response_class(conn.sock, method='CONNECT')
-            response.begin()
-            self.assertEqual(response.status, 204)
-        finally:
-            self.persistent = False
-
-        self.persistent = True
-        try:
-            conn = self.HTTP_CONN
-            conn.request('CONNECT', 'body.example.com:3128')
-            response = conn.response_class(conn.sock, method='CONNECT')
-            response.begin()
-            self.assertEqual(response.status, 200)
-            self.body = response.read()
-            self.assertBody(b'CONNECTed to /body.example.com:3128')
-        finally:
-            self.persistent = False
-
-    def test_CONNECT_method_invalid_authority(self):
-        for request_target in ['example.com', 'http://example.com:33',
-                               '/path/', 'path/', '/?q=f', '#f']:
-            self.persistent = True
-            try:
-                conn = self.HTTP_CONN
-                conn.request('CONNECT', request_target)
-                response = conn.response_class(conn.sock, method='CONNECT')
-                response.begin()
-                self.assertEqual(response.status, 400)
-                self.body = response.read()
-                self.assertBody(b'Invalid path in Request-URI: request-target '
-                                b'must match authority-form.')
-            finally:
-                self.persistent = False
-
-    def testEmptyThreadlocals(self):
-        results = []
-        for x in range(20):
-            self.getPage('/threadlocal/')
-            results.append(self.body)
-        self.assertEqual(results, [b'None'] * 20)
diff --git a/libraries/cherrypy/test/test_routes.py b/libraries/cherrypy/test/test_routes.py
deleted file mode 100644
index cc714765..00000000
--- a/libraries/cherrypy/test/test_routes.py
+++ /dev/null
@@ -1,80 +0,0 @@
-"""Test Routes dispatcher."""
-import os
-import importlib
-
-import pytest
-
-import cherrypy
-from cherrypy.test import helper
-
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-
-class RoutesDispatchTest(helper.CPWebCase):
-    """Routes dispatcher test suite."""
-
-    @staticmethod
-    def setup_server():
-        """Set up cherrypy test instance."""
-        try:
-            importlib.import_module('routes')
-        except ImportError:
-            pytest.skip('Install routes to test RoutesDispatcher code')
-
-        class Dummy:
-
-            def index(self):
-                return 'I said good day!'
-
-        class City:
-
-            def __init__(self, name):
-                self.name = name
-                self.population = 10000
-
-            @cherrypy.config(**{
-                'tools.response_headers.on': True,
-                'tools.response_headers.headers': [
-                    ('Content-Language', 'en-GB'),
-                ],
-            })
-            def index(self, **kwargs):
-                return 'Welcome to %s, pop. %s' % (self.name, self.population)
-
-            def update(self, **kwargs):
-                self.population = kwargs['pop']
-                return 'OK'
-
-        d = cherrypy.dispatch.RoutesDispatcher()
-        d.connect(action='index', name='hounslow', route='/hounslow',
-                  controller=City('Hounslow'))
-        d.connect(
-            name='surbiton', route='/surbiton', controller=City('Surbiton'),
-            action='index', conditions=dict(method=['GET']))
-        d.mapper.connect('/surbiton', controller='surbiton',
-                         action='update', conditions=dict(method=['POST']))
-        d.connect('main', ':action', controller=Dummy())
-
-        conf = {'/': {'request.dispatch': d}}
-        cherrypy.tree.mount(root=None, config=conf)
-
-    def test_Routes_Dispatch(self):
-        """Check that routes package based URI dispatching works correctly."""
-        self.getPage('/hounslow')
-        self.assertStatus('200 OK')
-        self.assertBody('Welcome to Hounslow, pop. 10000')
-
-        self.getPage('/foo')
-        self.assertStatus('404 Not Found')
-
-        self.getPage('/surbiton')
-        self.assertStatus('200 OK')
-        self.assertBody('Welcome to Surbiton, pop. 10000')
-
-        self.getPage('/surbiton', method='POST', body='pop=1327')
-        self.assertStatus('200 OK')
-        self.assertBody('OK')
-        self.getPage('/surbiton')
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Language', 'en-GB')
-        self.assertBody('Welcome to Surbiton, pop. 1327')
diff --git a/libraries/cherrypy/test/test_session.py b/libraries/cherrypy/test/test_session.py
deleted file mode 100644
index 0083c97c..00000000
--- a/libraries/cherrypy/test/test_session.py
+++ /dev/null
@@ -1,512 +0,0 @@
-import os
-import threading
-import time
-import socket
-import importlib
-
-from six.moves.http_client import HTTPConnection
-
-import pytest
-from path import Path
-
-import cherrypy
-from cherrypy._cpcompat import (
-    json_decode,
-    HTTPSConnection,
-)
-from cherrypy.lib import sessions
-from cherrypy.lib import reprconf
-from cherrypy.lib.httputil import response_codes
-from cherrypy.test import helper
-
-localDir = os.path.dirname(__file__)
-
-
-def http_methods_allowed(methods=['GET', 'HEAD']):
-    method = cherrypy.request.method.upper()
-    if method not in methods:
-        cherrypy.response.headers['Allow'] = ', '.join(methods)
-        raise cherrypy.HTTPError(405)
-
-
-cherrypy.tools.allow = cherrypy.Tool('on_start_resource', http_methods_allowed)
-
-
-def setup_server():
-
-    @cherrypy.config(**{
-        'tools.sessions.on': True,
-        'tools.sessions.storage_class': sessions.RamSession,
-        'tools.sessions.storage_path': localDir,
-        'tools.sessions.timeout': (1.0 / 60),
-        'tools.sessions.clean_freq': (1.0 / 60),
-    })
-    class Root:
-
-        @cherrypy.expose
-        def clear(self):
-            cherrypy.session.cache.clear()
-
-        @cherrypy.expose
-        def data(self):
-            cherrypy.session['aha'] = 'foo'
-            return repr(cherrypy.session._data)
-
-        @cherrypy.expose
-        def testGen(self):
-            counter = cherrypy.session.get('counter', 0) + 1
-            cherrypy.session['counter'] = counter
-            yield str(counter)
-
-        @cherrypy.expose
-        def testStr(self):
-            counter = cherrypy.session.get('counter', 0) + 1
-            cherrypy.session['counter'] = counter
-            return str(counter)
-
-        @cherrypy.expose
-        @cherrypy.config(**{'tools.sessions.on': False})
-        def set_session_cls(self, new_cls_name):
-            new_cls = reprconf.attributes(new_cls_name)
-            cfg = {'tools.sessions.storage_class': new_cls}
-            self.__class__._cp_config.update(cfg)
-            if hasattr(cherrypy, 'session'):
-                del cherrypy.session
-            if new_cls.clean_thread:
-                new_cls.clean_thread.stop()
-                new_cls.clean_thread.unsubscribe()
-                del new_cls.clean_thread
-
-        @cherrypy.expose
-        def index(self):
-            sess = cherrypy.session
-            c = sess.get('counter', 0) + 1
-            time.sleep(0.01)
-            sess['counter'] = c
-            return str(c)
-
-        @cherrypy.expose
-        def keyin(self, key):
-            return str(key in cherrypy.session)
-
-        @cherrypy.expose
-        def delete(self):
-            cherrypy.session.delete()
-            sessions.expire()
-            return 'done'
-
-        @cherrypy.expose
-        def delkey(self, key):
-            del cherrypy.session[key]
-            return 'OK'
-
-        @cherrypy.expose
-        def redir_target(self):
-            return self._cp_config['tools.sessions.storage_class'].__name__
-
-        @cherrypy.expose
-        def iredir(self):
-            raise cherrypy.InternalRedirect('/redir_target')
-
-        @cherrypy.expose
-        @cherrypy.config(**{
-            'tools.allow.on': True,
-            'tools.allow.methods': ['GET'],
-        })
-        def restricted(self):
-            return cherrypy.request.method
-
-        @cherrypy.expose
-        def regen(self):
-            cherrypy.tools.sessions.regenerate()
-            return 'logged in'
-
-        @cherrypy.expose
-        def length(self):
-            return str(len(cherrypy.session))
-
-        @cherrypy.expose
-        @cherrypy.config(**{
-            'tools.sessions.path': '/session_cookie',
-            'tools.sessions.name': 'temp',
-            'tools.sessions.persistent': False,
-        })
-        def session_cookie(self):
-            # Must load() to start the clean thread.
-            cherrypy.session.load()
-            return cherrypy.session.id
-
-    cherrypy.tree.mount(Root())
-
-
-class SessionTest(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def tearDown(self):
-        # Clean up sessions.
-        for fname in os.listdir(localDir):
-            if fname.startswith(sessions.FileSession.SESSION_PREFIX):
-                path = Path(localDir) / fname
-                path.remove_p()
-
-    @pytest.mark.xfail(reason='#1534')
-    def test_0_Session(self):
-        self.getPage('/set_session_cls/cherrypy.lib.sessions.RamSession')
-        self.getPage('/clear')
-
-        # Test that a normal request gets the same id in the cookies.
-        # Note: this wouldn't work if /data didn't load the session.
-        self.getPage('/data')
-        self.assertBody("{'aha': 'foo'}")
-        c = self.cookies[0]
-        self.getPage('/data', self.cookies)
-        self.assertEqual(self.cookies[0], c)
-
-        self.getPage('/testStr')
-        self.assertBody('1')
-        cookie_parts = dict([p.strip().split('=')
-                             for p in self.cookies[0][1].split(';')])
-        # Assert there is an 'expires' param
-        self.assertEqual(set(cookie_parts.keys()),
-                         set(['session_id', 'expires', 'Path']))
-        self.getPage('/testGen', self.cookies)
-        self.assertBody('2')
-        self.getPage('/testStr', self.cookies)
-        self.assertBody('3')
-        self.getPage('/data', self.cookies)
-        self.assertDictEqual(json_decode(self.body),
-                             {'counter': 3, 'aha': 'foo'})
-        self.getPage('/length', self.cookies)
-        self.assertBody('2')
-        self.getPage('/delkey?key=counter', self.cookies)
-        self.assertStatus(200)
-
-        self.getPage('/set_session_cls/cherrypy.lib.sessions.FileSession')
-        self.getPage('/testStr')
-        self.assertBody('1')
-        self.getPage('/testGen', self.cookies)
-        self.assertBody('2')
-        self.getPage('/testStr', self.cookies)
-        self.assertBody('3')
-        self.getPage('/delkey?key=counter', self.cookies)
-        self.assertStatus(200)
-
-        # Wait for the session.timeout (1 second)
-        time.sleep(2)
-        self.getPage('/')
-        self.assertBody('1')
-        self.getPage('/length', self.cookies)
-        self.assertBody('1')
-
-        # Test session __contains__
-        self.getPage('/keyin?key=counter', self.cookies)
-        self.assertBody('True')
-        cookieset1 = self.cookies
-
-        # Make a new session and test __len__ again
-        self.getPage('/')
-        self.getPage('/length', self.cookies)
-        self.assertBody('2')
-
-        # Test session delete
-        self.getPage('/delete', self.cookies)
-        self.assertBody('done')
-        self.getPage('/delete', cookieset1)
-        self.assertBody('done')
-
-        def f():
-            return [
-                x
-                for x in os.listdir(localDir)
-                if x.startswith('session-')
-            ]
-        self.assertEqual(f(), [])
-
-        # Wait for the cleanup thread to delete remaining session files
-        self.getPage('/')
-        self.assertNotEqual(f(), [])
-        time.sleep(2)
-        self.assertEqual(f(), [])
-
-    def test_1_Ram_Concurrency(self):
-        self.getPage('/set_session_cls/cherrypy.lib.sessions.RamSession')
-        self._test_Concurrency()
-
-    @pytest.mark.xfail(reason='#1306')
-    def test_2_File_Concurrency(self):
-        self.getPage('/set_session_cls/cherrypy.lib.sessions.FileSession')
-        self._test_Concurrency()
-
-    def _test_Concurrency(self):
-        client_thread_count = 5
-        request_count = 30
-
-        # Get initial cookie
-        self.getPage('/')
-        self.assertBody('1')
-        cookies = self.cookies
-
-        data_dict = {}
-        errors = []
-
-        def request(index):
-            if self.scheme == 'https':
-                c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
-            else:
-                c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
-            for i in range(request_count):
-                c.putrequest('GET', '/')
-                for k, v in cookies:
-                    c.putheader(k, v)
-                c.endheaders()
-                response = c.getresponse()
-                body = response.read()
-                if response.status != 200 or not body.isdigit():
-                    errors.append((response.status, body))
-                else:
-                    data_dict[index] = max(data_dict[index], int(body))
-                # Uncomment the following line to prove threads overlap.
-                # sys.stdout.write("%d " % index)
-
-        # Start <request_count> requests from each of
-        # <client_thread_count> concurrent clients
-        ts = []
-        for c in range(client_thread_count):
-            data_dict[c] = 0
-            t = threading.Thread(target=request, args=(c,))
-            ts.append(t)
-            t.start()
-
-        for t in ts:
-            t.join()
-
-        hitcount = max(data_dict.values())
-        expected = 1 + (client_thread_count * request_count)
-
-        for e in errors:
-            print(e)
-        self.assertEqual(hitcount, expected)
-
-    def test_3_Redirect(self):
-        # Start a new session
-        self.getPage('/testStr')
-        self.getPage('/iredir', self.cookies)
-        self.assertBody('FileSession')
-
-    def test_4_File_deletion(self):
-        # Start a new session
-        self.getPage('/testStr')
-        # Delete the session file manually and retry.
-        id = self.cookies[0][1].split(';', 1)[0].split('=', 1)[1]
-        path = os.path.join(localDir, 'session-' + id)
-        os.unlink(path)
-        self.getPage('/testStr', self.cookies)
-
-    def test_5_Error_paths(self):
-        self.getPage('/unknown/page')
-        self.assertErrorPage(404, "The path '/unknown/page' was not found.")
-
-        # Note: this path is *not* the same as above. The above
-        # takes a normal route through the session code; this one
-        # skips the session code's before_handler and only calls
-        # before_finalize (save) and on_end (close). So the session
-        # code has to survive calling save/close without init.
-        self.getPage('/restricted', self.cookies, method='POST')
-        self.assertErrorPage(405, response_codes[405][1])
-
-    def test_6_regenerate(self):
-        self.getPage('/testStr')
-        # grab the cookie ID
-        id1 = self.cookies[0][1].split(';', 1)[0].split('=', 1)[1]
-        self.getPage('/regen')
-        self.assertBody('logged in')
-        id2 = self.cookies[0][1].split(';', 1)[0].split('=', 1)[1]
-        self.assertNotEqual(id1, id2)
-
-        self.getPage('/testStr')
-        # grab the cookie ID
-        id1 = self.cookies[0][1].split(';', 1)[0].split('=', 1)[1]
-        self.getPage('/testStr',
-                     headers=[
-                         ('Cookie',
-                          'session_id=maliciousid; '
-                          'expires=Sat, 27 Oct 2017 04:18:28 GMT; Path=/;')])
-        id2 = self.cookies[0][1].split(';', 1)[0].split('=', 1)[1]
-        self.assertNotEqual(id1, id2)
-        self.assertNotEqual(id2, 'maliciousid')
-
-    def test_7_session_cookies(self):
-        self.getPage('/set_session_cls/cherrypy.lib.sessions.RamSession')
-        self.getPage('/clear')
-        self.getPage('/session_cookie')
-        # grab the cookie ID
-        cookie_parts = dict([p.strip().split('=')
-                            for p in self.cookies[0][1].split(';')])
-        # Assert there is no 'expires' param
-        self.assertEqual(set(cookie_parts.keys()), set(['temp', 'Path']))
-        id1 = cookie_parts['temp']
-        self.assertEqual(list(sessions.RamSession.cache), [id1])
-
-        # Send another request in the same "browser session".
-        self.getPage('/session_cookie', self.cookies)
-        cookie_parts = dict([p.strip().split('=')
-                            for p in self.cookies[0][1].split(';')])
-        # Assert there is no 'expires' param
-        self.assertEqual(set(cookie_parts.keys()), set(['temp', 'Path']))
-        self.assertBody(id1)
-        self.assertEqual(list(sessions.RamSession.cache), [id1])
-
-        # Simulate a browser close by just not sending the cookies
-        self.getPage('/session_cookie')
-        # grab the cookie ID
-        cookie_parts = dict([p.strip().split('=')
-                            for p in self.cookies[0][1].split(';')])
-        # Assert there is no 'expires' param
-        self.assertEqual(set(cookie_parts.keys()), set(['temp', 'Path']))
-        # Assert a new id has been generated...
-        id2 = cookie_parts['temp']
-        self.assertNotEqual(id1, id2)
-        self.assertEqual(set(sessions.RamSession.cache.keys()),
-                         set([id1, id2]))
-
-        # Wait for the session.timeout on both sessions
-        time.sleep(2.5)
-        cache = list(sessions.RamSession.cache)
-        if cache:
-            if cache == [id2]:
-                self.fail('The second session did not time out.')
-            else:
-                self.fail('Unknown session id in cache: %r', cache)
-
-    def test_8_Ram_Cleanup(self):
-        def lock():
-            s1 = sessions.RamSession()
-            s1.acquire_lock()
-            time.sleep(1)
-            s1.release_lock()
-
-        t = threading.Thread(target=lock)
-        t.start()
-        start = time.time()
-        while not sessions.RamSession.locks and time.time() - start < 5:
-            time.sleep(0.01)
-        assert len(sessions.RamSession.locks) == 1, 'Lock not acquired'
-        s2 = sessions.RamSession()
-        s2.clean_up()
-        msg = 'Clean up should not remove active lock'
-        assert len(sessions.RamSession.locks) == 1, msg
-        t.join()
-
-
-try:
-    importlib.import_module('memcache')
-
-    host, port = '127.0.0.1', 11211
-    for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
-                                  socket.SOCK_STREAM):
-        af, socktype, proto, canonname, sa = res
-        s = None
-        try:
-            s = socket.socket(af, socktype, proto)
-            # See http://groups.google.com/group/cherrypy-users/
-            #        browse_frm/thread/bbfe5eb39c904fe0
-            s.settimeout(1.0)
-            s.connect((host, port))
-            s.close()
-        except socket.error:
-            if s:
-                s.close()
-            raise
-        break
-except (ImportError, socket.error):
-    class MemcachedSessionTest(helper.CPWebCase):
-        setup_server = staticmethod(setup_server)
-
-        def test(self):
-            return self.skip('memcached not reachable ')
-else:
-    class MemcachedSessionTest(helper.CPWebCase):
-        setup_server = staticmethod(setup_server)
-
-        def test_0_Session(self):
-            self.getPage('/set_session_cls/cherrypy.Sessions.MemcachedSession')
-
-            self.getPage('/testStr')
-            self.assertBody('1')
-            self.getPage('/testGen', self.cookies)
-            self.assertBody('2')
-            self.getPage('/testStr', self.cookies)
-            self.assertBody('3')
-            self.getPage('/length', self.cookies)
-            self.assertErrorPage(500)
-            self.assertInBody('NotImplementedError')
-            self.getPage('/delkey?key=counter', self.cookies)
-            self.assertStatus(200)
-
-            # Wait for the session.timeout (1 second)
-            time.sleep(1.25)
-            self.getPage('/')
-            self.assertBody('1')
-
-            # Test session __contains__
-            self.getPage('/keyin?key=counter', self.cookies)
-            self.assertBody('True')
-
-            # Test session delete
-            self.getPage('/delete', self.cookies)
-            self.assertBody('done')
-
-        def test_1_Concurrency(self):
-            client_thread_count = 5
-            request_count = 30
-
-            # Get initial cookie
-            self.getPage('/')
-            self.assertBody('1')
-            cookies = self.cookies
-
-            data_dict = {}
-
-            def request(index):
-                for i in range(request_count):
-                    self.getPage('/', cookies)
-                    # Uncomment the following line to prove threads overlap.
-                    # sys.stdout.write("%d " % index)
-                if not self.body.isdigit():
-                    self.fail(self.body)
-                data_dict[index] = int(self.body)
-
-            # Start <request_count> concurrent requests from
-            # each of <client_thread_count> clients
-            ts = []
-            for c in range(client_thread_count):
-                data_dict[c] = 0
-                t = threading.Thread(target=request, args=(c,))
-                ts.append(t)
-                t.start()
-
-            for t in ts:
-                t.join()
-
-            hitcount = max(data_dict.values())
-            expected = 1 + (client_thread_count * request_count)
-            self.assertEqual(hitcount, expected)
-
-        def test_3_Redirect(self):
-            # Start a new session
-            self.getPage('/testStr')
-            self.getPage('/iredir', self.cookies)
-            self.assertBody('memcached')
-
-        def test_5_Error_paths(self):
-            self.getPage('/unknown/page')
-            self.assertErrorPage(
-                404, "The path '/unknown/page' was not found.")
-
-            # Note: this path is *not* the same as above. The above
-            # takes a normal route through the session code; this one
-            # skips the session code's before_handler and only calls
-            # before_finalize (save) and on_end (close). So the session
-            # code has to survive calling save/close without init.
-            self.getPage('/restricted', self.cookies, method='POST')
-            self.assertErrorPage(405, response_codes[405][1])
diff --git a/libraries/cherrypy/test/test_sessionauthenticate.py b/libraries/cherrypy/test/test_sessionauthenticate.py
deleted file mode 100644
index 63053fcb..00000000
--- a/libraries/cherrypy/test/test_sessionauthenticate.py
+++ /dev/null
@@ -1,61 +0,0 @@
-import cherrypy
-from cherrypy.test import helper
-
-
-class SessionAuthenticateTest(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-
-        def check(username, password):
-            # Dummy check_username_and_password function
-            if username != 'test' or password != 'password':
-                return 'Wrong login/password'
-
-        def augment_params():
-            # A simple tool to add some things to request.params
-            # This is to check to make sure that session_auth can handle
-            # request params (ticket #780)
-            cherrypy.request.params['test'] = 'test'
-
-        cherrypy.tools.augment_params = cherrypy.Tool(
-            'before_handler', augment_params, None, priority=30)
-
-        class Test:
-
-            _cp_config = {
-                'tools.sessions.on': True,
-                'tools.session_auth.on': True,
-                'tools.session_auth.check_username_and_password': check,
-                'tools.augment_params.on': True,
-            }
-
-            @cherrypy.expose
-            def index(self, **kwargs):
-                return 'Hi %s, you are logged in' % cherrypy.request.login
-
-        cherrypy.tree.mount(Test())
-
-    def testSessionAuthenticate(self):
-        # request a page and check for login form
-        self.getPage('/')
-        self.assertInBody('<form method="post" action="do_login">')
-
-        # setup credentials
-        login_body = 'username=test&password=password&from_page=/'
-
-        # attempt a login
-        self.getPage('/do_login', method='POST', body=login_body)
-        self.assertStatus((302, 303))
-
-        # get the page now that we are logged in
-        self.getPage('/', self.cookies)
-        self.assertBody('Hi test, you are logged in')
-
-        # do a logout
-        self.getPage('/do_logout', self.cookies, method='POST')
-        self.assertStatus((302, 303))
-
-        # verify we are logged out
-        self.getPage('/', self.cookies)
-        self.assertInBody('<form method="post" action="do_login">')
diff --git a/libraries/cherrypy/test/test_states.py b/libraries/cherrypy/test/test_states.py
deleted file mode 100644
index 606ca4f6..00000000
--- a/libraries/cherrypy/test/test_states.py
+++ /dev/null
@@ -1,473 +0,0 @@
-import os
-import signal
-import time
-import unittest
-import warnings
-
-from six.moves.http_client import BadStatusLine
-
-import pytest
-import portend
-
-import cherrypy
-import cherrypy.process.servers
-from cherrypy.test import helper
-
-engine = cherrypy.engine
-thisdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-
-class Dependency:
-
-    def __init__(self, bus):
-        self.bus = bus
-        self.running = False
-        self.startcount = 0
-        self.gracecount = 0
-        self.threads = {}
-
-    def subscribe(self):
-        self.bus.subscribe('start', self.start)
-        self.bus.subscribe('stop', self.stop)
-        self.bus.subscribe('graceful', self.graceful)
-        self.bus.subscribe('start_thread', self.startthread)
-        self.bus.subscribe('stop_thread', self.stopthread)
-
-    def start(self):
-        self.running = True
-        self.startcount += 1
-
-    def stop(self):
-        self.running = False
-
-    def graceful(self):
-        self.gracecount += 1
-
-    def startthread(self, thread_id):
-        self.threads[thread_id] = None
-
-    def stopthread(self, thread_id):
-        del self.threads[thread_id]
-
-
-db_connection = Dependency(engine)
-
-
-def setup_server():
-    class Root:
-
-        @cherrypy.expose
-        def index(self):
-            return 'Hello World'
-
-        @cherrypy.expose
-        def ctrlc(self):
-            raise KeyboardInterrupt()
-
-        @cherrypy.expose
-        def graceful(self):
-            engine.graceful()
-            return 'app was (gracefully) restarted succesfully'
-
-    cherrypy.tree.mount(Root())
-    cherrypy.config.update({
-        'environment': 'test_suite',
-    })
-
-    db_connection.subscribe()
-
-# ------------ Enough helpers. Time for real live test cases. ------------ #
-
-
-class ServerStateTests(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def setUp(self):
-        cherrypy.server.socket_timeout = 0.1
-        self.do_gc_test = False
-
-    def test_0_NormalStateFlow(self):
-        engine.stop()
-        # Our db_connection should not be running
-        self.assertEqual(db_connection.running, False)
-        self.assertEqual(db_connection.startcount, 1)
-        self.assertEqual(len(db_connection.threads), 0)
-
-        # Test server start
-        engine.start()
-        self.assertEqual(engine.state, engine.states.STARTED)
-
-        host = cherrypy.server.socket_host
-        port = cherrypy.server.socket_port
-        portend.occupied(host, port, timeout=0.1)
-
-        # The db_connection should be running now
-        self.assertEqual(db_connection.running, True)
-        self.assertEqual(db_connection.startcount, 2)
-        self.assertEqual(len(db_connection.threads), 0)
-
-        self.getPage('/')
-        self.assertBody('Hello World')
-        self.assertEqual(len(db_connection.threads), 1)
-
-        # Test engine stop. This will also stop the HTTP server.
-        engine.stop()
-        self.assertEqual(engine.state, engine.states.STOPPED)
-
-        # Verify that our custom stop function was called
-        self.assertEqual(db_connection.running, False)
-        self.assertEqual(len(db_connection.threads), 0)
-
-        # Block the main thread now and verify that exit() works.
-        def exittest():
-            self.getPage('/')
-            self.assertBody('Hello World')
-            engine.exit()
-        cherrypy.server.start()
-        engine.start_with_callback(exittest)
-        engine.block()
-        self.assertEqual(engine.state, engine.states.EXITING)
-
-    def test_1_Restart(self):
-        cherrypy.server.start()
-        engine.start()
-
-        # The db_connection should be running now
-        self.assertEqual(db_connection.running, True)
-        grace = db_connection.gracecount
-
-        self.getPage('/')
-        self.assertBody('Hello World')
-        self.assertEqual(len(db_connection.threads), 1)
-
-        # Test server restart from this thread
-        engine.graceful()
-        self.assertEqual(engine.state, engine.states.STARTED)
-        self.getPage('/')
-        self.assertBody('Hello World')
-        self.assertEqual(db_connection.running, True)
-        self.assertEqual(db_connection.gracecount, grace + 1)
-        self.assertEqual(len(db_connection.threads), 1)
-
-        # Test server restart from inside a page handler
-        self.getPage('/graceful')
-        self.assertEqual(engine.state, engine.states.STARTED)
-        self.assertBody('app was (gracefully) restarted succesfully')
-        self.assertEqual(db_connection.running, True)
-        self.assertEqual(db_connection.gracecount, grace + 2)
-        # Since we are requesting synchronously, is only one thread used?
-        # Note that the "/graceful" request has been flushed.
-        self.assertEqual(len(db_connection.threads), 0)
-
-        engine.stop()
-        self.assertEqual(engine.state, engine.states.STOPPED)
-        self.assertEqual(db_connection.running, False)
-        self.assertEqual(len(db_connection.threads), 0)
-
-    def test_2_KeyboardInterrupt(self):
-        # Raise a keyboard interrupt in the HTTP server's main thread.
-        # We must start the server in this, the main thread
-        engine.start()
-        cherrypy.server.start()
-
-        self.persistent = True
-        try:
-            # Make the first request and assert there's no "Connection: close".
-            self.getPage('/')
-            self.assertStatus('200 OK')
-            self.assertBody('Hello World')
-            self.assertNoHeader('Connection')
-
-            cherrypy.server.httpserver.interrupt = KeyboardInterrupt
-            engine.block()
-
-            self.assertEqual(db_connection.running, False)
-            self.assertEqual(len(db_connection.threads), 0)
-            self.assertEqual(engine.state, engine.states.EXITING)
-        finally:
-            self.persistent = False
-
-        # Raise a keyboard interrupt in a page handler; on multithreaded
-        # servers, this should occur in one of the worker threads.
-        # This should raise a BadStatusLine error, since the worker
-        # thread will just die without writing a response.
-        engine.start()
-        cherrypy.server.start()
-        # From python3.5 a new exception is retuned when the connection
-        # ends abruptly:
-        #   http.client.RemoteDisconnected
-        # RemoteDisconnected is a subclass of:
-        #   (ConnectionResetError, http.client.BadStatusLine)
-        # and ConnectionResetError is an indirect subclass of:
-        #    OSError
-        # From python 3.3 an up socket.error is an alias to OSError
-        # following PEP-3151, therefore http.client.RemoteDisconnected
-        # is considered a socket.error.
-        #
-        # raise_subcls specifies the classes that are not going
-        # to be considered as a socket.error for the retries.
-        # Given that RemoteDisconnected is part BadStatusLine
-        # we can use the same call for all py3 versions without
-        # sideffects. python < 3.5 will raise directly BadStatusLine
-        # which is not a subclass for socket.error/OSError.
-        try:
-            self.getPage('/ctrlc', raise_subcls=BadStatusLine)
-        except BadStatusLine:
-            pass
-        else:
-            print(self.body)
-            self.fail('AssertionError: BadStatusLine not raised')
-
-        engine.block()
-        self.assertEqual(db_connection.running, False)
-        self.assertEqual(len(db_connection.threads), 0)
-
-    @pytest.mark.xfail(
-        'sys.platform == "Darwin" '
-        'and sys.version_info > (3, 7) '
-        'and os.environ["TRAVIS"]',
-        reason='https://github.com/cherrypy/cherrypy/issues/1693',
-    )
-    def test_4_Autoreload(self):
-        # If test_3 has not been executed, the server won't be stopped,
-        # so we'll have to do it.
-        if engine.state != engine.states.EXITING:
-            engine.exit()
-
-        # Start the demo script in a new process
-        p = helper.CPProcess(ssl=(self.scheme.lower() == 'https'))
-        p.write_conf(extra='test_case_name: "test_4_Autoreload"')
-        p.start(imports='cherrypy.test._test_states_demo')
-        try:
-            self.getPage('/start')
-            start = float(self.body)
-
-            # Give the autoreloader time to cache the file time.
-            time.sleep(2)
-
-            # Touch the file
-            os.utime(os.path.join(thisdir, '_test_states_demo.py'), None)
-
-            # Give the autoreloader time to re-exec the process
-            time.sleep(2)
-            host = cherrypy.server.socket_host
-            port = cherrypy.server.socket_port
-            portend.occupied(host, port, timeout=5)
-
-            self.getPage('/start')
-            if not (float(self.body) > start):
-                raise AssertionError('start time %s not greater than %s' %
-                                     (float(self.body), start))
-        finally:
-            # Shut down the spawned process
-            self.getPage('/exit')
-        p.join()
-
-    def test_5_Start_Error(self):
-        # If test_3 has not been executed, the server won't be stopped,
-        # so we'll have to do it.
-        if engine.state != engine.states.EXITING:
-            engine.exit()
-
-        # If a process errors during start, it should stop the engine
-        # and exit with a non-zero exit code.
-        p = helper.CPProcess(ssl=(self.scheme.lower() == 'https'),
-                             wait=True)
-        p.write_conf(
-            extra="""starterror: True
-test_case_name: "test_5_Start_Error"
-"""
-        )
-        p.start(imports='cherrypy.test._test_states_demo')
-        if p.exit_code == 0:
-            self.fail('Process failed to return nonzero exit code.')
-
-
-class PluginTests(helper.CPWebCase):
-
-    def test_daemonize(self):
-        if os.name not in ['posix']:
-            return self.skip('skipped (not on posix) ')
-        self.HOST = '127.0.0.1'
-        self.PORT = 8081
-        # Spawn the process and wait, when this returns, the original process
-        # is finished.  If it daemonized properly, we should still be able
-        # to access pages.
-        p = helper.CPProcess(ssl=(self.scheme.lower() == 'https'),
-                             wait=True, daemonize=True,
-                             socket_host='127.0.0.1',
-                             socket_port=8081)
-        p.write_conf(
-            extra='test_case_name: "test_daemonize"')
-        p.start(imports='cherrypy.test._test_states_demo')
-        try:
-            # Just get the pid of the daemonization process.
-            self.getPage('/pid')
-            self.assertStatus(200)
-            page_pid = int(self.body)
-            self.assertEqual(page_pid, p.get_pid())
-        finally:
-            # Shut down the spawned process
-            self.getPage('/exit')
-        p.join()
-
-        # Wait until here to test the exit code because we want to ensure
-        # that we wait for the daemon to finish running before we fail.
-        if p.exit_code != 0:
-            self.fail('Daemonized parent process failed to exit cleanly.')
-
-
-class SignalHandlingTests(helper.CPWebCase):
-
-    def test_SIGHUP_tty(self):
-        # When not daemonized, SIGHUP should shut down the server.
-        try:
-            from signal import SIGHUP
-        except ImportError:
-            return self.skip('skipped (no SIGHUP) ')
-
-        # Spawn the process.
-        p = helper.CPProcess(ssl=(self.scheme.lower() == 'https'))
-        p.write_conf(
-            extra='test_case_name: "test_SIGHUP_tty"')
-        p.start(imports='cherrypy.test._test_states_demo')
-        # Send a SIGHUP
-        os.kill(p.get_pid(), SIGHUP)
-        # This might hang if things aren't working right, but meh.
-        p.join()
-
-    def test_SIGHUP_daemonized(self):
-        # When daemonized, SIGHUP should restart the server.
-        try:
-            from signal import SIGHUP
-        except ImportError:
-            return self.skip('skipped (no SIGHUP) ')
-
-        if os.name not in ['posix']:
-            return self.skip('skipped (not on posix) ')
-
-        # Spawn the process and wait, when this returns, the original process
-        # is finished.  If it daemonized properly, we should still be able
-        # to access pages.
-        p = helper.CPProcess(ssl=(self.scheme.lower() == 'https'),
-                             wait=True, daemonize=True)
-        p.write_conf(
-            extra='test_case_name: "test_SIGHUP_daemonized"')
-        p.start(imports='cherrypy.test._test_states_demo')
-
-        pid = p.get_pid()
-        try:
-            # Send a SIGHUP
-            os.kill(pid, SIGHUP)
-            # Give the server some time to restart
-            time.sleep(2)
-            self.getPage('/pid')
-            self.assertStatus(200)
-            new_pid = int(self.body)
-            self.assertNotEqual(new_pid, pid)
-        finally:
-            # Shut down the spawned process
-            self.getPage('/exit')
-        p.join()
-
-    def _require_signal_and_kill(self, signal_name):
-        if not hasattr(signal, signal_name):
-            self.skip('skipped (no %(signal_name)s)' % vars())
-
-        if not hasattr(os, 'kill'):
-            self.skip('skipped (no os.kill)')
-
-    def test_SIGTERM(self):
-        'SIGTERM should shut down the server whether daemonized or not.'
-        self._require_signal_and_kill('SIGTERM')
-
-        # Spawn a normal, undaemonized process.
-        p = helper.CPProcess(ssl=(self.scheme.lower() == 'https'))
-        p.write_conf(
-            extra='test_case_name: "test_SIGTERM"')
-        p.start(imports='cherrypy.test._test_states_demo')
-        # Send a SIGTERM
-        os.kill(p.get_pid(), signal.SIGTERM)
-        # This might hang if things aren't working right, but meh.
-        p.join()
-
-        if os.name in ['posix']:
-            # Spawn a daemonized process and test again.
-            p = helper.CPProcess(ssl=(self.scheme.lower() == 'https'),
-                                 wait=True, daemonize=True)
-            p.write_conf(
-                extra='test_case_name: "test_SIGTERM_2"')
-            p.start(imports='cherrypy.test._test_states_demo')
-            # Send a SIGTERM
-            os.kill(p.get_pid(), signal.SIGTERM)
-            # This might hang if things aren't working right, but meh.
-            p.join()
-
-    def test_signal_handler_unsubscribe(self):
-        self._require_signal_and_kill('SIGTERM')
-
-        # Although Windows has `os.kill` and SIGTERM is defined, the
-        #  platform does not implement signals and sending SIGTERM
-        #  will result in a forced termination of the process.
-        #  Therefore, this test is not suitable for Windows.
-        if os.name == 'nt':
-            self.skip('SIGTERM not available')
-
-        # Spawn a normal, undaemonized process.
-        p = helper.CPProcess(ssl=(self.scheme.lower() == 'https'))
-        p.write_conf(
-            extra="""unsubsig: True
-test_case_name: "test_signal_handler_unsubscribe"
-""")
-        p.start(imports='cherrypy.test._test_states_demo')
-        # Ask the process to quit
-        os.kill(p.get_pid(), signal.SIGTERM)
-        # This might hang if things aren't working right, but meh.
-        p.join()
-
-        # Assert the old handler ran.
-        log_lines = list(open(p.error_log, 'rb'))
-        assert any(
-            line.endswith(b'I am an old SIGTERM handler.\n')
-            for line in log_lines
-        )
-
-
-class WaitTests(unittest.TestCase):
-
-    def test_safe_wait_INADDR_ANY(self):
-        """
-        Wait on INADDR_ANY should not raise IOError
-
-        In cases where the loopback interface does not exist, CherryPy cannot
-        effectively determine if a port binding to INADDR_ANY was effected.
-        In this situation, CherryPy should assume that it failed to detect
-        the binding (not that the binding failed) and only warn that it could
-        not verify it.
-        """
-        # At such a time that CherryPy can reliably determine one or more
-        #  viable IP addresses of the host, this test may be removed.
-
-        # Simulate the behavior we observe when no loopback interface is
-        #  present by: finding a port that's not occupied, then wait on it.
-
-        free_port = portend.find_available_local_port()
-
-        servers = cherrypy.process.servers
-
-        inaddr_any = '0.0.0.0'
-
-        # Wait on the free port that's unbound
-        with warnings.catch_warnings(record=True) as w:
-            with servers._safe_wait(inaddr_any, free_port):
-                portend.occupied(inaddr_any, free_port, timeout=1)
-            self.assertEqual(len(w), 1)
-            self.assertTrue(isinstance(w[0], warnings.WarningMessage))
-            self.assertTrue(
-                'Unable to verify that the server is bound on ' in str(w[0]))
-
-        # The wait should still raise an IO error if INADDR_ANY was
-        #  not supplied.
-        with pytest.raises(IOError):
-            with servers._safe_wait('127.0.0.1', free_port):
-                portend.occupied('127.0.0.1', free_port, timeout=1)
diff --git a/libraries/cherrypy/test/test_static.py b/libraries/cherrypy/test/test_static.py
deleted file mode 100644
index 5dc5a144..00000000
--- a/libraries/cherrypy/test/test_static.py
+++ /dev/null
@@ -1,434 +0,0 @@
-# -*- coding: utf-8 -*-
-import contextlib
-import io
-import os
-import sys
-import platform
-import tempfile
-
-from six import text_type as str
-from six.moves import urllib
-from six.moves.http_client import HTTPConnection
-
-import pytest
-import py.path
-
-import cherrypy
-from cherrypy.lib import static
-from cherrypy._cpcompat import HTTPSConnection, ntou, tonative
-from cherrypy.test import helper
-
-
-@pytest.fixture
-def unicode_filesystem(tmpdir):
-    filename = tmpdir / ntou('☃', 'utf-8')
-    tmpl = 'File system encoding ({encoding}) cannot support unicode filenames'
-    msg = tmpl.format(encoding=sys.getfilesystemencoding())
-    try:
-        io.open(str(filename), 'w').close()
-    except UnicodeEncodeError:
-        pytest.skip(msg)
-
-
-def ensure_unicode_filesystem():
-    """
-    TODO: replace with simply pytest fixtures once webtest.TestCase
-    no longer implies unittest.
-    """
-    tmpdir = py.path.local(tempfile.mkdtemp())
-    try:
-        unicode_filesystem(tmpdir)
-    finally:
-        tmpdir.remove()
-
-
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-has_space_filepath = os.path.join(curdir, 'static', 'has space.html')
-bigfile_filepath = os.path.join(curdir, 'static', 'bigfile.log')
-
-# The file size needs to be big enough such that half the size of it
-# won't be socket-buffered (or server-buffered) all in one go. See
-# test_file_stream.
-MB = 2 ** 20
-BIGFILE_SIZE = 32 * MB
-
-
-class StaticTest(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-        if not os.path.exists(has_space_filepath):
-            with open(has_space_filepath, 'wb') as f:
-                f.write(b'Hello, world\r\n')
-        needs_bigfile = (
-            not os.path.exists(bigfile_filepath) or
-            os.path.getsize(bigfile_filepath) != BIGFILE_SIZE
-        )
-        if needs_bigfile:
-            with open(bigfile_filepath, 'wb') as f:
-                f.write(b'x' * BIGFILE_SIZE)
-
-        class Root:
-
-            @cherrypy.expose
-            @cherrypy.config(**{'response.stream': True})
-            def bigfile(self):
-                self.f = static.serve_file(bigfile_filepath)
-                return self.f
-
-            @cherrypy.expose
-            def tell(self):
-                if self.f.input.closed:
-                    return ''
-                return repr(self.f.input.tell()).rstrip('L')
-
-            @cherrypy.expose
-            def fileobj(self):
-                f = open(os.path.join(curdir, 'style.css'), 'rb')
-                return static.serve_fileobj(f, content_type='text/css')
-
-            @cherrypy.expose
-            def bytesio(self):
-                f = io.BytesIO(b'Fee\nfie\nfo\nfum')
-                return static.serve_fileobj(f, content_type='text/plain')
-
-        class Static:
-
-            @cherrypy.expose
-            def index(self):
-                return 'You want the Baron? You can have the Baron!'
-
-            @cherrypy.expose
-            def dynamic(self):
-                return 'This is a DYNAMIC page'
-
-        root = Root()
-        root.static = Static()
-
-        rootconf = {
-            '/static': {
-                'tools.staticdir.on': True,
-                'tools.staticdir.dir': 'static',
-                'tools.staticdir.root': curdir,
-            },
-            '/static-long': {
-                'tools.staticdir.on': True,
-                'tools.staticdir.dir': r'\\?\%s' % curdir,
-            },
-            '/style.css': {
-                'tools.staticfile.on': True,
-                'tools.staticfile.filename': os.path.join(curdir, 'style.css'),
-            },
-            '/docroot': {
-                'tools.staticdir.on': True,
-                'tools.staticdir.root': curdir,
-                'tools.staticdir.dir': 'static',
-                'tools.staticdir.index': 'index.html',
-            },
-            '/error': {
-                'tools.staticdir.on': True,
-                'request.show_tracebacks': True,
-            },
-            '/404test': {
-                'tools.staticdir.on': True,
-                'tools.staticdir.root': curdir,
-                'tools.staticdir.dir': 'static',
-                'error_page.404': error_page_404,
-            }
-        }
-        rootApp = cherrypy.Application(root)
-        rootApp.merge(rootconf)
-
-        test_app_conf = {
-            '/test': {
-                'tools.staticdir.index': 'index.html',
-                'tools.staticdir.on': True,
-                'tools.staticdir.root': curdir,
-                'tools.staticdir.dir': 'static',
-            },
-        }
-        testApp = cherrypy.Application(Static())
-        testApp.merge(test_app_conf)
-
-        vhost = cherrypy._cpwsgi.VirtualHost(rootApp, {'virt.net': testApp})
-        cherrypy.tree.graft(vhost)
-
-    @staticmethod
-    def teardown_server():
-        for f in (has_space_filepath, bigfile_filepath):
-            if os.path.exists(f):
-                try:
-                    os.unlink(f)
-                except Exception:
-                    pass
-
-    def test_static(self):
-        self.getPage('/static/index.html')
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html')
-        self.assertBody('Hello, world\r\n')
-
-        # Using a staticdir.root value in a subdir...
-        self.getPage('/docroot/index.html')
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html')
-        self.assertBody('Hello, world\r\n')
-
-        # Check a filename with spaces in it
-        self.getPage('/static/has%20space.html')
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html')
-        self.assertBody('Hello, world\r\n')
-
-        self.getPage('/style.css')
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/css')
-        # Note: The body should be exactly 'Dummy stylesheet\n', but
-        #   unfortunately some tools such as WinZip sometimes turn \n
-        #   into \r\n on Windows when extracting the CherryPy tarball so
-        #   we just check the content
-        self.assertMatchesBody('^Dummy stylesheet')
-
-    @pytest.mark.skipif(platform.system() != 'Windows', reason='Windows only')
-    def test_static_longpath(self):
-        """Test serving of a file in subdir of a Windows long-path
-        staticdir."""
-        self.getPage('/static-long/static/index.html')
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html')
-        self.assertBody('Hello, world\r\n')
-
-    def test_fallthrough(self):
-        # Test that NotFound will then try dynamic handlers (see [878]).
-        self.getPage('/static/dynamic')
-        self.assertBody('This is a DYNAMIC page')
-
-        # Check a directory via fall-through to dynamic handler.
-        self.getPage('/static/')
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html;charset=utf-8')
-        self.assertBody('You want the Baron? You can have the Baron!')
-
-    def test_index(self):
-        # Check a directory via "staticdir.index".
-        self.getPage('/docroot/')
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html')
-        self.assertBody('Hello, world\r\n')
-        # The same page should be returned even if redirected.
-        self.getPage('/docroot')
-        self.assertStatus(301)
-        self.assertHeader('Location', '%s/docroot/' % self.base())
-        self.assertMatchesBody(
-            "This resource .* <a href=(['\"])%s/docroot/\\1>"
-            '%s/docroot/</a>.'
-            % (self.base(), self.base())
-        )
-
-    def test_config_errors(self):
-        # Check that we get an error if no .file or .dir
-        self.getPage('/error/thing.html')
-        self.assertErrorPage(500)
-        if sys.version_info >= (3, 3):
-            errmsg = (
-                r'TypeError: staticdir\(\) missing 2 '
-                'required positional arguments'
-            )
-        else:
-            errmsg = (
-                r'TypeError: staticdir\(\) takes at least 2 '
-                r'(positional )?arguments \(0 given\)'
-            )
-        self.assertMatchesBody(errmsg.encode('ascii'))
-
-    def test_security(self):
-        # Test up-level security
-        self.getPage('/static/../../test/style.css')
-        self.assertStatus((400, 403))
-
-    def test_modif(self):
-        # Test modified-since on a reasonably-large file
-        self.getPage('/static/dirback.jpg')
-        self.assertStatus('200 OK')
-        lastmod = ''
-        for k, v in self.headers:
-            if k == 'Last-Modified':
-                lastmod = v
-        ims = ('If-Modified-Since', lastmod)
-        self.getPage('/static/dirback.jpg', headers=[ims])
-        self.assertStatus(304)
-        self.assertNoHeader('Content-Type')
-        self.assertNoHeader('Content-Length')
-        self.assertNoHeader('Content-Disposition')
-        self.assertBody('')
-
-    def test_755_vhost(self):
-        self.getPage('/test/', [('Host', 'virt.net')])
-        self.assertStatus(200)
-        self.getPage('/test', [('Host', 'virt.net')])
-        self.assertStatus(301)
-        self.assertHeader('Location', self.scheme + '://virt.net/test/')
-
-    def test_serve_fileobj(self):
-        self.getPage('/fileobj')
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/css;charset=utf-8')
-        self.assertMatchesBody('^Dummy stylesheet')
-
-    def test_serve_bytesio(self):
-        self.getPage('/bytesio')
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
-        self.assertHeader('Content-Length', 14)
-        self.assertMatchesBody('Fee\nfie\nfo\nfum')
-
-    @pytest.mark.xfail(reason='#1475')
-    def test_file_stream(self):
-        if cherrypy.server.protocol_version != 'HTTP/1.1':
-            return self.skip()
-
-        self.PROTOCOL = 'HTTP/1.1'
-
-        # Make an initial request
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.putrequest('GET', '/bigfile', skip_host=True)
-        conn.putheader('Host', self.HOST)
-        conn.endheaders()
-        response = conn.response_class(conn.sock, method='GET')
-        response.begin()
-        self.assertEqual(response.status, 200)
-
-        body = b''
-        remaining = BIGFILE_SIZE
-        while remaining > 0:
-            data = response.fp.read(65536)
-            if not data:
-                break
-            body += data
-            remaining -= len(data)
-
-            if self.scheme == 'https':
-                newconn = HTTPSConnection
-            else:
-                newconn = HTTPConnection
-            s, h, b = helper.webtest.openURL(
-                b'/tell', headers=[], host=self.HOST, port=self.PORT,
-                http_conn=newconn)
-            if not b:
-                # The file was closed on the server.
-                tell_position = BIGFILE_SIZE
-            else:
-                tell_position = int(b)
-
-            read_so_far = len(body)
-
-            # It is difficult for us to force the server to only read
-            # the bytes that we ask for - there are going to be buffers
-            # inbetween.
-            #
-            # CherryPy will attempt to write as much data as it can to
-            # the socket, and we don't have a way to determine what that
-            # size will be. So we make the following assumption - by
-            # the time we have read in the entire file on the server,
-            # we will have at least received half of it. If this is not
-            # the case, then this is an indicator that either:
-            #   - machines that are running this test are using buffer
-            #     sizes greater than half of BIGFILE_SIZE; or
-            #   - streaming is broken.
-            #
-            # At the time of writing, we seem to have encountered
-            # buffer sizes bigger than 512K, so we've increased
-            # BIGFILE_SIZE to 4MB and in 2016 to 20MB and then 32MB.
-            # This test is going to keep failing according to the
-            # improvements in hardware and OS buffers.
-            if tell_position >= BIGFILE_SIZE:
-                if read_so_far < (BIGFILE_SIZE / 2):
-                    self.fail(
-                        'The file should have advanced to position %r, but '
-                        'has already advanced to the end of the file. It '
-                        'may not be streamed as intended, or at the wrong '
-                        'chunk size (64k)' % read_so_far)
-            elif tell_position < read_so_far:
-                self.fail(
-                    'The file should have advanced to position %r, but has '
-                    'only advanced to position %r. It may not be streamed '
-                    'as intended, or at the wrong chunk size (64k)' %
-                    (read_so_far, tell_position))
-
-        if body != b'x' * BIGFILE_SIZE:
-            self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." %
-                      (BIGFILE_SIZE, body[:50], len(body)))
-        conn.close()
-
-    def test_file_stream_deadlock(self):
-        if cherrypy.server.protocol_version != 'HTTP/1.1':
-            return self.skip()
-
-        self.PROTOCOL = 'HTTP/1.1'
-
-        # Make an initial request but abort early.
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.putrequest('GET', '/bigfile', skip_host=True)
-        conn.putheader('Host', self.HOST)
-        conn.endheaders()
-        response = conn.response_class(conn.sock, method='GET')
-        response.begin()
-        self.assertEqual(response.status, 200)
-        body = response.fp.read(65536)
-        if body != b'x' * len(body):
-            self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." %
-                      (65536, body[:50], len(body)))
-        response.close()
-        conn.close()
-
-        # Make a second request, which should fetch the whole file.
-        self.persistent = False
-        self.getPage('/bigfile')
-        if self.body != b'x' * BIGFILE_SIZE:
-            self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." %
-                      (BIGFILE_SIZE, self.body[:50], len(body)))
-
-    def test_error_page_with_serve_file(self):
-        self.getPage('/404test/yunyeen')
-        self.assertStatus(404)
-        self.assertInBody("I couldn't find that thing")
-
-    def test_null_bytes(self):
-        self.getPage('/static/\x00')
-        self.assertStatus('404 Not Found')
-
-    @staticmethod
-    @contextlib.contextmanager
-    def unicode_file():
-        filename = ntou('Слава Україні.html', 'utf-8')
-        filepath = os.path.join(curdir, 'static', filename)
-        with io.open(filepath, 'w', encoding='utf-8') as strm:
-            strm.write(ntou('Героям Слава!', 'utf-8'))
-        try:
-            yield
-        finally:
-            os.remove(filepath)
-
-    py27_on_windows = (
-        platform.system() == 'Windows' and
-        sys.version_info < (3,)
-    )
-    @pytest.mark.xfail(py27_on_windows, reason='#1544')  # noqa: E301
-    def test_unicode(self):
-        ensure_unicode_filesystem()
-        with self.unicode_file():
-            url = ntou('/static/Слава Україні.html', 'utf-8')
-            # quote function requires str
-            url = tonative(url, 'utf-8')
-            url = urllib.parse.quote(url)
-            self.getPage(url)
-
-            expected = ntou('Героям Слава!', 'utf-8')
-            self.assertInBody(expected)
-
-
-def error_page_404(status, message, traceback, version):
-    path = os.path.join(curdir, 'static', '404.html')
-    return static.serve_file(path, content_type='text/html')
diff --git a/libraries/cherrypy/test/test_tools.py b/libraries/cherrypy/test/test_tools.py
deleted file mode 100644
index a73a3898..00000000
--- a/libraries/cherrypy/test/test_tools.py
+++ /dev/null
@@ -1,468 +0,0 @@
-"""Test the various means of instantiating and invoking tools."""
-
-import gzip
-import io
-import sys
-import time
-import types
-import unittest
-import operator
-
-import six
-from six.moves import range, map
-from six.moves.http_client import IncompleteRead
-
-import cherrypy
-from cherrypy import tools
-from cherrypy._cpcompat import ntou
-from cherrypy.test import helper, _test_decorators
-
-
-timeout = 0.2
-europoundUnicode = ntou('\x80\xa3')
-
-
-#                             Client-side code                             #
-
-
-class ToolTests(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-
-        # Put check_access in a custom toolbox with its own namespace
-        myauthtools = cherrypy._cptools.Toolbox('myauth')
-
-        def check_access(default=False):
-            if not getattr(cherrypy.request, 'userid', default):
-                raise cherrypy.HTTPError(401)
-        myauthtools.check_access = cherrypy.Tool(
-            'before_request_body', check_access)
-
-        def numerify():
-            def number_it(body):
-                for chunk in body:
-                    for k, v in cherrypy.request.numerify_map:
-                        chunk = chunk.replace(k, v)
-                    yield chunk
-            cherrypy.response.body = number_it(cherrypy.response.body)
-
-        class NumTool(cherrypy.Tool):
-
-            def _setup(self):
-                def makemap():
-                    m = self._merged_args().get('map', {})
-                    cherrypy.request.numerify_map = list(six.iteritems(m))
-                cherrypy.request.hooks.attach('on_start_resource', makemap)
-
-                def critical():
-                    cherrypy.request.error_response = cherrypy.HTTPError(
-                        502).set_response
-                critical.failsafe = True
-
-                cherrypy.request.hooks.attach('on_start_resource', critical)
-                cherrypy.request.hooks.attach(self._point, self.callable)
-
-        tools.numerify = NumTool('before_finalize', numerify)
-
-        # It's not mandatory to inherit from cherrypy.Tool.
-        class NadsatTool:
-
-            def __init__(self):
-                self.ended = {}
-                self._name = 'nadsat'
-
-            def nadsat(self):
-                def nadsat_it_up(body):
-                    for chunk in body:
-                        chunk = chunk.replace(b'good', b'horrorshow')
-                        chunk = chunk.replace(b'piece', b'lomtick')
-                        yield chunk
-                cherrypy.response.body = nadsat_it_up(cherrypy.response.body)
-            nadsat.priority = 0
-
-            def cleanup(self):
-                # This runs after the request has been completely written out.
-                cherrypy.response.body = [b'razdrez']
-                id = cherrypy.request.params.get('id')
-                if id:
-                    self.ended[id] = True
-            cleanup.failsafe = True
-
-            def _setup(self):
-                cherrypy.request.hooks.attach('before_finalize', self.nadsat)
-                cherrypy.request.hooks.attach('on_end_request', self.cleanup)
-        tools.nadsat = NadsatTool()
-
-        def pipe_body():
-            cherrypy.request.process_request_body = False
-            clen = int(cherrypy.request.headers['Content-Length'])
-            cherrypy.request.body = cherrypy.request.rfile.read(clen)
-
-        # Assert that we can use a callable object instead of a function.
-        class Rotator(object):
-
-            def __call__(self, scale):
-                r = cherrypy.response
-                r.collapse_body()
-                if six.PY3:
-                    r.body = [bytes([(x + scale) % 256 for x in r.body[0]])]
-                else:
-                    r.body = [chr((ord(x) + scale) % 256) for x in r.body[0]]
-        cherrypy.tools.rotator = cherrypy.Tool('before_finalize', Rotator())
-
-        def stream_handler(next_handler, *args, **kwargs):
-            actual = cherrypy.request.config.get('tools.streamer.arg')
-            assert actual == 'arg value'
-            cherrypy.response.output = o = io.BytesIO()
-            try:
-                next_handler(*args, **kwargs)
-                # Ignore the response and return our accumulated output
-                # instead.
-                return o.getvalue()
-            finally:
-                o.close()
-        cherrypy.tools.streamer = cherrypy._cptools.HandlerWrapperTool(
-            stream_handler)
-
-        class Root:
-
-            @cherrypy.expose
-            def index(self):
-                return 'Howdy earth!'
-
-            @cherrypy.expose
-            @cherrypy.config(**{
-                'tools.streamer.on': True,
-                'tools.streamer.arg': 'arg value',
-            })
-            def tarfile(self):
-                actual = cherrypy.request.config.get('tools.streamer.arg')
-                assert actual == 'arg value'
-                cherrypy.response.output.write(b'I am ')
-                cherrypy.response.output.write(b'a tarfile')
-
-            @cherrypy.expose
-            def euro(self):
-                hooks = list(cherrypy.request.hooks['before_finalize'])
-                hooks.sort()
-                cbnames = [x.callback.__name__ for x in hooks]
-                assert cbnames == ['gzip'], cbnames
-                priorities = [x.priority for x in hooks]
-                assert priorities == [80], priorities
-                yield ntou('Hello,')
-                yield ntou('world')
-                yield europoundUnicode
-
-            # Bare hooks
-            @cherrypy.expose
-            @cherrypy.config(**{'hooks.before_request_body': pipe_body})
-            def pipe(self):
-                return cherrypy.request.body
-
-            # Multiple decorators; include kwargs just for fun.
-            # Note that rotator must run before gzip.
-            @cherrypy.expose
-            def decorated_euro(self, *vpath):
-                yield ntou('Hello,')
-                yield ntou('world')
-                yield europoundUnicode
-            decorated_euro = tools.gzip(compress_level=6)(decorated_euro)
-            decorated_euro = tools.rotator(scale=3)(decorated_euro)
-
-        root = Root()
-
-        class TestType(type):
-            """Metaclass which automatically exposes all functions in each
-            subclass, and adds an instance of the subclass as an attribute
-            of root.
-            """
-            def __init__(cls, name, bases, dct):
-                type.__init__(cls, name, bases, dct)
-                for value in six.itervalues(dct):
-                    if isinstance(value, types.FunctionType):
-                        cherrypy.expose(value)
-                setattr(root, name.lower(), cls())
-        Test = TestType('Test', (object,), {})
-
-        # METHOD ONE:
-        # Declare Tools in _cp_config
-        @cherrypy.config(**{'tools.nadsat.on': True})
-        class Demo(Test):
-
-            def index(self, id=None):
-                return 'A good piece of cherry pie'
-
-            def ended(self, id):
-                return repr(tools.nadsat.ended[id])
-
-            def err(self, id=None):
-                raise ValueError()
-
-            def errinstream(self, id=None):
-                yield 'nonconfidential'
-                raise ValueError()
-                yield 'confidential'
-
-            # METHOD TWO: decorator using Tool()
-            # We support Python 2.3, but the @-deco syntax would look like
-            # this:
-            # @tools.check_access()
-            def restricted(self):
-                return 'Welcome!'
-            restricted = myauthtools.check_access()(restricted)
-            userid = restricted
-
-            def err_in_onstart(self):
-                return 'success!'
-
-            @cherrypy.config(**{'response.stream': True})
-            def stream(self, id=None):
-                for x in range(100000000):
-                    yield str(x)
-
-        conf = {
-            # METHOD THREE:
-            # Declare Tools in detached config
-            '/demo': {
-                'tools.numerify.on': True,
-                'tools.numerify.map': {b'pie': b'3.14159'},
-            },
-            '/demo/restricted': {
-                'request.show_tracebacks': False,
-            },
-            '/demo/userid': {
-                'request.show_tracebacks': False,
-                'myauth.check_access.default': True,
-            },
-            '/demo/errinstream': {
-                'response.stream': True,
-            },
-            '/demo/err_in_onstart': {
-                # Because this isn't a dict, on_start_resource will error.
-                'tools.numerify.map': 'pie->3.14159'
-            },
-            # Combined tools
-            '/euro': {
-                'tools.gzip.on': True,
-                'tools.encode.on': True,
-            },
-            # Priority specified in config
-            '/decorated_euro/subpath': {
-                'tools.gzip.priority': 10,
-            },
-            # Handler wrappers
-            '/tarfile': {'tools.streamer.on': True}
-        }
-        app = cherrypy.tree.mount(root, config=conf)
-        app.request_class.namespaces['myauth'] = myauthtools
-
-        root.tooldecs = _test_decorators.ToolExamples()
-
-    def testHookErrors(self):
-        self.getPage('/demo/?id=1')
-        # If body is "razdrez", then on_end_request is being called too early.
-        self.assertBody('A horrorshow lomtick of cherry 3.14159')
-        # If this fails, then on_end_request isn't being called at all.
-        time.sleep(0.1)
-        self.getPage('/demo/ended/1')
-        self.assertBody('True')
-
-        valerr = '\n    raise ValueError()\nValueError'
-        self.getPage('/demo/err?id=3')
-        # If body is "razdrez", then on_end_request is being called too early.
-        self.assertErrorPage(502, pattern=valerr)
-        # If this fails, then on_end_request isn't being called at all.
-        time.sleep(0.1)
-        self.getPage('/demo/ended/3')
-        self.assertBody('True')
-
-        # If body is "razdrez", then on_end_request is being called too early.
-        if (cherrypy.server.protocol_version == 'HTTP/1.0' or
-                getattr(cherrypy.server, 'using_apache', False)):
-            self.getPage('/demo/errinstream?id=5')
-            # Because this error is raised after the response body has
-            # started, the status should not change to an error status.
-            self.assertStatus('200 OK')
-            self.assertBody('nonconfidential')
-        else:
-            # Because this error is raised after the response body has
-            # started, and because it's chunked output, an error is raised by
-            # the HTTP client when it encounters incomplete output.
-            self.assertRaises((ValueError, IncompleteRead), self.getPage,
-                              '/demo/errinstream?id=5')
-        # If this fails, then on_end_request isn't being called at all.
-        time.sleep(0.1)
-        self.getPage('/demo/ended/5')
-        self.assertBody('True')
-
-        # Test the "__call__" technique (compile-time decorator).
-        self.getPage('/demo/restricted')
-        self.assertErrorPage(401)
-
-        # Test compile-time decorator with kwargs from config.
-        self.getPage('/demo/userid')
-        self.assertBody('Welcome!')
-
-    def testEndRequestOnDrop(self):
-        old_timeout = None
-        try:
-            httpserver = cherrypy.server.httpserver
-            old_timeout = httpserver.timeout
-        except (AttributeError, IndexError):
-            return self.skip()
-
-        try:
-            httpserver.timeout = timeout
-
-            # Test that on_end_request is called even if the client drops.
-            self.persistent = True
-            try:
-                conn = self.HTTP_CONN
-                conn.putrequest('GET', '/demo/stream?id=9', skip_host=True)
-                conn.putheader('Host', self.HOST)
-                conn.endheaders()
-                # Skip the rest of the request and close the conn. This will
-                # cause the server's active socket to error, which *should*
-                # result in the request being aborted, and request.close being
-                # called all the way up the stack (including WSGI middleware),
-                # eventually calling our on_end_request hook.
-            finally:
-                self.persistent = False
-            time.sleep(timeout * 2)
-            # Test that the on_end_request hook was called.
-            self.getPage('/demo/ended/9')
-            self.assertBody('True')
-        finally:
-            if old_timeout is not None:
-                httpserver.timeout = old_timeout
-
-    def testGuaranteedHooks(self):
-        # The 'critical' on_start_resource hook is 'failsafe' (guaranteed
-        # to run even if there are failures in other on_start methods).
-        # This is NOT true of the other hooks.
-        # Here, we have set up a failure in NumerifyTool.numerify_map,
-        # but our 'critical' hook should run and set the error to 502.
-        self.getPage('/demo/err_in_onstart')
-        self.assertErrorPage(502)
-        tmpl = "AttributeError: 'str' object has no attribute '{attr}'"
-        expected_msg = tmpl.format(attr='items' if six.PY3 else 'iteritems')
-        self.assertInBody(expected_msg)
-
-    def testCombinedTools(self):
-        expectedResult = (ntou('Hello,world') +
-                          europoundUnicode).encode('utf-8')
-        zbuf = io.BytesIO()
-        zfile = gzip.GzipFile(mode='wb', fileobj=zbuf, compresslevel=9)
-        zfile.write(expectedResult)
-        zfile.close()
-
-        self.getPage('/euro',
-                     headers=[
-                         ('Accept-Encoding', 'gzip'),
-                         ('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7')])
-        self.assertInBody(zbuf.getvalue()[:3])
-
-        zbuf = io.BytesIO()
-        zfile = gzip.GzipFile(mode='wb', fileobj=zbuf, compresslevel=6)
-        zfile.write(expectedResult)
-        zfile.close()
-
-        self.getPage('/decorated_euro', headers=[('Accept-Encoding', 'gzip')])
-        self.assertInBody(zbuf.getvalue()[:3])
-
-        # This returns a different value because gzip's priority was
-        # lowered in conf, allowing the rotator to run after gzip.
-        # Of course, we don't want breakage in production apps,
-        # but it proves the priority was changed.
-        self.getPage('/decorated_euro/subpath',
-                     headers=[('Accept-Encoding', 'gzip')])
-        if six.PY3:
-            self.assertInBody(bytes([(x + 3) % 256 for x in zbuf.getvalue()]))
-        else:
-            self.assertInBody(''.join([chr((ord(x) + 3) % 256)
-                              for x in zbuf.getvalue()]))
-
-    def testBareHooks(self):
-        content = 'bit of a pain in me gulliver'
-        self.getPage('/pipe',
-                     headers=[('Content-Length', str(len(content))),
-                              ('Content-Type', 'text/plain')],
-                     method='POST', body=content)
-        self.assertBody(content)
-
-    def testHandlerWrapperTool(self):
-        self.getPage('/tarfile')
-        self.assertBody('I am a tarfile')
-
-    def testToolWithConfig(self):
-        if not sys.version_info >= (2, 5):
-            return self.skip('skipped (Python 2.5+ only)')
-
-        self.getPage('/tooldecs/blah')
-        self.assertHeader('Content-Type', 'application/data')
-
-    def testWarnToolOn(self):
-        # get
-        try:
-            cherrypy.tools.numerify.on
-        except AttributeError:
-            pass
-        else:
-            raise AssertionError('Tool.on did not error as it should have.')
-
-        # set
-        try:
-            cherrypy.tools.numerify.on = True
-        except AttributeError:
-            pass
-        else:
-            raise AssertionError('Tool.on did not error as it should have.')
-
-    def testDecorator(self):
-        @cherrypy.tools.register('on_start_resource')
-        def example():
-            pass
-        self.assertTrue(isinstance(cherrypy.tools.example, cherrypy.Tool))
-        self.assertEqual(cherrypy.tools.example._point, 'on_start_resource')
-
-        @cherrypy.tools.register(  # noqa: F811
-            'before_finalize', name='renamed', priority=60,
-        )
-        def example():
-            pass
-        self.assertTrue(isinstance(cherrypy.tools.renamed, cherrypy.Tool))
-        self.assertEqual(cherrypy.tools.renamed._point, 'before_finalize')
-        self.assertEqual(cherrypy.tools.renamed._name, 'renamed')
-        self.assertEqual(cherrypy.tools.renamed._priority, 60)
-
-
-class SessionAuthTest(unittest.TestCase):
-
-    def test_login_screen_returns_bytes(self):
-        """
-        login_screen must return bytes even if unicode parameters are passed.
-        Issue 1132 revealed that login_screen would return unicode if the
-        username and password were unicode.
-        """
-        sa = cherrypy.lib.cptools.SessionAuth()
-        res = sa.login_screen(None, username=six.text_type('nobody'),
-                              password=six.text_type('anypass'))
-        self.assertTrue(isinstance(res, bytes))
-
-
-class TestHooks:
-    def test_priorities(self):
-        """
-        Hooks should sort by priority order.
-        """
-        Hook = cherrypy._cprequest.Hook
-        hooks = [
-            Hook(None, priority=48),
-            Hook(None),
-            Hook(None, priority=49),
-        ]
-        hooks.sort()
-        by_priority = operator.attrgetter('priority')
-        priorities = list(map(by_priority, hooks))
-        assert priorities == [48, 49, 50]
diff --git a/libraries/cherrypy/test/test_tutorials.py b/libraries/cherrypy/test/test_tutorials.py
deleted file mode 100644
index efa35b99..00000000
--- a/libraries/cherrypy/test/test_tutorials.py
+++ /dev/null
@@ -1,210 +0,0 @@
-import sys
-import imp
-import types
-import importlib
-
-import six
-
-import cherrypy
-from cherrypy.test import helper
-
-
-class TutorialTest(helper.CPWebCase):
-
-    @classmethod
-    def setup_server(cls):
-        """
-        Mount something so the engine starts.
-        """
-        class Dummy:
-            pass
-        cherrypy.tree.mount(Dummy())
-
-    @staticmethod
-    def load_module(name):
-        """
-        Import or reload tutorial module as needed.
-        """
-        target = 'cherrypy.tutorial.' + name
-        if target in sys.modules:
-            module = imp.reload(sys.modules[target])
-        else:
-            module = importlib.import_module(target)
-        return module
-
-    @classmethod
-    def setup_tutorial(cls, name, root_name, config={}):
-        cherrypy.config.reset()
-        module = cls.load_module(name)
-        root = getattr(module, root_name)
-        conf = getattr(module, 'tutconf')
-        class_types = type,
-        if six.PY2:
-            class_types += types.ClassType,
-        if isinstance(root, class_types):
-            root = root()
-        cherrypy.tree.mount(root, config=conf)
-        cherrypy.config.update(config)
-
-    def test01HelloWorld(self):
-        self.setup_tutorial('tut01_helloworld', 'HelloWorld')
-        self.getPage('/')
-        self.assertBody('Hello world!')
-
-    def test02ExposeMethods(self):
-        self.setup_tutorial('tut02_expose_methods', 'HelloWorld')
-        self.getPage('/show_msg')
-        self.assertBody('Hello world!')
-
-    def test03GetAndPost(self):
-        self.setup_tutorial('tut03_get_and_post', 'WelcomePage')
-
-        # Try different GET queries
-        self.getPage('/greetUser?name=Bob')
-        self.assertBody("Hey Bob, what's up?")
-
-        self.getPage('/greetUser')
-        self.assertBody('Please enter your name <a href="./">here</a>.')
-
-        self.getPage('/greetUser?name=')
-        self.assertBody('No, really, enter your name <a href="./">here</a>.')
-
-        # Try the same with POST
-        self.getPage('/greetUser', method='POST', body='name=Bob')
-        self.assertBody("Hey Bob, what's up?")
-
-        self.getPage('/greetUser', method='POST', body='name=')
-        self.assertBody('No, really, enter your name <a href="./">here</a>.')
-
-    def test04ComplexSite(self):
-        self.setup_tutorial('tut04_complex_site', 'root')
-
-        msg = '''
-            <p>Here are some extra useful links:</p>
-
-            <ul>
-                <li><a href="http://del.icio.us">del.icio.us</a></li>
-                <li><a href="http://www.cherrypy.org">CherryPy</a></li>
-            </ul>
-
-            <p>[<a href="../">Return to links page</a>]</p>'''
-        self.getPage('/links/extra/')
-        self.assertBody(msg)
-
-    def test05DerivedObjects(self):
-        self.setup_tutorial('tut05_derived_objects', 'HomePage')
-        msg = '''
-            <html>
-            <head>
-                <title>Another Page</title>
-            <head>
-            <body>
-            <h2>Another Page</h2>
-
-            <p>
-            And this is the amazing second page!
-            </p>
-
-            </body>
-            </html>
-        '''
-        # the tutorial has some annoying spaces in otherwise blank lines
-        msg = msg.replace('</h2>\n\n', '</h2>\n        \n')
-        msg = msg.replace('</p>\n\n', '</p>\n        \n')
-        self.getPage('/another/')
-        self.assertBody(msg)
-
-    def test06DefaultMethod(self):
-        self.setup_tutorial('tut06_default_method', 'UsersPage')
-        self.getPage('/hendrik')
-        self.assertBody('Hendrik Mans, CherryPy co-developer & crazy German '
-                        '(<a href="./">back</a>)')
-
-    def test07Sessions(self):
-        self.setup_tutorial('tut07_sessions', 'HitCounter')
-
-        self.getPage('/')
-        self.assertBody(
-            "\n            During your current session, you've viewed this"
-            '\n            page 1 times! Your life is a patio of fun!'
-            '\n        ')
-
-        self.getPage('/', self.cookies)
-        self.assertBody(
-            "\n            During your current session, you've viewed this"
-            '\n            page 2 times! Your life is a patio of fun!'
-            '\n        ')
-
-    def test08GeneratorsAndYield(self):
-        self.setup_tutorial('tut08_generators_and_yield', 'GeneratorDemo')
-        self.getPage('/')
-        self.assertBody('<html><body><h2>Generators rule!</h2>'
-                        '<h3>List of users:</h3>'
-                        'Remi<br/>Carlos<br/>Hendrik<br/>Lorenzo Lamas<br/>'
-                        '</body></html>')
-
-    def test09Files(self):
-        self.setup_tutorial('tut09_files', 'FileDemo')
-
-        # Test upload
-        filesize = 5
-        h = [('Content-type', 'multipart/form-data; boundary=x'),
-             ('Content-Length', str(105 + filesize))]
-        b = ('--x\n'
-             'Content-Disposition: form-data; name="myFile"; '
-             'filename="hello.txt"\r\n'
-             'Content-Type: text/plain\r\n'
-             '\r\n')
-        b += 'a' * filesize + '\n' + '--x--\n'
-        self.getPage('/upload', h, 'POST', b)
-        self.assertBody('''<html>
-        <body>
-            myFile length: %d<br />
-            myFile filename: hello.txt<br />
-            myFile mime-type: text/plain
-        </body>
-        </html>''' % filesize)
-
-        # Test download
-        self.getPage('/download')
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'application/x-download')
-        self.assertHeader('Content-Disposition',
-                          # Make sure the filename is quoted.
-                          'attachment; filename="pdf_file.pdf"')
-        self.assertEqual(len(self.body), 85698)
-
-    def test10HTTPErrors(self):
-        self.setup_tutorial('tut10_http_errors', 'HTTPErrorDemo')
-
-        @cherrypy.expose
-        def traceback_setting():
-            return repr(cherrypy.request.show_tracebacks)
-        cherrypy.tree.mount(traceback_setting, '/traceback_setting')
-
-        self.getPage('/')
-        self.assertInBody("""<a href="toggleTracebacks">""")
-        self.assertInBody("""<a href="/doesNotExist">""")
-        self.assertInBody("""<a href="/error?code=403">""")
-        self.assertInBody("""<a href="/error?code=500">""")
-        self.assertInBody("""<a href="/messageArg">""")
-
-        self.getPage('/traceback_setting')
-        setting = self.body
-        self.getPage('/toggleTracebacks')
-        self.assertStatus((302, 303))
-        self.getPage('/traceback_setting')
-        self.assertBody(str(not eval(setting)))
-
-        self.getPage('/error?code=500')
-        self.assertStatus(500)
-        self.assertInBody('The server encountered an unexpected condition '
-                          'which prevented it from fulfilling the request.')
-
-        self.getPage('/error?code=403')
-        self.assertStatus(403)
-        self.assertInBody("<h2>You can't do that!</h2>")
-
-        self.getPage('/messageArg')
-        self.assertStatus(500)
-        self.assertInBody("If you construct an HTTPError with a 'message'")
diff --git a/libraries/cherrypy/test/test_virtualhost.py b/libraries/cherrypy/test/test_virtualhost.py
deleted file mode 100644
index de88f927..00000000
--- a/libraries/cherrypy/test/test_virtualhost.py
+++ /dev/null
@@ -1,113 +0,0 @@
-import os
-
-import cherrypy
-from cherrypy.test import helper
-
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-
-class VirtualHostTest(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-        class Root:
-
-            @cherrypy.expose
-            def index(self):
-                return 'Hello, world'
-
-            @cherrypy.expose
-            def dom4(self):
-                return 'Under construction'
-
-            @cherrypy.expose
-            def method(self, value):
-                return 'You sent %s' % value
-
-        class VHost:
-
-            def __init__(self, sitename):
-                self.sitename = sitename
-
-            @cherrypy.expose
-            def index(self):
-                return 'Welcome to %s' % self.sitename
-
-            @cherrypy.expose
-            def vmethod(self, value):
-                return 'You sent %s' % value
-
-            @cherrypy.expose
-            def url(self):
-                return cherrypy.url('nextpage')
-
-            # Test static as a handler (section must NOT include vhost prefix)
-            static = cherrypy.tools.staticdir.handler(
-                section='/static', dir=curdir)
-
-        root = Root()
-        root.mydom2 = VHost('Domain 2')
-        root.mydom3 = VHost('Domain 3')
-        hostmap = {'www.mydom2.com': '/mydom2',
-                   'www.mydom3.com': '/mydom3',
-                   'www.mydom4.com': '/dom4',
-                   }
-        cherrypy.tree.mount(root, config={
-            '/': {
-                'request.dispatch': cherrypy.dispatch.VirtualHost(**hostmap)
-            },
-            # Test static in config (section must include vhost prefix)
-            '/mydom2/static2': {
-                'tools.staticdir.on': True,
-                'tools.staticdir.root': curdir,
-                'tools.staticdir.dir': 'static',
-                'tools.staticdir.index': 'index.html',
-            },
-        })
-
-    def testVirtualHost(self):
-        self.getPage('/', [('Host', 'www.mydom1.com')])
-        self.assertBody('Hello, world')
-        self.getPage('/mydom2/', [('Host', 'www.mydom1.com')])
-        self.assertBody('Welcome to Domain 2')
-
-        self.getPage('/', [('Host', 'www.mydom2.com')])
-        self.assertBody('Welcome to Domain 2')
-        self.getPage('/', [('Host', 'www.mydom3.com')])
-        self.assertBody('Welcome to Domain 3')
-        self.getPage('/', [('Host', 'www.mydom4.com')])
-        self.assertBody('Under construction')
-
-        # Test GET, POST, and positional params
-        self.getPage('/method?value=root')
-        self.assertBody('You sent root')
-        self.getPage('/vmethod?value=dom2+GET', [('Host', 'www.mydom2.com')])
-        self.assertBody('You sent dom2 GET')
-        self.getPage('/vmethod', [('Host', 'www.mydom3.com')], method='POST',
-                     body='value=dom3+POST')
-        self.assertBody('You sent dom3 POST')
-        self.getPage('/vmethod/pos', [('Host', 'www.mydom3.com')])
-        self.assertBody('You sent pos')
-
-        # Test that cherrypy.url uses the browser url, not the virtual url
-        self.getPage('/url', [('Host', 'www.mydom2.com')])
-        self.assertBody('%s://www.mydom2.com/nextpage' % self.scheme)
-
-    def test_VHost_plus_Static(self):
-        # Test static as a handler
-        self.getPage('/static/style.css', [('Host', 'www.mydom2.com')])
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/css;charset=utf-8')
-
-        # Test static in config
-        self.getPage('/static2/dirback.jpg', [('Host', 'www.mydom2.com')])
-        self.assertStatus('200 OK')
-        self.assertHeaderIn('Content-Type', ['image/jpeg', 'image/pjpeg'])
-
-        # Test static config with "index" arg
-        self.getPage('/static2/', [('Host', 'www.mydom2.com')])
-        self.assertStatus('200 OK')
-        self.assertBody('Hello, world\r\n')
-        # Since tools.trailing_slash is on by default, this should redirect
-        self.getPage('/static2', [('Host', 'www.mydom2.com')])
-        self.assertStatus(301)
diff --git a/libraries/cherrypy/test/test_wsgi_ns.py b/libraries/cherrypy/test/test_wsgi_ns.py
deleted file mode 100644
index 3545724c..00000000
--- a/libraries/cherrypy/test/test_wsgi_ns.py
+++ /dev/null
@@ -1,93 +0,0 @@
-import cherrypy
-from cherrypy.test import helper
-
-
-class WSGI_Namespace_Test(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-
-        class WSGIResponse(object):
-
-            def __init__(self, appresults):
-                self.appresults = appresults
-                self.iter = iter(appresults)
-
-            def __iter__(self):
-                return self
-
-            def next(self):
-                return self.iter.next()
-
-            def __next__(self):
-                return next(self.iter)
-
-            def close(self):
-                if hasattr(self.appresults, 'close'):
-                    self.appresults.close()
-
-        class ChangeCase(object):
-
-            def __init__(self, app, to=None):
-                self.app = app
-                self.to = to
-
-            def __call__(self, environ, start_response):
-                res = self.app(environ, start_response)
-
-                class CaseResults(WSGIResponse):
-
-                    def next(this):
-                        return getattr(this.iter.next(), self.to)()
-
-                    def __next__(this):
-                        return getattr(next(this.iter), self.to)()
-                return CaseResults(res)
-
-        class Replacer(object):
-
-            def __init__(self, app, map={}):
-                self.app = app
-                self.map = map
-
-            def __call__(self, environ, start_response):
-                res = self.app(environ, start_response)
-
-                class ReplaceResults(WSGIResponse):
-
-                    def next(this):
-                        line = this.iter.next()
-                        for k, v in self.map.iteritems():
-                            line = line.replace(k, v)
-                        return line
-
-                    def __next__(this):
-                        line = next(this.iter)
-                        for k, v in self.map.items():
-                            line = line.replace(k, v)
-                        return line
-                return ReplaceResults(res)
-
-        class Root(object):
-
-            @cherrypy.expose
-            def index(self):
-                return 'HellO WoRlD!'
-
-        root_conf = {'wsgi.pipeline': [('replace', Replacer)],
-                     'wsgi.replace.map': {b'L': b'X',
-                                          b'l': b'r'},
-                     }
-
-        app = cherrypy.Application(Root())
-        app.wsgiapp.pipeline.append(('changecase', ChangeCase))
-        app.wsgiapp.config['changecase'] = {'to': 'upper'}
-        cherrypy.tree.mount(app, config={'/': root_conf})
-
-    def test_pipeline(self):
-        if not cherrypy.server.httpserver:
-            return self.skip()
-
-        self.getPage('/')
-        # If body is "HEXXO WORXD!", the middleware was applied out of order.
-        self.assertBody('HERRO WORRD!')
diff --git a/libraries/cherrypy/test/test_wsgi_unix_socket.py b/libraries/cherrypy/test/test_wsgi_unix_socket.py
deleted file mode 100644
index 8f1cc00b..00000000
--- a/libraries/cherrypy/test/test_wsgi_unix_socket.py
+++ /dev/null
@@ -1,93 +0,0 @@
-import os
-import socket
-import atexit
-import tempfile
-
-from six.moves.http_client import HTTPConnection
-
-import pytest
-
-import cherrypy
-from cherrypy.test import helper
-
-
-def usocket_path():
-    fd, path = tempfile.mkstemp('cp_test.sock')
-    os.close(fd)
-    os.remove(path)
-    return path
-
-
-USOCKET_PATH = usocket_path()
-
-
-class USocketHTTPConnection(HTTPConnection):
-    """
-    HTTPConnection over a unix socket.
-    """
-
-    def __init__(self, path):
-        HTTPConnection.__init__(self, 'localhost')
-        self.path = path
-
-    def __call__(self, *args, **kwargs):
-        """
-        Catch-all method just to present itself as a constructor for the
-        HTTPConnection.
-        """
-        return self
-
-    def connect(self):
-        """
-        Override the connect method and assign a unix socket as a transport.
-        """
-        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-        sock.connect(self.path)
-        self.sock = sock
-        atexit.register(lambda: os.remove(self.path))
-
-
-@pytest.mark.skipif("sys.platform == 'win32'")
-class WSGI_UnixSocket_Test(helper.CPWebCase):
-    """
-    Test basic behavior on a cherrypy wsgi server listening
-    on a unix socket.
-
-    It exercises the config option `server.socket_file`.
-    """
-    HTTP_CONN = USocketHTTPConnection(USOCKET_PATH)
-
-    @staticmethod
-    def setup_server():
-        class Root(object):
-
-            @cherrypy.expose
-            def index(self):
-                return 'Test OK'
-
-            @cherrypy.expose
-            def error(self):
-                raise Exception('Invalid page')
-
-        config = {
-            'server.socket_file': USOCKET_PATH
-        }
-        cherrypy.config.update(config)
-        cherrypy.tree.mount(Root())
-
-    def tearDown(self):
-        cherrypy.config.update({'server.socket_file': None})
-
-    def test_simple_request(self):
-        self.getPage('/')
-        self.assertStatus('200 OK')
-        self.assertInBody('Test OK')
-
-    def test_not_found(self):
-        self.getPage('/invalid_path')
-        self.assertStatus('404 Not Found')
-
-    def test_internal_error(self):
-        self.getPage('/error')
-        self.assertStatus('500 Internal Server Error')
-        self.assertInBody('Invalid page')
diff --git a/libraries/cherrypy/test/test_wsgi_vhost.py b/libraries/cherrypy/test/test_wsgi_vhost.py
deleted file mode 100644
index 2b6e5ba9..00000000
--- a/libraries/cherrypy/test/test_wsgi_vhost.py
+++ /dev/null
@@ -1,35 +0,0 @@
-import cherrypy
-from cherrypy.test import helper
-
-
-class WSGI_VirtualHost_Test(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-
-        class ClassOfRoot(object):
-
-            def __init__(self, name):
-                self.name = name
-
-            @cherrypy.expose
-            def index(self):
-                return 'Welcome to the %s website!' % self.name
-
-        default = cherrypy.Application(None)
-
-        domains = {}
-        for year in range(1997, 2008):
-            app = cherrypy.Application(ClassOfRoot('Class of %s' % year))
-            domains['www.classof%s.example' % year] = app
-
-        cherrypy.tree.graft(cherrypy._cpwsgi.VirtualHost(default, domains))
-
-    def test_welcome(self):
-        if not cherrypy.server.using_wsgi:
-            return self.skip('skipped (not using WSGI)... ')
-
-        for year in range(1997, 2008):
-            self.getPage(
-                '/', headers=[('Host', 'www.classof%s.example' % year)])
-            self.assertBody('Welcome to the Class of %s website!' % year)
diff --git a/libraries/cherrypy/test/test_wsgiapps.py b/libraries/cherrypy/test/test_wsgiapps.py
deleted file mode 100644
index 1b3bf28f..00000000
--- a/libraries/cherrypy/test/test_wsgiapps.py
+++ /dev/null
@@ -1,120 +0,0 @@
-import sys
-
-import cherrypy
-from cherrypy._cpcompat import ntob
-from cherrypy.test import helper
-
-
-class WSGIGraftTests(helper.CPWebCase):
-
-    @staticmethod
-    def setup_server():
-
-        def test_app(environ, start_response):
-            status = '200 OK'
-            response_headers = [('Content-type', 'text/plain')]
-            start_response(status, response_headers)
-            output = ['Hello, world!\n',
-                      'This is a wsgi app running within CherryPy!\n\n']
-            keys = list(environ.keys())
-            keys.sort()
-            for k in keys:
-                output.append('%s: %s\n' % (k, environ[k]))
-            return [ntob(x, 'utf-8') for x in output]
-
-        def test_empty_string_app(environ, start_response):
-            status = '200 OK'
-            response_headers = [('Content-type', 'text/plain')]
-            start_response(status, response_headers)
-            return [
-                b'Hello', b'', b' ', b'', b'world',
-            ]
-
-        class WSGIResponse(object):
-
-            def __init__(self, appresults):
-                self.appresults = appresults
-                self.iter = iter(appresults)
-
-            def __iter__(self):
-                return self
-
-            if sys.version_info >= (3, 0):
-                def __next__(self):
-                    return next(self.iter)
-            else:
-                def next(self):
-                    return self.iter.next()
-
-            def close(self):
-                if hasattr(self.appresults, 'close'):
-                    self.appresults.close()
-
-        class ReversingMiddleware(object):
-
-            def __init__(self, app):
-                self.app = app
-
-            def __call__(self, environ, start_response):
-                results = app(environ, start_response)
-
-                class Reverser(WSGIResponse):
-
-                    if sys.version_info >= (3, 0):
-                        def __next__(this):
-                            line = list(next(this.iter))
-                            line.reverse()
-                            return bytes(line)
-                    else:
-                        def next(this):
-                            line = list(this.iter.next())
-                            line.reverse()
-                            return ''.join(line)
-
-                return Reverser(results)
-
-        class Root:
-
-            @cherrypy.expose
-            def index(self):
-                return ntob("I'm a regular CherryPy page handler!")
-
-        cherrypy.tree.mount(Root())
-
-        cherrypy.tree.graft(test_app, '/hosted/app1')
-        cherrypy.tree.graft(test_empty_string_app, '/hosted/app3')
-
-        # Set script_name explicitly to None to signal CP that it should
-        # be pulled from the WSGI environ each time.
-        app = cherrypy.Application(Root(), script_name=None)
-        cherrypy.tree.graft(ReversingMiddleware(app), '/hosted/app2')
-
-    wsgi_output = '''Hello, world!
-This is a wsgi app running within CherryPy!'''
-
-    def test_01_standard_app(self):
-        self.getPage('/')
-        self.assertBody("I'm a regular CherryPy page handler!")
-
-    def test_04_pure_wsgi(self):
-        if not cherrypy.server.using_wsgi:
-            return self.skip('skipped (not using WSGI)... ')
-        self.getPage('/hosted/app1')
-        self.assertHeader('Content-Type', 'text/plain')
-        self.assertInBody(self.wsgi_output)
-
-    def test_05_wrapped_cp_app(self):
-        if not cherrypy.server.using_wsgi:
-            return self.skip('skipped (not using WSGI)... ')
-        self.getPage('/hosted/app2/')
-        body = list("I'm a regular CherryPy page handler!")
-        body.reverse()
-        body = ''.join(body)
-        self.assertInBody(body)
-
-    def test_06_empty_string_app(self):
-        if not cherrypy.server.using_wsgi:
-            return self.skip('skipped (not using WSGI)... ')
-        self.getPage('/hosted/app3')
-        self.assertHeader('Content-Type', 'text/plain')
-        self.assertInBody('Hello world')
diff --git a/libraries/cherrypy/test/test_xmlrpc.py b/libraries/cherrypy/test/test_xmlrpc.py
deleted file mode 100644
index ad93b821..00000000
--- a/libraries/cherrypy/test/test_xmlrpc.py
+++ /dev/null
@@ -1,183 +0,0 @@
-import sys
-
-import six
-
-from six.moves.xmlrpc_client import (
-    DateTime, Fault,
-    ProtocolError, ServerProxy, SafeTransport
-)
-
-import cherrypy
-from cherrypy import _cptools
-from cherrypy.test import helper
-
-if six.PY3:
-    HTTPSTransport = SafeTransport
-
-    # Python 3.0's SafeTransport still mistakenly checks for socket.ssl
-    import socket
-    if not hasattr(socket, 'ssl'):
-        socket.ssl = True
-else:
-    class HTTPSTransport(SafeTransport):
-
-        """Subclass of SafeTransport to fix sock.recv errors (by using file).
-        """
-
-        def request(self, host, handler, request_body, verbose=0):
-            # issue XML-RPC request
-            h = self.make_connection(host)
-            if verbose:
-                h.set_debuglevel(1)
-
-            self.send_request(h, handler, request_body)
-            self.send_host(h, host)
-            self.send_user_agent(h)
-            self.send_content(h, request_body)
-
-            errcode, errmsg, headers = h.getreply()
-            if errcode != 200:
-                raise ProtocolError(host + handler, errcode, errmsg, headers)
-
-            self.verbose = verbose
-
-            # Here's where we differ from the superclass. It says:
-            # try:
-            #     sock = h._conn.sock
-            # except AttributeError:
-            #     sock = None
-            # return self._parse_response(h.getfile(), sock)
-
-            return self.parse_response(h.getfile())
-
-
-def setup_server():
-
-    class Root:
-
-        @cherrypy.expose
-        def index(self):
-            return "I'm a standard index!"
-
-    class XmlRpc(_cptools.XMLRPCController):
-
-        @cherrypy.expose
-        def foo(self):
-            return 'Hello world!'
-
-        @cherrypy.expose
-        def return_single_item_list(self):
-            return [42]
-
-        @cherrypy.expose
-        def return_string(self):
-            return 'here is a string'
-
-        @cherrypy.expose
-        def return_tuple(self):
-            return ('here', 'is', 1, 'tuple')
-
-        @cherrypy.expose
-        def return_dict(self):
-            return dict(a=1, b=2, c=3)
-
-        @cherrypy.expose
-        def return_composite(self):
-            return dict(a=1, z=26), 'hi', ['welcome', 'friend']
-
-        @cherrypy.expose
-        def return_int(self):
-            return 42
-
-        @cherrypy.expose
-        def return_float(self):
-            return 3.14
-
-        @cherrypy.expose
-        def return_datetime(self):
-            return DateTime((2003, 10, 7, 8, 1, 0, 1, 280, -1))
-
-        @cherrypy.expose
-        def return_boolean(self):
-            return True
-
-        @cherrypy.expose
-        def test_argument_passing(self, num):
-            return num * 2
-
-        @cherrypy.expose
-        def test_returning_Fault(self):
-            return Fault(1, 'custom Fault response')
-
-    root = Root()
-    root.xmlrpc = XmlRpc()
-    cherrypy.tree.mount(root, config={'/': {
-        'request.dispatch': cherrypy.dispatch.XMLRPCDispatcher(),
-        'tools.xmlrpc.allow_none': 0,
-    }})
-
-
-class XmlRpcTest(helper.CPWebCase):
-    setup_server = staticmethod(setup_server)
-
-    def testXmlRpc(self):
-
-        scheme = self.scheme
-        if scheme == 'https':
-            url = 'https://%s:%s/xmlrpc/' % (self.interface(), self.PORT)
-            proxy = ServerProxy(url, transport=HTTPSTransport())
-        else:
-            url = 'http://%s:%s/xmlrpc/' % (self.interface(), self.PORT)
-            proxy = ServerProxy(url)
-
-        # begin the tests ...
-        self.getPage('/xmlrpc/foo')
-        self.assertBody('Hello world!')
-
-        self.assertEqual(proxy.return_single_item_list(), [42])
-        self.assertNotEqual(proxy.return_single_item_list(), 'one bazillion')
-        self.assertEqual(proxy.return_string(), 'here is a string')
-        self.assertEqual(proxy.return_tuple(),
-                         list(('here', 'is', 1, 'tuple')))
-        self.assertEqual(proxy.return_dict(), {'a': 1, 'c': 3, 'b': 2})
-        self.assertEqual(proxy.return_composite(),
-                         [{'a': 1, 'z': 26}, 'hi', ['welcome', 'friend']])
-        self.assertEqual(proxy.return_int(), 42)
-        self.assertEqual(proxy.return_float(), 3.14)
-        self.assertEqual(proxy.return_datetime(),
-                         DateTime((2003, 10, 7, 8, 1, 0, 1, 280, -1)))
-        self.assertEqual(proxy.return_boolean(), True)
-        self.assertEqual(proxy.test_argument_passing(22), 22 * 2)
-
-        # Test an error in the page handler (should raise an xmlrpclib.Fault)
-        try:
-            proxy.test_argument_passing({})
-        except Exception:
-            x = sys.exc_info()[1]
-            self.assertEqual(x.__class__, Fault)
-            self.assertEqual(x.faultString, ('unsupported operand type(s) '
-                                             "for *: 'dict' and 'int'"))
-        else:
-            self.fail('Expected xmlrpclib.Fault')
-
-        # https://github.com/cherrypy/cherrypy/issues/533
-        # if a method is not found, an xmlrpclib.Fault should be raised
-        try:
-            proxy.non_method()
-        except Exception:
-            x = sys.exc_info()[1]
-            self.assertEqual(x.__class__, Fault)
-            self.assertEqual(x.faultString,
-                             'method "non_method" is not supported')
-        else:
-            self.fail('Expected xmlrpclib.Fault')
-
-        # Test returning a Fault from the page handler.
-        try:
-            proxy.test_returning_Fault()
-        except Exception:
-            x = sys.exc_info()[1]
-            self.assertEqual(x.__class__, Fault)
-            self.assertEqual(x.faultString, ('custom Fault response'))
-        else:
-            self.fail('Expected xmlrpclib.Fault')
diff --git a/libraries/cherrypy/test/webtest.py b/libraries/cherrypy/test/webtest.py
deleted file mode 100644
index 9fb6ce62..00000000
--- a/libraries/cherrypy/test/webtest.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# for compatibility, expose cheroot webtest here
-import warnings
-
-from cheroot.test.webtest import (  # noqa
-    interface,
-    WebCase, cleanHeaders, shb, openURL,
-    ServerError, server_error,
-)
-
-
-warnings.warn('Use cheroot.test.webtest', DeprecationWarning)
diff --git a/libraries/cherrypy/tutorial/README.rst b/libraries/cherrypy/tutorial/README.rst
deleted file mode 100644
index c47e7d32..00000000
--- a/libraries/cherrypy/tutorial/README.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-CherryPy Tutorials
-------------------
-
-This is a series of tutorials explaining how to develop dynamic web
-applications using CherryPy. A couple of notes:
-
-
-- Each of these tutorials builds on the ones before it. If you're
-  new to CherryPy, we recommend you start with 01_helloworld.py and
-  work your way upwards. :)
-
-- In most of these tutorials, you will notice that all output is done
-  by returning normal Python strings, often using simple Python
-  variable substitution. In most real-world applications, you will
-  probably want to use a separate template package (like Cheetah,
-  CherryTemplate or XML/XSL).
diff --git a/libraries/cherrypy/tutorial/__init__.py b/libraries/cherrypy/tutorial/__init__.py
deleted file mode 100644
index 08c142c5..00000000
--- a/libraries/cherrypy/tutorial/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-
-# This is used in test_config to test unrepr of "from A import B"
-thing2 = object()
diff --git a/libraries/cherrypy/tutorial/custom_error.html b/libraries/cherrypy/tutorial/custom_error.html
deleted file mode 100644
index d0f30c8a..00000000
--- a/libraries/cherrypy/tutorial/custom_error.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html>
-<head>
-    <title>403 Unauthorized</title>
-</head>
-    <body>
-        <h2>You can't do that!</h2>
-        <p>%(message)s</p>
-        <p>This is a custom error page that is read from a file.<p>
-        <pre>%(traceback)s</pre>
-    </body>
-</html>
diff --git a/libraries/cherrypy/tutorial/pdf_file.pdf b/libraries/cherrypy/tutorial/pdf_file.pdf
deleted file mode 100644
index 38b4f15e..00000000
Binary files a/libraries/cherrypy/tutorial/pdf_file.pdf and /dev/null differ
diff --git a/libraries/cherrypy/tutorial/tut01_helloworld.py b/libraries/cherrypy/tutorial/tut01_helloworld.py
deleted file mode 100644
index e86793c8..00000000
--- a/libraries/cherrypy/tutorial/tut01_helloworld.py
+++ /dev/null
@@ -1,34 +0,0 @@
-"""
-Tutorial - Hello World
-
-The most basic (working) CherryPy application possible.
-"""
-
-import os.path
-
-# Import CherryPy global namespace
-import cherrypy
-
-
-class HelloWorld:
-
-    """ Sample request handler class. """
-
-    # Expose the index method through the web. CherryPy will never
-    # publish methods that don't have the exposed attribute set to True.
-    @cherrypy.expose
-    def index(self):
-        # CherryPy will call this method for the root URI ("/") and send
-        # its return value to the client. Because this is tutorial
-        # lesson number 01, we'll just send something really simple.
-        # How about...
-        return 'Hello world!'
-
-
-tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
-
-if __name__ == '__main__':
-    # CherryPy always starts with app.root when trying to map request URIs
-    # to objects, so we need to mount a request handler root. A request
-    # to '/' will be mapped to HelloWorld().index().
-    cherrypy.quickstart(HelloWorld(), config=tutconf)
diff --git a/libraries/cherrypy/tutorial/tut02_expose_methods.py b/libraries/cherrypy/tutorial/tut02_expose_methods.py
deleted file mode 100644
index 8afbf7d8..00000000
--- a/libraries/cherrypy/tutorial/tut02_expose_methods.py
+++ /dev/null
@@ -1,32 +0,0 @@
-"""
-Tutorial - Multiple methods
-
-This tutorial shows you how to link to other methods of your request
-handler.
-"""
-
-import os.path
-
-import cherrypy
-
-
-class HelloWorld:
-
-    @cherrypy.expose
-    def index(self):
-        # Let's link to another method here.
-        return 'We have an <a href="show_msg">important message</a> for you!'
-
-    @cherrypy.expose
-    def show_msg(self):
-        # Here's the important message!
-        return 'Hello world!'
-
-
-tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
-
-if __name__ == '__main__':
-    # CherryPy always starts with app.root when trying to map request URIs
-    # to objects, so we need to mount a request handler root. A request
-    # to '/' will be mapped to HelloWorld().index().
-    cherrypy.quickstart(HelloWorld(), config=tutconf)
diff --git a/libraries/cherrypy/tutorial/tut03_get_and_post.py b/libraries/cherrypy/tutorial/tut03_get_and_post.py
deleted file mode 100644
index 0b3d4613..00000000
--- a/libraries/cherrypy/tutorial/tut03_get_and_post.py
+++ /dev/null
@@ -1,51 +0,0 @@
-"""
-Tutorial - Passing variables
-
-This tutorial shows you how to pass GET/POST variables to methods.
-"""
-
-import os.path
-
-import cherrypy
-
-
-class WelcomePage:
-
-    @cherrypy.expose
-    def index(self):
-        # Ask for the user's name.
-        return '''
-            <form action="greetUser" method="GET">
-            What is your name?
-            <input type="text" name="name" />
-            <input type="submit" />
-            </form>'''
-
-    @cherrypy.expose
-    def greetUser(self, name=None):
-        # CherryPy passes all GET and POST variables as method parameters.
-        # It doesn't make a difference where the variables come from, how
-        # large their contents are, and so on.
-        #
-        # You can define default parameter values as usual. In this
-        # example, the "name" parameter defaults to None so we can check
-        # if a name was actually specified.
-
-        if name:
-            # Greet the user!
-            return "Hey %s, what's up?" % name
-        else:
-            if name is None:
-                # No name was specified
-                return 'Please enter your name <a href="./">here</a>.'
-            else:
-                return 'No, really, enter your name <a href="./">here</a>.'
-
-
-tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
-
-if __name__ == '__main__':
-    # CherryPy always starts with app.root when trying to map request URIs
-    # to objects, so we need to mount a request handler root. A request
-    # to '/' will be mapped to HelloWorld().index().
-    cherrypy.quickstart(WelcomePage(), config=tutconf)
diff --git a/libraries/cherrypy/tutorial/tut04_complex_site.py b/libraries/cherrypy/tutorial/tut04_complex_site.py
deleted file mode 100644
index 3caa1775..00000000
--- a/libraries/cherrypy/tutorial/tut04_complex_site.py
+++ /dev/null
@@ -1,103 +0,0 @@
-"""
-Tutorial - Multiple objects
-
-This tutorial shows you how to create a site structure through multiple
-possibly nested request handler objects.
-"""
-
-import os.path
-
-import cherrypy
-
-
-class HomePage:
-
-    @cherrypy.expose
-    def index(self):
-        return '''
-            <p>Hi, this is the home page! Check out the other
-            fun stuff on this site:</p>
-
-            <ul>
-                <li><a href="/joke/">A silly joke</a></li>
-                <li><a href="/links/">Useful links</a></li>
-            </ul>'''
-
-
-class JokePage:
-
-    @cherrypy.expose
-    def index(self):
-        return '''
-            <p>"In Python, how do you create a string of random
-            characters?" -- "Read a Perl file!"</p>
-            <p>[<a href="../">Return</a>]</p>'''
-
-
-class LinksPage:
-
-    def __init__(self):
-        # Request handler objects can create their own nested request
-        # handler objects. Simply create them inside their __init__
-        # methods!
-        self.extra = ExtraLinksPage()
-
-    @cherrypy.expose
-    def index(self):
-        # Note the way we link to the extra links page (and back).
-        # As you can see, this object doesn't really care about its
-        # absolute position in the site tree, since we use relative
-        # links exclusively.
-        return '''
-            <p>Here are some useful links:</p>
-
-            <ul>
-                <li>
-                    <a href="http://www.cherrypy.org">The CherryPy Homepage</a>
-                </li>
-                <li>
-                    <a href="http://www.python.org">The Python Homepage</a>
-                </li>
-            </ul>
-
-            <p>You can check out some extra useful
-            links <a href="./extra/">here</a>.</p>
-
-            <p>[<a href="../">Return</a>]</p>
-        '''
-
-
-class ExtraLinksPage:
-
-    @cherrypy.expose
-    def index(self):
-        # Note the relative link back to the Links page!
-        return '''
-            <p>Here are some extra useful links:</p>
-
-            <ul>
-                <li><a href="http://del.icio.us">del.icio.us</a></li>
-                <li><a href="http://www.cherrypy.org">CherryPy</a></li>
-            </ul>
-
-            <p>[<a href="../">Return to links page</a>]</p>'''
-
-
-# Of course we can also mount request handler objects right here!
-root = HomePage()
-root.joke = JokePage()
-root.links = LinksPage()
-
-# Remember, we don't need to mount ExtraLinksPage here, because
-# LinksPage does that itself on initialization. In fact, there is
-# no reason why you shouldn't let your root object take care of
-# creating all contained request handler objects.
-
-
-tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
-
-if __name__ == '__main__':
-    # CherryPy always starts with app.root when trying to map request URIs
-    # to objects, so we need to mount a request handler root. A request
-    # to '/' will be mapped to HelloWorld().index().
-    cherrypy.quickstart(root, config=tutconf)
diff --git a/libraries/cherrypy/tutorial/tut05_derived_objects.py b/libraries/cherrypy/tutorial/tut05_derived_objects.py
deleted file mode 100644
index f626e03f..00000000
--- a/libraries/cherrypy/tutorial/tut05_derived_objects.py
+++ /dev/null
@@ -1,80 +0,0 @@
-"""
-Tutorial - Object inheritance
-
-You are free to derive your request handler classes from any base
-class you wish. In most real-world applications, you will probably
-want to create a central base class used for all your pages, which takes
-care of things like printing a common page header and footer.
-"""
-
-import os.path
-
-import cherrypy
-
-
-class Page:
-    # Store the page title in a class attribute
-    title = 'Untitled Page'
-
-    def header(self):
-        return '''
-            <html>
-            <head>
-                <title>%s</title>
-            <head>
-            <body>
-            <h2>%s</h2>
-        ''' % (self.title, self.title)
-
-    def footer(self):
-        return '''
-            </body>
-            </html>
-        '''
-
-    # Note that header and footer don't get their exposed attributes
-    # set to True. This isn't necessary since the user isn't supposed
-    # to call header or footer directly; instead, we'll call them from
-    # within the actually exposed handler methods defined in this
-    # class' subclasses.
-
-
-class HomePage(Page):
-    # Different title for this page
-    title = 'Tutorial 5'
-
-    def __init__(self):
-        # create a subpage
-        self.another = AnotherPage()
-
-    @cherrypy.expose
-    def index(self):
-        # Note that we call the header and footer methods inherited
-        # from the Page class!
-        return self.header() + '''
-            <p>
-            Isn't this exciting? There's
-            <a href="./another/">another page</a>, too!
-            </p>
-        ''' + self.footer()
-
-
-class AnotherPage(Page):
-    title = 'Another Page'
-
-    @cherrypy.expose
-    def index(self):
-        return self.header() + '''
-            <p>
-            And this is the amazing second page!
-            </p>
-        ''' + self.footer()
-
-
-tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
-
-if __name__ == '__main__':
-    # CherryPy always starts with app.root when trying to map request URIs
-    # to objects, so we need to mount a request handler root. A request
-    # to '/' will be mapped to HelloWorld().index().
-    cherrypy.quickstart(HomePage(), config=tutconf)
diff --git a/libraries/cherrypy/tutorial/tut06_default_method.py b/libraries/cherrypy/tutorial/tut06_default_method.py
deleted file mode 100644
index 0ce4cabe..00000000
--- a/libraries/cherrypy/tutorial/tut06_default_method.py
+++ /dev/null
@@ -1,61 +0,0 @@
-"""
-Tutorial - The default method
-
-Request handler objects can implement a method called "default" that
-is called when no other suitable method/object could be found.
-Essentially, if CherryPy2 can't find a matching request handler object
-for the given request URI, it will use the default method of the object
-located deepest on the URI path.
-
-Using this mechanism you can easily simulate virtual URI structures
-by parsing the extra URI string, which you can access through
-cherrypy.request.virtualPath.
-
-The application in this tutorial simulates an URI structure looking
-like /users/<username>. Since the <username> bit will not be found (as
-there are no matching methods), it is handled by the default method.
-"""
-
-import os.path
-
-import cherrypy
-
-
-class UsersPage:
-
-    @cherrypy.expose
-    def index(self):
-        # Since this is just a stupid little example, we'll simply
-        # display a list of links to random, made-up users. In a real
-        # application, this could be generated from a database result set.
-        return '''
-            <a href="./remi">Remi Delon</a><br/>
-            <a href="./hendrik">Hendrik Mans</a><br/>
-            <a href="./lorenzo">Lorenzo Lamas</a><br/>
-        '''
-
-    @cherrypy.expose
-    def default(self, user):
-        # Here we react depending on the virtualPath -- the part of the
-        # path that could not be mapped to an object method. In a real
-        # application, we would probably do some database lookups here
-        # instead of the silly if/elif/else construct.
-        if user == 'remi':
-            out = 'Remi Delon, CherryPy lead developer'
-        elif user == 'hendrik':
-            out = 'Hendrik Mans, CherryPy co-developer & crazy German'
-        elif user == 'lorenzo':
-            out = 'Lorenzo Lamas, famous actor and singer!'
-        else:
-            out = 'Unknown user. :-('
-
-        return '%s (<a href="./">back</a>)' % out
-
-
-tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
-
-if __name__ == '__main__':
-    # CherryPy always starts with app.root when trying to map request URIs
-    # to objects, so we need to mount a request handler root. A request
-    # to '/' will be mapped to HelloWorld().index().
-    cherrypy.quickstart(UsersPage(), config=tutconf)
diff --git a/libraries/cherrypy/tutorial/tut07_sessions.py b/libraries/cherrypy/tutorial/tut07_sessions.py
deleted file mode 100644
index 204322b5..00000000
--- a/libraries/cherrypy/tutorial/tut07_sessions.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""
-Tutorial - Sessions
-
-Storing session data in CherryPy applications is very easy: cherrypy
-provides a dictionary called "session" that represents the session
-data for the current user. If you use RAM based sessions, you can store
-any kind of object into that dictionary; otherwise, you are limited to
-objects that can be pickled.
-"""
-
-import os.path
-
-import cherrypy
-
-
-class HitCounter:
-
-    _cp_config = {'tools.sessions.on': True}
-
-    @cherrypy.expose
-    def index(self):
-        # Increase the silly hit counter
-        count = cherrypy.session.get('count', 0) + 1
-
-        # Store the new value in the session dictionary
-        cherrypy.session['count'] = count
-
-        # And display a silly hit count message!
-        return '''
-            During your current session, you've viewed this
-            page %s times! Your life is a patio of fun!
-        ''' % count
-
-
-tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
-
-if __name__ == '__main__':
-    # CherryPy always starts with app.root when trying to map request URIs
-    # to objects, so we need to mount a request handler root. A request
-    # to '/' will be mapped to HelloWorld().index().
-    cherrypy.quickstart(HitCounter(), config=tutconf)
diff --git a/libraries/cherrypy/tutorial/tut08_generators_and_yield.py b/libraries/cherrypy/tutorial/tut08_generators_and_yield.py
deleted file mode 100644
index 18f42f93..00000000
--- a/libraries/cherrypy/tutorial/tut08_generators_and_yield.py
+++ /dev/null
@@ -1,44 +0,0 @@
-"""
-Bonus Tutorial: Using generators to return result bodies
-
-Instead of returning a complete result string, you can use the yield
-statement to return one result part after another. This may be convenient
-in situations where using a template package like CherryPy or Cheetah
-would be overkill, and messy string concatenation too uncool. ;-)
-"""
-
-import os.path
-
-import cherrypy
-
-
-class GeneratorDemo:
-
-    def header(self):
-        return '<html><body><h2>Generators rule!</h2>'
-
-    def footer(self):
-        return '</body></html>'
-
-    @cherrypy.expose
-    def index(self):
-        # Let's make up a list of users for presentation purposes
-        users = ['Remi', 'Carlos', 'Hendrik', 'Lorenzo Lamas']
-
-        # Every yield line adds one part to the total result body.
-        yield self.header()
-        yield '<h3>List of users:</h3>'
-
-        for user in users:
-            yield '%s<br/>' % user
-
-        yield self.footer()
-
-
-tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
-
-if __name__ == '__main__':
-    # CherryPy always starts with app.root when trying to map request URIs
-    # to objects, so we need to mount a request handler root. A request
-    # to '/' will be mapped to HelloWorld().index().
-    cherrypy.quickstart(GeneratorDemo(), config=tutconf)
diff --git a/libraries/cherrypy/tutorial/tut09_files.py b/libraries/cherrypy/tutorial/tut09_files.py
deleted file mode 100644
index 48585cbe..00000000
--- a/libraries/cherrypy/tutorial/tut09_files.py
+++ /dev/null
@@ -1,105 +0,0 @@
-"""
-
-Tutorial: File upload and download
-
-Uploads
--------
-
-When a client uploads a file to a CherryPy application, it's placed
-on disk immediately. CherryPy will pass it to your exposed method
-as an argument (see "myFile" below); that arg will have a "file"
-attribute, which is a handle to the temporary uploaded file.
-If you wish to permanently save the file, you need to read()
-from myFile.file and write() somewhere else.
-
-Note the use of 'enctype="multipart/form-data"' and 'input type="file"'
-in the HTML which the client uses to upload the file.
-
-
-Downloads
----------
-
-If you wish to send a file to the client, you have two options:
-First, you can simply return a file-like object from your page handler.
-CherryPy will read the file and serve it as the content (HTTP body)
-of the response. However, that doesn't tell the client that
-the response is a file to be saved, rather than displayed.
-Use cherrypy.lib.static.serve_file for that; it takes four
-arguments:
-
-serve_file(path, content_type=None, disposition=None, name=None)
-
-Set "name" to the filename that you expect clients to use when they save
-your file. Note that the "name" argument is ignored if you don't also
-provide a "disposition" (usually "attachement"). You can manually set
-"content_type", but be aware that if you also use the encoding tool, it
-may choke if the file extension is not recognized as belonging to a known
-Content-Type. Setting the content_type to "application/x-download" works
-in most cases, and should prompt the user with an Open/Save dialog in
-popular browsers.
-
-"""
-
-import os
-import os.path
-
-import cherrypy
-from cherrypy.lib import static
-
-localDir = os.path.dirname(__file__)
-absDir = os.path.join(os.getcwd(), localDir)
-
-
-class FileDemo(object):
-
-    @cherrypy.expose
-    def index(self):
-        return """
-        <html><body>
-            <h2>Upload a file</h2>
-            <form action="upload" method="post" enctype="multipart/form-data">
-            filename: <input type="file" name="myFile" /><br />
-            <input type="submit" />
-            </form>
-            <h2>Download a file</h2>
-            <a href='download'>This one</a>
-        </body></html>
-        """
-
-    @cherrypy.expose
-    def upload(self, myFile):
-        out = """<html>
-        <body>
-            myFile length: %s<br />
-            myFile filename: %s<br />
-            myFile mime-type: %s
-        </body>
-        </html>"""
-
-        # Although this just counts the file length, it demonstrates
-        # how to read large files in chunks instead of all at once.
-        # CherryPy reads the uploaded file into a temporary file;
-        # myFile.file.read reads from that.
-        size = 0
-        while True:
-            data = myFile.file.read(8192)
-            if not data:
-                break
-            size += len(data)
-
-        return out % (size, myFile.filename, myFile.content_type)
-
-    @cherrypy.expose
-    def download(self):
-        path = os.path.join(absDir, 'pdf_file.pdf')
-        return static.serve_file(path, 'application/x-download',
-                                 'attachment', os.path.basename(path))
-
-
-tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
-
-if __name__ == '__main__':
-    # CherryPy always starts with app.root when trying to map request URIs
-    # to objects, so we need to mount a request handler root. A request
-    # to '/' will be mapped to HelloWorld().index().
-    cherrypy.quickstart(FileDemo(), config=tutconf)
diff --git a/libraries/cherrypy/tutorial/tut10_http_errors.py b/libraries/cherrypy/tutorial/tut10_http_errors.py
deleted file mode 100644
index 18f02fd0..00000000
--- a/libraries/cherrypy/tutorial/tut10_http_errors.py
+++ /dev/null
@@ -1,84 +0,0 @@
-"""
-
-Tutorial: HTTP errors
-
-HTTPError is used to return an error response to the client.
-CherryPy has lots of options regarding how such errors are
-logged, displayed, and formatted.
-
-"""
-
-import os
-import os.path
-
-import cherrypy
-
-localDir = os.path.dirname(__file__)
-curpath = os.path.normpath(os.path.join(os.getcwd(), localDir))
-
-
-class HTTPErrorDemo(object):
-
-    # Set a custom response for 403 errors.
-    _cp_config = {'error_page.403':
-                  os.path.join(curpath, 'custom_error.html')}
-
-    @cherrypy.expose
-    def index(self):
-        # display some links that will result in errors
-        tracebacks = cherrypy.request.show_tracebacks
-        if tracebacks:
-            trace = 'off'
-        else:
-            trace = 'on'
-
-        return """
-        <html><body>
-            <p>Toggle tracebacks <a href="toggleTracebacks">%s</a></p>
-            <p><a href="/doesNotExist">Click me; I'm a broken link!</a></p>
-            <p>
-              <a href="/error?code=403">
-                Use a custom error page from a file.
-              </a>
-            </p>
-            <p>These errors are explicitly raised by the application:</p>
-            <ul>
-                <li><a href="/error?code=400">400</a></li>
-                <li><a href="/error?code=401">401</a></li>
-                <li><a href="/error?code=402">402</a></li>
-                <li><a href="/error?code=500">500</a></li>
-            </ul>
-            <p><a href="/messageArg">You can also set the response body
-            when you raise an error.</a></p>
-        </body></html>
-        """ % trace
-
-    @cherrypy.expose
-    def toggleTracebacks(self):
-        # simple function to toggle tracebacks on and off
-        tracebacks = cherrypy.request.show_tracebacks
-        cherrypy.config.update({'request.show_tracebacks': not tracebacks})
-
-        # redirect back to the index
-        raise cherrypy.HTTPRedirect('/')
-
-    @cherrypy.expose
-    def error(self, code):
-        # raise an error based on the get query
-        raise cherrypy.HTTPError(status=code)
-
-    @cherrypy.expose
-    def messageArg(self):
-        message = ("If you construct an HTTPError with a 'message' "
-                   'argument, it wil be placed on the error page '
-                   '(underneath the status line by default).')
-        raise cherrypy.HTTPError(500, message=message)
-
-
-tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
-
-if __name__ == '__main__':
-    # CherryPy always starts with app.root when trying to map request URIs
-    # to objects, so we need to mount a request handler root. A request
-    # to '/' will be mapped to HelloWorld().index().
-    cherrypy.quickstart(HTTPErrorDemo(), config=tutconf)
diff --git a/libraries/cherrypy/tutorial/tutorial.conf b/libraries/cherrypy/tutorial/tutorial.conf
deleted file mode 100644
index 43dfa60f..00000000
--- a/libraries/cherrypy/tutorial/tutorial.conf
+++ /dev/null
@@ -1,4 +0,0 @@
-[global]
-server.socket_host = "127.0.0.1"
-server.socket_port = 8080
-server.thread_pool = 10
diff --git a/libraries/contextlib2.py b/libraries/contextlib2.py
deleted file mode 100644
index f08df14c..00000000
--- a/libraries/contextlib2.py
+++ /dev/null
@@ -1,436 +0,0 @@
-"""contextlib2 - backports and enhancements to the contextlib module"""
-
-import sys
-import warnings
-from collections import deque
-from functools import wraps
-
-__all__ = ["contextmanager", "closing", "ContextDecorator", "ExitStack",
-           "redirect_stdout", "redirect_stderr", "suppress"]
-
-# Backwards compatibility
-__all__ += ["ContextStack"]
-
-class ContextDecorator(object):
-    "A base class or mixin that enables context managers to work as decorators."
-
-    def refresh_cm(self):
-        """Returns the context manager used to actually wrap the call to the
-        decorated function.
-
-        The default implementation just returns *self*.
-
-        Overriding this method allows otherwise one-shot context managers
-        like _GeneratorContextManager to support use as decorators via
-        implicit recreation.
-
-        DEPRECATED: refresh_cm was never added to the standard library's
-                    ContextDecorator API
-        """
-        warnings.warn("refresh_cm was never added to the standard library",
-                      DeprecationWarning)
-        return self._recreate_cm()
-
-    def _recreate_cm(self):
-        """Return a recreated instance of self.
-
-        Allows an otherwise one-shot context manager like
-        _GeneratorContextManager to support use as
-        a decorator via implicit recreation.
-
-        This is a private interface just for _GeneratorContextManager.
-        See issue #11647 for details.
-        """
-        return self
-
-    def __call__(self, func):
-        @wraps(func)
-        def inner(*args, **kwds):
-            with self._recreate_cm():
-                return func(*args, **kwds)
-        return inner
-
-
-class _GeneratorContextManager(ContextDecorator):
-    """Helper for @contextmanager decorator."""
-
-    def __init__(self, func, args, kwds):
-        self.gen = func(*args, **kwds)
-        self.func, self.args, self.kwds = func, args, kwds
-        # Issue 19330: ensure context manager instances have good docstrings
-        doc = getattr(func, "__doc__", None)
-        if doc is None:
-            doc = type(self).__doc__
-        self.__doc__ = doc
-        # Unfortunately, this still doesn't provide good help output when
-        # inspecting the created context manager instances, since pydoc
-        # currently bypasses the instance docstring and shows the docstring
-        # for the class instead.
-        # See http://bugs.python.org/issue19404 for more details.
-
-    def _recreate_cm(self):
-        # _GCM instances are one-shot context managers, so the
-        # CM must be recreated each time a decorated function is
-        # called
-        return self.__class__(self.func, self.args, self.kwds)
-
-    def __enter__(self):
-        try:
-            return next(self.gen)
-        except StopIteration:
-            raise RuntimeError("generator didn't yield")
-
-    def __exit__(self, type, value, traceback):
-        if type is None:
-            try:
-                next(self.gen)
-            except StopIteration:
-                return
-            else:
-                raise RuntimeError("generator didn't stop")
-        else:
-            if value is None:
-                # Need to force instantiation so we can reliably
-                # tell if we get the same exception back
-                value = type()
-            try:
-                self.gen.throw(type, value, traceback)
-                raise RuntimeError("generator didn't stop after throw()")
-            except StopIteration as exc:
-                # Suppress StopIteration *unless* it's the same exception that
-                # was passed to throw().  This prevents a StopIteration
-                # raised inside the "with" statement from being suppressed.
-                return exc is not value
-            except RuntimeError as exc:
-                # Don't re-raise the passed in exception
-                if exc is value:
-                    return False
-                # Likewise, avoid suppressing if a StopIteration exception
-                # was passed to throw() and later wrapped into a RuntimeError
-                # (see PEP 479).
-                if _HAVE_EXCEPTION_CHAINING and exc.__cause__ is value:
-                    return False
-                raise
-            except:
-                # only re-raise if it's *not* the exception that was
-                # passed to throw(), because __exit__() must not raise
-                # an exception unless __exit__() itself failed.  But throw()
-                # has to raise the exception to signal propagation, so this
-                # fixes the impedance mismatch between the throw() protocol
-                # and the __exit__() protocol.
-                #
-                if sys.exc_info()[1] is not value:
-                    raise
-
-
-def contextmanager(func):
-    """@contextmanager decorator.
-
-    Typical usage:
-
-        @contextmanager
-        def some_generator(<arguments>):
-            <setup>
-            try:
-                yield <value>
-            finally:
-                <cleanup>
-
-    This makes this:
-
-        with some_generator(<arguments>) as <variable>:
-            <body>
-
-    equivalent to this:
-
-        <setup>
-        try:
-            <variable> = <value>
-            <body>
-        finally:
-            <cleanup>
-
-    """
-    @wraps(func)
-    def helper(*args, **kwds):
-        return _GeneratorContextManager(func, args, kwds)
-    return helper
-
-
-class closing(object):
-    """Context to automatically close something at the end of a block.
-
-    Code like this:
-
-        with closing(<module>.open(<arguments>)) as f:
-            <block>
-
-    is equivalent to this:
-
-        f = <module>.open(<arguments>)
-        try:
-            <block>
-        finally:
-            f.close()
-
-    """
-    def __init__(self, thing):
-        self.thing = thing
-    def __enter__(self):
-        return self.thing
-    def __exit__(self, *exc_info):
-        self.thing.close()
-
-
-class _RedirectStream(object):
-
-    _stream = None
-
-    def __init__(self, new_target):
-        self._new_target = new_target
-        # We use a list of old targets to make this CM re-entrant
-        self._old_targets = []
-
-    def __enter__(self):
-        self._old_targets.append(getattr(sys, self._stream))
-        setattr(sys, self._stream, self._new_target)
-        return self._new_target
-
-    def __exit__(self, exctype, excinst, exctb):
-        setattr(sys, self._stream, self._old_targets.pop())
-
-
-class redirect_stdout(_RedirectStream):
-    """Context manager for temporarily redirecting stdout to another file.
-
-        # How to send help() to stderr
-        with redirect_stdout(sys.stderr):
-            help(dir)
-
-        # How to write help() to a file
-        with open('help.txt', 'w') as f:
-            with redirect_stdout(f):
-                help(pow)
-    """
-
-    _stream = "stdout"
-
-
-class redirect_stderr(_RedirectStream):
-    """Context manager for temporarily redirecting stderr to another file."""
-
-    _stream = "stderr"
-
-
-class suppress(object):
-    """Context manager to suppress specified exceptions
-
-    After the exception is suppressed, execution proceeds with the next
-    statement following the with statement.
-
-         with suppress(FileNotFoundError):
-             os.remove(somefile)
-         # Execution still resumes here if the file was already removed
-    """
-
-    def __init__(self, *exceptions):
-        self._exceptions = exceptions
-
-    def __enter__(self):
-        pass
-
-    def __exit__(self, exctype, excinst, exctb):
-        # Unlike isinstance and issubclass, CPython exception handling
-        # currently only looks at the concrete type hierarchy (ignoring
-        # the instance and subclass checking hooks). While Guido considers
-        # that a bug rather than a feature, it's a fairly hard one to fix
-        # due to various internal implementation details. suppress provides
-        # the simpler issubclass based semantics, rather than trying to
-        # exactly reproduce the limitations of the CPython interpreter.
-        #
-        # See http://bugs.python.org/issue12029 for more details
-        return exctype is not None and issubclass(exctype, self._exceptions)
-
-
-# Context manipulation is Python 3 only
-_HAVE_EXCEPTION_CHAINING = sys.version_info[0] >= 3
-if _HAVE_EXCEPTION_CHAINING:
-    def _make_context_fixer(frame_exc):
-        def _fix_exception_context(new_exc, old_exc):
-            # Context may not be correct, so find the end of the chain
-            while 1:
-                exc_context = new_exc.__context__
-                if exc_context is old_exc:
-                    # Context is already set correctly (see issue 20317)
-                    return
-                if exc_context is None or exc_context is frame_exc:
-                    break
-                new_exc = exc_context
-            # Change the end of the chain to point to the exception
-            # we expect it to reference
-            new_exc.__context__ = old_exc
-        return _fix_exception_context
-
-    def _reraise_with_existing_context(exc_details):
-        try:
-            # bare "raise exc_details[1]" replaces our carefully
-            # set-up context
-            fixed_ctx = exc_details[1].__context__
-            raise exc_details[1]
-        except BaseException:
-            exc_details[1].__context__ = fixed_ctx
-            raise
-else:
-    # No exception context in Python 2
-    def _make_context_fixer(frame_exc):
-        return lambda new_exc, old_exc: None
-
-    # Use 3 argument raise in Python 2,
-    # but use exec to avoid SyntaxError in Python 3
-    def _reraise_with_existing_context(exc_details):
-        exc_type, exc_value, exc_tb = exc_details
-        exec ("raise exc_type, exc_value, exc_tb")
-
-# Handle old-style classes if they exist
-try:
-    from types import InstanceType
-except ImportError:
-    # Python 3 doesn't have old-style classes
-    _get_type = type
-else:
-    # Need to handle old-style context managers on Python 2
-    def _get_type(obj):
-        obj_type = type(obj)
-        if obj_type is InstanceType:
-            return obj.__class__ # Old-style class
-        return obj_type # New-style class
-
-# Inspired by discussions on http://bugs.python.org/issue13585
-class ExitStack(object):
-    """Context manager for dynamic management of a stack of exit callbacks
-
-    For example:
-
-        with ExitStack() as stack:
-            files = [stack.enter_context(open(fname)) for fname in filenames]
-            # All opened files will automatically be closed at the end of
-            # the with statement, even if attempts to open files later
-            # in the list raise an exception
-
-    """
-    def __init__(self):
-        self._exit_callbacks = deque()
-
-    def pop_all(self):
-        """Preserve the context stack by transferring it to a new instance"""
-        new_stack = type(self)()
-        new_stack._exit_callbacks = self._exit_callbacks
-        self._exit_callbacks = deque()
-        return new_stack
-
-    def _push_cm_exit(self, cm, cm_exit):
-        """Helper to correctly register callbacks to __exit__ methods"""
-        def _exit_wrapper(*exc_details):
-            return cm_exit(cm, *exc_details)
-        _exit_wrapper.__self__ = cm
-        self.push(_exit_wrapper)
-
-    def push(self, exit):
-        """Registers a callback with the standard __exit__ method signature
-
-        Can suppress exceptions the same way __exit__ methods can.
-
-        Also accepts any object with an __exit__ method (registering a call
-        to the method instead of the object itself)
-        """
-        # We use an unbound method rather than a bound method to follow
-        # the standard lookup behaviour for special methods
-        _cb_type = _get_type(exit)
-        try:
-            exit_method = _cb_type.__exit__
-        except AttributeError:
-            # Not a context manager, so assume its a callable
-            self._exit_callbacks.append(exit)
-        else:
-            self._push_cm_exit(exit, exit_method)
-        return exit # Allow use as a decorator
-
-    def callback(self, callback, *args, **kwds):
-        """Registers an arbitrary callback and arguments.
-
-        Cannot suppress exceptions.
-        """
-        def _exit_wrapper(exc_type, exc, tb):
-            callback(*args, **kwds)
-        # We changed the signature, so using @wraps is not appropriate, but
-        # setting __wrapped__ may still help with introspection
-        _exit_wrapper.__wrapped__ = callback
-        self.push(_exit_wrapper)
-        return callback # Allow use as a decorator
-
-    def enter_context(self, cm):
-        """Enters the supplied context manager
-
-        If successful, also pushes its __exit__ method as a callback and
-        returns the result of the __enter__ method.
-        """
-        # We look up the special methods on the type to match the with statement
-        _cm_type = _get_type(cm)
-        _exit = _cm_type.__exit__
-        result = _cm_type.__enter__(cm)
-        self._push_cm_exit(cm, _exit)
-        return result
-
-    def close(self):
-        """Immediately unwind the context stack"""
-        self.__exit__(None, None, None)
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, *exc_details):
-        received_exc = exc_details[0] is not None
-
-        # We manipulate the exception state so it behaves as though
-        # we were actually nesting multiple with statements
-        frame_exc = sys.exc_info()[1]
-        _fix_exception_context = _make_context_fixer(frame_exc)
-
-        # Callbacks are invoked in LIFO order to match the behaviour of
-        # nested context managers
-        suppressed_exc = False
-        pending_raise = False
-        while self._exit_callbacks:
-            cb = self._exit_callbacks.pop()
-            try:
-                if cb(*exc_details):
-                    suppressed_exc = True
-                    pending_raise = False
-                    exc_details = (None, None, None)
-            except:
-                new_exc_details = sys.exc_info()
-                # simulate the stack of exceptions by setting the context
-                _fix_exception_context(new_exc_details[1], exc_details[1])
-                pending_raise = True
-                exc_details = new_exc_details
-        if pending_raise:
-            _reraise_with_existing_context(exc_details)
-        return received_exc and suppressed_exc
-
-# Preserve backwards compatibility
-class ContextStack(ExitStack):
-    """Backwards compatibility alias for ExitStack"""
-
-    def __init__(self):
-        warnings.warn("ContextStack has been renamed to ExitStack",
-                      DeprecationWarning)
-        super(ContextStack, self).__init__()
-
-    def register_exit(self, callback):
-        return self.push(callback)
-
-    def register(self, callback, *args, **kwds):
-        return self.callback(callback, *args, **kwds)
-
-    def preserve(self):
-        return self.pop_all()
diff --git a/libraries/more_itertools/__init__.py b/libraries/more_itertools/__init__.py
deleted file mode 100644
index bba462c3..00000000
--- a/libraries/more_itertools/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from more_itertools.more import *  # noqa
-from more_itertools.recipes import *  # noqa
diff --git a/libraries/more_itertools/more.py b/libraries/more_itertools/more.py
deleted file mode 100644
index 05e851ee..00000000
--- a/libraries/more_itertools/more.py
+++ /dev/null
@@ -1,2211 +0,0 @@
-from __future__ import print_function
-
-from collections import Counter, defaultdict, deque
-from functools import partial, wraps
-from heapq import merge
-from itertools import (
-    chain,
-    compress,
-    count,
-    cycle,
-    dropwhile,
-    groupby,
-    islice,
-    repeat,
-    starmap,
-    takewhile,
-    tee
-)
-from operator import itemgetter, lt, gt, sub
-from sys import maxsize, version_info
-try:
-    from collections.abc import Sequence
-except ImportError:
-    from collections import Sequence
-
-from six import binary_type, string_types, text_type
-from six.moves import filter, map, range, zip, zip_longest
-
-from .recipes import consume, flatten, take
-
-__all__ = [
-    'adjacent',
-    'always_iterable',
-    'always_reversible',
-    'bucket',
-    'chunked',
-    'circular_shifts',
-    'collapse',
-    'collate',
-    'consecutive_groups',
-    'consumer',
-    'count_cycle',
-    'difference',
-    'distinct_permutations',
-    'distribute',
-    'divide',
-    'exactly_n',
-    'first',
-    'groupby_transform',
-    'ilen',
-    'interleave_longest',
-    'interleave',
-    'intersperse',
-    'islice_extended',
-    'iterate',
-    'last',
-    'locate',
-    'lstrip',
-    'make_decorator',
-    'map_reduce',
-    'numeric_range',
-    'one',
-    'padded',
-    'peekable',
-    'replace',
-    'rlocate',
-    'rstrip',
-    'run_length',
-    'seekable',
-    'SequenceView',
-    'side_effect',
-    'sliced',
-    'sort_together',
-    'split_at',
-    'split_after',
-    'split_before',
-    'spy',
-    'stagger',
-    'strip',
-    'unique_to_each',
-    'windowed',
-    'with_iter',
-    'zip_offset',
-]
-
-_marker = object()
-
-
-def chunked(iterable, n):
-    """Break *iterable* into lists of length *n*:
-
-        >>> list(chunked([1, 2, 3, 4, 5, 6], 3))
-        [[1, 2, 3], [4, 5, 6]]
-
-    If the length of *iterable* is not evenly divisible by *n*, the last
-    returned list will be shorter:
-
-        >>> list(chunked([1, 2, 3, 4, 5, 6, 7, 8], 3))
-        [[1, 2, 3], [4, 5, 6], [7, 8]]
-
-    To use a fill-in value instead, see the :func:`grouper` recipe.
-
-    :func:`chunked` is useful for splitting up a computation on a large number
-    of keys into batches, to be pickled and sent off to worker processes. One
-    example is operations on rows in MySQL, which does not implement
-    server-side cursors properly and would otherwise load the entire dataset
-    into RAM on the client.
-
-    """
-    return iter(partial(take, n, iter(iterable)), [])
-
-
-def first(iterable, default=_marker):
-    """Return the first item of *iterable*, or *default* if *iterable* is
-    empty.
-
-        >>> first([0, 1, 2, 3])
-        0
-        >>> first([], 'some default')
-        'some default'
-
-    If *default* is not provided and there are no items in the iterable,
-    raise ``ValueError``.
-
-    :func:`first` is useful when you have a generator of expensive-to-retrieve
-    values and want any arbitrary one. It is marginally shorter than
-    ``next(iter(iterable), default)``.
-
-    """
-    try:
-        return next(iter(iterable))
-    except StopIteration:
-        # I'm on the edge about raising ValueError instead of StopIteration. At
-        # the moment, ValueError wins, because the caller could conceivably
-        # want to do something different with flow control when I raise the
-        # exception, and it's weird to explicitly catch StopIteration.
-        if default is _marker:
-            raise ValueError('first() was called on an empty iterable, and no '
-                             'default value was provided.')
-        return default
-
-
-def last(iterable, default=_marker):
-    """Return the last item of *iterable*, or *default* if *iterable* is
-    empty.
-
-        >>> last([0, 1, 2, 3])
-        3
-        >>> last([], 'some default')
-        'some default'
-
-    If *default* is not provided and there are no items in the iterable,
-    raise ``ValueError``.
-    """
-    try:
-        try:
-            # Try to access the last item directly
-            return iterable[-1]
-        except (TypeError, AttributeError, KeyError):
-            # If not slice-able, iterate entirely using length-1 deque
-            return deque(iterable, maxlen=1)[0]
-    except IndexError:  # If the iterable was empty
-        if default is _marker:
-            raise ValueError('last() was called on an empty iterable, and no '
-                             'default value was provided.')
-        return default
-
-
-class peekable(object):
-    """Wrap an iterator to allow lookahead and prepending elements.
-
-    Call :meth:`peek` on the result to get the value that will be returned
-    by :func:`next`. This won't advance the iterator:
-
-        >>> p = peekable(['a', 'b'])
-        >>> p.peek()
-        'a'
-        >>> next(p)
-        'a'
-
-    Pass :meth:`peek` a default value to return that instead of raising
-    ``StopIteration`` when the iterator is exhausted.
-
-        >>> p = peekable([])
-        >>> p.peek('hi')
-        'hi'
-
-    peekables also offer a :meth:`prepend` method, which "inserts" items
-    at the head of the iterable:
-
-        >>> p = peekable([1, 2, 3])
-        >>> p.prepend(10, 11, 12)
-        >>> next(p)
-        10
-        >>> p.peek()
-        11
-        >>> list(p)
-        [11, 12, 1, 2, 3]
-
-    peekables can be indexed. Index 0 is the item that will be returned by
-    :func:`next`, index 1 is the item after that, and so on:
-    The values up to the given index will be cached.
-
-        >>> p = peekable(['a', 'b', 'c', 'd'])
-        >>> p[0]
-        'a'
-        >>> p[1]
-        'b'
-        >>> next(p)
-        'a'
-
-    Negative indexes are supported, but be aware that they will cache the
-    remaining items in the source iterator, which may require significant
-    storage.
-
-    To check whether a peekable is exhausted, check its truth value:
-
-        >>> p = peekable(['a', 'b'])
-        >>> if p:  # peekable has items
-        ...     list(p)
-        ['a', 'b']
-        >>> if not p:  # peekable is exhaused
-        ...     list(p)
-        []
-
-    """
-    def __init__(self, iterable):
-        self._it = iter(iterable)
-        self._cache = deque()
-
-    def __iter__(self):
-        return self
-
-    def __bool__(self):
-        try:
-            self.peek()
-        except StopIteration:
-            return False
-        return True
-
-    def __nonzero__(self):
-        # For Python 2 compatibility
-        return self.__bool__()
-
-    def peek(self, default=_marker):
-        """Return the item that will be next returned from ``next()``.
-
-        Return ``default`` if there are no items left. If ``default`` is not
-        provided, raise ``StopIteration``.
-
-        """
-        if not self._cache:
-            try:
-                self._cache.append(next(self._it))
-            except StopIteration:
-                if default is _marker:
-                    raise
-                return default
-        return self._cache[0]
-
-    def prepend(self, *items):
-        """Stack up items to be the next ones returned from ``next()`` or
-        ``self.peek()``. The items will be returned in
-        first in, first out order::
-
-            >>> p = peekable([1, 2, 3])
-            >>> p.prepend(10, 11, 12)
-            >>> next(p)
-            10
-            >>> list(p)
-            [11, 12, 1, 2, 3]
-
-        It is possible, by prepending items, to "resurrect" a peekable that
-        previously raised ``StopIteration``.
-
-            >>> p = peekable([])
-            >>> next(p)
-            Traceback (most recent call last):
-              ...
-            StopIteration
-            >>> p.prepend(1)
-            >>> next(p)
-            1
-            >>> next(p)
-            Traceback (most recent call last):
-              ...
-            StopIteration
-
-        """
-        self._cache.extendleft(reversed(items))
-
-    def __next__(self):
-        if self._cache:
-            return self._cache.popleft()
-
-        return next(self._it)
-
-    next = __next__  # For Python 2 compatibility
-
-    def _get_slice(self, index):
-        # Normalize the slice's arguments
-        step = 1 if (index.step is None) else index.step
-        if step > 0:
-            start = 0 if (index.start is None) else index.start
-            stop = maxsize if (index.stop is None) else index.stop
-        elif step < 0:
-            start = -1 if (index.start is None) else index.start
-            stop = (-maxsize - 1) if (index.stop is None) else index.stop
-        else:
-            raise ValueError('slice step cannot be zero')
-
-        # If either the start or stop index is negative, we'll need to cache
-        # the rest of the iterable in order to slice from the right side.
-        if (start < 0) or (stop < 0):
-            self._cache.extend(self._it)
-        # Otherwise we'll need to find the rightmost index and cache to that
-        # point.
-        else:
-            n = min(max(start, stop) + 1, maxsize)
-            cache_len = len(self._cache)
-            if n >= cache_len:
-                self._cache.extend(islice(self._it, n - cache_len))
-
-        return list(self._cache)[index]
-
-    def __getitem__(self, index):
-        if isinstance(index, slice):
-            return self._get_slice(index)
-
-        cache_len = len(self._cache)
-        if index < 0:
-            self._cache.extend(self._it)
-        elif index >= cache_len:
-            self._cache.extend(islice(self._it, index + 1 - cache_len))
-
-        return self._cache[index]
-
-
-def _collate(*iterables, **kwargs):
-    """Helper for ``collate()``, called when the user is using the ``reverse``
-    or ``key`` keyword arguments on Python versions below 3.5.
-
-    """
-    key = kwargs.pop('key', lambda a: a)
-    reverse = kwargs.pop('reverse', False)
-
-    min_or_max = partial(max if reverse else min, key=itemgetter(0))
-    peekables = [peekable(it) for it in iterables]
-    peekables = [p for p in peekables if p]  # Kill empties.
-    while peekables:
-        _, p = min_or_max((key(p.peek()), p) for p in peekables)
-        yield next(p)
-        peekables = [x for x in peekables if x]
-
-
-def collate(*iterables, **kwargs):
-    """Return a sorted merge of the items from each of several already-sorted
-    *iterables*.
-
-        >>> list(collate('ACDZ', 'AZ', 'JKL'))
-        ['A', 'A', 'C', 'D', 'J', 'K', 'L', 'Z', 'Z']
-
-    Works lazily, keeping only the next value from each iterable in memory. Use
-    :func:`collate` to, for example, perform a n-way mergesort of items that
-    don't fit in memory.
-
-    If a *key* function is specified, the iterables will be sorted according
-    to its result:
-
-        >>> key = lambda s: int(s)  # Sort by numeric value, not by string
-        >>> list(collate(['1', '10'], ['2', '11'], key=key))
-        ['1', '2', '10', '11']
-
-
-    If the *iterables* are sorted in descending order, set *reverse* to
-    ``True``:
-
-        >>> list(collate([5, 3, 1], [4, 2, 0], reverse=True))
-        [5, 4, 3, 2, 1, 0]
-
-    If the elements of the passed-in iterables are out of order, you might get
-    unexpected results.
-
-    On Python 2.7, this function delegates to :func:`heapq.merge` if neither
-    of the keyword arguments are specified. On Python 3.5+, this function
-    is an alias for :func:`heapq.merge`.
-
-    """
-    if not kwargs:
-        return merge(*iterables)
-
-    return _collate(*iterables, **kwargs)
-
-
-# If using Python version 3.5 or greater, heapq.merge() will be faster than
-# collate - use that instead.
-if version_info >= (3, 5, 0):
-    _collate_docstring = collate.__doc__
-    collate = partial(merge)
-    collate.__doc__ = _collate_docstring
-
-
-def consumer(func):
-    """Decorator that automatically advances a PEP-342-style "reverse iterator"
-    to its first yield point so you don't have to call ``next()`` on it
-    manually.
-
-        >>> @consumer
-        ... def tally():
-        ...     i = 0
-        ...     while True:
-        ...         print('Thing number %s is %s.' % (i, (yield)))
-        ...         i += 1
-        ...
-        >>> t = tally()
-        >>> t.send('red')
-        Thing number 0 is red.
-        >>> t.send('fish')
-        Thing number 1 is fish.
-
-    Without the decorator, you would have to call ``next(t)`` before
-    ``t.send()`` could be used.
-
-    """
-    @wraps(func)
-    def wrapper(*args, **kwargs):
-        gen = func(*args, **kwargs)
-        next(gen)
-        return gen
-    return wrapper
-
-
-def ilen(iterable):
-    """Return the number of items in *iterable*.
-
-        >>> ilen(x for x in range(1000000) if x % 3 == 0)
-        333334
-
-    This consumes the iterable, so handle with care.
-
-    """
-    # maxlen=1 only stores the last item in the deque
-    d = deque(enumerate(iterable, 1), maxlen=1)
-    # since we started enumerate at 1,
-    # the first item of the last pair will be the length of the iterable
-    # (assuming there were items)
-    return d[0][0] if d else 0
-
-
-def iterate(func, start):
-    """Return ``start``, ``func(start)``, ``func(func(start))``, ...
-
-        >>> from itertools import islice
-        >>> list(islice(iterate(lambda x: 2*x, 1), 10))
-        [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
-
-    """
-    while True:
-        yield start
-        start = func(start)
-
-
-def with_iter(context_manager):
-    """Wrap an iterable in a ``with`` statement, so it closes once exhausted.
-
-    For example, this will close the file when the iterator is exhausted::
-
-        upper_lines = (line.upper() for line in with_iter(open('foo')))
-
-    Any context manager which returns an iterable is a candidate for
-    ``with_iter``.
-
-    """
-    with context_manager as iterable:
-        for item in iterable:
-            yield item
-
-
-def one(iterable, too_short=None, too_long=None):
-    """Return the first item from *iterable*, which is expected to contain only
-    that item. Raise an exception if *iterable* is empty or has more than one
-    item.
-
-    :func:`one` is useful for ensuring that an iterable contains only one item.
-    For example, it can be used to retrieve the result of a database query
-    that is expected to return a single row.
-
-    If *iterable* is empty, ``ValueError`` will be raised. You may specify a
-    different exception with the *too_short* keyword:
-
-        >>> it = []
-        >>> one(it)  # doctest: +IGNORE_EXCEPTION_DETAIL
-        Traceback (most recent call last):
-        ...
-        ValueError: too many items in iterable (expected 1)'
-        >>> too_short = IndexError('too few items')
-        >>> one(it, too_short=too_short)  # doctest: +IGNORE_EXCEPTION_DETAIL
-        Traceback (most recent call last):
-        ...
-        IndexError: too few items
-
-    Similarly, if *iterable* contains more than one item, ``ValueError`` will
-    be raised. You may specify a different exception with the *too_long*
-    keyword:
-
-        >>> it = ['too', 'many']
-        >>> one(it)  # doctest: +IGNORE_EXCEPTION_DETAIL
-        Traceback (most recent call last):
-        ...
-        ValueError: too many items in iterable (expected 1)'
-        >>> too_long = RuntimeError
-        >>> one(it, too_long=too_long)  # doctest: +IGNORE_EXCEPTION_DETAIL
-        Traceback (most recent call last):
-        ...
-        RuntimeError
-
-    Note that :func:`one` attempts to advance *iterable* twice to ensure there
-    is only one item. If there is more than one, both items will be discarded.
-    See :func:`spy` or :func:`peekable` to check iterable contents less
-    destructively.
-
-    """
-    it = iter(iterable)
-
-    try:
-        value = next(it)
-    except StopIteration:
-        raise too_short or ValueError('too few items in iterable (expected 1)')
-
-    try:
-        next(it)
-    except StopIteration:
-        pass
-    else:
-        raise too_long or ValueError('too many items in iterable (expected 1)')
-
-    return value
-
-
-def distinct_permutations(iterable):
-    """Yield successive distinct permutations of the elements in *iterable*.
-
-        >>> sorted(distinct_permutations([1, 0, 1]))
-        [(0, 1, 1), (1, 0, 1), (1, 1, 0)]
-
-    Equivalent to ``set(permutations(iterable))``, except duplicates are not
-    generated and thrown away. For larger input sequences this is much more
-    efficient.
-
-    Duplicate permutations arise when there are duplicated elements in the
-    input iterable. The number of items returned is
-    `n! / (x_1! * x_2! * ... * x_n!)`, where `n` is the total number of
-    items input, and each `x_i` is the count of a distinct item in the input
-    sequence.
-
-    """
-    def perm_unique_helper(item_counts, perm, i):
-        """Internal helper function
-
-        :arg item_counts: Stores the unique items in ``iterable`` and how many
-            times they are repeated
-        :arg perm: The permutation that is being built for output
-        :arg i: The index of the permutation being modified
-
-        The output permutations are built up recursively; the distinct items
-        are placed until their repetitions are exhausted.
-        """
-        if i < 0:
-            yield tuple(perm)
-        else:
-            for item in item_counts:
-                if item_counts[item] <= 0:
-                    continue
-                perm[i] = item
-                item_counts[item] -= 1
-                for x in perm_unique_helper(item_counts, perm, i - 1):
-                    yield x
-                item_counts[item] += 1
-
-    item_counts = Counter(iterable)
-    length = sum(item_counts.values())
-
-    return perm_unique_helper(item_counts, [None] * length, length - 1)
-
-
-def intersperse(e, iterable, n=1):
-    """Intersperse filler element *e* among the items in *iterable*, leaving
-    *n* items between each filler element.
-
-        >>> list(intersperse('!', [1, 2, 3, 4, 5]))
-        [1, '!', 2, '!', 3, '!', 4, '!', 5]
-
-        >>> list(intersperse(None, [1, 2, 3, 4, 5], n=2))
-        [1, 2, None, 3, 4, None, 5]
-
-    """
-    if n == 0:
-        raise ValueError('n must be > 0')
-    elif n == 1:
-        # interleave(repeat(e), iterable) -> e, x_0, e, e, x_1, e, x_2...
-        # islice(..., 1, None) -> x_0, e, e, x_1, e, x_2...
-        return islice(interleave(repeat(e), iterable), 1, None)
-    else:
-        # interleave(filler, chunks) -> [e], [x_0, x_1], [e], [x_2, x_3]...
-        # islice(..., 1, None) -> [x_0, x_1], [e], [x_2, x_3]...
-        # flatten(...) -> x_0, x_1, e, x_2, x_3...
-        filler = repeat([e])
-        chunks = chunked(iterable, n)
-        return flatten(islice(interleave(filler, chunks), 1, None))
-
-
-def unique_to_each(*iterables):
-    """Return the elements from each of the input iterables that aren't in the
-    other input iterables.
-
-    For example, suppose you have a set of packages, each with a set of
-    dependencies::
-
-        {'pkg_1': {'A', 'B'}, 'pkg_2': {'B', 'C'}, 'pkg_3': {'B', 'D'}}
-
-    If you remove one package, which dependencies can also be removed?
-
-    If ``pkg_1`` is removed, then ``A`` is no longer necessary - it is not
-    associated with ``pkg_2`` or ``pkg_3``. Similarly, ``C`` is only needed for
-    ``pkg_2``, and ``D`` is only needed for ``pkg_3``::
-
-        >>> unique_to_each({'A', 'B'}, {'B', 'C'}, {'B', 'D'})
-        [['A'], ['C'], ['D']]
-
-    If there are duplicates in one input iterable that aren't in the others
-    they will be duplicated in the output. Input order is preserved::
-
-        >>> unique_to_each("mississippi", "missouri")
-        [['p', 'p'], ['o', 'u', 'r']]
-
-    It is assumed that the elements of each iterable are hashable.
-
-    """
-    pool = [list(it) for it in iterables]
-    counts = Counter(chain.from_iterable(map(set, pool)))
-    uniques = {element for element in counts if counts[element] == 1}
-    return [list(filter(uniques.__contains__, it)) for it in pool]
-
-
-def windowed(seq, n, fillvalue=None, step=1):
-    """Return a sliding window of width *n* over the given iterable.
-
-        >>> all_windows = windowed([1, 2, 3, 4, 5], 3)
-        >>> list(all_windows)
-        [(1, 2, 3), (2, 3, 4), (3, 4, 5)]
-
-    When the window is larger than the iterable, *fillvalue* is used in place
-    of missing values::
-
-        >>> list(windowed([1, 2, 3], 4))
-        [(1, 2, 3, None)]
-
-    Each window will advance in increments of *step*:
-
-        >>> list(windowed([1, 2, 3, 4, 5, 6], 3, fillvalue='!', step=2))
-        [(1, 2, 3), (3, 4, 5), (5, 6, '!')]
-
-    """
-    if n < 0:
-        raise ValueError('n must be >= 0')
-    if n == 0:
-        yield tuple()
-        return
-    if step < 1:
-        raise ValueError('step must be >= 1')
-
-    it = iter(seq)
-    window = deque([], n)
-    append = window.append
-
-    # Initial deque fill
-    for _ in range(n):
-        append(next(it, fillvalue))
-    yield tuple(window)
-
-    # Appending new items to the right causes old items to fall off the left
-    i = 0
-    for item in it:
-        append(item)
-        i = (i + 1) % step
-        if i % step == 0:
-            yield tuple(window)
-
-    # If there are items from the iterable in the window, pad with the given
-    # value and emit them.
-    if (i % step) and (step - i < n):
-        for _ in range(step - i):
-            append(fillvalue)
-        yield tuple(window)
-
-
-class bucket(object):
-    """Wrap *iterable* and return an object that buckets it iterable into
-    child iterables based on a *key* function.
-
-        >>> iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3']
-        >>> s = bucket(iterable, key=lambda x: x[0])
-        >>> a_iterable = s['a']
-        >>> next(a_iterable)
-        'a1'
-        >>> next(a_iterable)
-        'a2'
-        >>> list(s['b'])
-        ['b1', 'b2', 'b3']
-
-    The original iterable will be advanced and its items will be cached until
-    they are used by the child iterables. This may require significant storage.
-
-    By default, attempting to select a bucket to which no items belong  will
-    exhaust the iterable and cache all values.
-    If you specify a *validator* function, selected buckets will instead be
-    checked against it.
-
-        >>> from itertools import count
-        >>> it = count(1, 2)  # Infinite sequence of odd numbers
-        >>> key = lambda x: x % 10  # Bucket by last digit
-        >>> validator = lambda x: x in {1, 3, 5, 7, 9}  # Odd digits only
-        >>> s = bucket(it, key=key, validator=validator)
-        >>> 2 in s
-        False
-        >>> list(s[2])
-        []
-
-    """
-    def __init__(self, iterable, key, validator=None):
-        self._it = iter(iterable)
-        self._key = key
-        self._cache = defaultdict(deque)
-        self._validator = validator or (lambda x: True)
-
-    def __contains__(self, value):
-        if not self._validator(value):
-            return False
-
-        try:
-            item = next(self[value])
-        except StopIteration:
-            return False
-        else:
-            self._cache[value].appendleft(item)
-
-        return True
-
-    def _get_values(self, value):
-        """
-        Helper to yield items from the parent iterator that match *value*.
-        Items that don't match are stored in the local cache as they
-        are encountered.
-        """
-        while True:
-            # If we've cached some items that match the target value, emit
-            # the first one and evict it from the cache.
-            if self._cache[value]:
-                yield self._cache[value].popleft()
-            # Otherwise we need to advance the parent iterator to search for
-            # a matching item, caching the rest.
-            else:
-                while True:
-                    try:
-                        item = next(self._it)
-                    except StopIteration:
-                        return
-                    item_value = self._key(item)
-                    if item_value == value:
-                        yield item
-                        break
-                    elif self._validator(item_value):
-                        self._cache[item_value].append(item)
-
-    def __getitem__(self, value):
-        if not self._validator(value):
-            return iter(())
-
-        return self._get_values(value)
-
-
-def spy(iterable, n=1):
-    """Return a 2-tuple with a list containing the first *n* elements of
-    *iterable*, and an iterator with the same items as *iterable*.
-    This allows you to "look ahead" at the items in the iterable without
-    advancing it.
-
-    There is one item in the list by default:
-
-        >>> iterable = 'abcdefg'
-        >>> head, iterable = spy(iterable)
-        >>> head
-        ['a']
-        >>> list(iterable)
-        ['a', 'b', 'c', 'd', 'e', 'f', 'g']
-
-    You may use unpacking to retrieve items instead of lists:
-
-        >>> (head,), iterable = spy('abcdefg')
-        >>> head
-        'a'
-        >>> (first, second), iterable = spy('abcdefg', 2)
-        >>> first
-        'a'
-        >>> second
-        'b'
-
-    The number of items requested can be larger than the number of items in
-    the iterable:
-
-        >>> iterable = [1, 2, 3, 4, 5]
-        >>> head, iterable = spy(iterable, 10)
-        >>> head
-        [1, 2, 3, 4, 5]
-        >>> list(iterable)
-        [1, 2, 3, 4, 5]
-
-    """
-    it = iter(iterable)
-    head = take(n, it)
-
-    return head, chain(head, it)
-
-
-def interleave(*iterables):
-    """Return a new iterable yielding from each iterable in turn,
-    until the shortest is exhausted.
-
-        >>> list(interleave([1, 2, 3], [4, 5], [6, 7, 8]))
-        [1, 4, 6, 2, 5, 7]
-
-    For a version that doesn't terminate after the shortest iterable is
-    exhausted, see :func:`interleave_longest`.
-
-    """
-    return chain.from_iterable(zip(*iterables))
-
-
-def interleave_longest(*iterables):
-    """Return a new iterable yielding from each iterable in turn,
-    skipping any that are exhausted.
-
-        >>> list(interleave_longest([1, 2, 3], [4, 5], [6, 7, 8]))
-        [1, 4, 6, 2, 5, 7, 3, 8]
-
-    This function produces the same output as :func:`roundrobin`, but may
-    perform better for some inputs (in particular when the number of iterables
-    is large).
-
-    """
-    i = chain.from_iterable(zip_longest(*iterables, fillvalue=_marker))
-    return (x for x in i if x is not _marker)
-
-
-def collapse(iterable, base_type=None, levels=None):
-    """Flatten an iterable with multiple levels of nesting (e.g., a list of
-    lists of tuples) into non-iterable types.
-
-        >>> iterable = [(1, 2), ([3, 4], [[5], [6]])]
-        >>> list(collapse(iterable))
-        [1, 2, 3, 4, 5, 6]
-
-    String types are not considered iterable and will not be collapsed.
-    To avoid collapsing other types, specify *base_type*:
-
-        >>> iterable = ['ab', ('cd', 'ef'), ['gh', 'ij']]
-        >>> list(collapse(iterable, base_type=tuple))
-        ['ab', ('cd', 'ef'), 'gh', 'ij']
-
-    Specify *levels* to stop flattening after a certain level:
-
-    >>> iterable = [('a', ['b']), ('c', ['d'])]
-    >>> list(collapse(iterable))  # Fully flattened
-    ['a', 'b', 'c', 'd']
-    >>> list(collapse(iterable, levels=1))  # Only one level flattened
-    ['a', ['b'], 'c', ['d']]
-
-    """
-    def walk(node, level):
-        if (
-            ((levels is not None) and (level > levels)) or
-            isinstance(node, string_types) or
-            ((base_type is not None) and isinstance(node, base_type))
-        ):
-            yield node
-            return
-
-        try:
-            tree = iter(node)
-        except TypeError:
-            yield node
-            return
-        else:
-            for child in tree:
-                for x in walk(child, level + 1):
-                    yield x
-
-    for x in walk(iterable, 0):
-        yield x
-
-
-def side_effect(func, iterable, chunk_size=None, before=None, after=None):
-    """Invoke *func* on each item in *iterable* (or on each *chunk_size* group
-    of items) before yielding the item.
-
-    `func` must be a function that takes a single argument. Its return value
-    will be discarded.
-
-    *before* and *after* are optional functions that take no arguments. They
-    will be executed before iteration starts and after it ends, respectively.
-
-    `side_effect` can be used for logging, updating progress bars, or anything
-    that is not functionally "pure."
-
-    Emitting a status message:
-
-        >>> from more_itertools import consume
-        >>> func = lambda item: print('Received {}'.format(item))
-        >>> consume(side_effect(func, range(2)))
-        Received 0
-        Received 1
-
-    Operating on chunks of items:
-
-        >>> pair_sums = []
-        >>> func = lambda chunk: pair_sums.append(sum(chunk))
-        >>> list(side_effect(func, [0, 1, 2, 3, 4, 5], 2))
-        [0, 1, 2, 3, 4, 5]
-        >>> list(pair_sums)
-        [1, 5, 9]
-
-    Writing to a file-like object:
-
-        >>> from io import StringIO
-        >>> from more_itertools import consume
-        >>> f = StringIO()
-        >>> func = lambda x: print(x, file=f)
-        >>> before = lambda: print(u'HEADER', file=f)
-        >>> after = f.close
-        >>> it = [u'a', u'b', u'c']
-        >>> consume(side_effect(func, it, before=before, after=after))
-        >>> f.closed
-        True
-
-    """
-    try:
-        if before is not None:
-            before()
-
-        if chunk_size is None:
-            for item in iterable:
-                func(item)
-                yield item
-        else:
-            for chunk in chunked(iterable, chunk_size):
-                func(chunk)
-                for item in chunk:
-                    yield item
-    finally:
-        if after is not None:
-            after()
-
-
-def sliced(seq, n):
-    """Yield slices of length *n* from the sequence *seq*.
-
-        >>> list(sliced((1, 2, 3, 4, 5, 6), 3))
-        [(1, 2, 3), (4, 5, 6)]
-
-    If the length of the sequence is not divisible by the requested slice
-    length, the last slice will be shorter.
-
-        >>> list(sliced((1, 2, 3, 4, 5, 6, 7, 8), 3))
-        [(1, 2, 3), (4, 5, 6), (7, 8)]
-
-    This function will only work for iterables that support slicing.
-    For non-sliceable iterables, see :func:`chunked`.
-
-    """
-    return takewhile(bool, (seq[i: i + n] for i in count(0, n)))
-
-
-def split_at(iterable, pred):
-    """Yield lists of items from *iterable*, where each list is delimited by
-    an item where callable *pred* returns ``True``. The lists do not include
-    the delimiting items.
-
-        >>> list(split_at('abcdcba', lambda x: x == 'b'))
-        [['a'], ['c', 'd', 'c'], ['a']]
-
-        >>> list(split_at(range(10), lambda n: n % 2 == 1))
-        [[0], [2], [4], [6], [8], []]
-    """
-    buf = []
-    for item in iterable:
-        if pred(item):
-            yield buf
-            buf = []
-        else:
-            buf.append(item)
-    yield buf
-
-
-def split_before(iterable, pred):
-    """Yield lists of items from *iterable*, where each list starts with an
-    item where callable *pred* returns ``True``:
-
-        >>> list(split_before('OneTwo', lambda s: s.isupper()))
-        [['O', 'n', 'e'], ['T', 'w', 'o']]
-
-        >>> list(split_before(range(10), lambda n: n % 3 == 0))
-        [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
-
-    """
-    buf = []
-    for item in iterable:
-        if pred(item) and buf:
-            yield buf
-            buf = []
-        buf.append(item)
-    yield buf
-
-
-def split_after(iterable, pred):
-    """Yield lists of items from *iterable*, where each list ends with an
-    item where callable *pred* returns ``True``:
-
-        >>> list(split_after('one1two2', lambda s: s.isdigit()))
-        [['o', 'n', 'e', '1'], ['t', 'w', 'o', '2']]
-
-        >>> list(split_after(range(10), lambda n: n % 3 == 0))
-        [[0], [1, 2, 3], [4, 5, 6], [7, 8, 9]]
-
-    """
-    buf = []
-    for item in iterable:
-        buf.append(item)
-        if pred(item) and buf:
-            yield buf
-            buf = []
-    if buf:
-        yield buf
-
-
-def padded(iterable, fillvalue=None, n=None, next_multiple=False):
-    """Yield the elements from *iterable*, followed by *fillvalue*, such that
-    at least *n* items are emitted.
-
-        >>> list(padded([1, 2, 3], '?', 5))
-        [1, 2, 3, '?', '?']
-
-    If *next_multiple* is ``True``, *fillvalue* will be emitted until the
-    number of items emitted is a multiple of *n*::
-
-        >>> list(padded([1, 2, 3, 4], n=3, next_multiple=True))
-        [1, 2, 3, 4, None, None]
-
-    If *n* is ``None``, *fillvalue* will be emitted indefinitely.
-
-    """
-    it = iter(iterable)
-    if n is None:
-        for item in chain(it, repeat(fillvalue)):
-            yield item
-    elif n < 1:
-        raise ValueError('n must be at least 1')
-    else:
-        item_count = 0
-        for item in it:
-            yield item
-            item_count += 1
-
-        remaining = (n - item_count) % n if next_multiple else n - item_count
-        for _ in range(remaining):
-            yield fillvalue
-
-
-def distribute(n, iterable):
-    """Distribute the items from *iterable* among *n* smaller iterables.
-
-        >>> group_1, group_2 = distribute(2, [1, 2, 3, 4, 5, 6])
-        >>> list(group_1)
-        [1, 3, 5]
-        >>> list(group_2)
-        [2, 4, 6]
-
-    If the length of *iterable* is not evenly divisible by *n*, then the
-    length of the returned iterables will not be identical:
-
-        >>> children = distribute(3, [1, 2, 3, 4, 5, 6, 7])
-        >>> [list(c) for c in children]
-        [[1, 4, 7], [2, 5], [3, 6]]
-
-    If the length of *iterable* is smaller than *n*, then the last returned
-    iterables will be empty:
-
-        >>> children = distribute(5, [1, 2, 3])
-        >>> [list(c) for c in children]
-        [[1], [2], [3], [], []]
-
-    This function uses :func:`itertools.tee` and may require significant
-    storage. If you need the order items in the smaller iterables to match the
-    original iterable, see :func:`divide`.
-
-    """
-    if n < 1:
-        raise ValueError('n must be at least 1')
-
-    children = tee(iterable, n)
-    return [islice(it, index, None, n) for index, it in enumerate(children)]
-
-
-def stagger(iterable, offsets=(-1, 0, 1), longest=False, fillvalue=None):
-    """Yield tuples whose elements are offset from *iterable*.
-    The amount by which the `i`-th item in each tuple is offset is given by
-    the `i`-th item in *offsets*.
-
-        >>> list(stagger([0, 1, 2, 3]))
-        [(None, 0, 1), (0, 1, 2), (1, 2, 3)]
-        >>> list(stagger(range(8), offsets=(0, 2, 4)))
-        [(0, 2, 4), (1, 3, 5), (2, 4, 6), (3, 5, 7)]
-
-    By default, the sequence will end when the final element of a tuple is the
-    last item in the iterable. To continue until the first element of a tuple
-    is the last item in the iterable, set *longest* to ``True``::
-
-        >>> list(stagger([0, 1, 2, 3], longest=True))
-        [(None, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, None), (3, None, None)]
-
-    By default, ``None`` will be used to replace offsets beyond the end of the
-    sequence. Specify *fillvalue* to use some other value.
-
-    """
-    children = tee(iterable, len(offsets))
-
-    return zip_offset(
-        *children, offsets=offsets, longest=longest, fillvalue=fillvalue
-    )
-
-
-def zip_offset(*iterables, **kwargs):
-    """``zip`` the input *iterables* together, but offset the `i`-th iterable
-    by the `i`-th item in *offsets*.
-
-        >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1)))
-        [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e')]
-
-    This can be used as a lightweight alternative to SciPy or pandas to analyze
-    data sets in which somes series have a lead or lag relationship.
-
-    By default, the sequence will end when the shortest iterable is exhausted.
-    To continue until the longest iterable is exhausted, set *longest* to
-    ``True``.
-
-        >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1), longest=True))
-        [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e'), (None, 'f')]
-
-    By default, ``None`` will be used to replace offsets beyond the end of the
-    sequence. Specify *fillvalue* to use some other value.
-
-    """
-    offsets = kwargs['offsets']
-    longest = kwargs.get('longest', False)
-    fillvalue = kwargs.get('fillvalue', None)
-
-    if len(iterables) != len(offsets):
-        raise ValueError("Number of iterables and offsets didn't match")
-
-    staggered = []
-    for it, n in zip(iterables, offsets):
-        if n < 0:
-            staggered.append(chain(repeat(fillvalue, -n), it))
-        elif n > 0:
-            staggered.append(islice(it, n, None))
-        else:
-            staggered.append(it)
-
-    if longest:
-        return zip_longest(*staggered, fillvalue=fillvalue)
-
-    return zip(*staggered)
-
-
-def sort_together(iterables, key_list=(0,), reverse=False):
-    """Return the input iterables sorted together, with *key_list* as the
-    priority for sorting. All iterables are trimmed to the length of the
-    shortest one.
-
-    This can be used like the sorting function in a spreadsheet. If each
-    iterable represents a column of data, the key list determines which
-    columns are used for sorting.
-
-    By default, all iterables are sorted using the ``0``-th iterable::
-
-        >>> iterables = [(4, 3, 2, 1), ('a', 'b', 'c', 'd')]
-        >>> sort_together(iterables)
-        [(1, 2, 3, 4), ('d', 'c', 'b', 'a')]
-
-    Set a different key list to sort according to another iterable.
-    Specifying mutliple keys dictates how ties are broken::
-
-        >>> iterables = [(3, 1, 2), (0, 1, 0), ('c', 'b', 'a')]
-        >>> sort_together(iterables, key_list=(1, 2))
-        [(2, 3, 1), (0, 0, 1), ('a', 'c', 'b')]
-
-    Set *reverse* to ``True`` to sort in descending order.
-
-        >>> sort_together([(1, 2, 3), ('c', 'b', 'a')], reverse=True)
-        [(3, 2, 1), ('a', 'b', 'c')]
-
-    """
-    return list(zip(*sorted(zip(*iterables),
-                            key=itemgetter(*key_list),
-                            reverse=reverse)))
-
-
-def divide(n, iterable):
-    """Divide the elements from *iterable* into *n* parts, maintaining
-    order.
-
-        >>> group_1, group_2 = divide(2, [1, 2, 3, 4, 5, 6])
-        >>> list(group_1)
-        [1, 2, 3]
-        >>> list(group_2)
-        [4, 5, 6]
-
-    If the length of *iterable* is not evenly divisible by *n*, then the
-    length of the returned iterables will not be identical:
-
-        >>> children = divide(3, [1, 2, 3, 4, 5, 6, 7])
-        >>> [list(c) for c in children]
-        [[1, 2, 3], [4, 5], [6, 7]]
-
-    If the length of the iterable is smaller than n, then the last returned
-    iterables will be empty:
-
-        >>> children = divide(5, [1, 2, 3])
-        >>> [list(c) for c in children]
-        [[1], [2], [3], [], []]
-
-    This function will exhaust the iterable before returning and may require
-    significant storage. If order is not important, see :func:`distribute`,
-    which does not first pull the iterable into memory.
-
-    """
-    if n < 1:
-        raise ValueError('n must be at least 1')
-
-    seq = tuple(iterable)
-    q, r = divmod(len(seq), n)
-
-    ret = []
-    for i in range(n):
-        start = (i * q) + (i if i < r else r)
-        stop = ((i + 1) * q) + (i + 1 if i + 1 < r else r)
-        ret.append(iter(seq[start:stop]))
-
-    return ret
-
-
-def always_iterable(obj, base_type=(text_type, binary_type)):
-    """If *obj* is iterable, return an iterator over its items::
-
-        >>> obj = (1, 2, 3)
-        >>> list(always_iterable(obj))
-        [1, 2, 3]
-
-    If *obj* is not iterable, return a one-item iterable containing *obj*::
-
-        >>> obj = 1
-        >>> list(always_iterable(obj))
-        [1]
-
-    If *obj* is ``None``, return an empty iterable:
-
-        >>> obj = None
-        >>> list(always_iterable(None))
-        []
-
-    By default, binary and text strings are not considered iterable::
-
-        >>> obj = 'foo'
-        >>> list(always_iterable(obj))
-        ['foo']
-
-    If *base_type* is set, objects for which ``isinstance(obj, base_type)``
-    returns ``True`` won't be considered iterable.
-
-        >>> obj = {'a': 1}
-        >>> list(always_iterable(obj))  # Iterate over the dict's keys
-        ['a']
-        >>> list(always_iterable(obj, base_type=dict))  # Treat dicts as a unit
-        [{'a': 1}]
-
-    Set *base_type* to ``None`` to avoid any special handling and treat objects
-    Python considers iterable as iterable:
-
-        >>> obj = 'foo'
-        >>> list(always_iterable(obj, base_type=None))
-        ['f', 'o', 'o']
-    """
-    if obj is None:
-        return iter(())
-
-    if (base_type is not None) and isinstance(obj, base_type):
-        return iter((obj,))
-
-    try:
-        return iter(obj)
-    except TypeError:
-        return iter((obj,))
-
-
-def adjacent(predicate, iterable, distance=1):
-    """Return an iterable over `(bool, item)` tuples where the `item` is
-    drawn from *iterable* and the `bool` indicates whether
-    that item satisfies the *predicate* or is adjacent to an item that does.
-
-    For example, to find whether items are adjacent to a ``3``::
-
-        >>> list(adjacent(lambda x: x == 3, range(6)))
-        [(False, 0), (False, 1), (True, 2), (True, 3), (True, 4), (False, 5)]
-
-    Set *distance* to change what counts as adjacent. For example, to find
-    whether items are two places away from a ``3``:
-
-        >>> list(adjacent(lambda x: x == 3, range(6), distance=2))
-        [(False, 0), (True, 1), (True, 2), (True, 3), (True, 4), (True, 5)]
-
-    This is useful for contextualizing the results of a search function.
-    For example, a code comparison tool might want to identify lines that
-    have changed, but also surrounding lines to give the viewer of the diff
-    context.
-
-    The predicate function will only be called once for each item in the
-    iterable.
-
-    See also :func:`groupby_transform`, which can be used with this function
-    to group ranges of items with the same `bool` value.
-
-    """
-    # Allow distance=0 mainly for testing that it reproduces results with map()
-    if distance < 0:
-        raise ValueError('distance must be at least 0')
-
-    i1, i2 = tee(iterable)
-    padding = [False] * distance
-    selected = chain(padding, map(predicate, i1), padding)
-    adjacent_to_selected = map(any, windowed(selected, 2 * distance + 1))
-    return zip(adjacent_to_selected, i2)
-
-
-def groupby_transform(iterable, keyfunc=None, valuefunc=None):
-    """An extension of :func:`itertools.groupby` that transforms the values of
-    *iterable* after grouping them.
-    *keyfunc* is a function used to compute a grouping key for each item.
-    *valuefunc* is a function for transforming the items after grouping.
-
-        >>> iterable = 'AaaABbBCcA'
-        >>> keyfunc = lambda x: x.upper()
-        >>> valuefunc = lambda x: x.lower()
-        >>> grouper = groupby_transform(iterable, keyfunc, valuefunc)
-        >>> [(k, ''.join(g)) for k, g in grouper]
-        [('A', 'aaaa'), ('B', 'bbb'), ('C', 'cc'), ('A', 'a')]
-
-    *keyfunc* and *valuefunc* default to identity functions if they are not
-    specified.
-
-    :func:`groupby_transform` is useful when grouping elements of an iterable
-    using a separate iterable as the key. To do this, :func:`zip` the iterables
-    and pass a *keyfunc* that extracts the first element and a *valuefunc*
-    that extracts the second element::
-
-        >>> from operator import itemgetter
-        >>> keys = [0, 0, 1, 1, 1, 2, 2, 2, 3]
-        >>> values = 'abcdefghi'
-        >>> iterable = zip(keys, values)
-        >>> grouper = groupby_transform(iterable, itemgetter(0), itemgetter(1))
-        >>> [(k, ''.join(g)) for k, g in grouper]
-        [(0, 'ab'), (1, 'cde'), (2, 'fgh'), (3, 'i')]
-
-    Note that the order of items in the iterable is significant.
-    Only adjacent items are grouped together, so if you don't want any
-    duplicate groups, you should sort the iterable by the key function.
-
-    """
-    valuefunc = (lambda x: x) if valuefunc is None else valuefunc
-    return ((k, map(valuefunc, g)) for k, g in groupby(iterable, keyfunc))
-
-
-def numeric_range(*args):
-    """An extension of the built-in ``range()`` function whose arguments can
-    be any orderable numeric type.
-
-    With only *stop* specified, *start* defaults to ``0`` and *step*
-    defaults to ``1``. The output items will match the type of *stop*:
-
-        >>> list(numeric_range(3.5))
-        [0.0, 1.0, 2.0, 3.0]
-
-    With only *start* and *stop* specified, *step* defaults to ``1``. The
-    output items will match the type of *start*:
-
-        >>> from decimal import Decimal
-        >>> start = Decimal('2.1')
-        >>> stop = Decimal('5.1')
-        >>> list(numeric_range(start, stop))
-        [Decimal('2.1'), Decimal('3.1'), Decimal('4.1')]
-
-    With *start*, *stop*, and *step*  specified the output items will match
-    the type of ``start + step``:
-
-        >>> from fractions import Fraction
-        >>> start = Fraction(1, 2)  # Start at 1/2
-        >>> stop = Fraction(5, 2)  # End at 5/2
-        >>> step = Fraction(1, 2)  # Count by 1/2
-        >>> list(numeric_range(start, stop, step))
-        [Fraction(1, 2), Fraction(1, 1), Fraction(3, 2), Fraction(2, 1)]
-
-    If *step* is zero, ``ValueError`` is raised. Negative steps are supported:
-
-        >>> list(numeric_range(3, -1, -1.0))
-        [3.0, 2.0, 1.0, 0.0]
-
-    Be aware of the limitations of floating point numbers; the representation
-    of the yielded numbers may be surprising.
-
-    """
-    argc = len(args)
-    if argc == 1:
-        stop, = args
-        start = type(stop)(0)
-        step = 1
-    elif argc == 2:
-        start, stop = args
-        step = 1
-    elif argc == 3:
-        start, stop, step = args
-    else:
-        err_msg = 'numeric_range takes at most 3 arguments, got {}'
-        raise TypeError(err_msg.format(argc))
-
-    values = (start + (step * n) for n in count())
-    if step > 0:
-        return takewhile(partial(gt, stop), values)
-    elif step < 0:
-        return takewhile(partial(lt, stop), values)
-    else:
-        raise ValueError('numeric_range arg 3 must not be zero')
-
-
-def count_cycle(iterable, n=None):
-    """Cycle through the items from *iterable* up to *n* times, yielding
-    the number of completed cycles along with each item. If *n* is omitted the
-    process repeats indefinitely.
-
-    >>> list(count_cycle('AB', 3))
-    [(0, 'A'), (0, 'B'), (1, 'A'), (1, 'B'), (2, 'A'), (2, 'B')]
-
-    """
-    iterable = tuple(iterable)
-    if not iterable:
-        return iter(())
-    counter = count() if n is None else range(n)
-    return ((i, item) for i in counter for item in iterable)
-
-
-def locate(iterable, pred=bool, window_size=None):
-    """Yield the index of each item in *iterable* for which *pred* returns
-    ``True``.
-
-    *pred* defaults to :func:`bool`, which will select truthy items:
-
-        >>> list(locate([0, 1, 1, 0, 1, 0, 0]))
-        [1, 2, 4]
-
-    Set *pred* to a custom function to, e.g., find the indexes for a particular
-    item.
-
-        >>> list(locate(['a', 'b', 'c', 'b'], lambda x: x == 'b'))
-        [1, 3]
-
-    If *window_size* is given, then the *pred* function will be called with
-    that many items. This enables searching for sub-sequences:
-
-        >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]
-        >>> pred = lambda *args: args == (1, 2, 3)
-        >>> list(locate(iterable, pred=pred, window_size=3))
-        [1, 5, 9]
-
-    Use with :func:`seekable` to find indexes and then retrieve the associated
-    items:
-
-        >>> from itertools import count
-        >>> from more_itertools import seekable
-        >>> source = (3 * n + 1 if (n % 2) else n // 2 for n in count())
-        >>> it = seekable(source)
-        >>> pred = lambda x: x > 100
-        >>> indexes = locate(it, pred=pred)
-        >>> i = next(indexes)
-        >>> it.seek(i)
-        >>> next(it)
-        106
-
-    """
-    if window_size is None:
-        return compress(count(), map(pred, iterable))
-
-    if window_size < 1:
-        raise ValueError('window size must be at least 1')
-
-    it = windowed(iterable, window_size, fillvalue=_marker)
-    return compress(count(), starmap(pred, it))
-
-
-def lstrip(iterable, pred):
-    """Yield the items from *iterable*, but strip any from the beginning
-    for which *pred* returns ``True``.
-
-    For example, to remove a set of items from the start of an iterable:
-
-        >>> iterable = (None, False, None, 1, 2, None, 3, False, None)
-        >>> pred = lambda x: x in {None, False, ''}
-        >>> list(lstrip(iterable, pred))
-        [1, 2, None, 3, False, None]
-
-    This function is analogous to to :func:`str.lstrip`, and is essentially
-    an wrapper for :func:`itertools.dropwhile`.
-
-    """
-    return dropwhile(pred, iterable)
-
-
-def rstrip(iterable, pred):
-    """Yield the items from *iterable*, but strip any from the end
-    for which *pred* returns ``True``.
-
-    For example, to remove a set of items from the end of an iterable:
-
-        >>> iterable = (None, False, None, 1, 2, None, 3, False, None)
-        >>> pred = lambda x: x in {None, False, ''}
-        >>> list(rstrip(iterable, pred))
-        [None, False, None, 1, 2, None, 3]
-
-    This function is analogous to :func:`str.rstrip`.
-
-    """
-    cache = []
-    cache_append = cache.append
-    for x in iterable:
-        if pred(x):
-            cache_append(x)
-        else:
-            for y in cache:
-                yield y
-            del cache[:]
-            yield x
-
-
-def strip(iterable, pred):
-    """Yield the items from *iterable*, but strip any from the
-    beginning and end for which *pred* returns ``True``.
-
-    For example, to remove a set of items from both ends of an iterable:
-
-        >>> iterable = (None, False, None, 1, 2, None, 3, False, None)
-        >>> pred = lambda x: x in {None, False, ''}
-        >>> list(strip(iterable, pred))
-        [1, 2, None, 3]
-
-    This function is analogous to :func:`str.strip`.
-
-    """
-    return rstrip(lstrip(iterable, pred), pred)
-
-
-def islice_extended(iterable, *args):
-    """An extension of :func:`itertools.islice` that supports negative values
-    for *stop*, *start*, and *step*.
-
-        >>> iterable = iter('abcdefgh')
-        >>> list(islice_extended(iterable, -4, -1))
-        ['e', 'f', 'g']
-
-    Slices with negative values require some caching of *iterable*, but this
-    function takes care to minimize the amount of memory required.
-
-    For example, you can use a negative step with an infinite iterator:
-
-        >>> from itertools import count
-        >>> list(islice_extended(count(), 110, 99, -2))
-        [110, 108, 106, 104, 102, 100]
-
-    """
-    s = slice(*args)
-    start = s.start
-    stop = s.stop
-    if s.step == 0:
-        raise ValueError('step argument must be a non-zero integer or None.')
-    step = s.step or 1
-
-    it = iter(iterable)
-
-    if step > 0:
-        start = 0 if (start is None) else start
-
-        if (start < 0):
-            # Consume all but the last -start items
-            cache = deque(enumerate(it, 1), maxlen=-start)
-            len_iter = cache[-1][0] if cache else 0
-
-            # Adjust start to be positive
-            i = max(len_iter + start, 0)
-
-            # Adjust stop to be positive
-            if stop is None:
-                j = len_iter
-            elif stop >= 0:
-                j = min(stop, len_iter)
-            else:
-                j = max(len_iter + stop, 0)
-
-            # Slice the cache
-            n = j - i
-            if n <= 0:
-                return
-
-            for index, item in islice(cache, 0, n, step):
-                yield item
-        elif (stop is not None) and (stop < 0):
-            # Advance to the start position
-            next(islice(it, start, start), None)
-
-            # When stop is negative, we have to carry -stop items while
-            # iterating
-            cache = deque(islice(it, -stop), maxlen=-stop)
-
-            for index, item in enumerate(it):
-                cached_item = cache.popleft()
-                if index % step == 0:
-                    yield cached_item
-                cache.append(item)
-        else:
-            # When both start and stop are positive we have the normal case
-            for item in islice(it, start, stop, step):
-                yield item
-    else:
-        start = -1 if (start is None) else start
-
-        if (stop is not None) and (stop < 0):
-            # Consume all but the last items
-            n = -stop - 1
-            cache = deque(enumerate(it, 1), maxlen=n)
-            len_iter = cache[-1][0] if cache else 0
-
-            # If start and stop are both negative they are comparable and
-            # we can just slice. Otherwise we can adjust start to be negative
-            # and then slice.
-            if start < 0:
-                i, j = start, stop
-            else:
-                i, j = min(start - len_iter, -1), None
-
-            for index, item in list(cache)[i:j:step]:
-                yield item
-        else:
-            # Advance to the stop position
-            if stop is not None:
-                m = stop + 1
-                next(islice(it, m, m), None)
-
-            # stop is positive, so if start is negative they are not comparable
-            # and we need the rest of the items.
-            if start < 0:
-                i = start
-                n = None
-            # stop is None and start is positive, so we just need items up to
-            # the start index.
-            elif stop is None:
-                i = None
-                n = start + 1
-            # Both stop and start are positive, so they are comparable.
-            else:
-                i = None
-                n = start - stop
-                if n <= 0:
-                    return
-
-            cache = list(islice(it, n))
-
-            for item in cache[i::step]:
-                yield item
-
-
-def always_reversible(iterable):
-    """An extension of :func:`reversed` that supports all iterables, not
-    just those which implement the ``Reversible`` or ``Sequence`` protocols.
-
-        >>> print(*always_reversible(x for x in range(3)))
-        2 1 0
-
-    If the iterable is already reversible, this function returns the
-    result of :func:`reversed()`. If the iterable is not reversible,
-    this function will cache the remaining items in the iterable and
-    yield them in reverse order, which may require significant storage.
-    """
-    try:
-        return reversed(iterable)
-    except TypeError:
-        return reversed(list(iterable))
-
-
-def consecutive_groups(iterable, ordering=lambda x: x):
-    """Yield groups of consecutive items using :func:`itertools.groupby`.
-    The *ordering* function determines whether two items are adjacent by
-    returning their position.
-
-    By default, the ordering function is the identity function. This is
-    suitable for finding runs of numbers:
-
-        >>> iterable = [1, 10, 11, 12, 20, 30, 31, 32, 33, 40]
-        >>> for group in consecutive_groups(iterable):
-        ...     print(list(group))
-        [1]
-        [10, 11, 12]
-        [20]
-        [30, 31, 32, 33]
-        [40]
-
-    For finding runs of adjacent letters, try using the :meth:`index` method
-    of a string of letters:
-
-        >>> from string import ascii_lowercase
-        >>> iterable = 'abcdfgilmnop'
-        >>> ordering = ascii_lowercase.index
-        >>> for group in consecutive_groups(iterable, ordering):
-        ...     print(list(group))
-        ['a', 'b', 'c', 'd']
-        ['f', 'g']
-        ['i']
-        ['l', 'm', 'n', 'o', 'p']
-
-    """
-    for k, g in groupby(
-        enumerate(iterable), key=lambda x: x[0] - ordering(x[1])
-    ):
-        yield map(itemgetter(1), g)
-
-
-def difference(iterable, func=sub):
-    """By default, compute the first difference of *iterable* using
-    :func:`operator.sub`.
-
-        >>> iterable = [0, 1, 3, 6, 10]
-        >>> list(difference(iterable))
-        [0, 1, 2, 3, 4]
-
-    This is the opposite of :func:`accumulate`'s default behavior:
-
-        >>> from more_itertools import accumulate
-        >>> iterable = [0, 1, 2, 3, 4]
-        >>> list(accumulate(iterable))
-        [0, 1, 3, 6, 10]
-        >>> list(difference(accumulate(iterable)))
-        [0, 1, 2, 3, 4]
-
-    By default *func* is :func:`operator.sub`, but other functions can be
-    specified. They will be applied as follows::
-
-        A, B, C, D, ... --> A, func(B, A), func(C, B), func(D, C), ...
-
-    For example, to do progressive division:
-
-        >>> iterable = [1, 2, 6, 24, 120]  # Factorial sequence
-        >>> func = lambda x, y: x // y
-        >>> list(difference(iterable, func))
-        [1, 2, 3, 4, 5]
-
-    """
-    a, b = tee(iterable)
-    try:
-        item = next(b)
-    except StopIteration:
-        return iter([])
-    return chain([item], map(lambda x: func(x[1], x[0]), zip(a, b)))
-
-
-class SequenceView(Sequence):
-    """Return a read-only view of the sequence object *target*.
-
-    :class:`SequenceView` objects are analagous to Python's built-in
-    "dictionary view" types. They provide a dynamic view of a sequence's items,
-    meaning that when the sequence updates, so does the view.
-
-        >>> seq = ['0', '1', '2']
-        >>> view = SequenceView(seq)
-        >>> view
-        SequenceView(['0', '1', '2'])
-        >>> seq.append('3')
-        >>> view
-        SequenceView(['0', '1', '2', '3'])
-
-    Sequence views support indexing, slicing, and length queries. They act
-    like the underlying sequence, except they don't allow assignment:
-
-        >>> view[1]
-        '1'
-        >>> view[1:-1]
-        ['1', '2']
-        >>> len(view)
-        4
-
-    Sequence views are useful as an alternative to copying, as they don't
-    require (much) extra storage.
-
-    """
-    def __init__(self, target):
-        if not isinstance(target, Sequence):
-            raise TypeError
-        self._target = target
-
-    def __getitem__(self, index):
-        return self._target[index]
-
-    def __len__(self):
-        return len(self._target)
-
-    def __repr__(self):
-        return '{}({})'.format(self.__class__.__name__, repr(self._target))
-
-
-class seekable(object):
-    """Wrap an iterator to allow for seeking backward and forward. This
-    progressively caches the items in the source iterable so they can be
-    re-visited.
-
-    Call :meth:`seek` with an index to seek to that position in the source
-    iterable.
-
-    To "reset" an iterator, seek to ``0``:
-
-        >>> from itertools import count
-        >>> it = seekable((str(n) for n in count()))
-        >>> next(it), next(it), next(it)
-        ('0', '1', '2')
-        >>> it.seek(0)
-        >>> next(it), next(it), next(it)
-        ('0', '1', '2')
-        >>> next(it)
-        '3'
-
-    You can also seek forward:
-
-        >>> it = seekable((str(n) for n in range(20)))
-        >>> it.seek(10)
-        >>> next(it)
-        '10'
-        >>> it.seek(20)  # Seeking past the end of the source isn't a problem
-        >>> list(it)
-        []
-        >>> it.seek(0)  # Resetting works even after hitting the end
-        >>> next(it), next(it), next(it)
-        ('0', '1', '2')
-
-    The cache grows as the source iterable progresses, so beware of wrapping
-    very large or infinite iterables.
-
-    You may view the contents of the cache with the :meth:`elements` method.
-    That returns a :class:`SequenceView`, a view that updates automatically:
-
-        >>> it = seekable((str(n) for n in range(10)))
-        >>> next(it), next(it), next(it)
-        ('0', '1', '2')
-        >>> elements = it.elements()
-        >>> elements
-        SequenceView(['0', '1', '2'])
-        >>> next(it)
-        '3'
-        >>> elements
-        SequenceView(['0', '1', '2', '3'])
-
-    """
-
-    def __init__(self, iterable):
-        self._source = iter(iterable)
-        self._cache = []
-        self._index = None
-
-    def __iter__(self):
-        return self
-
-    def __next__(self):
-        if self._index is not None:
-            try:
-                item = self._cache[self._index]
-            except IndexError:
-                self._index = None
-            else:
-                self._index += 1
-                return item
-
-        item = next(self._source)
-        self._cache.append(item)
-        return item
-
-    next = __next__
-
-    def elements(self):
-        return SequenceView(self._cache)
-
-    def seek(self, index):
-        self._index = index
-        remainder = index - len(self._cache)
-        if remainder > 0:
-            consume(self, remainder)
-
-
-class run_length(object):
-    """
-    :func:`run_length.encode` compresses an iterable with run-length encoding.
-    It yields groups of repeated items with the count of how many times they
-    were repeated:
-
-        >>> uncompressed = 'abbcccdddd'
-        >>> list(run_length.encode(uncompressed))
-        [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
-
-    :func:`run_length.decode` decompresses an iterable that was previously
-    compressed with run-length encoding. It yields the items of the
-    decompressed iterable:
-
-        >>> compressed = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
-        >>> list(run_length.decode(compressed))
-        ['a', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd', 'd']
-
-    """
-
-    @staticmethod
-    def encode(iterable):
-        return ((k, ilen(g)) for k, g in groupby(iterable))
-
-    @staticmethod
-    def decode(iterable):
-        return chain.from_iterable(repeat(k, n) for k, n in iterable)
-
-
-def exactly_n(iterable, n, predicate=bool):
-    """Return ``True`` if exactly ``n`` items in the iterable are ``True``
-    according to the *predicate* function.
-
-        >>> exactly_n([True, True, False], 2)
-        True
-        >>> exactly_n([True, True, False], 1)
-        False
-        >>> exactly_n([0, 1, 2, 3, 4, 5], 3, lambda x: x < 3)
-        True
-
-    The iterable will be advanced until ``n + 1`` truthy items are encountered,
-    so avoid calling it on infinite iterables.
-
-    """
-    return len(take(n + 1, filter(predicate, iterable))) == n
-
-
-def circular_shifts(iterable):
-    """Return a list of circular shifts of *iterable*.
-
-        >>> circular_shifts(range(4))
-        [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)]
-    """
-    lst = list(iterable)
-    return take(len(lst), windowed(cycle(lst), len(lst)))
-
-
-def make_decorator(wrapping_func, result_index=0):
-    """Return a decorator version of *wrapping_func*, which is a function that
-    modifies an iterable. *result_index* is the position in that function's
-    signature where the iterable goes.
-
-    This lets you use itertools on the "production end," i.e. at function
-    definition. This can augment what the function returns without changing the
-    function's code.
-
-    For example, to produce a decorator version of :func:`chunked`:
-
-        >>> from more_itertools import chunked
-        >>> chunker = make_decorator(chunked, result_index=0)
-        >>> @chunker(3)
-        ... def iter_range(n):
-        ...     return iter(range(n))
-        ...
-        >>> list(iter_range(9))
-        [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
-
-    To only allow truthy items to be returned:
-
-        >>> truth_serum = make_decorator(filter, result_index=1)
-        >>> @truth_serum(bool)
-        ... def boolean_test():
-        ...     return [0, 1, '', ' ', False, True]
-        ...
-        >>> list(boolean_test())
-        [1, ' ', True]
-
-    The :func:`peekable` and :func:`seekable` wrappers make for practical
-    decorators:
-
-        >>> from more_itertools import peekable
-        >>> peekable_function = make_decorator(peekable)
-        >>> @peekable_function()
-        ... def str_range(*args):
-        ...     return (str(x) for x in range(*args))
-        ...
-        >>> it = str_range(1, 20, 2)
-        >>> next(it), next(it), next(it)
-        ('1', '3', '5')
-        >>> it.peek()
-        '7'
-        >>> next(it)
-        '7'
-
-    """
-    # See https://sites.google.com/site/bbayles/index/decorator_factory for
-    # notes on how this works.
-    def decorator(*wrapping_args, **wrapping_kwargs):
-        def outer_wrapper(f):
-            def inner_wrapper(*args, **kwargs):
-                result = f(*args, **kwargs)
-                wrapping_args_ = list(wrapping_args)
-                wrapping_args_.insert(result_index, result)
-                return wrapping_func(*wrapping_args_, **wrapping_kwargs)
-
-            return inner_wrapper
-
-        return outer_wrapper
-
-    return decorator
-
-
-def map_reduce(iterable, keyfunc, valuefunc=None, reducefunc=None):
-    """Return a dictionary that maps the items in *iterable* to categories
-    defined by *keyfunc*, transforms them with *valuefunc*, and
-    then summarizes them by category with *reducefunc*.
-
-    *valuefunc* defaults to the identity function if it is unspecified.
-    If *reducefunc* is unspecified, no summarization takes place:
-
-        >>> keyfunc = lambda x: x.upper()
-        >>> result = map_reduce('abbccc', keyfunc)
-        >>> sorted(result.items())
-        [('A', ['a']), ('B', ['b', 'b']), ('C', ['c', 'c', 'c'])]
-
-    Specifying *valuefunc* transforms the categorized items:
-
-        >>> keyfunc = lambda x: x.upper()
-        >>> valuefunc = lambda x: 1
-        >>> result = map_reduce('abbccc', keyfunc, valuefunc)
-        >>> sorted(result.items())
-        [('A', [1]), ('B', [1, 1]), ('C', [1, 1, 1])]
-
-    Specifying *reducefunc* summarizes the categorized items:
-
-        >>> keyfunc = lambda x: x.upper()
-        >>> valuefunc = lambda x: 1
-        >>> reducefunc = sum
-        >>> result = map_reduce('abbccc', keyfunc, valuefunc, reducefunc)
-        >>> sorted(result.items())
-        [('A', 1), ('B', 2), ('C', 3)]
-
-    You may want to filter the input iterable before applying the map/reduce
-    procedure:
-
-        >>> all_items = range(30)
-        >>> items = [x for x in all_items if 10 <= x <= 20]  # Filter
-        >>> keyfunc = lambda x: x % 2  # Evens map to 0; odds to 1
-        >>> categories = map_reduce(items, keyfunc=keyfunc)
-        >>> sorted(categories.items())
-        [(0, [10, 12, 14, 16, 18, 20]), (1, [11, 13, 15, 17, 19])]
-        >>> summaries = map_reduce(items, keyfunc=keyfunc, reducefunc=sum)
-        >>> sorted(summaries.items())
-        [(0, 90), (1, 75)]
-
-    Note that all items in the iterable are gathered into a list before the
-    summarization step, which may require significant storage.
-
-    The returned object is a :obj:`collections.defaultdict` with the
-    ``default_factory`` set to ``None``, such that it behaves like a normal
-    dictionary.
-
-    """
-    valuefunc = (lambda x: x) if (valuefunc is None) else valuefunc
-
-    ret = defaultdict(list)
-    for item in iterable:
-        key = keyfunc(item)
-        value = valuefunc(item)
-        ret[key].append(value)
-
-    if reducefunc is not None:
-        for key, value_list in ret.items():
-            ret[key] = reducefunc(value_list)
-
-    ret.default_factory = None
-    return ret
-
-
-def rlocate(iterable, pred=bool, window_size=None):
-    """Yield the index of each item in *iterable* for which *pred* returns
-    ``True``, starting from the right and moving left.
-
-    *pred* defaults to :func:`bool`, which will select truthy items:
-
-        >>> list(rlocate([0, 1, 1, 0, 1, 0, 0]))  # Truthy at 1, 2, and 4
-        [4, 2, 1]
-
-    Set *pred* to a custom function to, e.g., find the indexes for a particular
-    item:
-
-        >>> iterable = iter('abcb')
-        >>> pred = lambda x: x == 'b'
-        >>> list(rlocate(iterable, pred))
-        [3, 1]
-
-    If *window_size* is given, then the *pred* function will be called with
-    that many items. This enables searching for sub-sequences:
-
-        >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]
-        >>> pred = lambda *args: args == (1, 2, 3)
-        >>> list(rlocate(iterable, pred=pred, window_size=3))
-        [9, 5, 1]
-
-    Beware, this function won't return anything for infinite iterables.
-    If *iterable* is reversible, ``rlocate`` will reverse it and search from
-    the right. Otherwise, it will search from the left and return the results
-    in reverse order.
-
-    See :func:`locate` to for other example applications.
-
-    """
-    if window_size is None:
-        try:
-            len_iter = len(iterable)
-            return (
-                len_iter - i - 1 for i in locate(reversed(iterable), pred)
-            )
-        except TypeError:
-            pass
-
-    return reversed(list(locate(iterable, pred, window_size)))
-
-
-def replace(iterable, pred, substitutes, count=None, window_size=1):
-    """Yield the items from *iterable*, replacing the items for which *pred*
-    returns ``True`` with the items from the iterable *substitutes*.
-
-        >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1]
-        >>> pred = lambda x: x == 0
-        >>> substitutes = (2, 3)
-        >>> list(replace(iterable, pred, substitutes))
-        [1, 1, 2, 3, 1, 1, 2, 3, 1, 1]
-
-    If *count* is given, the number of replacements will be limited:
-
-        >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1, 0]
-        >>> pred = lambda x: x == 0
-        >>> substitutes = [None]
-        >>> list(replace(iterable, pred, substitutes, count=2))
-        [1, 1, None, 1, 1, None, 1, 1, 0]
-
-    Use *window_size* to control the number of items passed as arguments to
-    *pred*. This allows for locating and replacing subsequences.
-
-        >>> iterable = [0, 1, 2, 5, 0, 1, 2, 5]
-        >>> window_size = 3
-        >>> pred = lambda *args: args == (0, 1, 2)  # 3 items passed to pred
-        >>> substitutes = [3, 4] # Splice in these items
-        >>> list(replace(iterable, pred, substitutes, window_size=window_size))
-        [3, 4, 5, 3, 4, 5]
-
-    """
-    if window_size < 1:
-        raise ValueError('window_size must be at least 1')
-
-    # Save the substitutes iterable, since it's used more than once
-    substitutes = tuple(substitutes)
-
-    # Add padding such that the number of windows matches the length of the
-    # iterable
-    it = chain(iterable, [_marker] * (window_size - 1))
-    windows = windowed(it, window_size)
-
-    n = 0
-    for w in windows:
-        # If the current window matches our predicate (and we haven't hit
-        # our maximum number of replacements), splice in the substitutes
-        # and then consume the following windows that overlap with this one.
-        # For example, if the iterable is (0, 1, 2, 3, 4...)
-        # and the window size is 2, we have (0, 1), (1, 2), (2, 3)...
-        # If the predicate matches on (0, 1), we need to zap (0, 1) and (1, 2)
-        if pred(*w):
-            if (count is None) or (n < count):
-                n += 1
-                for s in substitutes:
-                    yield s
-                consume(windows, window_size - 1)
-                continue
-
-        # If there was no match (or we've reached the replacement limit),
-        # yield the first item from the window.
-        if w and (w[0] is not _marker):
-            yield w[0]
diff --git a/libraries/more_itertools/recipes.py b/libraries/more_itertools/recipes.py
deleted file mode 100644
index 3a7706cb..00000000
--- a/libraries/more_itertools/recipes.py
+++ /dev/null
@@ -1,565 +0,0 @@
-"""Imported from the recipes section of the itertools documentation.
-
-All functions taken from the recipes section of the itertools library docs
-[1]_.
-Some backward-compatible usability improvements have been made.
-
-.. [1] http://docs.python.org/library/itertools.html#recipes
-
-"""
-from collections import deque
-from itertools import (
-    chain, combinations, count, cycle, groupby, islice, repeat, starmap, tee
-)
-import operator
-from random import randrange, sample, choice
-
-from six import PY2
-from six.moves import filter, filterfalse, map, range, zip, zip_longest
-
-__all__ = [
-    'accumulate',
-    'all_equal',
-    'consume',
-    'dotproduct',
-    'first_true',
-    'flatten',
-    'grouper',
-    'iter_except',
-    'ncycles',
-    'nth',
-    'nth_combination',
-    'padnone',
-    'pairwise',
-    'partition',
-    'powerset',
-    'prepend',
-    'quantify',
-    'random_combination_with_replacement',
-    'random_combination',
-    'random_permutation',
-    'random_product',
-    'repeatfunc',
-    'roundrobin',
-    'tabulate',
-    'tail',
-    'take',
-    'unique_everseen',
-    'unique_justseen',
-]
-
-
-def accumulate(iterable, func=operator.add):
-    """
-    Return an iterator whose items are the accumulated results of a function
-    (specified by the optional *func* argument) that takes two arguments.
-    By default, returns accumulated sums with :func:`operator.add`.
-
-        >>> list(accumulate([1, 2, 3, 4, 5]))  # Running sum
-        [1, 3, 6, 10, 15]
-        >>> list(accumulate([1, 2, 3], func=operator.mul))  # Running product
-        [1, 2, 6]
-        >>> list(accumulate([0, 1, -1, 2, 3, 2], func=max))  # Running maximum
-        [0, 1, 1, 2, 3, 3]
-
-    This function is available in the ``itertools`` module for Python 3.2 and
-    greater.
-
-    """
-    it = iter(iterable)
-    try:
-        total = next(it)
-    except StopIteration:
-        return
-    else:
-        yield total
-
-    for element in it:
-        total = func(total, element)
-        yield total
-
-
-def take(n, iterable):
-    """Return first *n* items of the iterable as a list.
-
-        >>> take(3, range(10))
-        [0, 1, 2]
-        >>> take(5, range(3))
-        [0, 1, 2]
-
-    Effectively a short replacement for ``next`` based iterator consumption
-    when you want more than one item, but less than the whole iterator.
-
-    """
-    return list(islice(iterable, n))
-
-
-def tabulate(function, start=0):
-    """Return an iterator over the results of ``func(start)``,
-    ``func(start + 1)``, ``func(start + 2)``...
-
-    *func* should be a function that accepts one integer argument.
-
-    If *start* is not specified it defaults to 0. It will be incremented each
-    time the iterator is advanced.
-
-        >>> square = lambda x: x ** 2
-        >>> iterator = tabulate(square, -3)
-        >>> take(4, iterator)
-        [9, 4, 1, 0]
-
-    """
-    return map(function, count(start))
-
-
-def tail(n, iterable):
-    """Return an iterator over the last *n* items of *iterable*.
-
-        >>> t = tail(3, 'ABCDEFG')
-        >>> list(t)
-        ['E', 'F', 'G']
-
-    """
-    return iter(deque(iterable, maxlen=n))
-
-
-def consume(iterator, n=None):
-    """Advance *iterable* by *n* steps. If *n* is ``None``, consume it
-    entirely.
-
-    Efficiently exhausts an iterator without returning values. Defaults to
-    consuming the whole iterator, but an optional second argument may be
-    provided to limit consumption.
-
-        >>> i = (x for x in range(10))
-        >>> next(i)
-        0
-        >>> consume(i, 3)
-        >>> next(i)
-        4
-        >>> consume(i)
-        >>> next(i)
-        Traceback (most recent call last):
-          File "<stdin>", line 1, in <module>
-        StopIteration
-
-    If the iterator has fewer items remaining than the provided limit, the
-    whole iterator will be consumed.
-
-        >>> i = (x for x in range(3))
-        >>> consume(i, 5)
-        >>> next(i)
-        Traceback (most recent call last):
-          File "<stdin>", line 1, in <module>
-        StopIteration
-
-    """
-    # Use functions that consume iterators at C speed.
-    if n is None:
-        # feed the entire iterator into a zero-length deque
-        deque(iterator, maxlen=0)
-    else:
-        # advance to the empty slice starting at position n
-        next(islice(iterator, n, n), None)
-
-
-def nth(iterable, n, default=None):
-    """Returns the nth item or a default value.
-
-        >>> l = range(10)
-        >>> nth(l, 3)
-        3
-        >>> nth(l, 20, "zebra")
-        'zebra'
-
-    """
-    return next(islice(iterable, n, None), default)
-
-
-def all_equal(iterable):
-    """
-    Returns ``True`` if all the elements are equal to each other.
-
-        >>> all_equal('aaaa')
-        True
-        >>> all_equal('aaab')
-        False
-
-    """
-    g = groupby(iterable)
-    return next(g, True) and not next(g, False)
-
-
-def quantify(iterable, pred=bool):
-    """Return the how many times the predicate is true.
-
-        >>> quantify([True, False, True])
-        2
-
-    """
-    return sum(map(pred, iterable))
-
-
-def padnone(iterable):
-    """Returns the sequence of elements and then returns ``None`` indefinitely.
-
-        >>> take(5, padnone(range(3)))
-        [0, 1, 2, None, None]
-
-    Useful for emulating the behavior of the built-in :func:`map` function.
-
-    See also :func:`padded`.
-
-    """
-    return chain(iterable, repeat(None))
-
-
-def ncycles(iterable, n):
-    """Returns the sequence elements *n* times
-
-        >>> list(ncycles(["a", "b"], 3))
-        ['a', 'b', 'a', 'b', 'a', 'b']
-
-    """
-    return chain.from_iterable(repeat(tuple(iterable), n))
-
-
-def dotproduct(vec1, vec2):
-    """Returns the dot product of the two iterables.
-
-        >>> dotproduct([10, 10], [20, 20])
-        400
-
-    """
-    return sum(map(operator.mul, vec1, vec2))
-
-
-def flatten(listOfLists):
-    """Return an iterator flattening one level of nesting in a list of lists.
-
-        >>> list(flatten([[0, 1], [2, 3]]))
-        [0, 1, 2, 3]
-
-    See also :func:`collapse`, which can flatten multiple levels of nesting.
-
-    """
-    return chain.from_iterable(listOfLists)
-
-
-def repeatfunc(func, times=None, *args):
-    """Call *func* with *args* repeatedly, returning an iterable over the
-    results.
-
-    If *times* is specified, the iterable will terminate after that many
-    repetitions:
-
-        >>> from operator import add
-        >>> times = 4
-        >>> args = 3, 5
-        >>> list(repeatfunc(add, times, *args))
-        [8, 8, 8, 8]
-
-    If *times* is ``None`` the iterable will not terminate:
-
-        >>> from random import randrange
-        >>> times = None
-        >>> args = 1, 11
-        >>> take(6, repeatfunc(randrange, times, *args))  # doctest:+SKIP
-        [2, 4, 8, 1, 8, 4]
-
-    """
-    if times is None:
-        return starmap(func, repeat(args))
-    return starmap(func, repeat(args, times))
-
-
-def pairwise(iterable):
-    """Returns an iterator of paired items, overlapping, from the original
-
-        >>> take(4, pairwise(count()))
-        [(0, 1), (1, 2), (2, 3), (3, 4)]
-
-    """
-    a, b = tee(iterable)
-    next(b, None)
-    return zip(a, b)
-
-
-def grouper(n, iterable, fillvalue=None):
-    """Collect data into fixed-length chunks or blocks.
-
-        >>> list(grouper(3, 'ABCDEFG', 'x'))
-        [('A', 'B', 'C'), ('D', 'E', 'F'), ('G', 'x', 'x')]
-
-    """
-    args = [iter(iterable)] * n
-    return zip_longest(fillvalue=fillvalue, *args)
-
-
-def roundrobin(*iterables):
-    """Yields an item from each iterable, alternating between them.
-
-        >>> list(roundrobin('ABC', 'D', 'EF'))
-        ['A', 'D', 'E', 'B', 'F', 'C']
-
-    This function produces the same output as :func:`interleave_longest`, but
-    may perform better for some inputs (in particular when the number of
-    iterables is small).
-
-    """
-    # Recipe credited to George Sakkis
-    pending = len(iterables)
-    if PY2:
-        nexts = cycle(iter(it).next for it in iterables)
-    else:
-        nexts = cycle(iter(it).__next__ for it in iterables)
-    while pending:
-        try:
-            for next in nexts:
-                yield next()
-        except StopIteration:
-            pending -= 1
-            nexts = cycle(islice(nexts, pending))
-
-
-def partition(pred, iterable):
-    """
-    Returns a 2-tuple of iterables derived from the input iterable.
-    The first yields the items that have ``pred(item) == False``.
-    The second yields the items that have ``pred(item) == True``.
-
-        >>> is_odd = lambda x: x % 2 != 0
-        >>> iterable = range(10)
-        >>> even_items, odd_items = partition(is_odd, iterable)
-        >>> list(even_items), list(odd_items)
-        ([0, 2, 4, 6, 8], [1, 3, 5, 7, 9])
-
-    """
-    # partition(is_odd, range(10)) --> 0 2 4 6 8   and  1 3 5 7 9
-    t1, t2 = tee(iterable)
-    return filterfalse(pred, t1), filter(pred, t2)
-
-
-def powerset(iterable):
-    """Yields all possible subsets of the iterable.
-
-        >>> list(powerset([1,2,3]))
-        [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
-
-    """
-    s = list(iterable)
-    return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1))
-
-
-def unique_everseen(iterable, key=None):
-    """
-    Yield unique elements, preserving order.
-
-        >>> list(unique_everseen('AAAABBBCCDAABBB'))
-        ['A', 'B', 'C', 'D']
-        >>> list(unique_everseen('ABBCcAD', str.lower))
-        ['A', 'B', 'C', 'D']
-
-    Sequences with a mix of hashable and unhashable items can be used.
-    The function will be slower (i.e., `O(n^2)`) for unhashable items.
-
-    """
-    seenset = set()
-    seenset_add = seenset.add
-    seenlist = []
-    seenlist_add = seenlist.append
-    if key is None:
-        for element in iterable:
-            try:
-                if element not in seenset:
-                    seenset_add(element)
-                    yield element
-            except TypeError:
-                if element not in seenlist:
-                    seenlist_add(element)
-                    yield element
-    else:
-        for element in iterable:
-            k = key(element)
-            try:
-                if k not in seenset:
-                    seenset_add(k)
-                    yield element
-            except TypeError:
-                if k not in seenlist:
-                    seenlist_add(k)
-                    yield element
-
-
-def unique_justseen(iterable, key=None):
-    """Yields elements in order, ignoring serial duplicates
-
-        >>> list(unique_justseen('AAAABBBCCDAABBB'))
-        ['A', 'B', 'C', 'D', 'A', 'B']
-        >>> list(unique_justseen('ABBCcAD', str.lower))
-        ['A', 'B', 'C', 'A', 'D']
-
-    """
-    return map(next, map(operator.itemgetter(1), groupby(iterable, key)))
-
-
-def iter_except(func, exception, first=None):
-    """Yields results from a function repeatedly until an exception is raised.
-
-    Converts a call-until-exception interface to an iterator interface.
-    Like ``iter(func, sentinel)``, but uses an exception instead of a sentinel
-    to end the loop.
-
-        >>> l = [0, 1, 2]
-        >>> list(iter_except(l.pop, IndexError))
-        [2, 1, 0]
-
-    """
-    try:
-        if first is not None:
-            yield first()
-        while 1:
-            yield func()
-    except exception:
-        pass
-
-
-def first_true(iterable, default=False, pred=None):
-    """
-    Returns the first true value in the iterable.
-
-    If no true value is found, returns *default*
-
-    If *pred* is not None, returns the first item for which
-    ``pred(item) == True`` .
-
-        >>> first_true(range(10))
-        1
-        >>> first_true(range(10), pred=lambda x: x > 5)
-        6
-        >>> first_true(range(10), default='missing', pred=lambda x: x > 9)
-        'missing'
-
-    """
-    return next(filter(pred, iterable), default)
-
-
-def random_product(*args, **kwds):
-    """Draw an item at random from each of the input iterables.
-
-        >>> random_product('abc', range(4), 'XYZ')  # doctest:+SKIP
-        ('c', 3, 'Z')
-
-    If *repeat* is provided as a keyword argument, that many items will be
-    drawn from each iterable.
-
-        >>> random_product('abcd', range(4), repeat=2)  # doctest:+SKIP
-        ('a', 2, 'd', 3)
-
-    This equivalent to taking a random selection from
-    ``itertools.product(*args, **kwarg)``.
-
-    """
-    pools = [tuple(pool) for pool in args] * kwds.get('repeat', 1)
-    return tuple(choice(pool) for pool in pools)
-
-
-def random_permutation(iterable, r=None):
-    """Return a random *r* length permutation of the elements in *iterable*.
-
-    If *r* is not specified or is ``None``, then *r* defaults to the length of
-    *iterable*.
-
-        >>> random_permutation(range(5))  # doctest:+SKIP
-        (3, 4, 0, 1, 2)
-
-    This equivalent to taking a random selection from
-    ``itertools.permutations(iterable, r)``.
-
-    """
-    pool = tuple(iterable)
-    r = len(pool) if r is None else r
-    return tuple(sample(pool, r))
-
-
-def random_combination(iterable, r):
-    """Return a random *r* length subsequence of the elements in *iterable*.
-
-        >>> random_combination(range(5), 3)  # doctest:+SKIP
-        (2, 3, 4)
-
-    This equivalent to taking a random selection from
-    ``itertools.combinations(iterable, r)``.
-
-    """
-    pool = tuple(iterable)
-    n = len(pool)
-    indices = sorted(sample(range(n), r))
-    return tuple(pool[i] for i in indices)
-
-
-def random_combination_with_replacement(iterable, r):
-    """Return a random *r* length subsequence of elements in *iterable*,
-    allowing individual elements to be repeated.
-
-        >>> random_combination_with_replacement(range(3), 5) # doctest:+SKIP
-        (0, 0, 1, 2, 2)
-
-    This equivalent to taking a random selection from
-    ``itertools.combinations_with_replacement(iterable, r)``.
-
-    """
-    pool = tuple(iterable)
-    n = len(pool)
-    indices = sorted(randrange(n) for i in range(r))
-    return tuple(pool[i] for i in indices)
-
-
-def nth_combination(iterable, r, index):
-    """Equivalent to ``list(combinations(iterable, r))[index]``.
-
-    The subsequences of *iterable* that are of length *r* can be ordered
-    lexicographically. :func:`nth_combination` computes the subsequence at
-    sort position *index* directly, without computing the previous
-    subsequences.
-
-    """
-    pool = tuple(iterable)
-    n = len(pool)
-    if (r < 0) or (r > n):
-        raise ValueError
-
-    c = 1
-    k = min(r, n - r)
-    for i in range(1, k + 1):
-        c = c * (n - k + i) // i
-
-    if index < 0:
-        index += c
-
-    if (index < 0) or (index >= c):
-        raise IndexError
-
-    result = []
-    while r:
-        c, n, r = c * r // n, n - 1, r - 1
-        while index >= c:
-            index -= c
-            c, n = c * (n - r) // n, n - 1
-        result.append(pool[-1 - n])
-
-    return tuple(result)
-
-
-def prepend(value, iterator):
-    """Yield *value*, followed by the elements in *iterator*.
-
-        >>> value = '0'
-        >>> iterator = ['1', '2', '3']
-        >>> list(prepend(value, iterator))
-        ['0', '1', '2', '3']
-
-    To prepend multiple values, see :func:`itertools.chain`.
-
-    """
-    return chain([value], iterator)
diff --git a/libraries/more_itertools/tests/__init__.py b/libraries/more_itertools/tests/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/libraries/more_itertools/tests/test_more.py b/libraries/more_itertools/tests/test_more.py
deleted file mode 100644
index a1b1e431..00000000
--- a/libraries/more_itertools/tests/test_more.py
+++ /dev/null
@@ -1,2074 +0,0 @@
-from __future__ import division, print_function, unicode_literals
-
-from collections import OrderedDict
-from decimal import Decimal
-from doctest import DocTestSuite
-from fractions import Fraction
-from functools import partial, reduce
-from heapq import merge
-from io import StringIO
-from itertools import (
-    chain,
-    count,
-    groupby,
-    islice,
-    permutations,
-    product,
-    repeat,
-)
-from operator import add, mul, itemgetter
-from unittest import TestCase
-
-from six.moves import filter, map, range, zip
-
-import more_itertools as mi
-
-
-def load_tests(loader, tests, ignore):
-    # Add the doctests
-    tests.addTests(DocTestSuite('more_itertools.more'))
-    return tests
-
-
-class CollateTests(TestCase):
-    """Unit tests for ``collate()``"""
-    # Also accidentally tests peekable, though that could use its own tests
-
-    def test_default(self):
-        """Test with the default `key` function."""
-        iterables = [range(4), range(7), range(3, 6)]
-        self.assertEqual(
-            sorted(reduce(list.__add__, [list(it) for it in iterables])),
-            list(mi.collate(*iterables))
-        )
-
-    def test_key(self):
-        """Test using a custom `key` function."""
-        iterables = [range(5, 0, -1), range(4, 0, -1)]
-        actual = sorted(
-            reduce(list.__add__, [list(it) for it in iterables]), reverse=True
-        )
-        expected = list(mi.collate(*iterables, key=lambda x: -x))
-        self.assertEqual(actual, expected)
-
-    def test_empty(self):
-        """Be nice if passed an empty list of iterables."""
-        self.assertEqual([], list(mi.collate()))
-
-    def test_one(self):
-        """Work when only 1 iterable is passed."""
-        self.assertEqual([0, 1], list(mi.collate(range(2))))
-
-    def test_reverse(self):
-        """Test the `reverse` kwarg."""
-        iterables = [range(4, 0, -1), range(7, 0, -1), range(3, 6, -1)]
-
-        actual = sorted(
-            reduce(list.__add__, [list(it) for it in iterables]), reverse=True
-        )
-        expected = list(mi.collate(*iterables, reverse=True))
-        self.assertEqual(actual, expected)
-
-    def test_alias(self):
-        self.assertNotEqual(merge.__doc__, mi.collate.__doc__)
-        self.assertNotEqual(partial.__doc__, mi.collate.__doc__)
-
-
-class ChunkedTests(TestCase):
-    """Tests for ``chunked()``"""
-
-    def test_even(self):
-        """Test when ``n`` divides evenly into the length of the iterable."""
-        self.assertEqual(
-            list(mi.chunked('ABCDEF', 3)), [['A', 'B', 'C'], ['D', 'E', 'F']]
-        )
-
-    def test_odd(self):
-        """Test when ``n`` does not divide evenly into the length of the
-        iterable.
-
-        """
-        self.assertEqual(
-            list(mi.chunked('ABCDE', 3)), [['A', 'B', 'C'], ['D', 'E']]
-        )
-
-
-class FirstTests(TestCase):
-    """Tests for ``first()``"""
-
-    def test_many(self):
-        """Test that it works on many-item iterables."""
-        # Also try it on a generator expression to make sure it works on
-        # whatever those return, across Python versions.
-        self.assertEqual(mi.first(x for x in range(4)), 0)
-
-    def test_one(self):
-        """Test that it doesn't raise StopIteration prematurely."""
-        self.assertEqual(mi.first([3]), 3)
-
-    def test_empty_stop_iteration(self):
-        """It should raise StopIteration for empty iterables."""
-        self.assertRaises(ValueError, lambda: mi.first([]))
-
-    def test_default(self):
-        """It should return the provided default arg for empty iterables."""
-        self.assertEqual(mi.first([], 'boo'), 'boo')
-
-
-class IterOnlyRange:
-    """User-defined iterable class which only support __iter__.
-
-    It is not specified to inherit ``object``, so indexing on a instance will
-    raise an ``AttributeError`` rather than ``TypeError`` in Python 2.
-
-    >>> r = IterOnlyRange(5)
-    >>> r[0]
-    AttributeError: IterOnlyRange instance has no attribute '__getitem__'
-
-    Note: In Python 3, ``TypeError`` will be raised because ``object`` is
-    inherited implicitly by default.
-
-    >>> r[0]
-    TypeError: 'IterOnlyRange' object does not support indexing
-    """
-    def __init__(self, n):
-        """Set the length of the range."""
-        self.n = n
-
-    def __iter__(self):
-        """Works same as range()."""
-        return iter(range(self.n))
-
-
-class LastTests(TestCase):
-    """Tests for ``last()``"""
-
-    def test_many_nonsliceable(self):
-        """Test that it works on many-item non-slice-able iterables."""
-        # Also try it on a generator expression to make sure it works on
-        # whatever those return, across Python versions.
-        self.assertEqual(mi.last(x for x in range(4)), 3)
-
-    def test_one_nonsliceable(self):
-        """Test that it doesn't raise StopIteration prematurely."""
-        self.assertEqual(mi.last(x for x in range(1)), 0)
-
-    def test_empty_stop_iteration_nonsliceable(self):
-        """It should raise ValueError for empty non-slice-able iterables."""
-        self.assertRaises(ValueError, lambda: mi.last(x for x in range(0)))
-
-    def test_default_nonsliceable(self):
-        """It should return the provided default arg for empty non-slice-able
-        iterables.
-        """
-        self.assertEqual(mi.last((x for x in range(0)), 'boo'), 'boo')
-
-    def test_many_sliceable(self):
-        """Test that it works on many-item slice-able iterables."""
-        self.assertEqual(mi.last([0, 1, 2, 3]), 3)
-
-    def test_one_sliceable(self):
-        """Test that it doesn't raise StopIteration prematurely."""
-        self.assertEqual(mi.last([3]), 3)
-
-    def test_empty_stop_iteration_sliceable(self):
-        """It should raise ValueError for empty slice-able iterables."""
-        self.assertRaises(ValueError, lambda: mi.last([]))
-
-    def test_default_sliceable(self):
-        """It should return the provided default arg for empty slice-able
-        iterables.
-        """
-        self.assertEqual(mi.last([], 'boo'), 'boo')
-
-    def test_dict(self):
-        """last(dic) and last(dic.keys()) should return same result."""
-        dic = {'a': 1, 'b': 2, 'c': 3}
-        self.assertEqual(mi.last(dic), mi.last(dic.keys()))
-
-    def test_ordereddict(self):
-        """last(dic) should return the last key."""
-        od = OrderedDict()
-        od['a'] = 1
-        od['b'] = 2
-        od['c'] = 3
-        self.assertEqual(mi.last(od), 'c')
-
-    def test_customrange(self):
-        """It should work on custom class where [] raises AttributeError."""
-        self.assertEqual(mi.last(IterOnlyRange(5)), 4)
-
-
-class PeekableTests(TestCase):
-    """Tests for ``peekable()`` behavor not incidentally covered by testing
-    ``collate()``
-
-    """
-    def test_peek_default(self):
-        """Make sure passing a default into ``peek()`` works."""
-        p = mi.peekable([])
-        self.assertEqual(p.peek(7), 7)
-
-    def test_truthiness(self):
-        """Make sure a ``peekable`` tests true iff there are items remaining in
-        the iterable.
-
-        """
-        p = mi.peekable([])
-        self.assertFalse(p)
-
-        p = mi.peekable(range(3))
-        self.assertTrue(p)
-
-    def test_simple_peeking(self):
-        """Make sure ``next`` and ``peek`` advance and don't advance the
-        iterator, respectively.
-
-        """
-        p = mi.peekable(range(10))
-        self.assertEqual(next(p), 0)
-        self.assertEqual(p.peek(), 1)
-        self.assertEqual(next(p), 1)
-
-    def test_indexing(self):
-        """
-        Indexing into the peekable shouldn't advance the iterator.
-        """
-        p = mi.peekable('abcdefghijkl')
-
-        # The 0th index is what ``next()`` will return
-        self.assertEqual(p[0], 'a')
-        self.assertEqual(next(p), 'a')
-
-        # Indexing further into the peekable shouldn't advance the itertor
-        self.assertEqual(p[2], 'd')
-        self.assertEqual(next(p), 'b')
-
-        # The 0th index moves up with the iterator; the last index follows
-        self.assertEqual(p[0], 'c')
-        self.assertEqual(p[9], 'l')
-
-        self.assertEqual(next(p), 'c')
-        self.assertEqual(p[8], 'l')
-
-        # Negative indexing should work too
-        self.assertEqual(p[-2], 'k')
-        self.assertEqual(p[-9], 'd')
-        self.assertRaises(IndexError, lambda: p[-10])
-
-    def test_slicing(self):
-        """Slicing the peekable shouldn't advance the iterator."""
-        seq = list('abcdefghijkl')
-        p = mi.peekable(seq)
-
-        # Slicing the peekable should just be like slicing a re-iterable
-        self.assertEqual(p[1:4], seq[1:4])
-
-        # Advancing the iterator moves the slices up also
-        self.assertEqual(next(p), 'a')
-        self.assertEqual(p[1:4], seq[1:][1:4])
-
-        # Implicit starts and stop should work
-        self.assertEqual(p[:5], seq[1:][:5])
-        self.assertEqual(p[:], seq[1:][:])
-
-        # Indexing past the end should work
-        self.assertEqual(p[:100], seq[1:][:100])
-
-        # Steps should work, including negative
-        self.assertEqual(p[::2], seq[1:][::2])
-        self.assertEqual(p[::-1], seq[1:][::-1])
-
-    def test_slicing_reset(self):
-        """Test slicing on a fresh iterable each time"""
-        iterable = ['0', '1', '2', '3', '4', '5']
-        indexes = list(range(-4, len(iterable) + 4)) + [None]
-        steps = [1, 2, 3, 4, -1, -2, -3, 4]
-        for slice_args in product(indexes, indexes, steps):
-            it = iter(iterable)
-            p = mi.peekable(it)
-            next(p)
-            index = slice(*slice_args)
-            actual = p[index]
-            expected = iterable[1:][index]
-            self.assertEqual(actual, expected, slice_args)
-
-    def test_slicing_error(self):
-        iterable = '01234567'
-        p = mi.peekable(iter(iterable))
-
-        # Prime the cache
-        p.peek()
-        old_cache = list(p._cache)
-
-        # Illegal slice
-        with self.assertRaises(ValueError):
-            p[1:-1:0]
-
-        # Neither the cache nor the iteration should be affected
-        self.assertEqual(old_cache, list(p._cache))
-        self.assertEqual(list(p), list(iterable))
-
-    def test_passthrough(self):
-        """Iterating a peekable without using ``peek()`` or ``prepend()``
-        should just give the underlying iterable's elements (a trivial test but
-        useful to set a baseline in case something goes wrong)"""
-        expected = [1, 2, 3, 4, 5]
-        actual = list(mi.peekable(expected))
-        self.assertEqual(actual, expected)
-
-    # prepend() behavior tests
-
-    def test_prepend(self):
-        """Tests intersperesed ``prepend()`` and ``next()`` calls"""
-        it = mi.peekable(range(2))
-        actual = []
-
-        # Test prepend() before next()
-        it.prepend(10)
-        actual += [next(it), next(it)]
-
-        # Test prepend() between next()s
-        it.prepend(11)
-        actual += [next(it), next(it)]
-
-        # Test prepend() after source iterable is consumed
-        it.prepend(12)
-        actual += [next(it)]
-
-        expected = [10, 0, 11, 1, 12]
-        self.assertEqual(actual, expected)
-
-    def test_multi_prepend(self):
-        """Tests prepending multiple items and getting them in proper order"""
-        it = mi.peekable(range(5))
-        actual = [next(it), next(it)]
-        it.prepend(10, 11, 12)
-        it.prepend(20, 21)
-        actual += list(it)
-        expected = [0, 1, 20, 21, 10, 11, 12, 2, 3, 4]
-        self.assertEqual(actual, expected)
-
-    def test_empty(self):
-        """Tests prepending in front of an empty iterable"""
-        it = mi.peekable([])
-        it.prepend(10)
-        actual = list(it)
-        expected = [10]
-        self.assertEqual(actual, expected)
-
-    def test_prepend_truthiness(self):
-        """Tests that ``__bool__()`` or ``__nonzero__()`` works properly
-        with ``prepend()``"""
-        it = mi.peekable(range(5))
-        self.assertTrue(it)
-        actual = list(it)
-        self.assertFalse(it)
-        it.prepend(10)
-        self.assertTrue(it)
-        actual += [next(it)]
-        self.assertFalse(it)
-        expected = [0, 1, 2, 3, 4, 10]
-        self.assertEqual(actual, expected)
-
-    def test_multi_prepend_peek(self):
-        """Tests prepending multiple elements and getting them in reverse order
-        while peeking"""
-        it = mi.peekable(range(5))
-        actual = [next(it), next(it)]
-        self.assertEqual(it.peek(), 2)
-        it.prepend(10, 11, 12)
-        self.assertEqual(it.peek(), 10)
-        it.prepend(20, 21)
-        self.assertEqual(it.peek(), 20)
-        actual += list(it)
-        self.assertFalse(it)
-        expected = [0, 1, 20, 21, 10, 11, 12, 2, 3, 4]
-        self.assertEqual(actual, expected)
-
-    def test_prepend_after_stop(self):
-        """Test resuming iteration after a previous exhaustion"""
-        it = mi.peekable(range(3))
-        self.assertEqual(list(it), [0, 1, 2])
-        self.assertRaises(StopIteration, lambda: next(it))
-        it.prepend(10)
-        self.assertEqual(next(it), 10)
-        self.assertRaises(StopIteration, lambda: next(it))
-
-    def test_prepend_slicing(self):
-        """Tests interaction between prepending and slicing"""
-        seq = list(range(20))
-        p = mi.peekable(seq)
-
-        p.prepend(30, 40, 50)
-        pseq = [30, 40, 50] + seq  # pseq for prepended_seq
-
-        # adapt the specific tests from test_slicing
-        self.assertEqual(p[0], 30)
-        self.assertEqual(p[1:8], pseq[1:8])
-        self.assertEqual(p[1:], pseq[1:])
-        self.assertEqual(p[:5], pseq[:5])
-        self.assertEqual(p[:], pseq[:])
-        self.assertEqual(p[:100], pseq[:100])
-        self.assertEqual(p[::2], pseq[::2])
-        self.assertEqual(p[::-1], pseq[::-1])
-
-    def test_prepend_indexing(self):
-        """Tests interaction between prepending and indexing"""
-        seq = list(range(20))
-        p = mi.peekable(seq)
-
-        p.prepend(30, 40, 50)
-
-        self.assertEqual(p[0], 30)
-        self.assertEqual(next(p), 30)
-        self.assertEqual(p[2], 0)
-        self.assertEqual(next(p), 40)
-        self.assertEqual(p[0], 50)
-        self.assertEqual(p[9], 8)
-        self.assertEqual(next(p), 50)
-        self.assertEqual(p[8], 8)
-        self.assertEqual(p[-2], 18)
-        self.assertEqual(p[-9], 11)
-        self.assertRaises(IndexError, lambda: p[-21])
-
-    def test_prepend_iterable(self):
-        """Tests prepending from an iterable"""
-        it = mi.peekable(range(5))
-        # Don't directly use the range() object to avoid any range-specific
-        # optimizations
-        it.prepend(*(x for x in range(5)))
-        actual = list(it)
-        expected = list(chain(range(5), range(5)))
-        self.assertEqual(actual, expected)
-
-    def test_prepend_many(self):
-        """Tests that prepending a huge number of elements works"""
-        it = mi.peekable(range(5))
-        # Don't directly use the range() object to avoid any range-specific
-        # optimizations
-        it.prepend(*(x for x in range(20000)))
-        actual = list(it)
-        expected = list(chain(range(20000), range(5)))
-        self.assertEqual(actual, expected)
-
-    def test_prepend_reversed(self):
-        """Tests prepending from a reversed iterable"""
-        it = mi.peekable(range(3))
-        it.prepend(*reversed((10, 11, 12)))
-        actual = list(it)
-        expected = [12, 11, 10, 0, 1, 2]
-        self.assertEqual(actual, expected)
-
-
-class ConsumerTests(TestCase):
-    """Tests for ``consumer()``"""
-
-    def test_consumer(self):
-        @mi.consumer
-        def eater():
-            while True:
-                x = yield  # noqa
-
-        e = eater()
-        e.send('hi')  # without @consumer, would raise TypeError
-
-
-class DistinctPermutationsTests(TestCase):
-    def test_distinct_permutations(self):
-        """Make sure the output for ``distinct_permutations()`` is the same as
-        set(permutations(it)).
-
-        """
-        iterable = ['z', 'a', 'a', 'q', 'q', 'q', 'y']
-        test_output = sorted(mi.distinct_permutations(iterable))
-        ref_output = sorted(set(permutations(iterable)))
-        self.assertEqual(test_output, ref_output)
-
-    def test_other_iterables(self):
-        """Make sure ``distinct_permutations()`` accepts a different type of
-        iterables.
-
-        """
-        # a generator
-        iterable = (c for c in ['z', 'a', 'a', 'q', 'q', 'q', 'y'])
-        test_output = sorted(mi.distinct_permutations(iterable))
-        # "reload" it
-        iterable = (c for c in ['z', 'a', 'a', 'q', 'q', 'q', 'y'])
-        ref_output = sorted(set(permutations(iterable)))
-        self.assertEqual(test_output, ref_output)
-
-        # an iterator
-        iterable = iter(['z', 'a', 'a', 'q', 'q', 'q', 'y'])
-        test_output = sorted(mi.distinct_permutations(iterable))
-        # "reload" it
-        iterable = iter(['z', 'a', 'a', 'q', 'q', 'q', 'y'])
-        ref_output = sorted(set(permutations(iterable)))
-        self.assertEqual(test_output, ref_output)
-
-
-class IlenTests(TestCase):
-    def test_ilen(self):
-        """Sanity-checks for ``ilen()``."""
-        # Non-empty
-        self.assertEqual(
-            mi.ilen(filter(lambda x: x % 10 == 0, range(101))), 11
-        )
-
-        # Empty
-        self.assertEqual(mi.ilen((x for x in range(0))), 0)
-
-        # Iterable with __len__
-        self.assertEqual(mi.ilen(list(range(6))), 6)
-
-
-class WithIterTests(TestCase):
-    def test_with_iter(self):
-        s = StringIO('One fish\nTwo fish')
-        initial_words = [line.split()[0] for line in mi.with_iter(s)]
-
-        # Iterable's items should be faithfully represented
-        self.assertEqual(initial_words, ['One', 'Two'])
-        # The file object should be closed
-        self.assertEqual(s.closed, True)
-
-
-class OneTests(TestCase):
-    def test_basic(self):
-        it = iter(['item'])
-        self.assertEqual(mi.one(it), 'item')
-
-    def test_too_short(self):
-        it = iter([])
-        self.assertRaises(ValueError, lambda: mi.one(it))
-        self.assertRaises(IndexError, lambda: mi.one(it, too_short=IndexError))
-
-    def test_too_long(self):
-        it = count()
-        self.assertRaises(ValueError, lambda: mi.one(it))  # burn 0 and 1
-        self.assertEqual(next(it), 2)
-        self.assertRaises(
-            OverflowError, lambda: mi.one(it, too_long=OverflowError)
-        )
-
-
-class IntersperseTest(TestCase):
-    """ Tests for intersperse() """
-
-    def test_even(self):
-        iterable = (x for x in '01')
-        self.assertEqual(
-            list(mi.intersperse(None, iterable)), ['0', None, '1']
-        )
-
-    def test_odd(self):
-        iterable = (x for x in '012')
-        self.assertEqual(
-            list(mi.intersperse(None, iterable)), ['0', None, '1', None, '2']
-        )
-
-    def test_nested(self):
-        element = ('a', 'b')
-        iterable = (x for x in '012')
-        actual = list(mi.intersperse(element, iterable))
-        expected = ['0', ('a', 'b'), '1', ('a', 'b'), '2']
-        self.assertEqual(actual, expected)
-
-    def test_not_iterable(self):
-        self.assertRaises(TypeError, lambda: mi.intersperse('x', 1))
-
-    def test_n(self):
-        for n, element, expected in [
-            (1, '_', ['0', '_', '1', '_', '2', '_', '3', '_', '4', '_', '5']),
-            (2, '_', ['0', '1', '_', '2', '3', '_', '4', '5']),
-            (3, '_', ['0', '1', '2', '_', '3', '4', '5']),
-            (4, '_', ['0', '1', '2', '3', '_', '4', '5']),
-            (5, '_', ['0', '1', '2', '3', '4', '_', '5']),
-            (6, '_', ['0', '1', '2', '3', '4', '5']),
-            (7, '_', ['0', '1', '2', '3', '4', '5']),
-            (3, ['a', 'b'], ['0', '1', '2', ['a', 'b'], '3', '4', '5']),
-        ]:
-            iterable = (x for x in '012345')
-            actual = list(mi.intersperse(element, iterable, n=n))
-            self.assertEqual(actual, expected)
-
-    def test_n_zero(self):
-        self.assertRaises(
-            ValueError, lambda: list(mi.intersperse('x', '012', n=0))
-        )
-
-
-class UniqueToEachTests(TestCase):
-    """Tests for ``unique_to_each()``"""
-
-    def test_all_unique(self):
-        """When all the input iterables are unique the output should match
-        the input."""
-        iterables = [[1, 2], [3, 4, 5], [6, 7, 8]]
-        self.assertEqual(mi.unique_to_each(*iterables), iterables)
-
-    def test_duplicates(self):
-        """When there are duplicates in any of the input iterables that aren't
-        in the rest, those duplicates should be emitted."""
-        iterables = ["mississippi", "missouri"]
-        self.assertEqual(
-            mi.unique_to_each(*iterables), [['p', 'p'], ['o', 'u', 'r']]
-        )
-
-    def test_mixed(self):
-        """When the input iterables contain different types the function should
-        still behave properly"""
-        iterables = ['x', (i for i in range(3)), [1, 2, 3], tuple()]
-        self.assertEqual(mi.unique_to_each(*iterables), [['x'], [0], [3], []])
-
-
-class WindowedTests(TestCase):
-    """Tests for ``windowed()``"""
-
-    def test_basic(self):
-        actual = list(mi.windowed([1, 2, 3, 4, 5], 3))
-        expected = [(1, 2, 3), (2, 3, 4), (3, 4, 5)]
-        self.assertEqual(actual, expected)
-
-    def test_large_size(self):
-        """
-        When the window size is larger than the iterable, and no fill value is
-        given,``None`` should be filled in.
-        """
-        actual = list(mi.windowed([1, 2, 3, 4, 5], 6))
-        expected = [(1, 2, 3, 4, 5, None)]
-        self.assertEqual(actual, expected)
-
-    def test_fillvalue(self):
-        """
-        When sizes don't match evenly, the given fill value should be used.
-        """
-        iterable = [1, 2, 3, 4, 5]
-
-        for n, kwargs, expected in [
-            (6, {}, [(1, 2, 3, 4, 5, '!')]),  # n > len(iterable)
-            (3, {'step': 3}, [(1, 2, 3), (4, 5, '!')]),  # using ``step``
-        ]:
-            actual = list(mi.windowed(iterable, n, fillvalue='!', **kwargs))
-            self.assertEqual(actual, expected)
-
-    def test_zero(self):
-        """When the window size is zero, an empty tuple should be emitted."""
-        actual = list(mi.windowed([1, 2, 3, 4, 5], 0))
-        expected = [tuple()]
-        self.assertEqual(actual, expected)
-
-    def test_negative(self):
-        """When the window size is negative, ValueError should be raised."""
-        with self.assertRaises(ValueError):
-            list(mi.windowed([1, 2, 3, 4, 5], -1))
-
-    def test_step(self):
-        """The window should advance by the number of steps provided"""
-        iterable = [1, 2, 3, 4, 5, 6, 7]
-        for n, step, expected in [
-            (3, 2, [(1, 2, 3), (3, 4, 5), (5, 6, 7)]),  # n > step
-            (3, 3, [(1, 2, 3), (4, 5, 6), (7, None, None)]),  # n == step
-            (3, 4, [(1, 2, 3), (5, 6, 7)]),  # line up nicely
-            (3, 5, [(1, 2, 3), (6, 7, None)]),  # off by one
-            (3, 6, [(1, 2, 3), (7, None, None)]),  # off by two
-            (3, 7, [(1, 2, 3)]),  # step past the end
-            (7, 8, [(1, 2, 3, 4, 5, 6, 7)]),  # step > len(iterable)
-        ]:
-            actual = list(mi.windowed(iterable, n, step=step))
-            self.assertEqual(actual, expected)
-
-        # Step must be greater than or equal to 1
-        with self.assertRaises(ValueError):
-            list(mi.windowed(iterable, 3, step=0))
-
-
-class BucketTests(TestCase):
-    """Tests for ``bucket()``"""
-
-    def test_basic(self):
-        iterable = [10, 20, 30, 11, 21, 31, 12, 22, 23, 33]
-        D = mi.bucket(iterable, key=lambda x: 10 * (x // 10))
-
-        # In-order access
-        self.assertEqual(list(D[10]), [10, 11, 12])
-
-        # Out of order access
-        self.assertEqual(list(D[30]), [30, 31, 33])
-        self.assertEqual(list(D[20]), [20, 21, 22, 23])
-
-        self.assertEqual(list(D[40]), [])  # Nothing in here!
-
-    def test_in(self):
-        iterable = [10, 20, 30, 11, 21, 31, 12, 22, 23, 33]
-        D = mi.bucket(iterable, key=lambda x: 10 * (x // 10))
-
-        self.assertTrue(10 in D)
-        self.assertFalse(40 in D)
-        self.assertTrue(20 in D)
-        self.assertFalse(21 in D)
-
-        # Checking in-ness shouldn't advance the iterator
-        self.assertEqual(next(D[10]), 10)
-
-    def test_validator(self):
-        iterable = count(0)
-        key = lambda x: int(str(x)[0])  # First digit of each number
-        validator = lambda x: 0 < x < 10  # No leading zeros
-        D = mi.bucket(iterable, key, validator=validator)
-        self.assertEqual(mi.take(3, D[1]), [1, 10, 11])
-        self.assertNotIn(0, D)  # Non-valid entries don't return True
-        self.assertNotIn(0, D._cache)  # Don't store non-valid entries
-        self.assertEqual(list(D[0]), [])
-
-
-class SpyTests(TestCase):
-    """Tests for ``spy()``"""
-
-    def test_basic(self):
-        original_iterable = iter('abcdefg')
-        head, new_iterable = mi.spy(original_iterable)
-        self.assertEqual(head, ['a'])
-        self.assertEqual(
-            list(new_iterable), ['a', 'b', 'c', 'd', 'e', 'f', 'g']
-        )
-
-    def test_unpacking(self):
-        original_iterable = iter('abcdefg')
-        (first, second, third), new_iterable = mi.spy(original_iterable, 3)
-        self.assertEqual(first, 'a')
-        self.assertEqual(second, 'b')
-        self.assertEqual(third, 'c')
-        self.assertEqual(
-            list(new_iterable), ['a', 'b', 'c', 'd', 'e', 'f', 'g']
-        )
-
-    def test_too_many(self):
-        original_iterable = iter('abc')
-        head, new_iterable = mi.spy(original_iterable, 4)
-        self.assertEqual(head, ['a', 'b', 'c'])
-        self.assertEqual(list(new_iterable), ['a', 'b', 'c'])
-
-    def test_zero(self):
-        original_iterable = iter('abc')
-        head, new_iterable = mi.spy(original_iterable, 0)
-        self.assertEqual(head, [])
-        self.assertEqual(list(new_iterable), ['a', 'b', 'c'])
-
-
-class InterleaveTests(TestCase):
-    def test_even(self):
-        actual = list(mi.interleave([1, 4, 7], [2, 5, 8], [3, 6, 9]))
-        expected = [1, 2, 3, 4, 5, 6, 7, 8, 9]
-        self.assertEqual(actual, expected)
-
-    def test_short(self):
-        actual = list(mi.interleave([1, 4], [2, 5, 7], [3, 6, 8]))
-        expected = [1, 2, 3, 4, 5, 6]
-        self.assertEqual(actual, expected)
-
-    def test_mixed_types(self):
-        it_list = ['a', 'b', 'c', 'd']
-        it_str = '12345'
-        it_inf = count()
-        actual = list(mi.interleave(it_list, it_str, it_inf))
-        expected = ['a', '1', 0, 'b', '2', 1, 'c', '3', 2, 'd', '4', 3]
-        self.assertEqual(actual, expected)
-
-
-class InterleaveLongestTests(TestCase):
-    def test_even(self):
-        actual = list(mi.interleave_longest([1, 4, 7], [2, 5, 8], [3, 6, 9]))
-        expected = [1, 2, 3, 4, 5, 6, 7, 8, 9]
-        self.assertEqual(actual, expected)
-
-    def test_short(self):
-        actual = list(mi.interleave_longest([1, 4], [2, 5, 7], [3, 6, 8]))
-        expected = [1, 2, 3, 4, 5, 6, 7, 8]
-        self.assertEqual(actual, expected)
-
-    def test_mixed_types(self):
-        it_list = ['a', 'b', 'c', 'd']
-        it_str = '12345'
-        it_gen = (x for x in range(3))
-        actual = list(mi.interleave_longest(it_list, it_str, it_gen))
-        expected = ['a', '1', 0, 'b', '2', 1, 'c', '3', 2, 'd', '4', '5']
-        self.assertEqual(actual, expected)
-
-
-class TestCollapse(TestCase):
-    """Tests for ``collapse()``"""
-
-    def test_collapse(self):
-        l = [[1], 2, [[3], 4], [[[5]]]]
-        self.assertEqual(list(mi.collapse(l)), [1, 2, 3, 4, 5])
-
-    def test_collapse_to_string(self):
-        l = [["s1"], "s2", [["s3"], "s4"], [[["s5"]]]]
-        self.assertEqual(list(mi.collapse(l)), ["s1", "s2", "s3", "s4", "s5"])
-
-    def test_collapse_flatten(self):
-        l = [[1], [2], [[3], 4], [[[5]]]]
-        self.assertEqual(list(mi.collapse(l, levels=1)), list(mi.flatten(l)))
-
-    def test_collapse_to_level(self):
-        l = [[1], 2, [[3], 4], [[[5]]]]
-        self.assertEqual(list(mi.collapse(l, levels=2)), [1, 2, 3, 4, [5]])
-        self.assertEqual(
-            list(mi.collapse(mi.collapse(l, levels=1), levels=1)),
-            list(mi.collapse(l, levels=2))
-        )
-
-    def test_collapse_to_list(self):
-        l = (1, [2], (3, [4, (5,)], 'ab'))
-        actual = list(mi.collapse(l, base_type=list))
-        expected = [1, [2], 3, [4, (5,)], 'ab']
-        self.assertEqual(actual, expected)
-
-
-class SideEffectTests(TestCase):
-    """Tests for ``side_effect()``"""
-
-    def test_individual(self):
-        # The function increments the counter for each call
-        counter = [0]
-
-        def func(arg):
-            counter[0] += 1
-
-        result = list(mi.side_effect(func, range(10)))
-        self.assertEqual(result, list(range(10)))
-        self.assertEqual(counter[0], 10)
-
-    def test_chunked(self):
-        # The function increments the counter for each call
-        counter = [0]
-
-        def func(arg):
-            counter[0] += 1
-
-        result = list(mi.side_effect(func, range(10), 2))
-        self.assertEqual(result, list(range(10)))
-        self.assertEqual(counter[0], 5)
-
-    def test_before_after(self):
-        f = StringIO()
-        collector = []
-
-        def func(item):
-            print(item, file=f)
-            collector.append(f.getvalue())
-
-        def it():
-            yield u'a'
-            yield u'b'
-            raise RuntimeError('kaboom')
-
-        before = lambda: print('HEADER', file=f)
-        after = f.close
-
-        try:
-            mi.consume(mi.side_effect(func, it(), before=before, after=after))
-        except RuntimeError:
-            pass
-
-        # The iterable should have been written to the file
-        self.assertEqual(collector, [u'HEADER\na\n', u'HEADER\na\nb\n'])
-
-        # The file should be closed even though something bad happened
-        self.assertTrue(f.closed)
-
-    def test_before_fails(self):
-        f = StringIO()
-        func = lambda x: print(x, file=f)
-
-        def before():
-            raise RuntimeError('ouch')
-
-        try:
-            mi.consume(
-                mi.side_effect(func, u'abc', before=before, after=f.close)
-            )
-        except RuntimeError:
-            pass
-
-        # The file should be closed even though something bad happened in the
-        # before function
-        self.assertTrue(f.closed)
-
-
-class SlicedTests(TestCase):
-    """Tests for ``sliced()``"""
-
-    def test_even(self):
-        """Test when the length of the sequence is divisible by *n*"""
-        seq = 'ABCDEFGHI'
-        self.assertEqual(list(mi.sliced(seq, 3)), ['ABC', 'DEF', 'GHI'])
-
-    def test_odd(self):
-        """Test when the length of the sequence is not divisible by *n*"""
-        seq = 'ABCDEFGHI'
-        self.assertEqual(list(mi.sliced(seq, 4)), ['ABCD', 'EFGH', 'I'])
-
-    def test_not_sliceable(self):
-        seq = (x for x in 'ABCDEFGHI')
-
-        with self.assertRaises(TypeError):
-            list(mi.sliced(seq, 3))
-
-
-class SplitAtTests(TestCase):
-    """Tests for ``split()``"""
-
-    def comp_with_str_split(self, str_to_split, delim):
-        pred = lambda c: c == delim
-        actual = list(map(''.join, mi.split_at(str_to_split, pred)))
-        expected = str_to_split.split(delim)
-        self.assertEqual(actual, expected)
-
-    def test_seperators(self):
-        test_strs = ['', 'abcba', 'aaabbbcccddd', 'e']
-        for s, delim in product(test_strs, 'abcd'):
-            self.comp_with_str_split(s, delim)
-
-
-class SplitBeforeTest(TestCase):
-    """Tests for ``split_before()``"""
-
-    def test_starts_with_sep(self):
-        actual = list(mi.split_before('xooxoo', lambda c: c == 'x'))
-        expected = [['x', 'o', 'o'], ['x', 'o', 'o']]
-        self.assertEqual(actual, expected)
-
-    def test_ends_with_sep(self):
-        actual = list(mi.split_before('ooxoox', lambda c: c == 'x'))
-        expected = [['o', 'o'], ['x', 'o', 'o'], ['x']]
-        self.assertEqual(actual, expected)
-
-    def test_no_sep(self):
-        actual = list(mi.split_before('ooo', lambda c: c == 'x'))
-        expected = [['o', 'o', 'o']]
-        self.assertEqual(actual, expected)
-
-
-class SplitAfterTest(TestCase):
-    """Tests for ``split_after()``"""
-
-    def test_starts_with_sep(self):
-        actual = list(mi.split_after('xooxoo', lambda c: c == 'x'))
-        expected = [['x'], ['o', 'o', 'x'], ['o', 'o']]
-        self.assertEqual(actual, expected)
-
-    def test_ends_with_sep(self):
-        actual = list(mi.split_after('ooxoox', lambda c: c == 'x'))
-        expected = [['o', 'o', 'x'], ['o', 'o', 'x']]
-        self.assertEqual(actual, expected)
-
-    def test_no_sep(self):
-        actual = list(mi.split_after('ooo', lambda c: c == 'x'))
-        expected = [['o', 'o', 'o']]
-        self.assertEqual(actual, expected)
-
-
-class PaddedTest(TestCase):
-    """Tests for ``padded()``"""
-
-    def test_no_n(self):
-        seq = [1, 2, 3]
-
-        # No fillvalue
-        self.assertEqual(mi.take(5, mi.padded(seq)), [1, 2, 3, None, None])
-
-        # With fillvalue
-        self.assertEqual(
-            mi.take(5, mi.padded(seq, fillvalue='')), [1, 2, 3, '', '']
-        )
-
-    def test_invalid_n(self):
-        self.assertRaises(ValueError, lambda: list(mi.padded([1, 2, 3], n=-1)))
-        self.assertRaises(ValueError, lambda: list(mi.padded([1, 2, 3], n=0)))
-
-    def test_valid_n(self):
-        seq = [1, 2, 3, 4, 5]
-
-        # No need for padding: len(seq) <= n
-        self.assertEqual(list(mi.padded(seq, n=4)), [1, 2, 3, 4, 5])
-        self.assertEqual(list(mi.padded(seq, n=5)), [1, 2, 3, 4, 5])
-
-        # No fillvalue
-        self.assertEqual(
-            list(mi.padded(seq, n=7)), [1, 2, 3, 4, 5, None, None]
-        )
-
-        # With fillvalue
-        self.assertEqual(
-            list(mi.padded(seq, fillvalue='', n=7)), [1, 2, 3, 4, 5, '', '']
-        )
-
-    def test_next_multiple(self):
-        seq = [1, 2, 3, 4, 5, 6]
-
-        # No need for padding: len(seq) % n == 0
-        self.assertEqual(
-            list(mi.padded(seq, n=3, next_multiple=True)), [1, 2, 3, 4, 5, 6]
-        )
-
-        # Padding needed: len(seq) < n
-        self.assertEqual(
-            list(mi.padded(seq, n=8, next_multiple=True)),
-            [1, 2, 3, 4, 5, 6, None, None]
-        )
-
-        # No padding needed: len(seq) == n
-        self.assertEqual(
-            list(mi.padded(seq, n=6, next_multiple=True)), [1, 2, 3, 4, 5, 6]
-        )
-
-        # Padding needed: len(seq) > n
-        self.assertEqual(
-            list(mi.padded(seq, n=4, next_multiple=True)),
-            [1, 2, 3, 4, 5, 6, None, None]
-        )
-
-        # With fillvalue
-        self.assertEqual(
-            list(mi.padded(seq, fillvalue='', n=4, next_multiple=True)),
-            [1, 2, 3, 4, 5, 6, '', '']
-        )
-
-
-class DistributeTest(TestCase):
-    """Tests for distribute()"""
-
-    def test_invalid_n(self):
-        self.assertRaises(ValueError, lambda: mi.distribute(-1, [1, 2, 3]))
-        self.assertRaises(ValueError, lambda: mi.distribute(0, [1, 2, 3]))
-
-    def test_basic(self):
-        iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-
-        for n, expected in [
-            (1, [iterable]),
-            (2, [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]),
-            (3, [[1, 4, 7, 10], [2, 5, 8], [3, 6, 9]]),
-            (10, [[n] for n in range(1, 10 + 1)]),
-        ]:
-            self.assertEqual(
-                [list(x) for x in mi.distribute(n, iterable)], expected
-            )
-
-    def test_large_n(self):
-        iterable = [1, 2, 3, 4]
-        self.assertEqual(
-            [list(x) for x in mi.distribute(6, iterable)],
-            [[1], [2], [3], [4], [], []]
-        )
-
-
-class StaggerTest(TestCase):
-    """Tests for ``stagger()``"""
-
-    def test_default(self):
-        iterable = [0, 1, 2, 3]
-        actual = list(mi.stagger(iterable))
-        expected = [(None, 0, 1), (0, 1, 2), (1, 2, 3)]
-        self.assertEqual(actual, expected)
-
-    def test_offsets(self):
-        iterable = [0, 1, 2, 3]
-        for offsets, expected in [
-            ((-2, 0, 2), [('', 0, 2), ('', 1, 3)]),
-            ((-2, -1), [('', ''), ('', 0), (0, 1), (1, 2), (2, 3)]),
-            ((1, 2), [(1, 2), (2, 3)]),
-        ]:
-            all_groups = mi.stagger(iterable, offsets=offsets, fillvalue='')
-            self.assertEqual(list(all_groups), expected)
-
-    def test_longest(self):
-        iterable = [0, 1, 2, 3]
-        for offsets, expected in [
-            (
-                (-1, 0, 1),
-                [('', 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, ''), (3, '', '')]
-            ),
-            ((-2, -1), [('', ''), ('', 0), (0, 1), (1, 2), (2, 3), (3, '')]),
-            ((1, 2), [(1, 2), (2, 3), (3, '')]),
-        ]:
-            all_groups = mi.stagger(
-                iterable, offsets=offsets, fillvalue='', longest=True
-            )
-            self.assertEqual(list(all_groups), expected)
-
-
-class ZipOffsetTest(TestCase):
-    """Tests for ``zip_offset()``"""
-
-    def test_shortest(self):
-        a_1 = [0, 1, 2, 3]
-        a_2 = [0, 1, 2, 3, 4, 5]
-        a_3 = [0, 1, 2, 3, 4, 5, 6, 7]
-        actual = list(
-            mi.zip_offset(a_1, a_2, a_3, offsets=(-1, 0, 1), fillvalue='')
-        )
-        expected = [('', 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5)]
-        self.assertEqual(actual, expected)
-
-    def test_longest(self):
-        a_1 = [0, 1, 2, 3]
-        a_2 = [0, 1, 2, 3, 4, 5]
-        a_3 = [0, 1, 2, 3, 4, 5, 6, 7]
-        actual = list(
-            mi.zip_offset(a_1, a_2, a_3, offsets=(-1, 0, 1), longest=True)
-        )
-        expected = [
-            (None, 0, 1),
-            (0, 1, 2),
-            (1, 2, 3),
-            (2, 3, 4),
-            (3, 4, 5),
-            (None, 5, 6),
-            (None, None, 7),
-        ]
-        self.assertEqual(actual, expected)
-
-    def test_mismatch(self):
-        iterables = [0, 1, 2], [2, 3, 4]
-        offsets = (-1, 0, 1)
-        self.assertRaises(
-            ValueError,
-            lambda: list(mi.zip_offset(*iterables, offsets=offsets))
-        )
-
-
-class SortTogetherTest(TestCase):
-    """Tests for sort_together()"""
-
-    def test_key_list(self):
-        """tests `key_list` including default, iterables include duplicates"""
-        iterables = [
-            ['GA', 'GA', 'GA', 'CT', 'CT', 'CT'],
-            ['May', 'Aug.', 'May', 'June', 'July', 'July'],
-            [97, 20, 100, 70, 100, 20]
-        ]
-
-        self.assertEqual(
-            mi.sort_together(iterables),
-            [
-                ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'),
-                ('June', 'July', 'July', 'May', 'Aug.', 'May'),
-                (70, 100, 20, 97, 20, 100)
-            ]
-        )
-
-        self.assertEqual(
-            mi.sort_together(iterables, key_list=(0, 1)),
-            [
-                ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'),
-                ('July', 'July', 'June', 'Aug.', 'May', 'May'),
-                (100, 20, 70, 20, 97, 100)
-            ]
-        )
-
-        self.assertEqual(
-            mi.sort_together(iterables, key_list=(0, 1, 2)),
-            [
-                ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'),
-                ('July', 'July', 'June', 'Aug.', 'May', 'May'),
-                (20, 100, 70, 20, 97, 100)
-            ]
-        )
-
-        self.assertEqual(
-            mi.sort_together(iterables, key_list=(2,)),
-            [
-                ('GA', 'CT', 'CT', 'GA', 'GA', 'CT'),
-                ('Aug.', 'July', 'June', 'May', 'May', 'July'),
-                (20, 20, 70, 97, 100, 100)
-            ]
-        )
-
-    def test_invalid_key_list(self):
-        """tests `key_list` for indexes not available in `iterables`"""
-        iterables = [
-            ['GA', 'GA', 'GA', 'CT', 'CT', 'CT'],
-            ['May', 'Aug.', 'May', 'June', 'July', 'July'],
-            [97, 20, 100, 70, 100, 20]
-        ]
-
-        self.assertRaises(
-            IndexError, lambda: mi.sort_together(iterables, key_list=(5,))
-        )
-
-    def test_reverse(self):
-        """tests `reverse` to ensure a reverse sort for `key_list` iterables"""
-        iterables = [
-            ['GA', 'GA', 'GA', 'CT', 'CT', 'CT'],
-            ['May', 'Aug.', 'May', 'June', 'July', 'July'],
-            [97, 20, 100, 70, 100, 20]
-        ]
-
-        self.assertEqual(
-            mi.sort_together(iterables, key_list=(0, 1, 2), reverse=True),
-            [('GA', 'GA', 'GA', 'CT', 'CT', 'CT'),
-             ('May', 'May', 'Aug.', 'June', 'July', 'July'),
-             (100, 97, 20, 70, 100, 20)]
-        )
-
-    def test_uneven_iterables(self):
-        """tests trimming of iterables to the shortest length before sorting"""
-        iterables = [['GA', 'GA', 'GA', 'CT', 'CT', 'CT', 'MA'],
-                     ['May', 'Aug.', 'May', 'June', 'July', 'July'],
-                     [97, 20, 100, 70, 100, 20, 0]]
-
-        self.assertEqual(
-            mi.sort_together(iterables),
-            [
-                ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'),
-                ('June', 'July', 'July', 'May', 'Aug.', 'May'),
-                (70, 100, 20, 97, 20, 100)
-            ]
-        )
-
-
-class DivideTest(TestCase):
-    """Tests for divide()"""
-
-    def test_invalid_n(self):
-        self.assertRaises(ValueError, lambda: mi.divide(-1, [1, 2, 3]))
-        self.assertRaises(ValueError, lambda: mi.divide(0, [1, 2, 3]))
-
-    def test_basic(self):
-        iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-
-        for n, expected in [
-            (1, [iterable]),
-            (2, [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]),
-            (3, [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]),
-            (10, [[n] for n in range(1, 10 + 1)]),
-        ]:
-            self.assertEqual(
-                [list(x) for x in mi.divide(n, iterable)], expected
-            )
-
-    def test_large_n(self):
-        iterable = [1, 2, 3, 4]
-        self.assertEqual(
-            [list(x) for x in mi.divide(6, iterable)],
-            [[1], [2], [3], [4], [], []]
-        )
-
-
-class TestAlwaysIterable(TestCase):
-    """Tests for always_iterable()"""
-    def test_single(self):
-        self.assertEqual(list(mi.always_iterable(1)), [1])
-
-    def test_strings(self):
-        for obj in ['foo', b'bar', u'baz']:
-            actual = list(mi.always_iterable(obj))
-            expected = [obj]
-            self.assertEqual(actual, expected)
-
-    def test_base_type(self):
-        dict_obj = {'a': 1, 'b': 2}
-        str_obj = '123'
-
-        # Default: dicts are iterable like they normally are
-        default_actual = list(mi.always_iterable(dict_obj))
-        default_expected = list(dict_obj)
-        self.assertEqual(default_actual, default_expected)
-
-        # Unitary types set: dicts are not iterable
-        custom_actual = list(mi.always_iterable(dict_obj, base_type=dict))
-        custom_expected = [dict_obj]
-        self.assertEqual(custom_actual, custom_expected)
-
-        # With unitary types set, strings are iterable
-        str_actual = list(mi.always_iterable(str_obj, base_type=None))
-        str_expected = list(str_obj)
-        self.assertEqual(str_actual, str_expected)
-
-    def test_iterables(self):
-        self.assertEqual(list(mi.always_iterable([0, 1])), [0, 1])
-        self.assertEqual(
-            list(mi.always_iterable([0, 1], base_type=list)), [[0, 1]]
-        )
-        self.assertEqual(
-            list(mi.always_iterable(iter('foo'))), ['f', 'o', 'o']
-        )
-        self.assertEqual(list(mi.always_iterable([])), [])
-
-    def test_none(self):
-        self.assertEqual(list(mi.always_iterable(None)), [])
-
-    def test_generator(self):
-        def _gen():
-            yield 0
-            yield 1
-
-        self.assertEqual(list(mi.always_iterable(_gen())), [0, 1])
-
-
-class AdjacentTests(TestCase):
-    def test_typical(self):
-        actual = list(mi.adjacent(lambda x: x % 5 == 0, range(10)))
-        expected = [(True, 0), (True, 1), (False, 2), (False, 3), (True, 4),
-                    (True, 5), (True, 6), (False, 7), (False, 8), (False, 9)]
-        self.assertEqual(actual, expected)
-
-    def test_empty_iterable(self):
-        actual = list(mi.adjacent(lambda x: x % 5 == 0, []))
-        expected = []
-        self.assertEqual(actual, expected)
-
-    def test_length_one(self):
-        actual = list(mi.adjacent(lambda x: x % 5 == 0, [0]))
-        expected = [(True, 0)]
-        self.assertEqual(actual, expected)
-
-        actual = list(mi.adjacent(lambda x: x % 5 == 0, [1]))
-        expected = [(False, 1)]
-        self.assertEqual(actual, expected)
-
-    def test_consecutive_true(self):
-        """Test that when the predicate matches multiple consecutive elements
-        it doesn't repeat elements in the output"""
-        actual = list(mi.adjacent(lambda x: x % 5 < 2, range(10)))
-        expected = [(True, 0), (True, 1), (True, 2), (False, 3), (True, 4),
-                    (True, 5), (True, 6), (True, 7), (False, 8), (False, 9)]
-        self.assertEqual(actual, expected)
-
-    def test_distance(self):
-        actual = list(mi.adjacent(lambda x: x % 5 == 0, range(10), distance=2))
-        expected = [(True, 0), (True, 1), (True, 2), (True, 3), (True, 4),
-                    (True, 5), (True, 6), (True, 7), (False, 8), (False, 9)]
-        self.assertEqual(actual, expected)
-
-        actual = list(mi.adjacent(lambda x: x % 5 == 0, range(10), distance=3))
-        expected = [(True, 0), (True, 1), (True, 2), (True, 3), (True, 4),
-                    (True, 5), (True, 6), (True, 7), (True, 8), (False, 9)]
-        self.assertEqual(actual, expected)
-
-    def test_large_distance(self):
-        """Test distance larger than the length of the iterable"""
-        iterable = range(10)
-        actual = list(mi.adjacent(lambda x: x % 5 == 4, iterable, distance=20))
-        expected = list(zip(repeat(True), iterable))
-        self.assertEqual(actual, expected)
-
-        actual = list(mi.adjacent(lambda x: False, iterable, distance=20))
-        expected = list(zip(repeat(False), iterable))
-        self.assertEqual(actual, expected)
-
-    def test_zero_distance(self):
-        """Test that adjacent() reduces to zip+map when distance is 0"""
-        iterable = range(1000)
-        predicate = lambda x: x % 4 == 2
-        actual = mi.adjacent(predicate, iterable, 0)
-        expected = zip(map(predicate, iterable), iterable)
-        self.assertTrue(all(a == e for a, e in zip(actual, expected)))
-
-    def test_negative_distance(self):
-        """Test that adjacent() raises an error with negative distance"""
-        pred = lambda x: x
-        self.assertRaises(
-            ValueError, lambda: mi.adjacent(pred, range(1000), -1)
-        )
-        self.assertRaises(
-            ValueError, lambda: mi.adjacent(pred, range(10), -10)
-        )
-
-    def test_grouping(self):
-        """Test interaction of adjacent() with groupby_transform()"""
-        iterable = mi.adjacent(lambda x: x % 5 == 0, range(10))
-        grouper = mi.groupby_transform(iterable, itemgetter(0), itemgetter(1))
-        actual = [(k, list(g)) for k, g in grouper]
-        expected = [
-            (True, [0, 1]),
-            (False, [2, 3]),
-            (True, [4, 5, 6]),
-            (False, [7, 8, 9]),
-        ]
-        self.assertEqual(actual, expected)
-
-    def test_call_once(self):
-        """Test that the predicate is only called once per item."""
-        already_seen = set()
-        iterable = range(10)
-
-        def predicate(item):
-            self.assertNotIn(item, already_seen)
-            already_seen.add(item)
-            return True
-
-        actual = list(mi.adjacent(predicate, iterable))
-        expected = [(True, x) for x in iterable]
-        self.assertEqual(actual, expected)
-
-
-class GroupByTransformTests(TestCase):
-    def assertAllGroupsEqual(self, groupby1, groupby2):
-        """Compare two groupby objects for equality, both keys and groups."""
-        for a, b in zip(groupby1, groupby2):
-            key1, group1 = a
-            key2, group2 = b
-            self.assertEqual(key1, key2)
-            self.assertListEqual(list(group1), list(group2))
-        self.assertRaises(StopIteration, lambda: next(groupby1))
-        self.assertRaises(StopIteration, lambda: next(groupby2))
-
-    def test_default_funcs(self):
-        """Test that groupby_transform() with default args mimics groupby()"""
-        iterable = [(x // 5, x) for x in range(1000)]
-        actual = mi.groupby_transform(iterable)
-        expected = groupby(iterable)
-        self.assertAllGroupsEqual(actual, expected)
-
-    def test_valuefunc(self):
-        iterable = [(int(x / 5), int(x / 3), x) for x in range(10)]
-
-        # Test the standard usage of grouping one iterable using another's keys
-        grouper = mi.groupby_transform(
-            iterable, keyfunc=itemgetter(0), valuefunc=itemgetter(-1)
-        )
-        actual = [(k, list(g)) for k, g in grouper]
-        expected = [(0, [0, 1, 2, 3, 4]), (1, [5, 6, 7, 8, 9])]
-        self.assertEqual(actual, expected)
-
-        grouper = mi.groupby_transform(
-            iterable, keyfunc=itemgetter(1), valuefunc=itemgetter(-1)
-        )
-        actual = [(k, list(g)) for k, g in grouper]
-        expected = [(0, [0, 1, 2]), (1, [3, 4, 5]), (2, [6, 7, 8]), (3, [9])]
-        self.assertEqual(actual, expected)
-
-        # and now for something a little different
-        d = dict(zip(range(10), 'abcdefghij'))
-        grouper = mi.groupby_transform(
-            range(10), keyfunc=lambda x: x // 5, valuefunc=d.get
-        )
-        actual = [(k, ''.join(g)) for k, g in grouper]
-        expected = [(0, 'abcde'), (1, 'fghij')]
-        self.assertEqual(actual, expected)
-
-    def test_no_valuefunc(self):
-        iterable = range(1000)
-
-        def key(x):
-            return x // 5
-
-        actual = mi.groupby_transform(iterable, key, valuefunc=None)
-        expected = groupby(iterable, key)
-        self.assertAllGroupsEqual(actual, expected)
-
-        actual = mi.groupby_transform(iterable, key)  # default valuefunc
-        expected = groupby(iterable, key)
-        self.assertAllGroupsEqual(actual, expected)
-
-
-class NumericRangeTests(TestCase):
-    def test_basic(self):
-        for args, expected in [
-            ((4,), [0, 1, 2, 3]),
-            ((4.0,), [0.0, 1.0, 2.0, 3.0]),
-            ((1.0, 4), [1.0, 2.0, 3.0]),
-            ((1, 4.0), [1, 2, 3]),
-            ((1.0, 5), [1.0, 2.0, 3.0, 4.0]),
-            ((0, 20, 5), [0, 5, 10, 15]),
-            ((0, 20, 5.0), [0.0, 5.0, 10.0, 15.0]),
-            ((0, 10, 3), [0, 3, 6, 9]),
-            ((0, 10, 3.0), [0.0, 3.0, 6.0, 9.0]),
-            ((0, -5, -1), [0, -1, -2, -3, -4]),
-            ((0.0, -5, -1), [0.0, -1.0, -2.0, -3.0, -4.0]),
-            ((1, 2, Fraction(1, 2)), [Fraction(1, 1), Fraction(3, 2)]),
-            ((0,), []),
-            ((0.0,), []),
-            ((1, 0), []),
-            ((1.0, 0.0), []),
-            ((Fraction(2, 1),), [Fraction(0, 1), Fraction(1, 1)]),
-            ((Decimal('2.0'),), [Decimal('0.0'), Decimal('1.0')]),
-        ]:
-            actual = list(mi.numeric_range(*args))
-            self.assertEqual(actual, expected)
-            self.assertTrue(
-                all(type(a) == type(e) for a, e in zip(actual, expected))
-            )
-
-    def test_arg_count(self):
-        self.assertRaises(TypeError, lambda: list(mi.numeric_range()))
-        self.assertRaises(
-            TypeError, lambda: list(mi.numeric_range(0, 1, 2, 3))
-        )
-
-    def test_zero_step(self):
-        self.assertRaises(
-            ValueError, lambda: list(mi.numeric_range(1, 2, 0))
-        )
-
-
-class CountCycleTests(TestCase):
-    def test_basic(self):
-        expected = [
-            (0, 'a'), (0, 'b'), (0, 'c'),
-            (1, 'a'), (1, 'b'), (1, 'c'),
-            (2, 'a'), (2, 'b'), (2, 'c'),
-        ]
-        for actual in [
-            mi.take(9, mi.count_cycle('abc')),  # n=None
-            list(mi.count_cycle('abc', 3)),  # n=3
-        ]:
-            self.assertEqual(actual, expected)
-
-    def test_empty(self):
-        self.assertEqual(list(mi.count_cycle('')), [])
-        self.assertEqual(list(mi.count_cycle('', 2)), [])
-
-    def test_negative(self):
-        self.assertEqual(list(mi.count_cycle('abc', -3)), [])
-
-
-class LocateTests(TestCase):
-    def test_default_pred(self):
-        iterable = [0, 1, 1, 0, 1, 0, 0]
-        actual = list(mi.locate(iterable))
-        expected = [1, 2, 4]
-        self.assertEqual(actual, expected)
-
-    def test_no_matches(self):
-        iterable = [0, 0, 0]
-        actual = list(mi.locate(iterable))
-        expected = []
-        self.assertEqual(actual, expected)
-
-    def test_custom_pred(self):
-        iterable = ['0', 1, 1, '0', 1, '0', '0']
-        pred = lambda x: x == '0'
-        actual = list(mi.locate(iterable, pred))
-        expected = [0, 3, 5, 6]
-        self.assertEqual(actual, expected)
-
-    def test_window_size(self):
-        iterable = ['0', 1, 1, '0', 1, '0', '0']
-        pred = lambda *args: args == ('0', 1)
-        actual = list(mi.locate(iterable, pred, window_size=2))
-        expected = [0, 3]
-        self.assertEqual(actual, expected)
-
-    def test_window_size_large(self):
-        iterable = [1, 2, 3, 4]
-        pred = lambda a, b, c, d, e: True
-        actual = list(mi.locate(iterable, pred, window_size=5))
-        expected = [0]
-        self.assertEqual(actual, expected)
-
-    def test_window_size_zero(self):
-        iterable = [1, 2, 3, 4]
-        pred = lambda: True
-        with self.assertRaises(ValueError):
-            list(mi.locate(iterable, pred, window_size=0))
-
-
-class StripFunctionTests(TestCase):
-    def test_hashable(self):
-        iterable = list('www.example.com')
-        pred = lambda x: x in set('cmowz.')
-
-        self.assertEqual(list(mi.lstrip(iterable, pred)), list('example.com'))
-        self.assertEqual(list(mi.rstrip(iterable, pred)), list('www.example'))
-        self.assertEqual(list(mi.strip(iterable, pred)), list('example'))
-
-    def test_not_hashable(self):
-        iterable = [
-            list('http://'), list('www'), list('.example'), list('.com')
-        ]
-        pred = lambda x: x in [list('http://'), list('www'), list('.com')]
-
-        self.assertEqual(list(mi.lstrip(iterable, pred)), iterable[2:])
-        self.assertEqual(list(mi.rstrip(iterable, pred)), iterable[:3])
-        self.assertEqual(list(mi.strip(iterable, pred)), iterable[2: 3])
-
-    def test_math(self):
-        iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2]
-        pred = lambda x: x <= 2
-
-        self.assertEqual(list(mi.lstrip(iterable, pred)), iterable[3:])
-        self.assertEqual(list(mi.rstrip(iterable, pred)), iterable[:-3])
-        self.assertEqual(list(mi.strip(iterable, pred)), iterable[3:-3])
-
-
-class IsliceExtendedTests(TestCase):
-    def test_all(self):
-        iterable = ['0', '1', '2', '3', '4', '5']
-        indexes = list(range(-4, len(iterable) + 4)) + [None]
-        steps = [1, 2, 3, 4, -1, -2, -3, 4]
-        for slice_args in product(indexes, indexes, steps):
-            try:
-                actual = list(mi.islice_extended(iterable, *slice_args))
-            except Exception as e:
-                self.fail((slice_args, e))
-
-            expected = iterable[slice(*slice_args)]
-            self.assertEqual(actual, expected, slice_args)
-
-    def test_zero_step(self):
-        with self.assertRaises(ValueError):
-            list(mi.islice_extended([1, 2, 3], 0, 1, 0))
-
-
-class ConsecutiveGroupsTest(TestCase):
-    def test_numbers(self):
-        iterable = [-10, -8, -7, -6, 1, 2, 4, 5, -1, 7]
-        actual = [list(g) for g in mi.consecutive_groups(iterable)]
-        expected = [[-10], [-8, -7, -6], [1, 2], [4, 5], [-1], [7]]
-        self.assertEqual(actual, expected)
-
-    def test_custom_ordering(self):
-        iterable = ['1', '10', '11', '20', '21', '22', '30', '31']
-        ordering = lambda x: int(x)
-        actual = [list(g) for g in mi.consecutive_groups(iterable, ordering)]
-        expected = [['1'], ['10', '11'], ['20', '21', '22'], ['30', '31']]
-        self.assertEqual(actual, expected)
-
-    def test_exotic_ordering(self):
-        iterable = [
-            ('a', 'b', 'c', 'd'),
-            ('a', 'c', 'b', 'd'),
-            ('a', 'c', 'd', 'b'),
-            ('a', 'd', 'b', 'c'),
-            ('d', 'b', 'c', 'a'),
-            ('d', 'c', 'a', 'b'),
-        ]
-        ordering = list(permutations('abcd')).index
-        actual = [list(g) for g in mi.consecutive_groups(iterable, ordering)]
-        expected = [
-            [('a', 'b', 'c', 'd')],
-            [('a', 'c', 'b', 'd'), ('a', 'c', 'd', 'b'), ('a', 'd', 'b', 'c')],
-            [('d', 'b', 'c', 'a'), ('d', 'c', 'a', 'b')],
-        ]
-        self.assertEqual(actual, expected)
-
-
-class DifferenceTest(TestCase):
-    def test_normal(self):
-        iterable = [10, 20, 30, 40, 50]
-        actual = list(mi.difference(iterable))
-        expected = [10, 10, 10, 10, 10]
-        self.assertEqual(actual, expected)
-
-    def test_custom(self):
-        iterable = [10, 20, 30, 40, 50]
-        actual = list(mi.difference(iterable, add))
-        expected = [10, 30, 50, 70, 90]
-        self.assertEqual(actual, expected)
-
-    def test_roundtrip(self):
-        original = list(range(100))
-        accumulated = mi.accumulate(original)
-        actual = list(mi.difference(accumulated))
-        self.assertEqual(actual, original)
-
-    def test_one(self):
-        self.assertEqual(list(mi.difference([0])), [0])
-
-    def test_empty(self):
-        self.assertEqual(list(mi.difference([])), [])
-
-
-class SeekableTest(TestCase):
-    def test_exhaustion_reset(self):
-        iterable = [str(n) for n in range(10)]
-
-        s = mi.seekable(iterable)
-        self.assertEqual(list(s), iterable)  # Normal iteration
-        self.assertEqual(list(s), [])  # Iterable is exhausted
-
-        s.seek(0)
-        self.assertEqual(list(s), iterable)  # Back in action
-
-    def test_partial_reset(self):
-        iterable = [str(n) for n in range(10)]
-
-        s = mi.seekable(iterable)
-        self.assertEqual(mi.take(5, s), iterable[:5])  # Normal iteration
-
-        s.seek(1)
-        self.assertEqual(list(s), iterable[1:])  # Get the rest of the iterable
-
-    def test_forward(self):
-        iterable = [str(n) for n in range(10)]
-
-        s = mi.seekable(iterable)
-        self.assertEqual(mi.take(1, s), iterable[:1])  # Normal iteration
-
-        s.seek(3)  # Skip over index 2
-        self.assertEqual(list(s), iterable[3:])  # Result is similar to slicing
-
-        s.seek(0)  # Back to 0
-        self.assertEqual(list(s), iterable)  # No difference in result
-
-    def test_past_end(self):
-        iterable = [str(n) for n in range(10)]
-
-        s = mi.seekable(iterable)
-        self.assertEqual(mi.take(1, s), iterable[:1])  # Normal iteration
-
-        s.seek(20)
-        self.assertEqual(list(s), [])  # Iterable is exhausted
-
-        s.seek(0)  # Back to 0
-        self.assertEqual(list(s), iterable)  # No difference in result
-
-    def test_elements(self):
-        iterable = map(str, count())
-
-        s = mi.seekable(iterable)
-        mi.take(10, s)
-
-        elements = s.elements()
-        self.assertEqual(
-            [elements[i] for i in range(10)], [str(n) for n in range(10)]
-        )
-        self.assertEqual(len(elements), 10)
-
-        mi.take(10, s)
-        self.assertEqual(list(elements), [str(n) for n in range(20)])
-
-
-class SequenceViewTests(TestCase):
-    def test_init(self):
-        view = mi.SequenceView((1, 2, 3))
-        self.assertEqual(repr(view), "SequenceView((1, 2, 3))")
-        self.assertRaises(TypeError, lambda: mi.SequenceView({}))
-
-    def test_update(self):
-        seq = [1, 2, 3]
-        view = mi.SequenceView(seq)
-        self.assertEqual(len(view), 3)
-        self.assertEqual(repr(view), "SequenceView([1, 2, 3])")
-
-        seq.pop()
-        self.assertEqual(len(view), 2)
-        self.assertEqual(repr(view), "SequenceView([1, 2])")
-
-    def test_indexing(self):
-        seq = ('a', 'b', 'c', 'd', 'e', 'f')
-        view = mi.SequenceView(seq)
-        for i in range(-len(seq), len(seq)):
-            self.assertEqual(view[i], seq[i])
-
-    def test_slicing(self):
-        seq = ('a', 'b', 'c', 'd', 'e', 'f')
-        view = mi.SequenceView(seq)
-        n = len(seq)
-        indexes = list(range(-n - 1, n + 1)) + [None]
-        steps = list(range(-n, n + 1))
-        steps.remove(0)
-        for slice_args in product(indexes, indexes, steps):
-            i = slice(*slice_args)
-            self.assertEqual(view[i], seq[i])
-
-    def test_abc_methods(self):
-        # collections.Sequence should provide all of this functionality
-        seq = ('a', 'b', 'c', 'd', 'e', 'f', 'f')
-        view = mi.SequenceView(seq)
-
-        # __contains__
-        self.assertIn('b', view)
-        self.assertNotIn('g', view)
-
-        # __iter__
-        self.assertEqual(list(iter(view)), list(seq))
-
-        # __reversed__
-        self.assertEqual(list(reversed(view)), list(reversed(seq)))
-
-        # index
-        self.assertEqual(view.index('b'), 1)
-
-        # count
-        self.assertEqual(seq.count('f'), 2)
-
-
-class RunLengthTest(TestCase):
-    def test_encode(self):
-        iterable = (int(str(n)[0]) for n in count(800))
-        actual = mi.take(4, mi.run_length.encode(iterable))
-        expected = [(8, 100), (9, 100), (1, 1000), (2, 1000)]
-        self.assertEqual(actual, expected)
-
-    def test_decode(self):
-        iterable = [('d', 4), ('c', 3), ('b', 2), ('a', 1)]
-        actual = ''.join(mi.run_length.decode(iterable))
-        expected = 'ddddcccbba'
-        self.assertEqual(actual, expected)
-
-
-class ExactlyNTests(TestCase):
-    """Tests for ``exactly_n()``"""
-
-    def test_true(self):
-        """Iterable has ``n`` ``True`` elements"""
-        self.assertTrue(mi.exactly_n([True, False, True], 2))
-        self.assertTrue(mi.exactly_n([1, 1, 1, 0], 3))
-        self.assertTrue(mi.exactly_n([False, False], 0))
-        self.assertTrue(mi.exactly_n(range(100), 10, lambda x: x < 10))
-
-    def test_false(self):
-        """Iterable does not have ``n`` ``True`` elements"""
-        self.assertFalse(mi.exactly_n([True, False, False], 2))
-        self.assertFalse(mi.exactly_n([True, True, False], 1))
-        self.assertFalse(mi.exactly_n([False], 1))
-        self.assertFalse(mi.exactly_n([True], -1))
-        self.assertFalse(mi.exactly_n(repeat(True), 100))
-
-    def test_empty(self):
-        """Return ``True`` if the iterable is empty and ``n`` is 0"""
-        self.assertTrue(mi.exactly_n([], 0))
-        self.assertFalse(mi.exactly_n([], 1))
-
-
-class AlwaysReversibleTests(TestCase):
-    """Tests for ``always_reversible()``"""
-
-    def test_regular_reversed(self):
-        self.assertEqual(list(reversed(range(10))),
-                         list(mi.always_reversible(range(10))))
-        self.assertEqual(list(reversed([1, 2, 3])),
-                         list(mi.always_reversible([1, 2, 3])))
-        self.assertEqual(reversed([1, 2, 3]).__class__,
-                         mi.always_reversible([1, 2, 3]).__class__)
-
-    def test_nonseq_reversed(self):
-        # Create a non-reversible generator from a sequence
-        with self.assertRaises(TypeError):
-            reversed(x for x in range(10))
-
-        self.assertEqual(list(reversed(range(10))),
-                         list(mi.always_reversible(x for x in range(10))))
-        self.assertEqual(list(reversed([1, 2, 3])),
-                         list(mi.always_reversible(x for x in [1, 2, 3])))
-        self.assertNotEqual(reversed((1, 2)).__class__,
-                            mi.always_reversible(x for x in (1, 2)).__class__)
-
-
-class CircularShiftsTests(TestCase):
-    def test_empty(self):
-        # empty iterable -> empty list
-        self.assertEqual(list(mi.circular_shifts([])), [])
-
-    def test_simple_circular_shifts(self):
-        # test the a simple iterator case
-        self.assertEqual(
-            mi.circular_shifts(range(4)),
-            [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)]
-        )
-
-    def test_duplicates(self):
-        # test non-distinct entries
-        self.assertEqual(
-            mi.circular_shifts([0, 1, 0, 1]),
-            [(0, 1, 0, 1), (1, 0, 1, 0), (0, 1, 0, 1), (1, 0, 1, 0)]
-        )
-
-
-class MakeDecoratorTests(TestCase):
-    def test_basic(self):
-        slicer = mi.make_decorator(islice)
-
-        @slicer(1, 10, 2)
-        def user_function(arg_1, arg_2, kwarg_1=None):
-            self.assertEqual(arg_1, 'arg_1')
-            self.assertEqual(arg_2, 'arg_2')
-            self.assertEqual(kwarg_1, 'kwarg_1')
-            return map(str, count())
-
-        it = user_function('arg_1', 'arg_2', kwarg_1='kwarg_1')
-        actual = list(it)
-        expected = ['1', '3', '5', '7', '9']
-        self.assertEqual(actual, expected)
-
-    def test_result_index(self):
-        def stringify(*args, **kwargs):
-            self.assertEqual(args[0], 'arg_0')
-            iterable = args[1]
-            self.assertEqual(args[2], 'arg_2')
-            self.assertEqual(kwargs['kwarg_1'], 'kwarg_1')
-            return map(str, iterable)
-
-        stringifier = mi.make_decorator(stringify, result_index=1)
-
-        @stringifier('arg_0', 'arg_2', kwarg_1='kwarg_1')
-        def user_function(n):
-            return count(n)
-
-        it = user_function(1)
-        actual = mi.take(5, it)
-        expected = ['1', '2', '3', '4', '5']
-        self.assertEqual(actual, expected)
-
-    def test_wrap_class(self):
-        seeker = mi.make_decorator(mi.seekable)
-
-        @seeker()
-        def user_function(n):
-            return map(str, range(n))
-
-        it = user_function(5)
-        self.assertEqual(list(it), ['0', '1', '2', '3', '4'])
-
-        it.seek(0)
-        self.assertEqual(list(it), ['0', '1', '2', '3', '4'])
-
-
-class MapReduceTests(TestCase):
-    def test_default(self):
-        iterable = (str(x) for x in range(5))
-        keyfunc = lambda x: int(x) // 2
-        actual = sorted(mi.map_reduce(iterable, keyfunc).items())
-        expected = [(0, ['0', '1']), (1, ['2', '3']), (2, ['4'])]
-        self.assertEqual(actual, expected)
-
-    def test_valuefunc(self):
-        iterable = (str(x) for x in range(5))
-        keyfunc = lambda x: int(x) // 2
-        valuefunc = int
-        actual = sorted(mi.map_reduce(iterable, keyfunc, valuefunc).items())
-        expected = [(0, [0, 1]), (1, [2, 3]), (2, [4])]
-        self.assertEqual(actual, expected)
-
-    def test_reducefunc(self):
-        iterable = (str(x) for x in range(5))
-        keyfunc = lambda x: int(x) // 2
-        valuefunc = int
-        reducefunc = lambda value_list: reduce(mul, value_list, 1)
-        actual = sorted(
-            mi.map_reduce(iterable, keyfunc, valuefunc, reducefunc).items()
-        )
-        expected = [(0, 0), (1, 6), (2, 4)]
-        self.assertEqual(actual, expected)
-
-    def test_ret(self):
-        d = mi.map_reduce([1, 0, 2, 0, 1, 0], bool)
-        self.assertEqual(d, {False: [0, 0, 0], True: [1, 2, 1]})
-        self.assertRaises(KeyError, lambda: d[None].append(1))
-
-
-class RlocateTests(TestCase):
-    def test_default_pred(self):
-        iterable = [0, 1, 1, 0, 1, 0, 0]
-        for it in (iterable[:], iter(iterable)):
-            actual = list(mi.rlocate(it))
-            expected = [4, 2, 1]
-            self.assertEqual(actual, expected)
-
-    def test_no_matches(self):
-        iterable = [0, 0, 0]
-        for it in (iterable[:], iter(iterable)):
-            actual = list(mi.rlocate(it))
-            expected = []
-            self.assertEqual(actual, expected)
-
-    def test_custom_pred(self):
-        iterable = ['0', 1, 1, '0', 1, '0', '0']
-        pred = lambda x: x == '0'
-        for it in (iterable[:], iter(iterable)):
-            actual = list(mi.rlocate(it, pred))
-            expected = [6, 5, 3, 0]
-            self.assertEqual(actual, expected)
-
-    def test_efficient_reversal(self):
-        iterable = range(10 ** 10)  # Is efficiently reversible
-        target = 10 ** 10 - 2
-        pred = lambda x: x == target  # Find-able from the right
-        actual = next(mi.rlocate(iterable, pred))
-        self.assertEqual(actual, target)
-
-    def test_window_size(self):
-        iterable = ['0', 1, 1, '0', 1, '0', '0']
-        pred = lambda *args: args == ('0', 1)
-        for it in (iterable, iter(iterable)):
-            actual = list(mi.rlocate(it, pred, window_size=2))
-            expected = [3, 0]
-            self.assertEqual(actual, expected)
-
-    def test_window_size_large(self):
-        iterable = [1, 2, 3, 4]
-        pred = lambda a, b, c, d, e: True
-        for it in (iterable, iter(iterable)):
-            actual = list(mi.rlocate(iterable, pred, window_size=5))
-            expected = [0]
-            self.assertEqual(actual, expected)
-
-    def test_window_size_zero(self):
-        iterable = [1, 2, 3, 4]
-        pred = lambda: True
-        for it in (iterable, iter(iterable)):
-            with self.assertRaises(ValueError):
-                list(mi.locate(iterable, pred, window_size=0))
-
-
-class ReplaceTests(TestCase):
-    def test_basic(self):
-        iterable = range(10)
-        pred = lambda x: x % 2 == 0
-        substitutes = []
-        actual = list(mi.replace(iterable, pred, substitutes))
-        expected = [1, 3, 5, 7, 9]
-        self.assertEqual(actual, expected)
-
-    def test_count(self):
-        iterable = range(10)
-        pred = lambda x: x % 2 == 0
-        substitutes = []
-        actual = list(mi.replace(iterable, pred, substitutes, count=4))
-        expected = [1, 3, 5, 7, 8, 9]
-        self.assertEqual(actual, expected)
-
-    def test_window_size(self):
-        iterable = range(10)
-        pred = lambda *args: args == (0, 1, 2)
-        substitutes = []
-        actual = list(mi.replace(iterable, pred, substitutes, window_size=3))
-        expected = [3, 4, 5, 6, 7, 8, 9]
-        self.assertEqual(actual, expected)
-
-    def test_window_size_end(self):
-        iterable = range(10)
-        pred = lambda *args: args == (7, 8, 9)
-        substitutes = []
-        actual = list(mi.replace(iterable, pred, substitutes, window_size=3))
-        expected = [0, 1, 2, 3, 4, 5, 6]
-        self.assertEqual(actual, expected)
-
-    def test_window_size_count(self):
-        iterable = range(10)
-        pred = lambda *args: (args == (0, 1, 2)) or (args == (7, 8, 9))
-        substitutes = []
-        actual = list(
-            mi.replace(iterable, pred, substitutes, count=1, window_size=3)
-        )
-        expected = [3, 4, 5, 6, 7, 8, 9]
-        self.assertEqual(actual, expected)
-
-    def test_window_size_large(self):
-        iterable = range(4)
-        pred = lambda a, b, c, d, e: True
-        substitutes = [5, 6, 7]
-        actual = list(mi.replace(iterable, pred, substitutes, window_size=5))
-        expected = [5, 6, 7]
-        self.assertEqual(actual, expected)
-
-    def test_window_size_zero(self):
-        iterable = range(10)
-        pred = lambda *args: True
-        substitutes = []
-        with self.assertRaises(ValueError):
-            list(mi.replace(iterable, pred, substitutes, window_size=0))
-
-    def test_iterable_substitutes(self):
-        iterable = range(5)
-        pred = lambda x: x % 2 == 0
-        substitutes = iter('__')
-        actual = list(mi.replace(iterable, pred, substitutes))
-        expected = ['_', '_', 1, '_', '_', 3, '_', '_']
-        self.assertEqual(actual, expected)
diff --git a/libraries/more_itertools/tests/test_recipes.py b/libraries/more_itertools/tests/test_recipes.py
deleted file mode 100644
index 98981fe8..00000000
--- a/libraries/more_itertools/tests/test_recipes.py
+++ /dev/null
@@ -1,616 +0,0 @@
-from doctest import DocTestSuite
-from unittest import TestCase
-
-from itertools import combinations
-from six.moves import range
-
-import more_itertools as mi
-
-
-def load_tests(loader, tests, ignore):
-    # Add the doctests
-    tests.addTests(DocTestSuite('more_itertools.recipes'))
-    return tests
-
-
-class AccumulateTests(TestCase):
-    """Tests for ``accumulate()``"""
-
-    def test_empty(self):
-        """Test that an empty input returns an empty output"""
-        self.assertEqual(list(mi.accumulate([])), [])
-
-    def test_default(self):
-        """Test accumulate with the default function (addition)"""
-        self.assertEqual(list(mi.accumulate([1, 2, 3])), [1, 3, 6])
-
-    def test_bogus_function(self):
-        """Test accumulate with an invalid function"""
-        with self.assertRaises(TypeError):
-            list(mi.accumulate([1, 2, 3], func=lambda x: x))
-
-    def test_custom_function(self):
-        """Test accumulate with a custom function"""
-        self.assertEqual(
-            list(mi.accumulate((1, 2, 3, 2, 1), func=max)), [1, 2, 3, 3, 3]
-        )
-
-
-class TakeTests(TestCase):
-    """Tests for ``take()``"""
-
-    def test_simple_take(self):
-        """Test basic usage"""
-        t = mi.take(5, range(10))
-        self.assertEqual(t, [0, 1, 2, 3, 4])
-
-    def test_null_take(self):
-        """Check the null case"""
-        t = mi.take(0, range(10))
-        self.assertEqual(t, [])
-
-    def test_negative_take(self):
-        """Make sure taking negative items results in a ValueError"""
-        self.assertRaises(ValueError, lambda: mi.take(-3, range(10)))
-
-    def test_take_too_much(self):
-        """Taking more than an iterator has remaining should return what the
-        iterator has remaining.
-
-        """
-        t = mi.take(10, range(5))
-        self.assertEqual(t, [0, 1, 2, 3, 4])
-
-
-class TabulateTests(TestCase):
-    """Tests for ``tabulate()``"""
-
-    def test_simple_tabulate(self):
-        """Test the happy path"""
-        t = mi.tabulate(lambda x: x)
-        f = tuple([next(t) for _ in range(3)])
-        self.assertEqual(f, (0, 1, 2))
-
-    def test_count(self):
-        """Ensure tabulate accepts specific count"""
-        t = mi.tabulate(lambda x: 2 * x, -1)
-        f = (next(t), next(t), next(t))
-        self.assertEqual(f, (-2, 0, 2))
-
-
-class TailTests(TestCase):
-    """Tests for ``tail()``"""
-
-    def test_greater(self):
-        """Length of iterable is greather than requested tail"""
-        self.assertEqual(list(mi.tail(3, 'ABCDEFG')), ['E', 'F', 'G'])
-
-    def test_equal(self):
-        """Length of iterable is equal to the requested tail"""
-        self.assertEqual(
-            list(mi.tail(7, 'ABCDEFG')), ['A', 'B', 'C', 'D', 'E', 'F', 'G']
-        )
-
-    def test_less(self):
-        """Length of iterable is less than requested tail"""
-        self.assertEqual(
-            list(mi.tail(8, 'ABCDEFG')), ['A', 'B', 'C', 'D', 'E', 'F', 'G']
-        )
-
-
-class ConsumeTests(TestCase):
-    """Tests for ``consume()``"""
-
-    def test_sanity(self):
-        """Test basic functionality"""
-        r = (x for x in range(10))
-        mi.consume(r, 3)
-        self.assertEqual(3, next(r))
-
-    def test_null_consume(self):
-        """Check the null case"""
-        r = (x for x in range(10))
-        mi.consume(r, 0)
-        self.assertEqual(0, next(r))
-
-    def test_negative_consume(self):
-        """Check that negative consumsion throws an error"""
-        r = (x for x in range(10))
-        self.assertRaises(ValueError, lambda: mi.consume(r, -1))
-
-    def test_total_consume(self):
-        """Check that iterator is totally consumed by default"""
-        r = (x for x in range(10))
-        mi.consume(r)
-        self.assertRaises(StopIteration, lambda: next(r))
-
-
-class NthTests(TestCase):
-    """Tests for ``nth()``"""
-
-    def test_basic(self):
-        """Make sure the nth item is returned"""
-        l = range(10)
-        for i, v in enumerate(l):
-            self.assertEqual(mi.nth(l, i), v)
-
-    def test_default(self):
-        """Ensure a default value is returned when nth item not found"""
-        l = range(3)
-        self.assertEqual(mi.nth(l, 100, "zebra"), "zebra")
-
-    def test_negative_item_raises(self):
-        """Ensure asking for a negative item raises an exception"""
-        self.assertRaises(ValueError, lambda: mi.nth(range(10), -3))
-
-
-class AllEqualTests(TestCase):
-    """Tests for ``all_equal()``"""
-
-    def test_true(self):
-        """Everything is equal"""
-        self.assertTrue(mi.all_equal('aaaaaa'))
-        self.assertTrue(mi.all_equal([0, 0, 0, 0]))
-
-    def test_false(self):
-        """Not everything is equal"""
-        self.assertFalse(mi.all_equal('aaaaab'))
-        self.assertFalse(mi.all_equal([0, 0, 0, 1]))
-
-    def test_tricky(self):
-        """Not everything is identical, but everything is equal"""
-        items = [1, complex(1, 0), 1.0]
-        self.assertTrue(mi.all_equal(items))
-
-    def test_empty(self):
-        """Return True if the iterable is empty"""
-        self.assertTrue(mi.all_equal(''))
-        self.assertTrue(mi.all_equal([]))
-
-    def test_one(self):
-        """Return True if the iterable is singular"""
-        self.assertTrue(mi.all_equal('0'))
-        self.assertTrue(mi.all_equal([0]))
-
-
-class QuantifyTests(TestCase):
-    """Tests for ``quantify()``"""
-
-    def test_happy_path(self):
-        """Make sure True count is returned"""
-        q = [True, False, True]
-        self.assertEqual(mi.quantify(q), 2)
-
-    def test_custom_predicate(self):
-        """Ensure non-default predicates return as expected"""
-        q = range(10)
-        self.assertEqual(mi.quantify(q, lambda x: x % 2 == 0), 5)
-
-
-class PadnoneTests(TestCase):
-    """Tests for ``padnone()``"""
-
-    def test_happy_path(self):
-        """wrapper iterator should return None indefinitely"""
-        r = range(2)
-        p = mi.padnone(r)
-        self.assertEqual([0, 1, None, None], [next(p) for _ in range(4)])
-
-
-class NcyclesTests(TestCase):
-    """Tests for ``nyclces()``"""
-
-    def test_happy_path(self):
-        """cycle a sequence three times"""
-        r = ["a", "b", "c"]
-        n = mi.ncycles(r, 3)
-        self.assertEqual(
-            ["a", "b", "c", "a", "b", "c", "a", "b", "c"],
-            list(n)
-        )
-
-    def test_null_case(self):
-        """asking for 0 cycles should return an empty iterator"""
-        n = mi.ncycles(range(100), 0)
-        self.assertRaises(StopIteration, lambda: next(n))
-
-    def test_pathalogical_case(self):
-        """asking for negative cycles should return an empty iterator"""
-        n = mi.ncycles(range(100), -10)
-        self.assertRaises(StopIteration, lambda: next(n))
-
-
-class DotproductTests(TestCase):
-    """Tests for ``dotproduct()``'"""
-
-    def test_happy_path(self):
-        """simple dotproduct example"""
-        self.assertEqual(400, mi.dotproduct([10, 10], [20, 20]))
-
-
-class FlattenTests(TestCase):
-    """Tests for ``flatten()``"""
-
-    def test_basic_usage(self):
-        """ensure list of lists is flattened one level"""
-        f = [[0, 1, 2], [3, 4, 5]]
-        self.assertEqual(list(range(6)), list(mi.flatten(f)))
-
-    def test_single_level(self):
-        """ensure list of lists is flattened only one level"""
-        f = [[0, [1, 2]], [[3, 4], 5]]
-        self.assertEqual([0, [1, 2], [3, 4], 5], list(mi.flatten(f)))
-
-
-class RepeatfuncTests(TestCase):
-    """Tests for ``repeatfunc()``"""
-
-    def test_simple_repeat(self):
-        """test simple repeated functions"""
-        r = mi.repeatfunc(lambda: 5)
-        self.assertEqual([5, 5, 5, 5, 5], [next(r) for _ in range(5)])
-
-    def test_finite_repeat(self):
-        """ensure limited repeat when times is provided"""
-        r = mi.repeatfunc(lambda: 5, times=5)
-        self.assertEqual([5, 5, 5, 5, 5], list(r))
-
-    def test_added_arguments(self):
-        """ensure arguments are applied to the function"""
-        r = mi.repeatfunc(lambda x: x, 2, 3)
-        self.assertEqual([3, 3], list(r))
-
-    def test_null_times(self):
-        """repeat 0 should return an empty iterator"""
-        r = mi.repeatfunc(range, 0, 3)
-        self.assertRaises(StopIteration, lambda: next(r))
-
-
-class PairwiseTests(TestCase):
-    """Tests for ``pairwise()``"""
-
-    def test_base_case(self):
-        """ensure an iterable will return pairwise"""
-        p = mi.pairwise([1, 2, 3])
-        self.assertEqual([(1, 2), (2, 3)], list(p))
-
-    def test_short_case(self):
-        """ensure an empty iterator if there's not enough values to pair"""
-        p = mi.pairwise("a")
-        self.assertRaises(StopIteration, lambda: next(p))
-
-
-class GrouperTests(TestCase):
-    """Tests for ``grouper()``"""
-
-    def test_even(self):
-        """Test when group size divides evenly into the length of
-        the iterable.
-
-        """
-        self.assertEqual(
-            list(mi.grouper(3, 'ABCDEF')), [('A', 'B', 'C'), ('D', 'E', 'F')]
-        )
-
-    def test_odd(self):
-        """Test when group size does not divide evenly into the length of the
-        iterable.
-
-        """
-        self.assertEqual(
-            list(mi.grouper(3, 'ABCDE')), [('A', 'B', 'C'), ('D', 'E', None)]
-        )
-
-    def test_fill_value(self):
-        """Test that the fill value is used to pad the final group"""
-        self.assertEqual(
-            list(mi.grouper(3, 'ABCDE', 'x')),
-            [('A', 'B', 'C'), ('D', 'E', 'x')]
-        )
-
-
-class RoundrobinTests(TestCase):
-    """Tests for ``roundrobin()``"""
-
-    def test_even_groups(self):
-        """Ensure ordered output from evenly populated iterables"""
-        self.assertEqual(
-            list(mi.roundrobin('ABC', [1, 2, 3], range(3))),
-            ['A', 1, 0, 'B', 2, 1, 'C', 3, 2]
-        )
-
-    def test_uneven_groups(self):
-        """Ensure ordered output from unevenly populated iterables"""
-        self.assertEqual(
-            list(mi.roundrobin('ABCD', [1, 2], range(0))),
-            ['A', 1, 'B', 2, 'C', 'D']
-        )
-
-
-class PartitionTests(TestCase):
-    """Tests for ``partition()``"""
-
-    def test_bool(self):
-        """Test when pred() returns a boolean"""
-        lesser, greater = mi.partition(lambda x: x > 5, range(10))
-        self.assertEqual(list(lesser), [0, 1, 2, 3, 4, 5])
-        self.assertEqual(list(greater), [6, 7, 8, 9])
-
-    def test_arbitrary(self):
-        """Test when pred() returns an integer"""
-        divisibles, remainders = mi.partition(lambda x: x % 3, range(10))
-        self.assertEqual(list(divisibles), [0, 3, 6, 9])
-        self.assertEqual(list(remainders), [1, 2, 4, 5, 7, 8])
-
-
-class PowersetTests(TestCase):
-    """Tests for ``powerset()``"""
-
-    def test_combinatorics(self):
-        """Ensure a proper enumeration"""
-        p = mi.powerset([1, 2, 3])
-        self.assertEqual(
-            list(p),
-            [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
-        )
-
-
-class UniqueEverseenTests(TestCase):
-    """Tests for ``unique_everseen()``"""
-
-    def test_everseen(self):
-        """ensure duplicate elements are ignored"""
-        u = mi.unique_everseen('AAAABBBBCCDAABBB')
-        self.assertEqual(
-            ['A', 'B', 'C', 'D'],
-            list(u)
-        )
-
-    def test_custom_key(self):
-        """ensure the custom key comparison works"""
-        u = mi.unique_everseen('aAbACCc', key=str.lower)
-        self.assertEqual(list('abC'), list(u))
-
-    def test_unhashable(self):
-        """ensure things work for unhashable items"""
-        iterable = ['a', [1, 2, 3], [1, 2, 3], 'a']
-        u = mi.unique_everseen(iterable)
-        self.assertEqual(list(u), ['a', [1, 2, 3]])
-
-    def test_unhashable_key(self):
-        """ensure things work for unhashable items with a custom key"""
-        iterable = ['a', [1, 2, 3], [1, 2, 3], 'a']
-        u = mi.unique_everseen(iterable, key=lambda x: x)
-        self.assertEqual(list(u), ['a', [1, 2, 3]])
-
-
-class UniqueJustseenTests(TestCase):
-    """Tests for ``unique_justseen()``"""
-
-    def test_justseen(self):
-        """ensure only last item is remembered"""
-        u = mi.unique_justseen('AAAABBBCCDABB')
-        self.assertEqual(list('ABCDAB'), list(u))
-
-    def test_custom_key(self):
-        """ensure the custom key comparison works"""
-        u = mi.unique_justseen('AABCcAD', str.lower)
-        self.assertEqual(list('ABCAD'), list(u))
-
-
-class IterExceptTests(TestCase):
-    """Tests for ``iter_except()``"""
-
-    def test_exact_exception(self):
-        """ensure the exact specified exception is caught"""
-        l = [1, 2, 3]
-        i = mi.iter_except(l.pop, IndexError)
-        self.assertEqual(list(i), [3, 2, 1])
-
-    def test_generic_exception(self):
-        """ensure the generic exception can be caught"""
-        l = [1, 2]
-        i = mi.iter_except(l.pop, Exception)
-        self.assertEqual(list(i), [2, 1])
-
-    def test_uncaught_exception_is_raised(self):
-        """ensure a non-specified exception is raised"""
-        l = [1, 2, 3]
-        i = mi.iter_except(l.pop, KeyError)
-        self.assertRaises(IndexError, lambda: list(i))
-
-    def test_first(self):
-        """ensure first is run before the function"""
-        l = [1, 2, 3]
-        f = lambda: 25
-        i = mi.iter_except(l.pop, IndexError, f)
-        self.assertEqual(list(i), [25, 3, 2, 1])
-
-
-class FirstTrueTests(TestCase):
-    """Tests for ``first_true()``"""
-
-    def test_something_true(self):
-        """Test with no keywords"""
-        self.assertEqual(mi.first_true(range(10)), 1)
-
-    def test_nothing_true(self):
-        """Test default return value."""
-        self.assertEqual(mi.first_true([0, 0, 0]), False)
-
-    def test_default(self):
-        """Test with a default keyword"""
-        self.assertEqual(mi.first_true([0, 0, 0], default='!'), '!')
-
-    def test_pred(self):
-        """Test with a custom predicate"""
-        self.assertEqual(
-            mi.first_true([2, 4, 6], pred=lambda x: x % 3 == 0), 6
-        )
-
-
-class RandomProductTests(TestCase):
-    """Tests for ``random_product()``
-
-    Since random.choice() has different results with the same seed across
-    python versions 2.x and 3.x, these tests use highly probably events to
-    create predictable outcomes across platforms.
-    """
-
-    def test_simple_lists(self):
-        """Ensure that one item is chosen from each list in each pair.
-        Also ensure that each item from each list eventually appears in
-        the chosen combinations.
-
-        Odds are roughly 1 in 7.1 * 10e16 that one item from either list will
-        not be chosen after 100 samplings of one item from each list. Just to
-        be safe, better use a known random seed, too.
-
-        """
-        nums = [1, 2, 3]
-        lets = ['a', 'b', 'c']
-        n, m = zip(*[mi.random_product(nums, lets) for _ in range(100)])
-        n, m = set(n), set(m)
-        self.assertEqual(n, set(nums))
-        self.assertEqual(m, set(lets))
-        self.assertEqual(len(n), len(nums))
-        self.assertEqual(len(m), len(lets))
-
-    def test_list_with_repeat(self):
-        """ensure multiple items are chosen, and that they appear to be chosen
-        from one list then the next, in proper order.
-
-        """
-        nums = [1, 2, 3]
-        lets = ['a', 'b', 'c']
-        r = list(mi.random_product(nums, lets, repeat=100))
-        self.assertEqual(2 * 100, len(r))
-        n, m = set(r[::2]), set(r[1::2])
-        self.assertEqual(n, set(nums))
-        self.assertEqual(m, set(lets))
-        self.assertEqual(len(n), len(nums))
-        self.assertEqual(len(m), len(lets))
-
-
-class RandomPermutationTests(TestCase):
-    """Tests for ``random_permutation()``"""
-
-    def test_full_permutation(self):
-        """ensure every item from the iterable is returned in a new ordering
-
-        15 elements have a 1 in 1.3 * 10e12 of appearing in sorted order, so
-        we fix a seed value just to be sure.
-
-        """
-        i = range(15)
-        r = mi.random_permutation(i)
-        self.assertEqual(set(i), set(r))
-        if i == r:
-            raise AssertionError("Values were not permuted")
-
-    def test_partial_permutation(self):
-        """ensure all returned items are from the iterable, that the returned
-        permutation is of the desired length, and that all items eventually
-        get returned.
-
-        Sampling 100 permutations of length 5 from a set of 15 leaves a
-        (2/3)^100 chance that an item will not be chosen. Multiplied by 15
-        items, there is a 1 in 2.6e16 chance that at least 1 item will not
-        show up in the resulting output. Using a random seed will fix that.
-
-        """
-        items = range(15)
-        item_set = set(items)
-        all_items = set()
-        for _ in range(100):
-            permutation = mi.random_permutation(items, 5)
-            self.assertEqual(len(permutation), 5)
-            permutation_set = set(permutation)
-            self.assertLessEqual(permutation_set, item_set)
-            all_items |= permutation_set
-        self.assertEqual(all_items, item_set)
-
-
-class RandomCombinationTests(TestCase):
-    """Tests for ``random_combination()``"""
-
-    def test_psuedorandomness(self):
-        """ensure different subsets of the iterable get returned over many
-        samplings of random combinations"""
-        items = range(15)
-        all_items = set()
-        for _ in range(50):
-            combination = mi.random_combination(items, 5)
-            all_items |= set(combination)
-        self.assertEqual(all_items, set(items))
-
-    def test_no_replacement(self):
-        """ensure that elements are sampled without replacement"""
-        items = range(15)
-        for _ in range(50):
-            combination = mi.random_combination(items, len(items))
-            self.assertEqual(len(combination), len(set(combination)))
-        self.assertRaises(
-            ValueError, lambda: mi.random_combination(items, len(items) + 1)
-        )
-
-
-class RandomCombinationWithReplacementTests(TestCase):
-    """Tests for ``random_combination_with_replacement()``"""
-
-    def test_replacement(self):
-        """ensure that elements are sampled with replacement"""
-        items = range(5)
-        combo = mi.random_combination_with_replacement(items, len(items) * 2)
-        self.assertEqual(2 * len(items), len(combo))
-        if len(set(combo)) == len(combo):
-            raise AssertionError("Combination contained no duplicates")
-
-    def test_pseudorandomness(self):
-        """ensure different subsets of the iterable get returned over many
-        samplings of random combinations"""
-        items = range(15)
-        all_items = set()
-        for _ in range(50):
-            combination = mi.random_combination_with_replacement(items, 5)
-            all_items |= set(combination)
-        self.assertEqual(all_items, set(items))
-
-
-class NthCombinationTests(TestCase):
-    def test_basic(self):
-        iterable = 'abcdefg'
-        r = 4
-        for index, expected in enumerate(combinations(iterable, r)):
-            actual = mi.nth_combination(iterable, r, index)
-            self.assertEqual(actual, expected)
-
-    def test_long(self):
-        actual = mi.nth_combination(range(180), 4, 2000000)
-        expected = (2, 12, 35, 126)
-        self.assertEqual(actual, expected)
-
-    def test_invalid_r(self):
-        for r in (-1, 3):
-            with self.assertRaises(ValueError):
-                mi.nth_combination([], r, 0)
-
-    def test_invalid_index(self):
-        with self.assertRaises(IndexError):
-            mi.nth_combination('abcdefg', 3, -36)
-
-
-class PrependTests(TestCase):
-    def test_basic(self):
-        value = 'a'
-        iterator = iter('bcdefg')
-        actual = list(mi.prepend(value, iterator))
-        expected = list('abcdefg')
-        self.assertEqual(actual, expected)
-
-    def test_multiple(self):
-        value = 'ab'
-        iterator = iter('cdefg')
-        actual = tuple(mi.prepend(value, iterator))
-        expected = ('ab',) + tuple('cdefg')
-        self.assertEqual(actual, expected)
diff --git a/libraries/portend.py b/libraries/portend.py
deleted file mode 100644
index 4c393806..00000000
--- a/libraries/portend.py
+++ /dev/null
@@ -1,212 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-A simple library for managing the availability of ports.
-"""
-
-from __future__ import print_function, division
-
-import time
-import socket
-import argparse
-import sys
-import itertools
-import contextlib
-import collections
-import platform
-
-from tempora import timing
-
-
-def client_host(server_host):
-	"""Return the host on which a client can connect to the given listener."""
-	if server_host == '0.0.0.0':
-		# 0.0.0.0 is INADDR_ANY, which should answer on localhost.
-		return '127.0.0.1'
-	if server_host in ('::', '::0', '::0.0.0.0'):
-		# :: is IN6ADDR_ANY, which should answer on localhost.
-		# ::0 and ::0.0.0.0 are non-canonical but common
-		# ways to write IN6ADDR_ANY.
-		return '::1'
-	return server_host
-
-
-class Checker(object):
-	def __init__(self, timeout=1.0):
-		self.timeout = timeout
-
-	def assert_free(self, host, port=None):
-		"""
-		Assert that the given addr is free
-		in that all attempts to connect fail within the timeout
-		or raise a PortNotFree exception.
-
-		>>> free_port = find_available_local_port()
-
-		>>> Checker().assert_free('localhost', free_port)
-		>>> Checker().assert_free('127.0.0.1', free_port)
-		>>> Checker().assert_free('::1', free_port)
-
-		Also accepts an addr tuple
-
-		>>> addr = '::1', free_port, 0, 0
-		>>> Checker().assert_free(addr)
-
-		Host might refer to a server bind address like '::', which
-		should use localhost to perform the check.
-
-		>>> Checker().assert_free('::', free_port)
-		"""
-		if port is None and isinstance(host, collections.Sequence):
-			host, port = host[:2]
-		if platform.system() == 'Windows':
-			host = client_host(host)
-		info = socket.getaddrinfo(
-			host, port, socket.AF_UNSPEC, socket.SOCK_STREAM,
-		)
-		list(itertools.starmap(self._connect, info))
-
-	def _connect(self, af, socktype, proto, canonname, sa):
-		s = socket.socket(af, socktype, proto)
-		# fail fast with a small timeout
-		s.settimeout(self.timeout)
-
-		with contextlib.closing(s):
-			try:
-				s.connect(sa)
-			except socket.error:
-				return
-
-		# the connect succeeded, so the port isn't free
-		port, host = sa[:2]
-		tmpl = "Port {port} is in use on {host}."
-		raise PortNotFree(tmpl.format(**locals()))
-
-
-class Timeout(IOError):
-	pass
-
-
-class PortNotFree(IOError):
-	pass
-
-
-def free(host, port, timeout=float('Inf')):
-	"""
-	Wait for the specified port to become free (dropping or rejecting
-	requests). Return when the port is free or raise a Timeout if timeout has
-	elapsed.
-
-	Timeout may be specified in seconds or as a timedelta.
-	If timeout is None or ∞, the routine will run indefinitely.
-
-	>>> free('localhost', find_available_local_port())
-	"""
-	if not host:
-		raise ValueError("Host values of '' or None are not allowed.")
-
-	timer = timing.Timer(timeout)
-
-	while not timer.expired():
-		try:
-			# Expect a free port, so use a small timeout
-			Checker(timeout=0.1).assert_free(host, port)
-			return
-		except PortNotFree:
-			# Politely wait.
-			time.sleep(0.1)
-
-	raise Timeout("Port {port} not free on {host}.".format(**locals()))
-wait_for_free_port = free
-
-
-def occupied(host, port, timeout=float('Inf')):
-	"""
-	Wait for the specified port to become occupied (accepting requests).
-	Return when the port is occupied or raise a Timeout if timeout has
-	elapsed.
-
-	Timeout may be specified in seconds or as a timedelta.
-	If timeout is None or ∞, the routine will run indefinitely.
-
-	>>> occupied('localhost', find_available_local_port(), .1) # doctest: +IGNORE_EXCEPTION_DETAIL
-	Traceback (most recent call last):
-	    ...
-	Timeout: Port ... not bound on localhost.
-	"""
-	if not host:
-		raise ValueError("Host values of '' or None are not allowed.")
-
-	timer = timing.Timer(timeout)
-
-	while not timer.expired():
-		try:
-			Checker(timeout=.5).assert_free(host, port)
-			# Politely wait
-			time.sleep(0.1)
-		except PortNotFree:
-			# port is occupied
-			return
-
-	raise Timeout("Port {port} not bound on {host}.".format(**locals()))
-wait_for_occupied_port = occupied
-
-
-def find_available_local_port():
-	"""
-	Find a free port on localhost.
-
-	>>> 0 < find_available_local_port() < 65536
-	True
-	"""
-	sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
-	addr = '', 0
-	sock.bind(addr)
-	addr, port = sock.getsockname()[:2]
-	sock.close()
-	return port
-
-
-class HostPort(str):
-	"""
-	A simple representation of a host/port pair as a string
-
-	>>> hp = HostPort('localhost:32768')
-
-	>>> hp.host
-	'localhost'
-
-	>>> hp.port
-	32768
-
-	>>> len(hp)
-	15
-	"""
-
-	@property
-	def host(self):
-		host, sep, port = self.partition(':')
-		return host
-
-	@property
-	def port(self):
-		host, sep, port = self.partition(':')
-		return int(port)
-
-
-def _main():
-	parser = argparse.ArgumentParser()
-	global_lookup = lambda key: globals()[key]
-	parser.add_argument('target', metavar='host:port', type=HostPort)
-	parser.add_argument('func', metavar='state', type=global_lookup)
-	parser.add_argument('-t', '--timeout', default=None, type=float)
-	args = parser.parse_args()
-	try:
-		args.func(args.target.host, args.target.port, timeout=args.timeout)
-	except Timeout as timeout:
-		print(timeout, file=sys.stderr)
-		raise SystemExit(1)
-
-
-if __name__ == '__main__':
-	_main()
diff --git a/libraries/tempora/__init__.py b/libraries/tempora/__init__.py
deleted file mode 100644
index e0cdead0..00000000
--- a/libraries/tempora/__init__.py
+++ /dev/null
@@ -1,505 +0,0 @@
-# -*- coding: UTF-8 -*-
-
-"Objects and routines pertaining to date and time (tempora)"
-
-from __future__ import division, unicode_literals
-
-import datetime
-import time
-import re
-import numbers
-import functools
-
-import six
-
-__metaclass__ = type
-
-
-class Parser:
-	"""
-	Datetime parser: parses a date-time string using multiple possible
-	formats.
-
-	>>> p = Parser(('%H%M', '%H:%M'))
-	>>> tuple(p.parse('1319'))
-	(1900, 1, 1, 13, 19, 0, 0, 1, -1)
-	>>> dateParser = Parser(('%m/%d/%Y', '%Y-%m-%d', '%d-%b-%Y'))
-	>>> tuple(dateParser.parse('2003-12-20'))
-	(2003, 12, 20, 0, 0, 0, 5, 354, -1)
-	>>> tuple(dateParser.parse('16-Dec-1994'))
-	(1994, 12, 16, 0, 0, 0, 4, 350, -1)
-	>>> tuple(dateParser.parse('5/19/2003'))
-	(2003, 5, 19, 0, 0, 0, 0, 139, -1)
-	>>> dtParser = Parser(('%Y-%m-%d %H:%M:%S', '%a %b %d %H:%M:%S %Y'))
-	>>> tuple(dtParser.parse('2003-12-20 19:13:26'))
-	(2003, 12, 20, 19, 13, 26, 5, 354, -1)
-	>>> tuple(dtParser.parse('Tue Jan 20 16:19:33 2004'))
-	(2004, 1, 20, 16, 19, 33, 1, 20, -1)
-
-	Be forewarned, a ValueError will be raised if more than one format
-	matches:
-
-	>>> Parser(('%H%M', '%H%M%S')).parse('732')
-	Traceback (most recent call last):
-		...
-	ValueError: More than one format string matched target 732.
-	"""
-
-	formats = ('%m/%d/%Y', '%m/%d/%y', '%Y-%m-%d', '%d-%b-%Y', '%d-%b-%y')
-	"some common default formats"
-
-	def __init__(self, formats=None):
-		if formats:
-			self.formats = formats
-
-	def parse(self, target):
-		self.target = target
-		results = tuple(filter(None, map(self._parse, self.formats)))
-		del self.target
-		if not results:
-			tmpl = "No format strings matched the target {target}."
-			raise ValueError(tmpl.format(**locals()))
-		if not len(results) == 1:
-			tmpl = "More than one format string matched target {target}."
-			raise ValueError(tmpl.format(**locals()))
-		return results[0]
-
-	def _parse(self, format):
-		try:
-			result = time.strptime(self.target, format)
-		except ValueError:
-			result = False
-		return result
-
-
-# some useful constants
-osc_per_year = 290091329207984000
-"""
-mean vernal equinox year expressed in oscillations of atomic cesium at the
-year 2000 (see http://webexhibits.org/calendars/timeline.html for more info).
-"""
-osc_per_second = 9192631770
-seconds_per_second = 1
-seconds_per_year = 31556940
-seconds_per_minute = 60
-minutes_per_hour = 60
-hours_per_day = 24
-seconds_per_hour = seconds_per_minute * minutes_per_hour
-seconds_per_day = seconds_per_hour * hours_per_day
-days_per_year = seconds_per_year / seconds_per_day
-thirty_days = datetime.timedelta(days=30)
-# these values provide useful averages
-six_months = datetime.timedelta(days=days_per_year / 2)
-seconds_per_month = seconds_per_year / 12
-hours_per_month = hours_per_day * days_per_year / 12
-
-
-def strftime(fmt, t):
-	"""A class to replace the strftime in datetime package or time module.
-	Identical to strftime behavior in those modules except supports any
-	year.
-	Also supports datetime.datetime times.
-	Also supports milliseconds using %s
-	Also supports microseconds using %u"""
-	if isinstance(t, (time.struct_time, tuple)):
-		t = datetime.datetime(*t[:6])
-	assert isinstance(t, (datetime.datetime, datetime.time, datetime.date))
-	try:
-		year = t.year
-		if year < 1900:
-			t = t.replace(year=1900)
-	except AttributeError:
-		year = 1900
-	subs = (
-		('%Y', '%04d' % year),
-		('%y', '%02d' % (year % 100)),
-		('%s', '%03d' % (t.microsecond // 1000)),
-		('%u', '%03d' % (t.microsecond % 1000))
-	)
-
-	def doSub(s, sub):
-		return s.replace(*sub)
-
-	def doSubs(s):
-		return functools.reduce(doSub, subs, s)
-
-	fmt = '%%'.join(map(doSubs, fmt.split('%%')))
-	return t.strftime(fmt)
-
-
-def strptime(s, fmt, tzinfo=None):
-	"""
-	A function to replace strptime in the time module.  Should behave
-	identically to the strptime function except it returns a datetime.datetime
-	object instead of a time.struct_time object.
-	Also takes an optional tzinfo parameter which is a time zone info object.
-	"""
-	res = time.strptime(s, fmt)
-	return datetime.datetime(tzinfo=tzinfo, *res[:6])
-
-
-class DatetimeConstructor:
-	"""
-	>>> cd = DatetimeConstructor.construct_datetime
-	>>> cd(datetime.datetime(2011,1,1))
-	datetime.datetime(2011, 1, 1, 0, 0)
-	"""
-	@classmethod
-	def construct_datetime(cls, *args, **kwargs):
-		"""Construct a datetime.datetime from a number of different time
-		types found in python and pythonwin"""
-		if len(args) == 1:
-			arg = args[0]
-			method = cls.__get_dt_constructor(
-				type(arg).__module__,
-				type(arg).__name__,
-			)
-			result = method(arg)
-			try:
-				result = result.replace(tzinfo=kwargs.pop('tzinfo'))
-			except KeyError:
-				pass
-			if kwargs:
-				first_key = kwargs.keys()[0]
-				tmpl = (
-					"{first_key} is an invalid keyword "
-					"argument for this function."
-				)
-				raise TypeError(tmpl.format(**locals()))
-		else:
-			result = datetime.datetime(*args, **kwargs)
-		return result
-
-	@classmethod
-	def __get_dt_constructor(cls, moduleName, name):
-		try:
-			method_name = '__dt_from_{moduleName}_{name}__'.format(**locals())
-			return getattr(cls, method_name)
-		except AttributeError:
-			tmpl = (
-				"No way to construct datetime.datetime from "
-				"{moduleName}.{name}"
-			)
-			raise TypeError(tmpl.format(**locals()))
-
-	@staticmethod
-	def __dt_from_datetime_datetime__(source):
-		dtattrs = (
-			'year', 'month', 'day', 'hour', 'minute', 'second',
-			'microsecond', 'tzinfo',
-		)
-		attrs = map(lambda a: getattr(source, a), dtattrs)
-		return datetime.datetime(*attrs)
-
-	@staticmethod
-	def __dt_from___builtin___time__(pyt):
-		"Construct a datetime.datetime from a pythonwin time"
-		fmtString = '%Y-%m-%d %H:%M:%S'
-		result = strptime(pyt.Format(fmtString), fmtString)
-		# get milliseconds and microseconds.  The only way to do this is
-		#  to use the __float__ attribute of the time, which is in days.
-		microseconds_per_day = seconds_per_day * 1000000
-		microseconds = float(pyt) * microseconds_per_day
-		microsecond = int(microseconds % 1000000)
-		result = result.replace(microsecond=microsecond)
-		return result
-
-	@staticmethod
-	def __dt_from_timestamp__(timestamp):
-		return datetime.datetime.utcfromtimestamp(timestamp)
-	__dt_from___builtin___float__ = __dt_from_timestamp__
-	__dt_from___builtin___long__ = __dt_from_timestamp__
-	__dt_from___builtin___int__ = __dt_from_timestamp__
-
-	@staticmethod
-	def __dt_from_time_struct_time__(s):
-		return datetime.datetime(*s[:6])
-
-
-def datetime_mod(dt, period, start=None):
-	"""
-	Find the time which is the specified date/time truncated to the time delta
-	relative to the start date/time.
-	By default, the start time is midnight of the same day as the specified
-	date/time.
-
-	>>> datetime_mod(datetime.datetime(2004, 1, 2, 3),
-	...     datetime.timedelta(days = 1.5),
-	...     start = datetime.datetime(2004, 1, 1))
-	datetime.datetime(2004, 1, 1, 0, 0)
-	>>> datetime_mod(datetime.datetime(2004, 1, 2, 13),
-	...     datetime.timedelta(days = 1.5),
-	...     start = datetime.datetime(2004, 1, 1))
-	datetime.datetime(2004, 1, 2, 12, 0)
-	>>> datetime_mod(datetime.datetime(2004, 1, 2, 13),
-	...     datetime.timedelta(days = 7),
-	...     start = datetime.datetime(2004, 1, 1))
-	datetime.datetime(2004, 1, 1, 0, 0)
-	>>> datetime_mod(datetime.datetime(2004, 1, 10, 13),
-	...     datetime.timedelta(days = 7),
-	...     start = datetime.datetime(2004, 1, 1))
-	datetime.datetime(2004, 1, 8, 0, 0)
-	"""
-	if start is None:
-		# use midnight of the same day
-		start = datetime.datetime.combine(dt.date(), datetime.time())
-	# calculate the difference between the specified time and the start date.
-	delta = dt - start
-
-	# now aggregate the delta and the period into microseconds
-	# Use microseconds because that's the highest precision of these time
-	# pieces.  Also, using microseconds ensures perfect precision (no floating
-	# point errors).
-	def get_time_delta_microseconds(td):
-		return (td.days * seconds_per_day + td.seconds) * 1000000 + td.microseconds
-	delta, period = map(get_time_delta_microseconds, (delta, period))
-	offset = datetime.timedelta(microseconds=delta % period)
-	# the result is the original specified time minus the offset
-	result = dt - offset
-	return result
-
-
-def datetime_round(dt, period, start=None):
-	"""
-	Find the nearest even period for the specified date/time.
-
-	>>> datetime_round(datetime.datetime(2004, 11, 13, 8, 11, 13),
-	...     datetime.timedelta(hours = 1))
-	datetime.datetime(2004, 11, 13, 8, 0)
-	>>> datetime_round(datetime.datetime(2004, 11, 13, 8, 31, 13),
-	...     datetime.timedelta(hours = 1))
-	datetime.datetime(2004, 11, 13, 9, 0)
-	>>> datetime_round(datetime.datetime(2004, 11, 13, 8, 30),
-	...     datetime.timedelta(hours = 1))
-	datetime.datetime(2004, 11, 13, 9, 0)
-	"""
-	result = datetime_mod(dt, period, start)
-	if abs(dt - result) >= period // 2:
-		result += period
-	return result
-
-
-def get_nearest_year_for_day(day):
-	"""
-	Returns the nearest year to now inferred from a Julian date.
-	"""
-	now = time.gmtime()
-	result = now.tm_year
-	# if the day is far greater than today, it must be from last year
-	if day - now.tm_yday > 365 // 2:
-		result -= 1
-	# if the day is far less than today, it must be for next year.
-	if now.tm_yday - day > 365 // 2:
-		result += 1
-	return result
-
-
-def gregorian_date(year, julian_day):
-	"""
-	Gregorian Date is defined as a year and a julian day (1-based
-	index into the days of the year).
-
-	>>> gregorian_date(2007, 15)
-	datetime.date(2007, 1, 15)
-	"""
-	result = datetime.date(year, 1, 1)
-	result += datetime.timedelta(days=julian_day - 1)
-	return result
-
-
-def get_period_seconds(period):
-	"""
-	return the number of seconds in the specified period
-
-	>>> get_period_seconds('day')
-	86400
-	>>> get_period_seconds(86400)
-	86400
-	>>> get_period_seconds(datetime.timedelta(hours=24))
-	86400
-	>>> get_period_seconds('day + os.system("rm -Rf *")')
-	Traceback (most recent call last):
-	...
-	ValueError: period not in (second, minute, hour, day, month, year)
-	"""
-	if isinstance(period, six.string_types):
-		try:
-			name = 'seconds_per_' + period.lower()
-			result = globals()[name]
-		except KeyError:
-			msg = "period not in (second, minute, hour, day, month, year)"
-			raise ValueError(msg)
-	elif isinstance(period, numbers.Number):
-		result = period
-	elif isinstance(period, datetime.timedelta):
-		result = period.days * get_period_seconds('day') + period.seconds
-	else:
-		raise TypeError('period must be a string or integer')
-	return result
-
-
-def get_date_format_string(period):
-	"""
-	For a given period (e.g. 'month', 'day', or some numeric interval
-	such as 3600 (in secs)), return the format string that can be
-	used with strftime to format that time to specify the times
-	across that interval, but no more detailed.
-	For example,
-
-	>>> get_date_format_string('month')
-	'%Y-%m'
-	>>> get_date_format_string(3600)
-	'%Y-%m-%d %H'
-	>>> get_date_format_string('hour')
-	'%Y-%m-%d %H'
-	>>> get_date_format_string(None)
-	Traceback (most recent call last):
-		...
-	TypeError: period must be a string or integer
-	>>> get_date_format_string('garbage')
-	Traceback (most recent call last):
-		...
-	ValueError: period not in (second, minute, hour, day, month, year)
-	"""
-	# handle the special case of 'month' which doesn't have
-	#  a static interval in seconds
-	if isinstance(period, six.string_types) and period.lower() == 'month':
-		return '%Y-%m'
-	file_period_secs = get_period_seconds(period)
-	format_pieces = ('%Y', '-%m-%d', ' %H', '-%M', '-%S')
-	seconds_per_second = 1
-	intervals = (
-		seconds_per_year,
-		seconds_per_day,
-		seconds_per_hour,
-		seconds_per_minute,
-		seconds_per_second,
-	)
-	mods = list(map(lambda interval: file_period_secs % interval, intervals))
-	format_pieces = format_pieces[: mods.index(0) + 1]
-	return ''.join(format_pieces)
-
-
-def divide_timedelta_float(td, divisor):
-	"""
-	Divide a timedelta by a float value
-
-	>>> one_day = datetime.timedelta(days=1)
-	>>> half_day = datetime.timedelta(days=.5)
-	>>> divide_timedelta_float(one_day, 2.0) == half_day
-	True
-	>>> divide_timedelta_float(one_day, 2) == half_day
-	True
-	"""
-	# td is comprised of days, seconds, microseconds
-	dsm = [getattr(td, attr) for attr in ('days', 'seconds', 'microseconds')]
-	dsm = map(lambda elem: elem / divisor, dsm)
-	return datetime.timedelta(*dsm)
-
-
-def calculate_prorated_values():
-	"""
-	A utility function to prompt for a rate (a string in units per
-	unit time), and return that same rate for various time periods.
-	"""
-	rate = six.moves.input("Enter the rate (3/hour, 50/month)> ")
-	res = re.match('(?P<value>[\d.]+)/(?P<period>\w+)$', rate).groupdict()
-	value = float(res['value'])
-	value_per_second = value / get_period_seconds(res['period'])
-	for period in ('minute', 'hour', 'day', 'month', 'year'):
-		period_value = value_per_second * get_period_seconds(period)
-		print("per {period}: {period_value}".format(**locals()))
-
-
-def parse_timedelta(str):
-	"""
-	Take a string representing a span of time and parse it to a time delta.
-	Accepts any string of comma-separated numbers each with a unit indicator.
-
-	>>> parse_timedelta('1 day')
-	datetime.timedelta(days=1)
-
-	>>> parse_timedelta('1 day, 30 seconds')
-	datetime.timedelta(days=1, seconds=30)
-
-	>>> parse_timedelta('47.32 days, 20 minutes, 15.4 milliseconds')
-	datetime.timedelta(days=47, seconds=28848, microseconds=15400)
-
-	Supports weeks, months, years
-
-	>>> parse_timedelta('1 week')
-	datetime.timedelta(days=7)
-
-	>>> parse_timedelta('1 year, 1 month')
-	datetime.timedelta(days=395, seconds=58685)
-
-	Note that months and years strict intervals, not aligned
-	to a calendar:
-
-	>>> now = datetime.datetime.now()
-	>>> later = now + parse_timedelta('1 year')
-	>>> later.replace(year=now.year) - now
-	datetime.timedelta(seconds=20940)
-	"""
-	deltas = (_parse_timedelta_part(part.strip()) for part in str.split(','))
-	return sum(deltas, datetime.timedelta())
-
-
-def _parse_timedelta_part(part):
-	match = re.match('(?P<value>[\d.]+) (?P<unit>\w+)', part)
-	if not match:
-		msg = "Unable to parse {part!r} as a time delta".format(**locals())
-		raise ValueError(msg)
-	unit = match.group('unit').lower()
-	if not unit.endswith('s'):
-		unit += 's'
-	value = float(match.group('value'))
-	if unit == 'months':
-		unit = 'years'
-		value = value / 12
-	if unit == 'years':
-		unit = 'days'
-		value = value * days_per_year
-	return datetime.timedelta(**{unit: value})
-
-
-def divide_timedelta(td1, td2):
-	"""
-	Get the ratio of two timedeltas
-
-	>>> one_day = datetime.timedelta(days=1)
-	>>> one_hour = datetime.timedelta(hours=1)
-	>>> divide_timedelta(one_hour, one_day) == 1 / 24
-	True
-	"""
-	try:
-		return td1 / td2
-	except TypeError:
-		# Python 3.2 gets division
-		# http://bugs.python.org/issue2706
-		return td1.total_seconds() / td2.total_seconds()
-
-
-def date_range(start=None, stop=None, step=None):
-	"""
-	Much like the built-in function range, but works with dates
-
-	>>> range_items = date_range(
-	...     datetime.datetime(2005,12,21),
-	...     datetime.datetime(2005,12,25),
-	... )
-	>>> my_range = tuple(range_items)
-	>>> datetime.datetime(2005,12,21) in my_range
-	True
-	>>> datetime.datetime(2005,12,22) in my_range
-	True
-	>>> datetime.datetime(2005,12,25) in my_range
-	False
-	"""
-	if step is None:
-		step = datetime.timedelta(days=1)
-	if start is None:
-		start = datetime.datetime.now()
-	while start < stop:
-		yield start
-		start += step
diff --git a/libraries/tempora/schedule.py b/libraries/tempora/schedule.py
deleted file mode 100644
index 1ad093b2..00000000
--- a/libraries/tempora/schedule.py
+++ /dev/null
@@ -1,202 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-Classes for calling functions a schedule.
-"""
-
-from __future__ import absolute_import
-
-import datetime
-import numbers
-import abc
-import bisect
-
-import pytz
-
-__metaclass__ = type
-
-
-def now():
-    """
-    Provide the current timezone-aware datetime.
-
-    A client may override this function to change the default behavior,
-    such as to use local time or timezone-naïve times.
-    """
-    return datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
-
-
-def from_timestamp(ts):
-    """
-    Convert a numeric timestamp to a timezone-aware datetime.
-
-    A client may override this function to change the default behavior,
-    such as to use local time or timezone-naïve times.
-    """
-    return datetime.datetime.utcfromtimestamp(ts).replace(tzinfo=pytz.utc)
-
-
-class DelayedCommand(datetime.datetime):
-    """
-    A command to be executed after some delay (seconds or timedelta).
-    """
-
-    @classmethod
-    def from_datetime(cls, other):
-        return cls(
-            other.year, other.month, other.day, other.hour,
-            other.minute, other.second, other.microsecond,
-            other.tzinfo,
-        )
-
-    @classmethod
-    def after(cls, delay, target):
-        if not isinstance(delay, datetime.timedelta):
-            delay = datetime.timedelta(seconds=delay)
-        due_time = now() + delay
-        cmd = cls.from_datetime(due_time)
-        cmd.delay = delay
-        cmd.target = target
-        return cmd
-
-    @staticmethod
-    def _from_timestamp(input):
-        """
-        If input is a real number, interpret it as a Unix timestamp
-        (seconds sinc Epoch in UTC) and return a timezone-aware
-        datetime object. Otherwise return input unchanged.
-        """
-        if not isinstance(input, numbers.Real):
-            return input
-        return from_timestamp(input)
-
-    @classmethod
-    def at_time(cls, at, target):
-        """
-        Construct a DelayedCommand to come due at `at`, where `at` may be
-        a datetime or timestamp.
-        """
-        at = cls._from_timestamp(at)
-        cmd = cls.from_datetime(at)
-        cmd.delay = at - now()
-        cmd.target = target
-        return cmd
-
-    def due(self):
-        return now() >= self
-
-
-class PeriodicCommand(DelayedCommand):
-    """
-    Like a delayed command, but expect this command to run every delay
-    seconds.
-    """
-    def _next_time(self):
-        """
-        Add delay to self, localized
-        """
-        return self._localize(self + self.delay)
-
-    @staticmethod
-    def _localize(dt):
-        """
-        Rely on pytz.localize to ensure new result honors DST.
-        """
-        try:
-            tz = dt.tzinfo
-            return tz.localize(dt.replace(tzinfo=None))
-        except AttributeError:
-            return dt
-
-    def next(self):
-        cmd = self.__class__.from_datetime(self._next_time())
-        cmd.delay = self.delay
-        cmd.target = self.target
-        return cmd
-
-    def __setattr__(self, key, value):
-        if key == 'delay' and not value > datetime.timedelta():
-            raise ValueError(
-                "A PeriodicCommand must have a positive, "
-                "non-zero delay."
-            )
-        super(PeriodicCommand, self).__setattr__(key, value)
-
-
-class PeriodicCommandFixedDelay(PeriodicCommand):
-    """
-    Like a periodic command, but don't calculate the delay based on
-    the current time. Instead use a fixed delay following the initial
-    run.
-    """
-
-    @classmethod
-    def at_time(cls, at, delay, target):
-        at = cls._from_timestamp(at)
-        cmd = cls.from_datetime(at)
-        if isinstance(delay, numbers.Number):
-            delay = datetime.timedelta(seconds=delay)
-        cmd.delay = delay
-        cmd.target = target
-        return cmd
-
-    @classmethod
-    def daily_at(cls, at, target):
-        """
-        Schedule a command to run at a specific time each day.
-        """
-        daily = datetime.timedelta(days=1)
-        # convert when to the next datetime matching this time
-        when = datetime.datetime.combine(datetime.date.today(), at)
-        if when < now():
-            when += daily
-        return cls.at_time(cls._localize(when), daily, target)
-
-
-class Scheduler:
-    """
-    A rudimentary abstract scheduler accepting DelayedCommands
-    and dispatching them on schedule.
-    """
-    def __init__(self):
-        self.queue = []
-
-    def add(self, command):
-        assert isinstance(command, DelayedCommand)
-        bisect.insort(self.queue, command)
-
-    def run_pending(self):
-        while self.queue:
-            command = self.queue[0]
-            if not command.due():
-                break
-            self.run(command)
-            if isinstance(command, PeriodicCommand):
-                self.add(command.next())
-            del self.queue[0]
-
-    @abc.abstractmethod
-    def run(self, command):
-        """
-        Run the command
-        """
-
-
-class InvokeScheduler(Scheduler):
-    """
-    Command targets are functions to be invoked on schedule.
-    """
-    def run(self, command):
-        command.target()
-
-
-class CallbackScheduler(Scheduler):
-    """
-    Command targets are passed to a dispatch callable on schedule.
-    """
-    def __init__(self, dispatch):
-        super(CallbackScheduler, self).__init__()
-        self.dispatch = dispatch
-
-    def run(self, command):
-        self.dispatch(command.target)
diff --git a/libraries/tempora/tests/test_schedule.py b/libraries/tempora/tests/test_schedule.py
deleted file mode 100644
index 38eb8dc9..00000000
--- a/libraries/tempora/tests/test_schedule.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import time
-import random
-import datetime
-
-import pytest
-import pytz
-import freezegun
-
-from tempora import schedule
-
-__metaclass__ = type
-
-
-@pytest.fixture
-def naive_times(monkeypatch):
-	monkeypatch.setattr(
-		'irc.schedule.from_timestamp',
-		datetime.datetime.fromtimestamp)
-	monkeypatch.setattr('irc.schedule.now', datetime.datetime.now)
-
-
-do_nothing = type(None)
-try:
-	do_nothing()
-except TypeError:
-	# Python 2 compat
-	def do_nothing():
-		return None
-
-
-def test_delayed_command_order():
-	"""
-	delayed commands should be sorted by delay time
-	"""
-	delays = [random.randint(0, 99) for x in range(5)]
-	cmds = sorted([
-		schedule.DelayedCommand.after(delay, do_nothing)
-		for delay in delays
-	])
-	assert [c.delay.seconds for c in cmds] == sorted(delays)
-
-
-def test_periodic_command_delay():
-	"A PeriodicCommand must have a positive, non-zero delay."
-	with pytest.raises(ValueError) as exc_info:
-		schedule.PeriodicCommand.after(0, None)
-	assert str(exc_info.value) == test_periodic_command_delay.__doc__
-
-
-def test_periodic_command_fixed_delay():
-	"""
-	Test that we can construct a periodic command with a fixed initial
-	delay.
-	"""
-	fd = schedule.PeriodicCommandFixedDelay.at_time(
-		at=schedule.now(),
-		delay=datetime.timedelta(seconds=2),
-		target=lambda: None,
-	)
-	assert fd.due() is True
-	assert fd.next().due() is False
-
-
-class TestCommands:
-	def test_delayed_command_from_timestamp(self):
-		"""
-		Ensure a delayed command can be constructed from a timestamp.
-		"""
-		t = time.time()
-		schedule.DelayedCommand.at_time(t, do_nothing)
-
-	def test_command_at_noon(self):
-		"""
-		Create a periodic command that's run at noon every day.
-		"""
-		when = datetime.time(12, 0, tzinfo=pytz.utc)
-		cmd = schedule.PeriodicCommandFixedDelay.daily_at(when, target=None)
-		assert cmd.due() is False
-		next_cmd = cmd.next()
-		daily = datetime.timedelta(days=1)
-		day_from_now = schedule.now() + daily
-		two_days_from_now = day_from_now + daily
-		assert day_from_now < next_cmd < two_days_from_now
-
-
-class TestTimezones:
-	def test_alternate_timezone_west(self):
-		target_tz = pytz.timezone('US/Pacific')
-		target = schedule.now().astimezone(target_tz)
-		cmd = schedule.DelayedCommand.at_time(target, target=None)
-		assert cmd.due()
-
-	def test_alternate_timezone_east(self):
-		target_tz = pytz.timezone('Europe/Amsterdam')
-		target = schedule.now().astimezone(target_tz)
-		cmd = schedule.DelayedCommand.at_time(target, target=None)
-		assert cmd.due()
-
-	def test_daylight_savings(self):
-		"""
-		A command at 9am should always be 9am regardless of
-		a DST boundary.
-		"""
-		with freezegun.freeze_time('2018-03-10 08:00:00'):
-			target_tz = pytz.timezone('US/Eastern')
-			target_time = datetime.time(9, tzinfo=target_tz)
-			cmd = schedule.PeriodicCommandFixedDelay.daily_at(
-				target_time,
-				target=lambda: None,
-			)
-
-		def naive(dt):
-			return dt.replace(tzinfo=None)
-
-		assert naive(cmd) == datetime.datetime(2018, 3, 10, 9, 0, 0)
-		next_ = cmd.next()
-		assert naive(next_) == datetime.datetime(2018, 3, 11, 9, 0, 0)
-		assert next_ - cmd == datetime.timedelta(hours=23)
diff --git a/libraries/tempora/timing.py b/libraries/tempora/timing.py
deleted file mode 100644
index 03c22454..00000000
--- a/libraries/tempora/timing.py
+++ /dev/null
@@ -1,219 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import unicode_literals, absolute_import
-
-import datetime
-import functools
-import numbers
-import time
-
-__metaclass__ = type
-
-
-class Stopwatch:
-	"""
-	A simple stopwatch which starts automatically.
-
-	>>> w = Stopwatch()
-	>>> _1_sec = datetime.timedelta(seconds=1)
-	>>> w.split() < _1_sec
-	True
-	>>> import time
-	>>> time.sleep(1.0)
-	>>> w.split() >= _1_sec
-	True
-	>>> w.stop() >= _1_sec
-	True
-	>>> w.reset()
-	>>> w.start()
-	>>> w.split() < _1_sec
-	True
-
-	It should be possible to launch the Stopwatch in a context:
-
-	>>> with Stopwatch() as watch:
-	...     assert isinstance(watch.split(), datetime.timedelta)
-
-	In that case, the watch is stopped when the context is exited,
-	so to read the elapsed time::
-
-	>>> watch.elapsed
-	datetime.timedelta(...)
-	>>> watch.elapsed.seconds
-	0
-	"""
-	def __init__(self):
-		self.reset()
-		self.start()
-
-	def reset(self):
-		self.elapsed = datetime.timedelta(0)
-		if hasattr(self, 'start_time'):
-			del self.start_time
-
-	def start(self):
-		self.start_time = datetime.datetime.utcnow()
-
-	def stop(self):
-		stop_time = datetime.datetime.utcnow()
-		self.elapsed += stop_time - self.start_time
-		del self.start_time
-		return self.elapsed
-
-	def split(self):
-		local_duration = datetime.datetime.utcnow() - self.start_time
-		return self.elapsed + local_duration
-
-	# context manager support
-	def __enter__(self):
-		self.start()
-		return self
-
-	def __exit__(self, exc_type, exc_value, traceback):
-		self.stop()
-
-
-class IntervalGovernor:
-	"""
-	Decorate a function to only allow it to be called once per
-	min_interval. Otherwise, it returns None.
-	"""
-	def __init__(self, min_interval):
-		if isinstance(min_interval, numbers.Number):
-			min_interval = datetime.timedelta(seconds=min_interval)
-		self.min_interval = min_interval
-		self.last_call = None
-
-	def decorate(self, func):
-		@functools.wraps(func)
-		def wrapper(*args, **kwargs):
-			allow = (
-				not self.last_call
-				or self.last_call.split() > self.min_interval
-			)
-			if allow:
-				self.last_call = Stopwatch()
-				return func(*args, **kwargs)
-		return wrapper
-
-	__call__ = decorate
-
-
-class Timer(Stopwatch):
-	"""
-	Watch for a target elapsed time.
-
-	>>> t = Timer(0.1)
-	>>> t.expired()
-	False
-	>>> __import__('time').sleep(0.15)
-	>>> t.expired()
-	True
-	"""
-	def __init__(self, target=float('Inf')):
-		self.target = self._accept(target)
-		super(Timer, self).__init__()
-
-	def _accept(self, target):
-		"Accept None or ∞ or datetime or numeric for target"
-		if isinstance(target, datetime.timedelta):
-			target = target.total_seconds()
-
-		if target is None:
-			# treat None as infinite target
-			target = float('Inf')
-
-		return target
-
-	def expired(self):
-		return self.split().total_seconds() > self.target
-
-
-class BackoffDelay:
-	"""
-	Exponential backoff delay.
-
-	Useful for defining delays between retries. Consider for use
-	with ``jaraco.functools.retry_call`` as the cleanup.
-
-	Default behavior has no effect; a delay or jitter must
-	be supplied for the call to be non-degenerate.
-
-	>>> bd = BackoffDelay()
-	>>> bd()
-	>>> bd()
-
-	The following instance will delay 10ms for the first call,
-	20ms for the second, etc.
-
-	>>> bd = BackoffDelay(delay=0.01, factor=2)
-	>>> bd()
-	>>> bd()
-
-	Inspect and adjust the state of the delay anytime.
-
-	>>> bd.delay
-	0.04
-	>>> bd.delay = 0.01
-
-	Set limit to prevent the delay from exceeding bounds.
-
-	>>> bd = BackoffDelay(delay=0.01, factor=2, limit=0.015)
-	>>> bd()
-	>>> bd.delay
-	0.015
-
-	Limit may be a callable taking a number and returning
-	the limited number.
-
-	>>> at_least_one = lambda n: max(n, 1)
-	>>> bd = BackoffDelay(delay=0.01, factor=2, limit=at_least_one)
-	>>> bd()
-	>>> bd.delay
-	1
-
-	Pass a jitter to add or subtract seconds to the delay.
-
-	>>> bd = BackoffDelay(jitter=0.01)
-	>>> bd()
-	>>> bd.delay
-	0.01
-
-	Jitter may be a callable. To supply a non-deterministic jitter
-	between -0.5 and 0.5, consider:
-
-	>>> import random
-	>>> jitter=functools.partial(random.uniform, -0.5, 0.5)
-	>>> bd = BackoffDelay(jitter=jitter)
-	>>> bd()
-	>>> 0 <= bd.delay <= 0.5
-	True
-	"""
-
-	delay = 0
-
-	factor = 1
-	"Multiplier applied to delay"
-
-	jitter = 0
-	"Number or callable returning extra seconds to add to delay"
-
-	def __init__(self, delay=0, factor=1, limit=float('inf'), jitter=0):
-		self.delay = delay
-		self.factor = factor
-		if isinstance(limit, numbers.Number):
-			limit_ = limit
-
-			def limit(n):
-				return max(0, min(limit_, n))
-		self.limit = limit
-		if isinstance(jitter, numbers.Number):
-			jitter_ = jitter
-
-			def jitter():
-				return jitter_
-		self.jitter = jitter
-
-	def __call__(self):
-		time.sleep(self.delay)
-		self.delay = self.limit(self.delay * self.factor + self.jitter())
diff --git a/libraries/tempora/utc.py b/libraries/tempora/utc.py
deleted file mode 100644
index 35bfdb06..00000000
--- a/libraries/tempora/utc.py
+++ /dev/null
@@ -1,36 +0,0 @@
-"""
-Facilities for common time operations in UTC.
-
-Inspired by the `utc project <https://pypi.org/project/utc>`_.
-
->>> dt = now()
->>> dt == fromtimestamp(dt.timestamp())
-True
->>> dt.tzinfo
-datetime.timezone.utc
-
->>> from time import time as timestamp
->>> now().timestamp() - timestamp() < 0.1
-True
-
->>> datetime(2018, 6, 26, 0).tzinfo
-datetime.timezone.utc
-
->>> time(0, 0).tzinfo
-datetime.timezone.utc
-"""
-
-import datetime as std
-import functools
-
-
-__all__ = ['now', 'fromtimestamp', 'datetime', 'time']
-
-
-now = functools.partial(std.datetime.now, std.timezone.utc)
-fromtimestamp = functools.partial(
-	std.datetime.fromtimestamp,
-	tz=std.timezone.utc,
-)
-datetime = functools.partial(std.datetime, tzinfo=std.timezone.utc)
-time = functools.partial(std.time, tzinfo=std.timezone.utc)
diff --git a/libraries/zc/__init__.py b/libraries/zc/__init__.py
deleted file mode 100644
index 146c3362..00000000
--- a/libraries/zc/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__namespace__ = 'zc'
\ No newline at end of file
diff --git a/libraries/zc/lockfile/README.txt b/libraries/zc/lockfile/README.txt
deleted file mode 100644
index 89ef33e9..00000000
--- a/libraries/zc/lockfile/README.txt
+++ /dev/null
@@ -1,70 +0,0 @@
-Lock file support
-=================
-
-The ZODB lock_file module provides support for creating file system
-locks.  These are locks that are implemented with lock files and
-OS-provided locking facilities.  To create a lock, instantiate a
-LockFile object with a file name:
-
-    >>> import zc.lockfile
-    >>> lock = zc.lockfile.LockFile('lock')
-
-If we try to lock the same name, we'll get a lock error:
-
-    >>> import zope.testing.loggingsupport
-    >>> handler = zope.testing.loggingsupport.InstalledHandler('zc.lockfile')
-    >>> try:
-    ...     zc.lockfile.LockFile('lock')
-    ... except zc.lockfile.LockError:
-    ...     print("Can't lock file")
-    Can't lock file
-
-.. We don't log failure to acquire.
-
-    >>> for record in handler.records: # doctest: +ELLIPSIS
-    ...     print(record.levelname+' '+record.getMessage())
-
-To release the lock, use it's close method:
-
-    >>> lock.close()
-
-The lock file is not removed.  It is left behind:
-
-    >>> import os
-    >>> os.path.exists('lock')
-    True
-
-Of course, now that we've released the lock, we can create it again:
-
-    >>> lock = zc.lockfile.LockFile('lock')
-    >>> lock.close()
-
-.. Cleanup
-
-    >>> import os
-    >>> os.remove('lock')
-
-Hostname in lock file
-=====================
-
-In a container environment (e.g. Docker), the PID is typically always
-identical even if multiple containers are running under the same operating
-system instance.
-
-Clearly, inspecting lock files doesn't then help much in debugging. To identify
-the container which created the lock file, we need information about the
-container in the lock file. Since Docker uses the container identifier or name
-as the hostname, this information can be stored in the lock file in addition to
-or instead of the PID.
-
-Use the ``content_template`` keyword argument to ``LockFile`` to specify a
-custom lock file content format:
-
-    >>> lock = zc.lockfile.LockFile('lock', content_template='{pid};{hostname}')
-    >>> lock.close()
-
-If you now inspected the lock file, you would see e.g.:
-
-    $ cat lock
-     123;myhostname
-
diff --git a/libraries/zc/lockfile/__init__.py b/libraries/zc/lockfile/__init__.py
deleted file mode 100644
index a0ac2ff1..00000000
--- a/libraries/zc/lockfile/__init__.py
+++ /dev/null
@@ -1,104 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE
-#
-##############################################################################
-
-import os
-import errno
-import logging
-logger = logging.getLogger("zc.lockfile")
-
-class LockError(Exception):
-    """Couldn't get a lock
-    """
-
-try:
-    import fcntl
-except ImportError:
-    try:
-        import msvcrt
-    except ImportError:
-        def _lock_file(file):
-            raise TypeError('No file-locking support on this platform')
-        def _unlock_file(file):
-            raise TypeError('No file-locking support on this platform')
-
-    else:
-        # Windows
-        def _lock_file(file):
-            # Lock just the first byte
-            try:
-                msvcrt.locking(file.fileno(), msvcrt.LK_NBLCK, 1)
-            except IOError:
-                raise LockError("Couldn't lock %r" % file.name)
-
-        def _unlock_file(file):
-            try:
-                file.seek(0)
-                msvcrt.locking(file.fileno(), msvcrt.LK_UNLCK, 1)
-            except IOError:
-                raise LockError("Couldn't unlock %r" % file.name)
-
-else:
-    # Unix
-    _flags = fcntl.LOCK_EX | fcntl.LOCK_NB
-
-    def _lock_file(file):
-        try:
-            fcntl.flock(file.fileno(), _flags)
-        except IOError:
-            raise LockError("Couldn't lock %r" % file.name)
-
-    def _unlock_file(file):
-        fcntl.flock(file.fileno(), fcntl.LOCK_UN)
-
-class LazyHostName(object):
-    """Avoid importing socket and calling gethostname() unnecessarily"""
-    def __str__(self):
-        import socket
-        return socket.gethostname()
-
-
-class LockFile:
-
-    _fp = None
-
-    def __init__(self, path, content_template='{pid}'):
-        self._path = path
-        try:
-            # Try to open for writing without truncation:
-            fp = open(path, 'r+')
-        except IOError:
-            # If the file doesn't exist, we'll get an IO error, try a+
-            # Note that there may be a race here. Multiple processes
-            # could fail on the r+ open and open the file a+, but only
-            # one will get the the lock and write a pid.
-            fp = open(path, 'a+')
-
-        try:
-            _lock_file(fp)
-        except:
-            fp.close()
-            raise
-
-        # We got the lock, record info in the file.
-        self._fp = fp
-        fp.write(" %s\n" % content_template.format(pid=os.getpid(),
-                                                   hostname=LazyHostName()))
-        fp.truncate()
-        fp.flush()
-
-    def close(self):
-        if self._fp is not None:
-            _unlock_file(self._fp)
-            self._fp.close()
-            self._fp = None
diff --git a/libraries/zc/lockfile/tests.py b/libraries/zc/lockfile/tests.py
deleted file mode 100644
index e9fcbff3..00000000
--- a/libraries/zc/lockfile/tests.py
+++ /dev/null
@@ -1,193 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-import os, re, sys, unittest, doctest
-import zc.lockfile, time, threading
-from zope.testing import renormalizing, setupstack
-import tempfile
-try:
-    from unittest.mock import Mock, patch
-except ImportError:
-    from mock import Mock, patch
-
-checker = renormalizing.RENormalizing([
-    # Python 3 adds module path to error class name.
-    (re.compile("zc\.lockfile\.LockError:"),
-     r"LockError:"),
-    ])
-
-def inc():
-    while 1:
-        try:
-            lock = zc.lockfile.LockFile('f.lock')
-        except zc.lockfile.LockError:
-            continue
-        else:
-            break
-    f = open('f', 'r+b')
-    v = int(f.readline().strip())
-    time.sleep(0.01)
-    v += 1
-    f.seek(0)
-    f.write(('%d\n' % v).encode('ASCII'))
-    f.close()
-    lock.close()
-
-def many_threads_read_and_write():
-    r"""
-    >>> with open('f', 'w+b') as file:
-    ...     _ = file.write(b'0\n')
-    >>> with open('f.lock', 'w+b') as file:
-    ...     _ = file.write(b'0\n')
-
-    >>> n = 50
-    >>> threads = [threading.Thread(target=inc) for i in range(n)]
-    >>> _ = [thread.start() for thread in threads]
-    >>> _ = [thread.join() for thread in threads]
-    >>> with open('f', 'rb') as file:
-    ...     saved = int(file.read().strip())
-    >>> saved == n
-    True
-
-    >>> os.remove('f')
-
-    We should only have one pid in the lock file:
-
-    >>> f = open('f.lock')
-    >>> len(f.read().strip().split())
-    1
-    >>> f.close()
-
-    >>> os.remove('f.lock')
-
-    """
-
-def pid_in_lockfile():
-    r"""
-    >>> import os, zc.lockfile
-    >>> pid = os.getpid()
-    >>> lock = zc.lockfile.LockFile("f.lock")
-    >>> f = open("f.lock")
-    >>> _ = f.seek(1)
-    >>> f.read().strip() == str(pid)
-    True
-    >>> f.close()
-
-    Make sure that locking twice does not overwrite the old pid:
-
-    >>> lock = zc.lockfile.LockFile("f.lock")
-    Traceback (most recent call last):
-      ...
-    LockError: Couldn't lock 'f.lock'
-
-    >>> f = open("f.lock")
-    >>> _ = f.seek(1)
-    >>> f.read().strip() == str(pid)
-    True
-    >>> f.close()
-
-    >>> lock.close()
-    """
-
-
-def hostname_in_lockfile():
-    r"""
-    hostname is correctly written into the lock file when it's included in the
-    lock file content template
-
-    >>> import zc.lockfile
-    >>> with patch('socket.gethostname', Mock(return_value='myhostname')):
-    ...     lock = zc.lockfile.LockFile("f.lock", content_template='{hostname}')
-    >>> f = open("f.lock")
-    >>> _ = f.seek(1)
-    >>> f.read().rstrip()
-    'myhostname'
-    >>> f.close()
-
-    Make sure that locking twice does not overwrite the old hostname:
-
-    >>> lock = zc.lockfile.LockFile("f.lock", content_template='{hostname}')
-    Traceback (most recent call last):
-      ...
-    LockError: Couldn't lock 'f.lock'
-
-    >>> f = open("f.lock")
-    >>> _ = f.seek(1)
-    >>> f.read().rstrip()
-    'myhostname'
-    >>> f.close()
-
-    >>> lock.close()
-    """
-
-
-class TestLogger(object):
-    def __init__(self):
-        self.log_entries = []
-
-    def exception(self, msg, *args):
-        self.log_entries.append((msg,) + args)
-
-
-class LockFileLogEntryTestCase(unittest.TestCase):
-    """Tests for logging in case of lock failure"""
-    def setUp(self):
-        self.here = os.getcwd()
-        self.tmp = tempfile.mkdtemp(prefix='zc.lockfile-test-')
-        os.chdir(self.tmp)
-
-    def tearDown(self):
-        os.chdir(self.here)
-        setupstack.rmtree(self.tmp)
-
-    def test_log_formatting(self):
-        # PID and hostname are parsed and logged from lock file on failure
-        with patch('os.getpid', Mock(return_value=123)):
-            with patch('socket.gethostname', Mock(return_value='myhostname')):
-                lock = zc.lockfile.LockFile('f.lock',
-                                            content_template='{pid}/{hostname}')
-                with open('f.lock') as f:
-                    self.assertEqual(' 123/myhostname\n', f.read())
-
-                lock.close()
-
-    def test_unlock_and_lock_while_multiprocessing_process_running(self):
-        import multiprocessing
-
-        lock = zc.lockfile.LockFile('l')
-        q = multiprocessing.Queue()
-        p = multiprocessing.Process(target=q.get)
-        p.daemon = True
-        p.start()
-
-        # release and re-acquire should work (obviously)
-        lock.close()
-        lock = zc.lockfile.LockFile('l')
-        self.assertTrue(p.is_alive())
-
-        q.put(0)
-        lock.close()
-        p.join()
-
-
-def test_suite():
-    suite = unittest.TestSuite()
-    suite.addTest(doctest.DocFileSuite(
-        'README.txt', checker=checker,
-        setUp=setupstack.setUpDirectory, tearDown=setupstack.tearDown))
-    suite.addTest(doctest.DocTestSuite(
-        setUp=setupstack.setUpDirectory, tearDown=setupstack.tearDown,
-        checker=checker))
-    # Add unittest test cases from this module
-    suite.addTest(unittest.defaultTestLoader.loadTestsFromName(__name__))
-    return suite
diff --git a/resources/lib/webservice.py b/resources/lib/webservice.py
index 7f692846..512b844c 100644
--- a/resources/lib/webservice.py
+++ b/resources/lib/webservice.py
@@ -2,13 +2,13 @@
 
 #################################################################################################
 
+import BaseHTTPServer
 import logging
+import httplib
 import threading
+import urlparse
 
 import xbmc
-import xbmcvfs
-
-import cherrypy
 
 #################################################################################################
 
@@ -17,60 +17,125 @@ LOG = logging.getLogger("EMBY."+__name__)
 
 #################################################################################################
 
-
-class Root(object):
-
-    @cherrypy.expose
-    def default(self, *args, **kwargs):
-
-        try:
-            if not kwargs.get('Id').isdigit():
-                raise IndexError("Incorrect Id format: %s" % kwargs.get('Id'))
-
-            LOG.info("Webservice called with params: %s", kwargs)
-
-            return ("plugin://plugin.video.emby?mode=play&id=%s&dbid=%s&filename=%s&transcode=%s"
-                    % (kwargs.get('Id'), kwargs.get('KodiId'), kwargs.get('Name'), kwargs.get('transcode') or False))
-
-        except IndexError as error:
-            LOG.error(error)
-
-            raise cherrypy.HTTPError(404, error)
-
-        except Exception as error:
-            LOG.exception(error)
-
-            raise cherrypy.HTTPError(500, "Exception occurred: %s" % error)
-
 class WebService(threading.Thread):
 
-    root = None
+    ''' Run a webservice to trigger playback.
+    '''
+    stop_thread = False
 
     def __init__(self):
-
-        self.root = Root()
-        cherrypy.config.update({
-            'engine.autoreload.on' : False,
-            'log.screen': False,
-            'engine.timeout_monitor.frequency': 5,
-            'server.shutdown_timeout': 1,
-        })
         threading.Thread.__init__(self)
 
-    def run(self):
-        
-        LOG.info("--->[ webservice/%s ]", PORT)
-        conf = {
-            'global': {
-                'server.socket_host': '0.0.0.0',
-                'server.socket_port': PORT
-            }, '/': {}
-        }
-        cherrypy.quickstart(self.root, '/', conf)
-
     def stop(self):
 
-        cherrypy.engine.exit()
-        self.join(0)
+        ''' Called when the thread needs to stop
+        '''
+        try:
+            conn = httplib.HTTPConnection("127.0.0.1:%d" % PORT)
+            conn.request("QUIT", "/")
+            conn.getresponse()
+            self.stop_thread = True
+        except Exception as error:
+            pass
+
+    def run(self):
+
+        ''' Called to start the webservice.
+        '''
+        LOG.info("--->[ webservice/%s ]", PORT)
+
+        try:
+            server = HttpServer(('127.0.0.1', PORT), requestHandler)
+            server.serve_forever()
+        except Exception as error:
+
+            if '10053' not in error: # ignore host diconnected errors
+                LOG.exception(error)
+
+        LOG.info("---<[ webservice ]")
+
+
+class HttpServer(BaseHTTPServer.HTTPServer):
+
+    ''' Http server that reacts to self.stop flag.
+    '''
+    def serve_forever(self):
+
+        ''' Handle one request at a time until stopped.
+        '''
+        self.stop = False
+
+        while not self.stop:
+            self.handle_request()
+
+
+class requestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+
+    #Handler for the GET requests
+
+    def log_message(self, format, *args):
+
+        ''' Mute the webservice requests.
+        '''
+        pass
+
+    def get_params(self):
+
+        ''' Get the params
+        '''
+        try:
+            path = self.path[1:]
+
+            if '?' in path:
+                path = path.split('?', 1)[1]
+
+            params = dict(urlparse.parse_qsl(path))
+        except Exception:
+            params = {}
+
+        return params
+
+    def do_HEAD(self):
+
+        ''' Called on HEAD requests
+        '''
+        self.send_response(200)
+        self.end_headers()
+
+        return
+
+    def do_GET(self):
+
+        ''' Return plugin path
+        '''
+        try:
+            params = self.get_params()
+
+            if not params:
+                raise IndexError("Incomplete URL format")
+
+            if not params.get('Id').isdigit():
+                raise IndexError("Incorrect Id format %s" % params.get('Id'))
+
+            xbmc.log("[ webservice ] path: %s params: %s" % (str(self.path), str(params)), xbmc.LOGWARNING)
+
+            path = ("plugin://plugin.video.emby?mode=play&id=%s&dbid=%s&filename=%s&transcode=%s"
+                    % (params.get('Id'), params.get('KodiId'), params.get('Name'), params.get('transcode') or False))
+
+            self.send_response(200)
+            self.send_header('Content-type','text/html')
+            self.end_headers()
+            self.wfile.write(path)
+
+        except IndexError as error:
+
+            xbmc.log(str(error), xbmc.LOGWARNING)
+            self.send_error(404, "Exception occurred: %s" % error)
+
+        except Exception as error:
+
+            xbmc.log(str(error), xbmc.LOGWARNING)
+            self.send_error(500, "Exception occurred: %s" % error)
+
+        return
 
-        del self.root