简体   繁体   English

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

[英]Determining application path in a Python EXE generated by pyInstaller

I have an application that resides in a single.py file.我有一个驻留在 single.py 文件中的应用程序。 I've been able to get pyInstaller to bundle it successfully into an EXE for Windows. The problem is, the application requires a.cfg file that always sits directly beside the application in the same directory.我已经能够让 pyInstaller 成功地将它捆绑到 Windows 的 EXE 中。问题是,该应用程序需要一个 .cfg 文件,该文件始终直接位于同一目录中的应用程序旁边。

Normally, I build the path using the following code:通常,我使用以下代码构建路径:

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

However, it seems the sys.path is blank when its called from an EXE generated by pyInstaller.但是,从 pyInstaller 生成的 EXE 调用时,sys.path 似乎是空白的。 This same behaviour occurs when you run the python interactive command line and try to fetch sys.path[0].当您运行 python 交互式命令行并尝试获取 sys.path[0] 时,也会出现同样的行为。

Is there a more concrete way of getting the path of the currently running application so that I can find files that are relative to it?是否有更具体的方法来获取当前正在运行的应用程序的路径,以便我可以找到与其相关的文件?

I found a solution.我找到了解决方案。 You need to check if the application is running as a script or as a frozen exe:您需要检查应用程序是作为脚本运行还是作为冻结的 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)

According to the documentation of PyInstaller, the suggested method of recovering application path is as follows:根据 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__))

Tested for PyInstaller v3.2, but this certainly has been working for earlier versions as well.已针对 PyInstaller v3.2 进行了测试,但这当然也适用于早期版本。

Soviut's solution does not work, at least not in general for recent versions of pyInstaller (note that the OP is many years old). Soviut 的解决方案不起作用,至少对于最近版本的 pyInstaller 而言通常不起作用(请注意,OP 已有多年历史)。 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.例如,在 MacOS 上,将应用程序捆绑到一个文件包中时, sys.executable仅指向嵌入式存档的位置,这不是pyInstaller 引导加载程序创建临时应用程序后应用程序实际运行的位置环境。 Only sys._MEIPASS correctly points to that location.只有sys._MEIPASS正确指向该位置。 Refer to this doc-page for further information on how PyInstaller works.有关 PyInstaller 如何工作的更多信息,请参阅此文档页面

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.但是, sys._MEIPASS指向了错误的目录。 I think it also needs sys._MEIPASS + \\app_name我认为它还需要sys._MEIPASS + \\app_name

os.path.dirname(sys.argv[0])

这对我行得通。

__file__ works from command line with python executable. __file__从命令行使用 python 可执行文件工作。 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>

I'm surprised nobody has mentioned that getattr() has a built-in default argument which will be returned if the attribute doesn't exist.我很惊讶没有人提到getattr()有一个内置的默认参数,如果该属性不存在,它将被返回。 This can also be made a bit more readable with pathlib .使用pathlib也可以使这更具可读性。 This code works whether or not the code is bundled with PyInstaller.无论代码是否与 PyInstaller 捆绑在一起,此代码都有效。

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

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)

My case is using a service that runs an executable.exe build with pyinstaller .我的情况是使用运行 executable.exe 构建的服务pyinstaller I use os.path.dirname(**os.path.realpath(sys.executable)**)我使用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__)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Python Pyinstaller没有生成exe文件 - Python Pyinstaller no exe file generated 使用 python & kivy 在 pyinstaller 中生成的 exe 不起作用 - exe generated in pyinstaller using python & kivy is not working 将 R 与 python exe 应用程序捆绑在一起(使用 Pyinstaller) - Bundle R with a python exe application (using Pyinstaller) 您可以更改 pyinstaller 认为 python.exe 所在的路径吗? - Can you change the path pyinstaller thinks python.exe is in? 在 C# 控制台应用程序中运行 Python EXE(在 PyInstaller 中创建) - Run a Python EXE (created in PyInstaller) in C# Console Application Pyinstaller 在运行 .exe 时给出“'chromedriver.exe' 可执行文件需要在 PATH 中”,但实际的 python 脚本工作正常 - Pyinstaller gives “ 'chromedriver.exe' executable needs to be in PATH” when the .exe is ran, but the actual python script works fine pyinstaller生成的EXE文件在启动时崩溃 - EXE file generated with pyinstaller crashes on start 如何更新PyInstaller生成的exe细节? - How to update exe details generated by PyInstaller? pyinstaller 生成的 exe 无法按预期工作 - exe generated by pyinstaller doesn't work as expected 为什么我的 PyInstaller 生成的 .exe 无法启动? - Why does my .exe generated by PyInstaller not start?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM