Aspen

A Python web framework that makes the most of the filesystem.
Simplates are the main attraction.

Request

The Aspen Request object tries to consistently model the actual structure of an HTTP Request message. Consequently, it is rather verbose. There are shortcuts available, however.

Where?

An instance of the aspen.http.request.Request class is made available to you in your simplates from the second page on.

Shortcuts

The most common parts of the HTTP Request message are given these shortcuts inside of simplate:

bodyrequest.body
cookierequest.headers.cookie
headers request.headers
pathrequest.line.uri.path
qsrequest.line.uri.querystring

For example, here’s how to pull a value out of the querystring:

foo = qs["foo"]

There are also booleans for each of the standard HTTP methods, so simplates in a REST style will end up looking like this:

if GET:
    pass # handle GET )
elif POST:
    pass # handle POST

There is no API that folds the querystring and body together. It’s better to be explicit about where you’re expecting your values from.

Full API

The following object model is used to comprehensively represent an HTTP Request message. Where “raw” attributes are noted those give the bytes as supposedly received on the wire. As an optimization, this attribute is lazily computed for request.body.raw. The request, itself a subclass of str, is also dealt with lazily.

nameclassbase type notes
requestRequeststr
request.lineLinestr
request.line.methodMethodunicode
request.line.method.rawstr
request.line.uriURIunicode
request.line.uri.schemeUnicodeWithRawunicode
request.line.uri.scheme.rawstr
request.line.uri.usernameUnicodeWithRawunicode
request.line.uri.username.rawstr
request.line.uri.passwordUnicodeWithRawunicode
request.line.uri.password.rawstr
request.line.uri.hostUnicodeWithRawunicode
request.line.uri.host.rawstr
request.line.uri.portIntWithRawint0 if absent
request.line.uri.port.rawstr
request.line.uri.pathPathMapping
request.line.uri.path.rawstr
request.line.uri.path.decodedunicode
request.line.uri.path.partsList of PathPartssee below
request.line.uri.querystringQuerystringMapping
request.line.uri.querystring.rawstr
request.line.uri.querystring.decoded unicode
request.line.uri.rawstr
request.line.versionVersionunicode
request.line.version.infotuple
request.line.version.majorint
request.line.version.minorint
request.line.version.rawstr
request.line.rawstr
request.headersHeaders
request.headers.cookieCookie.SimpleCookie
request.headers.hostunicodeX-Forwarded-For if it exists, Host otherwise (no Host will raise Response(400))
request.headers.schemeunicodeu"https" or u"http"
request.headers.rawstr
request.bodyBody
request.body.rawstr

Here are the methods on the Request object:

request.allow(*methods):
    """Given method strings, raise 405 if we don't meet the requirements.

    The method names are case insensitive (they are uppercased). If 405
    is raised then the Allow header is set to the methods given.

    """

request.is_xhr():
    """Check the value of X-Requested-With.
    """

request.redirect(location, code=None, permanent=False):
    """Given a string, an int, and a boolean, raise a Response.

    If code is None then it will be set to 301 (Moved Permanently)
    if permanent is True and 302 (Found) if it is False.

    """

Mapping Objects

The Body, Headers, Path, and Querystring objects all have the same API. They are asymetric dictionaries. With HTTP mappings you always need to deal with the “either list or value” problem. Using them as simple values is much more common then using them as lists of values, so the base dictionary access set/get operations are tuned for that, and then you get this API for working with them as lists of values:

obj.all(name, default=None):
    """Given a name, return a list of values.
    """

obj.add(name, value):
    """Given a name and value, add another entry.
    """

Putting those together, here’s what it looks like:

>>> headers['Content-Type'] = "text/html"
>>> headers['Content-Type'] = "text/plain" # changed my mind!
>>> headers['Content-Type']
text/plain
>>> headers.all('Content-Type')
['text/plain']

Here’s another example:

>>> qs.add('likes', 'cheese!')
>>> qs.add('likes', 'grommit!')
>>> qs['likes']
'grommit!'
>>> qs.all('likes')
['cheese!', 'grommit!']

They’re asymetric in that subscript access respects the underlying list while subscript assignment clobbers it.

File Uploads

If a request is of Content-Type multipart/form-data, then request.body values that are file uploads will be instances of the standard library’s cgi.FieldStorage class. Each such object has filename, value, and type attributes with the name, bytes, and advertised Content-Type of the file that was uploaded.

Path Parts

Aspen Path objects have a parts attribute, which is a list of PathPart objects. So if you're in a simplate at /foo/bar/baz.html.spt, then path.parts[1] will be 'bar' and path.parts[2] will be 'baz.html'.

RFC2396 specifies a syntax for attaching parameters to each path part, and Aspen exposes these in a params attribute (which is a Mapping object) on each unicode in the path.parts list.

Say what?

If you have:

/foo/index.html.spt

and you hit it with:

/foo;a=1;b;a=3/

then you can do:

if 'b' in path.parts[0].params:
    a = path.parts[0].params['a']
[---]
%(a)s
and you'll get: Home Response