Tweens

Introduction

Tweens are a light-weight framework component that sits between the web server and the app. It’s very similar to a WSGI middleware, except that a tween has access to the Morepath API and is therefore less low-level.

Tweens can be used to implement transaction handling, logging, error handling and the like.

signature of a handler

Morepath has an internal publish function that takes a single morepath.Request argument, and returns a morepath.Response as a result:

def publish(request):
    ...
    return response

Tweens have the same signature.

We call such functions handlers.

Under and over

Given a handler, we can create a factory that creates a tween that wraps around it:

def make_tween(app, handler):
    def my_tween(request):
        print "Enter"
        response = handler(request)
        print "Exit"
        return response
    return my_tween

We say that my_tween is over the handler argument, and conversely that handler is under my_tween.

The application constructs a chain of tween over tween, ultimately reaching the request handler. Requests arrive in the outermost tween and descend down the chain into the underlying tweens, and finally into the Morepath publish handler itself.

What can a tween do?

A tween can:

  • amend or replace the request before it goes in to the handler under it.
  • amend or replace the response before it goes back out to the handler over it.
  • inspect the request and completely take over response generation for some requests.
  • catch and handle exceptions raised by the handler under it.
  • do things before and after the request is handled: this can be logging, or commit or abort a database transaction.

Creating a tween factory

To have a tween, we need to add a tween factory to the app. The tween factory is a function that given a handler constructs a tween. You can register a tween factory using the App.tween_factory() directive:

@App.tween_factory()
def make_tween(app, handler):
    def my_tween(request):
        print "Enter"
        response = handler(request)
        print "Exit"
        return response
    return my_tween

The tween chain is now:

my_tween -> publish

It can be useful to control the order of the tween chain. You can do this by passing under or over to tween_factory:

@App.tween_factory(over=make_tween)
def make_another_tween(app, handler):
    def another_tween(request):
        print "Another"
        return handler(request)
    return another_tween

The tween chain is now:

another_tween -> my_tween -> publish

If instead you used under:

@App.tween_factory(under=make_tween)
def make_another_tween(app, handler):
    def another_tween(request):
        print "Another"
        return handler(request)
    return another_tween

Then the tween chain is:

my_tween -> another_tween -> publish

Tweens and settings

A tween factory may need access to some application settings in order to construct its tweens. A logging tween for instance needs access to a setting that indicates the path of the logfile.

The tween factory gets two arguments: the app and the handler. You can then access the app’s settings using app.registry.settings. See also the Settings section.

Tweens and apps

You can register different tween factories in different Morepath apps. A tween factory only has an effect when the app under which it is registered is being run directly as a WSGI app. A tween factory has no effect if its app is mounted under another app. Only the tweens of the outer app are in effect at that point, and they are also in effect for any apps mounted into it.

This means that if you install a logging tween in an app, and you run this app with a WSGI server, the logging takes place for that app and any other app that may be mounted into it, directly or indirectly.

more.transaction

If you need to integrate SQLAlchemy or the ZODB into Morepath, Morepath offers a special app you can extend that includes a transaction tween that interfaces with the transaction package. The morepath_sqlalchemy demo project gives an example of what that looks like with SQLAlchemy.