Distribuire applicazioni Python

Una volta che finiamo con lo sviluppo della nostra nuova applicazione si dovrebbe impacchettare per facilitare agli utenti l’installazione e distribuzione per noi.
In Python ci sono due moduli principali per questo scopo: distutils, che fa parte della libreria standard ed è stato il metodo più utilizzato fino a poco tempo, e setuptools, che estende le funzionalità della distutils ed è sempre più popolare.
In questo capitolo si esamina le prestazioni di entrambi gli strumenti, e finisce descrivendo come creare il file eseguibile .exe per Windows dal nostro programma in Python.
distutils
Qualsiasi programma distribuito con distutils contiene uno script denominato setup.py per convenzione, che installerà l’applicazione chiamando la funzione di setup di distutils.core. Questa funzione ha un sacco di argomenti, che controllano, tra le altre cose, come installare l’applicazione.
Destinati a descrivere l’applicazione abbiamo i seguenti argomenti

name: Il nome del pacchetto.
version: Il numero di versione.
description: Una linea che descrive il pacchetto.
long_description: Descrizione completa del pacchetto.
author: Nome dell’autore dell’applicazione.
author_email: Email dell’autore.
maintainer: Nome della persona responsabile della gestione del pacchetto, se diverso dall’autore.
maintainer_email: Email della persona responsabile della gestione del pacchetto, se diverso dall’autore.
url: Web dell’applicazione.
download_url: Url per scaricare l’applicazione.
license: Licenza dell’applicazione.

Abbiamo anche argomenti che controllano i files e le directories da installare, come packages, py_modules, script ed ext_modules.
Il parametro script, che è un elenco di stringhe, indica il nome del modulo o i moduli principali, cioè quelli eseguiti dall’utente. Se la nostra applicazione consiste, ad esempio, in uno script ejemplo.py, il codice di setup.py potrebbe essere simile a quanto segue:

			from distutils.core import setup
			    setup(name="Esempio di applicazione",
			    version="0.1",
			    description="Esempio di funzionamento di distutils",
			    author="Mio Nome",
			    author_email="mioemail@email.it",
			    url="http://miosito.net/tutoriale-python/",
			    license="GPL",
			    scripts=["esempio.py"]
			)
Se abbiamo scritto altri moduli da utilizzare dallo script principale, questi saranno indicati dal parametro py_modules. Ad esempio, supponiamo che l’applicazione è costituito da uno script principale esempio.py e un modulo di supporto supporto.py:
			from distutils.core import setup
			
			setup(name="Esempio di applicazione",
			    version="0.1",
			    description="Esempio di funzionamento di distutils",
			    author="Mio Nome",
			    author_email="mioemail@email.it",
			    url="http://miosito.net/tutoriale-python/",
			    license="GPL",
			    scripts=["esempio.py"],
			    py_modules=["supporto"]
			)
Per installare i pacchetti Python (directory contenenti diversi moduli e un file __init__.py) useremo il parametro packages. Se poi oltre il modulo esempio.py vogliamo installare i pacchetti e bbdd, per esempio, procederemo come segue:
			from distutils.core import setup
			
			setup(name="Esempio di applicazione",
			    version="0.1",
			    description="Esempio di funzionamento di distutils",
			    author="Mio Nome",
			    author_email="mioemail@email.it",
			    url="http://miosito.net/tutoriale-python/",
			    license="GPL",
			    scripts=["esempio.py"],
			    packages=["gui", "bbdd"]
			)
ext_modules, infine, serve a includere le estensioni che utilizza il programma in C, C++, Fortran, …
Vediamo come si usa il file setup.py che è stato creato.
Quando si esegue il comando
			python setup.py install
i moduli e i pacchetti specificati da py_modules e packages si installano nella cartella Lib di Python. I programmi indicati da script vengono copiati nella directory Script di Python.
Una volta che abbiamo verificato che l’applicazione sia installata correttamente, procederemo a creare i files da distribuire agli utenti. Per creare files con il codice sorgente si utilizza l’opzione sdist di setup.py, che di default crea un file tar.gz su Unix o un file .zip su Windows.
			python setup.py sdist
Tuttavia è possibile utilizzare --formats per specificare il formato o i formati che si desidera generare.

bztar .tar.bz2
gztar .tar.gz
tar .tar
zip .zip
ztar .tar.Z

Per creare un file tar.bz2, tar.gz e zip, per esempio, utilizzeremo il seguente comando:

			python setup.py sdist --formats=bztar,gztar,zip
Per generare una distribuzione binaria, si utilizza l’opzione bdist:
[expandsub expanded1=”true” trigclass=”noarrow”]
			python setup.py bdist
[/expandsub1] I formati supportati da bdist sono:

rpm RPM
gztar .tar.gz
bztar .tar.bz2
ztar .tar.Z
tar .tar
wininst Windows Installer
zip .zip

Per creare un file rpm e un programma di installazione di Windows, ad esempio, scrivere:

			python setup.py bdist --formats=wininst,rpm
È inoltre possibile creare altri tipi di file di distribuzione utilizzando scripts che estendono distutils, come è il caso dei pacchetti deb attraverso lo script stdeb (http://stdeb.python-hosting.com/)

setuptools
setuptools estende distutils aggiungendo una serie di funzionalità interessanti: introduce un nuovo formato di file per la distribuzione di applicazioni Python chiamato egg, ch’è responsabile di trovare tutti i pacchetti da installare e aggiungere le possibili dipendenze, permette installare pacchetti di PyPI con un singolo comando, ecc.
Inoltre, come setuptools si basa su distutils. Uno script di installazione di base utilizzando setuptools è quasi uguale al suo equivalente con distutils Basta cambiare l’istruzione import.
			from setuptools import setup
			
			setup(name="Esempio di applicazione",
			    version="0.1",
			    description="Esempio di funzionamento di distutils",
			    author="Mio Nome",
			    author_email="mioemail@email.it",
			    url="http://miosito.net/tutoriale-python/",
			    license="GPL",
			    scripts=["esempio.py"]
			)
L’unico inconveniente che possiamo trovare usando setuptools è che non è incluso di default in Python 2.5, ma è destinata a cambiare nelle versioni future dovute al suo grande utilizzo. Ma gli sviluppatori setuptools hanno pensato a tutto, e anche questo non dovrebbe porre alcun problema, perché con il minimo sforzo da parte nostra possiamo fare scaricare setuptools e installarlo automaticamente nel computer dell’utente se non è già nel sistema. Basta distribuire con il nostro pacchetto un piccolo modulo ez_setup.py che viene fornito come standard con setuptools (http://peak.telecommunity.com/dist/ez_setup.py) e chiamare la funzione use_setuptools all’inizio setup.py:
			from ez_setup import use_setuptools
			use_setuptools()
			from setuptools import setup
			
			setup(name="Esempio di applicazione",
			    version="0.1",
			    description="Esempio di funzionamento di distutils",
			    author="Mio Nome",
			    author_email="mioemail@email.it",
			    url="http://miosito.net/tutoriale-python/",
			    license="GPL",
			    scripts=["esempio.py"]
			)
Vediamo ora più da vicino alcuni dei cambiamenti e delle innovazioni introdotte dal setuptools.
Integrazione con PyPI
In stile CPAN di Perl setuptools consente di installare in mdo facile e semplice i pacchetti appartenenti a PyPI, l’indice dei pacchetti Python (http://pypi.python.org/pypi) così come caricare i propri pacchetti.
PyPI conta, al momento di scrivere questo post, con 4782 pacchetti: installare i pacchetti da questo repository con un semplice comando è di grande aiuto e da tenere a mente.
L’installazione di un pacchetto da PyPI è semplice come passare al comando easy_install il nome del pacchetto da installare.
				easy_install docutils
				Searching for docutils
				Reading http://pypi.python.org/simple/docutils/
				Reading http://docutils.sourceforge.net/
				Best match: docutils 0.5
				Downloading http://prdownloads.sourceforge.net/docutils/docutils-0.5.tar.gz?download
				Processing docutils-0.5.tar.gz
				Running docutils-0.5/setup.py -q bdist_egg --dist-dir /tmp/easy_install-wUAyUZ/docutils-0.5/egg-dist-tmp-kWkkkv
				"optparse" module already present; ignoring extras/optparse.py.
				"textwrap" module already present; ignoring extras/textwrap.py.
				zip_safe flag not set; analyzing archive contents…
				docutils.writers.newlatex2e.__init__: module references __file__
				docutils.writers.pep_html.__init__: module references __file__
				docutils.writers.html4css1.__init__: module references __file__
				docutils.writers.s5_html.__init__: module references __file__
				docutils.parsers.rst.directives.misc: module references __file__
				Adding docutils 0.5 to easy-install.pth file
				Installing rst2pseudoxml.py script to /usr/bin
				Installing rst2html.py script to /usr/bin
				Installing rst2latex.py script to /usr/bin
				Installing rst2s5.py script to /usr/bin
				Installing rst2newlatex.py script to /usr/bin
				Installing rstpep2html.py script to /usr/bin
				Installing rst2xml.py script to /usr/bin
				Installed /usr/lib/python2.5/site-packages/docutils-0.5-py2.5.egg
				Processing dependencies for docutils
				Finished processing dependencies for docutils
Poter caricare i nostri pacchetti a PyPI richiede un processo un po’ più laborioso. In primo luogo registriamo i dettagli della nostra applicazione PyPI utilizzando l’opzione register dello script setup.py, che chiederà il vostro username, password e indirizzo e-mail se non abbiamo nessun conto in PyPI, o il nome utente e password se abbiamo un account:
				python setup.py register
				running register
				running egg_info
				creating Esempio_di_applicazione.egg-info
				writing Esempio_di_applicazione.egg-info/PKG-INFO
				writing top-level names to Esempio_di_applicazione.egg-info/top_level.txt
				writing dependency_links to Esempio_di_applicazione.egg-info/dependency_links.txt
				writing manifest file ‘Esempio_di_applicazione.egg-info/SOURCES.txt’
				reading manifest file ‘Esempio_di_applicazione.egg-info/SOURCES.txt’
				writing manifest file ‘Esempio_di_applicazione.egg-info/SOURCES.txt’
				We need to know who you are, so please choose either:
				1. use your existing login,
				2. register as a new user,
				3. have the server generate a new password for you (and email it to you), or
				4. quit
				Your selection [default 1]: 1
				Username: mio_account
				Password:
				Server response (200): OK
				I can store your PyPI login so future submissions will be faster.
				(the login will be stored in /home/mio_account/.pypirc)
				Save your login (y/N)?y
Per creare e caricare una distribuzione con il codice sorgente della nostra applicazione viene utilizzata l’opzione sdist upload:
				python setup.py sdist upload
Possiamo anche creare e caricare un egg (un formato di file per la distribuzione di applicazioni Python che vedremo nella prossima sezione) utilizzando l’opzione bdist_egg upload:
				python setup.py bdist_egg upload
O combinare le tre fasi in un unico comando:
				python setup.py register sdist bdist_egg upload
Una volta caricato il pacchetto chiunque potrebbe installarlo utilizzando easy_install sul suo sistema, come qualsiasi altro pacchetto PyPI:
				easy_install mi-paquete
Eggs
Gli eggs (uovo in inglese) sono file con l’estensione .egg attraverso cui distribuire le applicazioni Python. Sarebbe qualcosa come l’equivalente .jar nel mondo Java. Sono multipiattaforma (cross-platform), consentono di gestire le dipendenze e permettono di installare diverse versioni dello stesso pacchetto.
Il modo più semplice per installare le applicazioni distribuite come files .egg è attraverso il comando easy_install, discusso brevemente nel punto precedente nella descrizione sul loro uso nell’installare i pacchetti di PyPI. Per installare un file .egg basta passare il nome del file al comando easy_install:
				easy_install mi-aplicacion.egg
oppure possiamo passare l’URL per scaricare il file egg:
				easy_install http://mundogeek.net/mi-aplicacion.egg
Per costruire le nostre proprie eggs possiamo usare il comando bdist_egg di setup.py, simile al modo in cui abbiamo costruito pacchetti RPM o gli installatori per Windows con distutils:
				python setup.py bdist_egg
Altri notevoli cambiamenti
Uno dei cambiamenti più interessanti è l’aggiunta di un nuovo argomento alla funzione di setup chiamato install_requires, che consiste di una stringa o una lista di stringhe che indicano i pacchetti di cui dipendono l’applicazione. Se l’applicazione avrà bisogno del pacchetto di supporto per poter eseguirsi, per esempio, si dovrebbe scrivere quanto segue:
				install_requires = ["supporto"]
E così, easy_install si occuperebbe di trovare e installare il pacchetto, se necessario, sia in PyPI, o qualsiasi altro repository indicato dal parametro dependency_links.
Possiamo anche specificare che abbiamo bisogno di una particolare versione del pacchetto, che sia maggiore o minore di una certa versione, o che no si tratt di una versione specifica utilizzando gli operatori relazionali (==, !=, <, <=, >, >=):
				install_requires = ["supporto >= 1.0 < 2.0"]
Ci sono anche argomenti simili per dichiarare pacchetti da installare per eseguire lo script di installazione (setup_requires) per eseguire possibili test inclusi nel pacchetto (tests_require) e per ottenere funzioni aggiuntive (extras_require, consistenti in questo caso in un dizionario).
setuptools include scorciatoie utili, come ad esempio la funzione find_packages(), che ci impedisce di dover elencare tutti e ciascuno dei pacchetti che usa il nostro script nel parametro packages, come è avvenuto con distutils:
				from ez_setup import use_setuptools
				
				use_setuptools()
				
				from setuptools import setup, find_packages
				setup(name="Esempio di applicazione",
				    version="0.1",
				    description="Esempio di funzionamento di distutils",
				    author="Mio Nome",
				    author_email="mioemail@email.it",
				    url="http://miosito.net/tutoriale-python/",
				    license="GPL",
				    scripts=["esempio.py"],
				    packages = find_packages()
				)
Creare eseguibili .exe
Così come in Mac OS e molte distribuzioni Linux l’interprete Python è installato di default, per consentire agli utenti di questi sistemi di non avere problemi durante l’installazione e l’esecuzione di applicazioni scritte in Python.
Nel caso di Windows, questo non è così, per cui sarebbe opportuno, per gli utenti di questo sistema operativo, che non sia necessario installare l’interprete Python. Sarebbe anche interessante che il nostro programma sia costituito da un file .exe, invece di uno o più file .py, per semplificare le cose.
Tutto questo si può ottenere con py2exe, un’estensione distutils che, come suggerisce il nome, consente di creare eseguibili per Windows dal codice Python e che permette di eseguire queste applicazioni senza avere bisogno di installare l’interprete Python sul sistema.
Py2exe lavori esaminando il nostro codice sorgente nella ricerca di moduli e pacchetti che usiamo, compiladoli e costruendo un nuovo file che contiene questi files con un piccolo interprete Python integrato.
Per testare le prestazioni di py2exe creiamo un piccolo programma esempio.py
			print "Sono un .exe"
e il file setup.py corrispondente. I cambiamenti che dobbiamo fare sul file setup.py sono semplici: importare py2exe, e utilizzare gli argomenti console e windows per indicare il nome dello script o gli scripts che si desidera convertire in eseguibili da console o eseguibili con interfaccia grafica (GUI), rispettivamente.
			from distutils.core import setup
			import py2exe

			setup(name="Esempio di applicazione",
			    version="0.1",
			    description="Esempio di funzionamento di distutils",
			    author="Mio Nome",
			    author_email="mioemail@email.it",
			    url="http://miosito.net/tutoriale-python/",
			    license="GPL",
			    scripts=["esempio.py"],
			    console=["esempio.py"]
			)			
Per creare l’eseguibile, utilizzare una nuova opzione con la riga di comando per setup.py disponibile dopo aver importato il modulo e la chiamata, naturalmente, a py2exe:
			python setup.py py2exe
Con questo Py2exe creerà una directory build con le librerie compilate e una directory dist con i files che compongono la nostra applicazione.
Tra i file che possiamo trovare in dist abbiamo uno o più files eseguibili con lo stesso nome, come specificato negli script da console e windows, un file python*.dll che è l’interprete Python, e un file library.zip che contiene diversi files pyc che --bundle di py2exe aggiunge al file library.zip le dll e i pyd (--bundle 2) o le dll, i pyd e l’interprete (--bundle 1).
			python setup.py py2exe --bundle 1
oppure possiamo aggiungere un nuovo argomento options alla funzione setup che indica il valore da utilizzare (opzione bundle_files), in modo da non avere bisogno di aggiungere il flag --bundle ogni volta che si usa il comando py2exe:
			from distutils.core import setup
			import py2exe

			setup(name="Esempio di applicazione",
			    version="0.1",
			    description="Esempio di funzionamento di distutils",
			    author="Mio Nome",
			    author_email="mioemail@email.it",
			    url="http://miosito.net/tutoriale-python/",
			    license="GPL",
			    scripts=["esempio.py"],
			    console=["esempio.py"],
			    options={"py2exe": {"bundle_files": 1}}
			)
Infine possiamo anche fare a meno di library.zip e incorporarlo nell’eseguibile utilizzando l’argomento zipfile=None.
			from distutils.core import setup
			import py2exe

			setup(name="Esempio di applicazione",
			    version="0.1",
			    description="Esempio di funzionamento di distutils",
			    author="Mio Nome",
			    author_email="mioemail@email.it",
			    url="http://miosito.net/tutoriale-python/",
			    license="GPL",
			    scripts=["esempio.py"],
			    console=["esempio.py"],
			    options={"py2exe": {"bundle_files": 1}},
			    zipfile=None
			)

Similari
Overloading di metodi in Java
12% Java
Un metodo overload viene utilizzato per riutilizzare il nome di un metodo ma con argomenti diversi, opzionalmente con un differente tipo di ritorno. [expand title=”Regole per overload” startwrap=”” endwrap=”” excerpt=”⤽” s…
Modi di fare e di non fare in Python
11% Python
Questo documento può essere considerato un compagno del tutorial di Python. Viene illustrato come utilizzare Python, e quasi ancora più importante, come non usare Python. [expand title=”Costrutti del linguaggio che non dov…
redirect 301 usando mod_alias
9% Server
mod_alias è fondamentalmente la versione più semplice di mod_rewrite. Non può fare le cose che fa mod_rewrite, ad esempio modificare la stringa di query. Per eseguire reindirizzamenti nel server web Apache è possibile di u…
Installare Python e Django su Windows
8% Django
Quando ci riferiamo allo sviluppo web con Python, la prima cosa che viene in mente è usare un qualche framework. Il più famoso e utilizzato da tutti è il Django, ma non è l’unico. Ci sono Pylons, Grok, TurboGears e Zope: t…
Metodi magici e costanti predefinite in PHP
8% Php
PHP fornisce un insieme di costanti predefinite e metodi magici per i nostri programmi. A differenza delle normali costanti i quali si impostano con define(), il valore delle costanti predefinite o speciali dipendono da do…