IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    python wheel 包命名规则和 abi 兼容

    spacewander发表于 2016-11-24 19:02:17
    love 0

    注意,本文内容基于python 2。python 3的处理方式可能有所不同,有需要的读者可以自己了解一下。

    wheel 包的命名规定

    wheel 包的命名格式为 {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl。
    其中各个 tag 的意义和取值在 PEP425 中有规定:https://www.python.org/dev/pe...

    python tag 标记了具体的 python 实现。其中:

    • py 无实现特定的拓展

    • cp CPython,也就是通常使用的 Python 实现

    • ip IronPython,跑在 Windows CLI 平台上的 Python 实现

    • pp PyPy,带 JIT 的 Python 实现

    • jy Jython,跑在 JVM 上的 Python 实现

    举个例子,如果 wheel 包里面包含了 C 拓展,那么打包出来的 python tag 就是 cpxx,其中 xx 是具体的版本号,如 cp27.

    platform tag 也好理解,就是系统 _ 架构。比如 linux_x86_64。

    最陌生的恐怕是之间的 abi tag,这正是本文讨论的主题。

    abi 这东西,看不见摸不着。系统上的东西嘛,敲下个命令就知道是什么操作系统;架构虽然玄乎点,不过也就是那么几种;然而有多少人知道自己当前使用的平台遵循着怎样的 abi 标准?什么时候 abi 可以兼容,什么时候又不可以?

    pip wheel 打包时,abi tag是怎么敲定的

    Python 对此有另外一个 PEP:https://www.python.org/dev/pe...

    如果 sysconfig 定义了 SOABI,那么就用 SOABI 的值。当然这是 Python 3 的事务,这里我自然不用管。如果没定义 SOABI,比如 Python 2,wheel 会生成一个类似的 abi tag。在 Python 的标准里,这个 abi 取决于打包时使用的 Python 实现。举 CPython 为例,首先必须包含的是实现名和版本号,比如 CPython2.7.9 对应的是 cp27.其次,需要包含构建 CPython 时特定的选项。具体来说,打包时会依次判断当前的 CPython 是否有下列的功能,如果有,加上对应的 flag:

    1. --with-pydebug (flag: d )

    2. --with-pymalloc (flag: m )

    3. --with-wide-unicode (flag: u )

    通常,我们看到的 abi tag 会是这样的 cp27mu,这是因为 --with-pymalloc 是默认开启的,而包管理中分发的 CPython 会加上 --with-wide-unicode 选项。

    有趣的是,如果打包时没办法判断 abi 类型,生成的 abi tag 会是 none。而如果 Python 包是不依赖特定的 abi 的纯 Python 实现,生成的 abi tag 也是 none。在安装时,值为 none 的 abi tag 会享受特殊待遇。这个下文再说。

    另外同样的 Python 代码打出来 abi tag 相同的包,不一定完全一样。以我的亲身经历举例,pycrypto 这个库,在打包的时候会判断 libgmp 是否存在,如果存在,就构建 _fastmath 这个库。如果打包平台上存在 libgmp,打出来的包就会包含 _fastmath。反之,则不存在。而这两种情形下打出来的包,名字是一模一样的。

    如何判断给定 wheel 包是否能够安装

    通常判断依赖的时候,需要看下是否符合最低版本。不过 pip 判断给定 wheel 包的 abi 兼容的做法与此有些许差异。pip 的做法是,计算出一个支持的 abi tag 集合,然后判断目标 abi tag 是否在这个集合里。这个计算过程跟在打包时是一样的。这意味着,打包拓展的 CPython 需要跟安装的机器上的 CPython 版本是一致的,否则就装不了。对于“永远的2.7”来说,这不是什么问题;不过如果用的是 Python 3,又不能控制具体的 CPython 版本,对于 C 拓展还是现场编译安装比较靠谱。

    其实说了这么多,还不如跑一个脚本:
    /usr/local/lib/python2.7/site-packages/pip/pep425tags.py

    pip 会运行这个脚本来判断 wheel 包。所以你只要

    from pip import pep425tags
    print(pep425tags.get_supported())

    就能报出该系统上支持的 wheel 包名字。

    当然对于 abi tag 为 none 的包,它可以在任何一个 abi 版本上安装。因为所有的平台都至少支持 none abi。但如果一个平台上的 Python 如此古怪,以致于没办法确定它的 abi 类型,那么也就只能装上 abi tag 为 none 的包,即纯 Python 实现抑或同样古怪得无法判断打包时的 abi 的包。真是同类相聚啊。顺便一提,在 Ubuntu 14.04 和同期的系统,通过包管理安装的 pip 有一个已知 bug,有些时候无法正确判断 abi 类型,所以打出来的包的 tag 是 none,且任何带特定 abi 的包都装不上去。我们曾经遇到这个 bug,最后通过升级 pip 解决了。感兴趣的读者可以比较这包管理版本和最新版本,两个 pip 的 pep425tags.py 有什么异同。



沪ICP备19023445号-2号
友情链接