社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python 小技巧:如何实现操作系统兼容性打包?

Python猫 • 6 年前 • 674 次点击  
👆 Python猫” ,一个值得加星标的公众号

有一个这样的问题:现要用 setuptools 把一个项目打包成 whl 文件,然后 pip install 在 Windows/Linux 两种操作系统上,但是该项目中有一些依赖库只有 Windows 上才有(例如 pywinauto、pywingui、pywinrm),那么问题是,如何实现打包文件的可兼容性安装?

从打包的角度,这个问题的关键还是看 setup.py 和 requirements.txt 文件。

关于 Python 的包构建分发和 setup.py 的使用,这里有篇文章 写得很好,推荐阅读。另外关于 Python 依赖库的管理(requirements.txt),这篇文章 详细比较了 pip、pipreqs、pigar、pip-tools 和 pipdeptree 等工具,也推荐一读。

有一个比较笨的实现方法:维护两份 requirements.txt 文件,分别用来打包,然后分发给不同操作系统去使用。

但是这样会有麻烦:维护两份依赖文件和两种包文件,本身就挺费劲的,而在生成过程中,每次还得对它们改名以作区分(注意包名有一定的规范约束,乱改的话,pip 可能识别不出),维护成本就很高。

其实,维护软件包在不同操作系统的版本,并不少见。如果你曾留意过不同版本 Python 库文件的话,你会注意到很多库都会按不同操作系统而分发不同的版本。例如,下面是同一版本号的 Numpy 在不同操作系统上的分发版(https://pypi.org/simple/numpy/):

可以看出它根据 macos、linux 和 win 三类操作系统及其位数,分成了 5 个版本。维护这么多版本,肯定是一件麻烦事,但是出现了这样的结果,就意味着 Numpy 官方认为分发不同系统版本是利大于弊的,而且是有办法实现的。

回到我们的问题,是否有必要像 Numpy 那样设法打包成多个操作系统定制的包呢?

答案是否定的。主要的原因:
  • Numpy 这么做是因为它是做科学计算的,为了提升效率,它把编译好的 C 拓展文件打包,从而不需要依赖环境上的 libxxx-devel 之类的库。如果你编译安装过 Python,应该有印象需要安装 zlib-devel、openssl-devel 和 libffi-devel 之类的系统依赖。但我们前面的问题比较简单,并不是有不同的编译依赖(系统级),而只是三方库依赖不同(项目级)。

  • 另一个主要的原因,Numpy 打包出的不同系统版本,并非简简单单地用 setuptools 之类的 Python 库就能打包,而是要借助标准的镜像进行构建。例如,manylinux 版本的打包,参见 Github(https://github.com/pypa/manylinux),就需要使用官方提供的 Docker 镜像。对于我们的问题,显然不想做到这么麻烦。

简而言之,根据前面的分析,如果要实现操作系统兼容的打包,维护多份依赖文件、使用不同构建包的方法、维护多系统专用的包,方法可行,但并不是很适用。

如果没有新的办法,这不失为一种考虑,但是有没有别的办法了呢?

我曾被这个问题困扰过,但是没有深入去研究解决,直到无意中在loguru 这个用来记录日志的库的 setup.py 中看到:

再翻看大名鼎鼎的requests 库文件,发现还可以这样写:

两个示例都是写在 setup.py 文件中,其实如果我们用 requirements.txt 文件,也可以按这种格式写,然后再读取进来。

这种神奇的写法是怎么回事呢?

它的依据是 2015 年 11 月创建的 PEP-508(以及相关的但已被撤销或拒绝了的 PEP-390、PEP-426、PEP-459、PEP-496),该 PEP 的主要意图是增强 pip 等工具查找软件包的能力。

比较重要的部分就是跟我们的问题相关的,即对操作系统作区分的标识,相关的有:

有了这样的扩展支持,在打包依赖项时,就可以解决兼容性问题了。

例如 colorama 库,如果我们只在 win32 系统才需要依赖,那么在打包时就可以指定:“colorama>=0.3.4 ; sys_platform=='win32' ”;如果不需要限定 win32 系统,而是在 windows 环境都安装,那么可以写成“colorama>=0.3.4 ; platform_system=='Windows' ”。

最终,我们解决了本文开头的问题。这个问题可能比较小众,解决起来也没有什么大文章可做,算是一个小小的 tips 分享给大家吧。

优质文章,推荐阅读:

Python 进阶:queue 队列源码分析

为什么Python不用设计模式?

记一次列表预分配空间的锅

len(x) 击败 x.len(),从内置函数看 Python 的设计思想

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/56434