Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Web Site Process Bus

A Bus object is used to contain and manage site-wide behavior: daemonization, HTTP server start/stop, process reload, signal handling, drop privileges, PID file management, logging for all of these, and many more.

In addition, a Bus object provides a place for each web framework or application to register code that runs in response to site-wide events (like process start and stop), or which controls or otherwise interacts with the site-wide components mentioned above. For example, a framework which uses file-based templates would add known template filenames to an autoreload component.

The Bus object is flexible enough to be useful in a variety of invocation scenarios:

  1. The deployer starts a site from the command line via a framework-neutral deployment script; applications from multiple frameworks are mixed in a single site. Command-line arguments and configuration files are used to define site-wide components such as the HTTP server, WSGI component graph, autoreload behavior, signal handling, etc.
  2. The deployer starts a site via some other process, such as Apache; applications from multiple frameworks are mixed in a single site. Autoreload and signal handling (from Python at least) are disabled.
  3. The deployer starts a site via a framework-specific mechanism; for example, when running tests, exploring tutorials, or deploying single applications from a single framework. The framework controls which site-wide components are enabled as it sees fit.

The Bus object in CherryPy uses topic-based publish-subscribe messaging to accomplish all this. A few topic channels are built in ('start', 'stop', 'exit', 'log(msg)', and 'graceful'). Frameworks and site containers are free to define their own. If a message is sent to a channel that has not been defined or has no listeners, there is no effect.

In general, there should only ever be a single Bus object per process. Frameworks and site containers share a single Bus object by publishing messages and subscribing listeners.

The Bus object works as a finite state machine which models the current state of the process. Bus methods move it from one state to another; those methods then publish to subscribed listeners on the channel for the method. See wiki:WSPBSpec for more details.

Writing deployment scripts

Using a bus object encourages you to write your startup and shutdown code as objects in a reusable fashion. For example, given a typical deployment script like this:

db = None

if __name__ == '__main__':
    config = get_config()
    
    sys.stderr.write("Opening database connection: %s." % self.connstr)
    global db
    db = dbapi.open(config['connstr'])
    try:
        cherrypy.engine.start()
        cherrypy.engine.block()
    finally:
        sys.stderr.write("Closing database connection: %s." % self.connstr)
        db.close()

...using a Bus object encourages you to move that code into an object:

class Persistence(plugins.SimplePlugin):
    """Manages the database."""
    
    def __init__(self, bus, connstr):
        self.bus = bus
        self.connstr = connstr
        self.db = None
    
    def start(self):
        self.bus.log("Opening database connection: %s." % self.connstr)
        self.db = dbapi.open(connstr)
    start.priority = 20
    
    def stop(self):
        self.bus.log("Closing database connection: %s." % self.connstr)
        self.db.close()
        self.db = None


if __name__ == '__main__':
    config = get_config()
    Persistence(cherrypy.engine, config['connstr']).subscribe()
    cherrypy.engine.start()
    cherrypy.engine.block()

The first approach works well enough when you're only deploying simple sites, with few services and a single framework. But when you multiply frameworks x services x servers the scripts get ugly real quick. Using Bus plugins allows you to decouple the definition of site-wide services from their attachment and execution.

Hosted by WebFaction

Log in as guest/cpguest to create tickets