tsunami

log in
history

Understanding the Pyramid web framework

Luke Breuer
2013-12-10 00:43 UTC

Pyramid homepage
my strategy for learning Pyramid
Fundamentally, serving web pages means that a request goes to the server and a response comes back. Ultimately, both of these are just strings of text, or binary in certain cases. Let's look at the simplest kind of request and response.

Note that some of the below may be wrong. I haven't yet made little demo pieces of code. Feel free to contact me (see bottom of page) with any bug reports. :-)
request
You type http://example.org/wiki/meteorite into your browser and hit [enter].

Let's not care about the http://example.org bit, and instead just look at /wiki/meteorite. That URL [fragment] gets sent to our web server. Some additional stuff (headers, cookies) might get sent too, but we can ignore them for now.
response
When a web server receives a request, it must parse it to do two things:
  1. figure out which function to call to turn it into a response
  2. figure out what the arguments are to that function

With the request of /wiki/meteorite, it is likely that:
  1. wiki determines the function
  2. meteorite is the single argument to that function

The slashes are really just standard syntax; before the web was all dynamic, many web pages were merely requests for files on web server filesystems. They can be thought of this way, or a different way. An experiment yielded a-zA-Z0-9_#~@#$%&-+=/?.,:;!'*() as the allowable URL characters, per tsunami syntax; the specifics of this syntax will be dealt with some other time.

Ultimately, we can just think of there being a function:
def render_wiki_page(title):
    return "Welcome to page %s." % title

The web server would need to somehow connect /wiki/meteorite to that function. There are two standard ways to do this, in Pyramid.
1. URL Dispatch
def render_wiki_page(title):
    return "Welcome to page %s." % title

def init_web_server():
    config.add_route('wiki_route', '/wiki/{title}')
    config.add_view(render_wiki_page, route_name='wiki_route')

The highlighted lines are what make this 'URL Dispatch'. The following version is more commonly used and has a different function signature for render_wiki_page:

@view_config(route_name='wiki_route')
def render_wiki_page(request):
    return "Welcome to page %s." % request.matchdict['title']

def init_web_server():
    config.add_route('wiki_route', '/wiki/{title}')
    config.scan()
2. Traversal

Consider the following:

/wiki/meteorite/wiki/view/meteorite
['wiki', 'view', 'meteorite']get_root_object()['wiki'].view('meteorite')

The first step is called URL rewriting; it is needed because Traversal does not allow:

/wiki/meteorite → ... → get_root_object()['wiki'].default_view('meteorite')

Instead, we'd get:

/wiki/meteorite → ... → get_root_object()['wiki'].meteorite()

Clearly, this is not what we want. Now, consider the following parts:
  • get_root_object() this returns an object which is either a dictionary or has a 'view' to render whatever hasn't been consumed in the URL path
  • ['wiki'] chooses which 'resource' to get from the root
  • .view('meteorite') calls the 'view' and passes the rest of the URL path

This is merely an example of how traversal might work; it is extremely flexible. You can think of it as navigating a hierarchy of resources, hitting a 'verb' at some point which is the name of the 'view', and passing the rest of the URL (if any) as an argument. You might think of Traversal as being more flexible than URL Dispatch.