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.
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)
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:
HTTPNotFound
exception
if no handler was found).WSGIApplication
.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()
.
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)
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.
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
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)
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.
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()
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!'
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')