Chapter 3. Reference

1. Application developer reference

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.

1.1. Filters

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:

  • First you must decide where to enable the filter. CherryPy maintains a tree of published objects; you must decide which branch should use the filter. The filter will then apply to that branch and all its children in the tree. Remember that the tree is accessed as a path and then mapped internally by the core to match the correct exposed object.
  • Second in the config file you must turn the filter on like this : filterName.on = True

Example 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.

1.2. Configuration system overview

The CherryPy configuration system provides fine-grained control over how each part of the application should react. You will use it for two reasons:

  • Web server settings
  • Enabling filters per path

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:

  • development
    • logDebugInfoFilter is enabled by default
    • HTTPErrors (and therefore the default _cpOnError) display tracebacks in the browser if errors occur
    • autoreload is enabled by default
  • production
    • logDebugInfoFilter is disabled by default
    • tracebacks are logged, but are not displayed in the browser

    • autoreload is disabled by default
  • staging (same as production for the moment)

1.3. Session Management

Abstract

CherryPy 2.1 includes a powerful sessions system provided via a new sessionFilter.

1.3.1. Using Sessions

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:

Example 3.5. 

            import cherrypy
            class Root:
                def index(self):
                    count = cherrypy.session.get('count', 0) + 1
                    cherrypy.session['count'] = count
                    return 'Counter: %s' % count
                index.exposed = True

            cherrypy.config.update({'sessionFilter.on': True})
            cherrypy.root = Root()
            cherrypy.server.start()
            

1.3.2. Configuring sessions

The following configuration options are available for "sessionFilter":

1.3.3. Choosing the 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.
Note that when using the 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.

1.3.4. Writing your own custom backend

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

1.3.5. Handling concurrent requests for the same session data

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.

1.3.6. Being notified when sessions are created/deleted

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.

1.4. Templating language independent

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.

1.5. Static content handling

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.

1.6. File upload

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 data
  • filename: the client-side filename
  • type: the content-type of the file

1.7. Exceptions and Error Handling

Unhandled exceptions raised inside CherryPy applications result in a call to the _cpOnError function. HTTPError and HTTPRedirect exceptions do not result in calls to _cpOnError. HTTPError exceptions force the server to set response status and return an error page.