About
This distribution provides WSGI middleware for "RESTful" mapping of URL paths to WSGI applications. Selector now also comes with components for environ based dispatch and on-the-fly middleware composition. There is a very simple optional mini-language for path expressions. Alternately we can easily use regular expressions directly or even create our own mini-language. There is a simple "mapping file" format that can be used. There are no architecture specific features (to MVC or whatever). Neither are there any framework specific features. Best of all, selector is the simplest thing that will work well (IMHO).
Download
Download the latest release from selector's Cheese Shop Page. (Blessed are the cheesemakers!)
Browse selector's public subversion repository.
Read and subscribe to selector updates and info.
Updates
Path Consumption (0.8.11)
Selector now supports path consumption and it is now the default behavior.
The __init__ method of the Selector class now
has an additional optional keyword parameter, consume_path,
which defaults to True. This sets the attribute
Selector.consume_path. To support this new feature,
Selector.select now returns a four tuple instead of a
three tuple. The additional item returned is the matched portion of the
path. If Selector.consume_path evaluates to true,
SCRIPT_NAME and PATH_INFO are handled
appropriately. The matched portion of the path is appended to a list found or created in
environ['selector.matches'].
To support the case of matching only some beginning portion of a path,
that will be more common with this feature, the default path
expression parser now supports using a pipe character at the end of
a path expression, indicating an open-ended match.
In other words, if you are going to match and consume a part of
the path and leave the rest for some further dispatch (or whatever),
you use an expression like this: /foo|.
This will match anything that starts with /foo and
only consume that part of the path.
Routing Args (0.8.10)
The previous experimental ['wsgi.url_vars'] environ key is now
['wsgiorg.routing_args']
and the spec is marked as accepted.
This is no longer experimental and is here to stay. The pliant
and opliant decorators are still considered experiments.
Changes One More Time (0.8.9)
['wsgi.url_vars']
is now used and is likely to be more portable so
will be preferable to ['selector.vars'].
Keep in mind that this is experimental, for now.
['selector.vars'] will stay
around for a good while but may be dropped eventually.
Positional args are now supported in path
expressions and will show up in both:
Decorators to apply ['wsgi.url_vars'] as extra args to wsgi-like
callables are included. These are quickly implemented and very experimental:
Selector now provides classes for naked object and HTTP method to object method based dispatch, for completeness. I am not advocating this style of dispatch, in general, but there is a time and a place for (most) everything. This should be pretty self explanatory, I hope. Email me if you have questions. I don't mind.
Yup, More Changes (0.8.7)
...and I still haven't folded everything into the rest of the tutorial. Sorry, everybody.
You can now specify a wrapper for the callables that you are mapping to. You could use YARO for instance:
You can also pass wrap into slurp or slurp_file
and you can use it as a directive in your mapping files
(@wrap yaro:Yaro) or even just set it directly.
Even More Little Changes (0.8.5)
You can now specify '_ANY_' as an HTTP method to catch any method of request. This is not recommended unless you have a good reason, like supporting a DAV component. (Thanks to Damjan Georgievski for pointing out good reasons to finally add this).
Little Changes (0.8.1)
StatusApphas been removed- 404s are now handled by
Selector.status404 - 405s are now handled by
Selector.status405and include appropriate HTTPAllowheader Selector.selectnow returns a tuple of 3 (rather than 2) in order to include supported methods of any matched path expression.Selector.__call__now expects a len-3 tuple fromSelector.select
Newer Features Still (0.8)
If you are new to selector, skip this section and come back to it at the end.
Support for a text file format for expressing mappings has been added. Lets call it a "mapping file".
Example
Explanation
Path expressions are each on a line with no leading whitespace.
HTTP Method to WSGI app mappings for a given path expression are
on lines with leading whitespace which immediately follow.
The @parser and @prefix directives
can be used to change the selector settings of the same names
while the file is being parsed.
Resolver Statements
The statements after the HTTP method names, that specify the WSGI app to be called, are in the following format. I am calling them "resolver statements" after the function which interprets them for lack of a better name.
Feeding Mapping Files to Selector
There are two ways to feed a mapping file to a Selector
instance. Either one may be given a file name or a file like object.
New New Features (0.7)
If you are new to selector, skip this section and come back to it at the end.
New classes EnvironDispatcher
and MiddlewareComposer have been added.
The Selector class itself is only intended for HTTP method and path based
delegation (based on the first line of an HTTP request, in other words).
This means that you can look at your mappings and easily see what is going
to respond to what. This traceability is very important. Other delegation
decision may be desirable, however.
After your selector instance has decided what to call, that
"handler" may conduct further delegation. I believe that Ward Cunningham
has called this "Compound Delegation". Usually this would mean examining
the environ to decide where to go next. (In theory the "handler"
could even be another Selector instance.)
To implement this environ based dispatch (probably a secondary dispatch),
selector now includes specialized WSGI middleware.
EnvironDispatcher is
instantiated with a list of (predicate, wsgi_app) pairs.
Each predicate is a callable that takes one argument (environ)
and return True or False.
When called, the instance iterates through the pairs until it finds
a predicate that returns True and runs the app paired with it.
Another new WSGI middleware included in selector allows
us compose middleware on the fly
(compose as in function composition) in a similar way.
MiddlewareComposer
also is instantiated with a list of rules,
only instead of WSGI apps you have WSGI middleware.
When called, the instance applies all the
middlewares whose predicates are true for environ
in reverse order, and calls the resulting app.
Give the code above a GET on /endpoint would be sent to
an app equivalent to a(c(e(app))).
a,
b,
c,
d and
e are, of course, WSGI middleware and
app
is a WSGI app.
Note: These changes to selector have not yet been folded into the rest of this tutorial.
New Features (0.6)
If you are new to selector, skip this section and come back to it at the end.
The default parser now supports two new features: optional portions and pattern types.
Optional portions of path expressions are indicated with square brackets. If you wanted to map to a path with or without a trailing slash, you would do something like this:
Nesting is supported, so the following will match
/foo, /foo/, /foo/2 or /foo/2/:
Pattern types are used to control the matching behavior of path variables. Pattern types are specified in path variables after a colon. To capture a number called bar you might do something like this:
Pattern types are defined in a dict patterns on the parser object.
There are a few types built in:
The default type (when none is specified) is chunk.
Simply modifying this dict will extend the types supported.
Note: These changes to selector have not yet been folded into the rest of this tutorial.
Tutorial
Selector works like this:
Simple, no?
If you have ever designed a REST protocol you have probably made a table that looks something like this:
| POST | Create a new foo with id == {id}. |
|---|---|
| GET | Retrieve the foo with id == {id}. |
| PUT | Update the foo with id == {id}. |
| DELETE | Delete the foo with id == {id}. |
Selector was designed to fit mappings of this kind.
Basic Example
Lets suppose that we are creating some very simple app.
The only requirement is that
http://example.com/myapp/hello/Jim
responds with some simple page that says hello to Jim
(where Jim can actually be any name at all).
The "URL space" looks like this:
| GET | Say hello to {name}. |
|---|
This is all the code we need:
The above probably makes sense to you if you are familiar with WSGI (PEP 333).
To run this you need will need flup and any HTTP server that supports SCGI. Flup also comes with FCGI and AJP servers; just change the import statement. It is easy to use flup with Apache or lighttpd.
Of course, you can use selector under any server that will run a WSGI app. A web server that supports WSGI (called wsgiref) is slated for inclusion in a future version of the Python standard library.
Mappings and Matches
Mappings consist of a path expression and a dictionary of WSGI apps keyed by HTTP methods.
When a mapping is added, the path expression is converted into a regular expression by a syntax parser. This parser can be turned off so that you can use regular expression syntax directly.
Upon invocation (usually by a server who has received a request)
selector will look for a match in the mappings it has been given,
in the order it has been given them.
If no match is found, it will send a 404 Not Found.
When a URL path is matched, the
named groups in the regex
are used to populate a dict in environ['selector.vars'].
The HTTP method is then located in the corresponding dictionary.
If it is found the appropriate WSGI app is run.
Otherwise a 405 Method Not Supported is returned.
This example explains the basic concept most simply:
... and you receive a request to:
... you can do this in load_archive
without raising an exception:
Path Expression Parsers
Selector objects have a parser member. This is a callable
that takes one argument and returns a regex string. By default
this member is an instance of SimpleParser.
A Selector object passes each path expression through its
parser and compiles the resulting regex.
SimpleParser makes a named group for each
{name} that appears in the path expression, escapes
the rest, and adds '^' and '$' to the ends, respectively.
So this:
... is translated into this:
To use regular expressions directly, set parser to a do-nothing. Our example would look like this:
This will now only say hello to names that begin with a capitol 'A' followed by one or more lowercase letters. You probably wont need the full power of regular expressions most of the time but when you do, it is that easy.
Using Prefix
Selector objects have a prefix member that can be used
to prepend some common string to the beginning of each path expression
as mappings are added. The prefix is attached to the path expression
before it is passed through the parser. The prefix can
be changed between adding mappings. Usually the prefix is set to
some common base path used throughout your application.
Using Selector.add()
The add() method can be called in a number of ways.
You can specify supported HTTP methods for a mapping and how to delegate them with keyword arguments:
Alternately we can pass in a dictionary:
If we do both, the keyword arguments will override the dictionary.
In this case new_get wins:
You could easily pass around a common dictionary and override methods for mappings that have special cases.
The add() method's third argument is prefix
which can be passed positionally or by keyword. The given prefix
will be used instead of the current prefix only for that mapping.
... is the same as:
Initializing a Selector
It is possible to populate a Selector object
with a whole list of mappings at once:
The Selector class's __init__() can be
used to set the prefix and parser as well:
Using Selector.slurp()
The slurp() method can also be used to add mappings
by bulk.
slurp() can also be passed prefix and parser as
keyword arguments. In this case the prefix and parser supplied
will be used only for the batch of mappings being "slurped".
Custom HTTP 404 and 405
The Selector class has WSGI app members
to send simple HTTP status messages for 404s and 405s.
You can subclass Selector to customize their
behavior:
Questions, comments, suggestions, bugs... : luke.arno@gmail.com