Comment utiliser PyInstaller pour créer des exécutables Python

Python, aussi puissant et polyvalent soit-il, manque de quelques fonctionnalités clés prêtes à l'emploi. D'une part, Python ne fournit aucun mécanisme natif pour compiler un programme Python dans un package exécutable autonome.

Pour être honnête, le cas d'utilisation d'origine de Python n'a jamais appelé de packages autonomes. Les programmes Python ont, dans l'ensemble, été exécutés sur place sur des systèmes où vivait une copie de l'interpréteur Python. Mais la popularité croissante de Python a créé une plus grande demande pour l'exécution d'applications Python sur des systèmes sans runtime Python installé.

Plusieurs tiers ont conçu des solutions pour déployer des applications Python autonomes. La solution la plus populaire et la plus mature est PyInstaller. PyInstaller ne rend pas le processus d'empaquetage d'une application Python totalement indolore, mais cela va très loin.

Dans cet article, nous explorerons les bases de l'utilisation de PyInstaller, y compris le fonctionnement de PyInstaller, comment utiliser PyInstaller pour créer un exécutable Python autonome, comment affiner les exécutables Python que vous créez et comment éviter certains des pièges courants. avec l'utilisation de PyInstaller.

Création d'un package PyInstaller

PyInstaller est un package Python, installé avec pip( pip install pyinstaller). PyInstaller peut être installé dans votre installation Python par défaut, mais il est préférable de créer un environnement virtuel pour le projet que vous souhaitez empaqueter et d'y installer PyInstaller.

PyInstaller fonctionne en lisant votre programme Python, en analysant toutes les importations qu'il effectue et en regroupant des copies de ces importations avec votre programme. PyInstaller lit votre programme à partir de son point d'entrée. Par exemple, si le point d'entrée de votre programme est myapp.py, vous exécutez pyinstaller myapp.pypour effectuer l'analyse. PyInstaller peut détecter et conditionner automatiquement de nombreux packages Python courants, tels que NumPy, mais vous devrez peut-être fournir des conseils dans certains cas. (Plus à ce sujet plus tard.)

Après avoir analysé votre code et découvert toutes les bibliothèques et modules qu'il utilise, PyInstaller génère alors un «fichier de spécifications». Un script Python avec l'extension .spec, ce fichier comprend des détails sur la façon dont votre application Python doit être compressée. La première fois que vous exécutez PyInstaller sur votre application, PyInstaller générera un fichier de spécifications à partir de zéro et le remplira avec des valeurs par défaut saines. Ne jetez pas ce fichier; c'est la clé pour affiner un déploiement PyInstaller!

Enfin, PyInstaller tente de produire un exécutable à partir de l'application, livré avec toutes ses dépendances. Une fois terminé, un sous-dossier nommé dist (par défaut; vous êtes libre de spécifier un autre nom) apparaîtra dans le répertoire du projet. Celui-ci contient à son tour un répertoire qui est votre application fournie - il a un .exefichier à exécuter, ainsi que toutes les bibliothèques et autres fichiers supplémentaires requis.

Tout ce que vous avez à faire pour distribuer votre programme est alors de créer un package dans ce répertoire sous forme de .zipfichier ou d'un autre bundle. Le bundle devra généralement être extrait dans un répertoire où l'utilisateur dispose des autorisations d'écriture pour pouvoir s'exécuter.

Test d'un package PyInstaller

Il y a de fortes chances que votre première tentative d'utilisation de PyInstaller pour empaqueter une application ne réussisse pas complètement.

Pour vérifier si votre package PyInstaller fonctionne, accédez au répertoire contenant l'exécutable fourni et exécutez le .exefichier à partir de la ligne de commande. S'il ne s'exécute pas, les erreurs que vous verrez imprimées sur la ligne de commande devraient fournir un indice sur ce qui ne va pas.

La raison la plus courante pour laquelle un package PyInstaller échoue est que PyInstaller n'a pas réussi à regrouper un fichier requis. Ces fichiers manquants appartiennent à quelques catégories:

  • Importations masquées ou manquantes : Parfois, PyInstaller ne peut pas détecter l'importation d'un package ou d'une bibliothèque, généralement parce qu'il est importé dynamiquement. Le package ou la bibliothèque devra être spécifié manuellement.
  • Fichiers autonomes manquants : si le programme dépend de fichiers de données externes qui doivent être fournis avec le programme, PyInstaller n'a aucun moyen de le savoir. Vous devrez inclure manuellement les fichiers.
  • Binaires manquants : Là encore, si votre programme dépend d'un binaire externe comme un .DLL que PyInstaller ne peut pas détecter, vous devrez l'inclure manuellement.

La bonne nouvelle est que PyInstaller offre un moyen simple de résoudre les problèmes ci-dessus. Le .specfichier créé par PyInstaller comprend des champs que nous pouvons remplir pour fournir les détails manqués par PyInstaller.

Ouvrez le .specfichier dans un éditeur de texte et recherchez la définition de l' Analysisobjet. Plusieurs des paramètres transmis Analysissont des listes vides, mais ils peuvent être modifiés pour spécifier les détails manquants:

  • hiddenimportspour les importations masquées ou manquantes : ajoutez à cette liste une ou plusieurs chaînes avec les noms des bibliothèques que vous souhaitez inclure dans votre application. Si vous vouliez ajouter pandaset bokeh, par exemple, vous le spécifieriez comme  ['pandas','bokeh']. Notez que les bibliothèques en question doivent être installées dans la même instance de Python où vous exécutez PyInstaller.
  • dataspour les fichiers autonomes manquants : ajoutez ici une ou plusieurs spécifications pour les fichiers de l'arborescence de votre projet que vous souhaitez inclure dans votre projet. Chaque fichier doit être passé sous forme de tuple indiquant le chemin relatif du fichier dans le répertoire de votre projet et le chemin relatif dans le répertoire de distribution où vous souhaitez placer le fichier. Par exemple, si vous aviez un fichier ./models/mainmodel.datque vous souhaitiez inclure dans votre application et que vous souhaitiez le placer dans un sous-répertoire correspondant de votre répertoire de distribution, vous utiliseriez ('./models/mainmodel.dat','./models')comme une entrée dans la hiddenimportsliste. Notez que vous pouvez utiliser globdes caractères génériques -style pour spécifier plusieurs fichiers.
  • binariespour les binaires autonomes manquants : comme avec datas, vous pouvez utiliser binariespour transmettre une liste de tuples qui spécifient les emplacements des binaires dans l'arborescence du projet et leurs destinations dans le répertoire de distribution. Encore une fois, vous pouvez utiliser globdes caractères génériques -style.

Gardez à l'esprit que toutes les listes transmises à Analysispeuvent être générées par programme plus tôt dans le .specfichier. Après tout, le .specfichier n'est qu'un script Python portant un autre nom.

Après avoir apporté des modifications au .specfichier, réexécutez PyInstaller pour reconstruire le package. Cependant, à partir de maintenant, assurez-vous de passer le .specfichier modifié comme paramètre (par exemple pyinstaller myapp.spec). Testez l'exécutable comme auparavant. Si quelque chose est toujours cassé, vous pouvez rééditer le .specfichier et répéter le processus jusqu'à ce que tout fonctionne.

Enfin, lorsque vous êtes satisfait que tout fonctionne comme prévu, vous souhaiterez peut-être modifier le  .specfichier pour empêcher votre application packagée de présenter une fenêtre de ligne de commande lors de son lancement. Dans les EXEparamètres d'objet du .specfichier, définissez  console=False. La suppression de la console est utile si votre application dispose d'une interface graphique et que vous ne voulez pas qu'une fausse fenêtre de ligne de commande induise les utilisateurs en erreur. Bien sûr, ne modifiez pas ce paramètre si votre application nécessite une ligne de commande.

Affiner un package PyInstaller

Une fois que votre application est empaquetée avec PyInstaller et qu'elle fonctionne correctement, la prochaine chose que vous voudrez probablement faire est de la réduire un peu. Les packages PyInstaller ne sont pas connus pour être sveltes.

Parce que Python est un langage dynamique, il est difficile de prédire exactement ce qui sera nécessaire à l'exécution par un programme donné. Pour cette raison, lorsque PyInstaller détecte une importation de package, il inclut tout ce qui se trouve dans ce package, qu'il soit réellement utilisé ou non au moment de l'exécution par votre programme. 

Voici la bonne nouvelle. PyInstaller inclut un mécanisme pour exclure sélectivement des packages entiers ou des espaces de noms individuels dans des packages. Par exemple, disons que votre programme importe le package foo, qui inclut foo.baret foo.bip. Si vous savez pertinemment que votre programme utilise uniquement la logique in foo.bar, vous pouvez exclure foo.bip et économiser de l'espace en toute sécurité .

Pour ce faire, vous utilisez le excludesparamètre passé à l' Analysisobjet dans le .specfichier. Vous pouvez transmettre une liste de noms - modules de niveau supérieur ou espaces de noms en pointillés - à exclure de votre package. Par exemple, pour exclure foo.bip, vous devez simplement spécifier  ['foo.bip'].

Une exclusion courante que vous pouvez faire est tkinterla bibliothèque Python pour la création d'interfaces utilisateur graphiques multiplateformes simples. Par défaut,  tkinteret tous ses fichiers de support sont emballés avec un projet PyInstaller. Si vous n'utilisez pas tkinterdans votre projet, vous pouvez l'exclure en l'ajoutant 'tkinter'à la excludesliste. Omettre tkinterréduira la taille du package d'environ 7 Mo.

Les suites de tests sont une autre exclusion courante. Si un package importé par votre programme a une suite de tests, la suite de tests peut finir par être incluse dans votre package PyInstaller. Sauf si vous exécutez réellement la suite de tests dans votre programme déployé, vous pouvez l'exclure en toute sécurité.

Gardez à l'esprit que les packages créés à l'aide d'exclusions doivent être soigneusement testés avant d'être utilisés. Si vous finissez par exclure des fonctionnalités utilisées dans un scénario futur que vous n'aviez pas prévu, votre application sera interrompue.

Conseils de PyInstaller

  • Créez votre package PyInstaller sur le système d'exploitation sur lequel vous prévoyez de déployer.  PyInstaller ne prend pas en charge les versions multiplateformes. Si vous devez déployer votre application Python autonome sur les systèmes MacOS, Linux et Windows, vous devrez installer PyInstaller et créer des versions distinctes de l'application sur chacun de ces systèmes d'exploitation. 
  • Créez votre package PyInstaller au fur et à mesure que vous développez votre application.  Dès que vous savez que vous déploierez votre projet avec PyInstaller, créez votre .specfichier et commencez à affiner le package PyInstaller en parallèle avec le développement de votre application. De cette façon, vous pouvez ajouter des exclusions ou des inclusions au fur et à mesure et tester la façon dont les nouvelles fonctionnalités sont déployées avec l'application au fur et à mesure que vous les écrivez.
  • N'utilisez pas le --onefilemode PyInstaller  .  PyInstaller comprend un commutateur de ligne de commande, --onefilequi regroupe toute votre application dans un seul exécutable auto-extractible. Cela semble être une excellente idée - vous n'avez qu'à livrer un fichier! - mais il comporte quelques écueils. Chaque fois que vous exécutez l'application, elle doit d'abord décompresser tous les fichiers de l'exécutable dans un répertoire temporaire. Si l'application est volumineuse (200 Mo, par exemple), le déballage peut signifier un délai de plusieurs secondes. Utilisez à la place le mode de répertoire unique par défaut et regroupez tout simplement sous forme de .zipfichier.
  • Créez un programme d'installation pour votre application PyInstaller.  Si vous souhaitez déployer votre application autre qu'un fichier .zip, envisagez d'utiliser un utilitaire d'installation comme le système d'installation open source Nullsoft Scriptable. Il ajoute très peu de surcharge à la taille du livrable et vous permet de configurer de nombreux aspects du processus d'installation, comme la création de raccourcis vers votre exécutable.
  • Ne vous attendez pas à des accélérations.  PyInstaller est un  système d' empaquetage , pas un  compilateur  ou un  optimiseur . Le code fourni avec PyInstaller ne s'exécute pas plus rapidement qu'il ne le ferait lorsqu'il était exécuté sur le système d'origine. Si vous souhaitez accélérer le code Python, utilisez une bibliothèque accélérée C adaptée à la tâche, ou un projet comme Cython.

Comment faire plus avec Python

  • Tutoriel Cython: Comment accélérer Python
  • Comment installer Python de manière intelligente
  • Meilleure gestion de projet Python avec Poetry
  • Virtualenv et venv: les environnements virtuels Python expliqués
  • Python Virtualenv et Venv à faire et à ne pas faire
  • Explication des threads et sous-processus Python
  • Comment utiliser le débogueur Python
  • Comment utiliser timeit pour profiler le code Python
  • Comment utiliser cProfile pour profiler le code Python
  • Démarrez avec async en Python
  • Comment utiliser asyncio en Python
  • Comment convertir Python en JavaScript (et inversement)