Abstract
CherryPy lets developers use Python to develop web applications, just as they would use Python for any other type of application. Building a web application with CherryPy is very straightforward and does not require the developer to change habits, or learn many features, before being able to produce a working application. This section will review the basic components which you will use to build a CherryPy application.Filters are one of the most important features of CherryPy. The CherryPy core can call user-defined functions at specific points during request processing; a filter is a class which defines those functions. Filters are designed to be called at a low level—the HTTP request/response level—and therefore should only be used in that context.
CherryPy comes with a set of built-in filters, but they're turned off by default. To enable them, you must use the configuration system as follows:
filterName.on = TrueExample 3.1. Turning on a default filter
[/entries/view]
tidyFilter.on = True
tidyFilter.tmpDir = "/tmp"
tidyFilter.strictXml = True
On the first line we define that the tidy filter will be used by the
core whenever the path /entries/view (or one of its sub-paths)
is called. On the two last lines we also define some parameters used by the
filter.
CherryPy lets you write your own filters as we will see in the
developer reference chapter. However, the way to use them is different from
the default filters. You do not declare custom filters within the
configuration file; instead, use the _cpFilterList attribute in
your source code:
Example 3.2. Using a non default filter
import cherrypy
from myfiltermodule import MyFilterClass
class Entry:
_cpFilterList = [ MyFilterClass() ]
def view(self, id):
# do suff...
view.exposed = True
class Root: pass
cherrypy.root = Root()
cherrypy.root.entries = Entry()
cherrypy.server.start()
As all objects below cherrypy.root.entries will inherit
the filter, there is no need to re-specify it in each
_cpFilterList underneath.
Keep in mind that the user-defined filters are called in the order you add them to the list.
The CherryPy configuration system provides fine-grained control over how each part of the application should react. You will use it for two reasons:
You will be able to declare the configuration settings either from a file or from a Python dictionary.
First of all, let's see how a typical configuration file is defined.
Example 3.3. Configuration file
# The configuration file called myconfigfile.conf
[global]
server.socketPort=8080
server.socketHost=""
server.socketFile=""
server.socketQueueSize=5
server.protocolVersion="HTTP/1.0"
server.logToScreen=True
server.logFile=""
server.reverseDNS=False
server.threadPool=10
server.environment="development"
[/service/xmlrpc]
xmlRpcFilter.on = True
[/admin]
sessionAuthenticateFilter.on=True
[/css/default.css]
staticFilter.on = True
staticFilter.file = "data/css/default.css"
# From your script...
cherrypy.config.update(file="myconfigfile.conf")
The settings can also be defined using a python dictionary instead of a file as follows:
Example 3.4. Configuration dictionary
settings = {
'global': {
'server.socketPort' : 8080,
'server.socketHost': "",
'server.socketFile': "",
'server.socketQueueSize': 5,
'server.protocolVersion': "HTTP/1.0",
'server.logToScreen': True,
'server.logFile': "",
'server.reverseDNS': False,
'server.threadPool': 10,
'server.environment': "development"
},
'/service/xmlrpc' : {
'xmlRpcFilter.on': True
},
'/admin': {
'sessionAuthenticateFilter.on' :True
},
'/css/default.css': {
'staticFilter.on': True,
'staticFilter.file': "data/css/default.css"
}
}
cherrypy.config.update(settings)
Each section of the configuration refers to a URL path; each path is
mapped to a published object of the tree handled by CherryPy. Therefore when
the server receives a request for /css/default.css, the static
filter will be called and the server will actually return the physical file
name data/css/default.css.
Since the path /service/xmlrpc has the XML-RPC filter
enabled, all the exposed methods of the object
cherrypy.root.service.xmlrpc will be treated as XML-RPC
methods.
The root entry, defined as global, is also responsible
for defining the server settings such as the port, the protocol version to
use by default, the number of threads to start with the server, etc.
All values in the configuration file must be valid Python values. Strings must be quoted, booleans must be True or False, etc.
The server.environment entry controls how CherryPy should
run. Three values are legal:
tracebacks are logged, but are not displayed in the browser
Abstract
CherryPy 2.1 includes a powerful sessions system provided via a new
sessionFilter.
First you need to enable the session filter through the
configuration system, by setting sessionFilter.on to
True. This gives you a variable called
cherrypy.session, which is a dictionary-like object
where you can read/store your session data. This dictionary always has a
special key called _id which contains the session
id.
Here is sample code showing how to implement a simple counter using sessions:
The following configuration options are available for "sessionFilter":
sessionFilter.on: True or
False (default): enable/disable sessions
sessionFilter.storageType: Specify which
storage type should be used for storing session data on the server.
Built-in types are Ram (default),
File and PostgreSQL (see Section 1.3.3, “Choosing the backend” for more info).
sessionFilter.storagePath: Specifies the directory
in which CherryPy puts the session files when sessionFilter.storageType is set
to File.
sessionFilter.timeout: The number of minutes of
inactivity before an individual session can be removed. It can be a
float (ex: 0.5 for 30 seconds). Defaults to 60.
sessionFilter.cleanUpDelay: Once in a while the
server cleans up old/expired sessions. This config option specifies
how often this clean up process should happen. The delay is in
minutes. Defaults to 5.
sessionFilter.cookieName: The name of the
cookie that CherryPy will use to store the session ID. Defaults to
sessionID.
sessionFilter.getDB: See the
PostgreSQL backend from Section 1.3.3, “Choosing the backend”.
sessionFilter.deadlockTimeout: See Section 1.3.5, “Handling concurrent requests for the same session data”.
sessionFilter.onCreateSession: See Section 1.3.6, “Being notified when sessions are created/deleted”.
sessionFilter.onDeleteSession: See Section 1.3.6, “Being notified when sessions are created/deleted”.
sessionFilter.storageClass: See Section 1.3.4, “Writing your own custom backend”.
CherryPy comes with multiple build-in backends for storing session data on the server side. They are:
Ram: All data is stored in RAM; this is the
fastest storage, but it means that the data will be lost if you
restart the server; and it also means that it won't scale to multiple
processes/machines
File: All data is stored on disk; this is a bit
slower than Ram storage, but the data will persist if you restart the
server. It also means that data can be shared amongst multiple
CherryPy processes, either on the same machine, or on multiple
machines if all machines have access to the same disk (for example,
via NFS).
PostgreSQL: This backend is included
with CherryPy to show how easy it is to implement your own custom
backend for the session system. All data is stored in a PostgreSQL
database; storing your data in a database is the recommend setup for
production if you have a very high traffic website and you need to scale
your site across multiple machines. To use this backend, you'll need to
create the following table in your PostgreSQL database:
create table session (
id varchar(40),
data text,
expiration_time timestamp
)
You also need to programmatically set the
sessionFilter.getDB config option to a function that
returns a DB connection.Ram backend, the session data is saved as soon as you stick it in cherrypy.session. So even if an error occurs later on in the page handler the data is still saved; this is not the case for the other backends.
By default, CherryPy comes with 3 built-in backends, but if you have specific needs, it is very easy to implement your own custom backend (for instance, another database, or an XML-RPC server, ...). To do so, all you have to do is write a class that implements the following methods:
class MyCustomBackend:
def save(self, id, data, expirationTime):
""" Save the session data and expirationTime for that session id """
def load(self, id):
""" Load the session data and expirationTime for 'id' and return
a tuple (data, expirationTime) (even if the session is
expired). Return None if id doesn't exist. """
def cleanUp(self):
""" Delete expired session data from storage and call
'onDeleteSession' for each deleted session id """
Note that if you want to use explicit
locking (see Section 1.3.5, “Handling concurrent requests for the same session data”), you also have to implement
two extra methods: acquireLock and
releaseLock.
Once you have written this class, you have to programmatically set
the sessionFilter.storageClass config option to this
class.
If you need help in writing your own custom backend it is a good
idea to look at how the current ones (ram, file and postgresql) are
implemented. They are implemented in the file
cherrypy/lib/filter/sessionfilter.py
It is normally quite rare to have two simultaneous requests with the same session ID. It means that a same browser is making 2 requests to your server at the same time (to dynamic pages ... static data like images don't have sessions). However, this case can happen (if you're using frames for instance), and it will happen more and more often as more and more people start using Ajax.
In that case, we need to make sure that access to the session data is serialized. This way, threads can't both modify the data at the same time and leave it in an inconsistent state.
You can easily make CherryPy serialize access to the session data by setting the sessionFilter.locking config option to implicit (the default is explicit, which means that CherryPy won't do any locking for you). In the implicit mode,
if a browser makes a second request while a first request is still being
handled by the server, the second request will block while the first
request is accessing the data. As soon as the first request is finished
then the second request will be able to access it.
This means that the second request will block until the first request is finished.
It is possible to configure the sessionFilter so
that it calls some special callback functions from your code when sessions
are being created/deleted. To do so you have to set the
sessionFilter.onCreateSession and
sessionFilter.onDeleteSession config options. When a
session is created/deleted, CherryPy will call these functions and pass
them the session data.
CherryPy is a low-level framework for building web applications, and thus does not offer high-level features such as an integrated templating system. This is quite a different point of view from many other web frameworks. CherryPy does not force you to use a specific templating language; instead, it allows you to plug in your favourite one as you see fit.
CherryPy works with all the main templating systems:
You will find recipes on how to use them on the CherryPy website.
Static content is now handled by a filter called "staticFilter" that
can easily be enabled and configured in your config file. For instance, if
you wanted to serve /style.css from
/home/site/style.css and /static/* from
/home/site/static/*, you can use the following
configuration:
Example 3.6. Static filter configuration
[global]
staticFilter.root = "/home/site"
[/style.css]
staticFilter.on = True
staticFilter.file = "style.css"
[/static]
staticFilter.on = True
staticFilter.dir = "static"
The staticFilter.root entry can be either absolute or
relative. If absolute, static content is sought within that absolute path.
Since CherryPy cannot guess where your application root is located, relative
paths are assumed to be relative to the directory where your
cherrypy.root class is defined (if you do not provide a root,
it defaults to "", and therefore to the directory of your
cherrypy.root class).
As an application developer, the design of your application affects whether you choose to use absolute or relative paths. If you are creating a one-off application that will only be deployed once, you might as well use absolute paths. But you can make multiple deployments easier by using relative paths, letting CherryPy calculate the absolute path each time for you. Absolute paths, however, give deployers the ability to place static content on read-only filesystems, or on faster disks.
Before version 2.1, CherryPy handled file uploads by reading the entire file into memory, storing it in a string, and passing it to the page handler method. This worked well for small files, but not so well for large files.
CherryPy 2.1 uses the python cgi module to parse the
POST data. When a file is being uploaded, the cgi module
stores it in a temp file and returns a FieldStorage instance
which contains information about this file. CherryPy then passes this
FieldStorage instance to the method. The
FieldStorage instance has the following attributes:
file: the file(-like) object from which you can
read the datafilename: the client-side filenametype: the content-type of the file