Pyinstaller 与相对导入
我正在尝试使用 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 个回答
在我的情况下,我使用的是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
扩展 PYTHONPATH
到 dirA
,而不是 dirB
。
我找不到方法让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