English|Python打包-过去、现在、未来【1】原创|BERNATGABOR翻译|,请勿用于商业或非法用途。你有没有想过当你运行pipinstall时到底发生了什么?这篇文章将详细概述过去涉及的步骤,以及它如何随着PEP-517和PEP-518的采用而发生变化。在之前的文章中,我描述了如何安装三种类型的内容:源代码树、源代码分发和wheel。只有最后两种上传到PyPi中央仓库,但你也可以获取源码树(例如通过为pip加入git协议)。wheels优于其他类型的优势在于它们不需要在用户机器上进行任何构建操作;只需要下载和提取。构建Python包现在独立于构建环境(用户或开发人员的机器),但您仍然需要构建包(sdist或wheel)。为此,您需要一些合适的构建器。过去,对第三方包的需求很早就表现出来了。遵循内置电池的原理,在2000年的Python1.6中,distutils[2]包被添加到Python标准库中。它引入了包含构建逻辑的setup.py文件的概念,由pythonsetup.py命令触发。它允许用户将代码打包成一个库,但是没有依赖库的声明和自动安装等功能。此外,它的升级周期与核心解释器的发布周期直接相关。创建于2004年,setuptools建立在distutils之上,并用其他强大的功能对其进行了扩展。它很快变得如此流行,以至于大多数Python包都开始将它与核心解释器一起提供。那时,所有包都是源代码分发。wheel分发方式出现的很晚,2014年。distutils是在只有少数人非常精通打包的时候创建的。所以它非常灵活和命令式,你写一个Python脚本可以修改包生成过程中的每一步。但这样做的缺点是它一点也不容易学习和理解。随着Python越来越受欢迎,这开始成为一个日益严重的问题,因为越来越多的用户不熟悉Python的内部工作原理。?CharlesPH/Unsplash拍摄的照片——呃构建依赖关系关于安装源分发,pip基本上执行以下操作:找到包下载源分发并解压缩它在解压缩的文件夹上运行pythonsetup.pyinstall(构建+安装)开发者运行pythonsetup.pysdist生成分发包,运行pythonsetup.pyupload上传到中央存储仓库(upload命令在2013年因为twine[3]工具被弃用,主要是upload使用了不安全的HTTP连接,上传命令执行新构建,这不允许最终用户在实际上传之前检查生成的包)。当pip运行pythonsetup.pyinstall时,它使用Python解释器来安装包。因此,构建操作可以访问该解释器中已经存在的所有第三方包。最值得注意的是,它完全使用安装在主机Python解释器上的setuptools版本。如果软件包使用了setuptools的新版本功能,完成安装的唯一方法是首先更新已安装的setuptools。如果新版本包含会破坏其他包的错误,这可能会导致问题。这在用户无法更改已安装软件包的系统上尤其麻烦。当构建器(例如setuptools)希望使用其他辅助包(例如cython)时,这也是一个问题。如果缺少构建器帮助,通常会抛出导入包失败的错误:File"setup_build.py",line99,inrunfromCython.BuildimportcythonizeImportError:NomodulenamedCython.Buildisonthe开发人员方面,没有提供此类构建依赖项的方法。在用户端,所有包构建依赖项都需要预先安装,即使它们不会在运行时使用。为了解决这个问题,创建了PEP-518[4]。这个想法是,与其将主机的Python与其当前安装的构建包一起使用,不如让包能够明确声明其构建运行所需的内容。此外,我们没有在主机Python上提供此功能,而是创建了一个单独的Python(某种虚拟环境)来运行打包。pythonsetup.pyinstall现在可以:创建一个临时文件夹创建一个隔离的(来自第三方库站点包)Python环境python-mvirtualenvour_build_env,我们称这个Python可执行文件python_isolated安装构建依赖项通过python_isolatedsetup生成一个用于安装的轮子。pybdist_wheel将wheel解压到Python的sitepackages文件夹有了这个,我们就可以安装依赖cython的包,而无需在运行的Python环境中实际安装cython。是pyproject.toml元数据文件,指定了构建依赖的文件和方法:[build-system]requires=["setuptools>=40.8.0","wheel>=0.30.0","cython>=0.29.4",]此外,它允许打包者指定他们需要的最低版本,这可以很容易地在用户机器上通过pip找到。在开发人员的机器上构建源代码分发或wheel时,也可以使用相同的机制。当一个人调用pipwheel时。--no-deps命令,该命令会在后台自动创建一个包含构建依赖项的独立Python,然后在该环境中调用pythonsetup.pybdist_wheel或pythonsetup.pysdist命令。BruceGalpin/Unsplash摄——耶!打包工具五花八门但是这里有个问题。注意,所有这些操作还是要经过20年前引入的机制,即执行setup.py。整个生态系统仍然建立在distutils和setuptools接口之上,并且为了保持向后兼容性,不能进行重大更改。此外,在打包过程中执行用户端Python代码是很危险的,这可能会导致经验不足的用户难以调试的细微错误。20年前,命令式构建系统对于灵活性非常重要,以至于我们不知道所有情况,但现在我们知道了,有可能创建非常健壮和简单的包构建器。引用PaulGanssle[5](setuptools和dateutil的维护者)的话:理想情况下,默认选项应该是在99%的时间内都有效的声明式构建配置,并在真正需要灵活性时回退到命令式系统选项。在这种情况下,如果你发现还是需要选择使用命令式构建,那我们就可以认为是有badsmell代码了。setup.py最大的问题是大多数人以声明方式使用它,因此当他们命令式使用它时,他们往往会在构建系统中引入错误。一个这样的例子:如果你有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环境,满足所有声明的构建依赖;后端提供前端从其隔离环境中调用的钩子,以生成源代码分发或轮子。此外,我们不使用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环境中运行它来控制后端:importflit.apibackend=flit.api.main#buildwheelviabackend.build_wheel()#buildsourcedistributionviabackend.build_sdist()这取决于backend决定去哪里以及如何公开自己的官方API:flit[11]通过flit.buildapi实现了setuptools[12],并提供了两个变体:setuptools.build_meta(后面会解释原因)poetry[13]通过poetry.masonry.api因为这些,我们拥有不再受distutils遗留决定约束的打包工具。?摄影:SarthakDubey/Unsplash--更多yay!tox和packagedtox是大多数项目使用的测试工具[14],用于确保某个包在多个Python解释器上的版本兼容性。它还可以轻松创建一个Python环境,在该环境中安装检测包以更快地重现问题。为了能够测试一个包,它首先需要构建一个源代码分发。虽然PEP-518和PEP-517都有良好的意图,但在某些情况下启用它们可能会破坏打包过程。所以当tox在3.3.0版本添加隔离构建时,决定暂时不默认启用它。您需要手动启用它(可能会在今年晚些时候-2019年的版本4中默认启用)。一旦你指定了一个pyproject.toml,写了适当的需求和构建后端,你需要在tox.ini中启用isolated_build标志:[tox]isolated_build=True在此之后,在打包过程中[15],tox将提供在单独的Python环境中根据PEP-518构建依赖项以构建源代码分发并调用PEP-517描述的构建后端。如果不启用此功能,tox将使用旧方法构建源代码分发,即使用与tox一起安装的解释器来调用pythonsetup.pysdist命令。MatthewHenry/Unsplash摄——天下没有免费的午餐!总结Python打包官方希望所有这些都有意义,从而拥有一个更加用户友好、防错和健壮的构建。这些标准的规范是在2015年到2017年的长期主题中编写和辩论的。这两个提案(PEP-517/518)被认为足以实现最大利益,但一些不太主流的场景可能会被忽略。如果您的案例被忽视,请不要担心,如果我们认为有必要,PEP会随时接受改进建议。在本系列的下一篇文章[16]中,我将讨论社区在发布这两个PEP时遇到的一些痛点。这些是我们应该吸取的教训,表明我们还有一些工作要做。并非一切都是完美的,但我们正在变得更好。如果您能提供帮助,请加入包装社区,让我们一起让事情变得更好!附件一:勘误在上一篇文章中,sourcedistribution被翻译为“源代码分发”,但它有一个被更多人使用的翻译方式是“sourcereleaseversion”。为了便于接受,本文进行了修改。翻译仓促,如有错误,欢迎读者指正!非常感谢!PS:以后如有更正,会在知乎专栏修改。请关注《Python进阶之旅》:https://zhuanlan.zhihu.com/pythonCat附件二:相关链接【1】Python打包-过去、现在、未来:https://www.bernat.tech/pep-5...[2]distutils:https://packaging.python.org/...[3]麻线:https://pypi.org/project/twine/[4]PEP-518:https://www.python.org/dev/pe...[5]PaulGanssle:https://twitter.com/pganssle[6]flit:https://pypi.org/project/flit/[7]计划提供:https://github.com/pypa/setup...[8]PEP-517:https://www.python.org/dev/pe...[9]build_wheel:https://www.python.org/dev/pe...[10]build_sdist:https://www.python.org/dev/pe...[11]flit:https://flit.readthedocs.io/e...[12]setuptools:https://setuptools.readthedoc...[13]诗歌:https://poetry.eustace.io/doc...[14]测试工具:https://tox.readthedocs.io/en...[15]打包过程中:https://tox.readthedocs.io/en...[16]下一篇:https://www.bernat.tech/growi...公众号【Python猫】,本号连载一系列优质文章,包括喵星哲学猫系列、Python进阶系列、好文书籍推荐系列、技术写作、优质英文推荐及翻译等,欢迎关注。
