rutter: Python3-compatible WSGI composite

rutter forks the paste.urlmap module in order to provide a Python3-compatible impolementation, as well as the improved test coverage needed to support using the module across all supported Python versions.

The primary export of rutter is the rutter.urlmap.URLMap class. URLMap instances are dictionary-like objects which dispatch to WSGI applications based on the URL.

The keys in a URLMap are URL patterns, which match as prefixes of the request URL (e.g., like PATH_INFO.startswith(key)). Its values are WSGI applications to which matching requests are dispatched. On finding a match, the URLMap adjusts the SCRIPT_NAME and PATH_INFO environmental variables to indicate the new context.

URL Matching Rules

  • URLs are matched most-specific-first, i.e., longest URL first.
  • URL prefixes can also include domains, e.g. http://blah.com/foo. Domains can also be specified as tuples (‘blah.com’, ‘/foo’).
  • If a given pattern includes a domain, its path will only be tested if the HTTP_HOST environment variable matches.
  • Patterns which do not have domains will be tested only if no domain-specifc pattern matches.

Examples

Sample Applications

Assume we want to serve two WSGI applications provided by separate modules, alpha:

from pyramid.config import Configurator
from pyramid.view import view_config

@view_config(renderer='string')
def hello_alpha(request):
    return 'Hello, Alpha'

def main(global_config=None, **local_config):
    config = Configurator()
    config.scan()
    return config.make_wsgi_app()

and bravo.

from pyramid.config import Configurator
from pyramid.view import view_config

@view_config(renderer='string')
def hello_bravo(request):
    return 'Hello, Bravo'

def main(global_config=None, **local_config):
    config = Configurator()
    config.scan()
    return config.make_wsgi_app()

Note

Although these examples use pyramid; any WSGI-compliant application can be used as a dispatch target.

Imperative Configuration in Python

from wsgiref.simple_server import make_server

from rutter.urlmap import URLMap

from alpha import main as alpha_main
from bravo import main as bravo_main

def main():
    # Grab the config, add a view, and make a WSGI app
    urlmap = URLMap()
    urlmap['/alpha'] = alpha_main()
    urlmap['/bravo'] = bravo_main()
    return urlmap

if __name__ == '__main__':
    # When run from command line, launch a WSGI server and app
    app = main()
    server = make_server('0.0.0.0', 6543, app)
    server.serve_forever()

Assuming that alpha and bravo are importable, along with rutter, we can run this application:

$ /path/to/python example/imperative.py

and then visit the two applications at http://localhost:6543/alpha and http://localhost:6543/bravo.

Declarative Configuration using paste.deploy INI files

Assuming that we have a paste.deploy-compatible server starter (such as the pserve script installed by pyramid), we can configure the :class:`~rutter.urlmap.URLMap via an INI file:

[app:alpha]
use = egg:rutter_example#alpha

[app:bravo]
use = egg:rutter_example#bravo

[composite:main]
use = egg:rutter#urlmap
/bravo = bravo
/alpha = alpha

[server:main]
use = egg:pyramid#wsgiref
port = 6543
host = 127.0.0.1

And then run the composite application using the starter:

$ /path/to/pserve example/development.ini

The two applications are again available at http://localhost:6543/alpha and http://localhost:6543/bravo.