Pyinstaller打包Python程序以及常见bugs

Pyinstaller打包Python程序以及常见bugs

Pyinstaller可以将Python脚本打包成独立的可执行程序,支持多平台Windows,Linux和Mac OS X等。

用pip安装pyinstaller:

pip install pyinstaller

使用方法非常简单:

pyinstaller myscript.py

这会产生两个文件夹,build和dist,build保存着打包前的配置文件和完成后的结果信息,最终的打包程序位于dist中。

常用参数:

-h, --help

查看帮助

-y, --noconfirm

不询问

-n NAME, --name NAME

打包后文件名

--distpath DIR

指定打包位置

--add-data <SRC;DEST or SRC:DEST>

添加数据文件

--add-binary <SRC;DEST or SRC:DEST>

添加二进制文件

--hidden-import MODULENAME, --hiddenimport MODULENAME

打包隐藏的模块

-w, --windowed, --noconsole

有GUI的程序可以不提供命令行界面

--version-file FILE

加入版本文件

更多用法请见Using PyInstaller

Pyinstaller在打包时会将尽可能地收集代码相关的依赖,包括python解释器,最后得到的软件包,其他用户无需安装其他依赖也能使用。但这同时也意味着打包出来的结果很可能非常庞大。比如我一个不到30k的脚本,最后打包出来的软件足足120m以上。

虽然Pyinstaller会尽力将所有的程序依赖都收集起来,但是难免会有遗漏的情况。在打包过程中经常可以看见报错信息,说找不到某某依赖,有时候这不会影响最后的运行结果,但有时候也会导致打包好的exe运行失败。

下面介绍几个常见bugs和解决方法。


python脚本可以运行,但是exe程序闪退,这时需要用命令行运行exe,直接在命令行输入完整路径,然后就会打印错误信息。

接下来按照这个方案一步步排查错误,Make sure everything is packaged correctly


ImportError: DLL load failed: 找不到指定的模块

遇到这样的问题指的是缺少dll文件,但是这个错误往往会让人很困扰,因为它不会提示具体缺少什么dll文件。想要正确的把缺少的dll找到,就得查看打包运行时的错误警告,除了在shell查看以外,还可以在/build/name/warnname.txt中查看。

找出缺失的dlls以后,可以在打包时加上--add-binary选项:

pyinstaller --add-binary '/path/to/some.dll:.' myscript.py

或者在.spec文件中添加更多的dlls:

a = Analysis(...
    binaries=[('/path/to/some.dll', '.'), ... ],
    ...)

或者在打包完成后,直接将dll文件复制到软件包即可。

例如,我在打包需要scipy的项目时遇到了这个bug,从错误信息中发现缺少了很多dlls,直接搜索其相应的名字,发现其实都在这个文件夹中,\Python36\Lib\site-packages\scipy\extra-dll。然后将这些dlls全部复制到打包程序文件夹中就行了。关于这个bug还可以参考pyinstaller/issues/3235


ModuleNotFoundError: No module named 'xxxxxx'

有时pyinstaller的分析器认为它已经找到了所有需要的模块,但实际却没有,这往往是存在隐藏的导入(hidden import)。当代码使用__import__或exec或eval等函数时,可能会出现隐藏的导入。当扩展模块使用python/c API进行导入时,也可能出现隐藏的导入。当这种情况发生时,分析器无法侦测,也不会有任何警告,只有在运行时才会出现错误。

解决办法是在打包时加上--hidden-import=some.module选项,或者在.spec文件中指定:

a = Analysis(...
    hiddenimports=['some.module', ... ],
    ...)

例如我在打包完成后,运行exe文件时遇到了错误:

ModuleNotFoundError: No module named 'scipy._lib.messagestream'

所以要重新打包并加上隐藏的导入:

pyinstaller --hidden-import=scipy._lib.messagestream myscript.py

编辑于 2018-04-05 20:10