User Guide

Path and Handler

You create a mapping between: a remainder of the request URL (virtual location or url path) and a handler.:

urls = [
     ('posts/2003', posts_for_2003),
     ('posts/{year}', posts_by_year),
     ('posts/(?P<year>\d+)/(?P<month>\d+)', posts_by_month)
]

It is completely up to you what is path or handler. If you have a look at Hello World example you notice the following:

def main(environ, start_response):
    handler, kwargs = r.match(environ['PATH_INFO'])
    return handler(environ, start_response)

or more precisely:

environ['PATH_INFO']

This operation takes WSGI environment varible PATH_INFO and pass to router for matching against available mapping. handler in this case is a simple callable that represents WSGI call handler.

def hello_world(environ, start_response):
    start_response('200 OK', [
        ('Content-type', 'text/html')
    ])
    return ["Hello World!".encode('utf8')]

Extend Mapping

Since mapping is nothing more than python list, you can make any manipulation you like, e.g. add other mappings, construct them dynamically, etc. Here is example from Server Time example:

home = [
    ('', welcome),
    ('server/', server_urls)
]

home += [
    url('{url:any}', not_found)
]

home mapping has been extended by simple adding another list.

Mapping Inclusion

Your application may be constructed with several modules, each of them can have own urls mapping. You can easily include them in case a handler place is used by other mapping. Here is an example from Server Time:

server_urls = [
    url('time', server_time, name='now')
]

home = [
    ('', welcome),
    ('server/', server_urls)
]

server_urls included into server/ subpath. So effective path for server_time handler is server/time.

Named Groups

Named groups is something that you can get out from url mapping:

urls = [
    ('posts/{year}', posts_by_year),
    ('posts/(?P<year>\d+)/(?P<month>\d+)', posts_by_month)
]

If we get back to Hello World example:

def main(environ, start_response):
    handler, kwargs = r.match(environ['PATH_INFO'])
    return handler(environ, start_response)

kwargs is assigned with dict that represends a key-value pair from match:

>>> handler, kwargs = r.match('posts/2011/09')
>>> kwargs
{'month': '09', 'year': '2011'}

Extra Parameters

While named groups get some information from matched path, you can also merge with some extra values during initialization of mapping (this is third parameter in tupple):

urls = [
    ('posts', latest_posts, {'blog_id': 100})
]

Note, that any non-empty values from path match override extra parameters passed during initialization.

url helper

There is wheezy.routing.url() function (that is actually just an exporting name for url()) that let you make your url mapping more readable:

from wheezy.routing import url


urls = [
    url('posts', latest_posts, kwargs={'blog_id': 100})
]

Named Mapping

Each path mapping you create is automatically named after the handler name. The convention as to the name is: translate handler name from camel case to underscope name and remove anything ended by handler, controller, etc. So LatestPostsHandler is named as latest_posts.

You can specify other name during mapping, it is convenient to use url()) function for this:

urls = [
    url('posts', latest_posts, name='posts')
]

When you know name for url mapping you can reconstruct it path.

Adding Routes

You have an instance of wheezy.routing.Router (PathRouter). Call it method add_routes() to add any mapping you have. Here is how we do it in Hello World example:

r = Router()
r.add_routes([
    ('/', hello_world)
])

... or Server Time:

import config
import urls

r = config.router
r.add_routes(urls.home)

Route Builders

Every path mapping you add to router is translated to appropriate route match strategy. The avaiable routing match strategies are definded in config module by route_builders list include:

  1. plain
  2. regex
  3. curly

Plain Route

The plain route is selected in case the path satisfy the following regular expression (at least one word, '/' or '-' character):

^[\w/-]+$

The matching paths include: account/login, blog/list, etc. The strategy performs exact string marching. In case the matching string ends with path segment delimiter character '/', the strategy is changed to match the beginning of path, thus server/ pattern match any path starting with server/, e.g. server/info, etc.

Regex Route

Any valid regular expression will match this strategy. However there few limitation that applies if you would like to build paths by name (reverse function to path matching). Use regex syntax only inside named groups, create them as much as necessary. The path build strategy simply replaces named groups with values supplied.

Curly Route

This is just a simplified version of regex routes. You define a named group by using curly brakets. The curly expression abc/{id} is convered into regex abc/(?P<id>[^/]+). The form of curly expression (pattern is optional and corresponds to segment by default):

{name[:pattern]}

The name inside curly expression can be constrained with the following patterns:

  • i, int, number, digits - one or more digits
  • w, word - one or more word characters
  • s, segment, part - everything until '/' (path segment)
  • a, any, rest - match anything

Here are examples of valid expressions:

posts/{year:i}/{month:i}
account/{name:w}

Building Paths

Once you have defined routes you can build paths from them. See Named Mapping how the name of url mapping is cunstructed. Here is a example from Server Time:

    return ["Welcome!  <a href='%s'>Server Time</a>" %
            r.path_for('now')]

You can pass optional values that will be used to replace named groups of the path matching pattern:

>>> r = RegexRoute(
...     r'abc/(?P<month>\d+)/(?P<day>\d+)'
... )
>>> r.path_for(dict(month=6, day=9))
'abc/6/9'
>>> r.path_for(dict(month=6))
'abc/6'
>>> r.path_for()
'abc'

Values passed to path_for() method override any values used during initialization of url mapping.