Python 打包的現狀:包的三種類型

英文 | The state of Python Packaging【1】python

原做 | BERNAT GABORgit

譯者 | 豌豆花下貓github

聲明 :本文得到原做者受權翻譯,轉載請保留原文出處,請勿用於商業或非法用途。segmentfault

pip 19.0 已經於 2019 年 1 月 22 日發佈。在其功能列表中,最值得注意的是它如今支持 PEP-517,默認狀況下是支持的,若是項目的根目錄中有一個 pyproject.toml。該 PEP 於 2015 年建立,並於 2017 年被接受。儘管 pip 花了一段時間才實現它,但該版本及其後續問題卻代表,不少人根本不熟悉它。ide

若是你想了解 Python 打包(packaging)生態的現狀及未來如何演變,請繼續閱讀。咱們但願,即便上述提到的 Python 加強提案(譯註:即 PEP,關於 PEP 的介紹,請閱讀這篇文章),現在可能會引發一些不愉快,但從長遠來看,咱們將從中受益。函數

我大約在三年前加入了 Python 開源社區(儘管使用它已有 8 年之久)。從早期開始,我就據說 Python 打包有一點黑匣子的名聲。它有不少未知的內容,人們一般只複製其它項目的構建配置文件,就使用上了。工具

在嘗試更好地理解這個黑匣子,並對其進行改進的過程當中,我已經成爲了 virtualenv 和 tox 項目的維護者,偶爾也爲 setuptools 和 pip 作些貢獻。性能

我但願對這個主題進行詳盡的(並但願是一個較高水平的)論述,並決定將其分爲三個部分。在這第一篇文章中,我將對 Python 打包的工做方式及其所具備的打包類型進行大概介紹。在第二篇文章中,我將詳細地介紹軟件包的安裝方式,以及 PEP-517/518 是如未嘗試對其進行改進的。最後,我再專門寫另外一篇文章,以介紹在引入這些改進時,咱們吸收的一些痛苦的教訓。單元測試

事先聲明,我將主要關注 Python 官方的打包系統(即 pip、setuptools,所以沒有 conda 或特定於操做系統的打包程序)。測試

Marcus Cramer 攝/Unsplash--人們第一次凝視 Python 打包時的臉

一個示例項目

爲了講這個故事,我須要先講講如何分發 Python 軟件包的故事;更具體地說,包的安裝在過去是如何運做的,以及咱們但願它在未來如何運做。

爲了有一個具體的示例,讓我介紹一下個人很棒的示例庫:pugs 。這個庫至關簡單:它只生成一個名爲 pugs 的包,僅包含一個名爲 logic 的模塊。關於 pugs,你猜對了,logic 被用於生成隨機的引號。這是一個展示爲源碼樹(source tree)的簡單示例結構(能夠在gaborbernat / pugs 【2】裏得到):

pugs-project
├── README.rst
├── setup.cfg
├── setup.py
├── LICENSE.txt
├── src
│   └── pugs
│       ├── __init__.py
│       └── logic.py
├── tests
│   ├── test_init.py
│   └── test_logic.py
├── tox.ini
└── azure-pipelines.yml

這裏有四類獨特的內容:

咱們的pugs 包在用戶機器的解釋器上能用,意味着什麼?在理想狀況下,一旦啓動解釋器,用戶應該可以 import 它,並調用其中的函數:

  • 業務邏輯代碼(src 文件夾中的內容)
  • 測試代碼(tests 文件夾和 tox.ini)
  • 包代碼和元數據(setup.py、setup.cfg、LICENSE.txt、README.rst--請注意,咱們現在使用的是事實上的標準打包工具setuptools【3】)
  • 有助於項目管理和維護的文件:
    • 持續集成(azure-pipelines.yml)
    • 版本控制(.git)
    • 項目管理(例如潛在的 .github 文件夾)
Python 3.7.2 (v3.7.2:9a3ffc0492, Dec 24 2018, 02:44:43)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pugs
>>> pugs.do_tell()
"An enlightened pug knows how to make the best of whatever he has to work with - A Pug's Guide to Dating -  Gemma Correll"

Ryan Antooa 攝/Unsplash--讓咱們開始吧,興奮!

Python 包的可用性

Python 怎麼知道什麼可用或不可用?簡短的答案是,它不知道。至少不在前期知道。相反,它將嘗試加載,並動態地檢查是否可用。

它從哪裏加載?有許多可能的位置,可是在大多數狀況下,咱們說的是從文件系統的文件夾中加載。這個文件夾在哪裏呢?對於給定的模塊,能夠打印該模塊的表示(representation)來找出:

>>> import pugs
>>> pugs
<module 'pugs' from '/Users/bernat/Library/Python/3.7/lib/python/site-packages/pugs/__init__.py'>

你會發現文件夾的位置取決於:

  • 軟件包的類型(三方庫或者標準庫的內置/aka部分)
  • 它是全局的或僅限於當前的用戶(請參閱PEP-370【4】)
  • 以及它是系統 Python 仍是一個虛擬環境

可是通常來講,對於給定的 Python 解釋器,能夠經過打印出 sys.path 變量的內容,來找到可能的目錄列表,例如在個人 MacOS 上:

>>> import sys
>>> print('\n'.join(sys.path))
/Library/Frameworks/Python.framework/Versions/3.7/lib/python37.zip
/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7
/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload
/Users/bernat/Library/Python/3.7/lib/python/site-packages
/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages

對於第三方軟件包,會是一些 site-packages 文件夾。在以上示例中,請注意哪些是在整個系統範圍內,哪些僅屬於一個特定的用戶。這些包是如何被放在此文件夾中的?它必定是由某些安裝程序放在那裏的。

下圖展現了大多數的運行狀況:

  1. 開發者在文件夾(稱爲源碼樹)內編寫一些 Python 代碼。
  2. 而後,某些工具(例如 setuptools)將源碼樹打包以進行從新分發。
  3. 生成的軟件包經過另外一個工具(twine),上傳到能夠被終端用戶計算機訪問的中央存儲倉(一般爲https://pypi.org【5】)。
  4. 終端用戶計算機使用一些安裝程序來查找、下載和安裝相關軟件包。安裝操做最終是在 site-packages 文件夾內,建立正確的目錄結構和元數據。

Pinho/攝--在探索新鮮事物

Python 包的類型

在安裝時,軟件包必須生成至少兩種類型的內容,以放入 site-packages 中:有關軟件包內容的元數據文件夾,其中包含 {package}-{version} .dist-info 和業務邏輯文件。

/Users/bgabor8/Library/Python/3.7/lib/python/site-packages/pugs
├── __init__.py
├── __pycache__
│   ├── __init__.cpython-37.pyc
│   └── logic.cpython-37.pyc
└── logic.py

/Users/bgabor8/Library/Python/3.7/lib/python/site-packages/pugs-0.0.1.dist-info
├── INSTALLER
├── LICENSE.txt
├── METADATA
├── RECORD
├── WHEEL
├── top_level.txt
└── zip-safe

發行信息(dist-info)文件夾描述了該軟件包:用於安裝該軟件包的安裝程序、該軟件包所附的許可證、在安裝過程當中建立的文件、頂層 Python 軟件包是什麼、該軟件包暴露的入口等等。在PEP-427【6】 中能夠找到每一個文件的詳細說明。

咱們如何從源碼樹中得到這兩種類型的內容呢?咱們面前有兩條大相徑庭的路徑:

  1. 從咱們的源碼樹生成此目錄結構和元數據,將其壓縮爲單個文件,而後將其發佈到中央軟件包存儲倉。在這種狀況下,安裝程序必須下載軟件包並將其解壓到 site-packages 文件夾中。咱們將這種類型的包稱爲 wheel 包。
  2. 或者,你能夠建立一個包含軟件包源碼的歸檔文件,構建所需的腳本和元數據,以生成可安裝的(installable)目錄結構,而後將其上傳到中央存儲倉。這稱爲源碼分發或 sdist。在這種狀況下,安裝程序還有不少工做要作,它須要解壓歸檔文件,運行構建器,而後再將其複製。

這兩個方法的區別主要在於包的編譯/構建操做發生在哪裏:在開發者的計算機上仍是在終端用戶的計算機上。若是它發生在開發者的一邊(例如在 wheel 的狀況下),則安裝過程很是輕巧。一切都已經在開發機器上完成了。用戶機器的操做僅是簡單的下載和解壓。

在本例中,咱們使用 setuptools 做爲構建器(從源碼樹生成要放入 site-packages 文件夾中的內容)。所以,爲了在用戶機器上執行構建操做,咱們須要確保在用戶機器上有合適版本的 setuptools (若是你使用的是 40.6.0 版的功能,則必須確保用戶具備該版本或大於該版本)。

要考慮的另外一種狀況是 Python 提供了從其內部訪問 C/C++ 庫的能力(在須要的地方得到額外的性能)。這樣的軟件包被稱爲 C 擴展包(C-extension packages),由於它們利用了 CPython 提供的 C 擴展 API。

此類擴展須要編譯 C/C++ 功能,才能適用與其交互的 C/C++ 庫和當前 Python 解釋器的 C-API 庫。在這些狀況下,構建操做實際上涉及到調用一個二進制編譯器,而不只僅是像純 Python 包(例如咱們的 pugs 庫)那樣,生成元數據和文件夾結構。

若是在用戶計算機上進行構建,則須要確保在構建時,有可用的正確的庫和編譯器。如今這是一項相對困難的工做,由於有些特定於平臺的二進制文件,也是經過平臺打包工具分發的。這些庫的缺失或版本不匹配一般會在構建時觸發隱祕的錯誤,使用戶感到沮喪和困惑。

所以,若是可能的話,始終選擇將 package 打包成 wheel。這將徹底避免用戶缺乏正確的構建依賴項的問題(純 Python 類型如 setuptools 或二進制類型的 C/C++ 編譯器)。即便這些構建依賴項易於配置(例如,使用純 Python 構建器--例如 setuptools),你徹底能夠避免此步驟,來節省安裝的時間。

話雖如此,仍然有兩種須要提供源碼分發的狀況(即便在你提供 wheel 的狀況下):

  1. C 擴展的源碼分發每每更易於審覈,由於人們能夠閱讀源代碼,從而在其內容上有更高的透明度:許多大型公司的環境出於此單一緣由,更傾向於使用 wheel(它們一般會將此擴展到純 Python wheel,主要是爲了不對哪些是純 Python 和什麼不是作分類)。
  2. 你可能沒法爲每一個可能的平臺都提供一個 wheel(在使用 C 擴展包的狀況下,尤爲如此),在這種狀況下,源碼分發可讓這些平臺自行生成 wheel。

小結

源碼樹(source tree)、源碼分發(source distribution)和 wheel 之間的區別:

  • 源碼樹——包含在開發者的機器/存儲倉上可用的全部項目文件(業務邏輯、測試、打包數據、CI 文件、IDE 文件、SVC 等),例如,請參見上面的示例項目。
  • 源碼分發——包含構建 wheel 所需的代碼文件(業務邏輯+打包數據+一般還包括單元測試文件,用於校驗構建;可是不包含開發者環境的內容,例如 CI/IDE/版本控制文件),格式:pugs-0.0 .1.tar.gz 。
  • wheel——包含包的元數據和源碼文件,被放到 site packages 文件夾,格式:pugs-0.0.1-py2.py3-NONE-any.whl 。

Charles PH 攝/Unsplash--hmmm

可在此閱讀本系列的下一篇文章【7】,瞭解在安裝軟件包時會發生什麼。謝謝閱讀!

相關連接

[1] The state of Python Packaging: https://www.bernat.tech/pep-5...

[2] gaborbernat / pugs: https://github.com/gaborberna...

[3] setuptools: https://pypi.org/project/setu...

[4] PEP-370: https://www.python.org/dev/pe...

[5] https://pypi.orghttps://pypi.org/

[6] PEP-427: https://www.python.org/dev/pe...

[7] 下一篇文章: https://www.bernat.tech/pep-5...

公衆號【Python貓】, 本號連載優質的系列文章,有喵星哲學貓系列、Python進階系列、好書推薦系列、技術寫做、優質英文推薦與翻譯等等,歡迎關注哦。

相關文章
相關標籤/搜索