Определение пути к приложению в исполняемом файле Python, сгенерированном PyInstaller
-
03-07-2019 - |
Вопрос
У меня есть приложение, которое находится в одном файле .py.Мне удалось заставить PyInstaller успешно объединить его в EXE-файл для Windows.Проблема в том, что приложению требуется файл .cfg, который всегда находится непосредственно рядом с приложением в том же каталоге.
Обычно я создаю путь, используя следующий код:
import os
config_name = 'myapp.cfg'
config_path = os.path.join(sys.path[0], config_name)
Однако, похоже, что sys.path пуст, когда он вызывается из EXE-файла, сгенерированного PyInstaller.Такое же поведение возникает, когда вы запускаете интерактивную командную строку python и пытаетесь получить sys.path[0].
Есть ли более конкретный способ получить путь к текущему запущенному приложению, чтобы я мог найти файлы, относящиеся к нему?
Решение
Я нашел решение.Вам нужно проверить, запущено ли приложение как скрипт или как замороженный exe-файл:
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)
Другие советы
В соответствии с Документация что касается PyInstaller, то предлагаемый метод восстановления пути к приложению заключается в следующем:
#!/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__))
Протестировано для PyInstaller версии v3.2, но это, безусловно, работало и для более ранних версий.
Решение Soviut не работает, по крайней мере, в целом для последних версий PyInstaller (обратите внимание, что OP много лет).Например, в macOS при объединении приложения в пакет из одного файла, sys.executable
указывает только на расположение встроенного архива, который является нет местоположение, в котором приложение фактически запускается после того, как загрузчик PyInstaller создал временную среду приложения.Только sys._MEIPASS
правильно указывает на это местоположение.Обратитесь к эта страница документа для получения дополнительной информации о том, как работает PyInstaller.
Я немного сократил код.
import os, sys
if getattr(sys, 'frozen', False):
application_path = os.path.dirname(sys.executable)
os.chdir(application_path)
logging.debug('CWD: ' + os.getcwd())
Но, sys._MEIPASS
указал не на тот каталог.Я думаю, что это тоже необходимо sys._MEIPASS
+ \app_name
os.path.dirname(sys.argv[0])
У меня это работает.
__file__
работает из командной строки с помощью исполняемого файла python.Он также дает имя файла скрипта без фактического пути в замороженном режиме.Однако это выдает ошибку в интерактивном режиме.
Следующее будет работать для всех трех режимов:
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)
Вывод в трех различных режимах:
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>
Здесь много ответов, но я обнаружил, что это решение работает в большинстве ситуаций:
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)