PK MQ'A ih h routr-latest/searchindex.jsSearch.setIndex({objects:{"":{"/api/news/{int}":[0,0,1,"get--api-news-{int}"],"/api/news":[0,1,1,"post--api-news"],routr:[0,2,1,""]},"routr.Route":{reverse:[0,4,1,""],match:[0,4,1,""]},"routr.schema":{Optional:[0,5,1,""],QueryParams:[0,5,1,""]},"routr.static":{"static":[0,3,1,""]},routr:{plug:[0,3,1,""],Endpoint:[0,5,1,""],Trace:[0,5,1,""],RouteGroup:[0,5,1,""],Route:[0,5,1,""],include:[0,3,1,""],route:[0,3,1,""],schema:[0,2,1,""]}},terms:{all:0,code:0,queri:0,month:0,prefix:0,follow:0,decid:1,readabl:0,under:0,spec:0,isinst:1,sourc:0,string:0,util:1,mechan:0,coland:0,tri:1,list:[0,1],"try":0,emul:1,api_create_new:0,pleas:0,upper:1,direct:0,second:0,design:0,pass:[0,1],compat:1,orig_method:1,access:0,delet:1,"new":[0,1],method:[0,1],variat:1,gener:0,here:0,behaviour:1,let:0,path:0,along:0,purpos:0,andreypopp:0,"0x475b0c0":[],extrem:0,implement:0,via:0,modul:0,foundat:1,api:0,post:[0,1],middlewar:0,from:[0,1],sofist:0,two:0,handler:0,call:[0,1],asset:0,recommend:0,dict:0,type:0,more:0,correspondingli:0,templat:1,must:0,dictat:0,none:0,endpoint:[0,1],word:0,augment:1,alia:0,setup:0,work:0,annot:[0,1],xhr:1,kwarg:[0,1],can:[0,1],nomatchfound:0,dec:1,def:[0,1],give:0,process:0,accept:[0,1],want:[0,1],serial:1,write:0,how:[0,1],"0x2d9b0c0":[],css:0,map:0,product:0,clone:0,after:0,create_com:0,intrus:0,lot:1,data:[0,1],get_new:0,github:0,correspond:[0,1],django:0,environ:0,allow:0,callabl:0,order:0,elif:1,"0x27e4fc0":[],paramet:[0,1],api_get_new:0,group:0,better:0,them:0,"return":[0,1],python:0,dai:0,frameworkish:0,name:0,"0x3d0afc0":[],revers:0,separ:0,mean:0,replac:0,autoroutr:0,"static":0,year:0,special:0,list_new:0,item:0,predefin:0,categori:0,wsgifi:1,urlpattern:0,insid:0,spot:1,free:0,like:[0,1],base:[0,1],put:1,teach:1,success:0,thing:0,place:0,isn:0,routr:[0,1],first:0,render:1,placehold:0,alreadi:0,primari:0,hood:0,given:0,associ:0,top:0,system:0,construct:0,schema:0,option:0,routegroup:0,tool:0,setuptool:0,httpdomain:0,part:0,than:0,serv:0,"0x2c73fc0":[],target:[0,1],keyword:0,provid:[0,1],structur:0,str:0,posit:[0,1],toward:0,browser:1,comput:0,arg:[0,1],mind:0,argument:[0,1],packag:0,have:[0,1],need:[0,1],seen:0,exclud:0,built:0,self:1,note:0,also:0,take:0,which:[0,1],singl:0,even:0,trace:[0,1],glob:0,object:[0,1],inject_argu:1,path_info:0,segment:0,"class":[0,1],exc:[0,1],url:0,request:[0,1],doe:0,declar:0,wsgi:[0,1],show:0,text:1,syntax:0,identifi:0,mymiddelwar:0,onli:[0,1],configur:0,solut:[0,1],should:0,suppos:0,analyz:1,payload:0,variou:0,get:[0,1],report:0,organ:0,patch:0,contain:0,view:0,wiki:0,set:[0,1],nourlpatternmatch:0,see:0,result:0,respons:[0,1],best:1,detect:1,parent:0,url_pattern_cl:0,pattern:[0,1],enumer:0,myview:0,"import":[0,1],neither:0,attribut:0,signatur:1,extend:0,api_list_new:0,javascript:[0,1],extens:0,distinguish:0,baserequest:1,addit:0,last:0,easi:0,anot:0,against:0,instanc:0,com:0,comment:0,point:0,dispatch:0,header:1,param:0,quit:0,compos:0,accumul:0,json:1,create_new:0,basic:0,fire:0,get_com:0,ani:0,static_view:0,understand:0,togeth:0,those:0,"case":0,plain:1,properti:1,defin:0,"while":0,routeguard:0,helper:0,readi:0,non:0,itself:[],myapp:0,lightweight:0,routereversalerror:0,develop:0,same:0,handl:0,html:[0,1],document:0,needs_request:1,complet:0,http:[0,1],nest:0,rais:[0,1],"0x2b120c0":[],older:1,entri:0,exampl:0,command:0,thi:[0,1],rout:[0,1],usual:0,explan:0,"0x3c0dfc0":[],just:0,expos:0,mix:0,except:0,shortcut:0,valid:0,"0x400b0c0":0,els:1,app:1,match:0,build:0,opt:0,applic:[0,1],format:1,nginx:0,"_method":1,httpexcept:0,best_match:1,specif:0,arbitrari:0,integ:0,collect:0,either:1,page:[0,1],right:0,specifi:0,captur:0,some:[0,1],"export":0,librari:1,definit:0,subclass:1,inject:1,refer:0,usag:[0,1],repositori:0,httpbadrequest:1,"super":1,plug:0,about:0,constructor:0,produc:0,block:1,own:0,automat:0,latter:1,guard:0,"0x31d0fc0":[],ensur:0,your:[0,1],git:0,wai:[0,1],typ:0,support:[0,1],submit:0,custom:1,avail:[],includ:0,artbitrari:0,treat:0,"function":[0,1],form:0,tupl:[0,1],methodnotallow:0,bug:0,pull:0,made:[],attr:0,possibl:1,"default":0,bugfix:0,start_respons:0,otherwis:0,creat:0,shim:1,"int":0,dure:0,utilit:0,doesn:0,repres:0,when:1,invalid:1,do_not_need_request:1,other:0,test:0,you:[0,1],webob:[0,1],queryparam:0,sphinx:0,directori:0,emul_method:1,cookbook:1},objtypes:{"0":"http:get","1":"http:post","2":"py:module","3":"py:function","4":"py:method","5":"py:class"},titles:["routr – lightweight request routing for WSGI","Cookbook"],objnames:{"0":["http","get","HTTP get"],"1":["http","post","HTTP post"],"2":["py","module","Python module"],"3":["py","function","Python function"],"4":["py","method","Python method"],"5":["py","class","Python class"]},filenames:["index","cookbook"]})PK MQ'A\ۯ ۯ routr-latest/index.html
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: |
|
---|
There are a lot of possibilities, variations and patterns of usage of routr – this page tries to provide some useful blocks as a foundation for your custom routing solution.
How to decide when to pass request as an argument to a function? You can analyze function signature for that – use routr.utils.inject_arguments() function which can inject needed arguments into arguments’ tuple based on function signature:
from webob.dec import wsgify
from routr import GET, route
from routr.utils import inject_arguments
def needs_request(request, id):
...
def do_not_need_request(id):
...
routes = route(
route(GET, '/a/{id}', needs_request),
route(GET, '/b/{id}', do_not_need_request),
)
@wsgify
def app(request):
trace = routes(request)
args = inject_arguments(trace.target, trace.args, request=request)
return trace.target(*args, **trace.kwargs)
This way function which have request positional argument in its signature will be passed a request object.
You want your functions to return only data and want to decide how to serialize it based on Accept header of a request:
from webob import Response
from webob.exc import HTTPBadRequest
from webob.dec import wsgify
from routr import GET, route
def list():
return [...]
def get(id):
return {...}
routes = route(
route(GET, '/news', list, template='list.html'),
route(GET, '/news/{id}', get, template='get.html'),
)
@wsgify
def app(request):
trace = routes(request)
response = trace.target(*trace.args, **trace.kwargs)
if not isinstance(response, Response):
best = request.accept.best_match(['application/json', 'text/html'])
if best == 'application/json':
response = Response(json=response)
elif best == 'text/html':
template = target.endpoint.annotations.get('template')
if not template:
raise HTTPBadRequest('invalid Accept header')
response = Response(template.render(response))
else:
raise HTTPBadRequest('invalid Accept header')
return response
That way functions can return only plain data, not a webob.Response object and based on Accept header of a request WSGI application will serialize data into application/json or either text/html format. The latter will be serialized using template which is set by template annotation on a corresponding route.
Some javascript libraries have compatibility shim for older browser which do not support methods like PUT or DELETE in XHR – it emulates DELETE and PUT methods by calling POST with method passed as GET parameter. You can teach routr how to spot this behaviour by subclassing of webob.Request object:
from webob import Request as BaseRequest
class Request(BaseRequest):
@property
def method(self):
orig_method = super(Request, self).method
if orig_method == 'POST':
emul_method = self.GET.get('_method').upper()
if emul_method in ('PUT', 'DELETE'):
return emul_method
return orig_method
Please activate JavaScript to enable the search functionality.
From here you can search these documents. Enter your search words into the box below and click "search". Note that the search function will automatically search for all of the words. Pages containing fewer words won't appear in the result list.