Newsgroup: it.comp.programmare
Titolo: Creazione eseguibile con python 2.1

Ciao,
ho scaricato il python 2.1 ma non ho capito come
creare l'eseguibile (.exe) dal sorgente (.py).
Lavoro in ambiente win2k.
Qualcuno mi puň aiutare?
Grazie,
Andrea

 Qualcuno mi puň aiutare?
In Python, gli eseguibili (.exe, su piattaforme
Windows) vengono normalmente creati in un passo
finale, quando l'intero sviluppo dell'applicazione
(analisi, progettazione, codifica, test, misura
delle prestazioni, ottimizzazione, documentazione)
e` completo, e resta solo il passo di "impacchettare"
l'applicazione stessa per distribuirla a clienti
finali (quando l'applicazione "completa" non va
"distribuita a clienti finali", ma, ad esempio,
gira invece su di un webserver, non c'e` ragione
alcuna di fare il passo finale di "packaging").
Se hai appena scaricato Python 2.1 (ottima scelta,
complimenti!), non mi e` chiaro esattamente come
sia possibile che tu abbia gia` completato l'intero
sviluppo della tua applicazione e ti trovi davanti
al problema di impacchettarla per la consegna ai
clienti finali.  Va bene che Python consente una
produttivita` incredibile, ma qui si esagera!
Comunque -- beato te!  Il migliore strumento di
packaging di applicazioni Python in ambienti di
tipo Windows oggi, secondo me, e` py2exe di Thomas
Heller -- gratuitamente scaricabile dalla rete.
http://starship.python.net/crew/theller/py2exe/
 e`
la "homepage" di questa ottima utility.  La versione
corrente (0.2.6) per Python 2.1 puoi averla da -- una
URL sola, anche se dovessi vederla rotta su 2 righe...:
http://starship.python.net/crew/theller/py2exe/py2exe-0.2.6.win32-py2.1.exe
In fondo alla homepage di py2exe trovi anche dei
link ad altri strumenti di packaging gratuiti per
applicazioni Python su varie piattaforme.  Ma
py2exe e` piu` semplice da usare, e assai potente,
se le tue esigenze sono limitate a piattaforme di
tipo Windows (se no non parleresti di ".exe"...!).
Attenzione che py2exe non crea *un installer*:
crea un .EXE che deve comunque venire usato assieme
alle .DLL (e .PYD, .OCX, o che altro!) da cui
dipende.  py2exe e` in grado di trovare tutte
queste dipendenze, ma si limita a copiare tutte
le .DLL &c necessarie in un apposito directory.
Tocca poi a te dare i tocchi finali al tuo
pacchetto d'istallazione, cosa che puoi fare
in vari modi a seconda delle tue esigenze.
Potresti banalmente impacchettare l'EXE ed
i moduli da cui dipende in un .ZIP e dare
per scontato che l'utente sa usare Winzip
per istallarlo.  Oppure potresti aggiungere
al detto .ZIP quel minimo di codice che lo
rende un .EXE autoscompattante, funzione che
e` prevista da Winzip almeno nella versione
commerciale (== se uno paga la licenza).  Ma
anche i gratuiti prodotti Infozip hanno delle
funzionalita` analoghe.  Oppure puoi usare
Wise, Installshield, ecc, ecc, e fare degli
installer grafici, magari non degli .EXE ma
dei .MSI cosi` usano il Microsoft Installer,
eccetera, eccetera.  La scelta e` tua...!
Facciamo un esempio.  Ho sviluppato una grandiosa
applicazione Python che vive nel file ciao.py:
++ d:/py21/ciao.py:
nome = raw_input("Come ti chiami? ")
print "Ciao, %s!"%nome
++ fine
Anzitutto verifico la sua possente funzionalita`:
D:\py21>python ciao.py
Come ti chiami? Alex
Ciao, Alex!
D:\py21>
Grandiosa, nevvero?  OK, sviluppo completato, ora
basta impacchettarla per venderla a un palo a botta.
Scrivo dunque il solito setup.py, come per qualsiasi
uso delle distutil (le normali utility per risolvere
problemi di distribuzione di codice Python -- spero
che ti siano ben familiari, prima di affrontare
problemi peculiari come "preparare un .exe"...):
++ d:/py21/setup.py:
from distutils.core import setup
import py2exe
setup(name="ciao",
      scripts=["ciao.py"],
)
++ fine
ed ora eseguo il setup dell'impacchettamento -- non
ti spaventare del ricchissimo feedback, e` li` solo
per tua informazione!-).
D:\py21>python setup.py py2exe
running py2exe
running build
running build_scripts
creating build
creating build\scripts
copying ciao.py -> build\scripts
running install_scripts
creating build\bdist.win32
creating build\bdist.win32\winexe
creating build\bdist.win32\winexe\lib
creating build\bdist.win32\winexe\lib\Python21
creating build\bdist.win32\winexe\lib\Python21\Scripts
copying build\scripts\ciao.py ->
build\bdist.win32\winexe\lib\Python21\Scripts
warning: install: modules installed to
'build\bdist.win32\winexe\lib\Python21\',
 which is not in Python's module search path (sys.path) -- you'll have to
change
 the search path yourself
creating dist
+----------------------------------------------------
| Processing script ciao.py with py2exe-0.2.6
+----------------------------------------------------
creating dist\ciao
creating build\bdist.win32\winexe\collect
creating build\bdist.win32\winexe\collect\ciao
creating build\bdist.win32\winexe\collect\ciao\Scripts.py2exe
Searching modules needed to run 'ciao.py' on path:
['D:\\py21\\build\\bdist.win32\\winexe\\lib\\Python21', '',
'd:\\python21\\pytho
nwin', 'd:\\python21\\win32', 'd:\\python21\\win32\\lib', 'd:\\python21',
'd:\\p
ython21\\dlls', 'd:\\python21\\lib', 'd:\\python21\\lib\\plat-win',
'd:\\python2
1\\lib\\lib-tk']
force_imports =
ext_mapping = {
}
Resolving binary dependencies:
  D:\WINNT\System32\python21.dll
  d:\python21\py2exe\run.exe
copying d:\python21\py2exe\support.py ->
build\bdist.win32\winexe\collect\ciao\S
cripts.py2exe
byte-compiling d:\python21\lib\imputil.py to imputil.pyc
copying ciao.py ->
build\bdist.win32\winexe\collect\ciao\Scripts.py2exe\__main__
.py
changing into 'build\bdist.win32\winexe\collect\ciao'
D:\WINNT\zip.exe -rq D:\py21\build\bdist.win32\winexe\ciao.zip .
changing back to 'D:\py21'
creating dist\ciao\ciao.exe
copying D:\WINNT\System32\python21.dll -> dist\ciao
removing 'build\bdist.win32\winexe\collect\ciao' (and everything under it)
removing 'build\bdist.win32\winexe' (and everything under it)
Fatto!  Tutta la fatica l'ha fatta il computer, ovvio:-).
A noi basta verificare il risultato...:
D:\py21>dir dist\ciao
 Volume in drive D has no label.
 Volume Serial Number is 0498-B4C2
 Directory of D:\py21\dist\ciao
05/16/01  04:03p        <DIR>          .
05/16/01  04:03p        <DIR>          ..
05/16/01  04:03p                48,168 ciao.exe
04/16/01  06:25p               708,669 python21.dll
               4 File(s)        756,837 bytes
                            198,168,576 bytes free
D:\py21>
Come previsto, il directory dist\ciao contiene la .exe
generata (ciao.exe) e tutti i moduli da cui essa dipende
(qui, solo python21.dll).
Supponiamo ora di volere preparare l'installer usando
il programma gratis Ghost Installer di Alex Popov (vedi
http://cnet.com/downloads/0-10088-100-4685208.html).
..
...bah, il post sta diventando un po' lungo!  Facciamo
invece solo:
D:\py21>zip ciao dist\ciao\*.*
  adding: dist/ciao/ciao.exe (92 bytes security) (deflated 53%)
  adding: dist/ciao/python21.dll (92 bytes security) (deflated 55%)
D:\py21>dir ciao.zip
 Volume in drive D has no label.
 Volume Serial Number is 0498-B4C2
 Directory of D:\py21
05/16/01  04:25p               342,803 ciao.zip
               1 File(s)        342,803 bytes
                            195,874,816 bytes free
D:\py21>unzip -l ciao
Archive:  ciao.zip
  Length     Date   Time    Name
 --------    ----   ----    ----
    48168  05-16-01 16:03   dist/ciao/ciao.exe
   708669  04-16-01 18:25   dist/ciao/python21.dll
 --------                   -------
   756837                   2 files
...e distribuiremo questo ciao.zip con le istruzioni
di spacchettarlo a mano.  "Come sviluppare dei begli
installer con GUI e tutto" e` un problema che poco
dipende da Python in specifico, e si puo` affrontare,
per questa .EXE+moduli da cui dipende, ne` piu` ne`
meno come si potrebbe farlo per qualsiasi programma
(EXE+DLL di runtime, ActiveX, ecc ecc) comunque lo
si sia sviluppato (a seconda del generatore di
installer che si vuole utilizzare -- Ghost, Wise,
o che altro).
Alex

Alla faccia della disponibilità!!!!! Grazie!
Mi sono copiato il tuo post sul disco per future consultazioni...
In realtà il programma che ho creato ieri sera non era molto
più complicato del print 'ciao':
Quando viene avviato verifica la presenza di un file chamato
andy.id
; se non c'è, lo crea, ti chiede il nome e lo salva sul file,
dopodichè ti saluta leggendo il nome dal file. Se invece il file 
andy.id
c'è,
ti saluta soltanto leggendo sempre il nome dal file. Tutto qui...
Per ora sono agli inizi, come avrai notato... creare l'eseguibile "stand
alone" in realtà non mi serviva più di tanto, era solo per soddisfazione
personale!
Però mi è rimasto un dubbio: quando cerco di aprire il file in modalità "r"
(se non
sbaglio in questa modalità il file deve esistere), e il file non esiste,
l'esecuzione del
programma si blocca. In C, invece, veniva semplicemente restituito il valore
0 (zero);
questo mi permetteva di lavorare con un costrutto if, e servirmi di questo
valore di
ritorno per verificare le presenza del file. In python invece come faccio a
sapere
se un file esiste oppure no?

 sapere se un file esiste oppure no?
Ci sono due idiomi fondamentali (nella maggior parte dei linguaggi)
per gestire queste situazioni.  Il primo, detto in inglese "Look
Before You Leap" (LBYL: "guarda prima di saltare") consiste nel
controllare accuratamente tutte le possibili situazioni anomale
PRIMA di eseguire una operazione.  Pare psicologicamente normale,
ma presenta vari problemi tecnici.  Il secondo, detto in inglese
"It's Easier to Ask Forgiveness than Permission" (EAFP: "e` piu`
facile chiedere scusa che chiedere permesso") consiste nel tentare
comunque l'operazione _E_ gestire le eventuali anomalie SE si
presentano.  Il motto EAFP e` dovuto al contrammiraglio Grace
Murray Hopper (1906-1992), certo la donna piu` significativa nella
storia dei calcolatori (co-architetto di UNIVAC, co-inventore di
COBOL, ...), che lo usava per descrivere come sopravvivere alla
burocrazia (ha vissuto quasi tutta la sua vita professionale nella
marina USA, salvo la pausa nell'industria privata per fare UNIVAC,
quindi certo di sopravvivere ai burocrati se ne intendeva:-).
In Python (e similmente in Java, C++, ecc), l'istruzione che ti
permette di applicare EAFP e` try/except/else:
    try:
        ilfile = open('
andy.id
')
    except IOError, diagnosi:
        print "Non posso aprire il file 
andy.id
: %s" % diagnosi
    else:
        print "Ciao, %s!" % ilfile.read()
Se una eccezione viene sollevata da istruzioni contenute in
una clausola try: (o funzioni da esse chiamate), le clausole
except:, dette "exception handlers", possono gestire questa
eccezione -- ogni handler puo` specificare con piu` o meno
generalita` quali tipi di eccezioni vuole gestire.  La clausola
else: entra in gioco solo se NON vi sono state eccezioni, e
mi serve per avere del codice le cui eventuali eccezioni NON
vanno gestite (se la .read() da` problemi, voglio saperli...)
ma che e` da eseguire solo in mancanza di eccezioni precedenti.
Pochi sono cosi` precisi e accurati in un piccolo programma
(ma Python e` adattissimo anche a sviluppare programmi enormi,
vedi Zope ad esempio, e in questi casi la precisione e` di
rigore:-) -- normalmente scriverei qualcosa del tipo:
    try: nome = open('
andy.id
').read().strip()
    except: nome = "sconosciuto"
    print "Ciao, %s!" % nome
(strip e` per togliere il whitespace davanti e dietro alla
stringa, tipo il \n terminale che ha in genere un testo).
Qui, 'except:' gestisce TUTTE le eccezioni avvenute nella
unica riga della clausola try (che siano in open, in read,
in strip, o che altro) e comunque pone nome a "sconosciuto".
Su piccola scala try/except non differisce molto dalla
possibilita` di usare if, ma su grande scala aiuta assai
(c'e` un vigoroso dibattito su questo proprio ora in
corso su it.comp.lang.c++, e non tutti la pensano cosi`,
comunque...:-).
Alex (MVP di Brainbench per il C++)

Io il problema lo avevo risolto molto meno elegantemente
cosě...
def saluto():
    apri_file = open('
nome.id
', 'r')
    print 'Ciao',apri_file.read(),'!'
    apri_file.close()
#******************************************#
#******************************************#
# Apertura del file '
nome.id
'
apri_file = open('
nome.id
', 'a+')
# Se il file non c'č (e quindi viene creato vuoto), viene
# richiesto il nome utente e salvato nel file
if not(apri_file.read()):
    nome_utente = str(raw_input('Scrivi il tuo nome:\n'))
    apri_file.write(nome_utente)
    apri_file.close()
# Dopodichč, viene chiamato saluto() che legge il
# nome dell'utente dal file.
saluto()
Ma ovviamente la gestione degli errori č nulla...
Comunque ho preso nota, comincerň ora con la gestione
delle eccezioni.

 delle eccezioni.
Buona idea!  Non e` difficile e spesso fa comodo.
