Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Déployer un script python en incluant les dépendances vers des bibliothèques n on standards

15 réponses
Avatar
Francois Lafont
Bonjour à tous,

Je pense que ma question est un problème assez classique mais 1) je n'ai
pas réussi à trouver des explications claires et 2) je suis très loin
d'être un expert python.

Par exemple, je veux déployer cette "application" (elle crée juste un
fichier pdf avec écrit "blabla" dedans) :

#---------------------------------
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm

styles = getSampleStyleSheet()

story = []

texte = "Blabla blabla. " * 40


story.append(Paragraph(texte, styles["Normal"]))
story.append(Spacer(0, 0.5 *cm))
story.append(Paragraph(texte, styles["Normal"]))


pdf = SimpleDocTemplate('test.pdf', pagesize=A4,
title='Premier Test', author='FL')
pdf.build(story)
#---------------------------------

Mon objectif est le suivant : je voudrais distribuer cette "application"
à d'autre personnes de manière à ce qu'ils aient à faire le minimum au
niveau installation.

L'idéal serait qu'un utilisateur ait seulement à faire ceci :

1. Il installe python sur son système.
2. Il télécharge une archive blablapdf.zip et procède à une extraction.
3. Il va dans le dossier blablapdf et lance un script python du genre
main.py.

Évidemment, si l'application n'utilisait que des modules de la
bibliothèque standard, ce serait facile. Mais là, reportlab est utilisé
et il n'en fait pas partie. Typiquement, je voudrais éviter à
l'utilisateur d'avoir à l'installer par lui-même sur son système. Est-ce
possible et comment ?

Merci d'avance pour votre aide.

PS : je suis sous Debian Squeeze et utilise python version 2.6.6.



--
François Lafont

10 réponses

1 2
Avatar
Nicolas
Le 23/08/2011 14:24, Francois Lafont a écrit :
Bonjour à tous,

Je pense que ma question est un problème assez classique mais 1) je n'ai
pas réussi à trouver des explications claires et 2) je suis très loin
d'être un expert python.

Par exemple, je veux déployer cette "application" (elle crée juste un
fichier pdf avec écrit "blabla" dedans) :

#---------------------------------
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm

styles = getSampleStyleSheet()

story = []

texte = "Blabla blabla. " * 40


story.append(Paragraph(texte, styles["Normal"]))
story.append(Spacer(0, 0.5 *cm))
story.append(Paragraph(texte, styles["Normal"]))


pdf = SimpleDocTemplate('test.pdf', pagesize¤,
title='Premier Test', author='FL')
pdf.build(story)
#---------------------------------

Mon objectif est le suivant : je voudrais distribuer cette "application"
à d'autre personnes de manière à ce qu'ils aient à faire le minimum au
niveau installation.

L'idéal serait qu'un utilisateur ait seulement à faire ceci :

1. Il installe python sur son système.
2. Il télécharge une archive blablapdf.zip et procède à une extraction.
3. Il va dans le dossier blablapdf et lance un script python du genre
main.py.

Évidemment, si l'application n'utilisait que des modules de la
bibliothèque standard, ce serait facile. Mais là, reportlab est utilisé
et il n'en fait pas partie. Typiquement, je voudrais éviter à
l'utilisateur d'avoir à l'installer par lui-même sur son système. Est-ce
possible et comment ?

Merci d'avance pour votre aide.

PS : je suis sous Debian Squeeze et utilise python version 2.6.6.





Bonjour,

Il y a la solution cx_freeze qui permet de générer un ensemble de
fichiers autonomes. Même pas la peine d'installer Python. Je n'ai jamais
utilisé cx_freeze par contre, j'ai déjà utilisé py2exe qui est
l'équivalent sous Windows. Avec py2exe, il est même possible de générer
un fichier unique, un installeur. L'utilisateur final n'a qu'a installer
l'application et l'utiliser.

Sinon, il me semble que les eggs sont faits pour résoudre ton problème
comme tu le décris. A vérifier.

Nicolas
Avatar
Francois Lafont
Bonjour,

Le 24/08/2011 08:50, Nicolas a écrit :

Il y a la solution cx_freeze qui permet de générer un ensemble de
fichiers autonomes. Même pas la peine d'installer Python. Je n'ai jamais
utilisé cx_freeze par contre, j'ai déjà utilisé py2exe qui est
l'équivalent sous Windows. Avec py2exe, il est même possible de générer
un fichier unique, un installeur. L'utilisateur final n'a qu'a installer
l'application et l'utiliser.

Sinon, il me semble que les eggs sont faits pour résoudre ton problème
comme tu le décris. A vérifier.



Merci bien pour la réponse Nicolas.

En fait, j'ai testé cx_freeze mais sans succès. J'avais des messages de
modules manquants et même en jouant avec les options et en ajoutant les
fameux modules, à l'exécution, j'avais plein d'erreur de modules
manquants malgré tout. Bref, je ne suis arrivé à rien de probant.

J'allais passer aux autres propositions, mais un peu par hasard je suis
arrivé à quelque chose qui marche très bien. En effet, sous Windows dans
C:Python27Libsite-packages, j'avais le dossier reportlab. Je me suis
rendu compte que si mon script main.py se trouvait dans le même dossier
que le dossier reportlab, tout fonctionnait (même si reportlab n'est pas
installé sur le système). Du coup, je vais opter pour une archive qui
contiendra ça :

- le fichier main.py
- le dossier-package reportlab (une quinzaine de Méga à tout casser)

C'est sûrement pas ce qu'il y a de plus propre, mais bon ça me convient.

J'ai bien essayé de regarder distutils, mais je n'ai pas compris grand
chose.


--
François Lafont
Avatar
Francois Lafont
Bonjour à tous,

Je me permets de remonter ce fil, car j'ai pu finalement arriver à
quelque chose qui marche avec cx_freeze appliqué à l'exemple de mon
script du premier message qui utilisait la bibliothèque non standard
"reportlab". Alors voici ce que j'ai fait, peut-être que ça pourrait
servir à quelqu'un d'autre.

Le 26/08/2011 00:40, Francois Lafont a écrit :

En fait, j'ai testé cx_freeze mais sans succès. J'avais des messages de
modules manquants et même en jouant avec les options et en ajoutant les
fameux modules, à l'exécution, j'avais plein d'erreur de modules
manquants malgré tout. Bref, je ne suis arrivé à rien de probant.



En fait, j'étais sur un Windows XP avec Python 2.7 et reportlab
installés dessus.

Quand je lançais « cxfreeze blabla.py », j'avais quelques lignes en
retour m'indiquant des modules manquants selon cxfreeze. Effectivement,
ces modules n'étaient pas installés sur mon système bien que blabla.py
fonctionnait parfaitement.

En fait, cxfreeze cherche à importer tous les modules qu'ils trouve dans
le code de blabla.py et dans ses dépendances surtout. Mais, vu que
blabla.py fonctionne sans erreur, ces imports de modules absents de mon
système n'ont tout simplement pas lieu à l'exécution et doivent se
trouver dans des blocs du genre "if plate_forme == UnTrucQuiNestPasXP:".
Donc, j'ai simplement ignoré ces messages indiquant des modules manquants.

Par contre, à ce stade, quand je lançais l'exécutable blabla.exe que
m'avait fabriqué cxfreeze, ça ne marchait et j'avais des messages
d'erreur du genre :

File "C:Python27libsite-packagesreportlabpdfpages_fontdata.py",
line 158 in <module>
module = __import__(modname, globals(), locals())
ImportError: No module named _fontdata_enc_winansi

Alors que ce module (_fontdata_enc_winansi), lui, il est bien installé
sur mon système dans (reportlab.pdfbase d'ailleurs). Et puis je suis
tombé sur cette page Web (ah Google, le meilleur débogueur du monde) :
http://pastebin.com/j4XH3Jtg

J'ai donc repris mon script blabla.py en ajoutant les imports indiqués
dans la page ci-dessus, ce qui donne ça au final :

#---------------------------------

# C'est juste pour cxfreeze
from reportlab.pdfbase import _fontdata_widths_courier
from reportlab.pdfbase import _fontdata_widths_courierbold
from reportlab.pdfbase import _fontdata_widths_courieroblique
from reportlab.pdfbase import _fontdata_widths_courierboldoblique
from reportlab.pdfbase import _fontdata_widths_helvetica
from reportlab.pdfbase import _fontdata_widths_helveticabold
from reportlab.pdfbase import _fontdata_widths_helveticaoblique
from reportlab.pdfbase import _fontdata_widths_helveticaboldoblique
from reportlab.pdfbase import _fontdata_widths_timesroman
from reportlab.pdfbase import _fontdata_widths_timesbold
from reportlab.pdfbase import _fontdata_widths_timesitalic
from reportlab.pdfbase import _fontdata_widths_timesbolditalic
from reportlab.pdfbase import _fontdata_widths_symbol
from reportlab.pdfbase import _fontdata_widths_zapfdingbats

from reportlab.pdfbase import _fontdata_enc_winansi
from reportlab.pdfbase import _fontdata_enc_macroman
from reportlab.pdfbase import _fontdata_enc_standard
from reportlab.pdfbase import _fontdata_enc_symbol
from reportlab.pdfbase import _fontdata_enc_zapfdingbats
from reportlab.pdfbase import _fontdata_enc_pdfdoc
from reportlab.pdfbase import _fontdata_enc_macexpert
######################FIN############################"

from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm

styles = getSampleStyleSheet()

story = []

texte = "Blabla blabla. " * 40


story.append(Paragraph(texte, styles["Normal"]))
story.append(Spacer(0, 0.5 *cm))
story.append(Paragraph(texte, styles["Normal"]))


pdf = SimpleDocTemplate('test.pdf', pagesize¤,
title='Premier Test', author='FL')
pdf.build(story)
#---------------------------------

Et là, après avoir lancé « cxfreeze blabla.py », j'ai obtenu un
exécutable qui marche impeccable (l'utilisateur n'a même pas besoin
d'installer Python).

Ce qui est curieux c'est que ces lignes d'import ajoutées sont
strictement inutiles pour le bon fonctionnement du script python
blabla.py, mais décisifs pour le bon fonctionnement de l'exécutable
fabriqué via csfreeze.

Ce que j'en comprends, c'est que cxfreeze doit lire le script qu'on lui
donne ainsi que toutes les dépendances (et les dépendances des
dépendances etc.) en regardant les lignes contenant le mot clé "import".
Mais dans les arcanes de reportlab et en l'occurrence dans le module
reportlab.pdfbase._fontdata à la ligne 158, il y a un import des modules
"reportlab.pdfbase._fontdata*" qui est un peu exotique dans sa forme et
qui doit échapper à la sagacité de cxfreeze. En effet, à la ligne 158 de
ce module il y a ça :

for keyname in standardEncodings:
modname = '_fontdata_enc_%s' % keyname.lower()[:-8]
module = __import__(modname, globals(), locals())
encodings[keyname] = getattr(module, keyname)

Même si je me doute bien qu'il y a un import de quelque chose, je serais
incapable de dire quoi et cxfreeze non plus. D'ailleurs, cette fonction
__import__(), je ne sais pas trop ce qu'elle fait. Mais si j'ajoute les
import *explicitement* dans mon script blabla.py, alors à cxfreeze le
voit et mon .exe fonctionne impeccable. Donc il faut ajouter des lignes
d'import inutiles d'un point de vue Python pour dire à cxfreeze «
attention mon cher cxfreeze, je t'avertis qu'il faudra que tu m'englobe
tous ces modules dans ton .exe ! »

Du coup, cela dépasse toute mes espérances car j'ai un .exe qui ne
nécessite même pas l'installation de Python par l'utilisateur. Bon, il y
a juste cette subtilité des modules à importer qui peuvent échapper à
cxfreeze, mais en fin de compte, on peut s'en sortir avec les messages
d'erreur.




--
François Lafont
Avatar
Nicolas
for keyname in standardEncodings:
modname = '_fontdata_enc_%s' % keyname.lower()[:-8]
module = __import__(modname, globals(), locals())
encodings[keyname] = getattr(module, keyname)

Même si je me doute bien qu'il y a un import de quelque chose, je serais
incapable de dire quoi et cxfreeze non plus. D'ailleurs, cette fonction
__import__(), je ne sais pas trop ce qu'elle fait. Mais si j'ajoute les
import *explicitement* dans mon script blabla.py, alors à cxfreeze le
voit et mon .exe fonctionne impeccable. Donc il faut ajouter des lignes
d'import inutiles d'un point de vue Python pour dire à cxfreeze «
attention mon cher cxfreeze, je t'avertis qu'il faudra que tu m'englobe
tous ces modules dans ton .exe ! »


Bonjour,

La fonction __import__() permet de faire des importations de modules de
façon dynamique.
C'est ça qui pose problème à cx_freeze car celui-ci ne peut analyser que
les import statiques. Les import dynamiques ne sont "vus" par cx_freeze
et par conséquent, les modules correspondants ne sont pas ajoutés à
l'exécutable.
J'ai déjà eu ce problème avec py2exe et d'autres librairies (PIL entre
autres).
Une solution est effectivement d'importer explicitement les modules qui
posent problème. Par contre, il serait préférable de faire ces import
dans le script de configuration de cx_freeze. De cette manière, ton soft
n'est pas modifié et tu peux en faire un exécutable quand même.

Nicolas
Avatar
Francois Lafont
Le 02/09/2011 08:35, Nicolas a écrit :

La fonction __import__() permet de faire des importations de modules de
façon dynamique.
C'est ça qui pose problème à cx_freeze car celui-ci ne peut analyser que
les import statiques. Les import dynamiques ne sont "vus" par cx_freeze
et par conséquent, les modules correspondants ne sont pas ajoutés à
l'exécutable.



Ok. Je m'en doutais. :-)

Une solution est effectivement d'importer explicitement les modules qui
posent problème. Par contre, il serait préférable de faire ces import
dans le script de configuration de cx_freeze. De cette manière, ton soft
n'est pas modifié et tu peux en faire un exécutable quand même.



Je vois à peu près ce que tu veux dire, sauf que je ne vois pas trop ce
que tu veux dire quand tu parles de « script de configuration de
cx_freeze ». Tu veux dire qu'au lieu de faire « cxfreeze MonScript.py »,
il faut que je tape un truc dans le genre :

cxfreeze --include-modules "reportlab.pdfbase_fontdata_widths_courier,
reportlab.pdfbase._fontdata_widths_courierbold, ETC..." MonScript.py

C'est ça ? Ou je n'ai pas compris.

En tout cas, merci Nicolas, car cx_freeze est une découverte bien
sympathique pour moi.



--
François Lafont
Avatar
Francois Lafont
Bonsoir,

Je continue dans mes essais de cx_freeze et j'ai voulu le tester sous ma
Debian (Squeeze) pour faire un "exécutable" sous Linux. Mais là, je me
heurte à un curieux problème.

1) Pas de problème pour l'installation de cx_freeze (j'ai dû installer
le paquet python-dev qui me faisait défaut). Mais ensuite, j'ai un
problème, avec le mini-script suivant :

exit()

Rien qu'avec ça, quand je « compile » mon script avec cx_freeze, et que
je lance l'exécutable, j'obtiens ça :

#-----------------------------------------
Traceback (most recent call last):
File "/home/francois/cxfreeze/cx_Freeze/initscripts/Console.py", line
27, in <module>
exec code in m.__dict__
File "test.py", line 1, in <module>
exit()
NameError: name 'exit' is not defined
#-----------------------------------------

La fonction exit() n'est pas définie, c'est un peu fort de café quand
même. Une idée ?

Je précise que mon installation de cx_freeze sur ma Debian semble
marcher car, par exemple, je peux «compiler» et exécuter ensuite un
basique «Hello World» et aussi un programme un peu plus gros sans problème.


2) Autre question : est-il possible, avec un cx_freeze installé sur une
Debian, de «compiler» un programme python pour obtenir un exécutable
pour Windows et non pas pour Linux ?



PS : pour l'installation de cx_freeze sur ma Debian je me suis contenté
d'une installation dans mon home pour ne pas interférer avec le système :

apt-get install python-dev
python setup.py build
python setup.py install --home='/home/francois/cxfreeze/'


--
François Lafont
Avatar
Alain Ketterlin
Francois Lafont writes:

#-----------------------------------------
Traceback (most recent call last):
File "/home/francois/cxfreeze/cx_Freeze/initscripts/Console.py", line
27, in <module>
exec code in m.__dict__
File "test.py", line 1, in <module>
exit()
NameError: name 'exit' is not defined
#-----------------------------------------

La fonction exit() n'est pas définie, c'est un peu fort de café quand
même. Une idée ?



Je ne savais même pas que ça existait... Elle n'est en tout cas p as
listée dans les fonctions "builtin" (oui je sais que "ça marche" dans un
script "normal", mais je ne sais pas pourquoi -- sûrement une
particularité de l'interpréteur). J'imagine que sys.exit() doit
fonctionner normalement.

2) Autre question : est-il possible, avec un cx_freeze installé sur une
Debian, de «compiler» un programme python pour obtenir un exà ©cutable
pour Windows et non pas pour Linux ?



cx_freeze fonctionne en empaquetant l'interpréteur (qui dépend de
l'architecture) et le code python (qui n'en dépend pas). Il faut donc
lui demander de n'empaqueter que le code python, et laisser cx_freeze
faire son boulot de l'autre coté.

-- Alain.
Avatar
Alain Ketterlin
Francois Lafont writes:

cx_freeze fonctionne en empaquetant l'interpréteur (qui dépend de
l'architecture) et le code python (qui n'en dépend pas). Il faut do nc
lui demander de n'empaqueter que le code python, et laisser cx_freeze
faire son boulot de l'autre coté.



a) J'imagine alors que l'exécutable obtenu ne sera pas "standalone" dans
ce cas et que le système devra avoir un interpréteur Python ins tallé,
c'est ça ? (ce qui, au passage, n'est pas un problème. Mais je voudrais
être sûr de bien comprendre.)



C'est ça. Cela dit, si il y a un python sur la machine cible, est-ce
bien la peine de faire tout ça ?

b) Comment fait-on pour demander à cx_freeze de ne pas empaqueter
l'interpréteur ? Je ne vois pas dans la mini sur internet de cx_free ze.



Via distutils et create-shared-zip.

D'autant plus que, lors de la « compilation cx_freeze » de mon script,
j'utilise quand même la bibliothèque tierce reportlab que je vo udrais,
quant à ellen, empaqueter dans l'exécutable.



Ce qui devrait se passer. Bien sûr si tu utilises des bibliothèqu es
natives, je ne sais pas ce que cx_freeze peut faire.

-- Alain.
Avatar
Laurent Pointal
Francois Lafont wrote:

Bonsoir,

Je continue dans mes essais de cx_freeze et j'ai voulu le tester sous ma
Debian (Squeeze) pour faire un "exécutable" sous Linux. Mais là, je me
heurte à un curieux problème.

1) Pas de problème pour l'installation de cx_freeze (j'ai dû installer
le paquet python-dev qui me faisait défaut). Mais ensuite, j'ai un
problème, avec le mini-script suivant :

exit()

Rien qu'avec ça, quand je « compile » mon script avec cx_freeze, et que
je lance l'exécutable, j'obtiens ça :

#-----------------------------------------
Traceback (most recent call last):
File "/home/francois/cxfreeze/cx_Freeze/initscripts/Console.py", line
27, in <module>
exec code in m.__dict__
File "test.py", line 1, in <module>
exit()
NameError: name 'exit' is not defined
#-----------------------------------------

La fonction exit() n'est pas définie, c'est un peu fort de café quand
même. Une idée ?



import sys
sys.exit()

http://docs.python.org/library/sys.html

De rien.
Avatar
Francois Lafont
Bonsoir,

Je reviens encore à la charge car je pense avoir réussi ce que je
voulais, mais je ne suis pas sûr de m'y prendre correctement.

D'abord, si j'ai bien compris, il est impossible de faire de la
cross-compilation (ex: compilation sur Debian pour obtenir un .exe
windows) et obtenir un exécutable standalone pour la plate-forme cible,
ne nécessitant même pas l'installation de l'interpréteur Python.
cx_freeze permet ce genre de compilations mais la plate-forme de
compilation et la plate-forme cible doivent être identiques.
Q1) Est-ce juste ?

En revanche, d'après ce que j'ai compris des indications d'Alain, il est
possible de créer une sorte de .zip contenant tout le code python
nécessaire à une appli et pouvant fonctionner sur n'importe quelle
plate-forme pourvu que l'interpréteur Python y soit installé en amont.
Et c'est sur la manière de s'y prendre que je ne suis pas sûr.

Je reprends mon exemple du premier message. Je suis sous Debian et j'ai
ça (prog.py) :

#---------------------------------
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm

styles = getSampleStyleSheet()

story = []

texte = "Blabla blabla. " * 40


story.append(Paragraph(texte, styles["Normal"]))
story.append(Spacer(0, 0.5 *cm))
story.append(Paragraph(texte, styles["Normal"]))


pdf = SimpleDocTemplate('test.pdf', pagesize¤,
title='Premier Test', author='FL')
pdf.build(story)
#---------------------------------

Je crée le fichier setup.py ci-dessous dans le même dossier que le
fichier prog.py précédent :

#---------------------------------
from cx_Freeze import setup, Executable

setup(
name = "prog",
version = "0.1",
description = "De la balle !!!",
executables = [Executable("prog.py")])
#---------------------------------

Ensuite, je tape en ligne de commandes :

python setup.py build_exe --create-shared-zip

J'obtiens alors l'archive .build/exe.linux-x86_64-2.6/library.zip qui
semble contenir (sous la forme de fichiers .pyc) mon code python et ses
dépendances. J'ai un fichier prog__main__.pyc que je peux lancer via mon
interpréteur python sous ma Debian et le programme fonctionne bien. Si
je teste le tout sur un Windows, en ayant au préalable installé Python,
et que je lance le prog__main__.pyc, via l'interpréteur, alors le
programme se lance correctement et j'obtiens bien un fichier pdf.

Q2) Est-ce comme cela qu'il faut s'y prendre ? En vérité, j'ai un doute
car j'ai testé ce procédé sur exemple un peu plus gros que l'exemple
donné ici et j'obtiens alors une erreur sous Win (et pas sous Debian).

Au passage, avec ce procédé, il fallait que j'utilise la même version
d'interpréteur (sur Win et Linux), sinon j'ai des erreurs. Mais ça, je
trouve que c'est logique.



--
François Lafont
1 2