【旁门python02】如何用setuptools搭建一个python whl包

目录

  • 前言
  • 前置准备
  • 一、wheel包的种类
  • 二、wheel包的打包过程
    • 2.1 pyproject.toml
    • 2.2 setup.py的编写
      • 2.2.1 基本组成
      • 2.2.2 命令行指令
      • 2.2.3 条件式依赖关系
      • 2.2.4 元数据
    • 2.3 打包/安装
  • 三、manylinux wheel包
  • 四、总结
  • 五、参考资料

前言

鸽了那么久终于决定把坑填完……本篇打算承接上一篇,讲讲python wheel包的创建。原则上需要涵盖的内容挺多的,但碍于篇幅只能把最相关的部分提炼出来。一般的开发者需要用到这部分内容的其实不多,但谁知道呢——也许哪一天愿意为开源社区提供自己写的程序包时,希望这篇博客能带来一定的指导。


前置准备

python的打包工具有很多,setuptools虽然已不能算是默认,但仍是目前最为流行的一种。首先,得确认自己的系统上有wheelsetuptools两个模块:

pip install wheel setuptools --user
  • setuptools是用来完成打包动作的
  • wheelsetuptools的一个衍生插件,提供一些命令行指令用于和wheel包交互

一、wheel包的种类

  • Universal wheel:带有py2.py3-none-any.whl在名字中的包。对python2和python3、操作系统等均没有要求,兼容性极好。
  • Pure-python wheel:带有py3-none-any.whlpy2-none-any.whl在名字中的包。和universal wheel相比,仅仅在python的版本上有所要求。
  • Platform wheel:对python版本和操作系统等都会有要求。创建这种类型的wheel一般意味着内部使用了一些由静态语言编写的代码(如C++)。这些代码在编译时会对平台有所要求,所以造成wheel包也会有平台依赖。

pip install在执行的时候会去寻找和当前环境兼容的版本,如果未找到兼容的版本,但是找到源发行版的话,会下载源发行版在本地进行编译安装;如果源发行版也找不到,那安装就会报错了。

二、wheel包的打包过程

2.1 pyproject.toml

pyproject.toml是在PEP518中被引入作为注明一个Python项目要用到什么编译工具的。在此之前人们没有办法让程序自动获知一个项目打包成wheel过程中需要的编译工具。除了PEP518之外,PEP517还指明了如何使用编译工具来完成打包编译,进一步实现了整体过程的标准化,统一了不同的编译工具和流程。当然,它们带来的好处还有一些,在这里暂不展开。

对于setuptools用户来说,以下的内容,阐明一下setuptools的版本就够了:

[build-system]
requires = ["setuptools >= 40.6.0", "wheel"]
build-backend = "setuptools.build_meta"

笔者稍微浏览几个大的开源项目,都没有发现pyproject.toml的存在。当然了,这只是统一化的过程,也许未来会做得更好一些。

2.2 setup.py的编写

2.2.1 基本组成

setup.py是打包过程的核心文件。我们以下面这个文件结构为例:

example_project/
├── exampleproject/      
│   ├── __init__.py      
│   └── example.py    
└── README.md

对应地,一个最小setup.py样例就如下:

from setuptools import setup, find_packages

setup(
    name='example',
    version='0.1.0',
    packages=find_packages(include=['exampleproject', 'exampleproject.*']),
    install_requires=[
    	'numpy',
    	'matplotlib',
    	'pandas'
    ]
)
  • name:必要字段,指定了pip搜索时的包名,不需要和项目文件夹同名。
  • version:必要字段,版本号,最好遵从国际标准的版本号命名方式。
  • packages:必要字段,指明所有的子模块位置。
  • install_requires:非必要字段,当你的包需要用到其他库的时候可以注明,方式跟平时写requirements.txt一致。

2.2.2 命令行指令

有些Python包在安装完成之后会自动提供命令行指令,比较著名的如jupyter-notebook指令。这其实也是通过在setup.py里添加字段来实现的。我们可以直接观察一下jupyter项目的setup.py:

...
entry_points = {
     
	'console_scripts': [
	    'jupyter-notebook = notebook.notebookapp:main',
	    'jupyter-nbextension = notebook.nbextensions:main',
	    'jupyter-serverextension = notebook.serverextensions:main',
	    'jupyter-bundlerextension = notebook.bundler.bundlerextensions:main',
	]
}
...

entry_points同上面的name等字段处于同等地位,其中添加了console_scripts字样并将所有的命令行指令列出,并标明了每个指令对应的函数所在位置。从语法上来看并不难理解。

2.2.3 条件式依赖关系

同一个包在不同的情况下会有不同的依赖关系,比如在开发时需要绘制图像观察各模块性能,但是部署到生产线上则不需要,这时就需要extras_require关键字(仍旧以jupyter项目为例):

extras_require = {
     
    'test': ['pytest', 'coverage', 'requests',
             'nbval', 'selenium', 'pytest', 'pytest-cov'],
    'docs': ['sphinx', 'nbsphinx', 'sphinxcontrib_github_alt',
             'sphinx_rtd_theme', 'myst-parser'],
    'test:sys_platform != "win32"': ['requests-unixsocket'],
    'json-logging': ['json-logging']
},

添加了这个字段的话,在执行普通的pip install jupyter时,只会安装install_requires字段里要求的依赖库;若执行pip install jupyter[test],则pytestcoverage等7个包会加装;执行pip install jupyter[docs],则sphinxnbsphinx等5个包会加装。

2.2.4 元数据

下面的这些字段都属于元数据——当把包发布到PyPI时最好能加上这些字段,让其他用户知道包的维护者信息。

  • author:作者名字
  • author_email:作者的邮箱
  • description:包的一句话描述
  • url:包所对应的项目的链接
  • license/license_files:项目用到的证书

2.3 打包/安装

设置完上述内容之后,打包只需要一句话即可:

python setup.py sdist bdist_wheel

或者,如果系统装有build包(pip install build)的话,也可以:

python -m build

完成之后,在项目的dist目录文件下就可以找到源发行包(.tar.gz)和wheel包了(.whl)。

三、manylinux wheel包

上述这些过程对universal类型的wheel是可用的,但是一旦遇上具有平台针对性的情况就不适用了,manylinux是linux环境下的一种解决手段。工业上比较常用的手段是通过持续集成(CI)服务器进行编译。如果想在本地执行manylinux编译的话,会需要Docker的支持。PyPA为开发者提供了一系列的Docker镜像和一个manylinux样例repo来支持这方面的工作。这其中的细节就不在本文中展开了,有兴趣的开发者可以点进这两个链接进行参考。

四、总结

本篇介绍了使用setuptools打包一个python包的简要步骤。这个过程中jupyter项目让我学到了很多东西,它们的setup.py写得非常漂亮,强烈建议有空去瞟一眼(见参考资料)

不过,前言中提到过,目前的打包手段已经不局限于setuptools了,下一篇如果没有其他计划的话打算去学习一下poetry看看它有什么不同。

五、参考资料

  • Python打包工具演变史(英文)
  • 扯淡!Python包管理工具的发展史
  • Jupyter项目
  • PEP518
  • What the heck is pyproject.toml?
  • A practical guide to using setup.py
  • Setuptools quickstart
  • How to Publish an Open-Source Python Package to PyPI

你可能感兴趣的:(编程,python)