Determinazione del percorso dell'applicazione in un file Python EXE generato da pyInstaller
-
03-07-2019 - |
Domanda
Ho un'applicazione che risiede in un singolo file .py. Sono stato in grado di ottenere pyInstaller per raggrupparlo con successo in un EXE per Windows. Il problema è che l'applicazione richiede un file .cfg che si trova sempre accanto all'applicazione nella stessa directory.
Normalmente, costruisco il percorso usando il seguente codice:
import os
config_name = 'myapp.cfg'
config_path = os.path.join(sys.path[0], config_name)
Tuttavia, sembra che sys.path sia vuoto quando viene chiamato da un EXE generato da pyInstaller. Questo stesso comportamento si verifica quando si esegue la riga di comando interattiva di Python e si tenta di recuperare sys.path [0].
Esiste un modo più concreto per ottenere il percorso dell'applicazione attualmente in esecuzione in modo da poter trovare i file relativi ad essa?
Soluzione
Ho trovato una soluzione. Devi verificare se l'applicazione è in esecuzione come script o come exe congelato:
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)
Altri suggerimenti
Secondo la documentazione di pyInstaller, il metodo suggerito del recupero del percorso dell'applicazione è il seguente:
#!/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__))
Testato per pyInstaller v3.2, ma sicuramente ha funzionato anche per le versioni precedenti.
La soluzione di Soviut non funziona, almeno non in generale per le recenti versioni di pyInstaller (si noti che l'OP ha molti anni). Ad esempio, su MacOS, quando si raggruppa un'applicazione in un pacchetto di un file, sys.executable
punta solo alla posizione dell'archivio incorporato, che è non la posizione dove l'applicazione viene effettivamente eseguita dopo che il bootloader pyInstaller ha creato un ambiente applicativo temporaneo. Solo sys._MEIPASS
punta correttamente a quella posizione. Fare riferimento a questa pagina dei documenti per ulteriori informazioni su come funziona pyInstaller.
Ho abbreviato un po 'il codice.
import os, sys
if getattr(sys, 'frozen', False):
application_path = os.path.dirname(sys.executable)
os.chdir(application_path)
logging.debug('CWD: ' + os.getcwd())
Ma sys._MEIPASS
indicava una directory sbagliata. Penso che abbia bisogno anche di sys._MEIPASS
+ \app_name
os.path.dirname(sys.argv[0])
Questo funziona per me.
__file__
funziona dalla riga di comando con eseguibile python. Fornisce inoltre il nome del file di script senza percorso effettivo in modalità bloccata. Tuttavia, genera un errore in modalità interattiva.
Quanto segue funzionerà per tutte e tre le modalità:
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)
Output in tre diverse modalità:
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>
Molte risposte qui, ma ho scoperto che questa soluzione funziona nella maggior parte dei casi:
import os
import sys
import os.path as op
try:
this_file = __file__
except NameError:
this_file = sys.argv[0]
this_file = op.abspath(this_file)
if getattr(sys, 'frozen', False):
application_path = getattr(sys, '_MEIPASS', op.dirname(sys.executable))
else:
application_path = op.dirname(this_file)