Python Table Manners - 虛擬環境和套件管理


為了避免不同 Python 專案使用的套件版本不同,導致本機的套件版本打架
開始 Python 專案時,第一步就是先建立一個專屬於專案的虛擬環境
理想上,本機的環境是不太需要安裝套件的
因為每一個專案都會在自己的虛擬環境內執行,並且在裡面安裝各自的套件

virutalenv

創建虛擬環境,最基本作法就是使用 virtualenv
透過以下幾個指令建立一個,並把相關的套件寫入 requirements.txt

# 創建 Python 虛擬環境
python -m venv ./venv

# 啟動 Python 虛擬環境 (Unix like)
source venv/bin/activate

# 將虛擬環境用到的套件寫入 requirements.txt
pip freeze >> requirements.txt

p.s. 基本上這個系列文會以 Unix 系統(macOS, Linux)為主

但這個做法會遇到幾個問題

  1. 忘記開啟/關閉虛擬環境
  2. 忘記把新安裝的套件寫入 requirements.txt
  3. pip freeze 安裝一些不必要的套件

一開始我會使用 pipreqs 來解決 pip freeze 所造成的雜亂
但是忘記寫入套件跟忘記開關虛擬環境的問題還是存在
常常我在本地測試成功,丟到伺服器就會告訴我又少了套件
只好再送一個 Pull Request 上去

pipenv

pipenv 可以用來同時管理虛擬環境跟套件
pipenv 不使用 requriements.txt,而是使用自定義的 PipfilePipfile.lock 管理套件
它的好處是能透過單一指令將套件安裝到虛擬環境中,並且更新到 Pipfile 以及 Pipfile.lock

安裝 pipenv

根據不同的系統,可以在 Installing Pipenv 找到安裝 pipenv 的方式

不過最簡單的做法就是直接透過 pip 安裝

python -m pip install --user pipenv

初始化環境

# 初始化虛擬環境
# 如果 Pipfile 跟 Pipfile.lock 還不存在,則會產生
pipenv install

# 指定用特定版本的 Python 來產生虛擬環境
pipenv install --python 3.6.4

# 移除虛擬環境
pipenv --rm

# 列出虛擬環境的所在位置
pipenv --where

初始化虛擬環境後,會產生 PipfilePipfile.lock

  • Pipfile 是一個 toml 格式的檔案
    • source: 指定要去找套件的 PyPI ,比較常見的使用案例是加上自己私有的 PyPI
    • dev-package: 開發環境所需套件
    • packages: 預設安裝套件(通常是 Production 用)
    • requires: 限定在某個版本的 Python
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]

[requires]
python_version = "3.7"
  • Pipfile.lock 則是 JSON 格式的檔案
    • 同樣是記錄安裝的套件,但會同時記錄下套件相依的其他套件(假設安裝了 requests 套件,而 requests 相依於 urllib3 ,則 requestsurllibs 都會列在這)
{
    "_meta": {
        "hash": {
            "sha256": "..."
        },
        "pipfile-spec": 6,
        "requires": {
            "python_version": "3.7"
        },
        "sources": [
            {
                "name": "pypi",
                "url": "https://pypi.org/simple",
                "verify_ssl": true
            }
        ]
    },
    "default": {},
    "develop": {}
}

通常沒有什麼特別的理由,可以不用動到 PipfilePipfile.lock
交給 pipenv 去管理就好

安裝套件

pipenv 的另一個好處是,它的 API 基本上跟 pip 是一樣的

# 安裝套件
pipenv install <package>==<version>

# 解除安裝套件
pipenv uninstall <package>

# 更新套件
pipenv update <package>

以安裝 requests 為例

Pipfile 會更新在 packages

[packages]
requests = "*"

Pipfile.lock 中除了 requests 外,還會列出 requests 的相依套件 urllib3 (Ref: setup.py#L47)

{
    ......
    "default": {
        ......

        "requests": {
            "hashes": [
                "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
                "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
            ],
            "index": "pypi",
            "version": "==2.22.0"
        },
        "urllib3": {
            "hashes": [
                "sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398",
                "sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86"
            ],
            "version": "==1.25.6"
        }
    },
    ......
}

可以注意到每個安裝的套件會有三個欄位 index, versionhashes

  • index: 套件從哪裡裝的
  • version: 安裝的版本
  • hashes 安裝這個套件時產生的雜湊值 (hashing)
    只要套件內容有改變,就會產生不同的雜湊值
    可以用於驗證下次安裝時,這個套件的內容是否跟這次相同
    有些套件雖然改了內容,但沒有更新版本號
    雜湊值可以用來避免安裝了不同的內容卻渾然不知

下面兩個指令就會運用到 hashes

# 安裝時同時確認 Pipfile.lock ,如果 lock 檔跟實際安裝的有衝突,則會跳出安裝
pipenv install --deploy

# 直接使用 Pipfile.lock 安裝套件
pipenv install --ignore-pipfile

安裝開發環境套件

有些套件(e.g., 測試工具)不需要跟著系統一起上線
以往會將不同的套件用不同的 requirements.txt 來管理 (e.g., requirments/dev.txt, requirements/prod.txt)

pipenv 則是將開發環境才需要的套件寫在 Pipfile 的 dev-packages
只要在安裝時後面加上選項 --dev

e.g.,

# 安裝開發環境套件
pipenv install <package>==<version> --dev

在虛擬環境中執行指令

# 在虛擬環境中執行 [command]
pipenv run <command>

# e.g.,
pipenv run python your_program.py

雖然可以透過 pipenv shell 進入到虛擬環境,但我不太建議使用
原因是我常常會進入虛擬環境後,亂下 pip install <package> 的指令
然後就沒被 Pipfile 給記錄到...

其他功能

  • pipenv check: 檢查安裝的套件是否有已知的安全性問題
  • pipenv graph: 檢視整個相依套件的相依圖
  • pipenv open <package>: 直接安裝的套件 (不知道什麼時候養成了「懶得看文件,直接 trace code」的習慣...)

pipenv 已經很久沒更新了,沒有問題嗎?

雖然我的確遇過一些小問題 (e.g., pipenv 和 poerty 如何處理在不同作業系統下相依套件不同)
但大部分的狀況下, pipenv 已經足夠解決我的問題
所以沒什麼意外我大概還是會繼續使用 pipenv

其他工具

poetry

poetry 是目前最多人說可以取代 pipenv 的工具
除了 pipenv 包含的功能外,它還能用來初始化專案、上傳套件

我使用下來體驗還算不錯
而且 poetry 採用 pyproject.toml 來做配置設定,這點我就蠻喜歡的

但我比較不喜歡的點是,它採用的 API 跟版本標示方式都跟 pip 不同
會多增加學習成本

pip-tools

pip-tools 主要的功能是產生 hashes ,並不能用來管理虛擬環境
這套工具比較適合習慣使用 pip 跟 virtualenv ,但又想要有 Pipfile.lock 的功能的情況

dephell

dephell 是一個 all-in-one 的工具
當初看到覺得很有趣,但還沒有時間好好研究
目前使用到對我最有幫助的功能是它能在轉換不同的格式 (e.g., Pipfile → pyproject.toml)

自動將舊版套件升級

以下再多提供兩個可以自動將相依套件升級的服務

Bouns: pipx - 在系統安裝 Python 工具

雖然說建議 Python 的套件都裝在虛擬環境
但如果平時要使用的工具 (e.g., invoke, awscli) 都裝在虛擬環境
還必須要每次進入虛擬環境就太麻煩了
所以,在這樣的情況下,我就會建議使用 pipx

  • 安裝 pipx
python -m pip install --user pipx
python -m pipx ensurepath
  • 安裝工具
pipx install [package]

Reference

#Python #Virtual Environment #Dependency
Python Table Manners
各種開發 Python 專案時用得到的工具們






Related Posts

GitHub note

GitHub note

簡明 Vim 文字編輯器操作入門教學

簡明 Vim 文字編輯器操作入門教學

[day 02] new & factory: 如何建立一個新物件

[day 02] new & factory: 如何建立一個新物件



Comments