The WSGI application

The WSGI application receives requests and dispatches the appropriate handler, returning a response to the client. It stores the URI routes that the app will accept, configuration variables and registered objects that can be shared between requests. The WSGI app is also responsible for handling uncaught exceptions, avoiding that stack traces “leak” to the client when in production. Let’s take an in depth look at it now.

Note

If the WSGI word looks totally unfamiliar to you, read the Another Do-It-Yourself Framework tutorial by Ian Bicking. It is a very recommended introduction to WSGI and you should at least take a quick look at the concepts, but following the whole tutorial is really worth.

A more advanced reading is the WSGI specification described in the PEP 333.

Initialization

The webapp2.WSGIApplication class is initialized with three optional arguments:

  • routes: a list of route definitions as described in URI routing.
  • debug: a boolean flag that enables debug mode.
  • config: a dictionary of configuration values for the application.

Compared to webapp, only config was added; it is used as a standard way to configure extra modules (sessions, internationalization, templates or your own app configuration values).

Everything is pretty straighforward:

import webapp2

routes = [
    (r'/', 'handlers.HelloWorldHandler'),
]

config = {}
config['webapp2_extras.sessions'] = {
    'secret_key': 'something-very-very-secret',
}

app = webapp2.WSGIApplication(routes=routes, debug=True, config=config)

Router

URI routing is a central piece in webapp2, and its main component is the webapp2.Router object, available in the application as the webapp2.WSGIApplication.router attribute.

The router object is responsible for everything related to mapping URIs to handlers. The router:

  • Stores registered “routes”, which map URIs to the application handlers that will handle those requests.
  • Matches the current request against the registered routes and returns the handler to be used for that request (or raises a HTTPNotFound exception if no handler was found).
  • Dispatches the matched handler, i.e., calling it and returning a response to the WSGIApplication.
  • Builds URIs for the registered routes.

Using the router attribute you can, for example, add new routes to the application after initialization using the add() method:

import webapp2

app = webapp2.WSGIApplication()
app.router.add((r'/', 'handlers.HelloWorldHandler'))

The router has several methods to override how URIs are matched or built or how handlers are adapted or dispatched without even requiring subclassing. For an example of extending the default dispatching mechanism, see Request handlers: returned values.

Also check the Router API documentation for a description of the methods webapp2.Router.set_matcher(), webapp2.Router.set_dispatcher(), webapp2.Router.set_adapter() and webapp2.Router.set_builder().

Config

When instantiating the app, you can pass a configuration dictionary which is then accessible through the webapp2.WSGIApplication.config attribute. A convention is to define configuration keys for each module, to avoid name clashes, but you can define them as you wish, really, unless the module requires a specific setup. First you define a configuration:

import webapp2

config = {'foo': 'bar'}

app = webapp2.WSGIApplication(routes=[
    (r'/', 'handlers.MyHandler'),
], config=config)

Then access it as you need. Inside a RequestHandler, for example:

import webapp2

class MyHandler(webapp2.RequestHandler):
    def get(self):
        foo = self.app.config.get('foo')
        self.response.write('foo value is %s' % foo)

Registry

A simple dictionary is available in the application to register instances that are shared between requests: it is the webapp2.WSGIApplication.registry attribute. It can be used by anything that your app requires and the intention is to avoid global variables in modules, so that you can have multiple app instances using different configurations: each app has its own extra instances for any kind of object that is shared between requests. A simple example that registers a fictitious MyParser instance if it is not yet registered:

import webapp2

def get_parser():
    app = webapp2.get_app()
    # Check if the instance is already registered.
    my_parser = app.registry.get('my_parser')
    if not my_parser:
        # Import the class lazily.
        cls = webapp2.import_string('my.module.MyParser')
        # Instantiate the imported class.
        my_parser = cls()
        # Register the instance in the registry.
        app.registry['my_parser'] = my_parser

    return my_parser

The registry can be used to lazily instantiate objects when needed, and keep a reference in the application to be reused.

A registry dictionary is also available in the request object, to store shared objects used during a single request.

Error handlers

As described in Exception handling, a dictionary is available in the app to register error handlers as the webapp2.WSGIApplication.error_handlers attribute. They will be used as a last resource if exceptions are not caught by handlers. It is a good idea to set at least error handlers for 404 and 500 status codes:

import logging

import webapp2

def handle_404(request, response, exception):
    logging.exception(exception)
    response.write('Oops! I could swear this page was here!')
    response.set_status(404)

def handle_500(request, response, exception):
    logging.exception(exception)
    response.write('A server error occurred!')
    response.set_status(500)

app = webapp2.WSGIApplication([
    webapp2.Route(r'/', handler='handlers.HomeHandler', name='home')
])
app.error_handlers[404] = handle_404
app.error_handlers[500] = handle_500

Debug flag

A debug flag is passed to the WSGI application on instantiation and is available as the webapp2.WSGIApplication.debug attribute. When in debug mode, any exception that is now caught is raised and the stack trace is displayed to the client, which helps debugging. When not in debug mode, a ‘500 Internal Server Error’ is displayed instead.

You can use that flag to set special behaviors for the application during development.

For App Engine, it is possible to detect if the code is running using the SDK or in production checking the ‘SERVER_SOFTWARE’ environ variable:

import os

import webapp2

debug = os.environ.get('SERVER_SOFTWARE', '').startswith('Dev')

app = webapp2.WSGIApplication(routes=[
    (r'/', 'handlers.HelloWorldHandler'),
], debug=debug)

Thread-safe application

By default, webapp2 is thread-safe when the module webapp2_extras.local is available. This means that it can be used outside of App Engine or in the upcoming App Engine Python 2.7 runtime. This also works in non-threaded environments such as App Engine Python 2.5.

See in the Quick start (to use webapp2 outside of App Engine) tutorial an explanation on how to use webapp2 outside of App Engine.

Running the app

The application is executed in a CGI environment using the method webapp2.WSGIApplication.run(). When using App Engine, it uses the functions run_bare_wsgi_app or run_wsgi_app from google.appengine.ext.webapp.util. Outside of App Engine, it uses the wsgiref.handlers module. Here’s the simplest example:

import webapp2

class HelloWebapp2(webapp2.RequestHandler):
    def get(self):
        self.response.write('Hello, webapp2!')

app = webapp2.WSGIApplication([
    ('/', HelloWebapp2),
], debug=True)

def main():
    app.run()

if __name__ == '__main__':
    main()

Unit testing

As described in Unit testing, the application has a convenience method to test handlers: webapp2.WSGIApplication.get_response(). It receives the same parameters as Request.blank() to build a request and call the application, returning the resulting response from a handler:

class HelloHandler(webapp2.RequestHandler):
    def get(self):
        self.response.write('Hello, world!')

app = webapp2.WSGIApplication([('/', HelloHandler)])

# Test the app, passing parameters to build a request.
response = app.get_response('/')
assert response.status_int == 200
assert response.body == 'Hello, world!'

Getting the current app

The active WSGIApplication instance can be accessed at any place of your app using the function webapp2.get_app(). This is useful, for example, to access the app registry or configuration values:

import webapp2

app = webapp2.get_app()
config_value = app.config.get('my-config-key')