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


Simplates are the file format Aspen uses to represent HTTP resources. They are Aspen’s main attraction. Here’s what a simplate looks like:

"""This is my simplate.
import random

n = random.choice(range(10))
extra_excitement = "!" * n

Greetings, program!%(extra_excitement)s

Simplates are a single file with one or more sections. The section separator is [---].


Assuming you’ve gone through the Quick Start, edit index.html.spt with the above content, then refresh.

You’re off and running!

Give it a shot: Anything you define in the first two (Python) sections is available in the third (template) section.

Can you guess what the difference is between the first two Python sections? The first one is run once, the first time the section is hit (not on startup), and again any time the simplate is changed. It’s designed for imports and constants. The second is run once for each request.


A simplate is a file that contains multiple sections of varying syntax, separated by [---].

The first section is Python syntax. It is exec'd once, the first time the resource represented by the simplate is served, and it has these variables as builtins:

__file__   the absolute path to the simplate file (symlinks preserved)

website    an instance of a Website object that gives you access to

The second section is Python syntax, and is exec'd once per request. Its context cascades from the first section, and adds:

request    a request object

response   a response object

The third page uses the templating language of your choosing—we call this the “renderer.” The default renderer is the basic templating language that Python uses to render strings: %(varname)s is replaced by the variable 'varname' as a string. More details are available in the Python docs. Other built-in templating languages are stdlib_format (using Python string formatting), stdlib_template (using Python template strings), and json. There are modules available with support for Tornado (aspen-tornado), Jinja2 (aspen-jinja2) and Pystache (aspen-pystache).

The way to specify a renderer is with a so-called “specline” at the start of the content page:

program = "program"
[---] via pystache
Greetings, {{ program }}!

You can set the default renderer for a given media type (presuming it is installed) like so:

website.default_renderers_by_media_type['text/html'] = 'pystache'

Here’s how to directly set the default renderer for all media types:

website.default_renderers_by_media_type.default_factory = lambda: 'pystache'

This can also be phrased as:

website.renderer_default = 'pystache'

You can also set the default renderer globally using the renderer_default keyword argument to Website, or the ASPEN_RENDERER_DEFAULT environment variable.

Bound Simplates

If a simplate file has a penultimate file extension (foo.html.spt as opposed to foo.spt), then it is considered to be a “bound simplate”: it is bound to a particular media type. In this case the first and second sections (the Python sections) are optional.

Unbound Simplates

If a simplate file has no penultimate file extension (foo.spt as opposed to foo.html.spt), then it is considered to be an “unbound simplate”: it is not bound to any particular media type.

Instead, unbound simplates can have multiple so-called “content” sections, and the order they appear in will determine their priority in HTTP content negotiation. If negotiation fails you’ll get a 406 Not Acceptable response, with a prioritized list of available media types. Joe Gregorio’s content negotiation module is vendored in to do the heavy lifting. The format of the file is like this:

import foo, json

data =

[---] text/plain via pystache
{{ data }}

[---] application/json
{{ json.dumps(data) }}

The first two sections of the file are logic sections as described above. Both are required for an unbound simplate. After that may appear an unlimited number of content sections. The first line of each content section is the so-called “specline,” where you must specify the media type of that content section. You may optionally specify a renderer on the specline as well.

If a file does have a file extension, then it is a “bound simplate” with a media type computed from the file extension. It is a SyntaxError for a file to have both a penultimate extension and multiple content sections.

Indirect Negotiation

The canonical way to perform HTTP media type negotiation is with the Accept request header. That’s much more cumbersome than changing the file extension in the URL, however, so Aspen supports both. If a request comes in for /foo.html and there’s no foo.html.spt on the filesystem but there is a foo.spt file, then Aspen will serve the request with that. If you request foo.txt but foo.spt doesn’t include a text/plain page then you’ll get a 404, whereas with direct negotiation as above you would get a 406 with a list of available media types.


By default, simplate files are presumed to be ASCII; if non-ASCII characters (utf8, for example) are to be included in the file, an encoding must be specifed as per PEP 263, which means putting the phrase 'encoding: utf8' somewhere on the first two lines of the file.


All simplate pages are cached in a compiled state. This cache is invalidated whenever the underlying file changes, so simplate changes are always immediately available. When you change Python libraries or configuration files, aspen can restart automatically. But even this extra second or so is a drag during development. When you change a simplate, the changes are always available immediately, without restarting the server.

Ack! Mixing logic and presentation?!?!?

Well, I like to think that simplates bring code and presentation as close together as possible without mixing them. There are no arbitrary Python blocks inside of the template page, à la PHP/ASP.

Simplates are more like MVC, but with controllers, views, and templates together in the same file instead of in separate files. Your models live in a Python library, use an ORM, etc. And URL routing is done via the filesystem. There’s all around less boilerplate.

Simplates make it easy to kick off development of an app or feature of an app. Simplates are, in fact, downright slippery. You won’t be able to stop innovating. You just open a single file and start writing Python, JavaScript, CSS, HTML, SQL—whatever. It takes zero wiring to get it on the network for the first time. And as apps/features mature, you will very naturally move code out of your single simplate into separate Python libraries, template files, JSON endpoint simplates, JavaScript libraries, CSS files, etc.

Don’t be scared! This isn’t PHP! It’s MVC with less crap.


My friend Steve came up with the original idea for simplates in the early aughts, when we were running a web design shop together. We were heavily into Zope and Plone at the time. This was in the Zope 2 days. The original idea was something like, “What if you could put a Script (Python) and a ZPT in THE SAME FILE!?” Steve has since switched careers to philosophy. I debuted simplates in Aspen 0.8, which came out in December of 2007. I made simplates the main attraction in Aspen 0.9.

Home Request