A WSGI dispatcher.
Project description
A WSGI dispatcher.
At a glance
matcha is a dispatcher for all WSGI application based on a matching pattern of PATH_INFO in environ.
Let’s register your WSGI applications to matchings and create WSGI application.
>>> from wsgiref.simple_server import make_server
>>> from matcha import Matching as m, bundle, make_wsgi_app
>>>
>>> from yourproject import home_app
>>> from yourproject.blog import post_list_app, post_detail_app
>>>
>>>
>>> matching = bundle(
... m('/', home_app, 'home'),
... m('/post/', post_list_app, 'post_list'),
... m('/post/{post_slug}/', post_detail_app, 'post_detail'),
... )
>>>
>>> if __name__ == '__main__':
... app = make_wsgi_app(matching)
...
... httpd = make_server('', 8000, app)
... httpd.serve_forever()
Now, accessing from your browser:
http://127.0.0.1:8000/ => home_app will be called
http://127.0.0.1:8000/post/ => post_list_app will be called
http://127.0.0.1:8000/post/some_slug/ => post_detail_app will be called
URL arguments
When path elements covered with braces, this handled as URL arguments. This path element can match any string and you can use that string in your WSGI application.
The URL arguments can get from environ[‘matcha.matched_dict’] in your WSGI application. For instance, accessing to ‘/post/hello_world/’, you can get ‘hello_world’ string as URL arguments like this:
>>> # In post_detail_app:
>>> environ['matcha.matched_dict']
{'post_slug': 'hello_world'}
Wildcard
If the last element in pattern starts with ‘*’, the following string is considered as wildcard_name. And correspond path will always match.
>>> matching = bundle(
m('/', home_app, 'home'),
m('/docs/*doc_tree', document_app, 'docs')
)
And then, you can access them:
http://127.0.0.1:8000/ => home_app
http://127.0.0.1:8000/docs => document_app
http://127.0.0.1:8000/docs/about => document_app
http://127.0.0.1:8000/docs/about/contributing => document_app
After /docs/ it will match any cases.
And getting the argument.
>>> # In document_app, when accessed by '/docs/about/contributing'
>>> environ['matcha.mathed_dict']
{'doc_tree', ['about', 'contributing']}
>>> environ['PATH_INFO']
'/about/contributing'
>>> environ['SCRIPT_NAME']
'/docs'
You can also use this implementation to call another WSGI application such as dispatching again by using the left PATH_INFO
Reversing
Web pages usually contains some URLs for a another page. In this case, post list page is for showing URLs to each Blog posts (to post_detail application).
Let’s take URL to post_detail application. You can do like this:
>>> # In post_list_app
>>> matching = environ['matcha.matching']
>>> matching.reverse('post_detail', post_slug='about_matcha')
'/post/about_matcha/'
The first positional argument is a signature string for applications. It provided as the third argument of each Matching’s constructors.
The keyword argument is a string to fill up the URL arguments.
Careflly, reverse method will raise NotReverced exception when any URLs is not matched.
OK. and then, you can provide this URL to some TemplateEngines to display HTML pages.
Including another matchings
For more reusability, let’s separate applications for blogs from core matching.
# In yourproject/blog/matching.py file
from matcha import Matching as m, bundle
from yourproject.blog import post_list_app, post_detail_app
matching = bundle(
m('/', post_list_app, 'post_list'),
m('/{post_slug}/', post_detail_app, 'post_detail'),
)
And then, applying this to core by using include function:
>>> from matcha import include
>>> from yourproject.blog.matching import matching as blog_matching
>>>
>>> matching = bundle(
... m('/', home_app, 'home'),
... include('/post/', blog_matching)
... )
Matching paths will be like this:
/ => home application
/post/ => post_list application
/post/some_slug/ => post_detail application
By using include, you can separate paths based on each applications and avoid repeating of descriptions (such as ‘/post/’).
Name for reverisng to childs are not affected in any way. This value should be unique, even in the all of matchings (fixed in matcha 0.3):
>>> matching.reverse('post_detail', post_slug='some_slug')
'/post/some_slug/'
Setting your 404 WSGI application
The path matching failed the maked application by matcha will return a plain 404 page. But most cases, you want to custorm this page more friendly for users.
For solving this, matcha.make_wsgi_app can take not_found_app keyword argument to provide your own WSGI application for showing 404 page.
By default, the not_found_app is matcha.not_found_app.
What is Matching objects
Almost core features provided by matcha dispatcher is implamented as Matching objects.
Now, through above example, you recognize matching is like this:
matching is created by using bundle function and Matching class.
Registering WSGI apllications to matching.
matching can get from environ dictionaly
Not wrong, but Matching class is something more flexible than your recognition.
Calling
matching is callable
taking environ dictionary
sideeffecting environ dictionary
returning matched case and dictionary
That sideeffection is for PATH_INFO and SCRIPT_NAME to tell which path elements are processed to another WSGI application.
>>> environ = {'PATH_INFO': '/htt', 'SCRIPT_NAME': '/about'}
>>> Matching('/htt', about_htt_app)(environ)
(about_htt_app, {})
>>> environ
{'PATH_INFO': '', 'SCRIPT_NAME': '/about/htt'}
Getitem from matching
cailling of matching requires environ dictionaly, but using getitem you can only apply path to get matched case and dictionaly.
>>> Matching('/htt', about_htt_app)['/htt']
(about_htt_app, {})
Registering not only WSGI app
Second positional argument (case keyword argument) of Matching class can take any objects you like, not only WSGI app.
>>> Matching('/home', 'home')['/home']
('home', {})
For instance, you can register strings and use this as signature for some views. something like route_name on Pyramid.
Adding matchings
Actually, bundle function used in above examples is just for adding provided positional arguments (addable objects). So you can make WSGI application without this function:
>>> app = make_wsgi_app(
... Matching('/', home_app) + \
... Matching('/abount', about_app)
... )
Thanks
matcha dispatcher has been influenced these dispatchers:
Django ‘s URL dispatcher
Thanks for them.
Resources
Changes
0.3
Un-supporting name argument on include function
Before this change, user applied include name, the reversing in included app will break, because the name for reversing already changed.
0.2
Supporting Wildcard
0.1
Initial release.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.