documentation.config

The config.py file in the Viverra distribution determines most of Viverra's run-time behavior, from URL dispatch to exception handling. It is fairly well-commented, and I hope it isn't difficult to understand on its own. However, for convenience here is a full description of all the default configuration variables, with in-depth sections on the more sparsely commented parts of the configuration file.

Path Manipulation

Please see the installation documentation for this -- in the current implementation, it consists largely of ensuring Viverra is in mod_python's PATH.

Site-specific Configuration

Since the viverra.config module is intended to be a convenient location for settings, and Viverra itself may well be installed in a central location (and should in any case be conveniently upgradeable by copying of files, without breaking an application) it includes a facility for importing settings into its namespace, through the local_override setting.

local_override can currently take one of three values:

Of course, if no site-specific configuration is set, Viverra will simply use the values already set in the main configuration file. If there is only one Viverra-powered site this is the easiest and fastest way to do things, and in such a situation local_override should be set to None so that Viverra doesn't waste time searching for auxiliary configuration data.

The Application

The 'application' that is run by Viverra's server adapter is an application in the WSGI sense, and is taken from the config.app variable, which is by default set to the core request-handling function at core.entrypoint. Its behavior can be modified by WSGI-style 'middleware' -- see the Middleware section for how it is handled.

URL Mapping

The URL mapping system employed by Viverra is modeled on that of Django, using strings and regular expressions to map URLs to Python objects.

The site's layout is structured as a list, although this will be 'compiled' to a dictionary. The very first element may be a site-wide path "prefix", with which you tell Viverra in which sub-directory of your site's document root it lives, although this is entirely optional.

Each of the remaining entries in the list should be a tuple, in one of the following forms:

  1. ( regex, callable[, extra_args])
  2. ( prefix, submap )

In a basic mapping entry (type 1), regex is a regular expression, callable is either a callable object or a string representing the path to a callable (e.g. module.function or "module.function"), and extra_args is an optional dictionary of additional keyword arguments to be passed to the callable. Captured text from the regular expression is passed in as positional arguments to the callable (or, in the case of named groups used in the regular expression, keyword arguments). All arguments are passed as plain strings, and in all cases the first argument to the callable is a Viverra request object. Thus, this entry:

( r'^/site/(.*?)/(?<item_id>\d+)/?$', callable.object, { 'foo': 'bar' } )

when matched against the path http://www.example.com/site/blog/1234/, would generate a call to callable.object( <viverra.core.Request instance>, 'blog', item_id='1234', foo='bar' ).

The other kind of mapping entry (type 2), allows your URL layout to be separated into modules. Here, prefix is a prefix string (like the site-wide prefix mentioned earlier) which describes the sub-directory of the site that you want the included mappings to apply to, and submap is a list in this same layout format. (Yes, this does mean you can have almost arbitrary nesting). It can also include an optional dictionary of keyword arguments, which will be 'inherited' by all the mappings this specifies. Thus the below would specify that the mappings in the sub-list should be applied to the "/john" subdirectory, with the dictionary of arguments supplied as keyword arguments to all pages in that subdirectory:

 ( "john",
    [ (r'/?blog', john.blog),
      (r'/?art', john.art)
    ],
    { 'name': 'john'} ) 

Using 'nested' layouts as much as possible allows Viverra to use optimized dictionary and string operations, which makes URL resolution much faster. So instead of writing:

    ( r'^blog/entries/(?P<title>.*?).html$', blog.view_title )
    ( r'^blog/entries/(?P<date>\d+?).html$', blog.view_date )
    ( r'^blog/author.html$', blog.author_info ) 

You could write the below, which would be faster:

 ( "blog", [ ( r'^author.html$', blog.author_info ),
            ("entries", [ ( r'^(?P<title>.*?).html$', blog.view_title ),
                          ( r'^(?P<date>\d+?).html$', blog.view_date ) ] )
) 

Of course, any of the nested values in the above could be specified in another variable, and just the variable included. However, if complicated nested layouts are confusing, avoid them! . Ease of use is more important, and Viverra will of course work perfectly well without complicated nesting!

The URL Resolver

The config.urlresolve variable is what is used by Viverra to map URLs to page-producing callable objects. If it's set to None, as it is by default, then the standard URL-to-callable mapping (as described in detail above) will be used and urlresolve will automatically be re-set to the mapper object's resolve method.

If you set this to your own URL resolver, note that it has to have the same behavior as the default method in order for Viverra to be able to use it. That is, it must take a URL path (in string form, with or without a leading /) and return a three-element tuple of callable object, positional arguments, and keyword arguments (in that order).

Error Handlers

Error handlers are configured in the error_handlers dictionary, which maps an HTTP response code to a page-producing callable that is called every time the error is encountered -- customized URL Not Found pages, and the like, are expected to use this method. The default error handlers are defined in viverra.core and return vanilla error pages.

Middleware

In the interests of keeping setup simple and centralized (an attempt at DRY), the configuration file specifies and applies WSGI middleware. The list of middleware is applied to the application in the order in which it is entered in the list (thus if one piece of middleware depends on another, it must be in the list after its dependency.

Each middleware must take the WSGI application to be wrapped as a first argument, and if no other arguments are to be given then simply enter the middleware object in the list. Alternatively, you can enter a tuple in the list, with the middleware as the first element and a dictionary of keyword arguments to be passed as the second argument, as below:

 middleware = [
        NoArgumentsMiddleware,
        ( ArgumentsMiddleware, { 'foo': 'bar', 'baz': 'quux' } )
        ] 

Of course, middleware can still be applied any other way, but if this is done in the configuration file, the wrapped and ready-to-be-served application object is made easily available by importing viverra.config.

Debugging

This is a wheel that I haven't reinvented -- debugging can and should be done with any WSGI debugging middleware. I personally find the one from Colubrid very useful.