28.4. zipapp
— Manage executable python zip archives¶
Nouveau dans la version 3.5.
Code source : Lib/zipapp.py
Ce module fournit des outils pour gérer la création de fichiers zip contenant du code Python, qui peuvent être exécutés directement par l’interpréteur Python. Le module fournit à la fois une interface de ligne de commande Interface en ligne de commande et une interface API Python.
28.4.1. Exemple de base¶
L’exemple suivant montre comment l’interface de ligne de commande Interface en ligne de commande peut être utilisée pour créer une archive exécutable depuis un répertoire contenant du code Python. Lors de l’exécution, l’archive exécutera la fonction main
du module myapp
dans l’archive.
$ python -m zipapp myapp -m "myapp:main"
$ python myapp.pyz
<output from myapp>
28.4.2. Interface en ligne de commande¶
Lorsqu’il est appelé en tant que programme à partir de la ligne de commande, la syntaxe suivante est utilisée :
$ python -m zipapp source [options]
Si source est un répertoire, une archive est créée à partir du contenu de source. Si source est un fichier, ce doit être une archive et il est copié dans l’archive cible (ou le contenu de sa ligne shebang est affiché si l’option --info
est indiquée).
Les options suivantes sont disponibles :
-
-o
<output>
,
--output
=<output>
¶ Écrit la sortie dans un fichier nommé output. Si cette option n’est pas spécifiée, le nom du fichier de sortie sera le même que celui de l’entrée source, avec l’extension
.pyz
. Si un nom de fichier explicite est donné, il est utilisé tel quel (une extension.pyz
doit donc être incluse si nécessaire).Un nom de fichier de sortie doit être spécifié si la source est une archive (et, dans ce cas, la sortie ne doit pas être la même que la source).
-
-p
<interpreter>
,
--python
=<interpreter>
¶ Ajoute une ligne
#!
à l’archive en spécifiant interpreter comme commande à exécuter. Aussi, sur un système POSIX, cela rend l’archive exécutable. Le comportement par défaut est de ne pas écrire la ligne#!
et de ne pas rendre le fichier exécutable.
-
-m
<mainfn>
,
--main
=<mainfn>
¶ Écrit un fichier
__main__.py
dans l’archive qui exécute mainfn. L’argument mainfn est de la forme « pkg.mod:fn », où « pkg.mod » est un paquet/module dans l’archive, et « fn » est un appelable dans le module donné. Le fichier__main__.py
réalise cet appel.--main
ne peut pas être spécifié lors de la copie d’une archive.
-
--info
¶
Affiche l’interpréteur intégré dans l’archive, à des fins de diagnostic. Dans ce cas, toutes les autres options sont ignorées et SOURCE doit être une archive et non un répertoire.
-
-h
,
--help
¶
Affiche un court message d’aide et quitte.
28.4.3. API Python¶
Ce module définit deux fonctions utilitaires :
-
zipapp.
create_archive
(source, target=None, interpreter=None, main=None)¶ Crée une archive d’application à partir de source. La source peut être de natures suivantes :
The name of a directory, or a
pathlib.Path
object referring to a directory, in which case a new application archive will be created from the content of that directory.The name of an existing application archive file, or a
pathlib.Path
object referring to such a file, in which case the file is copied to the target (modifying it to reflect the value given for the interpreter argument). The file name should include the.pyz
extension, if required.Un objet fichier ouvert pour la lecture en mode binaire. Le contenu du fichier doit être une archive d’application et Python suppose que l’objet fichier est positionné au début de l’archive.
L’argument target détermine où l’archive résultante sera écrite :
If it is the name of a file, or a
pathlb.Path
object, the archive will be written to that file.S’il s’agit d’un objet fichier ouvert, l’archive sera écrite dans cet objet fichier, qui doit être ouvert pour l’écriture en mode octets.
Si la cible est omise (ou
None
), la source doit être un répertoire et la cible sera un fichier portant le même nom que la source, avec une extension.pyz
ajoutée.
L’argument interpreter spécifie le nom de l’interpréteur Python avec lequel l’archive sera exécutée. Il est écrit dans une ligne shebang au début de l’archive. Sur un système POSIX, cela est interprété par le système d’exploitation et, sur Windows, il sera géré par le lanceur Python. L’omission de l”interpreter n’entraîne pas l’écriture d’une ligne shebang. Si un interpréteur est spécifié et que la cible est un nom de fichier, le bit exécutable du fichier cible sera mis à 1.
L’argument main spécifie le nom d’un appelable, utilisé comme programme principal pour l’archive. Il ne peut être spécifié que si la source est un répertoire et si la source ne contient pas déjà un fichier
__main__.py
. L’argument main doit prendre la formepkg.module:callable
et l’archive sera exécutée en importantpkg.module
et en exécutant l’appelable donné sans argument. Omettre main est une erreur si la source est un répertoire et ne contient pas un fichier__main__.py
car, dans ce cas, l’archive résultante ne serait pas exécutable.Si un objet fichier est spécifié pour source ou target, il est de la responsabilité de l’appelant de le fermer après avoir appelé
create_archive
.Lors de la copie d’une archive existante, les objets fichier fournis n’ont besoin que des méthodes
read
etreadline
ouwrite
. Lors de la création d’une archive à partir d’un répertoire, si la cible est un objet fichier, elle sera passée à la classezipfile.ZipFile
et devra fournir les méthodes nécessaires à cette classe.
-
zipapp.
get_interpreter
(archive)¶ Renvoie l’interpréteur spécifié dans la ligne
#!
au début de l’archive. S’il n’y a pas de ligne#!
, renvoieNone
. L’argument archive peut être un nom de fichier ou un objet de type fichier ouvert à la lecture en mode binaire. Python suppose qu’il est au début de l’archive.
28.4.4. Exemples¶
Regroupe le contenu d’un répertoire dans une archive, puis l’exécute.
$ python -m zipapp myapp
$ python myapp.pyz
<output from myapp>
The same can be done using the create_archive()
functon:
>>> import zipapp
>>> zipapp.create_archive('myapp.pyz', 'myapp')
Pour rendre l’application directement exécutable sur un système POSIX, spécifiez un interpréteur à utiliser.
$ python -m zipapp myapp -p "/usr/bin/env python"
$ ./myapp.pyz
<output from myapp>
Pour remplacer la ligne shebang sur une archive existante, créez une archive modifiée en utilisant la fonction create_archive()
:
>>> import zipapp
>>> zipapp.create_archive('old_archive.pyz', 'new_archive.pyz', '/usr/bin/python3')
Pour mettre à jour le fichier sans créer de copie locale, effectuez le remplacement en mémoire à l’aide d’un objet BytesIO
, puis écrasez la source par la suite. Notez qu’il y a un risque lors de l’écrasement d’un fichier local qu’une erreur entraîne la perte du fichier original. Ce code ne protège pas contre de telles erreurs, assurez-vous de prendre les mesures nécessaires en production. De plus, cette méthode ne fonctionnera que si l’archive tient en mémoire :
>>> import zipapp
>>> import io
>>> temp = io.BytesIO()
>>> zipapp.create_archive('myapp.pyz', temp, '/usr/bin/python2')
>>> with open('myapp.pyz', 'wb') as f:
>>> f.write(temp.getvalue())
28.4.5. Spécification de l’interprète¶
Notez que si vous spécifiez un interpréteur et que vous distribuez ensuite votre archive d’application, vous devez vous assurer que l’interpréteur utilisé est portable. Le lanceur Python pour Windows gère la plupart des formes courantes de la ligne POSIX #!
, mais il y a d’autres problèmes à considérer :
Si vous utilisez
/usr/bin/env python
(ou d’autres formes de la commande python, comme/usr/bin/python
), vous devez considérer que vos utilisateurs peuvent avoir Python 2 ou Python 3 par défaut, et écrire votre code pour fonctionner dans les deux versions.Si vous utilisez une version explicite, par exemple
/usr/bin/env python3
votre application ne fonctionnera pas pour les utilisateurs qui n’ont pas cette version. (C’est peut-être ce que vous voulez si vous n’avez pas rendu votre code compatible Python 2).Il n’y a aucun moyen de dire « python X.Y ou supérieur » donc faites attention si vous utilisez une version exacte comme
/usr/bin/env python3.4
car vous devrez changer votre ligne shebang pour les utilisateurs de Python 3.5, par exemple.
Normalement, vous devriez utiliser un /usr/bin/env python2
ou /usr/bin/env python3
, selon que votre code soit écrit pour Python 2 ou 3.
28.4.6. Création d’applications autonomes avec zipapp¶
En utilisant le module zipapp
, il est possible de créer des programmes Python qui peuvent être distribués à des utilisateurs finaux dont le seul pré-requis est d’avoir la bonne version de Python installée sur leur ordinateur. Pour y arriver, la clé est de regrouper toutes les dépendances de l’application dans l’archive avec le code source de l’application.
Les étapes pour créer une archive autonome sont les suivantes :
Créez votre application dans un répertoire comme d’habitude, de manière à avoir un répertoire
myapp
contenant un fichier__main__.py
et tout le code de l’application correspondante.Installez toutes les dépendances de votre application dans le répertoire
myapp
en utilisant pip :$ python -m pip install -r requirements.txt --target myapp
(ceci suppose que vous ayez vos dépendances de projet dans un fichier
requirements.txt
— sinon vous pouvez simplement lister les dépendances manuellement sur la ligne de commande pip).Si nécessaire, supprimez les répertoires
.dist-info
créés par pip dans le répertoiremyapp
. Ceux-ci contiennent des métadonnées pour pip afin de gérer les paquets et, comme vous n’utiliserez plus pip, ils ne sont pas nécessaires (c’est sans conséquence si vous les laissez).Regroupez le tout à l’aide de :
$ python -m zipapp -p "interpreter" myapp
Cela produira un exécutable autonome qui peut être exécuté sur n’importe quelle machine avec l’interpréteur approprié disponible. Voir Spécification de l’interprète pour plus de détails. Il peut être envoyé aux utilisateurs sous la forme d’un seul fichier.
Sous Unix, le fichier myapp.pyz
est exécutable tel quel. Vous pouvez renommer le fichier pour supprimer l’extension .pyz
si vous préférez un nom de commande « simple ». Sous Windows, le fichier myapp.pyz[w]
est exécutable en vertu du fait que l’interpréteur Python est associé aux extensions de fichier .pyz
et .pyzw
une fois installé.
28.4.6.1. Création d’un exécutable Windows¶
Sous Windows, l’association de Python à l’extension .pyz
est facultative et, de plus, il y a certains mécanismes qui ne reconnaissent pas les extensions enregistrées de manière « transparente » (l’exemple le plus simple est que subprocess.run(['myapp'])
ne trouvera pas votre application — vous devez explicitement spécifier l’extension).
Sous Windows, il est donc souvent préférable de créer un exécutable à partir du zipapp. C’est relativement facile bien que cela nécessite un compilateur C. L’astuce repose sur le fait que les fichiers zip peuvent avoir des données arbitraires au début et les fichiers exe de Windows peuvent avoir des données arbitraires à la fin. Ainsi, en créant un lanceur approprié et en rajoutant le fichier .pyz
à sa fin, vous obtenez un fichier unique qui exécute votre application.
Un lanceur approprié peut être aussi simple que ce qui suit :
#define Py_LIMITED_API 1
#include "Python.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifdef WINDOWS
int WINAPI wWinMain(
HINSTANCE hInstance, /* handle to current instance */
HINSTANCE hPrevInstance, /* handle to previous instance */
LPWSTR lpCmdLine, /* pointer to command line */
int nCmdShow /* show state of window */
)
#else
int wmain()
#endif
{
wchar_t **myargv = _alloca((__argc + 1) * sizeof(wchar_t*));
myargv[0] = __wargv[0];
memcpy(myargv + 1, __wargv, __argc * sizeof(wchar_t *));
return Py_Main(__argc+1, myargv);
}
Si vous définissez le symbole du préprocesseur WINDOWS
cela va générer un exécutable IUG, et sans lui, un exécutable console.
Pour compiler l’exécutable, vous pouvez soit simplement utiliser les outils standards en ligne de commande MSVC, soit profiter du fait que distutils sait comment compiler les sources Python :
>>> from distutils.ccompiler import new_compiler
>>> import distutils.sysconfig
>>> import sys
>>> import os
>>> from pathlib import Path
>>> def compile(src):
>>> src = Path(src)
>>> cc = new_compiler()
>>> exe = src.stem
>>> cc.add_include_dir(distutils.sysconfig.get_python_inc())
>>> cc.add_library_dir(os.path.join(sys.base_exec_prefix, 'libs'))
>>> # First the CLI executable
>>> objs = cc.compile([str(src)])
>>> cc.link_executable(objs, exe)
>>> # Now the GUI executable
>>> cc.define_macro('WINDOWS')
>>> objs = cc.compile([str(src)])
>>> cc.link_executable(objs, exe + 'w')
>>> if __name__ == "__main__":
>>> compile("zastub.c")
Le lanceur résultant utilise le « Limited ABI » donc il fonctionnera sans changement avec n’importe quelle version de Python 3.x. Tout ce dont il a besoin est que Python (python3.dll
) soit sur le PATH
de l’utilisateur.
Pour une distribution entièrement autonome vous pouvez distribuer le lanceur avec votre application en fin de fichier, empaqueté avec la distribution embedded Python. Ceci fonctionnera sur n’importe quel ordinateur avec l’architecture appropriée (32 bits ou 64 bits).
28.4.6.2. Mises en garde¶
Il y a certaines limites à l’empaquetage de votre application dans un seul fichier. Dans la plupart des cas, si ce n’est tous, elles peuvent être traitées sans qu’il soit nécessaire d’apporter de modifications majeures à votre application.
Si votre application dépend d’un paquet qui inclut une extension C, ce paquet ne peut pas être exécuté à partir d’un fichier zip (c’est une limitation du système d’exploitation, car le code exécutable doit être présent dans le système de fichiers pour que le lanceur de l’OS puisse le charger). Dans ce cas, vous pouvez exclure cette dépendance du fichier zip et, soit demander à vos utilisateurs de l’installer, soit la fournir avec votre fichier zip et ajouter du code à votre fichier
__main__.py
pour inclure le répertoire contenant le module décompressé danssys.path
. Dans ce cas, vous devrez vous assurer d’envoyer les binaires appropriés pour votre ou vos architecture(s) cible(s) (et éventuellement choisir la bonne version à ajouter àsys.path
au moment de l’exécution, basée sur la machine de l’utilisateur).Si vous livrez un exécutable Windows comme décrit ci-dessus, vous devez vous assurer que vos utilisateurs ont
python3.dll
sur leur PATH (ce qui n’est pas le comportement par défaut de l’installateur) ou vous devez inclure la distribution intégrée dans votre application.Le lanceur suggéré ci-dessus utilise l’API d’intégration Python. Cela signifie que dans votre application
sys.executable
sera votre application et pas un interpréteur Python classique. Votre code et ses dépendances doivent être préparés à cette possibilité. Par exemple, si votre application utilise le modulemultiprocessing
, elle devra appelermultiprocessing.set_executable()
pour que le module sache où trouver l’interpréteur Python standard.
28.4.7. Le format d’archive d’application Zip Python¶
Python est capable d’exécuter des fichiers zip qui contiennent un fichier __main__.py
depuis la version 2.6. Pour être exécutée par Python, une archive d’application doit simplement être un fichier zip standard contenant un fichier __main__.py
qui sera exécuté comme point d’entrée de l’application. Comme d’habitude pour tout script Python, le parent du script (dans ce cas le fichier zip) sera placé sur sys.path
et ainsi d’autres modules pourront être importés depuis le fichier zip.
Le format de fichier zip permet d’ajouter des données arbitraires à un fichier zip. Le format de l’application zip utilise cette possibilité pour préfixer une ligne shebang POSIX standard dans le fichier (#!/path/to/interpreter
).
Formellement, le format d’application zip de Python est donc :
Une ligne shebang facultative, contenant les caractères
b'#!
suivis d’un nom d’interpréteur, puis un caractère fin de ligne (b'\n'
). Le nom de l’interpréteur peut être n’importe quoi acceptable pour le traitement shebang de l’OS, ou le lanceur Python sous Windows. L’interpréteur doit être encodé en UTF-8 sous Windows, et ensys.getfilesystemencoding()
sur POSIX.Des données zipfile standards, telles que générées par le module
zipfile
. Le contenu du fichier zip doit inclure un fichier appelé__main__.py
(qui doit se trouver à la racine du fichier zip — c’est-à-dire qu’il ne peut se trouver dans un sous-répertoire). Les données du fichier zip peuvent être compressées ou non.
Si une archive d’application a une ligne shebang, elle peut avoir le bit exécutable activé sur les systèmes POSIX, pour lui permettre d’être exécutée directement.
Vous pouvez créer des archives d’applications sans utiliser les outils de ce module — le module existe pour faciliter les choses, mais les archives, créées par n’importe quel moyen tout en respectant le format ci-dessus, sont valides pour Python.