半年前我做了一个 side project,把 pip 魔改一番,使得我能够将 Maven 的 Nexus 仓库作为 pip 的后端仓库。于是原本通过 pip install xxx==version
命令,我们可以从 https://pypi.python.org/simple/
把指定版本的包下载下来然后安装,我可以用 mpip install xxx==version
从公司内网的 Nexus 仓库下载包然后安装。
之所以做这么个工具,是因为公司没有提供一个内部的 pypi 服务器。Python 不是公司的主流语言,公司自然不会投人力物力去维护这么一个服务,如果由我所在团队自行维护,又得付出额外的人力物力,并不是十分划算。于是我们之前的做法是将系统依赖的三方包,不管是 whl 还是 tgz,直接放在代码目录里,然后在生成部署包的时候再复制、安装。
对于一般的小系统,这么做也许也就够用了,但是我们维护的系统已经走到了服务化重构的十字路口,一旦重构,势必会产生大量的不断升级演进的内部软件包和类库,如果继续沿用原有的复制依赖文件的方法,正确处理系统的类库依赖会成为让每个开发者头疼不已的问题。
如何应对日渐复杂的 Python 包依赖,如何降低升级内部外部依赖的成本,成为让我感兴趣的问题。魔改 pip,将公司有专人维护的 Nexus 嫁接到 pip 上,则是我给出并实施的解决方案。这样我们就能依旧保持几乎和 pip 一样的使用习惯,同时利用公司为了 Java 生态而下精力维护的 Nexus 仓库。
成功实现 mpip 之后,由于我投入了别的项目,暂时无暇顾及将 mpip 落地的事情,而那时候在项目之间复制 whl 包还没繁琐到让同事们难以忍受的地步,mpip 的实际使用就一直搁置,直到最近才重新提上议程。
无论如何,mpip 也是在依赖管理和部署打包上给了团队一些信心和期待,相信以后随着我们的使用和推广,mpip 会一直帮助我们提高研发效率。
今天我遇到了一个需求,希望能在 Linux 机器上生成适合 Windows 使用的 Python 部署包。我们知道 Pyhton 虽然号称跨平台,但是实际上不少类库的分发包是有平台相关性的,在 Linux、macOS、Windows 上用 pip 安装同一个包,下载下来的 whl 文件却是不一样的。为了能够在 Linux 上生成 Windows 部署包,我需要让 mpip 下载适合 win32 平台的 whl 包。
在 pip 9 之前,pip 是不支持从参数指定目标平台的,全靠 pip 自动识别,在 Win32 上就给你下载 Win32 的包,Linux 上就给下载 Linux 的包。不巧,我开发 mpip 的时候是从 pip 8.1.2 fork 的代码,自然就不包含我想要的这个指定目标平台的参数,唯一的办法是合并上游代码变更。
魔改之后还要合并上游变更,这或许是最让开发者蛋疼菊紧的事情之一,特别是当你对原项目做了大范围的修改,合并上游就几乎不可能了。
为了能让 mpip 和 pip 和平共处,我 fork 代码之后,修改了包名,将 pip 改为 mpip,还把代码中所有出现 pip 的地方都改成了 mpip,这样的好处是无论是代码还是配置还是环境变量都完全隔离了,互不干扰;代价也很大,仓库里几乎每个文件都被我改过,合并代码难度极大。
真是让人发愁啊!
幸运的是我的提交次数并不太多,仅仅十多次的提交中,大部分都是在修改 README,少数几次提交才是修改代码,或许我只要把修改 cherry-pick 到 pip 9 的代码上,就能避免大部分的冲突。
如果我成功把我的变更合并到了 pip 9 的代码上,我也不会再在代码分支里做那种巨大的变更了。在开源代码上魔改,如果不方便回馈社区,就得时刻考虑如何合并上游更新的问题,不然许多的新特性和漏洞修复就得不到了。
或许在全项目范围修改 pip 为 mpip 这种事情,放在打包脚本中去做是更为合适的选择,只需要在编译时将代码复制一份到 build 目录,然后用 sed 之类的做丧心病狂的魔改替换就可以了,没必要污染代码树的。
其实我也曾经考虑过,不是这么直接的修改代码,而是让 mpip 依赖 pip,用一种「功能增强」的方式来实现,但是 pip 是一个快速演进的软件,如果让 mpip 去依赖 pip 的内部实现,很可能会导致 mpip 绑死在 pip 的某个版本上,降低了 mpip 的实用性;另外 mpip 需要修改 pip 的核心库实现,而 pip 甚至 Python 社区都没有使用 IoC 容器的习惯,我们很难去直接替换底层实现。相反,如果是一个依赖 Spring 的 Java 软件,替换一个底层组件真的是很轻松啊,软件工程做得久了,愈发觉得 Java 真的是适合软件工程的语言,各种框架、类库一应俱全,各种面向软件工程的特性信手拈来。
啊,果然 Java 才是最好的语言!
原文 https://blog.jamespan.me/posts/mpip-the-good-the-bad-and-the-ugly