Programmazione CGI PYTHON Protocol La programmazione CGI viene comunemente identificata con la programmazione in Perl, in quanto quest ultimo è universalmente riconosciuto come il principe tra i linguaggi di scripting. In realtà abbiamo a disposizione un ampio ventaglio di linguaggi, in funzione delle nostre esigenze, ma la fama del Perl è legata ad un suo grande sostenitore, Jhon Udell, ed al notevole successo della sua rubrica Web techniques dalle pagine di Byte. Nelle righe che seguono vedremo alcune delle possibilità offerte dal Python per la programmazione CGI, e proveremo a realizzare qualche esempio in modo da evidenziare la semplicità che ne sta alla base. Cosa significa CGI? Il significato di CGI è Common Gateway Interface, uno standard per interfacciare applicazioni esterne con un server web. Quando il nostro browser mostra una pagina HTML, viene letto un file che risiede sul server; questo documento è statico, in quanto il suo contenuto non può cambiare. Lo stesso browser è anche in grado di leggere un file CGI, un file dinamico il cui contenuto può variare. Il file CGI richiamato dal nostro browser infatti, viene eseguito sul server con un certo numero di parametri che possono anche essere inseriti dall utente tramite speciali moduli (form), ed in base a questo l output può essere diverso. Se volessimo creare un database con i prodotti di cui dispone la nostra azienda, potremo scrivere un CGI eseguibile da qualunque utente connesso ad Internet che interroga un archivio. L utente potrebbe trovarsi di fronte ad un form che mostra i vari articoli, in modo da selezionare quelli che desidera, per ricevere come output i relativi prezzi. Non ci sono limiti alla programmazione CGI, se non quello del tempo necessario all esecuzione. Se infatti la complessità del compito dovesse tradursi in un tempo di attesa eccessivo da parte dell utente, il lavoro sarebbe inutile: difficilmente un navigatore trascorre più di 10 secondi in attesa di un responso. Perché Python? Non intendo trattare le caratteristiche del Python in questa prima parte degli articoli; basti dire che si tratta di un solido linguaggio orientato agli oggetti (Object Oriented, diversamente dal Perl che è Object Based), con una sintassi estremamente chiara e semplice. Questo agevola la manutenibilità del codice da parte di più persone e la stessa riusabilità. L estensibilità del Python garantisce la disponibilità di moduli con eccellente documentazione relativi alle più disparate funzionalità. Questo è vero soprattutto per l insieme di moduli correlati ad Internet, con librerie dedicate alla gestione degli URL, del protocollo POP, del CGI e tanto altro ancora.
2 Non mi propongo di realizzare una trattazione teorica, ma auspico che chiunque legga questo documento sia pronto a realizzare i semplici esempi che verranno proposti per capire meglio i meccanismi che stanno alla base del CGI. Perché questo sia realizzabile bisogna disporre di un server HTTP; il più conosciuto, diffuso e meglio documentato è sicuramente il web server Apache. Da questo punto in poi presupporrò che disponiate di un Apache di versione almeno 1.1.x.
3 Questo nella sua configurazione di default non dovrebbe avere abilitata l esecuzione di programmi CGI. I file di configurazione dovrebbero risiedere in una directory tipo /etc/httpd/conf/ e sono tre: httpd.conf, access.conf, srm.conf. In genere il server viene impostato in modo che la directory root sia /home/httpd/html/; questo significa che l indirizzo locale http://localhost/ o http://127.0.0.1/ dovrebbe mostrare il file /home/httpd/html/index.html; nel caso dell Apache questo dovrebbe avere come titolo "It Worked!". A questo punto dobbiamo abilitare l esecuzione di programmi CGI; abbiamo due possibilità: Limitare l esecuzione ai soli programmi residenti nella dir /home/httpd/cgi-bin/. Per ottenere questo risultato è necessario modificare il file access.conf e modificare la seguente riga : <Directory /home/httpd/cgi-bin> Options Indexes FollowSymLinks </Directory> in <Directory /home/httpd/cgi-bin> Options FollowSymLinks ExecCGI </Directory> Garantire l esecuzione a tutti i programmi presenti nelle varie subdir di /home/httpd/html/. Per fare questo è necessario modificare il file acess.conf ed eliminare il cancelletto # in testa alla riga <Directory /home/httpd/html> Options All </Directory> Inoltre è necessario aggiungere al file srm.conf la riga: AddHandler cgi-script.py A questo punto perché un programma sia universalmente eseguibile, deve poter disporre dei relativi permessi, che possono essere conferiti mediante chmod ugo+rx miofile.py.
4 Il primo script. Di solito il primo esempio di programmazione è Hallo World!; ho pensato di mantenere invariata la tradizione; vediamo il listato di ciao.py: #!/usr/bin/python def main(): print "Content-type: text/html" print print "<TITLE> Hello, World!</TITLE>" print "Hello, World!" La prima riga contiene il percorso dell interprete; in questo modo se abbiamo il permesso di esecuzione, il programma può essere chiamato all interno della sua dir con./ciao.py. Se omettiamo la prima riga, il programma continua a girare se richiamato dalla shell, ma solo se invochiamo anche l interprete: python ciao.py. Ovviamente nel nostro caso sarà il browser a richiamare indirettamente il programma per cui la prima riga è assolutamente necessaria. La seconda riga definisce la funzione principale main(); le righe che seguono indentate rappresentano il corpo della funzione (per chiarire il significato dell indentazione nel Python vedi http://web.tiscalinet.it/python/doc/index.html ). Le righe che seguono iniziano tutte con print e si occupano di stampare il relativo contenuto nello standard output. In questo modo la prima comunica al browser il tipo del contenuto del documento: in questo caso si tratta di text/html. La riga successiva stampa una riga bianca, risultato che si sarebbe potuto ottenere anche con print "Content-Type: text/plain\n\n" usando il relativo carattere di escape. La porzione di codice che segue fornisce dapprima il titolo al documento che visualizzerà il browser, e quindi il contenuto: Hello,World!
5 Stampiamo un immagine Utilizziamo i concetti acquisiti nel precedente esempio per stampare una immagine. Ipotizziamo di disporre di una foto ferrari.gif; in linea di principio dovrebbe essere necessario comunicare al bowser che il contenuto del file è una immagine. In realtà lo script corretto è un po più complesso: import sys immagine =open('ferrari.gif','rb').read() sys.stdout.write('content-type:image/gif\r\n') sys.stdout.write('\r\n') sys.stdout.write(immagine) La apparente maggiore complessità di questo script è essenzialmente legata al fatto che stampare l immagine con print è poco opportuno: potrebbe non essere interpretato correttamente dal browser, o in concomitanza di una riga vuota potrebbe persino non essere interpretato. Per ovviare a questo inconveniente, si richiama inizialmente il modulo sys contenente le operazioni di sistema come sys.stout.write() che rappresenta l equivalente di print. La riga che segue serve per leggere il file in cui è contenuta l immagine (ferrari.gif), in modalità rb, dove r sta per read only, in quanto il file è aperto in sola lettura; l opzione b è necessaria solo nel caso in cui si debba lavorare con Windows, nel qual caso è necessario distinguere tra file binario (come in questo caso) o di testo. A questo punto troviamo la comunicazione al browser sul contenuto del documento, quindi i caratteri di escape per andare a capo, ed infine l effettiva stampa dell immagine.
6 Coordinate temporali Vediamo spesso delle pagine web in cui in un piccolo riquadro viene stampata l ora locale; proviamo a realizzarla. Il risultato che ci proponiamo di ottenere è incredibilmente semplice, come appare dal seguente listato: #!/usr/bin/python from time import * def main(): print "Content-type: text/html" print print "<TITLE>In mezzo al Mediterraneo</TITLE>" print "Ciao!" print "<P>Eccoci in Sardegna!<br>Oggi e'", ctime( time() ) + ".<P>" In pratica il compito è demandato al modulo time richiamato inizialmente che si basa sull orologio interno del server, e mediante la sua funzione ctime, visualizza giorno, ora ed anno.
7 I form I form sono una sorta di modulo virtuale nel quale l utente può inserire del testo, rispondere a quesiti, selezionare delle scelte mediante bottoni. Alla fine del form, si trovano in genere due bottoni: invia e resetta. Tutto questo è realizzato mediante codice HTML. Quando l utente invia il form, il contenuto viene passato ad un programma CGI, che lo valuta e ad esempio può aggiornare un database. Proviamo a realizzare un modulo per la ricerca di un dipendente tra quelli appartenenti ad una azienda. Ecco il codice HTML del file form.html: <HTML> <Head> <Title>Questionario per i Clienti</Title> </Head> </Body> <H1>Ricerca nell'archivio degli<em>impiegati</em></h1> <br> <Form Method = "POST" Action = "http://localhost/echoform.py"> <Input Name="nomi" type=hidden value="clienti"> <P>Codice :<Input Name="codice" Type=int> <P>Nome :<Input Name="nome" Type=text size=32> <P>Indirizzo : <Input Name="indirizzo" Type=text maxsize=80><br> Cap : <Input Name="cap" Type=textsize=8><br> Città : <Input Name="città" Type=text size=32> <P>Tel. : <Input Name="tel" Type=text size=16><br> Fax : <Input Name="fax" Type=text size=16> <P>Commenti :<br> <Textarea Name="commenti" Rows=2 Cols=80></Textarea> <P><Input Name="submit" Type=submit value="invia"> <Input Name="reset" Type=reset value="resetta"> </Form> </Body> </HTML> Credo che spiegare il contenuto di questo file sia inutile, in quanto è molto più immediato vederne il risultato; tuttavia è interessante notare la possibilità fornita dal HTML di definire un campo hidden, nascosto.
8 Vediamo adesso il listato del programma CGI echoform.cgi, che deve analizzare i dati: #! /usr/bin/python import cgi def print_header() : #stampa l'intestazione print "Content-type: text/html" print print "<HTML>" print "<Head><Title>Restituzione Parametri</Title></Head>" print "<Body><H1>Restituzione Parametri</H1><HR>" def display_query( form ) : #mostra i dati fields = form.keys() print "<P><H2>Sono stati ricevuti i seguenti parametri:</h2>" print "<UL>" if len( fields ) == 0 : print "<LI>Nothing" else : for f in fields : print "<LI>", f, " : ", cgi.escape(form[ f ] ) print "</UL>" def print_footer() : #stampa il termine della pagina print "</BODY></HTML>" def Main() : #funzione principale form = cgi.svformcontentdict() print_header() display_query( form ) print_footer() Main() Inizialmente importiamo il modulo built in CGI, nel quale sono definite funzioni come cgi.escape. A questo punto viene definita la funzione print_header(); si occupa di stampare l intestazione (come evidenzia la docstring) e non è molto diversa dal precedente Hello, World! La seconda funzione stampa i dati che sono stati inseriti; l istruzione if controlla se effettivamente sono stati inseriti dati; in questo caso, il ciclo for si occupa di stampare solo le voci relative ai dati inseriti dall utente. La funzione escape ha come effetto quello convertire caratteri come &, <, > in stringhe che possono essere interpretate senza equivoci dal browser.
9 La terza funzione ha come scopo quella di concludere il documento, fornendo le istruzioni terminali alla pagina HTML. In conclusione viene definita la funzione principale main(), dove il contenuto del form è assegnato alla classe SvFormContentDict(), che lo trasforma in un dizionario. Vengono poi richiamate le funzioni definite precedentemente. Conclusioni Spero che questo documento abbia stimolato il vostro interesse nei confronti di Python, e vi invito a dare uno sguardo presso la sua homepage italiana o il sito ufficiale in inglese, i cui indirizzi sono indicati in fondo all'articolo. Buona lettura! Siti Collegati http://web.tiscalinet.it/python http://www.python.org/