5 min read

Vocabulary

In order to avoid misunderstandings, we need to define a few key words that will be used.

Keyword

Definition

Web server

A web server is the interface dealing with the HTTP protocol. Its goal is to transform incoming HTTP requests into entities that are then passed to the application server and also transform information from the application server back into HTTP responses.

Application

An application is a piece of software that takes a unit of information, applies business logic to it, and returns a processed unit of information.

Application server

An application server is the component hosting one or more applications.

Web application server

A web application server is simply the aggregation of a web server and an application server into a single component.

CherryPy is a web application server.

Basic Example

To illustrate the CherryPy library we will go through a very basic web application allowing a user to leave a note on the main page through an HTML form. The notes will be stacked and be rendered in a reverse order of their creation date. We will use a session object to store the name of the author of the note.

CherryPy Essentials: Rapid Python Web Application Development

Each note will have a URI attached to itself, of the form /note/id.

CherryPy Essentials: Rapid Python Web Application Development

Create a blank file named note.py and copy the following source code.

#!/usr/bin/python
# -*- coding: utf-8 -*
# Python standard library imports
import os.path
import time
###############################################################
#The unique module to be imported to use cherrypy
###############################################################
import cherrypy
# CherryPy needs an absolute path when dealing with static data
_curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
###############################################################
# We will keep our notes into a global list
# Please not that it is hazardous to use a simple list here
# since we will run the application in a multi-threaded environment
# which will not protect the access to this list
# In a more realistic application we would need either to use a
# thread safe object or to manually protect from concurrent access
# to this list
###############################################################
_notes = []
###############################################################
# A few HTML templates
###############################################################
_header = """
<html>
<head>
<title>Random notes</<title>
<link rel="stylesheet" type="text/css" href="/style.css"></link>
</head>
<body>
<div class="container">"""
_footer = """
</div>
</body>
</html>"""
_note_form = """
<div class="form">
<form method="post" action="post" class="form">
<input type="text" value="Your note here..." name="text"
size="60"></input>
<input type="submit" value="Add"></input>
</form>
</div>"""
_author_form = """
<div class="form">
<form method="post" action="set">
<input type="text" name="name"></input>
<input type="submit" value="Switch"></input>
</form>
</div>"""
_note_view = """
<br />
<div>
%s
<div class="info">%s - %s <a href="/note/%d">(%d)</a></div>
</div>"""
###############################################################
# Our only domain object (sometimes referred as to a Model)
###############################################################
class Note(object):
def __init__(self, author, note):
self.id = None
self.author = author
self.note = note
self.timestamp = time.gmtime(time.time())
def __str__(self):
return self.note
###############################################################
# The main entry point of the Note application
###############################################################
class NoteApp:
"""
The base application which will be hosted by CherryPy
"""
# Here we tell CherryPy we will enable the session
# from this level of the tree of published objects
# as well as its sub-levels
_cp_config = { 'tools.sessions.on': True }
def _render_note(self, note):
"""Helper to render a note into HTML"""
return _note_view % (note, note.author,
time.strftime("%a, %d %b %Y %H:%M:%S",
note.timestamp),
note.id, note.id)
@cherrypy.expose
def index(self):
# Retrieve the author stored in the current session
# None if not defined
author = cherrypy.session.get('author', None)
page = [_header]
if author:
page.append("""
<div><span>Hello %s, please leave us a note.
<a href="author">Switch identity</a>.</span></div>"""
%(author,))
page.append(_note_form)
else:
page.append("""<div><a href="author">Set your
identity</a></span></div>""")
notes = _notes[:]
notes.reverse()
for note in notes:
page.append(self._render_note(note))
page.append(_footer)
# Returns to the CherryPy server the page to render
return page
@cherrypy.expose
def note(self, id):
# Retrieve the note attached to the given id
try:
note = _notes[int(id)]
except:
# If the ID was not valid, let's tell the
# client we did not find it
raise cherrypy.NotFound
return [_header, self._render_note(note), _footer]
@cherrypy.expose
def post(self, text):
author = cherrypy.session.get('author', None)
# Here if the author was not in the session
# we redirect the client to the author form
if not author:
raise cherrypy.HTTPRedirect('/author')
note = Note(author, text)
_notes.append(note)
note.id = _notes.index(note)
raise cherrypy.HTTPRedirect('/')
class Author(object):
@cherrypy.expose
def index(self):
return [_header, _author_form, _footer]
@cherrypy.expose
def set(self, name):
cherrypy.session['author'] = name
return [_header, """
Hi %s. You can now leave <a href="/" title="Home">notes</a>.
""" % (name,), _footer]
if __name__ == '__main__':
# Define the global configuration settings of CherryPy
global_conf = {
'global': { 'engine.autoreload.on': False,
'server.socket_host': 'localhost',
'server.socket_port': 8080,
}}
application_conf = {
'/style.css': {
'tools.staticfile.on': True,
'tools.staticfile.filename': os.path.join(_curdir,
'style.css'),
}
}
# Update the global CherryPy configuration
cherrypy.config.update(global_conf)
# Create an instance of the application
note_app = NoteApp()
# attach an instance of the Author class to the main application
note_app.author = Author()
# mount the application on the '/' base path
cherrypy.tree.mount(note_app, '/', config = application_conf)
# Start the CherryPy HTTP server
cherrypy.server.quickstart()
# Start the CherryPy engine
cherrypy.engine.start()

Following is the CSS which should be saved in a file named style.css and stored in the same directory as note.py.

html, body {
background-color: #DEDEDE;
padding: 0px;
marging: 0px;
height: 100%;
}
.container {
border-color: #A1A1A1;
border-style: solid;
border-width: 1px;
background-color: #FFF;
margin: 10px 150px 10px 150px;
height: 100%;
}
a:link {
text-decoration: none;
color: #A1A1A1;
}
a:visited {
text-decoration: none;
color: #A1A1A1;
}
a:hover {
text-decoration: underline;
}
input {
border: 1px solid #A1A1A1;
}
.form {
margin: 5px 5px 5px 5px;
}
.info {
font-size: 70%;
color: #A1A1A1;
}

In the rest of this article we will refer to the application to explain CherryPy’s design.

LEAVE A REPLY

Please enter your comment!
Please enter your name here