How to Write a Simplate

Aspen dispatches web requests to files on the filesystem based on paths, but what kind of files does it expect to find there? The answer is simplates. Simplates are a file format combining request processing logic—like you’d find in a Django view or a Rails controller—with template code, in one file with multiple sections.

Note

Check the Aspen homepage for links to simplate support for your favorite text editor.

Sections of a Simplate

What are the sections of a simplate? Let’s illustrate by example:

import random

[----------------------------------]
program = querystring['program']
excitement = '!' * random.randint(1, 10)

[----] text/html via stdlib_template
<h1>Greetings, $program$excitement</h1>

[-----] text/plain via stdlib_format
Greetings, {program}{excitement}

[---] application/json via json_dump
{"program": program, "excitement": excitement}

The first thing to notice is that the file is separated into multiple sections using lines that begin with the characters [---]. There must be at least three dashes, but more are fine.

Sections in a simplate are either “logic sections” or “content sections”. Content sections may have a “specline” after the [---] separator. The format of the specline is content-type via renderer. The syntax of the content sections depends on the renderer. The logic sections are Python.

Note

Simplates under Python 2.7 use the following __future__ features: absolute_import, division, print_function, and unicode_literals.

A simplate may have one or more sections. Here are the rules for determining which section is what:

  1. If a simplate only has one section, it’s a content section.
  2. If a simplate has two sections, the first is request logic (runs for every request), and the second is a content section.
  3. If a simplate has more than two sections:
  1. If the second section has a specline, then the first is request logic, and the rest are content sections.
  2. If the second section has no specline, then the first is initialization logic (runs once when the page is first hit), the second is request logic, and the rest are content sections.

Putting that all together, we see that the above example has five sections:

  1. a logic section containing Python that will run once when the page is first hit,
  2. a request section containing Python that will run every time the page is hit,
  3. a section for rendering text/html via Python templates,
  4. a section for rendering text/plain via new-style Python string formatting, and
  5. a section for rendering application/json via Python’s json library.

Context

The power of simplates is that objects you define in the logic sections are automatically available to the templates in your content sections. The above example illustrates this with the program and excitement variables. Moreover, Aspen makes various objects available to the logic sections of your simplates (besides the Python builtins).

Here’s what you get:

  • path—a representation of the URL path
  • querystring—a representation of the URL querystring
  • request_processor—a RequestProcessor instance
  • resource—a representation of the HTTP resource
  • state—the dictionary that contains the request state

Framework wrappers will add their own objects, as well.

Standard Renderers

Aspen includes five renderers out of the box:

  • json_dump—takes Python syntax, runs it through eval and then json.dumps
  • jsonp_dump—takes Python syntax, runs it through eval and json.dumps, and then wraps it in a JSONP callback if one is specified in the querystring (as either callback or jsonp)
  • stdlib_format—takes a Python string, runs it through format-style string replacement
  • stdlib_percent—takes a Python string, runs it through percent-style string replacement
  • stdlib_template—takes a Python string, runs it through template-style string replacement

Note

Check the Aspen homepage for links to plugins for other renderers.

Specline Defaults

Speclines are optional. The defaults ... I guess we should point to the API reference for this. And the framework wrappers will have something to say about this, as well.

Content Negotiation

Aspen negotiates with clients to determine how to best represent a resource for a given request. Aspen models resources using simplates, and the content sections of the simplate determine the available representations. Here are the rules for negotiation:

  1. If the URL path includes a file extension, Aspen looks in the Python mimetypes registry for a content type associated with the extension. If the extension is not in the registry, Aspen responds with 404 Not Found. If the extension is in the registry, Aspen looks for a match against the corresponding type. If no content section provides the requested representation, Aspen again responds with 404 Not Found.
  2. If the URL path does not include a file extension and there are multiple available types, Aspen turns to the Accept header. If the Accept header is missing or malformed, Aspen responds using the first available content section. If the Accept header is valid, Aspen looks for a match. If no content section provides an acceptable representation, Aspen responds with 406 Not Acceptable.
  3. If the URL path includes a file extension but there is only one available type, then Aspen ignores the Accept header (as the spec allows), responding with the only available representation.

Note

Aspen delegates to the python-mimeparse library to determine the best available match for a given media range.