Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

CherryPy Tutorial

Originale inglese di Carlos Ribeiro

Traduzione italiana di Lorenzo Bolognini con la collaborazione di Elena Annunziata.

Premessa alla traduzione italiana: la traduzione può contenere errori di ogni forma, colore e gravità... ma siccome è un wiki, piuttosto che puntare il dito contro di me cliccate sul bottone in fondo alla pagina, rimboccatevi le maniche e fate le vostre modifiche. Se una cosa non scorreva bene nell'edizione inglese mi sono permesso di dargli una oliata nella mia traduzione. I termini inglesi di uso comune nella programmazione (indipendentemente dal linguaggio) ho preferito lasciarli in inglese nonostante default si possa tradurre con predefinito, request con richiesta 'e via dicendo... ma a questa regola ci sono eccezioni che, così spero, possano servire a rendere il discorso più comprensibile.

Cos'è CherryPy

CherryPy è un framework per lo sviluppo web pitonico e object-oriented: fornisce le fondamenta sopra le quali applicazioni web complesse possono essere scritte, con poca o nessuna conoscenza dei protocolli sottostanti. CherryPy consente agli sviluppatori di costruire applicazioni web in modo molto simile a come scriverebbero un altro programma Python a oggetti. Questo solitamente risulta in meno linee di codice sviluppato in meno tempo.

CherryPy fa del suo meglio per non intralciare la strada fra il programmatore e il problema. Le applicazioni scritte in CherryPy sono solitamente molto semplici. Funziona da subito e il suo comportamento di default è impostato così da permettere il suo uso senza eccessivi setup e personalizzazioni. Il web server integrato consente di installare le applicazioni web ovunque Python sia presente. In breve, CherryPy è pitonico come più non si può!

Cosa non è CherryPy

Pur essendo fenomenale CherryPy, comunque, non è tutto. Fa una cosa particolarmente bene: permette una facile pubblicazione di oggetti Python. Ma ci sono certe cose che CherryPy non è progettato per fare.

Come framework per applicazioni web, CherryPy fa tutto il necessario per permettere l'esecuzione del codice Python quando riceve la richiesta di una risorsa (o un URL) da un utente. Nonostante ciò, non è un linguaggio di templating come il PHP. CherryPy può interagire con diversi linguaggi di templating, inclusi Cheetah, CherryTemplate? e diversi altri (n.d.T per es. HTMLTemplate). Ma ci tengo a precisare che, sebbene utili (e fino a un certo punto), i linguaggi di templating non sono strettamente necessari e per generare pagine web è sufficiente usare esclusivamente il codice Python.

CherryPy integra un web server leggero e potente abbastanza da gestire discreti carichi. E' stato stimato che CherryPy possa gestire circa 500 richieste/secondo su una macchina con specifiche hardware ragionevoli (dati di Gennaio 2005); questo si traduce, all'incirca, in un traffico di 15Mbps. Ciò nonostante, CherryPy non è Apache. Se, per qualsiasi ragione avete bisogno di un server web di classe enterprise potete far girare CherryPy e Apache insieme.

Cosa tratta questo tutorial?

Questo tutorial copre i primi passi che un principiante deve compiere per prendere confidenza con il modo singolare con cui CherryPy affronta lo sviluppo di applicazioni web. Dopo aver seguito questo tutorial il programmatore sarà in grado di capire il modo in cui funzionano le applicazioni CherryPy e di implementare per proprio conto applicazioni semplici e potenti. Si presume una minima conoscenza di Python: non che si debba essere degli esperti per lavorare con CherryPy ma una buona conoscenza della programmazione a oggetti è fortemente raccomandata.

Pre-requisiti

Si presume che l'utente abbia:

  • una conoscenza minima del linguaggio di programmazione Python;
  • una conoscenza delle basi della programmazione a oggetti;
  • una qualche conoscenza di HTML, necessaria per costruire le pagine Web.

Imparare Python

Come ho detto sopra questa non è una guida al Python; ci sono diverse buone risorse per imparare il Python. Il sito ufficiale di Python elenca alcune buone risorse, incluso un eccellente tutorial.

La tua prima applicazione in CherryPy

Uno standard 'Hello World!' scritto in CherryPy occupa meno di 10 linee di codice:

from cherrypy import cpg

class HelloWorld:
    def index(self):
        return "Hello world!"
    index.exposed = True

cpg.root = HelloWorld()
cpg.server.start()

Presumiamo che tu abbia già installato CherryPy. Copia il listato e salvalo localmente in un file hello.py, poi avvia l'applicazione dalla linea di comando con:

python hello.py

Usando il tuo browser preferito punta a http://localhost:8000, vedrai questo risultato: (includere il link per uno screenshot)

Come funziona?

Diamo un occhiata a hello.py:

  • Il comando from cherrypy import cpg importa un singolo oggetto chiamato cpg dal modulo CherryPy. Questo è tutto quel che serve per far funzionare CherryPy. cpg è, in un certo senso, il web server stesso. Altro su questo più avanti.
  • Dichiariamo una classe chiamata HelloWorld. L'istanza di questa classe è l'oggetto che sarà pubblicato da CherryPy contenente un solo metodo, chiamato index, che verrà richiamato quando l'URL di base (root) del sito verrà richiesto (per esempio, http://localhost/). Questo metodo ritorna i contenuti di una pagina web, in questo caso la stringa 'Hello World!'.
  • L'index.exposed = True è un passaggio necessario per dire a CherryPy che il metodo index() sarà esposto. Solo i metodi esposti possono essere chiamati a rispondere alle richieste. Questa feature consente agli utenti di selezionare quali metodi saranno accessibili dal web; non si puo' accedere a metodi non esposti.
  • Il cpg.root = HelloWorld() pubblica un'istanza della classe HelloWorld?.
  • La chiamata cpg.server.start() avvia il web server integrato che gira fino a che non viene esplicitamente interrotto con un Ctrl-C oppure tramite un segnale appropriato (un semplice kill su Unix è sufficiente). E' anche possibile implementare lo shutdown del server come parte dell'applicazione, ma ciò va oltre lo scopo di questo tutorial.

Quando l'applicazione viene lanciata, il server CherryPy viene avviato con la configurazione di default e resta in ascolto su localhost sulla porta 8000. Queste impostazioni di default possono essere modificate usando un file di configurazione (ulteriori informazioni sull'argomento saranno fornite in seguito).

Alla fine il web server riceve la richiesta per l'URL http://localhost:8000 cercando il metodo migliore per gestire la richiesta iniziando da cpg.root (che è l'istanza di HelloWorld). In questo particolare caso, la root del sito è automaticamente associata (n.d.T RFC: mappata vi sembra più comprensibile?) al metodo index() (simile all'index.html che è la pagina standard per i normali web server). La classe HelloWorld? definisce un metodo index() e lo espone. CherryPy richiama il cpg.root.index() e il risultato della chiamata viene restituito al browser sotto forma dei contenuti della pagina index del sito. Tutto il lavoro viene effettuato automaticamente: il programmatore deve solo preoccuparsi di fornire il contenuto della pagina come valore di return del metodo index.

Concetti

Pubblicare oggetti

Ciascun oggetto connesso al cpg.root si dice pubblicato; ciò significa che l'oggetto è accessibile attraverso la procedura interna di associazione URL-oggetti. Tuttavia, questo non vuol dire che lo stesso oggetto sia accessibile dal web: per ottenere ciò, l'oggetto deve essere esposto.

Esporre oggetti

CherryPy associa le richieste di URL agli oggetti e richiama il corrispondente metodo automaticamente. I metodi che possono essere richiamati come risultato di una richiesta esterna si dicono esposti.

Gli oggetti vengono esposti in CherryPy impostando l'attributo exposed. Questo può essere fatto direttamente sull'oggetto stesso <object>.exposed = True; i metodi possono anche essere esposti usando uno speciale decorator:

    @cpg.expose
    def index(self):
        ...

Perche' questa distinzione?

La distinzione fra oggetti pubblicati ed esposti può sembrare frivola e superflua a prima vista: per definizione tutti gli oggetti esposti sono anche pubblicati perciò perché dovremmo preoccuparci degli oggetti non esposti? La risposta va cercata nel fatto che, sebbene si possa accedere tramite una richiesta esterna solo agli oggetti esposti, ci sono situazioni dove le richieste possono essere ri-mappate (ri-associate) a qualsiasi oggetto pubblicato, inclusi quelli non esposti. Questa è una funzione avanzata che entrerà in gioco più tardi; per il momento limitiamoci a capire i concetti fondamentali.

Trovare l'oggetto giusto

Per un utente un'applicazione web è come un sito web con pagine statiche. L'utente scrive (o clicca) su un URL e arriva sulla pagina richiesta. Un normale webserver utilizza l'URL per recuperare i file statici dal filesystem. D'altro canto, un web application server non mostra il contenuto dei file statici ma incapsula l'URL che riceve in qualche oggetto e lo richiama. Il risultato è un'applicazione dinamica: per ogni URL un solo oggetto può essere chiamato a rispondere.

La chiave per comprendere come scrivere una nuova applicazione web è capire come si crea questa associazione URL/oggetto. CherryPy utilizza una procedura di associazione abbastanza semplice. La root del sito è l'oggetto cpg.root; questa, quando riceve un URL, lo scompone in parti di un percorso e procede a cercare all'interno del sito finchè trova l'oggetto che 'meglio corrisponde' a quel particolare URL. Per ogni parte che compone il percorso tenta di trovare un oggetto dello stesso nome partendo da cpg.root e discendendo nella gerarchia del sito per ogni parte di URL che trova finchè non raggiunge l'oggetto che meglio gli corrisponde. Un esempio può chiarire meglio:

cpg.root.onepage = OnePage()
cpg.root.otherpage = OtherPage()

In questo esempio l'URL http://localhost/onepage punta al primo oggetto e l'URL http://localhost/otherpage punta al secondo. Come al solito, questa ricerca è fatta automaticamente, ma va anche oltre:

cpg.root.some = Page()
cpg.root.some.page = Page()

In questo esempio l'URL http://localhost/some/page sarà associato all'oggetto cpg.root.some.page: se quest'oggetto è esposto o, alternativamente, lo è il suo metodo index, sarà richiamato per servire la richiesta di quell'URL.

Il metodo index

Il metodo index() ha un ruolo speciale in CherryPy (come il file index.html è la pagina di default) per ogni ramo interno della gerarchia degli oggetti del sito. Il metodo index() può prendere altri keyword arguments che sono automaticamente associati alle variabili generate dalla form e spedite attraverso i suoi metodi GET o POST.

Il metodo index() è richiamato solo quando c'è una corrispondenza precisa con l'URL. Se l'URL offre una corrispondenza parziale (in altre parole se CherryPy non riesce a trovare una corrispondenza precisa a un componente del percorso richiesto) non verrà chiamato.

CherryPy può anche richiamare direttamente metodi negli oggetti pubblicati se riceve un URL che gli è direttamente associato. Per esempio:

def foo():
    return 'Foo!'
foo.exposed = True

cpg.root.foo = foo

Nell'esempio cpg.root.foo contiene un oggetto funzione chiamato foo. Quando CherryPy riceve una richiesta per l'URL /foo, richiamerà automaticamente la funzione foo(). E' bene notare che può essere una semplice funzione o un metodo di qualsiasi oggetto, qualsiasi callable può fare allo scopo.

In qualche caso avanzato può esserci un conflitto quando CherryPy cerca di decidere quale metodo richiamerà per rispondere alla richiesta. Il metodo index() ha la precedenza ma, se CherryPy trova una corrispondeza precisa e l'ultimo oggetto che corrisponde (all'URL) è un callable (ovvero qualsiasi metodo, funzione, o qualisasi altro oggetto Python che supporta il metodo __call__) e se il callable non contiene un metodo index(), allora verrà richiamato l'oggetto stesso. Può sembrare complicato ma in pratica le regole sono molto semplici da imparare ed usare.

Ricevere dati da una form HTML

Ogni metodo richiamato da CherryPy - index o un altro metodo idoneo - può ricevere altri dati da form HTML usando keyword arguments. Per esempio la seguente form di login invia lo username e la password come argomenti della form usando il metodo POST:

    <form action="doLogin" method="post">
        <p>Username</p>
        <input type="text" name="username" value="" size="15" maxlength="40"/>
        <p>Password</p>
        <input type="password" name="password" value="" size="10" maxlength="40"/>
        <p><input type="submit" value="Login"/></p>
        <p><input type="reset" value="Clear"/></p>
    </form>

Il seguente codice può essere utilizzato per gestire l'URL:

class Root:
    def doLogin(self, username=None, password=None):
        # check the username & password
        ...
    doLogin.exposed = True

cpg.root = Root()

Entrambi gli argomenti devono essere dichiarati come keyword arguments. Il valore di default può essere utilizzato per fornire indifferentemente un valore di default idoneo per gli argomenti opzionali o per dare modo all'applicazione di controllare se qualche valore manca dalla request.

CherryPy supporta sia i metodi GET che POST delle form. Gli argomenti vengono passati nello stesso modo, indipendentemente dal modo originario usato dal browser per spedire i dati al web server.

Corrispondenza parziale e metodo di default

Si può andare incontro ad una corrispondenza parziale quando un URL contiente pezzi che non corrispondono alla gerarchia di oggetti di cpg.root. Questo può accadere per diverse ragioni: potrebbe essere un errore, l'utente potrebbe aver richiesto l'URL sbagliato, ecc... ma potrebbe anche voler dire che l'URL contiene ulteriori argomenti.

Quando si incappa in una corrispondenza parziale, CherryPy richiama il metodo default: questo è simile al metodo index; tuttavia è richiamato solo come ultima risorsa ed è consigliato in due casi:

  • Gestione degli errori: da richiamare quando un utente richiede l'URL sbagliato;
  • Gestione dei positional arguments.

La gestione dei positional arguments quando si gestiscono corrispondenze parziali agli URL richiesti è una delle nuove features di CherryPy. Per esempio, supponiamo di avere un'applicazione tipo blog, scritta in CherryPy e che prenda l'anno, il mese e il giorno come parte di un URL:

http://localhost/blog/2005/01/17

Questo URL può essere gestito dal seguente codice:

class Blog:
    def default(self, year, month, day):
        ...
    default.exposed = True
...
cpg.root.blog = Blog()

Così l'URL sopracitato troverà corrispondenza a una chiamata:

cpg.root.blog.default('2005', '1', '17')

In tal caso c'è una corrispondenza parziale fino a Blog; il resto dell'URL non può essere trovato nella gerarchia degli oggetti pubblicati, così il metodo default() viene richiamato ed i positional arguments ricevono le parti rimanenti del percorso come argomenti. I valori sono passati come stringhe (nel precedente esempio gli argomenti dovrebbero essere convertiti in numeri ma spero di aver reso l'idea).

Il file di configurazione di CherryPy

CherryPy usa un semplice file di configurazione per modificare alcuni aspetti del suo comportamento. Il file di configurazione può essere modificato con un semplice file di testo (Notepad può andar bene).

[server]
socketPort = 8000
threadPool = 10

[staticContent]
bitmaps = bitmaps
css = css

[session]
storageType=ram

Molte delle variabili si spiegano da sole (per esempio, socketPort, consente la modifica della porta su cui CherryPy ascolta); altre variabili richiedono invece una più approfondita conoscenza degli internals di CherryPy.

  • threadPool specifica il numero di thread the CherryPy avvia per servire la richiesta.
  • [staticContent] contiene una lista di valori che forzano la corrispondenza di URL a file statici. Quando CherryPy riceve un URL il cui prefisso corrisponde ad uno dei valori specificati in [staticContent], restituisce il contenuto del file (o della directory) specificato in questa sezione del file di configurazione.
  • [session] contiene opzioni per controllare la gestione delle sessioni in CherryPy. Le sessioni sono necessarie per sviluppare applicazioni web complesse, per esempio con autenticazione degli utenti.

Il file di configurazione può essere passato come parametro al metodo start: le variabili in esso contenute saranno lette appena prima che il server venga avviato.

cpg.server.start(configFile="myserver.conf")

La struttura di cpg

La maggior parte delle features di CherryPy sono accessibili attraverso l'oggetto cpg che contiene i seguenti membri:

  • cpg.root è la gerarchia degli oggetti pubblicati; l'oggetto corrispondente a cpg.root è la root del vostro sito e gli oggetti che contiene costituiscono la gerarchia di oggetti che compongono la vostra applicazione.
  • cpg.server contiene la API per il controllo del server
  • cpg.request contiene tutte le informazioni ricevute dalla HTTP request dopo essere stata parsata e analizzata da CherryPy
    • cpg.request.headerMap contiene un mapping con le opzioni degli header che sono state inviate come parte della richiesta. [n.d.T non vi fidate troppo della traduzione di questa frase, non mi è venuta molto bene!]
    • cpg.request.sessionMap è un mapping speciale che è generato automaticamente e codificato da CherryPy; può essere utilizzato per registrare variabili di sessione su un cookie.
  • cpg.response contiene i dati che vengono utilizzati per costruire la response HTTP
    • cpg.response.headerMap contiene un mapping con le opzioni dell'header che verrà ritornato dal server prima che il contenuto venga spedito.
    • cpg.response.body contiene i contenuti della pagina che viene spedita come response.

Filtri

Il cuore di CherryPy è estremamente leggero e pulito: contiene solo le features necessarie a supportare il protocollo HTTP ed a richiamare l'oggetto giusto per ogni richiesta; altre features possono essere aggiunte utilizzando i filtri.

Un filtro è un oggetto con la possibilità di lavorare su una richiesta man mano che viene elaborata da CherryPy. Sono disponibili diversi filtri come parte della dotazione standard di CherryPy (li trovate in cherrypy.lib.filter). Alcuni di questi sono:

  • DecodingFilter?: si occupa di gestire automaticamente i dati Unicode della request convertendo le semplici stringhe che vengono spedite dal browser in stringhe Python native.
  • EncodingFilter?: converte automaticamente la response da Unicode nativo di Python a qualche codifica idonea (per esempio Latin-1 o UTF-8)
  • GzipFilter: comprime i contenuti al volo, usando il formato gzip e serve a risparmiare banda.
  • XmlRpcFilter?: implementa uno speciale strato di astrazione XML-RPC sopra quello standard di CherryPy; si occupa di tradurre i dati provenienti da request e response (questo processo è chiamato 'marshalling').

I filtri forniscono una grande flessibilità. La lista dei filtri? può essere configurata per ogni sezione del sito: filtri diversi possono essere applicati a diverse parti del sito ed il loro ordine può essere cambiato. L'utente può scrivere i propri filtri per scopi particolari cambiando così il modo in cui CherryPy si comporta senza il bisogno di modificare i suoi internals.

La lista dei filtri per ogni parte del sito può essere configurata usando la variabile speciale _cpFilterList:

from cherrypy.lib.filter import EncodingFilter, GzipFilter

class Root:
    _cpFilterList = [EncodingFilter('utf8'), GzipFilter()]
    ...

In questo caso l'applicazione può utilizzare stringhe Unicode per i contenuti che genera: la trasformazione in UTF-8 verrà fatta automaticamente, inoltre, tutto il contenuto verrà compresso in gzip risparmiando banda.

Conclusione

Questo tutorial copre solo le features di base di CherryPy ma cerca di presentarle in un modo che rende facile all'utente lo scoprire come usarle. Il pacchetto CherryPy viene distribuito con diversi buoni tutorial, tuttavia il modo migliore per dominare CherryPy è utilizzarlo per scrivere le vostre applicazioni web. Il web server integrato facilita tutti non solo nel provare ma anche nell'installare applicazioni e nel fare siti internet. Provatelo, e fateci sapere cosa ci avete costruito!

Hosted by WebFaction

Log in as guest/cpguest to create tickets