优雅而方便的用Python Embeddable打包程序

最近用Python鼓捣出了一个小工具,刚好朋友也需要用到这个玩意,就想着打包发布一份可直接运行的程序发布出来。

不过众所周知,Python虽然是最受欢迎的语言之一,但项目的发布和部署上要比其他语言稍麻烦一些。

参考一下目前常用的Python项目发布方案(Windows环境)有这些:

  • 在目标运行环境上安装Python,并安装相应的包
  • 用docker打包成镜像
  • 用Nuitka、PyInstaller之类的程序把py文件编译成可执行文件
  • 把项目文件,和Python解释器、依赖包一起打包发布

考虑了一下实际情况,不是人人都愿意在自己电脑上装一堆东西,而且目标用户也不会敲代码,前两项直接pass。

综合来说Nuitka编译成可执行文件是个不错的选择,编译后的EXE能直接启动运行,发布到其他机器上运行也不用再装环境和依赖了,效果接近于发布后的C和C#程序。

但是缺点也是相对应的。编译前要做单独一些准备,并处理依赖问题。编译的时间比较长,有可能会失败。不太适合要频繁修改发布的项目。

所以最后考虑了一下,还是决定,用最后一种方案发布。也便于随时更新。

传统方式整合Python及依赖

一般来说,Python的项目开发时都会使用虚拟环境(venv),项目的包用pip装入venv中管理,和主Python环境隔离开(这个项目也是如此)。

要整合一个免安装的运行环境到项目中,至少要做这几件事:

  • 先要复制一份Python环境作为副本。
  • 接着整合依赖包,把venv中的Lib复制到这个副本的Lib中;或者你乐意的话,在Python副本中重新用pip安装一次包。
  • 最后写一个启动脚本(比如Windows中的bat),使用Python副本启动程序

但是这样有可能会引入一些无关的包,而且项目的体积比较大。而且不排除有可能在整合依赖的时候遇到问题。

如果追求更“纯净”的发布品,那就得完全新装一个Python,并在新环境中直接安装包(不使用venv),然后再把这个环境整合到项目中发布。

但是这样操作就麻烦了很多。又要安装卸载Python,又要重新下载包,不够优雅。

就没有又省事又优雅还不用动现有开发环境的做法吗?

嵌入式版本登场

随手翻下Python的下载页面,能看到有一个Embeddable版本(嵌入式版本)。

这个版本就是最核心的Python解释器,不含IDLE、帮助文档等,甚至连pip都没有带。下载页面上也有提到一句话“making it easy to redistribute Python as part of another software package”。也就是说,用这个版本做整合的话,能得到一个最干净、体积最小,几乎是解压即用的解释器。

这就不就解决了一些问题了?比较符合前面提到的需求了。

整合运行环境

OK,既然官方已经考虑到了Redistribute(再发行)的问题,那就可以用这个开整了。

下载并解压embeddable package(这里用的是3.8.10 64位),得到了最小解释器环境。

不过要怎么引入依赖包?一些关于使用embeddable package的教程中,会提到我们手动安装pip,然后再用pip安装其他包,但事实上我们完全不需要做得这么麻烦。

只需要两步:

1、把项目venv下的Lib目录复制到嵌入式版目录中

2、在嵌入式版目录里找到后缀为“._pth”文件(文件名视Python版本而定,例如这里是python38._pth),打开并将“import site”取消注释,保存

这样依赖也整合好了。

随意import一个包试试,正常运行。

启用同级包(模块)扫描

到此为止,如果你的项目足够简单,例如是只有一个文件构成Hello World,那双击启动脚本就已经可以正常运行了。

如果你的项目稍微复杂一点点,分了模块(模块和启动的文件在同级目录下)。那会遇到Python报找不到模块的错误。

例如,这里写了个测试脚本,test目录的run.py文件中,import了mod.py文件。

用整合后的嵌入式版Python无法启动,抛出“ModuleNotFoundError”。

稍加思索……不对啊,在安装版中运行明明可以正常import。为什么会这样??

Python的官方文档(https://docs.python.org/3.8/using/windows.html#windows-embeddable)上其实有这么一行文本。

直接说结论的话,嵌入式版不受环境变量的影响,安装版和嵌入式版本在加载依赖的方式上会有些差别

有个很简单的验证方式,在一个测试脚本写入以下内容(sys.path里是Python在导入依赖包时搜索的路径),并用两个版本的Python分别启动

import sys
print(sys.path)

可以观察到相比嵌入式版本,安装版会额外去扫描入口文件(也就是被运行的文件)所在的目录。

 

想不修改项目代码解决这个问题,有一个小技巧。

在嵌入式版“Lib\site-packages”下新建一个文件,以“.pth”为扩展名,输入以下内容并保存:

import sys;import os;sys.path.insert(0,os.path.dirname(sys.argv[0]));

例如这里命名为了“start_path.pth”

放入这个文件后,嵌入式版本的Python启动并扫描依赖包路径时,会执行到这个文件。而这段代码又把入口文件所在的目录加入了依赖查找的路径中,就不会出现上面无法导入模块的报错了。

再运行试一次,没有报错,问题解决~

收工

最后,得到的整合后的运行环境大概是这样的。现在只需要把运行环境复制进项目中,稍微修改启动脚本,就可以发布了。

OK,到此,就完成了项目发布前的整合了。

利用Python Embeddable版本并小做修改,就有了一个快速且很方便的项目发布方案了。

后续要发布升级,直接复制并覆盖想要的文件即可,也不再需要考虑配置Nuitka编译之类的问题了。

除特别注明外,本站内容皆为 咸鱼先锋 原创,可自由引用,但请注明来源和链接。
https://xyuxf.com/archives/2114
欢迎关注 咸鱼先锋 (微信号公众号:xyuxf),获取干货推送
THE END
分享
二维码
< <上一篇
下一篇>>
文章目录
关闭
目 录