在由 pyInstaller 生成的 Python EXE 中确定应用程序路径

我有一个应用程序驻留在一个。Py 文件。我已经能够让 pyInstaller 成功地将它捆绑到 Windows 的 EXE 中。问题是,应用程序需要一个。Cfg 文件,该文件总是直接位于同一目录中的应用程序旁边。

通常,我使用以下代码构建路径:

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

但是,当从 pyInstaller 生成的 EXE 调用 sys.path 时,它似乎是空白的。当您运行 python 交互式命令行并尝试获取 sys.path [0]时,也会发生同样的行为。

是否有更具体的方法来获取当前运行的应用程序的路径,以便我可以找到与它相关的文件?

99699 次浏览

I found a solution. You need to check if the application is running as a script or as a frozen 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)
os.path.dirname(sys.argv[0])

That works for me.

According to the documentation of PyInstaller, the suggested method of recovering application path is as follows:

#!/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__))

Tested for PyInstaller v3.2, but this certainly has been working for earlier versions as well.

Soviut's solution does not work, at least not in general for recent versions of pyInstaller (note that the OP is many years old). For instance, on MacOS, when bundling an application into a one-file-bundle, sys.executable points only to the location of the embedded archive, which is not the location where the application actually runs after the pyInstaller bootloader has created a temporary application environment. Only sys._MEIPASS correctly points to that location. Refer to this doc-page for further information on how PyInstaller works.

I shortened the code a bit.

import os, sys


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


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

But, sys._MEIPASS pointed to a wrong directory. I think it also needs sys._MEIPASS + \app_name

__file__ works from command line with python executable. It also gives the script file name without actual path in frozen mode. However it gives error in interactive mode.

The following will work for all three 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)

Output in three different modes:

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>

Many answers here but I found this solution works in most situations:

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)

I'm surprised nobody has mentioned that getattr() has a built-in default argument which will be returned if the attribute doesn't exist. This can also be made a bit more readable with pathlib. This code works whether or not the code is bundled with PyInstaller.

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

My case is using a service that runs an executable .exe build with pyinstaller. I use os.path.dirname(**os.path.realpath(sys.executable)**)

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