Pyinstaller 与相对导入

18 投票
2 回答
10828 浏览
提问于 2025-04-17 22:40

我正在尝试使用 Pyinstaller 2.1 为 Windows 7 创建一个可执行文件。这个模块使用相对导入,因为这个包通常在 Linux 上作为一个“常规”的 Python 包使用。请问有没有办法创建一个 spec 文件来处理这种设置?理想情况下,我希望能有一个 Python 包,可以用 Pyinstaller 制作 Windows 的 exe,同时在 Linux/OS X 上也能作为一个“常规”的 pip 可安装的 Python 包。

我在想,也许可以使用隐藏导入之类的方法来实现这个目标。

我尝试使用默认的 Pyinstaller 设置,并指向我的“主” Python 脚本。结果生成的 exe 出现了以下错误:

‘在非包中尝试相对导入’

这很有道理,因为我把 Pyinstaller 指向了包中的 main.py 文件,而 Pyinstaller 并没有识别我的整个包。这只是从命令行使用这个模块的起点。不过,你也可以在自己的代码中导入并使用它。

附带说明:

这个包需要 numpy 和 scipy。是的,我知道有很多好的方法可以在 Windows 上通过 Anaconda 等工具让它们工作。然而,由于一些历史原因,我现在被迫使用 exe 设置。

2 个回答

1

在我的情况下,我使用的是MacOS 12.x,我有一些相对导入的代码,类似于这个

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from utils import *

我的文件结构是这样的

dirA
 utils.py
 dirB
  my_app.py

但是生成的 .app 文件却无法正常工作。问题出在我是在 dirB 目录下运行 pyinstaller,而在这个目录中,pyinstaller 找不到 utils.py 模块。解决办法是指定 my_app.py 导入模块时的路径。这可以通过 pyinstaller-p 选项来明确指定,或者通过在 dirA 目录下运行 pyinstaller,并在 dirB 中添加一个空的 __init__.py 文件来隐式实现。添加 __init__.py 文件会“强制” pyinstaller 扩展 PYTHONPATHdirA,而不是 dirB

27

我找不到方法让Pyinstaller做到这一点。不过,我觉得这不是Pyinstaller的问题。更主要的是我打包的方式有点问题。

我之前是把一个属于我包的脚本传给Pyinstaller。其实更好的做法是提供一个简单的Python脚本,放在包外面,作为包的命令行界面(cli)前端。

举个例子,假设包的结构是这样的(假设文件使用相对导入):

repo_dir/
    setup.py
    README.md
    package_a/
        main.py
        support_module.py
        __init__.py

我之前的尝试是直接把main.py传给Pyinstaller,这导致了上面提到的错误。

不过,后来我添加了一个cli.py脚本,做了类似这样的事情:

from package_a.main import main
if __name__ == '__main__':
    main()

现在,我可以把cli.py传给Pyinstaller,而我的相对导入只在包内部使用。所以,一切都正常了。这里有一个可以参考的目录结构示例:

repo_dir/
    setup.py
    cli.py
    README.md
    package_a/
        main.py
        support_module.py
        __init__.py

撰写回答