Overview | Getting started | Documentation | Generated API documentation

Basic concepts

Sample application

Here's a simple web application giving an overview of the functions provided by pesto. To run this, copy this code into a file called recipes.py and run the file by typing python recipes.py:

#!/usr/bin/python

from wsgiref import simple_server

import pesto
from pesto import Response
despatcher = pesto.urldespatcher()

recipes = {
    'baked-beans' : "Open a tin of baked beans. Put into a saucepan, heat and serve.",
    'toast'       : "Put a slice of bread under a grill for 2-3 minutes, turning once. Serve.",
}

@despatcher.match('/', 'GET')
def recipe_index(request):
    """
    Display an index of available recipes.
    """

    return Response(
        ['<html><body><h1>List of recipes</h1><ul>']
        + [ '<li><a href="%s">%s</a></li>' % (show_recipe.url(recipe=r), r) for r in recipes ]
        + ['</ul></body></html>']
    )

@despatcher.match('/recipes/recipe:.*', 'GET')
def show_recipe(request, recipe):
    """
    Display a single recipe
    """
    return [
        '<html><body><h1>How to make %s</h1>' % recipe,
        '<p>%s</p><a href="%s">Back to index</a>' % (recipes[recipe], recipe_index.url()),
        '</body></html>'
    ]

if __name__ == "__main__":
    app = pesto.despatcher_app(despatcher)
    app = pesto.to_wsgi(app)
    httpd = simple_server.make_server('', 8080, app)
    httpd.serve_forever()

Pesto handlers

Pesto handlers are at the heart of the pesto library. The basic signature of a handler is:

def my_handler(request):
    return Response(["<h1>Whoa Nelly!</h1>"])

Handlers must accept a request object and must return a pesto.response.Response object. The Response object should contain an iterable payload. In the example above the payload is HTML, but any data can be returned. For example, the following are all valid Response objects:

# Simple textual response
return Response(['ok'])

# Efficient iterator over a data file
with open('data.gif', 'r') as img:
    return Response(
        iter(partial(img.read, 8192), None),
        content_type = 'image/gif'
    )

# Iterator over database query
def format_results(cursor):
    for row in iter(cursor.fetchone, None):
        yield '<tr><th>%s</th><td>%d</td></tr>' % row
return Response(format_results(cursor))

The request object

The request object gives access to all server variables and submitted form data. Form values are accessed in a dictionary like fashion:

request.get("email")
request["email"]

For more control over the submitted values, you can access request.form, a dictionary-style wrapper around python's cgi.FieldStorage. This provides the usual dictionary access, for example:

>>> submitted_items = request.form.keys()
>>> request.form['foo'] = 'bar'
>>> request.form.getfirst("name")
"Fred"
>>> request.form.getlist("name")
["Fred", "Ginger"]

Note that the underlying cgi.FieldStorage objects may be accessed via request.getstorage. This provides an easier way to access the lower-level API offered by cgi, useful for processing file uploads:

>>> storage = request.getstorage('fileupload')
>>> storage.filename
'uploaded.txt'
>>> storage.file
<cStringIO.StringO object at ...>
>>> storage.headers
<rfc822.Message instance at ...>

CGI server variables are exposed as attributes:

>>> request.document_root
'/home/fred/public_html'
>>> request.server_name
'www.example.com'

The response object

The response object allows you to set headers and provides shortcuts for common handler responses, such as redirection.

Constructing response objects

Headers can be supplied as keyword arguments:

>>> Response(
...     status = 405, # method not allowed
...     content_type = 'text/html',
...     allow = ['GET', 'POST'],
...     content = ['<html><body>Sorry, that method is not allowed</body></html>']
... )

Headers can be supplied as a list of tuples:

>>> Response(
...     status = 405, # method not allowed
...     headers = [ ('Content-Type', 'text/html'), ('Allow', 'GET'), ('Allow', 'POST') ]
...     content = ['<html><body>Sorry, that method is not allowed</body></html>']
... )

Or a mixture of the two.

Headers may be added, either singly:

>>> r = Response(content = ['Whoa nelly!'])
>>> r.headers
[]
>>> r = r.add_header('Content-Type', 'text/plain')
>>> r.headers
[('Content-Type', 'text/plain')]

or in groups:

>>> r = Response(content = ['Whoa nelly!'])
>>> r.headers
[]
>>> r = r.add_headers([('Content-Type', 'text/plain'), ('Cache-Control', 'Private')])
>>> r.headers
[('Content-Type', 'text/plain'), ('Cache-Control', 'Private')]
>>> r = r.add_headers(x_powered_by='pesto')
>>> r.headers
[('Content-Type', 'text/plain'), ('Cache-Control', 'Private'), ('X-Powered-By', 'Pesto')]

Removing and replacing headers is the same. See the API documentation for pesto.response.Response for details.

Common responses

Error responses

Many error responses are available as exceptions for convenience:

>>> from pesto.response import *
>>> def handler(request):
...     if not somecondition():
...         raise NotFound()
...     return Response(['ok'])
...

>>> def handler2(request):
...     if not somecondition():
...         raise Forbidden()
...     return Response(['ok'])
...

Redirect responses

A temporary or permanent redirect may be achieved either by raising pesto.response.RedirectTemporary or pesto.response.RedirectPermanent, or by returning pesto.response.redirect(). For example:

>>> from pesto.response import *
>>>
>>> def redirect1(request):
...     raise RedirectTemporary("http://www.example.com")
...
>>> def redirect2(request):
...     raise RedirectPermanent("http://www.example.com")
...
>>> def redirect3(request):
...     return redirect("http://www.example.com")
...

Cookies

You can read cookies through the request object, and construct cookies using the functions in pesto.cookies.

Reading a cookie

The easiest way to read a single cookie is to query the request.cookie_dict attribute. This is a dictionary mapping cookie names to single instances of pesto.cookie.Cookie:

def handler(request):
    if request.cookie_dict.get('secret_code').value == 'marmot':
        return Response(['pass, friend'])
    else:
        raise Forbidden()

Reading multiple cookies with the same name:

def handler(request):
    parts = [ cookie.value for cookie in request.cookies_by_name('partnumber') ]

Setting cookies

Simply assign an instance of pesto.cookie.Cookie to a set-cookie header:

def handler(request):
    return Response(
        ['blah'],
        set_cookie=pesto.cookie.Cookie(
            name='partnumber',
            value='Rocket_Launcher_0001',
            path='/acme',
            maxage=3600,
            domain='example.com'
        )
    )

Clearing cookies

To clear a cookie is to expire it. Set a new cookie with the same details as the one you are clearing, but with no value and maxage=0:

def handler(request):
    return Response(
        ['blah'],
        set_cookie=pesto.cookie.Cookie(
            name='partnumber',
            value='',
            path='/acme',
            maxage=0,
            domain='example.com'
        )
    )

Function decorators

Pesto works really well with function decorators. Here's a couple of examples of the sort of thing that are easy to do.

First up, a decorator to set caching headers on the response:

from functools import wraps

def nocache(func):
    """
    Pesto middleware to send no-cache headers.
    """
    @wraps(func)
    def nocache(request, *args, **kwargs):
        res = func(request, *args, **kwargs)
        res = res.add_header("Cache-Control", "no-cache, no-store, must-revalidate")
        res = res.add_header("Expires", "Mon, 26 Jul 1997 05:00:00 GMT")
        return res

    return nocache

Usage:

@nocache
def handler(request):
    return Response(['blah'])

Second: a decorator to allow handlers to return datastructures which are automatically converted into JSON notation (requires SimpleJSON):

def json(func):

    """
    Wrap a pesto handler to return a JSON-encoded string from a python
    data structure.
    """
    @wraps(func)
    def json(request, *args, **kwargs):
        result = func(request, *args, **kwargs)
        if isinstance(result, Response):
            return result
        return Response(
            content=[simplejson.dumps(result)],
            content_type='application/json'
        )

    json.__name__ = func.__name__
    return json

Finally, a decorator to turn 'water' into 'wine':

def water2wine(func):
    @wraps(func)
    def water2wine(*args, **kwargs):
        res = func(*args, **kwargs)
        return res.replace(
            content=(chunk.replace('water', 'wine') for chunk in res.content)
        )
    return miracle

These decorators are all used the same way and may be chained together. For example:

@despatch.match('/get-drink-preference')
@water2wine
@nocache
@json
def handler(request):
    return { 'preferred-drink' : 'water' }

URL despatch

Pesto's urldespatcher module allows you to map paths to handlers based on regular expressions, eg:

despatcher = pesto.urldespatcher()

...

@despatcher.match('/recipe.cgi', 'GET')
def recipe_index(request):
    ...

Despatchers can parse data out of URLs and pass it on to a handler. In the sample application, we used URLs of the form /recipe.cgi/show_recipe?recipe=toast to link to a recipe. Let's change that to the form /recipe.cgi/toast:

from pesto import urldespatcher, Response
despatcher = urldespatcher()

@despatcher.match('/recipe.cgi/recipe:.+', 'GET')
def show_recipe(request, recipe):
    return Response([
        '<html><body><h1>How to make %s</h1>' % recipe,
        '<p>%s</p><a href="%s">Back to index</a>' % (recipes[recipe], recipe_index.url()),
        '</body></html>'
    ])

The pattern /recipe.cgi/recipe:.+ tells the despatcher to match the regular expression .+, and pass the matched expression in as an extra keyword argument, recipe.

We can also map separate handlers to different HTTP methods for the same URL, eg the GET method could display a form, and the POST method of the same URL could handle the submission:

@despatcher.match('/contact-form', 'GET')
def contact_form(request):
    """
    Display a contact form
    """

@despatcher.match('/contact-form', 'POST'):
def contact_form_submit(request)
    """
    Process the form, eg by sending an email
    """

Despatchers do not have to be function decorators. The following code is equivalent to the previous example:

despatcher.match('/contact-form', GET=contact_form, POST=contact_form_submit)

URI generation

Functions mapped by the despatcher object acquire a url method, allowing URIs to be composed:

>>> from pesto import urldespatcher, Response
>>> despatcher = urldespatcher()
>>> @despatch.match('/recipes', 'GET')
... def recipe_index(request):
...     return Response(['this is the recipe index page'])
...
>>> @despatch.match('/recipe/recipe_id:[0-9]+', 'GET')
... def recipe_index(request, recipe_id):
...     return Response(['this is the recipe detail page for recipe #', recipe_id])
...
>>> recipe_index.url()
'/recipes'
>>> show_recipe.url(recipe=42)
'/recipes/42'

Running pesto applications

Pesto and WSGI

The to_wsgi utility function transforms a pesto handler into a WSGI application. This can then be run by any WSGI compliant server, eg wsgiref.simple_server, or in a CGI environment by using pesto.run_with_cgi:

app = pesto.to_wsgi(my_handler)
pesto.run_with_cgi(app)

Pesto has a function for creating handlers from urldespatcher instances. So to complete the recipe example above and create an application that will run under a CGI server (eg Apache):

despatcher = pesto.urldespatcher()
despatcher.match('/recipes', GET=handlers.recipe_index)
despatcher.match('/recipes/recipe_id:[0-9]+', GET=handlers.show_recipe)
despatcher.match('/recipes/recipe_id:[0-9]+/edit',
    GET=handlers.edit_recipe_form,
    POST=handlers.edit_recipe_save
)
app = pesto.to_wsgi(
    pesto.despatcher_app(despatcher)
)
pesto.run_with_cgi(app)

pesto.despatch

Full API documentation for pesto.despatch

Example:

despatcher = pesto.despatcher(prefix='/mywebsite')
@despatcher.match('/login', 'GET')
def login(request):
    ...

@despatcher.match('/showpage/page_name:page_.+', 'GET')
def showpage(request):
    ...

Pesto's despatch module is used for mapping URI paths and HTTP request methods (GET, POST etc) to pesto handlers.

Matching is based on the path of the URL, and uses either a raw regular expression, or a hybrid of regular expression sections and literal strings.

Match patterns

The hybrid form is more generally useful and expects a pattern composed of literal strings and named regular expression sections, separated by slashes.

Literal sections can be any string (but must not start with something looking like a python identified followed directly by a colon), eg the following are all valid:

content

my-blog

index.html

Named regular expression sections have the format:

<name>:<regex>

The name must be a valid python identifier.

The regex can be any regular expression (although a slash cannot be used in the expression). The meta-character . (a full point) will be translated to [^/], so that slashes won't be accidently matched by the expression, ie:

page_name:page_.+

Would match a section like page_1, but not page_1/2.

When a named regular expression section is matched, the despatcher will pass the value to the matched handler as an extra keyword argument (eg, in the previous example the handler would have to have the signature def handler(request, page_name).

Hybrid patterns are the default matching style used by urldespatcher.match and urldespatcher.imatch (case insensitive version).

Raw regular expression patterns

A raw regular expression matcher is also available, exposed by using matchre and imatchre:

@despatcher.matchre('/showpage/(?P<page_name>.+)', 'GET')
def showpage(request, page_name):
            ...

Unlike the default hybrid expressions, the entire pattern is taken as a single regular expression. For example, the following pattern:

/wiki/(?P<page>.+)/edit.html

Will match all these URLs:

/wiki/pesto/edit.html

/wiki/pesto/../../../../etc/passwd/edit.html

/wiki/pesto/edit!html

pesto.session

Full API documentation for pesto.session

Example:

@despatcher.match('/login', 'POST')
def login(request):

    username = request.get('username')
    password = request.get('password')

    if is_valid(username, password):
        request.session['username'] = username
        request.session['logged_in'] = True

    ...

@despatcher.match('/secure-area', 'GET')
def secure_area(request):

    if not request.session.get('logged_in'):
        return response.redirect(login.url())

    return ["Welcome to the secure area, %s" % request.session['username']]

...

from pesto.session.memorysessionmanager import MemorySessionManager

# Create a memory based sessioning middleware, that runs a purge every 600s
# for sessions older than 1800s..
sessioning = pesto.session_middleware(
    MemorySessionManager(),
    auto_purge_every=600,
    auto_purge_olderthan=1800
)

application = pesto.to_wsgi(pesto.despatcher_app(despatcher))
application =  sessioning(application)

Sessioning needs some kind of storage backend. Three are implemented, memory, dbm and rdbms backed.

Memory backend

Synopsis:

from pesto.session.memorysessionmanager import MemorySessionManager
app = pesto.session_middleware(MemorySessionManager())(app)

This is the fastest implementation, but only suitable for applications running in a single process persistent environment (eg not CGI).

File backed

This backend is the most suitable for CGI or other non-persistent environments.

Synopsis:

from pesto.session.filesessionmanager import FileSessionManager
session_manager = FileSessionManager('/tmp/sessions')
app = pesto.session_middleware(session_manager)(app)

There are also backends for storing sessions in a relational database or DBM file. See the API documentation for examples.

pesto.wsgiutils

Full API documentation for pesto.wsgiutils

pesto.wsgiutils.urlrewriter

Provides functionality similar to Apache's mod_rewrite.

Synopsis:

app = pesto.wsgiutils.urlrewriter(app)

rewriter.addrewrite(r"(.*)/index.php\?article=(.*)$", r"\1/articles/\2", 'redirect_permanent')

httpd = simple_server.make_server('', 8080, app)

Using pesto with a templating system

No hooks for templating are provided with Pesto, but integration should be straightforward.

For example, here is an application that uses Genshi for template rendering:

import os

from genshi.template.loader import TemplateLoader

import pesto
from pesto import Response

template_path = os.path.abspath('./templates')
templateloader = TemplateLoader([template_path], auto_reload=True)

def render_template(filepath, **kwargs):
    """
    Render an XHTML template in genshi, passing any keyword arguments to
    the template namespace.
    """
    _template =

    for chunk in templateloader.load(filepath).generate(**kwargs).serialize(method='xhtml'):
        yield unicode(chunk).encode('utf8')

def homepage(request):
    return = Response(template("homepage.html"))

def app_factory(global_cfg, **kwargs):
    """
    Create a new WSGI application
    """
    return pesto.to_wsgi(homepage)

if __name__ == "__main__":
    pesto.run_with_cgi(app_factory({}))