Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

CherryPy for CGI programmers

Replace your CGI scripts and build powerful web applications with this simple Python framework

Leonard Richardson (leonardr@segfault.org), Software Engineer, CollabNet
Leonard Richardson is the author of many Python applications and libraries, including NewsBruiser and Beautiful Soup. He is a co-author of the new tome Beginning Python, from Wrox.

Summary:  The CherryPy application framework for Python makes web applications easier to write than plain Common Gateway Interface (CGI). At the same time, it's simple -- not full of little-used features -- and easy to learn. This introduction shows everything needed to write web applications with CherryPy.

Date:  16 Aug 2005
Level:  Introductory
Also available in:   Japanese

Activity:  12251 views
Comments:  

For more than 10 years, web programmers have used the Common Gateway Interface (CGI) to connect applications to web servers and the web browsers on the other side. CGI has much to recommend it: It can be used with any programming language, and it's supported nearly universally across web servers and hosting services. Unfortunately, CGI also has serious drawbacks. The interface between the web server and a CGI script is somewhat convoluted. In addition, web servers spawn a separate process for every CGI request, which means poor performance and no persistence across requests.

Over the years, dissatisfied hackers have created alternate ways to bridge this gap between web servers and application code. In recent years, some popular ways of doing this have included Java™ servlets, the Ruby on Rails framework, and the Apache modules mod_perl and mod_python.

These bridges are so numerous that picking one can be difficult, and the problem of overabundance is especially acute in the Python world. Some of the server application bridges are full-fledged application frameworks, with their own templating systems, authentication services, object-relational mappers, and other such features. With so many choices offering so many features to learn, it's not surprising that programmers with no free time stick to what they already know.

This article introduces CherryPy, a simple but very usable web framework for Python. All it does is connect the web server to your Python code with as little fuss as possible. It doesn't make decisions about what other tools to use, so you're free to pick a templating system, database mapper, or other tool on its own terms. I'll show you how to write applications that use CherryPy. The article assumes you have some knowledge of Python and of how HTTP requests and responses work.

A CherryPy request

Instead of relying on Apache or another web server, CherryPy runs its own small Python-based web server. A traditional web server creates a web space out of a tree of directories disk, but the CherryPy server creates its web space out of a tree of Python objects.

Consider a request for the URL http://localhost:8080/hello/. In a traditional web server, this URL corresponds to the hello/ directory beneath the root of the web space. When you access it with a web browser, the web server reads the hello/ directory's index.html file or invokes that directory's index.cgi script as a CGI and sends you the output.

Instead of serving a web space rooted in a directory on disk, the CherryPy web server serves a web space rooted at a particular Python object, cpg.root. Each method and member of that object is accessible by tacking its name onto the root URL. Therefore, the hello/ URL corresponds in CherryPy to the hello member of cpg.root.

Serve a request by defining a method

If cpg.root.hello is a method, CherryPy calls that method, and the output of the method is sent to the web browser. The following code defines an object that exposes a hello method:


Listing 1. Defining an object that exposes a hello method

#!/usr/bin/env python
from cherrypy import cpg

class Application:
    @cpg.expose
    def hello(self):
        return "Hello, world!"
    
cpg.root = Application()
cpg.server.start()

Run this script through Python, and the CherryPy web server will start up. As long as you keep the script running, you can access http://localhost:8080/hello/ and be served the string Hello, world!. (Of course, this assumes you don't already have a server running on port 8080.) If you visit any other URL, including the web server root, you'll get a CherryPy error; /hello/ is the only URL this application knows how to serve.

Serve a request by defining an object tree

If cpg.root.hello is an object instead of a method, when a user accesses /hello/, CherryPy calls the hello object's index() method. This code serves the /hello/ URL the same way as the previous example:


Listing 2. CherryPy calls hello object's index() method

#!/usr/bin/env python
from cherrypy import cpg

class HelloWorld:
    @cpg.expose
    def index(self):
        return "Hello, world!"

class Application:
    hello = HelloWorld()
    
cpg.root = Application()
cpg.server.start()

When you visit the /hello/ URL, your request is mapped to the object cpg.root.hello, and its default method (index()) is called to handle the request.


Expose objects and methods

What's the point of the @cpg.expose decorator on the hello and index methods? It tells CherryPy that it's OK to call that method in response to a web request.

In a website that serves static files, it's assumed that every file and directory in the web space is intended for public consumption. There are exceptions, though. For instance, Apache won't serve hidden files, such as .htaccess, on UNIX® systems.

When you expose an object tree as a CherryPy URL space, the assumption goes the other way: Unless you explicitly label a method as exposed, it isn't served to outside users. Think of the distinction between public and private class members in many programming languages (and enforced unofficially by the _method() convention in Python). A well-designed CherryPy class will probably have only a few public methods exposed to web clients, but it may have many internal methods a client shouldn't be allowed to access directly.

I think the decorator is the best way to expose a method to CherryPy, but you can also expose a method by setting its exposed member to True:


Listing 3. Expose a method by setting its exposed member to True

class HelloWorld:
    def index(self):
        return "Hello, world!"
    index.exposed = True

Decorators don't exist in versions of Python prior to version 2.4, so the exposed trick may be the only way for you to expose methods to CherryPy.


Gather user input

When a user submits a form or otherwise provides information to a CGI script, the web browser gathers that information and passes it to the script through environment variables. It's the script's responsibility to parse and make sense of this information, even though a set of conventions governs the proper format and use of this information. CherryPy uses these informal conventions to eliminate several steps of this process, providing user input as the arguments to the Python method it decides to call.

Turn a query string into keyword arguments

Consider a URL that has query arguments, like http://localhost:8080/hello/?what=hello&who=world. The user may have clicked this URL or submitted it as the result of filling out an HTML form. A traditional CGI-based web server would pass what=hello&who=world to the CGI script in the QUERY_STRING environment variable. The CGI script would be responsible for fetching that variable and parsing the string. Python's cgi module does the parsing for you. But with CherryPy, you don't have to do anything. The CherryPy web server automatically transforms a URL's query string into a set of keyword arguments.


Listing 4. CherryPy web server automatically transforms URL query string into set of keyword arguments

class Application:
    @cpg.expose
    def hello(self, what='Hello', who='world'):
        return '%s, %s!' % (what, who)

When a user hits the /hello/ URL, CherryPy turns any what and the who in the query string into arguments to the hello() method. The transition from URL to Python method call is totally transparent to you. It doesn't even matter whether the original HTTP request came in through the GET or the POST method.

Turn extra path portions into positional arguments

The other source of user input for CGI scripts is the PATH_INFO environment variable. It's commonly used to make a web application's URLs look more like real web pages. For instance, consider the URL http://localhost:8080/hello/world/. If /hello/ designates a CGI script, then visiting /hello/world/ invokes that script with the PATH_INFO environment variable set to /world/.

In a CGI environment, it's the CGI script's responsibility to parse the extra path information. But as with query string arguments, CherryPy takes care of the parsing for you based on typical usage conventions. Whereas query string key-value pairs become keyword arguments in a CherryPy application, extra path info arguments become positional arguments to an object's default() method:


Listing 5. Extra path info arguments become positional arguments to an object's default() method

class Hello:
    @cpg.expose
    def default(self, who):
        return 'Hello, %s!' % who

class Application:
    hello = Hello()

cpg.root = Application()
cpg.server.start()

The hello/ portion of the /hello/world/ URL gets mapped to cpg.root.hello, an instance of Hello. The Hello object has no method or member object called world, so the world portion of the URL is passed in as a positional argument to that object's default() method.


Read headers from the request object

CherryPy takes care of parsing the URL the user requested and dispatching to a Python method with the appropriate arguments, but an HTTP request is more than just a URL. What about the incoming HTTP headers?

Your CherryPy methods have access to an object called cgp.request, which contains a lot of information about the user's HTTP request. The most interesting member of this object is requestMap, which contains all the incoming HTTP headers associated with a web request:


Listing 6. requestMap contains incoming HTTP headers associated with web request

class Application:
    @cpg.expose
    def index(self):
        items = [x + ': ' + y for x,y in cpg.request.headerMap.items()]
        return "<br />".join(items)

Run this application and visit http://localhost:8080/, and you'll see a listing of all the HTTP headers your browser sent along with its request.


Write to the response object

As with the HTTP request, so with the response. A CherryPy application method typically returns the body of the response as a string, but sometimes you need to set additional HTTP headers, do a redirect, or change the HTTP response code. You can do all of these things with the cpg.response object made available to each method.

cpg.response.headerMap is a map of outgoing HTTP headers, just as cpg.request.requestMap is a map of incoming headers:


Listing 7. cpg.response.headerMap is a map of outgoing HTTP headers

#!/usr/bin/env python
from cherrypy import cpg

class Application:
    @cpg.expose
    def setHeader(self, header, value):
        """Hit the '/setHeader?header=Value&foo=bar' URL to get a
	response in which the HTTP header "foo" has a value of
	"bar"."""
        cpg.response.headerMap[header] = value
        return 'Set HTTP response header "%s" to "%s"' % (header, value)

The HTTP status code is just an HTTP header called Status, so you can set it to 404, 503, or whatever other status you need:


Listing 8. Set HTTP header status

    @cpg.expose
    def forbidden(self):
        "Hit the '/forbidden' URL to be denied access."
        cpg.response.headerMap['Status'] = '503 Forbidden'
        return "You don't have permission to access this resource."
        

To do an HTTP redirect, you can manually set the Status and Location headers, or you can use the redirect method of CherryPy's httputils helper library, which does the same thing:


Listing 9. Use redirect method of CherryPy's httputils helper library

    @cpg.expose
    def redirect(self):
        "Hit the '/redirect' URL to be redirected."
        from cherrypy.lib import httptools
        httptools.redirect('./destination')

    @cpg.expose
    def destination(self):
        "This is where you end up if you hit the '/redirect' URL."
        from cherrypy.lib import httptools
        cpg.response.headerMap['Content-Type'] = 'text/plain'
        return 'Here is some plain text.'

cpg.root = Application()
cpg.server.start()


Keep persistent information in a session

Consider an application in which I can hit the URL /name/set?name=leonardr to set a bit of data. I can then hit the URL /name/show and be told, Your previously set name is leonardr. Because the second request used information from the first, both requests must have formed part of a single session. The string leonardr I sent on my first request was stored on the server somewhere. When I made my second request, I was somehow identified as the same person who made the first request, and the information I sent along with the first request was retrieved.

CherryPy hides most of this complexity and makes it easy to set up and use cookie-based sessions -- an important feature, but not one supported out of the box by CGI. You can store any Python object in the cpg.request.sessionMap map, and it will be there the next time the same user hits one of your pages. The following example works just like the application just described:


Listing 10. Store any Python object in the cpg.request.sessionMap map

#!/usr/bin/env python
from cherrypy import cpg

class Application:
    @cpg.expose
    def set(self, name):
        cpg.request.sessionMap['name'] = name
        return 'Set name to %s' % name

    @cpg.expose
    def show(self):
        return 'Your previously set name is %s.' % \
               cpg.request.sessionMap.get('name', '[none]')
               

Pretty simple so far. However, for this code to work, the CherryPy server needs to be set up to associate a session cookie with each HTTP response. Otherwise, CherryPy will never be able to associate two requests with the same session. You can do this configuration in a CherryPy server configuration file (see Resources), but it's easier to demonstrate it as part of the Python code that starts the CherryPy web server:


Listing 11. Part of Python code that starts CherryPy web server

cpg.root = Application()
cpg.server.start(configMap={'sessionStorageType' : 'ram',
                            'sessionCookieName' : 'CherryPySessionCookie',
                            'sessionTimeout' : 60}) #Session expires in an hour


Conclusion

CherryPy uses the same concepts as CGI to bind a web server to a web application, but it improves performance and gains persistence across requests by handling all its requests within a single process. Because it binds only to Python code, it doesn't need the often obscure information-passing techniques of CGI. This leads to fewer lines of more understandable code. CherryPy makes an excellent replacement for CGI and a good base on which to build Python web applications.



Download

DescriptionNameSizeDownload method
Sample applicationsos-CherryPy-sample-code.zip3 KBHTTP

Information about download methods


Resources

Learn

Get products and technologies

  • Visit the CherryPy website for downloads and documentation.

  • Visit the Python website for downloads and documentation.

About the author

Leonard Richardson is the author of many Python applications and libraries, including NewsBruiser and Beautiful Soup. He is a co-author of the new tome Beginning Python, from Wrox.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=91445
ArticleTitle=CherryPy for CGI programmers
publish-date=08162005