WSGI e applicazioni web in Python
difficile
WSGI
WSGI sta per python Web Server Gateway Interface. E' il modo standard in cui un applicazione Python si interfaccia con un Web server.
WSGI è supportato, oltre che dal modulo wsgiref della libreria standard, da Apache tramite il modulo mod-wsgi , da nginx (in modo sperimentale), da cherrypy da twisted e da altri.
Un applicazione wsgi è fatta così:
def my_app(environ, start_response):
status = '200 OK'
response_headers = [('Content-type','text/plain')]
start_response(status, response_headers)
return ["hello world"]
si tratta semplicemente di una funzione che prende in input un dizionario environ contenente i parametri di ambiente.
Di seguito riporto, come esempio, il contenuto di environ riportato su una mia applicazione (la pagina richiamata è http://localhost:7789/):
wsgi.multiprocess=False wsgi.multithread=True SERVER_SOFTWARE=CherryPy/3.1.1 WSGI Server SCRIPT_NAME=/admin/make_pdf ACTUAL_SERVER_PROTOCOL=HTTP/1.1 wsgi.input=<cherrypy.wsgiserver.SizeCheckWrapper object at 0x013180D0> REQUEST_METHOD=GET HTTP_HOST=localhost:7789 PATH_INFO= SERVER_PROTOCOL=HTTP/1.1 QUERY_STRING= wsgi.version=(1, 0) HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 HTTP_ACCEPT_CHARSET=ISO-8859-1,utf-8;q=0.7,*;q=0.7 HTTP_USER_AGENT=Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.8 HTTP_CONNECTION=keep-alive SERVER_NAME=D01C100164 REMOTE_ADDR=127.0.0.1 wsgi.run_once=False wsgi.errors=<open file '<stderr>', mode 'w' at 0x00A100B0> REMOTE_PORT=1780 HTTP_ACCEPT_LANGUAGE=it-it,it;q=0.8,en-us;q=0.5,en;q=0.3 wsgi.url_scheme=http SERVER_PORT=7789 HTTP_ACCEPT_ENCODING=gzip,deflate HTTP_KEEP_ALIVE=300
Come argomento viene passato anche un reference ad una funzione (start_response) che dovrà essere utilizzata per inizializzare la risposta: start_response prende in input una stringa che dovrà contenere l'http status e una lista di tuple (chiave valore) che fanno parte dell' intestazione http. Come output della funzione verrà restituito un iterabile (ad esempio una lista). Questa caratteristica rende possibile utilizzare yield al posto di return (questo può favorire l'utilizzo di webserver asincroni come nginx o twisted . . . ma questa è un altra storia).
Ecco un applicazione wsgi molto semplice di esempio che restituisce una pagina web.
Si tratta di un form che richiede un nome, premendo submit verrà proposta una pagina con Hello seguito dal nome scelto.
Vediamo il codice:
Come prima cosa definiamo un template che farà da base a tutte le nostre pagine html.
html_template="""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="it" lang="it"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta name="author" lang="it" content="Maurizio Lupo" /> <title>My web interface</title> <link rel="stylesheet" media="screen,projection" type="text/css" href="/static/default.css" /> </head> <body> <div id="header"><img src="/logo.png" /></div> <div id="menu"> <a href="/admin">Main menu</a> <a href="/admin/about">About</a> </div> <div id="main"> %s </div> <div id="footer"> My web interface </div> </body> </html>"""
Riporto una semplice funzione che trasforma la query string in un dizionario:
ad esempio: http://miosito/applicazione?nome=mickey+mouse&indirizzo=house+of+mouse diventerà
{nome=mickey mouse,indirizzo=house of mouse}
from urllib import unquote_plus
def get_args(data):
ret = {}
for val in data.split('&'):
val = val.split('=', 1)
k = val[0].lower()
if len(val) > 1: v = val[1]
else: v = ''
ret[k] = unquote_plus(v)
return ret
webinterface è la mia semplicissima applicazione WSGI realizzata come autosubmitting for: in base alla presenza dei dati richiesti nella query string sottoporrò la form con la richiesta dei dati oppure risponderò con l'elaborazione dei dati.
def webinterface(environ, start_response):
args=get_args(environ['QUERY_STRING'])
if 'name' in args:
content="<h1>Hello %s</h1>" % (args['name'])
else:
content="""
<div class="form">
<div class="title">Form</div>
<form action="/admin/new" method="get">
<div class="label">Name:</div><input type="text" name="Name" /><br />
<input type="submit" value="Submit" />
</form>
</div>
"""
status = '200 OK'
response_headers = [('Content-type','text/html')]
start_response(status, response_headers)
return [html_template % (content,)]
Nella prossima pagina vedremo come pubblicare un applicazione scritta in questo modo con il web server WSGI CheeryPy.