Commits

Gustavo Picon  committed fe2f518

Renamed _cp*.py libraries

  • Participants
  • Parent commits 10d739a
  • Branches cp4

Comments (0)

Files changed (100)

File cherrypy/__init__.py

 
 __version__ = "4.0.0alpha"
 
-from cherrypy.lib._cpcompat import urljoin as _urljoin, urlencode as _urlencode
-from cherrypy.lib._cpcompat import basestring, unicodestr
+from cherrypy.lib.compat import urljoin as _urljoin, urlencode as _urlencode
+from cherrypy.lib.compat import basestring, unicodestr
 
-from cherrypy.lib._cperror import HTTPError, HTTPRedirect, InternalRedirect
-from cherrypy.lib._cperror import NotFound, CherryPyException, TimeoutError
+from cherrypy.lib.error import HTTPError, HTTPRedirect, InternalRedirect
+from cherrypy.lib.error import NotFound, CherryPyException, TimeoutError
 
-from cherrypy.lib import _cpdispatch as dispatch
+from cherrypy.lib import dispatch
 
-from cherrypy.lib import _cprequest
+from cherrypy.lib import request as _cprequest
 from cherrypy.lib import httputil as _httputil
 
-from cherrypy.lib import _cptree
+from cherrypy.lib import tree as _cptree
 tree = _cptree.Tree()
-from cherrypy.lib._cptree import Application
-from cherrypy.lib import _cpwsgi as wsgi
+from cherrypy.lib.tree import Application
+from cherrypy.lib import wsgi
 
 import magicbus
 engine = magicbus.bus
 engine.signals = _HandleSignalsPlugin(engine)
 
 
-from cherrypy.lib import _cpserver
+from cherrypy.lib import server as _cpserver
 server = _cpserver.Server()
 server.subscribe()
 
     pass
 
 
-from cherrypy.lib import _cplogging
+from cherrypy.lib import logger as _cplogging
 
 
 class _GlobalLogManager(_cplogging.LogManager):
 
     """A site-wide LogManager; routes to app.log or global log as appropriate.
 
-    This :class:`LogManager<cherrypy.lib._cplogging.LogManager>` implements
+    This :class:`LogManager<cherrypy.lib.logger.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
 
 
 # import _cpconfig last so it can reference other top-level objects
-from cherrypy.lib import _cpconfig
+from cherrypy.lib import config as _cpconfig
 # Use _global_conf_alias so quickstart can use 'config' as an arg
 # without shadowing cherrypy.config.
 config = _global_conf_alias = _cpconfig.Config()
 # Must reset to get our defaults applied.
 config.reset()
 
-from cherrypy.lib import _cpchecker
+from cherrypy.lib import checker as _cpchecker
 checker = _cpchecker.Checker()
 engine.subscribe('start', checker)

File cherrypy/lib/_cpchecker.py

-import os
-import warnings
-
-import cherrypy
-from cherrypy.lib._cpcompat import iteritems, copykeys, builtins
-
-
-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):
-        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):
-        """Function to 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 Application 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 iteritems(cherrypy.tree.apps):
-            if not isinstance(app, cherrypy.Application):
-                continue
-
-            msg = []
-            for section, entries in iteritems(app.config):
-                if section.startswith('/'):
-                    for key, value in 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 Application 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, v in conf.items():
-                    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(copykeys(app.toolboxes))
-        ns.extend(copykeys(app.namespaces))
-        ns.extend(copykeys(app.request_class.namespaces))
-        ns.extend(copykeys(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, v in conf.items():
-                    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 isinstance(conf, dict):
-                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__))
-            else:
-                k, v = section, conf
-                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.")

File cherrypy/lib/_cpcompat.py

-"""Compatibility code for using CherryPy with various versions of Python.
-
-CherryPy 3.2 is compatible with Python versions 2.7+. 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 new 'bytestr', 'unicodestr', and 'nativestr' attributes, as well as
-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. This also provides a 'BytesIO' name for dealing
-specifically with bytes, and a 'StringIO' name for dealing with native strings.
-It also provides a 'base64_decode' function with native strings as input and
-output.
-"""
-import re
-import sys
-
-if sys.version_info >= (3, 0):
-    py3k = True
-    bytestr = bytes
-    unicodestr = str
-    nativestr = unicodestr
-    basestring = (bytes, str)
-
-    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
-    # type("")
-    from io import StringIO
-    # bytes:
-    from io import BytesIO as BytesIO
-else:
-    # Python 2
-    py3k = False
-    bytestr = str
-    unicodestr = unicode
-    nativestr = bytestr
-    basestring = basestring
-
-    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 unicode(
-                re.sub(r'\\u([0-9a-zA-Z]{4})',
-                       lambda m: 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, unicode):
-            return n.encode(encoding)
-        return n
-    try:
-        # type("")
-        from cStringIO import StringIO
-    except ImportError:
-        # type("")
-        from StringIO import StringIO
-    # bytes:
-    BytesIO = StringIO
-
-
-def assert_native(n):
-    if not isinstance(n, nativestr):
-        raise TypeError("n must be a native str (got %s)" % type(n).__name__)
-
-try:
-    from base64 import decodebytes as _base64_decodebytes
-except ImportError:
-    from base64 import decodestring as _base64_decodebytes
-
-
-def base64_decode(n, encoding='ISO-8859-1'):
-    """Return the native string base64-decoded (as a native string)."""
-    if isinstance(n, unicodestr):
-        b = n.encode(encoding)
-    else:
-        b = n
-    b = _base64_decodebytes(b)
-    if nativestr is unicodestr:
-        return b.decode(encoding)
-    else:
-        return b
-
-try:
-    # Python 3
-    from urllib.parse import urljoin, urlencode
-    from urllib.parse import quote, quote_plus
-    from urllib.request import unquote, urlopen
-    from urllib.request import parse_http_list, parse_keqv_list
-except ImportError:
-    # Python 2
-    from urlparse import urljoin
-    from urllib import urlencode, urlopen
-    from urllib import quote, quote_plus
-    from urllib import unquote
-    from urllib2 import parse_http_list, parse_keqv_list
-
-
-try:
-    dict.iteritems
-    # Python 2
-    iteritems = lambda d: d.iteritems()
-    copyitems = lambda d: d.items()
-except AttributeError:
-    # Python 3
-    iteritems = lambda d: d.items()
-    copyitems = lambda d: list(d.items())
-
-try:
-    dict.iterkeys
-    # Python 2
-    iterkeys = lambda d: d.iterkeys()
-    copykeys = lambda d: d.keys()
-except AttributeError:
-    # Python 3
-    iterkeys = lambda d: d.keys()
-    copykeys = lambda d: list(d.keys())
-
-try:
-    dict.itervalues
-    # Python 2
-    itervalues = lambda d: d.itervalues()
-    copyvalues = lambda d: d.values()
-except AttributeError:
-    # Python 3
-    itervalues = lambda d: d.values()
-    copyvalues = lambda d: list(d.values())
-
-try:
-    # Python 3
-    import builtins
-except ImportError:
-    # Python 2
-    import __builtin__ as builtins
-
-try:
-    # Python 2. We try Python 2 first clients on Python 2
-    # don't try to import the 'http' module from cherrypy.lib
-    from Cookie import SimpleCookie, CookieError
-    from httplib import BadStatusLine, HTTPConnection, IncompleteRead
-    from httplib import NotConnected
-    from BaseHTTPServer import BaseHTTPRequestHandler
-except ImportError:
-    # Python 3
-    from http.cookies import SimpleCookie, CookieError
-    from http.client import BadStatusLine, HTTPConnection, IncompleteRead
-    from http.client import NotConnected
-    from http.server import BaseHTTPRequestHandler
-
-# Some platforms don't expose HTTPSConnection, so handle it separately
-if py3k:
-    try:
-        from http.client import HTTPSConnection
-    except ImportError:
-        # Some platforms which don't have SSL don't expose HTTPSConnection
-        HTTPSConnection = None
-else:
-    try:
-        from httplib import HTTPSConnection
-    except ImportError:
-        HTTPSConnection = None
-
-try:
-    # Python 2
-    xrange = xrange
-except NameError:
-    # Python 3
-    xrange = range
-
-
-try:
-    from email.utils import formatdate
-
-    def HTTPDate(timeval=None):
-        return formatdate(timeval, usegmt=True)
-except ImportError:
-    from rfc822 import formatdate as HTTPDate
-
-try:
-    # Python 3
-    from urllib.parse import unquote as parse_unquote
-
-    def unquote_qs(atom, encoding, errors='strict'):
-        return parse_unquote(
-            atom.replace('+', ' '),
-            encoding=encoding,
-            errors=errors)
-except ImportError:
-    # Python 2
-    from urllib import unquote as parse_unquote
-
-    def unquote_qs(atom, encoding, errors='strict'):
-        return parse_unquote(atom.replace('+', ' ')).decode(encoding, errors)
-
-try:
-    # Prefer simplejson, which is usually more advanced than the builtin
-    # module.
-    import simplejson as json
-    json_decode = json.JSONDecoder().decode
-    _json_encode = json.JSONEncoder().iterencode
-except ImportError:
-    import json
-    json_decode = json.JSONDecoder().decode
-    _json_encode = json.JSONEncoder().iterencode
-finally:
-    if json and py3k:
-        # The two Python 3 implementations (simplejson/json)
-        # outputs str. We need bytes.
-        def json_encode(value):
-            for chunk in _json_encode(value):
-                yield chunk.encode('utf8')
-    else:
-        json_encode = _json_encode
-
-
-try:
-    import cPickle as pickle
-except ImportError:
-    # In Python 2, pickle is a Python version.
-    # In Python 3, pickle is the sped-up C version.
-    import pickle
-
-
-try:
-    from _thread import get_ident as get_thread_ident
-except ImportError:
-    from thread import get_ident as get_thread_ident
-
-try:
-    # Python 3
-    next = next
-except NameError:
-    # Python 2
-    def next(i):
-        return i.next()
-
-import threading
-
-if sys.version_info >= (3, 3):
-    Event = threading.Event
-else:
-    Event = threading._Event

File cherrypy/lib/_cpconfig.py

-"""
-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. For example::
-
-    class Demo:
-        _cp_config = {'tools.gzip.on': True}
-
-        def index(self):
-            return "Hello world"
-        index.exposed = True
-        index._cp_config = {'request.show_tracebacks': False}
-
-.. note::
-
-    This behavior is only guaranteed for the default dispatcher.
-    Other dispatchers may have different restrictions on where
-    you can attach _cp_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.lib._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 context manager.
-"""
-
-import cherrypy
-from cherrypy.lib._cpcompat import basestring
-from cherrypy.lib import reprconf
-
-
-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 isinstance(other, basestring):
-        cherrypy.engine.autoreload.files.add(other)
-
-    # Load other into base
-    for section, value_map in reprconf.as_dict(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 isinstance(config, basestring):
-            # Filename
-            cherrypy.engine.autoreload.files.add(config)
-        reprconf.Config.update(self, 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"
-        reprconf.Config._apply(self, config)
-
-    def __call__(self, *args, **kwargs):
-        """Decorator for page handlers to set _cp_config."""
-        if args:
-            raise TypeError(
-                "The cherrypy.config decorator does not accept positional "
-                "arguments; you must use keyword arguments.")
-
-        def tool_decorator(f):
-            if not hasattr(f, "_cp_config"):
-                f._cp_config = {}
-            for k, v in kwargs.items():
-                f._cp_config[k] = v
-            return f
-        return tool_decorator
-
-
-# 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.lib 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):
-    """Backward compatibility handler for the "engine" namespace."""
-    engine = cherrypy.engine
-
-    deprecated = {
-        'autoreload_on': 'autoreload.on',
-        'autoreload_frequency': 'autoreload.frequency',
-        'autoreload_match': 'autoreload.match',
-        'reload_files': 'autoreload.files',
-        'deadlock_poll_freq': 'timeout_monitor.frequency'
-    }
-
-    if k in deprecated:
-        engine.log(
-            'WARNING: Use of engine.%s is deprecated and will be removed in a '
-            'future version. Use engine.%s instead.' % (k, deprecated[k]))
-
-    if k == 'autoreload_on':
-        if v:
-            engine.autoreload.subscribe()
-        else:
-            engine.autoreload.unsubscribe()
-    elif k == 'autoreload_frequency':
-        engine.autoreload.frequency = v
-    elif k == 'autoreload_match':
-        engine.autoreload.match = v
-    elif k == 'reload_files':
-        engine.autoreload.files = set(v)
-    elif k == 'deadlock_poll_freq':
-        engine.timeout_monitor.frequency = v
-    elif k == 'SIGHUP':
-        engine.listeners['SIGHUP'] = set([v])
-    elif k == 'SIGTERM':
-        engine.listeners['SIGTERM'] = set([v])
-    elif "." in k:
-        plugin, attrname = k.split(".", 1)
-        plugin = getattr(engine, plugin)
-        if attrname == 'on':
-            if v and hasattr(getattr(plugin, 'subscribe', None), '__call__'):
-                plugin.subscribe()
-                return
-            elif (
-                (not v) and
-                hasattr(getattr(plugin, 'unsubscribe', None), '__call__')
-            ):
-                plugin.unsubscribe()
-                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)
-            cherrypy.engine.log("Mounted: %s on %s" %
-                                (app, script_name or "/"))
-    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

File cherrypy/lib/_cpdispatch.py

-"""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
-from cherrypy.lib.tools import xmlrpcutil
-
-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:
-                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) = inspect.getargspec(callable)
-    except TypeError:
-        if isinstance(callable, object) and hasattr(callable, '__call__'):
-            (args, varargs, varkw,
-             defaults) = inspect.getargspec(callable.__call__)
-        else:
-            # If it wasn't one of our own types, re-raise
-            # the original error
-            raise
-
-    if args and args[0] == '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:
-    test_callable_spec = lambda callable, args, kwargs: None
-
-
-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://bitbucket.org/cherrypy/cherrypy/issue/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()):
-    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://bitbucket.org/cherrypy/cherrypy/issue/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

File cherrypy/lib/_cperror.py

-"""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.lib._cperror.HTTPError>`
-or :class:`HTTPRedirect<cherrypy.lib._cperror.HTTPRedirect>` as the
-:attr:`request.handler<cherrypy.lib._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.lib._cprequest.Request.error_response>` to
-set the response status, headers, and body. By default, this is the same
-output as
-:class:`HTTPError(500) <cherrypy.lib._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 occured</body></html>"
-        ]
-        sendMail('error@domain.com',
-                 'Error in your web app',
-                 _cperror.format_exc())
-
-    class Root:
-        _cp_config = {'request.error_response': handle_error}
-
-
-Note that you have to explicitly set
-:attr:`response.body <cherrypy.lib._cprequest.Response.body>`
-and not simply return an error message as a result.
-"""
-
-from cgi import escape as _escape
-from sys import exc_info as _exc_info
-from traceback import format_exception as _format_exception
-from cherrypy.lib._cpcompat import basestring, bytestr, iteritems, ntob
-from cherrypy.lib._cpcompat import tonative, urljoin as _urljoin
-from cherrypy.lib import httputil as _httputil
-
-
-class CherryPyException(Exception):
-
-    """A base class for CherryPy exceptions."""
-    pass
-
-
-class TimeoutError(CherryPyException):
-
-    """Exception raised when Response.timed_out is detected."""
-    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=""):
-        import cherrypy
-        self.request = cherrypy.serving.request
-
-        self.query_string = query_string
-        if "?" in path:
-            # Separate any params included in the path
-            path, self.query_string = path.split("?", 1)
-
-        # Note that urljoin will "do the right thing" whether url is:
-        #  1. a URL relative to root (e.g. "/dummy")
-        #  2. a URL relative to the current path
-        # Note that any query string will be discarded.
-        path = _urljoin(self.request.path_info, path)
-
-        # Set a 'path' member attribute so that code which traps this
-        # error can have access to it.
-        self.path = path
-
-        CherryPyException.__init__(self, path, self.query_string)
-
-
-class HTTPRedirect(CherryPyException):
-
-    """Exception raised when the request should be redirected.
-
-    This exception will force a HTTP redirect to the URL or URL's you give it.
-    The new URL must be passed as the first argument to the Exception,
-    e.g., HTTPRedirect(newUrl). Multiple URLs are allowed in a list.
-    If 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.
-    """
-
-    status = None
-    """The integer HTTP status code to emit."""
-
-    urls = None
-    """The list of URL's to emit."""
-
-    encoding = 'utf-8'
-    """The encoding when passed urls are not native strings"""
-
-    def __init__(self, urls, status=None, encoding=None):
-        import cherrypy
-        request = cherrypy.serving.request
-
-        if isinstance(urls, basestring):
-            urls = [urls]
-
-        abs_urls = []
-        for url in urls:
-            url = tonative(url, encoding or self.encoding)
-
-            # Note that urljoin will "do the right thing" whether url is:
-            #  1. a complete URL with host (e.g. "http://www.example.com/test")
-            #  2. a URL relative to root (e.g. "/dummy")
-            #  3. a URL relative to the current path
-            # Note that any query string in cherrypy.request is discarded.
-            url = _urljoin(cherrypy.url(), url)
-            abs_urls.append(url)
-        self.urls = abs_urls
-
-        # RFC 2616 indicates a 301 response code fits our goal; however,
-        # browser support for 301 is quite messy. Do 302/303 instead. See
-        # http://www.alanflavell.org.uk/www/post-redirect.html
-        if status is None:
-            if request.protocol >= (1, 1):
-                status = 303
-            else:
-                status = 302
-        else:
-            status = int(status)
-            if status < 300 or status > 399:
-                raise ValueError("status must be between 300 and 399.")
-
-        self.status = status
-        CherryPyException.__init__(self, abs_urls, status)
-
-    def set_response(self):
-        """Modify cherrypy.response status, headers, and body to represent
-        self.
-
-        CherryPy uses this internally, but you can also use it to create an
-        HTTPRedirect object and set its output without *raising* the exception.
-        """
-        import cherrypy
-        response = cherrypy.serving.response
-        response.status = status = self.status
-
-        if status in (300, 301, 302, 303, 307):
-            response.headers['Content-Type'] = "text/html;charset=utf-8"
-            # "The ... URI SHOULD be given by the Location field
-            # in the response."
-            response.headers['Location'] = self.urls[0]
-
-            # "Unless the request method was HEAD, the entity of the response
-            # SHOULD contain a short hypertext note with a hyperlink to the
-            # new URI(s)."
-            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]