Following system colour scheme - Python 增強提案 Selected dark colour scheme - Python 增強提案 Selected light colour scheme - Python 增強提案

Python 增強提案

PEP 396 – 模組版本號

作者:
Barry Warsaw <barry at python.org>
狀態:
已撤回
型別:
資訊性
主題:
打包
建立日期:
2011年3月16日
釋出歷史:
2011年4月5日

目錄

重要

本 PEP 已撤回。

×

有關在執行時訪問包版本資訊的最新建議,以及定義與包分發元資料自動保持一致的執行時 __version__ 屬性,請參閱 Python Packaging User Guide 中的 在執行時訪問版本資訊單源版本

摘要

鑑於為 Python 模組指定版本號既有用又常見,並且考慮到社群中已自然衍生出多種方法來執行此操作,因此有必要建立模組作者應遵循和引用的標準約定。本資訊性 PEP 描述了希望定義其 Python 模組版本號的模組作者的最佳實踐。

遵循此 PEP 是可選的;但是,其他 Python 工具(如 distutils2 [1])可能會改編以使用此處定義的約定。

PEP 拒絕/撤回

此 PEP 於 2021 年 4 月 14 日被正式拒絕。自本 PEP 初次撰寫以來,打包生態系統已發生顯著變化,並且 importlib.metadata.version() [11] 等 API 提供了更好的體驗。

此拒絕於 2024 年 10 月 21 日被重新歸類為撤回,因為之前的狀態被誤解 [12],暗示 *不應* 有任何模組定義 __version__ 屬性,但這絕非事實。

模組仍可根據其選擇定義 __version__。但是,選擇 *不* 這樣做不會妨礙查詢已安裝分發包的版本資訊,因此資訊性 PEP 不是記錄有關模組 __version__ 屬性使用的社群慣例的正確工具(這些慣例最好在 Python Packaging User Guide 中涵蓋)。

使用者故事

Alice 正在編寫一個名為 alice 的新模組,她希望與其他 Python 開發人員共享。 alice 是一個簡單的模組,位於一個檔案中 alice.py。Alice 希望指定一個版本號,以便她的使用者可以知道他們正在使用哪個版本。由於她的模組完全位於一個檔案中,因此她希望將版本號新增到該檔案中。

Bob 編寫了一個名為 bob 的模組,並與許多使用者共享。 bob.py 包含一個版本號,以方便使用者。Bob 瞭解 Cheeseshop [2],並使用經典的 distutils 添加了一些簡單的打包,以便他可以將 *The Bob Bundle* 上傳到 Cheeseshop。由於 bob.py 已經指定了一個使用者可以以程式設計方式訪問的版本號,因此他希望即使他的使用者現在從 Cheeseshop 獲取該版本號,相同的 API 也能繼續工作。

Carol 維護著幾個名稱空間包,每個包都經過獨立開發和分發。為了讓她的使用者能夠正確指定對其包正確版本的依賴項,她在名稱空間包的 setup.py 檔案中指定了版本號。由於 Carol 希望每個包只更新一個版本號,因此她在模組中指定版本號,並在構建 *sdist* 存檔時由 setup.py 提取模組版本號。

David 維護著標準庫中的一個包,並且還為其他 Python 版本製作獨立版本。標準庫副本在模組中定義了版本號,並且相同的版本號也用於獨立分發。

基本原理

Python 模組(包括標準庫和第三方提供的模組)長期以來一直包含版本號。有描述版本號的既定事實標準,多年來也自然衍生出許多臨時的做法。通常,可以透過匯入模組並檢查屬性來以程式設計方式檢索模組的版本號。經典的 Python distutils setup() 函式 [3] 描述了一個 version 引數,可以在其中指定發行版的版本號。 PEP 8 描述了使用名為 __version__ 的模組屬性來記錄使用關鍵字擴充套件的“Subversion, CVS, 或 RCS”版本字串。在 PEP 作者自己的電子郵件存檔中,獨立模組開發者使用 __version__ 模組屬性的最早例子可以追溯到 1995 年。

版本資訊的另一個例子是 sqlite3 [5] 模組,它具有 sqlite_version_infoversionversion_info 屬性。可能不清楚哪個屬性包含模組的版本號,哪個屬性包含底層 SQLite3 庫的版本號。

本資訊性 PEP 規範了既定實踐,並推薦了描述模組版本號的標準方法,以及包含或 *不* 包含它們的某些用例。模組作者是否採用純屬自願;標準庫中的打包工具將為本文件定義的標準提供可選支援,Python 宇宙中的其他工具也可能遵守。

規範

  1. 一般來說,標準庫中的模組 *不應* 包含版本號。它們隱式承載其包含的 Python 發行版的版本號。
  2. 根據具體情況,同時以獨立形式為其他 Python 版本釋出的標準庫模組,在包含在標準庫中時 *可以* 包含模組版本號,並且在單獨打包時 *應* 包含版本號。
  3. 當模組(或包)包含版本號時,版本號 *應* 可在 __version__ 屬性中訪問。
  4. 對於位於名稱空間包內的模組,模組 *應* 包含 __version__ 屬性。名稱空間包本身 *不應* 包含其自己的 __version__ 屬性。
  5. __version__ 屬性的值 *應* 為字串。
  6. 模組版本號 *應* 符合 PEP 386 中指定的規範化版本格式。
  7. 模組版本號 *不應* 包含版本控制系統提供的修訂號,或任何其他語義上不同的版本號(例如底層庫的版本號)。
  8. 經典 distutils setup.py 檔案中的 version 屬性,或 PEP 345 Version 元資料欄位 *應* 源自 __version__ 欄位,反之亦然。

示例

從第三方包檢索版本號

>>> import bzrlib
>>> bzrlib.__version__
'2.3.0'

從也作為獨立模組分發的標準庫包中檢索版本號

>>> import email
>>> email.__version__
'5.1.0'

名稱空間包的版本號

>>> import flufl.i18n
>>> import flufl.enum
>>> import flufl.lock

>>> print flufl.i18n.__version__
1.0.4
>>> print flufl.enum.__version__
3.1
>>> print flufl.lock.__version__
2.1

>>> import flufl
>>> flufl.__version__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__version__'
>>>

推導

模組版本號可以出現在至少兩個地方,有時甚至更多。例如,根據本 PEP,它們可以透過模組的 __version__ 屬性以程式設計方式訪問。在經典的 distutils setup.py 檔案中,setup() 函式接受一個 version 引數,而 distutils2 setup.cfg 檔案有一個 version 鍵。版本號還必須進入 PEP 345 元資料,最好是在構建 *sdist* 存檔時。模組作者最好只指定一次版本號,並讓所有其他用法都源自此單一定義。

這可以透過多種方式完成,其中一些方式概述如下。這些僅供說明之用,並非旨在具有決定性、完整性或包容性。其他方法也是可能的,並且下面包含的一些方法可能存在限制,導致它們在某些情況下無法使用。

假設 Elle 將此屬性新增到她的模組檔案 elle.py

__version__ = '3.1.1'

經典 distutils

在經典 distutils 中,將版本字串新增到 setup.pysetup() 函式的最簡單方法是這樣做:

from elle import __version__
setup(name='elle', version=__version__)

然而,根據 PEP 作者的經驗,這在某些情況下可能會失敗,例如當模組透過 2to3 程式使用自動 Python 3 轉換時(因為 setup.pyelle 模組轉換之前由 Python 3 執行)。

在這種情況下,編寫一些程式碼來解析檔案中的 __version__ 而不是匯入它,難度並不大。不提供太多細節,像 distutils2 這樣的模組很可能會提供一種從檔案中解析版本字串的方法。例如:

from distutils2 import get_version
setup(name='elle', version=get_version('elle.py'))

Distutils2

由於 distutils2 風格的 setup.cfg 是宣告式的,我們無法執行任何程式碼來提取 __version__ 屬性,無論是透過匯入還是透過解析。

與 distutils-sig [9] 協商後,提出了兩個選項。兩者都包含將版本號儲存在一個檔案中,並在 setup.cfg 中宣告該檔案。當檔案內容包含版本號時,將使用 version-file 鍵。

[metadata]
version-file: version.txt

當版本號包含在更大的檔案中時,例如 Python 程式碼中,因此必須解析該檔案以提取版本,將使用鍵 version-from-file

[metadata]
version-from-file: elle.py

將對冒號後面的檔名執行類似上述的解析方法。具體的實現方法將在相應的 distutils2 開發論壇中討論。

另一種選擇是僅在 setup.cfg 中定義版本號,並使用 pkgutil 模組 [8] 以程式設計方式提供它。例如,在 elle.py 中:

from distutils2._backport import pkgutil
__version__ = pkgutil.get_distribution('elle').metadata['version']

PEP 376 元資料

PEP 376 定義了靜態元資料的標準,但沒有描述建立此元資料的過程。最好在構建時而不是安裝時將派生的版本資訊放入 PEP 376 .dist-info 元資料中。這樣,即使程式碼未安裝,元資料也可以用於內省。

參考資料


來源:https://github.com/python/peps/blob/main/peps/pep-0396.rst

最後修改:2025-02-01 08:50:23 GMT