Bookmark and Share
Document Actions

Gestire progetti Plone con zc.buildout

Note: Return to tutorial view.

Scopri gli egg, setuptools e la gestione delle dipendenze, e come usare zc.buildout in Plone per predisporre un ambiente di sviluppo.

Introduzione

Ovvero: "Cosa c'e' di sbagliato in una classica normale istanza Zope?"

Traduzione dall'originale di Martin Aspeli: http://plone.org/documentation/tutorial/buildout

Prima di Plone 3.0, la maggior parte degli sviluppatori e degli utenti che non usavano un installer grafico, avrebbero predisposto un'istanza Zope, aggiunto qualche prodotto nella cartella Products, e il gioco era fatto. Sfortunatamente questo approccio presenta qualche problema:

  • le vecchie istanze Zope non sono molto predisposte a gestire paccheti distribuiti come egg python o ad usare pacchetti namespace di setuptools. Molti nuovi pacchetti in Plone 3 sono costruiti in questo modo, e sempre più moduli di estensione lo saranno a loro volta.
  • Senza accedere ai metadati contenuti negli egg, gli sviluppatori potrebbero ritenere troppo lungo o caotico il fattorizzare il proprio lavoro in diversi pacchetti maggiormente riusabili, preferendo prodotti monolitici, che sono pero' impossibili da riusare fuori dal contesto Zope.
  • Senza strumenti specifici, è piuttosto pesante replicare un'istallazione tra ambienti diversi
Mentre le Egg acquistano importanza, gli sviluppatori dovrebbero cercare di utilizzare strumenti più appropriati per gestire il loro codice: zc.buildout, da qui in avanti chiamato solo "buildout", è un tale strumento. Questa guida mostra come usare buildout sia per lo sviluppo di tutti i giorni che per la messa in produzione.

Pacchetti, prodotti e egg

Diamo uno sguardo ai concetti di base con maggior dettaglio

Terminologia

Prima di iniziare, chiariamo il significato di alcuni termini

Installazione Zope
Quanto ottenuto scaricando e compilando Zope, o usando una delle distribuzioni binarie di Zope.
Software home (variabile ambiente $SOFTWARE_HOME)
Dentro l'installazione Zope, la cartella lib/python contiene tutto il codice Python del nucleo dell'application server Zope, noto come software home. I vari pacchetti Zope 3 sono distribuiti qui.
Instanza Zope
Una installazione Zope può supportare molte istanze. Lo script mkzopeinstance.py nell'installazione Zope è usato per creare nuove istanze. Ogni istanza ha uno script di controllo per avviare e fermare il servizio, un file di configurazione zope.conf, una cartella Products, e un file Data.fs che contiene lo ZODB. In questa guida faremo in modo che buildout generi e configuri una istanza Zope per noi.
Instance home (variabile ambiente $INSTANCEHOME)
Il path dell'istanza Zope corrente.
Path Python (variabile ambiente $PYTHONPATH, nota anche come sys.path)
L'interprete Python cercherà moduli in una o più cartelle, note come path Python. Quando Zope è in esecuzione, questo normalmente comprende i moduli Python globali che fanno parte della library standard, la cartella site-packages dell'interprete, dove sono installati moduli ed egg di estensione "globali", la software home di Zope, e la cartella lib/python nella instance home.
In questa guida vedremo in che modo buildout aggiunge un certo numero di egg specifiche al path Python, addirittura in fase di esecuzione.
Pacchetto Python
Un termine generico che descrive un modulo Python ridistribuibile. Nella forma più semplice, un pacchetto è una cartella con un file __init__.py  e del codice Python.
Prodotto Zope
Un tipo speciale di pacchetto Python usato per estendere Zope. Nelle vecchie versioni di Zope tutti i prodotti erano cartelle all'interno della cartella speciale Products di una istanza Zope, e avevano il nome del modulo Python che inizia con "Products". Per esempio, il nucleo di Plone è un prodotto chiamato CMFPlone, noto in Python come Products.CMFPlone.
Egg Python
Un modo di impacchettare e distribuire pacchetti Python. Un Egg contiene un file setup.py con metadati quali il nome e la mail dell'autore e le informazioni di licenza, come pure informazioni relative alle dipendenze. setuptools, la libreria Python che potenzia il meccanismo degli egg, è capace di trovare e scaricare automaticamente le dipendenze per le egg che installi. E' perfino possibile per due egg diverse usare versioni diverse della stessa dipendenza. Le Egg supportano anche una funzione nota come entry points, una sorta di meccanismo di plug-in generico. Non parleremo degli extension points in dettaglio durante questo tutorial, ma potrai leggere altro a riguardo (e altre funzionalità degli egg) sul sito PEAK.
Il Cheese Shop (noto anche come PyPI, Python Package Index)
Il Cheese Shop è un indice che ospita migliaia di pacchetti Python. Puoi navigarlo se sei in cerca di un pacchetto specifico. Ancora più importante, setuptools (e buildout, e lo script easy_install) può interrogare questo indice per scaricare e installare automaticamente egg.
 easy_install
Uno script per la shell che può essere usato per cercare nel Cheese Shop e installare pacchetti nell'ambiente Python globale. Lo utilizzeremo solo per installare alcuni pacchetti globali, dato che buildout gestisce le egg per noi in modalità locale ad ogni progetto  buildout, evitando ambiguità di versione globali.
Pacchetto Namespace
Una funzionalità di setuptools che rende possibile distribuire pacchetti multipli e separati condividendo un singolo namespace di primo livello. Per esempio, i pacchetti plone.theme e plone.portlets condividono entrambi lo stesso namespace di primo livello "plone", ma sono distribuiti con egg separati. Quando vengono installati, ogni egg vive nella sua cartella (o in alternativa in una archivio compresso di quella cartella). Senza pacchetti namespace, dovremmo distribuire un unico pacchetto Plone gigante, con una cartella Plone di primo livello contenente tutti i possibili figli, come ad esempio plone/theme e plone/portlets.

Il namespace magico Products

Quando Zope trova un "prodotto", crea una voce in Control_Panel/Products nella root della ZMI, ed esegue il metodo initialize(), che trova nel file __init__.py nella root del prodotto, ogni volta che Zope si avvia. Non tutti i pacchetti usati in un contesto di Plone devono essere prodotti, ma alcune caratteristiche proprie dei prodotti veri e propri sono richieste per:

  • i profili di GenericSetup
  • cartelle Skin installate come layer nel tool portal_skins (ma non per i browser view in stile Zope 3)

Il modo più semplice di creare un prodotto è di farne una cartella nella cartella Products di una istanza (o buildout). Ad ogni modo, fino a Zope 2.11 (che trasforma il namespace Products.* in un pacchetto namespace corretto), non possiamo distribuire prodotti sotto forma di egg. Inoltre molti sviluppatori trovano poco naturale mantenere tutto in un singolo namespace "piatto".

E' possibile usare pacchetti (inclusi quelli distribuiti con gli egg) fuori dalla cartella/namespace Products come prodotti Zope 2. Per fare questo dobbiamo aggiungere una linea come la seguente al file configure.zcml del pacchetto:

<configure 
    xmlns="http://namespaces.zope.org/zope"
    xmlns:five="http://namespaces.zope.org/five">

    <five:registerPackage package="." initialize=".initialize" />

</configure>

In secondo luogo è importante capire che i pacchetti fuori dal namespace Products non sono rilevati automaticamente all'avvio di Zope. Se contengono file configure.zcml (come faranno la maggior parte dei pacchetti), questi dovranno essere esplicitamente inclusi in qualche modo. Ciò potrebbe essere fatto tramite:

  • il file configure.zcml di un altro pacchetto;
  • site.zcml di Zope, la radice di tutti i file ZCML, che si trova nella cartella etc nella instance home;
  • uno "slug" ZCML, un file monoriga creato nella cartella etc/package-includes dell'istanza Zope, con un nome del tipo my.package-configure.zcml.

In ogni caso, la sintassi è la stessa:

<include package="my.package" file="configure.zcml" />

Se hai i file meta.zcml o overrides.zcml, puoi aggiungere direttive <include /> per tali pacchetti, ottenendo lo stesso effetto. Se usi gli slug, devono essere nominati allo stesso modo, ad esempio my.package-meta.zcml o my.package-overrides.zcml. Uno slug non può contenere più di una linea.

Più avanti in questa guida mostreremo come buildout può gestire gli slugs per noi automaticamente.

 

Prerequisiti

Alcune cose di cui hai bisogno prima di iniziare

Prima di poter creare un buildout per gestire Zope e Plone, ci sono alcuni prerequisiti di cui preoccuparsi.

Primo, avrai bisogno di un interprete Python adeguato, se non ne hai già uno:

  • Installa Python 2.4 per la tua piattaforma, e aggiungilo alla PATH del tuo sistema. La cosa più semplice si ottiene se Python 2.4 è quello che ottieni scrivendo in una linea di comando python -V.
  • Se hai installato Python da un pacchetto binario (ad esempio un RPM), fai in modo di utilizzare un pacchetto di sviluppo (ad esempio python-devel). Questo include i file header Python che useremo in seguito per compilare Zope. Se hai installato da sorgente, o hai usato il Windows installer di Python, dovresti già averli al loro posto.
  • Installa PIL, Python Imaging Library per questo interprete interprete Python.

Da ora in avanti fai caso al prompt: $ indica che il comando può essere effettuato da un qualsiasi utente, mentre # indica che deve essere eseguito da root (puoi anche utilizzare sudo).

Inoltre tieni presente che è necessario utilizzare python 2.4 mentre nelle distribuzioni attuali il comando "python" corrisponde ad un interprete di una versione successiva (ad esempio 2.6). Per evitare questo problema puoi utilizzare python2.4  invece di python.

Quindi scarica ez_setup.py e mandalo in esecuzione:

# python ez_setup.py

Questo scaricherà e installerà setuptools e lo script easy_install. Osserva l'output dello script per capire dove viene installato easy_install. Se non si trova nel tuo PATH di sistema, dovresti aggiungere anche questa cartella alla path.

Infine, usa easy_install per ottenere ZopeSkel, una collezione di template skeleton per lo sviluppo Zope e Plone (attenzione! se hai installato easy_install in più versioni di python è meglio specificare la versione utilizzando easy_install-2.4):

# easy_install -U ZopeSkel

In questo modo otterrai lo script Paste e varie altre dipendenze. Se hai aggiunto alla tua path di sistema la cartella degli script Python da console (dove è stato collocato easy_install), ora dovresti essere in grado di eseguire il comando paster. Puoi verificarlo con:

$ paster create --list-templates
Available templates:
  basic_namespace:          A project with a namespace package
  basic_package:            A basic setuptools-enabled package
  basic_zope:               A Zope project
  nested_namespace:         A project with two nested namespaces.
  plone:                    A Plone project
  plone2.5_theme:           A Theme for Plone 2.5
  plone2_theme:             A Theme for Plone 2.1 & Plone 2.5
  plone3_buildout:          A buildout for Plone 3 projects
  plone3_theme:             A Theme for Plone 3.0
  plone_app:                A Plone App project

Il tuo output potrebbe differire leggermente, ma assicurati di avere almeno i template plone3_buildout e plone.

Passi di installazione aggiuntivi per Windows

Se stai usando Windows, ci sono acnora alcune cose che devi fare.

Primo, procurati e installa le estension Python Win3 per Python 2.4.

Se vuoi compilare Zope da solo, piuttosto che usare un pacchetto di installazione binario, o se avrai mai bisogno di compilare un egg con estensioni C, avrai bisogno del compilatore mingw32. Assicurati di selezionare almeno i moduli "base" e "make"  quando la procedura di installazione te lo chiederà. Di base, si installa in C:\MingW32. Nella cartella di installazione ci sarà una cartella bin, ad esempio C:\MingW32\bin. Aggiungila al tuo PATH di sistema.

Infine devi configurare il pacchetto distutils di Python per usare il compilatore mingw32. Crea un file chiamato distutils.cfg nella cartella C:\Python24\Lib\distutils (presumendo che Python sia installato in C:\Python24, come dovrebbe per default). Modificalo con Notepad, e aggiungi questo:

[build]
compiler=mingw32

Creare un buildout per il tuo progetto

Come creare un nuovo buildout per un progetto, aggiungendo Plone e altri prodotti estensione come dipendenze.

Ora siamo pronti per creare un nuovo buildout. Il "buildout" è una cartella contenente tutte le parti che realizzano un progetto, inclusa una instanza Zope, i sorgenti Plone, opzioni di configurazione personalizzate e il codice sorgente del tuo progetto. Creane uno in questo modo:

$ paster create -t plone3_buildout myproject

Questo ti farà una serie di domande. Se vuoi usare una installazione esistente di Zope piuttosto che lasciare che buildout ne scarichi e compili una per te, specifica una path assoluta come zope2_install. Analogamente, se non vuoi che buildout scarichi i prodotti del nucleo di Plone, puoi farlo puntare ad una cartella esistente che contenga tutti i prodotti necessari (scaricherà ugualmente le egg di Plone 3, ma, come vedremo in seguito, è possibile condividere una cartella di egg tra vari buildout). Dovrai inserire username and password per un Amministratore Zope, e potresti voler attivare debug mode e verbose security durante lo sviluppo.

Ora spostati nella cartella myproject appena creata, ed esegui lo script di bootstrap di buildout:

$ cd myproject
$ python bootstrap.py

Questo genererà una serie di cartelle e script e scaricherà l'ultima versione dell'egg di zc.buildout. Tutto ciò dovrebbe essere necessario solo una prima volta.

Per partire direttamente esegui:

$ ./bin/buildout

Questo legge il file buildout.cfg generato ed esegue le sue varie "parti", predisponendo Zope, creando una istanza Zope, scaricando ed installando Plone. A breve spiegheremo in maggior dettaglio questo file.

Dovrai rieseguire ./bin/buildout ogni volta che modificherai buildout.cfg. Se non vuoi che buildout vada online a cercare versioni aggiornate degli egg o che scarichi altri archivi, puoi eseguirlo in modalità offline con:

$ ./bin/buildout -No

Per avviare Zope, esegui:

$ ./bin/instance fg

Lo script instance è analogo a zopectl che trovi nell'istanza Zope standard. Puoi usare ./bin/instance start per eseguire Zope in modalità servizio. Può anche essere usato per eseguire i test:

$ ./bin/instance test -s plone.portlets

Cartelle nel buildout

Prima di immergerci nel buildout.cfg, diamo un veloce sguardo alle cartelle che buildout ha creato per noi:

bin/
Contiene vari eseguibili, inclusi il comando buildout, e lo script di controllo dell'istanza Zope.
eggs/
Contiene egg che buildout ha scaricato. Queste saranno attivate esplicitamente dallo script di controllo nella cartella bin/ .
downloads/
Contiene download non-egg, come l'archivio del codice sorgente di Zope.
var/
Contiene i file di log (in var/log/) e il file di storage dei dati dello ZODB (in var/filestorage/Data.fs). Buildout non li sovrascriverà mai.
src/
Inizialmente vuoto. Puoi mettere qui le tue egg di sviluppo e referenziarle in buildout.cfg. Altro su questo più avanti.
products/
Analoga alla cartella Products/ di una istanza Zope (nota la differenza nella lettera maiuscola). Se stai sviluppando un qualsiasi prodotto nel vecchio stile Zope 2, sistemalo qui. Vedremo come buildout può automaticamente scaricare e gestire archivi di prodotti, ma se vuoi estrarre la dipendenza di un prodotto manualmente, o da Subversione, questo è il posto per farlo.
parts/
Contiene codice e dati gestiti da buildout. Nel nostro caso includerà l'installazione locale di Zope, una istanza Zope gestita da buildout, e il codice sorgente di Plone. In generale non dovresti modificare nulla in questa cartella, perchè buildout potrebbe sovrascrivere i tuoi cambiamenti.

Puoi effettuare il check in di una cartella buildout verso un repository di codice sorgente per condividerlo tra gli sviluppatori. In questo caso, dovresti ignorare le cartelle bin/, eggs/, downloads/, var/, and parts/. Ogni sviluppatore può eseguire bootstrap.py per riaverle di nuovo, e normalmente avrà bisogno di copie locali in ogni caso. Tutte le tue configurazioni dovrebbero stare nel file buildout.cfg, e tutto il codice personalizzato in src/ o products/.

Capire buildout.cfg

Come gestire il file di configurazione principale di buildout

buildout.cfg è il file più importante nel tuo nuovo ambiente buildout. Ecco come appare:

[buildout]
parts =
    plone
    zope2
    productdistros
    instance
    zopepy

# Aggiungi sorgenti di scaricamente egg addizionali qui. dist.plone.org contiene gli archivi 
# dei pacchetti Plone.
find-links =
    http://dist.plone.org
    http://download.zope.org/ppix/
    http://download.zope.org/distribution/
    http://effbot.org/downloads

# Aggiungi egg addizionali qui
# elementtree è richiesto da Plone
eggs =
    elementtree
    
# Referenzia qualsiasi egg che stai sviluppando qui, una per linea
# esempio: develop = src/my.package
develop =

[plone]
recipe = plone.recipe.plone

[zope2]
recipe = plone.recipe.zope2install
url = ${plone:zope2-url}

# Usa questa sezione per scaricare prodotti vecchio stile aggiuntivi.
# Elenca tutte le URL necessarie per i tarballs dei prodotti sotto urls
# (separate da uno spazio bianco, o su più linee, con le linee successive
# indentate). Se un archivio contiene molti prodotti all'interno di una
# cartella di primo livello, elenca il filename dell'archivio (cioè
# l'ultima parte della URL, normalmente con un suffisso .tar.gz o simile)
# sotto 'nested-packages'.
# Se un archivio si estrae in una cartella product con un suffisso di versione,
# elenca il nome dell'archivio sotto 'version-suffix-packages'.
[productdistros]
recipe = plone.recipe.distros
urls =
nested-packages =
version-suffix-packages = 

[instance]
recipe = plone.recipe.zope2instance
zope2-location = ${zope2:location}
user = admin:admin
http-address = 8080
debug-mode = on
verbose-security = on

# Se vuoi che Zope conosca un qualsiasi egg aggiuntivo, elencalo qui.
# questo dovrebbe includere qualsiasi egg di sviluppo che hai elencato
# sopra in develop-eggs, ad es. eggs = ${buildout:eggs} ${plone:eggs} my.package
eggs =
    ${buildout:eggs}
    ${plone:eggs}

# Se vuoi registrare slug ZCML per un qualsiasi pacchetto, elencalo qui.
# ad es. zcml = my.package my.other.package
zcml = 

products =
    ${buildout:directory}/products
    ${productdistros:location}
    ${plone:products}

[zopepy]
recipe = zc.recipe.egg
eggs = ${instance:eggs}
interpreter = zopepy
extra-paths = ${zope2:location}/lib/python
scripts = zopepy

Ispezioniamo questo file passo passo:

La sezione principale [buildout]

La sezione [buildout] è il punto di inizio del file. Elenca un numero di "parti" che sono configurate in sezioni separate successivamente nel file. Ogni parte ha una ricetta associata, la quale rappresenta il nome di un egg che sa come portare a termine uno specifico compito, ad esempio compilare Zope o creare una istanza Zope. Una ricetta tipicamente accetta alcune opzioni di configurazione.

Le nostre impostazioni globali sono le seguenti:

[buildout]
parts =
    plone
    zope2
    productdistros
    instance
    zopepy

find-links =
    http://dist.plone.org
    http://download.zope.org/ppix/
    http://download.zope.org/distribution/
    http://effbot.org/downloads

eggs =
    elementtree
    
develop =

Questo specifica che le parti plone, zope2, productdistros, instance and zopepy saranno eseguite in quell'ordine. Quindi diciamo a buildout che può cercare in una di una serie di URL quando deve scaricare delle egg. Inoltre cercherà sempre e comunque nel Cheese Shop.

Dopo possiamo elencare qualsiasi egg che buildout dovrebbe scaricare e installare per noi. Questo potrebbe includere la richiesta di specifiche versioni. Ad esempio, se vuoi sqlalchemy 0.3,  ma non 0.4, potresti elencare:

eggs = 
    elementtree
    sqlalchemy>=0.3,<0.4dev

Infine possiamo elencare gli egg di sviluppo, specificando una cartella dove gli egg saranno estratti in formato sorgente. Ad esempio:

eggs =
    elementtree
    my.package

develop = 
    src/my.package

Questo presuppone che esista un egg chiamato my.package nella cartella src/ . Impareremo a costruire simili egg fra un po' nel corso della guida. Nota come dobbiamo anche elencare my.package come una vera egg dipendente: le egg di sviluppo non sono automaticamente aggiunte al "working set" di egg che vengono installate per Zope.

La sezione [plone]

Questa è molto semplice - usa solo plone.recipe.plone per scaricare prodotti ed egg di Plone.

[plone]
recipe = plone.recipe.plone

Utilizzerà l'ultima release disponibile. I numeri di versione per plone.recipe.plone corrispondono ai numeri di versione di Plone stesso. Quindi per essere certo di ottenere sempre una release 3.0.x, ma non una 3.1, puoi fare così:

[plone]
recipe = plone.recipe.plone>=3.0,<3.1dev

Quando la ricetta viene eseguita, i prodotti di Plone saranno installati in parts/plone. Le egg sono rese disponibili mediante la variabile  buildout ${plone:eggs}, che referenzieremo nella sezione [instance] più avanti, e la URL di una versione nota come "buona" di Zope è disponibile nella variabile ${plone:zope2-url}.

La sezione [zope2]

Questa parte costruisce Zope 2, usando plone.recipe.zope2install. Se hai specificato una istallazione Zope esistente, non avrai questa parte. Altrimenti ha questo aspetto:

[zope2]
recipe = plone.recipe.zope2install
url = ${plone:zope2-url}

Qui referenziamo l'URL da cui scaricare Zope come specificato dalla parte [plone] . Questo assicura di ottenere comunque la versione raccomandata di Zope. Potresti specificare manualmente una URL di download, se volessi usare una versione diversa di Zope.

Quando la ricetta viene eseguita, Zope 2 viene installato in parts/zope2. La software home di Zope diventa parts/zope2/lib/python.

La sezione [productdistros]

Questa usa la ricetta plone.recipe.distros , che riesce a scaricare distribuzioni (archivi) di prodotti stile Zope 2 e li rende disponibili in Zope. Per cominciare è vuota:

[productdistros]
recipe = plone.recipe.distros
urls =
nested-packages =
version-suffix-packages =

In ogni caso puoi elencare qualsiasi numero di download. La ricetta è anche in grado di gestire archivi con una singola cartella di primo livello contenente un bundle di vere cartelle prodotto (nested-packages), o pacchetti che hanno un numero di versione nel nome della cartella e devono perciò essere rinominati per avere la vera cartella prodotto (version-suffix-packages).

Ad esempio, ecco come potresti scaricare CacheFu 1.1:

[productdistros]
recipe = plone.recipe.distros
urls =
    http://plone.org/products/cachefu/releases/1.1/CacheFu-1.1.tgz
nested-packages =
    CacheFu-1.1.tgz
version-suffix-packages = 

Puoi specificare download multipli usando diverse righe. Quando la ricetta viene eseguita, le cartelle prodotto dei prodotti scaricati si trovano in parts/productdistros.

La sezione [instance]

La sezione instance collega il tutto: configura un'istanza Zope usando lo script plone.recipe.zope2instance . Ecco come appare:

[instance]
recipe = plone.recipe.zope2instance
zope2-location = ${zope2:location}
user = admin:admin
http-address = 8080
debug-mode = on
verbose-security = on
eggs =
    ${buildout:eggs}
    ${plone:eggs}
zcml = 
products =
    ${buildout:directory}/products
    ${productdistros:location}
    ${plone:products}

Qui referenziamo l'installazione Zope 2 dalla parte [zope2]: se hai specificato una posizione tu stesso quando hai creato il buildout, vedrai quella in tal caso. Quindi specifichiamo user e password dell'admin iniziale, e la porta a cui risponderà Zope. Attiviamo anche il debug mode e verbose security. Queste opzioni sono usate per generare un file zope.conf appropriato per questa istanza. Vedi la pagina della ricetta nel Cheese Shop per ulteriori dettagli sulle opzioni disponibili.

Successivamente specifichiamo quali egg saranno rese disponibili a Zope. Questo referenzia le egg "globali" dalla sezione [buildout], come pure le egg specificate da Plone. Potresti aggiungere egg ulteriori qui, sebbene risulta generalmente più semplice specificarle all'inizio del file, in modo che vengano incluse nel working set ${buildout:eggs}.

Come spiegato in precedenza, i file configure.zcml di Zope 3 non sono caricati automaticamente per le egg o i pacchetti che non si trovano nel namespace Products. Per caricare i file ZCML per un pacchetto regolare, possiamo far creare a buildout uno slug ZCML elencando il pacchetto sotto l'opzione zcml:

zcml =
    my.package
    my.package-overrides

SI presuppone che my.package fosse precedentemente referenziato nel buildout. Questo caricherebbe sia il configure.zcml principale che il file overrides.zcml da questo pacchetto.

Infine elenchiamo le varie cartelle che contengono prodotti in stile Zope 2, affine alla cartella Products/ in una istanza tradizionale. Nota come la cartella products/ nella cartella principale di buildout viene prima di tutto, seguita dai prodotti scaricati con la parte [productdistros], seguita dai prodotti scaricati dalla parte [plone]. Questo significa che se anche Plone viene distribuito con un prodotto, potresti fare un override (ad es. con un prodotto più aggiornato) mettendo un prodotto con lo stesso nome nella cartella products/ di primo livello.

Quando la ricetta viene eseguita, l'istance home di Zope sarà parts/instance, e uno script di controllo verrà generato in ./bin/instance.

La sezione [zopepy]

Questa sezione finale crea un interprete Python che dispone di tutte le egg e pacchetti (ma non dei prodotti in stile Zope 2) di cui Zope disporrà quando in esecuzione. Questo può essere utile per effettuare del testing.

[zopepy]
recipe = zc.recipe.egg
eggs = ${instance:eggs}
interpreter = zopepy
extra-paths = ${zope2:location}/lib/python
scripts = zopepy

Qui copiamo le egg dalla sezione [instance], e includiamo nella pythonpath la instance home di Zope.

Quando la ricetta viene eseguita lo script sarà generato in ./bin/zopepy.

Creare un file buildout di default

Questo permette di condividere configurazioni tra vari buildout, e fa risparmiare del tempo e dello spazio disco.

Per impostare delle opzioni "globali" che interessino tutti i buildout, crea una cartella .buildout (nota il punto iniziale) nella tua cartella  home, e aggiungi lì un file chiamato defaults.cfg. Qualsiasi gruppo di opzioni qui presente sarà applicato alla corrispondente sezione di qualsiasi buildout.cfg che esegui, a meno che non prevalga una opzione più specifica nel file buildout.cfg stesso.

Le opzioni più comuni sono:

python
Specifica un interprete Python diverso da quello di default del sistema. Questo è utile se, ad esempio, hai Python 2.5 installato ma vuoi che i tuoi buildout usino l'installazione di Python 2.4.
eggs-directory
Specifica una cartella dove gli egg saranno scaricati. Questo permette a diversi buildout di condividere gli stessi egg, risparmiando spazio disco e tempo di download. Nota che solo gli egg esplicitamente richiesti dallo specifico buildout saranno attivati. La cartella degli egg potrebbe contenerne molti di più (o molte versioni diverse dello stesso pacchetto) di quelli usati da un qualsiasi buildout.
download-directory
Specifica una cartella condivisa per gli archivi scaricati. Di nuovo, questo permette di risparmiare spazio disco e tempo di scaricamento.

Ecco un esempio di ~/.buildout/defaults.cfg che imposta le tre opzioni:

[buildout]
python = /opt/python24/bin/python
eggs-directory = ~/.buildout/eggs
download-directory = ~/.buildout/downloads

Questo presuppone che Python 2.4 sia installato in /opt/python2.4. Per far funzionare le ultime due opzioni, dovrai creare le cartelle eggs e downloads all'interno della cartella ~/.buildout .

Installare un prodotto estensione

Come installare un nuovo pacchetto usando questi strumenti

L'installazione di un nuovo prodotto estensione dipenderà dal fatto che sia distribuito come un egg o come un prodotto Zope 2 classico.

Installare un prodotto Zope 2 classico

Il modo più semplice per sperimentare un prodotto Zope 2 classico consiste nell'estrarlo all'interno della cartella products/ del buildout. Se leggi la documentazione relativa alla cartella Products/ di una istanza Zope, questa è la stessa cosa.

Tuttavia questo approccio rende più difficile ridistribuire il tuo progetto e condividerlo con altri sviluppatori. Spesso è più facile mantenere il controllo se si lascia che buildout scarichi e installi il pacchetto per te. Puoi farlo con la sezione [productdistros] di buildout.cfg. Ad esempio, ecco come potresti installare DocFinderTab e CacheFu per il tuo progetto:

[productdistros]
recipe = plone.recipe.distros
urls =
    http://www.zope.org/Members/shh/DocFinderTab/1.0.2/DocFinderTab-1.0.2.tar.gz
    http://plone.org/products/cachefu/releases/1.1/CacheFu-1.1.tgz
nested-packages =
    CacheFu-1.1.tgz
version-suffix-packages =

Nota che CacheFu viene distribuito come una singola cartella contenente un numero di prodotti in sotto-cartelle, perciò lo elenchiamo sotto nested-packages.

Come sempre, se modifichi buildout.cfg, devi ri-eseguire buildout:

$ ./bin/buildout

Installare un egg

Finchè un egg ha una release in Cheese Shop o altrove, buildout può scaricarlo e installarlo, inclusa qualsiasi dipendenza esplicitamente specificata. Basta elencare l'egg, e opzionalmente una versione (altrimenti otterrai l'ultima disponibile), nell'opzione eggs.

[buildout]
...
eggs = 
    elementtree
    borg.project>=1.0b1,<2.0dev

Se vuoi che buildout utilizzi un indice ulteriore rispetto a quello di Cheese Shop, puoi aggiungere una URL a find-links che contiene i link di download per gli egg. In effetti abbiamo già visto un esempio di questo: elementtree si trova in http://effbot.org/downloads, non direttamente nel Cheese Shop. Quindi dovremo specificare:

[buildout]
...

find-links =
    http://dist.plone.org
    http://download.zope.org/ppix/
    http://download.zope.org/distribution/
    http://effbot.org/downloads

eggs =
    elementtree

Abbiamo anche elencato alcune delle locazioni di download per egg di Zope e Plone.

Riesegui ancora buildout per fare in modo che i cambiamenti abbiano effetto:

$ ./bin/buildout

Egg di sviluppo

Se non esiste una release per il tuo egg, o vuoi tracciare un egg in Subversion, estrailo nella cartella src/ . Fai in modo di ottenere l'egg completo, incluso il file di primo livello setup.py . Ad esempio, per ottenere il trunk di sviluppo di plone.portlets, esegui:

$ cd src
$ svn co https://svn.plone.org/svn/plone/plone.portlets/trunk plone.portlets

Quindi aggiungi quanto segue a buildout.cfg:

[buildout]
...
eggs =
    ...
    plone.portlets

develop =
    src/plone.portlets

Nota che:

  • L'opzione develop contiene un path relativo verso il punto in cui l'egg sorgente è installato. Buildout si aspetta di trovare un setup.py coerente in questa cartella.
  • Gli egg di sviluppo hanno sempre precedenza sugli egg normali.
  • Dovrai ancora elencare il nome dell'egg nell'opzione eggs per fare in modo che venga installato.
  • Se stai facendo override di un egg che viene distribuito con Plone, potresti doverlo elencare nella sezione eggs della parte [plone] piuttosto:
[buildout]
...
develop =
    src/plone.portlets

...

[plone]
recipe = plone.recipe.plone
eggs = 
    plone.portlets

Questo perchè plone.recipe.plone è molto esplicito riguardo a quali versioni dei suoi vari egg devono essere usate, per fare in modo che  Plone venga eseguito nel modo in cui è stato rilasciato.

Le ricette Buildout (come ad esempio plone.recipe.plone) sono distribuite sotto forma di egg. Puoi usare un egg di sviluppo di una ricetta elencandolo sotto l'opzione develop. Non c'e' bisogno di elencarlo esplicitamente sotto l'opzione eggs, dato che è referenziato dall'opzione recipe della parte rilevante.

Gestire file ZCML

E' importante comprendere che Zope non caricherà automaticamente file configure.zcml per i pacchetti che non si trovano nel namespace Products.*. Piuttosto devi referenziare esplicitamente il pacchetto. Buildout può creare tali referenze (note come slug ZCML) con l'opzione zcml sotto la parte [instance]. Ecco come assicurarsi che borg.project sia disponibile in Zope:

[buildout]
...
eggs =
    elementtree
    borg.project

...

[instance]
...
zcml = 
    borg.project

Dovessi avere bisogno di caricare un overrides.zcml o un meta.zcml, puoi usare una sintassi come la seguente:

zcml =
    some.package
    some.package-overrides
    some.package-meta

Prodotto Policy

Molti sviluppatori preferiscono creare un singolo "prodotto policy" (noto anche come "prodotto deployment") che orchestra le varie dipendenze. Se hai un tale prodotto potresti voler includere varie dipendenze direttamente dal file configure.zcml del prodotto policy,  con righe simili alle seguenti:

<configure xmlns="http://namespace.zope.org/zope">

    <include package="borg.project" />

</configure>

In questo caso potresti anche aver bisogno di uno slug (usando l'opzione zcml come sopra descritto) per il prodotto policy.

Creare un nuovo pacchetto

Come aggiungere un nuovo pacchetto basato su egg al tuo buildout

Aggiungere un nuovo pacchetto personalizzato non è molto diverso dall'installare un pacchetto estensione.

Creare un prodotto Zope 2 classico

Per creare un prodotto Zope 2 classico, collocalo nella cartella products/ di primo livello e riavvia Zope. Non dovrebbe essere necessario fare altro. Come precedentemente illustrato, i prodotti sistemati qui saranno automaticamente trovati allo start-up, e i loro file configure.zcml saranno eseguiti automaticamente.

Creare un egg

Chiaramente, se stai usando dei prodotti, non potrai beneficiare delle funzioni aggiuntive degli egg, inclusa la gestione automatica delle dipendenze, la distribuzione via Cheese Shop e i nested namespace.

Il modo più semplice per creare un nuovo egg è di usare il comando paster, che abbiamo già utilizzato per creare il buildout. Per creare un nuovo pacchetto di base, con un namespace di primo livello (ad esempio il nome della tua azienda) e un nome specifico, vai nella cartella src/ ed esegui:

$ cd src
$ paster create -t plone myorg.mypackage

Ti verranno fatte una serie di domande. Fai in modo che il pacchetto namespace e il nome del pacchetto corrispondano al nome dell'egg. Nel nostro caso il pacchetto namespace è myorg e il nome del pacchetto è mypackage. In generale, rispondi False alla domanda sul fatto che il tuo pacchetto sia "zip safe". Inserisci gli altri metadati come richiesto.

Ora avrai:

  • Un setup.py che contiene i metadati che hai inserito
  • Un pacchetto in myorg.mypackage/myorg/mypackage. Il tuo codice sorgente va qui.
  • Un modello di partenza per configure.zcml, tests.py e alcuni ulteriori utili punti di partenza.
  • Della documentazione generica in myorg.mypackage/docs.

Chiaramente devi anche aggiungere questo pacchetto al buildout. In buildout.cfg, dovresti avere:

[buildout]
...
eggs =
    ...
    myorg.mypackage

develop =
    src/myorg.mypackage

A meno che non pensi di includere questo pacchetto tramite un altro pacchetto, probabilmente avrai anche bisogno di uno slug ZCML:

[instance]
...
zcml =
    myorg.mypackage

Non dimenticare di rieseguire buildout dopo aver fatto la modifica:

$ ./bin/buildout

Specificare dipendenze

Se il tuo nuovo pacchetto ha delle dipendenze esplicite, puoi elencarle in setup.py. In questo modo buildout potrà scaricarle e installarle insieme al pacchetto. Le dipendenze sono elencate tramite l'argomento install_requires del metodo setup(): di default setuptools viene elencato qui, poichè ne abbiamo bisogno per supportare i pacchetti namespace. Per aggiungere sqlalchemy 0.3 (ma non 0.4), e il driver MySQL-Python, potresti modificarlo in questo modo:

install_requires=[
          'setuptools',
          'sqlalchemy>=0.3,<0.4dev',
          'MySQL-Python',
      ],

Caricare il tuo egg su Cheese Shop

Se vuoi condividere il tuo pacchetto con il resto della comunità Python e renderne semplice l'installazione usando strumenti come buildout e easy_install, puoi caricare il pacchetto su Cheese Shop.

Prima di farlo dovresti:

  • Fare un commit delle tue ultime modifiche e taggare la versione in Subversion, se possibile.
  • Rimuovere (temporaneamente) il file setup.cfg: questo rende il tuo pacchetto una release di sviluppo.
  • Fai in modo che il numero di versione in setup.py sia corretto. Questo dovrebbe seguire convenzioni comuni come ad esempio "1.0b2" per la seconda beta della versione 1.0, o "2.1.3rc1" per la prima release candidate della versione 2.1.3.
  • Se usi Mac OS X, esegui export COPY_EXTENDED_ATTRIBUTES_DISABLE=true prima nella shell, altrimenti l'egg conterrà dei fork delle risorse di Mac OS X che causano problemi se verrà usato su Windows.

Quando sei pronto, esegui il seguente comando dalla cartella del tuo pacchetto (cioè src/myorg.mypackage):

$ python setup.py egg_info -RDb "" sdist bdist_egg register upload

Questo ti chiederà di creare un account su Cheese Shop se non ne hai già uno. Puoi eseguire questo comando tutte le volte che vorrai rilasciare una nuova versione (probabilmente con un nuovo numero di versione).

Una configurazione di produzione

Come usare buildout per configurare ambienti di produzione

Per finire diamo uno sguardo ad una configurazione più avanzata, adeguata ad un ambiente di produzione. Salva questo file come deployment.cfg nella root del buildout vicino al file principale buildout.cfg:

[buildout]
extends =
    buildout.cfg

parts =
    plone
    zope2
    productdistros
    deploymentproducts
    zeoserver
    primary
    secondary
    varnish-build
    varnish-instance

[deploymentproducts]
recipe = plone.recipe.distros
urls =
    http://plone.org/products/cachefu/releases/1.1/CacheFu-1.1.tgz
nested-packages =
    CacheFu-1.1.tgz
version-suffix-packages = 

[zeoserver]
recipe = plone.recipe.zope2zeoserver
zope2-location = ${zope2:location}
zeo-address = 8100

[primary]
recipe = plone.recipe.zope2instance
zope2-location = ${zope2:location}
zeo-client = true
zeo-address = 8100
zodb-cache-size = 5000
zeo-client-cache-size = 300MB
user = admin:admin
http-address = 8080
debug-mode = off
verbose-security = off
eggs =
    ${plone:eggs}
    ${buildout:eggs}
zcml = 
    optilux.policy
products =
    ${buildout:directory}/products
    ${productdistros:location}
    ${deploymentproducts:location}
    ${plone:products}

[secondary]
recipe = plone.recipe.zope2instance
zope2-location = ${zope2:location}
zeo-client = true
zeo-address = 8100
zodb-cache-size = 5000
zeo-client-cache-size = 300MB
user = ${primary:user}
http-address = 8081
debug-mode = on
verbose-security = on
eggs = ${primary:eggs}
zcml = ${primary:zcml}
products = ${primary:products}
zope-conf-additional =
    zserver-threads 1
    
[varnish-build]
recipe = plone.recipe.varnish:build
url = http://puzzle.dl.sourceforge.net/sourceforge/varnish/varnish-1.1.1.tar.gz

[varnish-instance]
recipe = plone.recipe.varnish:instance
bind = 127.0.0.1:8081
backends = 127.0.0.1:8080
cache-size = 1G

Eccoci qua:

  • Stiamo referenziando il file principale buildout.cfg, estendendolo e facendo override con una configurazione più appropriata ad un ambiente di produzione.
  • Stiamo predisponendo uno ZEO server con due istanze client, primary e secondary (vedi plone.recipe.zope2zeoserver e plone.recipe.zope2instance per maggiori dettagli)
  • Stiamo compilando il cache server Varnish (vedi plone.recipe.varnish per ulteriori dettagli).

Combinando i file di configurazione di buildout in questo modo, potrai creare configurazioni mirate per diversi scenari di produzione. Per imparare di più riguardo le funzioni avanzate di buildout studia la sua documentazione.

Per costruire questo ambiente devi esplicitamente specificare un file di configurazione:

$ ./bin/buildout -c deployment.cfg

Per avviare Zope e Plone, dovrai avviare il server ZEO, i due client (o almeno quello primario), e il server Varnish:

$ ./bin/zeoserver start
$ ./bin/primary start
$ ./bin/secondary start
$ ./bin/varnish-instance

Le ricette creano anche gli script per effettuare il backup dello ZODB (in ./bin/repozo) e per il pack del database (in ./bin/zeopack).

Opzioni ulteriori

zc.buildout è un sistema molto flessibile. Risulta piuttosto semplice creare nuove ricette, e potrai combinare ricette esistenti in modo molto potente. Cerca "buildout" nel Cheese Shop per trovare altre ricette, o dai uno sguardo al codice sorgente per alcune delle ricette di Plone per capire come vengono generate.