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
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:
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']]
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:
[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.
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)