Bottle

Documents

Simple usage

In [11]:
from bottle import route, run
In [2]:
@route('/hello')
def hello():
    return "Hello World!"
In [3]:
run(host='localhost', port=8080, debug=True)
Bottle v0.12.8 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.

127.0.0.1 - - [28/Apr/2015 22:20:54] "GET / HTTP/1.1" 404 720
127.0.0.1 - - [28/Apr/2015 22:20:55] "GET /favicon.ico HTTP/1.1" 404 742
127.0.0.1 - - [28/Apr/2015 22:21:02] "GET /hello HTTP/1.1" 200 12

In [3]:
 

Bottle Class

Default: use the global object;
But we can use our own object.

In [4]:
from bottle import Bottle, run
In [5]:
app = Bottle()
In [6]:
@app.route('/hello')   #Use the "app" object
def hello():
    return "Hello World!"
In [7]:
run(app, host = 'localhost', port = 8080)   #Use the "app" object
Bottle v0.12.8 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.

127.0.0.1 - - [28/Apr/2015 22:28:42] "GET /hello HTTP/1.1" 200 12
127.0.0.1 - - [28/Apr/2015 22:28:42] "GET /hello HTTP/1.1" 200 12

In [7]:
 

A function can be bound to a URL.

Some part of URL can transmit to the function.

In [8]:
from bottle import template
In [9]:
#Use two decorators
@route('/')
@route('/hello/<name>')   #transmit the "name" variable to the function
def greet(name='stranger'): 
    return template('Hello , how are you?', NAME=name)
    #Use template, and transmit the "NAME" variable by keyword argument.
In [10]:
run(host='localhost', port=8080)
Bottle v0.12.8 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.

127.0.0.1 - - [28/Apr/2015 22:32:36] "GET / HTTP/1.1" 200 28
127.0.0.1 - - [28/Apr/2015 22:33:00] "GET /hello/zk HTTP/1.1" 200 22
127.0.0.1 - - [28/Apr/2015 22:33:04] "GET /hello HTTP/1.1" 200 12
127.0.0.1 - - [28/Apr/2015 22:33:08] "GET /hello/ HTTP/1.1" 404 732
127.0.0.1 - - [28/Apr/2015 22:33:15] "GET /hello/stranger HTTP/1.1" 200 28

In [11]:
 

Filter & Config

<name:filter> or <name:filter:config>
Filter:

  1. int: matches integer only
    @route('/object/<id:int>')
  2. float:matches float only
  3. path: matches all characters including the slash character in a non-greedy way
    @route('/static/<path:path>')
  4. re: regular expression
    @route('/show/<name:re:[a-z]+>')
In [16]:
from bottle import static_file
In [12]:
@route('/object/<id:int>')
def callback(id):
    assert isinstance(id, int)
In [13]:
@route('/show/<name:re:[a-z]+>')
def callback(name):
    assert name.isalpha()
In [20]:
@route('/static/<filepath:path>')
def server_static(filepath):
    return static_file(filepath, root='/')
In [0]:
 

Request methods

GET is the defalut method...
The argument method of route can be used...
The decorator get(), post(), put(), delete() and patch() are ok as well!

In [5]:
from bottle import get, post, request, route, run
In [2]:
#Get the HTML form
@get('/login')  #or @route('/login')
def login():
    return '''
        <form action="/login" method="post">
            Username: <input name="username" type="text" />
            Password: <input name="password" type="password" />
            <input value="Login" type="submit" />
        </form>
        '''
In [9]:
#Get the value of the form that is posted
@post('/login')  #or @route('/login', method='POST')
def do_login():
    #Get the value of input whose name is "username" or "password" int the form
    username = request.forms.get('username')
    password = request.forms.get('password')
    if check_login(username, password):
        return "<p>Your login information was correct.</p>"
    else:
        return "<p>Login failed.</p>"
In [7]:
#Assume that the login is allowed
def check_login(username, password):
    return 1
In [8]:
run(host='localhost', port=8080)
Bottle v0.12.8 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.

127.0.0.1 - - [29/Apr/2015 04:32:02] "POST /login HTTP/1.1" 200 42

In [9]:
 

Routing static files

The function of static_file:
The first argument is the name of the static file...
You can give the root directory by using the argument root

In [10]:
from bottle import static_file
In [21]:
@route('/static/<filename:path>')  #"path" means we can access the deeper directory but not only the root.
def server_static(filename):
    return static_file(filename, root='/home/zk/joinus_raw/joinus_2015spring_raw/')
In [22]:
run(host='localhost', port=8080)
Bottle v0.12.8 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.

127.0.0.1 - - [29/Apr/2015 05:32:17] "GET /static/form.html HTTP/1.1" 304 0
127.0.0.1 - - [29/Apr/2015 05:32:17] "GET /static/css/styles.min.css HTTP/1.1" 200 8108
127.0.0.1 - - [29/Apr/2015 05:32:17] "GET /static/js/jquery.min.js HTTP/1.1" 200 93435
127.0.0.1 - - [29/Apr/2015 05:32:17] "GET /static/js/scripts.min.js HTTP/1.1" 200 2375
127.0.0.1 - - [29/Apr/2015 05:32:17] "GET /static/js/jquery-ui.min.js HTTP/1.1" 200 228077

In [22]:
 

Open or download the files

In [23]:
from bottle import static_file
In [28]:
@route('/images/<filename:re:.*\.png>')
def send_image(filename):
    return static_file(filename, root='/home/zk/joinus_raw/joinus_2015spring_raw/image/',mimetype='image/png')
    #Point out that the mimetype is "image/png", but generally it is not necessary
In [29]:
run(host='localhost', port=8080)
Bottle v0.12.8 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.


Most browsers will open the files directly if the mimetype is known...
If you want to force it to download the file, you can give the argument download

In [30]:
@route('/download/<filename:re:.*\.png>')
def download_image(filename):
    return static_file(filename, root='/home/zk/joinus_raw/joinus_2015spring_raw/image/', download=filename)
    #The value of download is the name of the file you want.
In [31]:
run(host='localhost', port=8080)
Bottle v0.12.8 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.

127.0.0.1 - - [29/Apr/2015 05:56:57] "GET /download/alert%E4%BA%BA%E8%B5%84.png HTTP/1.1" 200 161318

In [31]:
 

Response Header

Most headers are unique, and we can use response.set_header to set them;
But some are allowed to appear more than once in a response, and we can use response.add_header to add more.
Both functions take 2 parameters, a header name and its value.

In [32]:
@route('/wiki/<page>')
def wiki(page):
    response.set_header('Set-Cookie', 'name=value')
    response.add_header('Set-Cookie', 'name2=value2')
In []:
 

Cookies

We can use request.get_cookie to get the value of a cookie.
It takes a parament, the name of the cookie.

And use response.set_cookie to set a cookie.
It takes 2 paraments, the name of the cookie and its value.
More keyword arguments are allowed:

  • max_age:(None), seconds.
    If max_age is None, the cookie will expire when the browser is closed.
  • path:(/), limit the cookie to a given path
  • secure:(off), limit the cookie to HTTPS connections
  • httponly(off), prevent client-side javascript to read this cookie

What's more, we can use keyword argument secret both in request.get_cookie and response.set_cookie to make the imformation in cookies securier.

In [34]:
@route('/hello')
def hello_again():
    if request.get_cookie("visited", secret="hello"):
        return "Welcome back! Nice to see you again"
    else:
        response.set_cookie("visited", "yes", secret="hello")
        return "Hello there! Nice to meet you"
In [34]:
 

Request Data

In [36]:
from bottle import request, route, template

request.cookies.xxx get the cookie named "xxx".
If "xxx" doesn't exist, return ''.

In [40]:
@route('hello')
def hello():
    name = request.cookies.username or 'Guest'
    return template('Hello ', name=name)

Get the form

It is similar to getting the cookie.
But we can get all value of the form as a list by request.forms.getall.

In [41]:
for choice in request.forms.getall('multiple_choice'):
    do_something(choice)
In [41]:
 

Get the IP

The ip is stored in the dictionary request.environ.
The key of ip is REMOTE_ADDR

In [42]:
@route('/my_ip')
def show_ip():
    ip = request.environ.get('REMOTE_ADDR')
    # or ip = request.get('REMOTE_ADDR')
    # or ip = request['REMOTE_ADDR']
    return template("Your IP is: ", ip=ip)
In [42]:
 
In [48]:
import bottle

Debug Mode

In [49]:
bottle.debug(True)
In [49]:
 

Auto Reloading

In [50]:
run(reloader=True)
An exception has occurred, use %tb to see the full traceback.

SystemExit: 1
To exit: use 'exit', 'quit', or Ctrl-D.

In [50]:
 

Command Line Interface

$ python -m bottle

Usage: bottle.py [options] package.module:app

Options:
-h, --help show this help message and exit
--version show version number.
-b ADDRESS, --bind=ADDRESS
bind socket to ADDRESS.
-s SERVER, --server=SERVER
use SERVER as backend.
-p PLUGIN, --plugin=PLUGIN
install additional plugin/s.
--debug start server in debug mode.
--reload auto-reload on file changes

Some examples:

Grab the 'app' object from the 'myapp.controller' module and start a paste server on port 80 on all interfaces.
python -m bottle -server paste -bind 0.0.0.0:80 myapp.controller:app

Start a self-reloading development server and serve the global default application. The routes are defined in 'test.py'
python -m bottle --debug --reload test

Install a custom debug plugin with some parameters
python -m bottle --debug --reload --plugin 'utils:DebugPlugin(exc=True)'' test

Serve an application that is created with 'myapp.controller.make_app()' on demand.
python -m bottle 'myapp.controller:make_app()''

In []: