About
This distribution provides Yet Another Request Object (for WSGI) in a way that is intended to be simple and useful for web developers who don't want to have to know a lot about WSGI to get the job done. It's also a handy convenience for those who do like to get under the hood but would be happy to eliminate some boilerplate without the encumbrance of some all-singing-all-dancing framework.
Download
Download the latest release from yaro's Cheese Shop Page. (Blessed are the cheesemakers!)
Browse yaro's public subversion repository.
Read and subscribe to yaro
updates and info.
Hello World
Making a "hello world" WSGI app with yaro is easy.
from yaro import Yaro
@Yaro
def hello_world(req):
return "Hello World!"
yaro.Yaro
The Yaro class's __init__() method
takes a callable which will accept a request object. The instance
that is created is a WSGI application that uses
this callable to process requests.
Another way to write your hello world is:
class MyApp(object):
def __call__(self, req):
req.res.body = "Hello World!"
app = Yaro(MyApp())
When handling a request, the Yaro instance creates
a yaro.Request
and passes it to the callable you have given it. If it gets
None back it looks in Request.res.body
(the body of the response of the request object). What is returned
or found in Request.res.body can be a string, an iterable
(such as a list or generator) or a file-like object.
Yaro (since version 0.3) now supports "extra properties" that are
added to the yaro.Request object when it is instantiated.
Rules for adding these properties are passed to the
yaro.Yaro object when it is instantiated. Rules take on
the format of 2 or 3 length tuples:
('propname', environ_key_or_callable, optional_default).
rules = [('session', get_session), ('user', 'REMOTE_USER', "nobody")]
app = Yaro(myapp, extra_props=rules)
yaro.Request
The Request object has a number of useful members:
| Member | Description |
|---|---|
method | The method of the request ('GET', 'POST'...) |
content_type | The content type of the request |
content_length | The content length of the request |
query | A dictionary of query string parameters |
form | A dictionary of fields from a web form (lazy) |
body | A string of the request body (lazy) |
cookie | A cookie.SimpleCookie populated from the headers (lazy) |
uri | An object representing the request URI |
redirect | A method to redirect the request |
forward | Hand off to a yaro compatible callable |
wsgi_forward | Hand off to a WSGI compatible callable |
res | An object representing the response to this request |
environ | The WSGI provided environ |
The content_type and content_length
fields come strait from the HTTP request, if those headers were provided.
environ depends on the environment, as its name implies.
This is a dictionary of variables like those that you would be provided
by CGI. See PEP 333
for details.
Some further descriptions can be found below.
yaro.Request.query
The query dictionary will represent the parameters of
the request query string.
Its values will be strings or lists of strings.
Values associated with parameter names ending in a pair of
brackets will end up in lists even if that parameter
occurs only once. This is better explained by example:
?foo=1&bar=2&bar=3 -> {'foo':'1', 'bar':['2','3']}
?foo[]=1&bar=2&bar=3 -> {'foo':['1'], 'bar':['2','3']}
The brackets are a way of indicating that you want a list so as to avoid boilerplate. This may seem a little odd but it saves you from having to write silly code to uncollapse single values.
# HAVING TO DO THIS IS LAME. DON'T.
if not isinstance(req.query['foo'], list):
req.query['foo'] = [req.query['foo']]
Please note that if 'foo[]' and 'foo' are both used as input names, they will step on each other. Use one or the other.
yaro.Request.form
The form dictionary will have strings,
cgi.FieldStorage
objects (used for file uploads) or lists of strings and/or
cgi.FieldStorage objects as its values.
As with query parameters, field names ending in a pair of brackets indicate that you want a list even if there is only one. This likewise results in avoiding boilerplate whenever you are processing a form field where a user could have chosen or input one or many instances.
When a name does not end in brackets, you will end up with a list only if there is more than one field by that name. Again, if 'foo[]' and 'foo' are both used as input names, they will step on each other. Use one or the other.
This is loaded lazily to prevent reading in everything from the client when you don't want to.
yaro.Request.body
This is a string of the request body. Good for AJAX and web services.
This is loaded lazily to prevent reading in everything from the client when you don't want to.
yaro.URI
The URI class is itself a callable and has several useful members:
| Member | Description |
|---|---|
scheme | The URI scheme |
host | The URI host name |
port | The URI port number |
script | The script name portion of the path |
path | The rest of the path |
query | The raw query string |
host_uri | A method to recreate the host's root URI |
application_uri | The host's root URI plus the script name |
Calling the URI instance with no arguments
returns a reconstruction of the entire URI, sans query string.
Calling it with a with_qs=True will get you
the query string.
Passing it with another path will construct a URI
like the original uri, except for the path.
# processing a request: GET http://example.com:9999/foo/bar?baz=blarg
# where /foo is the script name
>>> req.uri.server_uri()
'http://example.com:9999'
>>> req.uri.application_uri()
'http://example.com:9999/foo'
>>> req.uri()
'http://example.com:9999/foo/bar'
>>> req.uri(with_qs=True)
'http://example.com:9999/foo/bar?baz=blarg'
>>> req.uri('eggs')
'http://example.com:9999/foo/eggs'
>>> req.uri('../beer')
'http://example.com:9999/beer'
yaro.Request.redirect()
The redirect() method is just what it sounds like.
It takes a URI to redirect to and an optional permanent=False.
The default behavior is to set a 301 status on the response and
add an appropriate Location header. If permanent=False
None is returned.
The following is a simple app that
just redirects the request at random.
If this were handling requests for http://example.com/cheeses/
users would land at the page of a randomly selected cheese.
from yaro import Yaro
from random import choice
cheeses = ['Camembert.html', 'Limberger.html', 'Ilchester.html']
@Yaro
def random_cheese(req):
return req.redirect(req.uri(choice(cheeses)))
yaro.Request.forward()
Send handling on to another yaro compatible callable.
def app(req):
return req.forward(otherapp)
yaro.Request.wsgi_forward()
Send handling on to another WSGI compatible callable.
def app(req):
return req.wsgi_forward(wsgi_app)
yaro.Response
The Response class, found in Request.res, is very simple.
| Member | Description |
|---|---|
status | HTTP status like '200 OK' |
headers | A dictionary-like object of response headers |
body | The response body |
status defaults to '200 OK'.
For more information about headers see the docs of
wsgiref.headers.
The body is a string, iterable, or file-like object,
as mentioned above.
Questions, comments, suggestions, bugs... : luke.arno@gmail.com