Routr provides a set of tools to map WSGI (WebOb by default) request to an artbitrary Python object. It was designed with the following points in mind:
I’ll just give you an example WSGI application with no explanations – code is better than words here:
from routr import route, POST, GET
from myapp.views import list_news, get_news, create_news
from myapp.views import get_comments, create_comment
routes = route("news",
route(GET, "/", list_news),
route(POST, "/", create_news),
route(GET, "/{id:int}/", get_news),
route(GET, "/{id:int}/comments", get_comments),
route(POST, "/{id:int}/comments", create_comment),
)
You use routr.route() function to define your routes, then you can dispatch request against them:
from routes.exc import NoMatchFound
from webob import Request, exc
def application(environ, start_response):
request = Request(environ)
try:
trace = routes(request)
view = trace.target
args, kwargs = trace.args, trace.kwargs
response = view(*args, **kwargs)
except NoMatchFound, e:
response = e.response
except exc.HTTPException, e:
response = e
return response(environ, start_response)
Note that neither of these are not dictating you how to build your application – you’re completely free about how to structure and organize your application’s code.
As we’ve already seen routes are defined via routr.route() function which can be called in two ways, the first one is for defining endpoint routes:
route([method] [, pattern] [, *guards] [, target], name=None, **anotations)
As you can see method and pattern are optional here, by default method will be set to GET and pattern to /. Argument target is an arbitrary object you want to associate endpoint route with, usually it will be a view callable or string which specifies one.
Second form is for grouping routes together:
route([method] [, pattern] [, *guards] [, *routes], name=None, **anotations)
There is *routes argument which can contain one or more route definitions via same routr.route() so you can nest routes one inside others.
URL pattern is a string which can contain placeholders for parameters. Parameters are captured into trace.args tuple in a corresponding order.
Parameters are typed to strings by default, but you can also annotate them to be integers or enumerations or path segments:
- /news/{year:int}-{month:int}-{day:int}/ will match against /news/2001-12-12/ and trace.args will contain tuple (2001, 12, 12).
- /show/{type:any(news, comments)} will match against /show/news or /show/comments
- /wiki/{page:path} will match against /wiki/category/page/comments, note that type path allows / to be captured while str (which is also default type) doesn’t do that
The result of matching routes is a routr.Trace object:
Result of route matching
Represents a trace of matched routes towards the last one called endpoint.
Attr args: | collected positional arguments |
---|---|
Attr kwargs: | collected keyword arguments |
Attr routes: | a list of matched routes with the endpoint route being the last one |
Attr endpoint: | matched endpoint route |
To allow route reversal you must provide name keyword argument to routr.route() directive:
routes = route("/page/{id:int}", myview, name="myview")
Then you are allowed to call reverse(name, *args, **kwargs) method on routes:
routes.reverse("myview", 43, q=12) # produces "/page/43?q=12"
You can match against query string parameters with routr.schema module which exposes routr.schema.QueryParams (or its qs alias) guard:
from routr import route
from routr.schema import qs, opt, Int, String
routes = route("/", myview,
guards=[qs(query=String, page=opt(Int))])
Class routr.schema.QueryParams represents a guard which processes request’s query string and validates it against predefined schema.
Route guards are callables which take request as its single argument and return a dict of params which then collected and accumulated by routr.Trace object or raise a webob.exc.HTTPException.
You can annotate any route with arbitrary objects, routr.route() accepts **kwargs arguments and all of those (except name and guards which have special meaning) will be passed to routr.Route constructor so you can access it via routr.Trace object after matching:
...
routes = route("/", myview, middleware=[mymiddelware])
...
trace = routes(request)
trace.endpoint.annotations["middleware"]
...
Note that routr.Trace objects also provide access for parent routes of endpoint route via Trace.routes attribute so you can accumulate annotations along the matched path. This, for example, can be useful for implementing middleware system like Django does but this allows only fire some middleware on those routes which was annotated correspondingly.
Routr provides a shortcut for defining routes for serving static data, it uses webob.static under the hood. While this is not a production-ready solution (I recommend nginx for this) it can be quite useful during development for serving static assets such as HTML, CSS or javascript.
To define route you just need to use the routr.static.static() function:
from routr.static import static
routes = route(
...
static('/static', 'static'),
...
)
Note, that handler for these routes accepts only two arguments – request and path, so you need to distinguish it using static_view annotation from other target objects and handle call to it separately like this:
...
trace = routes(request)
if trace.endpoint.annotations.get('static_view'):
return trace.target(request, *trace.args)
...
Let’s suppose we have following routes defined:
# examples.py
from routr import route, POST, GET
def api_list_news():
""" API list news items"""
def api_create_news():
""" API create news item"""
def api_get_news(id):
""" API get news item by ``id``"""
routes = route(
route("api",
route(GET, "news", api_list_news),
route(POST, "news", api_create_news),
route(GET, "news/{int}", api_get_news)))
We want to generate documentation from these definitions. For that purpose there’s autoroutr directive which is built on top of sphinx-httpdomain:
.. autoroutr:: examples:routes
That gives us as a result:
Directive autoroutr also supports :include: and :exclude: options which allow to include or exclude routes using glob pattern syntax.
Development takes place at GitHub, you can clone source code repository with the following command:
% git clone git://github.com/andreypopp/routr.git
In case submitting patch or GitHub pull request please ensure you have corresponding tests for your bugfix or new functionality.
Module routr contains routr.route() function which is used as a primary way to define routes:
Directive for configuring routes in application
Parameters: |
|
---|
Include routes by spec
Parameters: | spec – asset specification which points to Route instance |
---|
Plug routes by setuptools entry points, identified by name
Parameters: | name – entry point name to query routes |
---|
Result of route matching
Represents a trace of matched routes towards the last one called endpoint.
Attr args: | collected positional arguments |
---|---|
Attr kwargs: | collected keyword arguments |
Attr routes: | a list of matched routes with the endpoint route being the last one |
Attr endpoint: | matched endpoint route |
For the complete reference, these are classes which are constructed by routr.route():
Base class for routes
Parameters: |
|
---|
Match request against route
Return type: | |
---|---|
Raises: |
|
Reverse route with name using *args as pattern parameters and **kwargs as query string parameters
Raises routr.exc.RouteReversalError: | |
---|---|
if no reversal can be computed for given arguments |
Route which represents a group of other routes
Can have its own guards and a URL pattern.
Additional to Route params are:
Parameters: | routes – a list of Route objects |
---|
Endpoint route
Associated with some object target which will be returned in case of successful match and a method which matches against request’s method.
Additional to Route params are:
Parameters: |
|
---|
Module routr.schema contains predefined routr.schema.QueryParams guard and helper utilites:
Also routr.schema module re-exports colander package, so you can import any colander class or function right from there.
Define a route which serves static assets
Parameters: |
|
---|