Recently I rediscovered Bottle, a Python-based micro framework for web applications. Bottle gives you full control over your URLs, has a simple built-in templating engine and a lot of other commonly used stuff. It takes you five minutes to get started and perhaps one hour with its great documentation to know everything there is to know.
I would still use Django in serious web applications for its richer ecosystem, but when it comes to quick prototyping, Bottle really excels. All you need is a Python interpreter; Bottle itself comes in a single file that you can put next to your own script without installation.
Let's have a look at a simple web service I used a few weeks ago: When updating your Ubuntu system, the update sometimes pauses to ask you something. Since I was too lazy to get up from the sofa to check if Ubuntu needed anything, I wrote a Bottle-based web service that makes the output of top(1) available via HTTP. Then I used my tablet to regularly check system load. If the load dropped to zero, then it was time to answer questions. This may be a weird, indirect indicator, but it worked perfectly.
With Bottle, it was dead simple to implement. Just copy the following code to a file and run it:
import subprocess from bottle import route, response, run @route("/") def top(): response.content_type = "text/plain" return subprocess.check_output(["top", "-bn", "1"]) run(host="localhost", port=8080)
Setting the content type via a global variable looks dangerous from a multi-threading point of view. But actually, Bottle gives you access to request and response via thread-local variables, so it's perfectly safe.
A second interesting thing is the somewhat unusual call to top(1). I'm calling it in its non-interactive batch mode (-b) and tell it to quit after one iteration (-n 1).
Let's add multiple URLs, redirects, query parameter processing, and templates:
import subprocess from bottle import route, request, response, redirect, template, run HTML = """\ <html> <head> <meta http-equiv="refresh" content="{{ refresh }}"> </head> <body> <pre> {{ data }} </pre> </body> """ @route("/") def index(): redirect("/top/") @route("/top/") def top(): refresh = request.query.refresh or "3" top = subprocess.check_output(["top", "-bn", "1"]) return template(HTML, data=top, refresh=refresh) @route("/w/") def w(): response.content_type = "text/plain" return subprocess.check_output(["w"]) run(host="localhost", port=8080)
We serve three different resources, with one redirecting to another. Our new top(1) now creates an auto-refreshing HTML document based on a template. For this, we read an optional refresh parameter from the URL (ie. "/top/?refresh=5") and render both top(1)'s output and the value of the query parameter in a template (Bottle quotes special characters for safety reasons). I embedded a template in the script, but Bottle can load them from files, too. We also serve a third resource "/w/" that doesn't do auto refresh and uses a different content type.
And that's it. Building web services has never been easier.