Serveur Web en Python avec bottle
#
Il existe de nombreux framework Web pour Python : Django, Cherrypy, Pyramid, Flask, Bottle… Nous allons nous concentrer ici sur Bottle en raison de sa facilité d’utilisation et d’installation.
Site officiel de bottle : http://bottlepy.org/docs/dev/index.html
La version de bottle utilisée lors de la rédaction de ce document est la 0.12.
l’installation se résume à récupérer le fichier bottle.py
et à le
déposer dans le PATH
de Python (le mieux est de passer par le système
d’installation standard de votre machine).
Premiers tests, servir l’heure #
import bottle
import datetime
@bottle.route("/time")
def index() :
formatstr = "Nous sommes le %d/%m/%Y, il est %H:%M:%S"
heure = datetime.datetime.now().strftime(formatstr)
stri = "<h1>Horloge</h1>"+heure
return stri
bottle.run(bottle.app(), host='0.0.0.0', port=8080, debug= True, reloader=True)
bottle
contient un petit serveur Web (pour une application importante,
l’application Bottle est placée derrière un serveur capable de supporter
une charge élevée (Apache ou Nginx par exemple)). Ce serveur Web est
lancé par la dernière ligne :
bottle.run(bottle.app(), host='0.0.0.0', port=8080, debug= True, reloader=True)
Il sera accessible sur le port 8080, depuis n’importe quelle IP
(host='0.0.0.0'
).
L’utilisation de bottle
consiste à écrire des fonctions, et à mapper
ces fonctions sur des URLs.
Dans l’exemple qui précède, nous avons écrit la fonction index()
et
l’avons associé à l’URL /time
.
Après lancement de l’application, par exemple en local, on peut donc
consulter l’URL : http://localhost:8080/time
. Bottle exécutera alors
la fonction index
et renverra au client ce que renvoie la fonction. La
partie HTTP est entièrement gérée par bottle
. Nous avons juste à écrire
le texte (généralement une page HTML) qui sera renvoyé.
Et c’est tout ! Ce principe permet déjà de répondre à la plupart des besoins.
bottle
permet de faire beaucoup plus :
- Système de templates intégré, pour ne pas noyer son code Python dans du Html et avoir une maintenance plus simple
- Gestion des cookies
- Gestion des requêtes GET et POST, des formulaires
- Passage de paramètres aux fonctions par le biais de l’URL
Nous allons voir certaines de ces fonctionnalités dans la suite.
Utilisation des templates #
Un template permet d’isoler les parties les plus statiques de la page et d’injecter dans cette page le contenu dynamique. Toutes les pages Web ayant une structure commune, chaque site ayant des réglages communs, un thème etc… ces informations doivent idéalement figurer à un seul endroit du code.
L’utilisation d’un template se fait par l’utilisation du décorateur
bottle.view
:
@bottle.route("/time")
@bottle.view("page.tpl")
def index() :
formatstr = "Nous sommes le %d/%m/%Y, il est %H:%M:%S"
heure = datetime.datetime.now().strftime(formatstr)
return {"title":"Horloge", "body" : heure}
Le fichier page.tpl
peut être placé au même endroit que le fichier
Python ou bien dans le répertoire views
(cet endroit peut de plus être
configuré) :
<!doctype html>
<!-- page.tpl -->
<HTML lang="fr">
<HEAD>
<TITLE>{{title}}</TITLE>
<meta charset="UTF-8">
</HEAD>
<body>
<h1>{{title}}</h1>
{{!body}}
<hr/>
<font size="-1"><i>Page réalisée avec Bottle</i></font>
</body>
</html>
La fonction index
ne renvoie plus une chaîne, mais un dictionnaire,
contenant des clés (ici title
et body
) qui seront utilisées dans le
fichier template. Finalement bottle servira le contenu du fichier
template après avoir remplacé le contenu de {{title}}
par la valeur
associée à la clé title
et le contenu de {{!body}}
par la valeur
associée à la clé body
. La présence du !
permet d’indiquer à bottle
de ne pas échapper les caractères de balisage HTML dans la chaîne
body
. Elle pourra donc contenir des balises.
Un formulaire avec bottle #
Voici comment utiliser les informations d’un formulaire. Une première page présente le formulaire (rien de spécial à signaler)
@bottle.route("/qui")
@bottle.view("page.tpl")
def qui() :
stri = """
<form method='post' action='bonjour'>
<input type='text' name='nom' placeholder='Votre nom ?'/>
<input type='submit' value='Bonjour bottle !'/>
</form>
"""
return {"title":"Présentez-vous", "body" : stri}
L’URL appelée lors de la validation est /bonjour
, avec la méthode
POST
:
@bottle.route("/bonjour", method='POST')
@bottle.view("page.tpl")
def bonjour() :
nom = bottle.request.forms.get('nom')
stri = "Bonjour mon(a) che(è)r(e) {}".format(nom)
return {"title":"Bonjour", "body" : stri}
Il faut préciser method='POST'
car par défaut, les routes bottle ne
concernent que les requêtes de type GET
. Une fonction peut aussi
répondre aux deux types de requêtes en indiquant :
method=('POST', 'GET')
.
Les valeurs entrées dans le formulaire sont récupérables simplement avec :
nom = bottle.request.forms.get('nom')
Passage de paramètres aux fonctions #
Une fonctionnalité intéressante permet de passer des portions de l’URL comme paramètres de la fonction :
@bottle.route("/menu")
@bottle.view("page.tpl")
def menu() :
stri = """<ul>
<li><a href='/action/temp'>Température</a></li>
<li><a href='/action/pluie'>Pluie</a></li>
</ul>
"""
return {"title":"Site Météo", "body" : stri}
@bottle.route("/action/<nom_action>")
@bottle.view("page.tpl")
def action(nom_action) :
if nom_action == 'temp':
stri = "Il ne fera pas très froid"
elif nom_action == 'pluie':
stri = "Il ne pleuvra pas trop. Ou l'inverse"
else:
stri = "Demande erronée"
stri += "<hr/> <a href='../menu'>Retour menu</a>"
return {"title":"Site Météo", "body" : stri}
La route donnée pour la seconde fonction indique que la fonction
action
est associées aux URLs de type : /action/XXXX
où XXX
est
passé en paramètre nommé à la fonction.
Notons que l’utilisation de l’URL /action
ne fonctionnera pas (mais on
peut associer plusieurs URLs à une seule fonction, comme indiqué dans le
manuel).
Code complet #
(Une autre version, avec un template par page est proposée dans la section suivante)
import bottle
import datetime
@bottle.route("/time")
@bottle.view("page.tpl")
def index() :
heure = datetime.datetime.now().strftime("<p>Nous sommes le %d/%m/%Y, il est %H:%M:%S</p>")
return {"title":"Horloge", "body" : heure}
@bottle.route("/qui")
@bottle.view("page.tpl")
def qui() :
stri = """
<form method='post' action='bonjour'>
<input type='text' name='nom' placeholder='Votre nom ?'/>
<input type='submit' value='Bonjour bottle !'/>
</form>
"""
return {"title":"Présentez-vous", "body" : stri}
@bottle.route("/bonjour", method='POST')
@bottle.view("page.tpl")
def bonjour() :
nom = bottle.request.forms.get('nom')
stri = "Bonjour mon(a) che(è)r(e) {}".format(nom)
return {"title":"Bonjour", "body" : stri}
@bottle.route("/menu")
@bottle.view("page.tpl")
def menu() :
stri = """<ul>
<li><a href='/action/temp'>Température</a></li>
<li><a href='/action/pluie'>Pluie</a></li>
</ul>
"""
return {"title":"Site Météo", "body" : stri}
@bottle.route("/action/<nom_action>")
@bottle.view("page.tpl")
def action(nom_action) :
if nom_action == 'temp':
stri = "Il ne fera pas très froid"
elif nom_action == 'pluie':
stri = "Il ne pleura pas trop. Ou l'inverse"
else:
stri = "Demande erronée"
stri += "<hr/> <a href='../menu'>Retour menu</a>"
return {"title":"Site Météo", "body" : stri}
bottle.run(bottle.app(), host='0.0.0.0', port=8080, debug= True, reloader=True)
Pensez à récupérer aussi le fichier template : page.tpl
.
Code complet / un template par page #
Il peut être préférable d’avoir un template par page. On peut dans ce cas utiliser
la fonction rebase
qui permet de base un template sur un autre, de manière à ne
donner qu’une seule fois les parties communes à toutes les pages.
Une telle version est disponible dans l’archive ci-jointe. C’est probablement une bonne base pour démarrer un travail.
Servir de pages statiques #
Si votre site contient des images ou des feuilles de style, elles
peuvent aussi être servies par bottle. Dans ce cas, il convient de les
placer dans un répertoire nommé static
, puis d’ajouter la route
suivante :
@bottle.route('/static/<filename:path>')
def server_static(filename):
" Fichiers statiques du site placés dans le rép /static/ "
return bottle.static_file(filename, root='static')
On accède ensuite aux fichiers statiques par l’URL :
/static/mafeuilledestyle.css
.