Recipes

Deployment

Apache mod_wsgi

weblayer is WSGI compatible and so can be served using Apache mod_wsgi. By default, Apache mod_wsgi looks for a callable entry point called application in an application script file (essentially a Python module saved with a .wsgi extension).

In our example, ./src/weblayer/examples/deploy/mod_wsgi, the application script file is app.wsgi:

from weblayer import Bootstrapper, RequestHandler, WSGIApplication

class Hello(RequestHandler):
    def get(self, world):
        return u'hello %s' % world
    


mapping = [(r'/(.*)', Hello)]

config = {
    'cookie_secret': '...', 
    'static_files_path': '/var/www/static',
    'template_directories': ['templates']
}

bootstrapper = Bootstrapper(settings=config, url_mapping=mapping)
application = WSGIApplication(*bootstrapper())

This is then hooked up in your apache configuration using the WSGIScriptAlias directive, e.g.:

WSGIScriptAlias / /path/to/app.wsgi

Appengine

As a pure Python package that’s compatible with Python 2.5, weblayer works perfectly on Google App Engine. In our example, ./src/weblayer/examples/deploy/appengine, the application is in app.py:

from google.appengine.ext.webapp.util import run_wsgi_app
from weblayer import Bootstrapper, RequestHandler, WSGIApplication

class Hello(RequestHandler):
    def get(self, world):
        return u'hello %s' % world
    


mapping = [(r'/(.*)', Hello)]

config = {
    'cookie_secret': '...', 
    'static_files_path': '/var/www/static',
    'template_directories': ['templates']
}

def main():
    bootstrapper = Bootstrapper(settings=config, url_mapping=mapping)
    run_wsgi_app(WSGIApplication(*bootstrapper()))

With the App Engine configuration in app.yaml (you should replace weblayer-demo with your application id):

application: weblayer-demo
version: 1
runtime: python
api_version: 1

handlers:
- url: .*
  script: app.py

You can see this deployed at weblayer-demo.appspot.com.

Note

You must have weblayer and its dependencies included your Python Path, e.g.: by copying them into the same folder as app.py or by amending sys.path. Check the install_requires list in setup.py but at the time of writing this means you need to include:

  • mako,
  • MarkupSafe
  • pkg_resources.py
  • venusian,
  • weblayer,
  • webob,
  • zope.component
  • zope.event
  • zope.interface

Paste

weblayer is WSGI compatible and so can be served using Paste, as shown by ./src/weblayer/examples/deploy/paste. The demo application is in demo.py:

from weblayer import Bootstrapper, RequestHandler, WSGIApplication

class Hello(RequestHandler):
    def get(self, world):
        return u'hello %s' % world
    


# map urls to request handlers using regular expressions.
mapping = [(r'/(.*)', Hello)]

def app_factory(global_config, **local_conf):
    
    # merge the global and local config
    config = global_config
    config.update(local_conf)
    
    # make `config['template_directories']` a list
    config['template_directories'] = [config['template_directory_path']]
    
    # instantiate the bootstrapper
    bootstrapper = Bootstrapper(settings=config, url_mapping=mapping)
    
    # return a bootstrapped `WSGIApplication`
    return WSGIApplication(*bootstrapper())
    

setup.py defines a weblayer-pastedemo Egg with demo.app_factory as its main paste.app_factory entry point:

from setuptools import setup

setup(
    name = 'weblayer-pastedemo',
    version = '0.1',
    install_requires=[
        'weblayer',
        'PasteScript',
        'WSGIUtils'
    ],
    scripts = ['demo.py'],
    entry_points = {
        'paste.app_factory': [
            'main=demo:app_factory',
        ]
    }
)

config.ini tells Paste to use the main entry point to the weblayer-pastedemo egg:

[app:main]
use = egg:weblayer-pastedemo#main
static_files_path = %(here)s/static
template_directory_path = %(here)s/templates
cookie_secret = eH3d5sh4WSTs4ni6pULwiGgAdGy9GI3XNqi9YjznTVZ=

[server:main]
use = egg:PasteScript#wsgiutils
host = 127.0.0.1
port = 8080

To run the example, develop the weblayer-pastedemo egg:

cd ./src/weblayer/examples/paste
python setup.py develop

Then use Paste Script’s paster serve command:

paster serve config.ini

Note

The main difference between this and the Hello World example is the demo.app_factory function. Notice that it accepts global_config and local_config:

def app_factory(global_config, **local_conf):
    
    # merge the global and local config
    config = global_config
    config.update(local_conf)
    
    # make `config['template_directories']` a list
    config['template_directories'] = [config['template_directory_path']]
    
    # instantiate the bootstrapper
    bootstrapper = Bootstrapper(settings=config, url_mapping=mapping)
    
    # return a bootstrapped `WSGIApplication`
    return WSGIApplication(*bootstrapper())
    

global_config comes from the config file passed to paster serve, in this case config.ini. local_config comes from the command line, e.g.:

paster serve config.ini cookie_secret=blah

Note

All Paste config values are strings. If you want other types as your settings values, you will need to parse the config values yourself (i.e.: it is outside the scope of weblayer). We illustrate this in the example by parsing the template_directory_path string into the template_directories list required by weblayer.template:

    # make `config['template_directories']` a list
    config['template_directories'] = [config['template_directory_path']]

Buzzword Compliance

Async / Non-blocking

weblayer is agnostic as to whether it is served by a threaded, blocking server or an asynchronous, non-blocking one. The simplest way to serve a non-blocking weblayer application is to deploy it behind Gunicorn.

To use weblayer with Gunicorn:

  • follow the instructions above on deploying with Paste
  • amend your [server:main] section, as per these instructions, e.g.:
[server:main]
use = egg:gunicorn#main
worker_class = gevent
workers = 4
host = 127.0.0.1
port = 8080

Each request to your application will be handled in a Greenlet. This will be faster than a multi-threaded server if and only if your application is IO bound.

Websockets

The websocket specification is not yet fixed. Different browsers implement different handshake procedures. In December 2010, Firefox withdrew support for websockets, pending resolution of a security issue.

When these issues are resolved, it may be appropriate for weblayer to provide some sort of native web socket aware request handler. Until then, simply use existing WSGI middleware, such as gevent-websocket and then look in environ['wsgi.websocket'] or environ['wsgi.input'] (or wherever the middleware sticks the reference to the socket) when writing request handler code, e.g.:

class Echo(RequestHandler):
    """ Example websocket handler that echoes back any messages recieved.
    """

    def get(self):
        ws = self.request.environ['wsgi.websocket']
        while True:
            msg = ws.wait()
            if msg is None:
                break
            ws.send(msg)

Table Of Contents

Previous topic

User Guide

Next topic

Modules

This Page