Bobo is a light-weight framework for creating WSGI web applications.
It's goal is to be easy to learn and remember.
It provides 2 features:
- Mapping URLs to objects
- Calling objects to generate HTTP responses
It doesn't have a templateing language, a database integration layer, or a number of other features that can be provided by WSGI middle-ware or application-specific libraries.
Bobo builds on other frameworks, most notably WSGI and WebOb.
Bobo can be installed in the usual ways, including using the setup.py install command. You can, of course, use Easy Install, Buildout, or pip.
To use the setup.py install command, download and unpack the source distribution and run the setup script:
To run bobo's tests, just use the test command:
You can do this before or after installation.
Bobo works with Python 2.4, 2.5, and 2.6. Python 3.0 support is planned. Of course, when using Python 2.4 and 2.5, class decorator syntax can't be used. You can still use the decorators by calling them with a class after a class is created.
Let's create a minimal web application, "hello world". We'll put it in a file named "hello.py":
This application creates a single web resource, "/hello.html", that simply outputs the text "Hello world".
Bobo decorators, like used in the example above control how URLs are mapped to objects. They also control how functions are called and returned values converted to web responses. If a function returns a string, it's assumed to be HTML and used to construct a response. You can control the content type used by passing a content_type keyword argument to the decorator.
Let's try out our application. Assuming that bobo's installed, you can run the application on port 8080 using [1]:
This will start a web server running on localhost port 8080. If you visit:
http://localhost:8080/hello.html
you'll get the greeting:
The URL we used to access the application was determined by the name of the resource function and the content type used by the decorator, which defaults to "text/html; charset=UTF-8". Let's change the application so we can use a URL like:
We'll do this by providing a URL path:
Here, we passed a path to the decorator. We used a '/' string, which makes a URL like the one above work. (We also omitted the import for brevity.)
We don't need to restart the server to see our changes. The bobo development server automatically reloads the file if it changes.
As its name suggests, the decorator is meant to work with resources that return information, possibly using form data. Let's modify the application to allow the name of the person to greet to be given as form data:
If a function accepts named arguments, then data will be supplied from form data. If we visit:
http://localhost:8080/?name=Sally
We'll get the output:
The decorator will accept , and requests. It's appropriate when server data aren't modified. To accept form data and modify data on a server, you should use the decorator. The decorator works like the decorator accept that it only allows and requests and won't pass data provided in a query string as function arguments.
The and decorators are convenient when you want to just get user input passed as function arguments. If you want a bit more control, you can also get the request object by defining a bobo_request parameter:
The request object gives full access to all of the form data, as well as other information, such as cookies and input headers.
The and decorators introspect the function they're applied to. This means they can't be used with callable objects that don't provide function meta data. There's a low-level decorator, that does no introspection and can be used with any callable:
The decorator always passes the request object as the first positional argument to the callable it's given.
The , , and decorators provide automatic response generation when the value returned by an application isn't a object. The generation of the response is controlled by the content type given to the content_type decorator parameter.
If an application returns a string, then a response is constructed using the string with the content type.
If an application doesn't return a response or a string, then the handling depends on whether or not the content type is 'application/json . For 'application/json , the returned value is marshalled to JSON using the (or simplejson ) module, if present. If the module isn't importable, or if marshaling fails, then an exception will be raised.
If an application returns a unicode string and the content type isn't 'application/json' , the string is encoded using the character set given in the content_type, or using the UTF-8 encoding, if the content type doesn't include a charset parameter.
If an application returns a non-response non-string result and the content type isn't 'application/json' , then an exception is raised.
If an application wants greater control over a response, it will generally want to construct a webob.Response object and return that.
We saw earlier that we could control the URLs used to access resources by passing a path to a decorator. The path we pass can specify a multi-level URL and can have placeholders, which allow us to pass data to the resource as part of the URL.
Here, we modify the hello application to let us pass the name of the greeter in the URL:
Now, to access the resource, we use a URL like:
http://localhost:8080/greeters/myapp?name=Sally
for which we get the output:
Hello Sally! My name is myapp.
We call these paths because they use a syntax inspired loosely by the Ruby on Rails Routing system.
You can have any number of placeholders or constant URL paths in a route. The values associated with the placeholders will be made available as function arguments.
If a placeholder is followed by a question mark, then the route segment is optional. If we change the hello example:
we can use the URL:
http://localhost:8080/greeters?name=Sally
for which we get the output:
Hello Sally! My name is Bobo.
Note, however, if we use the URL:
http://localhost:8080/greeters/?name=Sally
we get the output:
Hello Sally! My name is .
Placeholders must be legal Python identifiers. A placeholder may be followed by an extension. For example, we could use:
Here, we've said that the name must have an ".html" suffix. To access the function, we use a URL like:
http://localhost:8080/greeters/myapp.html?name=Sally
And get:
Hello Sally! My name is myapp.
If the placeholder is optional:
Then we can use a URL like:
http://localhost:8080/greeters?name=Sally
or:
http://localhost:8080/greeters/jim.html?name=Sally
Subroutes
Sometimes, you want to split URL matching into multiple steps. You might do this to provide cleaner abstractions in your application, or to support more flexible resource organization. You can use the subroute decorator to do this. The subroute decorator decorates a callable object that returns a resource. The subroute uses the given route to match the beginning of the request path. The resource returned by the callable is matched against the remainder of the path. Let's look at an example:
With this example, if we visit:
http://localhost:8080/employees/1/summary.html
We'll get the summary for a user. The URL will be matched in 2 steps. First, the path /employees/1 will match the subroute. The class is called with the request and employee id. Then the routes defined for the individual methods are searched. The remainder of the path,/summary.html , matches the route for the summary method. (Note that we provided two decorators for the summary method, which allows us to get to it two ways.) The methods were scanned for routes because we used the keyword argument.
The method has a route that is an empty string. This is a special case that handles an empty path after matching a subroute. The base method will be called for a URL like:
http://localhost:8080/employees/1
which would redirect to:
http://localhost:8080/employees/1/
The method defines another subroute. Because we left off the route path, the method name is used. This returns a Folder instance. Let's look at the Folder class:
The and classes use the decorator. The class decorator scans a class to make routes defined for it's methods available. Using the decorator is equivalent to using the keyword with decorator [2]. Now consider a URL:
http://localhost:8080/employees/1/documents/hobbies/sports.html
which outputs:
I like to ski.
The URL is matched in multiple steps:
- The path /employees/1 matches the class.
- The path matches the method, which returns a using the employee documents dictionary.
- The path matches the method of the class, which returns the dictionary from the documents folder.
- The path /sports.html also matches the method, which returns a using the text for thesports.html key.
5, The empty path matches the method of the class.
Of course, the employee document tree can be arbitrarily deep.
The decorator can be applied to any callable object that takes a request and route data and returns a resource.
Methods and REST
When we define a resource, we can also specify the HTTP methods it will handle. The and decorators will handle GET, HEAD and POST methods by default. The decorator handles POST and PUT methods. You can specify one or more methods when using the , , and decorators:
If multiple resources (resource, query, or post) in a module or class have the same route strings, the resource used will be selected based on both the route and the methods allowed. (If multiple resources match a request, the first one defined will be used [3].)
The ability to provide handlers for specific methods provides support for the REST architectural style. .. _configuration:
The bobo server makes it easy to get started. Just run it with a source file and off you go. When you're ready to deploy your application, you'll want to put your source code in an importable Python module (or package). Bobo publishes modules, not source files. The bobo server provides the convenience of converting a source file to a module.
The bobo command-line server is convenient for getting started, but production applications will usually be configured with selected servers and middleware using Paste Deployment. Bobo includes a Paste Deployment application implementation. To use bobo with Paste Deployment, simply define an application section using the bobo egg:
[app:main] use = egg:bobo bobo_resources = helloapp bobo_configure = helloapp:config employees_database = /home/databases/employees.db [server:main] use = egg:Paste#http host = localhost port = 8080
In this example, we're using the HTTP server that is built into Paste.
The application section () contains bobo options, as well as application-specific options. In this example, we used the bobo_resources option to specify that we want to use resources found in the hellowapp module, and the bobo_configure option to specify a configuration handler to be called with configuration data.
You can put application-specific options in the application section, which can be used by configuration handlers. You can provide one or more configuration handlers using the bobo_configure option. Each configuration handler is specified as a module name and global name [4] separated by a colon.
Configuration handlers are called with a mapping object containing options from the application section and from the DEFAULT section, if present, with application options taking precedence.
To start the server, you'll run the paster script installed with PasteScript and specify the name of your configuration file:
You'll need to install Paste Script to use bobo with Paste Deployment.
See Assembling and running the example with Paste Deployment and Paste Script for a complete example.