【问题标题】:Determining application path in a Python EXE generated by pyInstaller在 pyInstaller 生成的 Python EXE 中确定应用程序路径
【发布时间】:2010-09-29 02:50:02
【问题描述】:

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

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

【问题讨论】:

标签: python executable relative-path pyinstaller


【解决方案1】:

我找到了解决方案。您需要检查应用程序是作为脚本运行还是作为冻结的 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 版本(3.0+),请参阅@normanius 回答。
  • 不应该只有else:而不是elif __file__:吗?
  • 对于喜欢使用 pathlib 的用户:bundle_dir = Path(sys.executable).parent 也可以查看hasattr(sys, '_MEIPASS')。 (应该是真的)
  • 这是唯一真正有效的解决方案,它很干净,而且它没有利用 sys._MEIPASS 愚蠢。
  • 另见@Max Tet 对 normanius 答案的评论。
【解决方案2】:

根据PyInstaller的documentation,建议的应用路径恢复方法如下:

#!/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 bootloader 已经创建了一个临时的应用环境。只有sys._MEIPASS 正确指向该位置。有关 PyInstaller 工作原理的更多信息,请参阅 this doc-page。

【讨论】:

  • 可以确认这适用于带有 PyInstaller 3.2 和 Python 3.4 的 Windows 10 和 Windows 7 -- 其他答案不再有效。
  • 只是根据文档添加,这适用于单文件和单文件夹分发
  • 使用sys._MEIPASS 不适用于单文件可执行文件。来自文档:For a one-folder bundle, this is the path to that folder, wherever the user may have put it. For a one-file bundle, this is the path to the _MEIxxxxxx temporary folder created by the bootloader 。将sys.executable 用于单文件可执行文件。
  • 对于 --onefile 可执行文件,应用程序的路径由application_path = os.path.dirname(sys.executable)给出
  • 好。现在我可以在生产中的其他文件夹中拥有 sqlite3,在“发布构建(exe)”中拥有其他文件夹。
【解决方案3】:

我稍微缩短了代码。

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

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:
      os.path.dirname(sys.argv[0])
      

      这对我有用。

      【讨论】:

      • 这只有在调用时使用可执行文件的绝对路径才有效。仅使用 exe 的名称并通过 PATH 找到会返回一个空路径。
      【解决方案6】:

      __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>
      

      【讨论】:

        【解决方案7】:

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

        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)
        

        【讨论】:

        • 对我来说,这会导致运行时路径,在 AppData\Local\Temp_MEI... 问题是关于 EXE 文件所在的文件夹。
        猜你喜欢
        • 2019-11-02
        • 1970-01-01
        • 2021-01-16
        • 2020-12-17
        • 1970-01-01
        • 2020-11-17
        • 1970-01-01
        相关资源
        最近更新 更多