114 votes

Détermination du chemin d'accès à l'application dans un EXE Python généré par pyInstaller

J'ai une application qui réside dans un seul fichier .py. J'ai réussi à faire en sorte que pyInstaller l'intègre avec succès dans un EXE pour Windows. Le problème est que l'application nécessite un fichier .cfg qui se trouve toujours directement à côté de l'application dans le même répertoire.

Normalement, je construis le chemin en utilisant le code suivant :

import os
config_name = 'myapp.cfg'
config_path = os.path.join(sys.path[0], config_name)

Cependant, il semble que le sys.path soit vide lorsqu'il est appelé à partir d'un EXE généré par pyInstaller. Ce même comportement se produit lorsque vous exécutez la ligne de commande interactive de python et que vous essayez de récupérer sys.path[0].

Existe-t-il un moyen plus concret d'obtenir le chemin d'accès de l'application en cours d'exécution afin de pouvoir trouver les fichiers qui lui sont relatifs ?

166voto

Soviut Points 26384

J'ai trouvé une solution. Vous devez vérifier si l'application fonctionne comme un script ou comme un exe gelé :

import os
import sys

config_name = 'myapp.cfg'

# determine if application is a script file or frozen exe
if getattr(sys, 'frozen', False):
    application_path = os.path.dirname(sys.executable)
elif __file__:
    application_path = os.path.dirname(__file__)

config_path = os.path.join(application_path, config_name)

92voto

normanius Points 118

Según la la documentation de PyInstaller, la méthode proposée pour récupérer le chemin d'accès à l'application est la suivante :

#!/usr/bin/python3
import sys, os
if getattr(sys, 'frozen', False):
    # If the application is run as a bundle, the PyInstaller bootloader
    # extends the sys module by a flag frozen=True and sets the app 
    # path into variable _MEIPASS'.
    application_path = sys._MEIPASS
else:
    application_path = os.path.dirname(os.path.abspath(__file__))

Testé pour PyInstaller v3.2, mais cela a certainement fonctionné pour des versions antérieures également.

La solution de Soviut ne fonctionne pas, du moins pas en général pour les versions récentes de pyInstaller (notez que l'OP date de plusieurs années). Par exemple, sur MacOS, lorsqu'on regroupe une application dans un fichier unique, sys.executable pointe uniquement sur l'emplacement de l'archive incorporée, qui est no l'emplacement où l'application s'exécute réellement après que le chargeur de démarrage pyInstaller a créé un environnement temporaire pour l'application. Seulement sys._MEIPASS pointe correctement vers cet endroit. Se référer à cette page du document pour plus d'informations sur le fonctionnement de PyInstaller.

12voto

Polv Points 130

J'ai un peu raccourci le code.

import os, sys

if getattr(sys, 'frozen', False):
    application_path = os.path.dirname(sys.executable)
    os.chdir(application_path)

logging.debug('CWD: ' + os.getcwd())

Mais.., sys._MEIPASS a pointé vers un mauvais répertoire. Je pense qu'il faut aussi sys._MEIPASS + \app_name

7voto

Elliott B Points 269

Je suis surpris que personne ne l'ait mentionné getattr() possède un argument par défaut intégré qui sera retourné si l'attribut n'existe pas. Ceci peut également être rendu un peu plus lisible avec pathlib . Ce code fonctionne, qu'il soit ou non fourni avec PyInstaller.

from pathlib import Path
bundle_dir = Path(getattr(sys, '_MEIPASS', Path.cwd()))
config_path = bundle_dir / 'myapp.cfg'

6voto

Rafiq Points 136

__file__ fonctionne à partir de la ligne de commande avec l'exécutable python. Il donne également le nom du fichier script sans le chemin réel en mode gelé. Cependant, il donne une erreur en mode interactif.

La procédure suivante s'applique aux trois modes :

import sys,os

config_name = 'myapp.cfg'

if getattr(sys, 'frozen', False):
    application_path = os.path.dirname(sys.executable)
    running_mode = 'Frozen/executable'
else:
    try:
        app_full_path = os.path.realpath(__file__)
        application_path = os.path.dirname(app_full_path)
        running_mode = "Non-interactive (e.g. 'python myapp.py')"
    except NameError:
        application_path = os.getcwd()
        running_mode = 'Interactive'

config_full_path = os.path.join(application_path, config_name)

print('Running mode:', running_mode)
print('  Appliction path  :', application_path)
print('  Config full path :', config_full_path)

Sortie en trois modes différents :

Running mode: Interactive
  Appliction path  : C:\Projects\MyAppDir
  Config full path : C:\Projects\MyAppDir\myapp.cfg

C:\Projects\MyAppDir>myapp.exe
Running mode: Frozen/executable
  Appliction path  : C:\Program Files\myapp
  Config full path : C:\Program Files\myapp\myapp.cfg

C:\Projects\MyAppDir>python myapp.py
Running mode: Non-interactive (e.g. 'python myapp.py')
  Appliction path  : C:\Projects\MyAppDir
  Config full path : C:\Projects\MyAppDir\myapp.cfg

C:\Projects\MyAppDir>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X