Python packaging - past, present and future

English | Python Packaging - Past, Present, Future [1]

Original | BERNAT GABOR

Translator | pea under the cat

Disclaimer : This article is the author obtained licensing rights for translations, reproduced leave the original source, not for commercial or illegal purposes.

Have you thought about what happens when you run pip install? This article will give you a detailed overview about the steps involved in the past, and how it is with the PEP-517 and PEP-518 uses changed.

In the previous article , I described how to do install three types of content: source tree (source tree), source release (source distribution) and wheel. Only the last two types will be uploaded to PyPi central storage bin, but you can also get the source tree (for example, by adding to pip git protocol). Compared with other types, advantages of the wheel is constructed does not require any actions on the user's machine; just download and extraction.

Build a Python package

You can now separate the built environment (user or developer's machine), but you still need to build the package (sdist or wheel). To do this, you need some proper builder. In the past, the demand for third-party packages have long shown.

Follow the principle of built-in battery, in the year 2000 Python 1.6, distutils [2] package is added to the Python standard library. It introduces a logical construct that contains the setup.pyconcept paper, and by python setup.pycommand triggers.

It allows the user code into a library package, but no statement (Declaration) automatically dependent libraries and other functions. And it's tied directly upgrade cycle and release cycle core interpreter.

setuptools founded in 2004, it built on distutils, and extends the other excellent features. It quickly became so popular that most of the Python installer will start its offer with the core interpreter.

At that time, all the packages are source release. wheel distribution methods appear very late, in 2014. distutils is created in only a few well-versed people when packed. So it is very flexible and imperative (imperative), you write a Python script, you can modify the package every step of the build process.

However, the disadvantage of this is that it is not easy to learn and understand. With the popularity of Python, which became an increasingly serious problem, because there are more and more users on the inner workings of Python is not very proficient.

Charles PH Photo / Unsplash - ehhh

Construction of dependency

About installing a source release, pip mainly undertook the following work:

  1. This package found

  2. Download the source release and extract it

  3. Running in the extracted folder python setup.py install(build + installation)

Developers to run python setup.py sdistraw ingredients contracting, running python setup.py uploaduploaded to a central storage bin (upload command was abandoned in 2013 because of twine [3] tools, but mainly because of the use of unsafe upload HTTP connection, and upload the command will do a new building, it does not allow the end user to detect before the actual upload (inspect) generated packets).

When pip run python setup.py install, it uses Python interpreter installation package. Therefore, the construction operation can access all of the packet interpreter constituents already present. Most notably, it is fully used setuptools version installed on the host Python interpreter. If a package uses a new version of setuptools characteristics, then the only way to complete the installation of the first update setuptools is installed.

如果新版本包含了能破坏其它包的 bug,就会导致出问题。在用户无法更改已安装包的系统上,这尤其麻烦。当构建器(例如 setuptools)希望使用其它辅助包(例如 cython)时,这也是个问题。

如果缺少构建器的辅助,通常会抛出导包失败的错误:

File "setup_build.py", line 99, in run
    from Cython.Build import cythonize
ImportError: No module named Cython.Build

在开发者们这边,没办法提供此类构建依赖项。而对于用户这边,则需要预先安装所有的包构建依赖,即使他们不会在运行时使用到。为了解决这个问题, PEP-518【4】被创建了。

其思想是,与其将主机的 Python 与其当前安装的构建包一起使用,不如给软件包提供一种能力,令其清楚地说明其构建操作所需的内容。另外,与其在主机 Python 上提供此功能,我们是创建了一个独立的 Python(类似某种虚拟环境)来运行打包。

python setup.py install 现在可以:

  1. 创建一个临时文件夹
  2. 创建一个隔离的(从三方库的 site packages 中)Python 环境 python -m virtualenv our_build_env,让我们将这个 Python 可执行文件称为python_isolated

  3. 安装构建的依赖项
  4. 通过python_isolated setup.py bdist_wheel,生成一个用于安装的 wheel
  5. 提取 wheel 到 Python 的 site packages 文件夹

有了这个,我们可以安装依赖于cython 的包,但不必在运行的 Python 环境中实际安装cython。指定构建依赖项的文件与方法的是pyproject.toml元数据文件:

[build-system]
requires = [
    "setuptools >= 40.8.0",
    "wheel >= 0.30.0",
    "cython >= 0.29.4",
]

此外,它还允许打包者指定他们需要的最小版本,而借助用户机器上的 pip,可以轻易地找出这些版本。

当在开发者的机器上生成源发行版或 wheel 时,也可以使用相同的机制。当一个人调用pip wheel . --no-deps命令时,该命令会自动在后台创建一个包含构建依赖项的独立 Python,然后在该环境中调用python setup.py bdist_wheelpython setup.py sdist 命令。

Bruce Galpin摄/Unsplash--yay!

多样的打包工具

但这里还有一个问题。请注意,所有这些操作仍然须通过 20 年前引入的机制,即执行setup.py。整个生态系统仍然构建在 distutils 和 setuptools 的接口基础之上,由于试图保持向后兼容性,没法作太大的变更。

此外,在打包过程中执行用户端 Python 代码是危险的,这可能会导致经验较少的用户难以调试的细微错误。命令式的(imperative)构建系统在 20 年前对于灵活性来说非常重要,当时我们还不知道所有的情况,但是现在我们已经认识清楚了,很可能可以为不同的情况创建出非常健壮和简单的包构建器。

引用Paul Ganssle【5】(setuptools 与 dateutil 的维护者)的话:

理想情况下,默认选项应该是一个声明式的(declarative)构建配置,适用于 99% 的情况,再提供一个退回到命令式系统的选项,供真正需要灵活性时使用。在这情况下,如果你发现还需要选择用命令式的构建,那么我们可以认为出现了坏味道代码。

setup.py 的最大的问题是大多数人是声明式地使用它,所以当他们用命令式时,往往会将 bug 引入到构建系统。一个这样的例子:如果你有一个 Python2.7 的依赖项,你可能会试图有条件地在 setup.py 中指定 sys.version,但 sys.version 仅指的是执行构建的解释器;相反,你应该对需求项使用声明式的环境标记…

在 2015 年的引入的flit【6】已经证明了这一假设的正确性。它已经成为许多 Python 新手最喜欢的打包工具,因为它可以确保新用户避免很多这样的麻烦。然而,要达到这个目的,flit 必须再次构建在 distutils/setuptools 之上,这使得它的实现非常关键,并且代码仓出现相当多的垫片层(例如,它仍然为源发行版生成 setup.py 文件)。

现在是时候把它从这些束缚中解放出来了,同时也鼓励其他人构建自己的打包工具来简化打包,是时候让 setup.py 成为例外而不是默认的了。setuptools 计划提供【7】一个用户专用的setup.cfg 接口来起带头作用,当一个 PEP-517 系统就位时,在大多数情况下,你应该选择它而不是使用 setup.py。

为了不把所有东西都绑定到 setuptools 和 distutils 上,并使后端的构建变得便利, PEP-517【8】被创建了。它将构建器分成后端和前端。前端提供了一个隔离的 Python 环境,满足所有声明的构建依赖项;后端提供了钩子,被前端从其隔离环境中调用,以生成源发行版或者 wheel。

此外,我们不再通过 setup.py 文件或命令与后端通信,而是使用了 Python 模块和函数。所有后端的打包必须提供一个 Python 对象 API,至少实现 build_wheel【9】和 build_sdist【10】两个方法。该 API 对象是通过 pyproject.toml 文件指定的,使用build-backend 键值:

[build-system]
requires = ["flit"]
build-backend = "flit.api:main"

上述代码对于前端意味着,你可以通过在隔离的 Python 环境中运行它来控制后端:

import flit.api
backend = flit.api.main

# build wheel via 
backend.build_wheel()

# build source distribution via
backend.build_sdist()

由后端决定要在哪里和怎样公开自己的官方 API:

  1. flit【11】通过flit.buildapi实现
  2. setuptools【12】提供了两种变体:setuptools.build_meta(后面会解释原因)
  3. poetry【13】通过poetry.masonry.api实现

因为这些,我们就拥有了不再受 distutils 遗留决策约束的打包工具。

Sarthak Dubey摄/Unsplash--更多 yay!

tox 和打包

tox 是一个测试工具【14】,大多数项目使用它来确保某个包在多个 Python 解释器上的版本兼容性。它还可以轻松地创建 Python 环境,在里面安装被监测的包,从而更快地复现问题。

为了能够测试一个包,它首先需要构建一个源发行版。虽然 PEP-518 和 PEP-517 都带有好的意图,但是在某些情况下,启用它们可能会破坏打包过程。因此,当 tox 在 3.3.0 版本中添加隔离构建时,决定暂时不默认启用它。你需要手动启用它(可能会在今年晚些时候——2019 年的版本 4 中默认启用)。

一旦你指定了一个pyproject.toml ,写了适当的requiresbuild-backend,你需要启用tox.ini 中的isolated_build标志:

[tox]
isolated_build = True

在此之后,在打包过程中【15】,tox 将在独立的 Python 环境中为每个 PEP-518 提供构建依赖项,来构建源发行版,并调用 PEP-517 所述的构建后端。

若不启用该功能,tox 将使用老方法构建源发行版,也就是使用安装了 tox 的解释器来调用python setup.py sdist命令。

Matthew Henry摄/Unsplash--这里没有免费的午餐呢!

小结

Python 打包官方希望所有这些都是有意义的,并因此拥有一个更用户友好的、防错的(error proof )和健壮的构建。这些标准的规范是在 2015 年至 2017 年的长期主题中写作并争论出来的。这两个提案(PEP-517/518)被认为是足够好的,可以获得最大的收益,但是一些不太主流的场景可能会被忽略。

如果你的情况是被忽略的,不要担心,如果我们认为必要的话,PEP 在任何时候都是拥抱改进意见的。在本系列的下一篇文章中【16】,我将讨论社区在发布这两个 PEP 时碰撞到的一些痛点。这些都是我们应该吸取的教训,并且表明着我们仍有一些工作要做。还不是一切都完美,但我们正在变得更好。如果你可以帮帮忙,就加入打包社区吧,让我们一起把事情做得更好!

附1:勘误

前一篇文章中,source distribution 被译成“源码分发”,但它还有一个更被人采用的译法是“源发行版”,为了便于接受,所以本文已作修改。翻译匆忙,如有错误,欢迎读者指正!万分感谢!PS:后续若有修正,会在知乎专栏修改,请关注“Python进阶之旅”:https://zhuanlan.zhihu.com/pythonCat

附2:相关链接

[1] Python packaging - Past, Present, Future: https://www.bernat.tech/pep-517-518/

[2] distutils: https://packaging.python.org/key_projects/#distutils

[3] twine: https://pypi.org/project/twine/

[4] PEP-518 : https://www.python.org/dev/peps/pep-0518/

[5] Paul Ganssle: https://twitter.com/pganssle

[6] flit: https://pypi.org/project/flit/

[7] 计划提供: https://github.com/pypa/setuptools/pull/1675

[8] PEP-517: https://www.python.org/dev/peps/pep-0517/

[9] build_wheel: https://www.python.org/dev/peps/pep-0517/#build-wheel

[10] build_sdist: https://www.python.org/dev/peps/pep-0517/#id9

[11] flit: https://flit.readthedocs.io/en/latest/

[12] setuptools: https://setuptools.readthedocs.io/en/latest/history.html#v40-8-0

[13] poetry: https://poetry.eustace.io/docs/pyproject/#poetry-and-pep-517

[14] 测试工具: https://tox.readthedocs.io/en/latest/

[15] 打包过程中: https://tox.readthedocs.io/en/latest/#system-overview

[16] 下一篇文章中: https://www.bernat.tech/growing-pain/

公众号【Python猫】, 本号连载优质的系列文章,有喵星哲学猫系列、Python进阶系列、好书推荐系列、技术写作、优质英文推荐与翻译等等,欢迎关注哦。

Guess you like

Origin www.cnblogs.com/pythonista/p/12149826.html