Bottle

_images/bottle.png

Bottle est un WSGI web-framework simple et rapide écrit en python qui est de plus packager en un seul fichier n’ayant aucune dépendance externe.

Core Features

Routes: Mapping URLs to code with a simple but powerful pattern syntax. Templates: Fast build-in template engine and support for mako, jinja2 and cheetah templates. Server: Build-in HTTP development server and support for paste, fapws3, flup, cherrypy or any other WSGI capable server. No dependencies: All in a single file and no dependencies other than the Python standard library.

Download / Install

Pour l’installer soit vous le télécharger et vous l’utiliser comme un simple fichier package soit vous l’installer via easy_install

easy_install -U bottle

Bottle peut être utilisé avec python 3 en utilisant 2to3

Features and Examples

Un exemple simple

from bottle import route, run

@route('/')
def index():
    return 'Hello World!'

run(host='localhost', port=8080)

lancer le fichier python et aller voir sur http://localhost:8080/

Note

pour que le serveur puisse répondre quelque soit l’interfaces 127.0.0.1, 192.*.*.*, ... il faut utiliser comme host 0.0.0.0

Routes

@route() est un decorator qui permet de ciblé l’URL utilisé

@route('/hello/:name')
def hello(name):
    return 'Hello, %s' % name

Templates

Bottle comporte aussi son propre système de template qui peut être appeler par le décorateur view

@route('/hello/template/:names')
@view('hello')
def template_hello(names):
    names = names.split(',')
    return dict(title='Hello World', names=names)

Dans ”./views/hello.tpl”:

<html>
 <head>
  <title>{{title}}</title>
 </head>
 <body>
  %for name in names:
    <p>Hello, <strong>{{name}}</strong></p>
  %end
 </body>
</html>

On peut aussi l’écrire ainsi

from bottle import template
@route('/hello/template/:names')
def template_hello(names):
    names = names.split(',')
    return template('view', dict(title='Hello World', names=names))

Warning

pour modifier le path des template on doit modifier la variable bottle.TEMPLATE_PATH; ex: TEMPLATE_PATH.insert(0,YOUR_PATH)

Bootle peut utiliser des template comme jinja2, mako ou cheetah

from bottle import mako_view as view

Static Files, Redirects and HTTP Errors

from bottle import static_file, redirect, abort

@route('/static/:filename')
def static_file(filename):
    return static_file(filename, root='/path/to/static/files')

@route('/wrong/url')
def wrong():
    redirect("/right/url")

@route('/restricted')
def restricted():
    abort(401, "Sorry, access denied.")

@error(404)
def error404(error):
    return template('error',{'error' :error,})

POST, GET, Header and Cookies

le plus facile est d’utiliser un dict()

from bottle import request, response, route

@route('/hello/cookie')
def cookie():
    name = request.COOKIES.get('name', 'Stranger')
    response.headers['Content-Type'] = 'text/plain'
    return 'Hello, %s' % name

@route('/hello/cookie', method='POST')
def set_cookie():
    if 'name' in request.POST:
        name = request.POST['name']
        response.set_cookie('name',name, path='/')
    return 'OK'

Note

le path d’un cookie est important car il permet d’identifier les urls qui pourrons l’utiliser

Note

il est possible de crypter la valeur d’un cookie

et un exemple de login

from bottle import get, post, request

@get('/login') # or @route('/login')
def login_form():
    return '''<form method="POST" action="/login">
                <input name="name"     type="text" />
                <input name="password" type="password" />
                <input type="submit" />
              </form>'''

@post('/login') # or @route('/login', method='POST')
def login_submit():
    name     = request.forms.get('name')
    password = request.forms.get('password')
    if check_login(name, password):
        return "<p>Your login was correct</p>"
    else:
        return "<p>Login failed</p>"

Session

Il n’y a pas de session avec Bottle mais on peut utiliser la notion de cookie

@app.route('/login')
def login():
    username = request.forms.get('username')
    password = request.forms.get('password')
    if check_user_credentials(username, password):
        response.set_cookie("account", username, secret='some-secret-key')
        return "Welcome %s! You are now logged in." % username
    else:
        return "Login failed."

@app.route('/restricted')
def restricted_area():
    username = request.get_cookie("account", secret='some-secret-key')
    if username:
        return "Hello %s. Welcome back." % username
    else:
        return "You are not logged in. Access denied."

HTTP Server

Bottle has a HTTP Server build in but also supports cherrypy, flup, paste and fapws3 as alternatives.

from bottle import PasteServer
run(server=PasteServer)

Gestion des Erreurs

le plus simple, contrairement a ce qu’on peut trouver sur la doc officiel, n’est pas de faire une route mais de modifier les handlers

voici un exemple simple

from bottle import Bottle, run, template, error, request
import os

TXT_ERROR_404 = """<h1>Error 404:</h1>
<br/><b>path:</b> %(path)s
<br/><b>url:</b> %(url)s
<br/><b>path scheme:</b> %(pathscheme)s
<br/><b>path netloc:</b> %(pathnetloc)s
<br/><b>path request:</b> %(pathrequest)s
<br/><b>path query :</b> %(pathquery)s
<br/><b>path fragment :</b> %(pathfragment)s
"""

app = Bottle()

def error404(error):
    info = {
    'path' : request.path,
    'url' : request.url,
    'pathscheme' : request.urlparts[0],
    'pathnetloc' : request.urlparts[1],
    'pathrequest' : request.urlparts[2],
    'pathquery' : request.urlparts[3],
    'pathfragment' : request.urlparts[4]
    }
    return TXT_ERROR_404 % info

def error500(error):
    return 'error code 500'

handler = {
    404: error404,
    500: error500,
}


app.error_handler = handler

@app.route('/hello')
def hello():
    return 1/0

run(app, host='0.0.0.0', port=8080)