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 forme pkg.module:callable et l’archive sera exécutée en important pkg.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 et readline ou write. 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 classe zipfile.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 #!, renvoie None. 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())

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.

28.4.5. 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 :

  1. 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 en sys.getfilesystemencoding() sur POSIX.
  2. 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.