周日趣事:看看python脚本怎么打包成可执行文件(应用)

2023-04-08  本文已影响0人  赖皮姑娘爱吃蛋糕

背景

同事A需要在一台只能访问局域网的mac mini上运行一个python脚本,脚本依赖了一些模块,有numpy这种三方模块,也有自己开发的本地模块,而mac mini上除了python,什么都没有安装。同事B接到求助后三下五除二生成了一个可执行文件,堪称满分答案。但对于具体细节,同事B表示很复杂说不清,于是就有了姑娘的周日趣事。

工具

浅百度一下,迅速定位到工具为PyInstaller, 查到官网上有以下描述,确认就是你没错!

PyInstaller bundles a Python application and all its dependencies into a single package. The user can run the packaged app without installing a Python interpreter or any modules.

从首页的描述来看,使用简直不能再简单:
安装工具:

pip install -U pyinstaller

使用工具:

pyinstaller your_program.py

真的这么简单吗?

如果同我一样顺手用了import numpy做demo的话,就不是了!
我的脚本tryPyinstaller.py:

import numpy as np
print("6 / 3 = " + str(np.divide(6,3)))

使用工具:

pyinstaller --onefile tryPyinstaller.py

运行生成的应用:

(base) leixiaoyues-MacBook-Pro:~ leixiaoyue$ /Users/leixiaoyue/Code/YuePythonScript/tryPyinstaller/dist/tryPyinstaller ; exit;
INTEL MKL ERROR: dlopen(/private/var/folders/fq/qx5csyvd54x6p60p38qvqfpm0000gn/T/_MEI8YGfHc/libmkl_intel_thread.1.dylib, 0x0009): Library not loaded: '@rpath/libiomp5.dylib'
Referenced from: '/private/var/folders/fq/qx5csyvd54x6p60p38qvqfpm0000gn/T/_MEI8YGfHc/libmkl_intel_thread.1.dylib'
Reason: tried: '/usr/local/lib/libiomp5.dylib' (no such file), '/usr/lib/libiomp5.dylib' (no such file).
Intel MKL FATAL ERROR: Cannot load libmkl_intel_thread.1.dylib.
logout
Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.

为什么会缺库啊?!这件事情还不得而知,选择暂时放过自己,先来解决问题现象!这就引出了PyInstaller一个非常重要的机制——钩子!

钩子🪝万岁

浅百度一下,又在官网中看到有“Understanding PyInstaller Hooks”一节,大概感到有救。钩子是编程中用来实现可拓展性的一个常用技巧。钩子已在!缺什么钩什么!

接下来看看PyInstsaller里具体怎么钩,抄 StackOverflow上的作业。

  1. 新建钩子脚本hook-numpy.py,把要钩的库列在里面

from PyInstaller import log as logging
from PyInstaller import compat

from os import listdir

mkldir = compat.base_prefix + "/lib"
logger = logging.getLogger(name)
logger.info("MKL installed as part of numpy, importing that!")
binaries = [(mkldir + "/" + mkl, '.') for mkl in listdir(mkldir) if mkl.startswith('libmkl_')]

  1. 通过参数additional-hooks-dir告诉程序新增钩子的路径,参数clean是为了清除之前运行的缓存,多个香炉多个鬼,清一清没啥不好

pyinstaller --additional-hooks-dir=. --clean --onefile tryPyinstaller.py

双击应用,成功运行

Last login: Sun Apr 9 15:02:31 on ttys008

The default interactive shell is now zsh.
To update your account to use zsh, please run chsh -s /bin/zsh.
For more details, please visit https://support.apple.com/kb/HT208050.
/Users/leixiaoyue/Code/YuePythonScript/tryPyinstaller/dist/tryPyinstaller/tryPyinstaller ; exit;
(base) leixiaoyues-MacBook-Pro:~ leixiaoyue$ /Users/leixiaoyue/Code/YuePythonScript/tryPyinstaller/dist/tryPyinstaller/tryPyinstaller ; exit;
6 / 3 = 2.0
logout

Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.

[Process completed]

运行是真的慢……

工程师的工具箱里又多了什么

首先,是把所有依赖统统打包成应用的思路。这在帮完全外行的朋友写软件时超级有用,这样简单直接的思路以前还真没想到过。
再就是又一次领略了🪝提供可拓展性的魅力。

解决旧问题带来新问题

为什么打包出来的程序运行那么那么慢……
为什么打包numpy会缺libmkl_xxx一系列Math Kernal Library?
Hook 真的是个好的命名吗?韩语후킹(hooking),日语フック(hook),直接用音译,大陆译作“钩子”,不能在亚洲圈获得统一的翻译,往往是命名不够准确的一个表现……

上一篇下一篇

猜你喜欢

热点阅读