Using bottle.py in production

Update: I wrote a post about how to call get_url from within a SimpleTemplate template. The same technique works to make any Python variable or function you want globally accessible by default from a SimpleTemplate template.

This is a quick guide to using bottle.py in a (semi-)production environment with lighttpd. Bottle.py is a super-lightweight (one source file!) web framework for Python which I use for quick, single-purpose web scripts. For example, I have used Bottle.py for small web applications to:

  • Monitor CPU and HDD temps
  • Provide a quick and easy personal file drop location
  • Administer hg repositories

This guide will show some techniques to write a URL-agnostic app in Bottle.py and then deploy it, using FCGI and lighttpd. Requirements:

  • python>=2.7
  • bottle.py>=0.10
  • flup

Bottle.py tips

get_url

Use the get_url function to write a URL-agnostic app. With get_url, you can mount your app at any root URL on your server. For example, if your app handles the /viewpost and /listposts URLs, and you mount it at http://mywebsite.com/blog/, the final URLs that you would access from your web browser would be http://mywebsite.com/blog/viewpost and http://mywebsite.com/blog/listposts. You can only use get_url with named routes:

import bottle
app = bottle.default_app()

@app.route('/viewposts', name='viewposts')
def handle_viewposts():
    return 'first post!'

# Calling get_url from handle_index
@app.route('/index')
def handle_index():
    return '<a href="{}">View posts</a>'.format(app.get_url('viewposts'))

You can also use get_url for dynamic routes (routes with parameters):

@app.route('/get/<name:path>', name='getobj')
def handle_getobj(name):
    return bottle.static_file(name, root='/path/to/files')

# Call get_url like this
url = app.get_url('getobj', name='filename.txt')

Deploying with flup

Usually I deploy Python web apps with flup (there's also a Python 3 version). I use FastCGI on a Unix socket, running behind lighttpd. For these examples, assume that the app socket is called /run/lighttpd/myapp.sock.

Python configuration

For Python 2.7 and flup, use Bottle's builtin flup server, setting the bindAddress to the path to the socket:

if __name__ == "__main__":
    run(server='flup', options={'bindAddress': '/run/lighttpd/myapp.sock'})

lighttpd

Make sure mod_fastcgi is loaded in your config. To mount an FCGI Bottle app at some URL, say /myapp, add this to your lighttpd.conf:

fastcgi.server = (
    "/myapp" => (
        "myapp-fcgi" => (
            "socket" => "/run/lighttpd/myapp.sock",
            "check-local" => "disable",
            "docroot" => "/",
        )
    )
)

Disabling check-local allows your app to handle URL requests even if a file of the same name does not exist on the local filesystem. For more information on lighttpd and FastCGI, see the official documentation.

Comments

comments powered by Disqus