Robert Brewer avatar Robert Brewer committed f1c00fb

Even more doc work.

Comments (0)

Files changed (21)

cherrypy/__init__.py

 from cherrypy import _cplogging
 
 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 as appropriate."""
         # Do NOT use try/except here. See http://www.cherrypy.org/ticket/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 as appropriate."""
         try:
             return request.app.log.access()
         except AttributeError:

cherrypy/_cplogging.py

-"""CherryPy logging."""
+"""
+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).
+
+
+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
     """
     
     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)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
                     h.release()
     
     def error(self, msg='', context='', severity=logging.INFO, traceback=False):
-        """Write to the error log.
+        """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``.
         """
         if traceback:
             msg += _cperror.format_exc()
         self.error_log.log(severity, ' '.join((self.time(), context, msg)))
     
     def __call__(self, *args, **kwargs):
-        """Write to the error log.
-        
-        This is not just for errors! Applications may call this at any time
-        to log application-specific information.
-        """
+        """An alias for ``error``."""
         return self.error(*args, **kwargs)
     
     def access(self):
         """Write to the access log (in Apache/NCSA Combined Log format).
         
+        See http://httpd.apache.org/docs/2.0/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
             if getattr(h, "_cpbuiltin", None) == key:
                 return h
     
-    
     # ------------------------- Screen handlers ------------------------- #
     
     def _set_screen_handler(self, log, enable, stream=None):
         self._set_screen_handler(self.error_log, newvalue, stream=sys.stderr)
         self._set_screen_handler(self.access_log, newvalue, stream=sys.stdout)
     screen = property(_get_screen, _set_screen,
-                      doc="If True, error and access will print to stderr.")
-    
+        doc="""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.
+        """)
     
     # -------------------------- File handlers -------------------------- #
     
     def _set_error_file(self, newvalue):
         self._set_file_handler(self.error_log, newvalue)
     error_file = property(_get_error_file, _set_error_file,
-                          doc="The filename for self.error_log.")
+        doc="""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.
+        """)
     
     def _get_access_file(self):
         h = self._get_builtin_handler(self.access_log, "file")
     def _set_access_file(self, newvalue):
         self._set_file_handler(self.access_log, newvalue)
     access_file = property(_get_access_file, _set_access_file,
-                           doc="The filename for self.access_log.")
-    
+        doc="""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.
+        """)
     
     # ------------------------- WSGI handlers ------------------------- #
     
     def _set_wsgi(self, newvalue):
         self._set_wsgi_handler(self.error_log, newvalue)
     wsgi = property(_get_wsgi, _set_wsgi,
-                      doc="If True, error messages will be sent to wsgi.errors.")
+        doc="""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.
+        """)
 
 
 class WSGIErrorHandler(logging.Handler):

cherrypy/_cpreqbody.py

 """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.
+   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 'request.body.processors' dict. If the full media
+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 (self.default_proc). For most
+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.
 
 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 'charset' attribute
+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.
 
 each part.
 
 For both the full entity and multipart parts, a Content-Disposition header may
-be used to fill .name and .filename attributes on the request.body or the Part.
+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(u"Content-Length", u""):
+                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[u'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 = {u'application/json': json_processor}
+
+Note that you can only replace the ``processors`` dict wholesale this way, not update the existing one.
 """
 
 import re
 
 
 class Entity(object):
-    """An HTTP request body, or MIME multipart body."""
+    """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[u'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 = [u'utf-8']
+    """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 = u'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
     """
     the 'before_request_body' and 'before_handler' hooks (assuming that
     process_request_body is True)."""
     
-    default_content_type = u'application/x-www-form-urlencoded'
-    # 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 = [u'utf-8']
     processors = {u'application/x-www-form-urlencoded': process_urlencoded,
                   u'multipart/form-data': process_multipart_form_data,
                   u'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
                     self.filename = self.filename[1:-1]
     
     # The 'type' attribute is deprecated in 3.2; remove it in 3.3.
-    type = property(lambda self: self.content_type)
+    type = property(lambda self: self.content_type,
+        doc="""A deprecated alias for :attr:`content_type<cherrypy._cpreqbody.Entity.content_type>`.""")
     
     def read(self, size=None, fp_out=None):
         return self.fp.read(size, fp_out)
         return fp_out
     
     def make_file(self):
-        """Return a file into which the request body will be read.
+        """Return a file-like object into which the request body will be read.
         
-        By default, this will return a TemporaryFile. Override as needed."""
+        By default, this will return a TemporaryFile. Override as needed.
+        See also :attr:`cherrypy._cpreqbody.Part.maxrambytes`."""
         return tempfile.TemporaryFile()
     
     def fullvalue(self):
             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.
 class Part(Entity):
     """A MIME part entity, part of a multipart entity."""
     
-    default_content_type = u'text/plain'
     # "The default character set, which must be assumed in the absence of a
     # charset parameter, is US-ASCII."
     attempt_charsets = [u'us-ascii', u'utf-8']
+    """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 = u'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)
             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()
 
 
 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 http://www.cherrypy.org/ticket/790
     default_content_type = u''
+    """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>`).
+    """
     
-    bufsize = 8 * 1024
     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)
         self.request_params = request_params
     
     def process(self):
-        """Include body params in request params."""
+        """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."

cherrypy/_cprequest.py

     cookie = SimpleCookie()
     """See help(Cookie)."""
     
-    body = None
-    """See help(cherrypy.request.body)"""
-    
     rfile = None
     """
     If the request included an entity (body), it will be available
     body = None
     """
     If the request Content-Type is 'application/x-www-form-urlencoded'
-    or multipart, this will be None. Otherwise, this will contain the
-    request entity body as an open file object (which you can .read());
-    this value is set between the 'before_request_body' and 'before_handler'
-    hooks (assuming that process_request_body is True)."""
+    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 'before_request_body' and 'before_handler' hooks (assuming that
     process_request_body is True).
     
-    Deprecated in 3.2, will be removed for 3.3""")
+    Deprecated in 3.2, will be removed for 3.3 in favor of
+    :attr:`request.body.params<cherrypy._cprequest.RequestBody.params>`.""")
 
 
 class ResponseBody(object):

cherrypy/lib/caching.py

+"""
+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 threading
 import time
 
 
 class Cache(object):
-    """Blank class
-    """
+    """Base class for Cache implementations."""
+    
     def get(self):
-        """Raises NotImplemented"""
+        """Return the current variant if in the cache, else None."""
         raise NotImplemented
     
     def put(self, obj, size):
-        """Raises NotImplemented"""
+        """Store the current variant in the cache."""
         raise NotImplemented
     
     def delete(self):
-        """Raises NotImplemented"""
+        """Remove ALL cached variants of the current resource."""
         raise NotImplemented
     
     def clear(self):
-        """Raises NotImplemented"""
+        """Reset the cache to its initial, empty state."""
         raise NotImplemented
 
 
 
 
 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.
     "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.
+    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.
+    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.cursize = 0
     
     def expire_cache(self):
-        """Runs in a separate thread which the servers are
-        not aware of. It's possible that "time" will be set to None
-        arbitrarily, so we check "while time" to avoid exceptions.
+        """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()
 
 
 def tee_output():
+    """Tee response output to cache storage. Internal."""
     # Used by CachingTool by attaching to request.hooks
     
     request = cherrypy.serving.request

cherrypy/process/plugins.py

 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
     
         USR1: bus.graceful
     """
     
-    # Map from signal numbers to names
+    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
         self.bus.publish(signame)
     
     def handle_SIGHUP(self):
+        """Restart if daemonized, else exit."""
         if os.isatty(sys.stdin.fileno()):
             # not daemonized (may be foreground or background)
             self.bus.log("SIGHUP caught but not daemonized. Exiting.")
 class Daemonizer(SimplePlugin):
     """Daemonize the running script.
     
-    Use this with a Web Site Process Bus via:
-        
+    Use this with a Web Site Process Bus via::
+    
         Daemonizer(bus).subscribe()
     
     When this component finishes, the process is completely decoupled from
 
 
 class Monitor(SimplePlugin):
-    """WSPBus listener to periodically run a callback in its own thread.
+    """WSPBus listener to periodically run a callback in its own thread."""
     
-    bus: a Web Site Process Bus object.
-    
-    callback: the function to call at intervals.
-    
-    frequency: the time in seconds between callback runs.
-    """
+    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)
 
 
 class Autoreloader(Monitor):
-    """Monitor which re-executes the process when files change."""
+    """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 = {}
     start.priority = 70 
     
     def sysfiles(self):
-        """Return a Set of filenames which the Autoreloader will monitor."""
+        """Return a Set of sys.modules filenames to monitor."""
         files = set()
         for k, m in sys.modules.items():
             if re.match(self.match, k):
     '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)

sphinx/source/_static/cpdocmain.css

 tt {
     background-color: #ecf0f3;
     padding: 0 1px 0 1px;
-    font-size: 0.95em;
 }
 
 .warning tt {

sphinx/source/index.rst

-.. _toc:
+.. toctree::
+   :hidden:
+
+   intro/index
+   progguide/index
+   deployguide/index
+   refman/index
+   appendix/index
+
 
 ********************************
 CherryPy |release| Documentation

sphinx/source/intro/concepts/config.rst

+:tocdepth: 3
 *************
 Configuration
 *************

sphinx/source/intro/whycherrypy.rst

 
 Check out the :doc:`/intro/concepts/index` and :doc:`/progguide/index` for
 more complete documentation.
-la
+

sphinx/source/progguide/REST.rst

 
 From the canonical `Roy Fielding dissertation <http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5>`_ :
 
-    REST is defined by four interface constraints: identification of resources; manipulation of resources through representations; self-descriptive messages; and, hypermedia as the engine of application state
+    REST is defined by four interface constraints: identification of resources;
+    manipulation of resources through representations; self-descriptive messages;
+    and, hypermedia as the engine of application state
 
-This section covers each of these four contstraints and demonstrates how each is applied in a CherryPy implementation.
+This section covers each of these four contstraints and demonstrates how each
+is applied in a CherryPy implementation.
 
 Identification of Resources
 ---------------------------
  - POST passes information to a resource to use at it sees fit,
  - DELETE removes resources.
 
-The default dispatcher in CherryPy stores the HTTP method name in the
-thread-local :class:`Request Object<cherrypy._cprequest.Request>`.
+The default dispatcher in CherryPy stores the HTTP method name at
+:attr:`cherrypy.request.method<cherrypy._cprequest.Request.method>`.
 
 Because HTTP defines these invocation methods, the most direct
 way to implement REST using CherryPy is to utilize the
 Self-Descriptive Messages
 -------------------------
 
+REST enables powerful clients and intermediaries by requiring messages to be
+self-descriptive; that is, everything you need to know about a message is
+carried within the message itself, either in headers or within the definition
+of the message's declared media type.
+
+CherryPy gives you easy access to the headers. It's as simple as
+:attr:`cherrypy.request.headers<cherrypy._cprequest.Request.headers>` and
+:attr:`cherrypy.response.headers<cherrypy._cprequest.Response.headers>`!
+Each is a normal Python dictionary which you can read and write as you like.
+They also have additional functions to help you parse complex values according
+to the HTTP spec.
+
+CherryPy also allows you to set whatever response Content-Type you prefer,
+just like any other response header. You have complete control. When reading
+request entities, you can register :ref:`custombodyprocessors` for different
+media types.
+
 Hypermedia as the Engine of Application State
 ---------------------------------------------
 
 REST is designed as a stateless protocol--all application state is
 maintained with the application at the client. Thus, concepts such as a
-"session" need not be maintained by the server. CherryPy by design does
-not enable sessions, so the default configuration is well-suited to the
-RESTful style.
+"session" need not be maintained by the server. CherryPy does not enable
+sessions by default, so it is well-suited to the RESTful style.
 
 In order for the client to maintain meaningful state, however, the REST
 server implementer must provide meaningful URIs which supply semantic

sphinx/source/progguide/autoreloader.rst

-****************
-Reload processes
-****************
-
-The autoreload :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 ``autoreloader.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` plugins, the autoreload plugin takes a
-``frequency`` argument. The default is 1 second; that is, the autoreloader
-will examine files each second.
-

sphinx/source/progguide/caching.rst

-:tocdepth: 1
-
-*******
-Caching
-*******
-
-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
-=====
-
-::
-
-    [/]
-    tools.caching.on = True
-
-Other configuration options:
-
-* **tools.caching.cache_class**: a class that implements the basic ``get`` and
-  ``put`` methods. The standard {{{MemoryCache}}} stores the data in memory,
-  using a standard Python dict. Provide this entry to implement custom caching
-  repositories; for example on disk, or in a database.
-* **tools.caching.key**: this defaults to the browser URL (including the querystring).
-* **tools.caching.delay**: time in seconds until the cached content expires;
-  defaults to 600 (10 minutes).
-* **tools.caching.maxobjsize**: max size of each cached object in bytes;
-  defaults to 100000 bytes (100 KB).
-* **tools.caching.maxsize**: max size of the entire cache in bytes;
-  defaults to 10000000 (10 MB).
-* **tools.caching.maxobjects**: max number of cached objects; defaults to 1000.
-

sphinx/source/progguide/exceptions.rst

 (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.
 
-*********************
 Custom Error Handling
-*********************
+=====================
 
 .. image:: 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
 
 
 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` to set

sphinx/source/progguide/extending/customplugins.rst

 The optional priority (0 - 100) allows multiple listeners to run in the correct
 order. Lower numbers run first. The default is 50.
 
+If you omit the priority argument to engine.subscribe (or pass ``None``),
+you can instead set it as an attribute on the callback function::
+
+    def setup_db():
+        ....
+    setup_db.priority = 90
+    engine.subscribe('start', setup_db)
+
 
 Plugins
 =======
         def start(self):
             self.fname = 'myapp_%d.db' % os.getpid()
             self.db = sqlite.connect(database=self.fname)
+        start.priority = 80
         
         def stop(self):
             self.db.close()

sphinx/source/progguide/index.rst

 Programmer's Guide
 ******************
 
+When you build a web application, you need more than just headers and bodies.
+Here are a number of discussions on how to add higher-level features to your
+CherryPy application.
+
 .. toctree::
    :maxdepth: 1
    :glob:

sphinx/source/progguide/logging.rst

-
-Logging
-*******
-
-CherryPy 3 uses the :mod:`logging` module from Python's standard library.
-
-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 ``cherrypy.log(msg, context='', severity=logging.DEBUG, traceback=False)``.
-
-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 ``cherrypy.log``, and the log manager for each application is found at ``app.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 `tree.mount()` or, if you used `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).
-
-Log Manager Objects
--------------------
-
-Each log manager possesses the following attributes:
-
- * access(): writes to the access log in `Apache/NCSA Combined Log Format <http://httpd.apache.org/docs/2.0/logs.html#combined>`_. CherryPy calls this automatically for you. Note there are no arguments; it collects the data itself from ``cherrypy.request``.
- * access_file: 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.
- * access_log: the actual ``logging.Logger`` instance for access messages.
- * appid: the id of the Application object which owns this log manager. If this is a global log manager, appid is None.
- * ``__call__``: an alias for ``error`` (see next).
- * ``error(msg='', context='', severity=logging.DEBUG, traceback=False)``: write ''msg'' to the error log. If ''traceback'' is True, the traceback of the current exception (if any) will be appended to ''msg''.
- * error_file: 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.
- * error_log: the actual ``logging.Logger`` instance for error messages.
- * logger_root: the "top-level" logger name ("<logger_root>.error.<appid>"). Defaults to "cherrypy".
- * screen: a boolean. If you set this to True, it'll add the appropriate ``StreamHandler(sys.stdout)`` for you. If you set it to False, it will remove the handler.
- * time(): returns now() in Apache Common Log Format (no timezone).
- * wsgi: a boolean. If you set this to True, it'll add the appropriate ``WSGIErrorHandler`` for you (which writes errors to ``wsgi.errors``). If you set it to False, it will remove the handler.
-
-
-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).
-

sphinx/source/progguide/requestbodies.rst

-**************
-Request Bodies
-**************
-
-.. versionadded:: 3.2
-
-Application authors have complete control over the
-parsing of HTTP request entities. In short, ``cherrypy.request.body`` is now always
-set to an instance of ``_cpreqbody.RequestBody``, and *that* class is a subclass
-of ``_cprequest.Entity``.
-
-Entity
-======
-
-The ``Entity`` 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 ``entity.parts``.
-
-Between the ``before_request_body`` and ``before_handler`` tools, CherryPy tries to
-process the request body (if any) by calling ``request.body.process()`` . This uses
-the ``content_type`` of the Entity to look up a suitable processor in ``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 ``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 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 ``request.body.parts`` . You can
-enable it with::
-
-    cherrypy.request.body.processors[u'multipart'] = _cpreqbody.process_multipart
-
-in an ``on_start_resource`` tool.
-
-
-Entity Attributes
------------------
-
- * attempt_charsets: 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 ``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: the successful decoding; see "attempt_charsets" above.
- * content_type: the value of the Content-Type request header, or, if the
-   Entity is part of a multipart payload, the Content-Type given in the MIME
-   headers for this part.
- * default_content_type: This defines a default ``Content-Type`` to use
-   if no Content-Type header is given. The empty string is used for
-   the ``request.body``, 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" (the built-in ``Part`` class does that).
- * default_proc: a method which is run if a more-specific processor cannot be
-   found for the given ``Content-Type``.
- * filename: if the entity (or part) bears a ``Content-Disposition`` header, its
-   filename parameter (if any) is stored here.
- * fp: the readable socket file object.
- * fullvalue: a method which returns the ``Entity`` as a string, regardless of
-   whether it is stored in a file or not.
- * headers: 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: the value of the ``Content-Length`` header, if provided.
- * make_file: a method which returns a file-like object in which to dump the
-   entity. By default, this is ``tempfile.TemporaryFile()`` . But see ``Part.maxrambytes``, below.
- * name: the "name" parameter of the ``Content-Disposition`` header, if any.
- * params: processors for some ``Content-Type`` (e.g.
-   'application/x-www-form-urlencoded' or 'multipart') attempt to parse these
-   formats into a dict of params. 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).
- * parts: a list of sub-Entity instances if the ``Content-Type`` is of major type
-   "multipart".
- * part_class: the class used for multipart parts. You can replace this with
-   custom subclasses to alter the processing of multipart parts.
- * processors: see discussion above.
- * type: a deprecated alias for ``content_type``.
-
-The ``request.body`` adds a couple more (which, like any ``request.body`` attribute, you can set in config):
-
- * bufsize: The buffer size used when reading the socket.
- * maxbytes: If more than ``maxbytes`` bytes are read from the socket, then ``MaxSizeExceeded`` is raised.
-
-The Part subclass is used for multipart parts, and adds the following attributes:
-
- * boundary: the MIME multipart boundary.
- * maxrambytes: the threshold of bytes after which point the ``Part`` will store
-   its data in a file (generated by the ``make_file`` method) instead of a string.
-   Defaults to 1000, just like the ``cgi`` module in Python's standard library.
-
-Custom Processors
-=================
-
-You can add your own processors for any specific or major MIME type. Simply add
-it to the ``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(u"Content-Length", u""):
-                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[u'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 = {u'application/json': json_processor}
-
-Note that you can only replace the ``processors`` dict wholesale this way, not update the existing one.

sphinx/source/refman/_cpreqbody.rst

    :members:
 
 .. autoclass:: Part
+   :show-inheritance:
    :members:
+   :inherited-members:
 
 .. autoclass:: RequestBody
+   :show-inheritance:
    :members:
+   :inherited-members:
 
 .. autoclass:: SizedReader
    :members:

sphinx/source/refman/cherrypy.rst

 .. autoclass:: TimeoutError
    :members:
 
-.. autoclass:: TimeoutError
-   :members:
-
-.. autoclass:: _GlobalLogManager
-   :members:
-   :inherited-members:
-
 
 Functions
 =========
 
 .. autofunction:: expose
 
+.. autofunction:: log
+
 .. autofunction:: quickstart
 
 .. autofunction:: url

sphinx/source/refman/process/plugins.rst

 Classes
 =======
 
-.. autoclass:: SimplePlugin
+.. autoclass:: SignalHandler
    :members:
 
-.. autoclass:: SignalHandler
+SimplePlugins
+-------------
+
+.. autoclass:: SimplePlugin
    :members:
 
 .. autoclass:: DropPrivileges
    :members:
+   :show-inheritance:
 
 .. autoclass:: Daemonizer
    :members:
+   :show-inheritance:
 
 .. autoclass:: PIDFile
    :members:
+   :show-inheritance:
+
+.. autoclass:: ThreadManager
+   :members:
+   :show-inheritance:
+
+Monitors
+--------
+
+.. autoclass:: BackgroundTask
+   :members:
 
 .. autoclass:: PerpetualTimer
    :members:
 
 .. autoclass:: Monitor
    :members:
+   :show-inheritance:
 
 .. autoclass:: Autoreloader
    :members:
+   :inherited-members:
+   :show-inheritance:
 
-.. autoclass:: ThreadManager
-   :members:
-
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.