一、项目需要。

  • 将Flask项目打包成Linux可执行【不至于源码太过于暴露在外】
  • 方便其他需要,不在赘述。

二、项目环境[主要库版本]。

  • Flask==0.10.1
  • Flask-SocketIO==4.3.2
  • eventlet==0.30.0
  • torch==1.5.1-CPU
  • torchvision==0.2.2.post3【特别注意】

三、踏坑记录

a、尝试直接编译文件

  • 项目启动文件main.py;直接尝试pyinstaller -F main.py,漫长的等待后【3分钟左右】会在main.py目录下生成distbuild文件夹和另外一个类似于CMakeList的文件main.spec[注意:后面需要修改],直接运行dist目录下的可执行文件main,报错:

    参照:https://github.com/miguelgrinberg/python-socketio/issues/35确定可行的解决方案:
    具体操作如下:
    在代码socketIO实例化的地方指定
  • socketio = SocketIO(app, async_mode="eventlet");
  • main.spechiddenimports选项中添加['engineio.async_drivers.eventlet'];
  • 在启动文件文件最上面添加两条导包语句,这些导包项目运行用不到,给pyinstaller使用:
    from eventlet.hubs import epolls, kqueue, selectsfrom dns import dnssec, e164, hash, namedict, tsigkeyring, update, version, zone
  • 注意:此时不可以直接运行pyinstaller main.py 因为重新生成的main.spec覆盖掉旧的;

b、添加静态文件

main.specdatas选项中配置:

参照另一位博主的方法【参见利用pyinstaller打包Python项目包含多个文件夹】:

python flv打包 pyinstaller打包flask项目_python flv打包


进行我的配置:

python flv打包 pyinstaller打包flask项目_http_02


说明如下:

(1) py文件打包配置

针对多目录多文件的python项目,打包时候需要将所有相关的py文件输入到Analysis类里。Analysis类中的pathex定义了打包的主目录,对于在此目录下的py文件可以只写文件名不写路径。如上的spec脚本,将所有项目中的py文件路径以列表形式写入Analysis,这里为了说明混合使用了绝对路径和相对路径。

(2) 资源文件打包配置

资源文件包括打包的python项目使用的相关文件,如图标文件,文本文件等。对于此类资源文件的打包需要设置Analysis的datas

比如:datas=[(SETUP_DIR + ‘\db\input‘,‘db\input‘),(SETUP_DIR + ‘\doc‘,‘doc‘)],只需要添加依赖的输入目录以及放图片、模板的目录即可。
注意:SETUP_DIR + ‘\db\input‘为源码中的绝对路径,Windows路径需要用\来转义,后面的db\input为打包后生成的目录,写相对路径即可。这样我们就不需要再手动把依赖的文件拷贝到run目录了
(3)Hidden import配置

pyinstaller在进行打包时,会解析打包的python文件,自动寻找py源文件的依赖模块。但是pyinstaller解析模块时可能会遗漏某些模块(not visible to the analysis phase),造成打包后执行程序时出现类似No Module named xxx。这时我们就需要在Analysis下hiddenimports中加入遗漏的模块。
如:

from core.barcode_handler import *
from conf import settings as ss
from core.log import Log as log
只需要写入自定义的模块即可
hiddenimports=[‘core‘,‘core.main‘,‘conf.settings‘,‘core.log‘,‘core.barcode_handler‘]

c、其他需要注意问题:

  • torchvision 以及torchvision 版本 导致Error:torch OSError: could not get source code
    .spec脚本中添加excluded_modules = ['torch.distributions'] 如果由于运行过程中还有其他问题导致该错误,需要将torchvision 降级【参照【已解决】OSERROR: COULD NOT GET SOURCE CODE的问题 ,WINDOWS下使用PYINSTALLER打包遇到此问题】
    pip uninstall torchvision pip install torchvision==0.2.2.post3 至此,利用pyinstaller 以目录方式打包pyinstaller -D main.spec已经可以正常运行;但,尝试将项目打包为单文件时提示找不到静态文件;

解决打包单文件找不到静态文件的问题:

  • 将静态文件添加进spec脚本的datas中后,运行依然提示找不到静态文件:
    重点:单文件模式下,运行可执行文件时,程序会先将可执行文件进行压缩,压缩的位置在 /temp目录 下,再执行,所以被打包进去的数据文件在被解压的路径下,而,程序是在运行的路径下搜索,即可执行文件的目录下,所以找不到数据文件 参见博文PyInstaller 带静态依赖文件打包教程 核心代码:
# 先获取当前运行时临时目录路径
if getattr(sys, 'frozen', None):
    basedir = sys._MEIPASS
else:
    basedir = path.dirname(__file__)
# 使用 os.path.join() 方法,将 临时目录路径与文件相对路径拼接
with open(path.join(basedir, 'file.txt'), 'r') as fp:
    pass

到这里依然无法找到打包进去的静态文件:
划重点:
即使利用 sys._MEIPASS拿到了/tmp/_MEI*路径,但还不够,需要切换工作目录【参见pyinstaller打包找不到文件的问题解决】:

import os, sys
 
def base_path():
  if getattr(sys, 'frozen', None):
    basedir = sys._MEIPASS
  else:
    basedir = os.getcwd()
  return basedir
 
# cwd = os.getcwd() # 这是程序的所在路径
wdir = base_path()
# 当需要调用打包的外部文件时
os.chdir(wdir) # 先把工作路径变成解压路径
do() # 执行你要干的事情
 
# 当需要写出文件到程序所在目录时
os.chdir(cwd) # 把工作路径切换回来
do() # 执行你要干的事情

意不意外,惊不惊喜~

补充:这种打包方案容易反编译,下一步准备尝试py文件 → c文件 → pyd文件进行编译,教程贴已备好【参见PyInstaller将Python文件打包为exe后如何反编译(破解源码)以及防止反编译】