94

我有一个驻留在单个 .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] 时,会发生同样的行为。

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

4

7 回答 7

147

我找到了解决方案。您需要检查应用程序是作为脚本运行还是作为冻结的 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)
于 2009-01-01T08:53:20.173 回答
70

根据 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 如何工作的更多信息,请参阅此文档页面。

于 2017-03-06T00:00:11.660 回答
9

我稍微缩短了代码。

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

于 2017-12-21T06:47:45.280 回答
5

我很惊讶没有人提到getattr()具有内置默认参数,如果该属性不存在,该参数将被返回。使用pathlib也可以使其更具可读性。无论代码是否与 PyInstaller 捆绑在一起,此代码都有效。

from pathlib import Path
bundle_dir = Path(getattr(sys, '_MEIPASS', Path.cwd()))
config_path = bundle_dir / 'myapp.cfg'
于 2020-09-04T00:30:38.133 回答
4
os.path.dirname(sys.argv[0])

这对我行得通。

于 2013-01-17T05:31:57.197 回答
4

__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>
于 2018-02-15T22:13:19.593 回答
1

这里有很多答案,但我发现这个解决方案在大多数情况下都有效:

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)
于 2018-04-26T03:27:17.753 回答