Sam & Max » cherrypy http://sametmax.com Du code, du cul Sat, 07 Nov 2015 10:56:13 +0000 en-US hourly 1 http://wordpress.org/?v=4.1 Qu’est-ce que WSGI et à quoi ça sert ? 29 http://sametmax.com/quest-ce-que-wsgi-et-a-quoi-ca-sert/ http://sametmax.com/quest-ce-que-wsgi-et-a-quoi-ca-sert/#comments Wed, 03 Jul 2013 10:36:16 +0000 http://sametmax.com/?p=6544 WSGI est typiquement le cas d'une notion simple et super mal expliquée sur le Web.]]> On va dire que je me répète, mais WSGI est typiquement le cas d’une notion simple et super mal expliquée sur le Web.

C’est terrible ça, une vrai maladie. Ça me donne envie de faire des vidéos comme le joueur du Grenier pour râler à voix haute.

Bref.

Donc WSGI a le même but que FastCGI, SCGI, or AJP : c’est une norme qui sert à définir comment un serveur Python et son application peuvent discuter. Ça été pondu pour que tous les serveurs et toutes les applications Python qui respectent la norme soient garantis de pouvoir s’interfacer.

Ça, c’est la définition que vous voyez partout. Super, mais concrètement ça veut dire quoi ?

WSGI, c’est juste une convention de discussion

Un bon dessin vaut mieux…

Schéma du fonctionnement de WSGI

C'est compliqué de faire un schéma comme ça sérieux ? Ah ça pond une norme de 30 pages mais ça peut pas faire 3 ronds dans paint.

D’un côté vous avez des serveurs comme ceux de cherrypy, de paste, de werkzeug ou comme la commande runserver de Django, ou tels que les serveurs gunicorn, Apache (avec mod_wsgi). Ces logiciels ont pour boulot d’accueillir une requête HTTP et de renvoyer la réponse. Ils ont des problématiques comme : gérer les sockets, gérer plusieurs processus en parallèle, éviter qu’un pirate se balade sur votre serveur, etc.

De l’autre côté vous avez votre application. 9 fois sur 10, votre site Web, donc votre code. Sa problématique c’est de recevoir une requête, la comprendre, et générer une réponse en tapant dans la base de données, et en faisant sa popotte HTML.

Il faut donc que votre serveur dialogue avec votre app, qu’il lui passe la requête, que votre app la gère, retourne la réponse, et file la réponse au serveur. Alors le serveur envoie la réponse au navigateur.

C’est la chose la plus mal expliquée : il y a deux parties à WSGI. Une pour le serveur, et une pour l’application.

Un serveur est dit “compatible WSGI” quand il est capable de transmettre une requête HTTP normale à votre application via le protocole WSGI, et qu’il est capable de récupérer une réponse HTTP depuis votre application via le protocole WSGI pour en faire une requête HTTP normale.

Une application est dite “compatible WSGI” quand elle est capable de recevoir une requête HTTP transformée en un objet Python selon la norme WSGI, et qu’elle retourne une réponse HTTP sous la forme d’un objet Python selon la norme WSGI.

J’avais demandé concrètement, show me the fucking code !

Côté serveur WSGI, on doit lui passer le point d’entrée de votre app.

Le point d’entrée est un module Python qui contient une variable nommé application.

C’est tout.

La variable application doit contenir votre application compatible WSGI.

Une application compatible WSGI a un certain nombre de méthodes pour accepter en paramètres un objet requête HTTP, et retourner un objet réponse HTTP, mais la bonne nouvelle, c’est que vous n’avez absolument pas besoin de savoir comment ça marche.

En effet, tous les frameworks compatibles WSGI (bottle, django, cherrypy, etc) ont une fonction qui retourne cette application toute faite.

Par exemple, avec bottle, mon fichier wsgi va contenir :

from site import app
 
application = app

C’est tout. app, le décorateur qui permet de faire @app.route('/') dans le code du site bottle, est déjà une application WSGI.

Pour Django, ça va donner un truc comme :

import os
# ont dit quel est le module de settings Django
os.environ["DJANGO_SETTINGS_MODULE"] = "project.settings"
 
# et on fabrique l'application WSGI avec une fonction
# que Django nous donne
from django.core.wsgi import get_wsgi_application
 
application = get_wsgi_application()

Le simple fait qu’il y ait une variable nommée application fait que le serveur va se débrouiller. Tout ce qu’il y a à faire, c’est passer en paramètre ce module au serveur, et comment le faire dépend du serveur.

Par exemple pour gunicorn, c’est le premier paramètre de la commande qu’on utilise pour lancer le serveur :

gunicorn nom_du_module

Il faut que le module soit importable (on peut passer l’option --pythonpath pour s’en assurer.)

Pour Apache et mod_wsgi, ça se fait dans le fichier de config apache.conf:

WSGIScriptAlias / /chemin/version/module.wsgi

Bref, faire dialoguer un serveur et une application WSGI, c’est con comme la lune :

  1. Faire un module qui contient une variable nommée application contenant l’app WSGI.
  2. Dire au serveur où se trouve le fichier

Avantages et inconvénients de WSGI

Parmi les avantages, on retrouve :

  • Pas de parsing de la requête au niveau de l’application.
  • Entrée de toutes les apps WSGI normalisées.
  • Tous les logiciels WSGI sont compatibles entre eux.
  • Le module WSGI reste chargé en mémoire une bonne fois pour toute, pas besoin de le recréer à chaque requête.
  • WSGI se suffit à lui même pour écrire une application. Dans la théorie, on n’a même pas besoin de framework.

Quant aux inconvénients :

  • En pratique il y a très peu de code partagé entre les applications WSGI. La communication sur WSGI s’est très mal passée et cela reste quelque chose de mystérieux pour la plupart des développeurs.
  • WSGI ne gère pas les websockets, et la norme est en train d’être mise à jour pour cela. Mais cela prend du temps et les websockets sont déjà là, du coup les gens se tournent vers des solutions non WSGI (tornado, twisted, etc).
  • Certains serveurs ne gèrent pas WSGI (comme nginx ou lighttpd), du coup on ne peut pas communiquer directement entre l’app et eux. Il faut un intermédiaire. C’est pour cela qu’on voit très souvent des setups comme nginx <=> gunicorn <=> django, avec un serveur WSGI qui sert d’intermédiaire. Il y a tout de même le bénéfice de pouvoir gérer parfaitement indépendamment le processus WSGI du serveur HTTP, ce qui est très pratique pour le debug, l’administration système, le déploiement, etc.
  • WSGI, c’est du Python. Qui dit Python, dit import, qui dit import, dit PYTHON PATH. 90% des erreurs de mise en prod sont des erreurs d’import, et pour cette raison sys.path est presque toujours manipulé dans un fichier qui contient une application WSGI.

Pour la culture

Voici à quoi ressemble un hello world en WSGI :

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    yield 'Hello World\n'

Et oui, c’est très simple, et il n’y a rien à importer. La norme WSGI est une convention : il doit y avoir une variable nommée application (ici la variable c’est le nom de la fonction), et cette variable doit contenir une application WSGI.

Mais une application WSGI, c’est juste un callable (ici une fonction) qui accepte en paramètres environ (la requête), start_response (une fonction qui écrit l’en-tête de la réponse) et qui retourne un générateur de string. C’est tout.

Il n’y a rien de magique. C’est une simple interface sur laquelle tout le monde s’est mis d’accord.

Quand on fait en Django:

application = get_wsgi_application()

En fait Django ne fait que fabriquer une fonction comme on vient de voir, qui derrière appelle votre application Django.

En revanche, comme on utilise souvent Django derrière nginx, le setup ressemble très souvent à ça :

Schém d'utilistation de WSGI avec Nginx

Quand on voit "socket", ça fait peur, mais c'est juste une ligne de config qui point généralement vers localhost:8000.

]]>
http://sametmax.com/quest-ce-que-wsgi-et-a-quoi-ca-sert/feed/ 29
Utiliser Cherrypy (serveur web léger) avec Bottle (Framework léger) 21 http://sametmax.com/utiliser-cherrypy-serveur-web-leger-avec-bottle-framework-leger/ http://sametmax.com/utiliser-cherrypy-serveur-web-leger-avec-bottle-framework-leger/#comments Thu, 15 Nov 2012 16:01:38 +0000 http://sametmax.com/?p=3081 Pour les sites/app qu’on developpe en une journée, Bottle et Cherrypy sont deux larons en foire qui s’accouplent parfaitement…

Rappel:

Bottle: Framework python light, allez voir “Créer un site avec bottle en 5 minutes”
Cherrypy: Framework python light + serveur web


Une bonne Pip pour commencer:

monsieur@fion:~# pip install cherrypy

Dans votre fichier start.py:

run(host='0.0.0.0', port=80, server='cherrypy')

Note: le 0.0.0.0 permet d’acceder à votre site depuis l’extérieur.

Pour lancer en daemon vous pouvez utiliser supervisord ou faire un script dans le répertoire où se trouve start.py de votre projet et y coller:

#! /bin/sh
 
### Run server in daemon mode
nohup python start.py &

Plusieurs serveurs web sur la même machine:
Si vous avez un autre serveur web qui tourne sur votre machine et qui vous empêche de lancer cherrypy sur le port 80 vous pouvez utiliser nginx en proxy. Pour ce faire modifiez le port de cherrypy dans votre fichier start.py (voir plus haut) et réglez-le sur 7777 par exemple avec un host 127.0.0.1.
Ouvrez un fichier de conf nginx et copiez ceci:

upstream monsite_cherrypy {
    server 127.0.0.1:7777;
}
 
server {
        listen       80;
        server_name monsite.com www.monsite.com ;
 
        location /favicon.ico {
            root  /home/monsite/static/img;
        }
 
        location / {
            proxy_pass http://monsite_cherrypy; 
        }
 
}

Relancez Nginx (service nginx restart). Et votre site est en place. C’est ce qu’on utilise pour multiboards par exemple.

C’est rapide et simple et ça peut tenir pas mal de visiteurs. ça évite d’installer des trucs lourds avec 3 tonnes de config.

]]>
http://sametmax.com/utiliser-cherrypy-serveur-web-leger-avec-bottle-framework-leger/feed/ 21
Récupérer l’IP client avec Nginx en proxy sous CherryPy/Django/Bottle – proxy_set_header http://sametmax.com/recuperer-lip-client-avec-nginx-en-proxy-sous-cherrypy-proxy_set_header/ http://sametmax.com/recuperer-lip-client-avec-nginx-en-proxy-sous-cherrypy-proxy_set_header/#comments Thu, 06 Sep 2012 02:11:26 +0000 http://sametmax.com/?p=2041 Si vous utilisez Nginx en proxy, vous allez vous heurter à son IP lorsque votre script tentera de récupérer l’IP du client soit 127.0.0.1.

En rajoutant ces quelques lignes à votre config Nginx:

location / { 
 # ici un proxy pass quelconque
 proxy_pass http://cherrypy;
 
 # et ici la magie opère
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Dans votre script python (ici Bottle), vous pouvez du coup la récupérer:

#get user IP
user_ip = request.remote_addr
]]>
http://sametmax.com/recuperer-lip-client-avec-nginx-en-proxy-sous-cherrypy-proxy_set_header/feed/ 0