Bottle Plugin Lifecycle

If you use Python‘s Bottle micro-framework there’ll be a time where you’ll want to add custom plugins. To get a better feeling on what code gets executed when, I created a minimal Bottle app with a test plugin that logs what code gets executed. I uesed it to test both global and route-specific plugins.

When Python loads the module you’ll see that the plugins’

__init__()

and

setup()

methods will be called immediately when they are installed on the app or applied to the route. This happens in the order they appear in the code. Then the app is started.

The first time a route is called Bottle executes the plugins’

apply()

methods. This happens in “reversed order” of installation (which makes sense for a nested callback chain). This means first the route-specific plugins get applied then the global ones. Their result is cached, i.e. only the inner/wrapped function is executed from here on out.

Then for every request the

apply()

method’s inner function is executed. This happens in the “original” order again.

Below you can see the code and example logs for two requests. You can also clone the Gist and do your own experiments.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import bottle
import logging
logger = logging.getLogger(__name__)
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.DEBUG)
logger.info("module load")
class LifecycleTestPlugin(object):
name = "lifecycle_test"
api = 2
def __init__(self, name):
self.name = name
logger.info("%s: plugin __init__", self.name)
def setup(self, app):
logger.info("%s: plugin setup", self.name)
def apply(self, callback, context):
logger.info("%s: plugin apply", self.name)
def _wrapper(*args, **kwargs):
logger.info("%s: plugin apply wrapper", self.name)
return callback(*args, **kwargs)
return _wrapper
def close(self):
logger.info("plugin close %s", self.name)
app = bottle.Bottle()
logger.info("installing plugins ...")
app.install(LifecycleTestPlugin("app_plugin1"))
app.install(LifecycleTestPlugin("app_plugin2"))
@app.get('/ping',
apply=[
LifecycleTestPlugin("route_plugin1"),
LifecycleTestPlugin("route_plugin2"),
]
)
def ping():
return 'pong'
if __name__ == "__main__":
logger.info("start app ...")
bottle.run(app, host="0.0.0.0", port="9000", reloader=True)
module load
installing plugins ...
app_plugin1: plugin __init__
app_plugin1: plugin setup
app_plugin2: plugin __init__
app_plugin2: plugin setup
route_plugin1: plugin __init__
route_plugin2: plugin __init__
start app ...
module load
installing plugins ...
app_plugin1: plugin __init__
app_plugin1: plugin setup
app_plugin2: plugin __init__
app_plugin2: plugin setup
route_plugin1: plugin __init__
route_plugin2: plugin __init__
start app ...
Bottle v0.12.8 server starting up (using WSGIRefServer())...
Listening on http://0.0.0.0:9000/
Hit Ctrl-C to quit.
route_plugin2: plugin apply
route_plugin1: plugin apply
app_plugin2: plugin apply
app_plugin1: plugin apply
app_plugin1: plugin apply wrapper
app_plugin2: plugin apply wrapper
route_plugin1: plugin apply wrapper
route_plugin2: plugin apply wrapper
127.0.0.1 - - [05/Jul/2015 14:07:25] "GET /ping HTTP/1.1" 200 4
app_plugin1: plugin apply wrapper
app_plugin2: plugin apply wrapper
route_plugin1: plugin apply wrapper
route_plugin2: plugin apply wrapper
127.0.0.1 - - [05/Jul/2015 14:07:28] "GET /ping HTTP/1.1" 200 4
view raw output.log hosted with ❤ by GitHub

https://twitter.com/riyadpr/status/617681143538786304

Leave a Reply

Your email address will not be published. Required fields are marked *