大型 Python 项目转成 exe 交付之后,每次迭代都要重新走一遍全量打包流程,耗时又费力,还得把整个程序重新发给客户。据 kkpack 相关技术分享介绍,kkpack 已经推出增量打包方式,用来化解这一反复发包的痛点。

首次完整打包,后续只更新源码

按照这套思路,第一次交付仍然生成完整程序;一旦进入迭代升级环节,开发者不用再把整个项目重新打包,只需对源码部分做打包处理,将产出物发送给客户,让他替换掉旧版本文件,即可完成一次升级。要使用该功能,需要通过 pip install kkpack 安装,并且版本必须锁定在 kkpack==0.1.2,不能选错。

kkpack 0.1.2 增量打包:Python 升级包只需替换源码

从 Hello world 到可交互程序的升级演示

为说明流程,示例先给出一段极简代码:包含 __name__ == '__main__' 判断,运行后打印 'Hello world'。打包分两步执行,先运行 kkpack init,再执行 kkpack main.py。由于这段程序没有任何依赖,经 kkpack 打包后,客户端不必额外下载依赖,直接运行就能看到 Hello world 输出。

随后客户提出新需求,希望程序支持输入内容。示例随即加入 f'您输入的是:{res}' 这样的交互逻辑。但问题随之而来:如果每次都从头打包,效率依旧很低。解决办法是打开 init 阶段生成的配置文件 kkpack.toml,把 onefile = false、exe_only = true 两项按此设置,并注意开启 exe_only 时必须关掉 onefile。配置完成后再次执行 kkpack main.py,打包范围就只覆盖源码和标准库部分,把这份新的 exe 交给客户替换即可。

为什么增量包里还要带上标准库

关于增量包为何仍包含标准库部分,文中给出的解释是:打包底层需要把这一部分内容放进 exe 内部,而不是让它在 exe 外部去调用 pyd。新生成的包会落在名为 upgrade 的文件夹内。客户打开新程序并尝试输入内容后,反馈十分满意。

kkpack 0.1.2 增量打包:Python 升级包只需替换源码

用 textual 美化界面时的打包方法

紧接着客户又提出界面美化的要求。示例选用 textual 来实现,先执行 pip install textual,再编写包含 textual.app 的 App、ComposeResult,以及 textual.widgets 下 Header、Footer、Button、Static 等组件的界面代码,其中包括向用户展示"Hello, 帅哥!"、在 on_button_pressed 中通过 self.query_one(Static).update 更新静态文本,并以 "__main__" 与 MyApp().run() 收尾。

这一次仍按 exe_only = true 打包,区别在于要在 requirments.txt 里写清依赖:textual==8.2.8,打包完成后把该文件一并发送给客户。客户双击程序需要先下载安装依赖,稍作等待后就能看到定制界面。示例中客户看到屏幕上的"帅哥"两个字后非常满意,还爽快支付了 1w 美金的开发费用。

kkpack 0.1.2 增量打包:Python 升级包只需替换源码

场景是虚构,功能是真实存在

需要说明的是,上述客户故事是为讲解 kkpack 某项功能而虚构出来的演示场景。但文中强调,场景虽为假设,增量打包这个功能确实可用,并希望使用者多多支持作者持续产出内容。

哪些场景更适合考虑增量打包

从演示流程可以看出,增量方案的核心局限与前提都集中在依赖处理上。纯标准库、零第三方依赖的程序,客户侧可以直接运行;一旦引入 textual 这类外部库,就必须把 requirments.txt 随包发送,且客户端首次运行需要联网安装依赖。也就是说,增量打包缩短的是开发者反复全量打包与传输的时间成本,并不改变依赖分发的要求。

小结:kkpack 0.1.2 把 Python exe 的升级方式从每次全量重打改成只打包源码与标准库,通过 onefile = false、exe_only = true 两项配置即可启用,新包放在 upgrade 文件夹中可直接替换。需要留意的是,引入 textual 等第三方库时,仍要把 requirments.txt 一并交付给客户端完成依赖安装。