Python 項目-本機虛擬環境管理 Redux

https://hynek.me/articles/python-virtualenv-redux/

Python 專案-本地 Virtualenv 管理 Redux

自從我第一篇 TIL(Today I Learned)的文章提到在類 UNIX 操作系統中如何模擬 Node 的 node_modules 的語義到 Python 以後,發生了很多事情(進步了!),該是時候來更新一下了。雖然 direnv 仍然很強大。

對 Python 來說發生了一個重大的變化,是出現了 uv(你看過我的有關它的視頻嗎?);而對我而言,是轉用了基於 ARM 的電腦,需要在 Intel 和 ARM 模式下運行 Python,這在某種程度上讓我的生活變得複雜了(也可參考如何使用 Fish 和 Direnv 自動切換到 Rosetta)。

我也接受了把專案的虛擬環境放在一個名為 .venv 的專案目錄中的新標準。

沒有改變的是我對 direnv 的熱情。廣義來說,當你進入一個目錄時,direnv 會執行一個名為 .envrc 的文件。但是它很聰明,只有在必要時(進入目錄,改變 .envrc)才會執行它,並在你離開目錄時撤銷變更。

它比受尊敬的 .env 文件更強大(它也能加載),我真的很喜歡它作為非壓倒性工具之間的粘合劑。

隨著 Astral 發布了 uv 並接管了 Rye,我認為可以說 Python 的封裝和專案流程工具已進入一個令人興奮的轉折時期。所以,現在還不清楚這篇文章會變得多好過時。但在最壞的情況下,它會成為我們都在騎著獨角獸穿越天際時的歷史記錄。

我不試圖發號施令,而是在解釋我構建工作流程的上下文。我的工作流程並不一定適合您。但我核心的信念是,我不應該記住任何東西,因為我經常忘記一切。

Python 安裝

現在,我從 https://www.python.org/downloads/ 直接獲取盡可能多的 Python 二進製安裝程序,因為它們是通用 2 版本,並且始終以 Intel 模式運行的特殊命令(例如,python3.12-intel64),這使得創建基於 Intel 的虛擬環境需少出錯。如 Glyph 所言:從 Python.org 獲取您的 Mac Python。

另外,我使用 Gregory Szorc 的 python-build-standalone 的安裝程序來補充那些我需要用於我的開源專案的缺失版本。它們的官方版本滯後太多,因此不適用於生產,但對於驅動 tox 和 Nox 卻很方便。

在 Linux 上,我們用 deadsnakes 來處理一切。

我使用不同的機制來管理虛擬環境,取決於開發一個沒有嚴格固定的封包還是一個需要嚴格固定的應用。

未固定的封包

我知道,在穩定的 CI 中釘選您的開發和測試依賴關係是有意義的。

但是,我沒有一個有足夠的變動和依賴關係引起的破壞,以避免在提交歷史中出現 99% 由 Dependabot 更新所造成的憂鬱。

對我來說,這是一個實用的折衷方案,當我遇到問題時,我就貼上或修復。

雖然 direnv 對標準庫的 venv 模組有內置支持,但速度很慢。虛擬環境速度快得多,而 uv 更快。

與應用程式不同,我的封包通常支持超過一個 Python 版本。為了有一個用於存儲封包當前默認開發版本的典型地方,我開始在我的專案中添加一個名為 .python-version-default 的文件,其中包含我想使用的版本。

然後,我在專案的 .envrc 中添加了以下內容:

test -d .venv || uv venv –python $(cat .python-version-default)
source .venv/bin/activate

direnv 確保每當我進入目錄時虛擬環境都存在並處於活動狀態:test -d .venv 檢查 .venv 是否是一個現有的目錄,如果不是,它使用 .python-version-default 中的版本創建它,使用 uv venv。

好處是我可以在 GitHub Actions 中使用相同的文件作為設置-python 的輸入:

– uses: actions/setup-python@v5
with:
cache: pip
python-version-file: .python-version-default

額外提示

這是一個 Fish shell 函數來重新創建虛擬環境(如果需要):

function ,repypkg
rm -rf .tox .nox .venv

# 確保 .venv 存在
direnv exec . python -m site

uv pip install –editable .[dev]
end

考慮到 uv 的速度多麽快,這也是更新所有依賴項的最佳方法。對於 structlog,如果熱緩存,大約需要 600 毫秒。

副作用:我使用 direnv exec 而不是 direnv reload,因為後者似乎是異步運行的,而且 uv pip install 會因為找不到文件或目錄而失敗(os 錯誤 2)。

固定的應用程式

由於在 ARM Mac 上為 Intel Linux 開發,我需要跨平台的鎖文件,這排除了否則優秀的 pip-tools,以及像 Rye 和 uv 這樣的 pip-tools 相關工具。

由於我在使用替代方案時有過糟糕的經驗,我決定使用 PDM,而我通常很滿意它。

PDM 內建支持虛擬環境管理,我直接在我的 .envrc 中使用它:

eval $(pdm venv activate)

額外提示

我們使用 GitLab CI 建立我們的 Python Docker 容器,我通過在專案的 pyproject.toml 中配置 requires-python 來提取正確的 Python 版本:

[project]
name = “some-app”
requires-python = “~=3.12.0”

這樣,我可以在 .gitlab-ci.yml 中的 CI 配置中提取出這個字符串:

# …
build:
stage: build
only: [main]
script:
– export PY=$(sed -nE ‘s/^requires-python = “~=(3\.[0-9]+)\.0″$/python\1/p’ pyproject.toml)
# 現在 PY 類似於 `python3.12`,
# 並且你可以將它作為 ENV 通過 `docker build`。

我可以在我的 .envrc 中完成相同操作,但為什麼不從 .gitlab-ci.yml 中提取命令行並將其評估呢?

eval “$(sed -nE ‘s/^.*- (export PY=.*)/\1/p’ .gitlab-ci.yml)”

現在,我有一個名為 PY 的 shell 變數,它基於 pyproject.toml 中的元數據提取出類似於 python3.12 的版本,而沒有重復。作為額外禮物,它還驗證了 CI 中版本提取是否有效。

在本地,當我想重新創建專案的虛擬環境時,我會使用那個變數:

rm -rf .venv

pdm venv create $(command -v $PY)
pdm use –ignore-remembered .venv/bin/python
pdm sync –dev

如果您對 command -v 不是 POSIX 不滿意,請參閱 TIL:。

via Hacker News

April 3, 2024 at 05:16PM

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *