One of the less known features introduced in TurboGears 2.3 are application wrappers.
Application wrappers are much like controller wrappers (available since 2.2), but instead of wrapping controllers they actually wrap the whole application providing an easier way to implement what in plain WSGI is done through middlewares.
The advantage of application wrappers over middlewares is that they have full access to TurboGears stack, they can access the current request, the database, session and caches as the application itself would do.
The great part is that, as they run between TGApp and TGController, they can also replace the TurboGears Context and the TurboGears Response providing a great way to hijack requests, responses or even replace entire features of the framework like the cache layer. A very similar concept is available in other frameworks like Pyramid Tweens.
A very simple application wrapper that intercepts exceptions and logs them without messing with the standard TurboGears error handling might look like:
class ErrorLoggingWrapper(object):
def __init__(self, handler, config):
self.handler = handler
self.config = config
def __call__(self, controller, environ, context):
path = context.request.path
try:
return self.handler(controller, environ, context)
except:
log.exception('Error while handling %s', path)
raise
The wrapper can then be enabled calling
base_config.register_wrapper(ErrorLoggingWrapper)
inside config/app_cfg.py
Now that we have an application wrapper able to log exceptions we can decide for example to add another one that suppresses exceptions and prints “Something went wrong!”, as it is possible to specify the order of execution for application wrappers we can register a SuppressErrorsWrapper that should execute after the ErrorLoggingWrapper:
from webob import Response
class SuppressErrorsWrapper(object):
def __init__(self, handler, config):
self.handler = handler
self.config = config
def __call__(self, controller, environ, context):
try:
return self.handler(controller, environ, context)
except:
return Response('Oh! Oh! Something went wrong!', status=500, content_type='text/plain')
Then it can be registered after the ErrorLoggingWrapper using:
base_config.register_wrapper(SuppressErrorsWrapper, after=ErrorLoggingWrapper)
While applications wrappers are a powerful feature, most of their power comes from the new response management refactoring that makes possible to access the current context and replace the outgoing response while working with high level objects instead of having to manually cope with WSGI.