Application framework

Launchpad is built around Zope components. Zope has meant substantially different things over the years (Wikipedia’s Zope article has a good overview, and The World of Zope is also a useful introduction to some terminology), so this document tries to be a little more specific. In modern terms, Launchpad uses the Zope Toolkit, but not the full Zope application server.

Component architecture

Launchpad uses the Zope Component Architecture (zope.interface and zope.component) extensively and pervasively. zope.interface defines most of Launchpad’s internal interfaces, and is also built upon by lazr.restful to define its external web service API. zope.component provides facilities for registering and looking up components, making it easier to decouple implementations from interfaces and reducing the need for circular imports.

The zope.schema package from the Zope Toolkit allows specifying more detailed types for attributes of interfaces, and zope.security allows enforcing interfaces using security policies. The latter is particularly important to Launchpad; it is the foundation of our ability to operate a complex multi-tenanted service in which users’ privileges often overlap in ways that cannot be accurately enforced purely at API boundaries, and must instead be enforced object-by-object. Most objects passed across interface boundaries within Launchpad are wrapped in Zope security proxies so that the security policy in lp.services.webapp.authorization is consulted on all attribute accesses.

URL traversal and publishing

Launchpad uses zope.traversing to “traverse” URLs (i.e. work segment-by-segment along them until reaching a model object with an associated view), and zope.publisher to define the details of how objects are “published” (i.e. the process of turning an HTTP request into an appropriate response, including various hooks that are run before and after calling the object itself).

Many of the details here are handled by custom code in lp.services.webapp, especially lp.services.webapp.publication which provides many application-specific hooks, and lp.services.webapp.publishing which defines much of Launchpad’s traversal framework. The URL structure is defined in various configure.zcml files: lp:url tags are used to build up the canonical URL for an object, and lp:navigation tags register classes called as part of traversal to resolve the next step from each intermediate object in the URL (starting at lp.app.browser.launchpad.LaunchpadRootNavigation).

Page templating

Launchpad uses Page Templates as its HTML templating mechanism. While systems like Jinja are more popular elsewhere, the Page Templates language has the benefit that source files are themselves valid XML rather than being a combination of two syntaxes, and it is not generally possible to write well-formed input that produces ill-formed output.

Page Templates were originally invented by Zope, and we still happen to use Zope’s original implementation of them, although Zope nowadays uses Chameleon instead.

Views and forms

Launchpad’s view layer (used for the web UI) is custom code, starting at lp.services.webapp.publisher.LaunchpadView. However, many views are built around “forms”, defined by building up a data schema using zope.interface and zope.schema and then using zope.formlib to generate HTML forms and to parse data from HTTP POST requests. zope.formlib also provides “widgets” whose job it is to define the precise rendering and parsing of particular form elements, and Launchpad defines a number of custom widgets for cases where zope.formlib’s widgets are insufficient.

Events

It’s often useful for parts of Launchpad to be able to subscribe to events generated by other parts of Launchpad. For example, a number of subscribers watch for objects such as comments being created in order to be able to assign “karma” to their creators. This is coordinated using zope.event, as well as subscriber tags in various configure.zcml files. lazr.lifecycle enhances this with common events for object creation, modification, and deletion.

WSGI server

Launchpad’s application server runs within Gunicorn, wrapped using Talisker to add several useful operational facilities.