Python 封裝與發佈:pip install -e(可編輯安裝)
簡介
在 Python 專案的開發與測試過程中,我們常常需要在本機環境裡安裝自己的套件。若每次修改程式碼都必須重新執行 pip install .,不僅耗時,也容易忘記重新安裝,導致測試時跑到舊的版本。
pip install -e(或稱 editable install)正是為了解決這個痛點而設計的。它會在 site‑packages 中放入一個指向專案目錄的 .pth 檔,使得 Python 直接讀取原始碼,而不需要每次重新打包安裝。開發者可以即時看到程式碼變動的效果,大幅提升開發效率。
本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,以及實務應用場景,完整介紹 pip install -e 的使用方式,讓你在日常開發中玩得更順手。
核心概念
1. Editable 安裝的原理
- 普通安裝:
pip install .會先執行setup.py sdist/bdist_wheel,產生發佈檔(.tar.gz、.whl),再把檔案解壓至site-packages。安裝完成後,Python 執行的都是 已編譯好的檔案,原始碼的任何變動都不會自動反映。 - Editable 安裝:
pip install -e .會在site-packages內建立一個指向專案根目錄的.pth檔(例如myproj.pth),並把src(或根目錄)加入sys.path。因此,import mypkg時會直接載入 專案目錄下的原始碼。只要修改檔案,下一次執行即能看到最新的行為。
重點:Editable 安裝只影響 開發環境,發布到 PyPI 時仍然需要正式的
sdist/wheel,不會把.pth檔上傳。
2. setup.cfg、pyproject.toml 與 setup.py 的差異
近年來 Python 專案越來越傾向使用 PEP 517/518 標準,將建構資訊寫在 pyproject.toml,而非傳統的 setup.py。pip install -e 完全支援兩種寫法,只要 pyproject.toml 中宣告了建構後端(如 setuptools、flit、poetry),pip 會自動呼叫對應的工具完成 editable 安裝。
以下示範兩種常見配置:
| 方式 | 主要檔案 | 範例 |
|---|---|---|
| 傳統 | setup.py + setup.cfg |
setup.py 只保留最小程式碼,設定放在 setup.cfg |
| PEP 517 | pyproject.toml |
完全不需要 setup.py,所有設定都在 pyproject.toml |
3. 為什麼要使用 -e?
- 即時測試:修改模組即時生效,配合
pytest、tox等測試工具,可省去重新安裝的步驟。 - 多套件協同開發:在 monorepo 或多子套件的情況下,使用 editable 安裝讓每個子套件都能互相引用最新程式碼。
- IDE/偵錯友好:大部分 IDE(VS Code、PyCharm)會直接讀取
site-packages,若是 editable 安裝,IDE 能正確定位到原始檔,提供完整的自動完成與除錯資訊。
程式碼範例
以下示範從最簡單的專案結構到進階的 pyproject.toml 配置,讓你一步步掌握 pip install -e 的使用方式。
範例 1:最小專案結構與 setup.py
myproj/
├─ mypkg/
│ ├─ __init__.py
│ └─ core.py
├─ tests/
│ └─ test_core.py
├─ setup.py
└─ README.md
setup.py(最簡化版):
# setup.py
from setuptools import setup, find_packages
setup(
name="myproj",
version="0.1.0",
packages=find_packages(),
# 讓 pip 知道可以 editable 安裝
# 這裡不需要額外參數,默認即支援 -e
)
安裝方式(在 myproj 根目錄執行):
pip install -e .
此時,import mypkg.core 會直接載入 myproj/mypkg/core.py,你在 core.py 加入以下程式碼並儲存,即可立刻在 REPL 中看到變化:
# mypkg/core.py
def greet(name: str) -> str:
"""回傳問候字串"""
return f"Hello, {name}!"
# 測試
if __name__ == "__main__":
print(greet("World"))
範例 2:使用 setup.cfg 分離設定
myproj/
├─ mypkg/
│ ├─ __init__.py
│ └─ utils.py
├─ setup.cfg
├─ setup.py
└─ README.md
setup.cfg:
[metadata]
name = myproj
version = 0.2.0
description = 範例專案,使用 setup.cfg
[options]
packages = find:
python_requires = >=3.8
setup.py(只保留必要的呼叫):
# setup.py
from setuptools import setup
if __name__ == "__main__":
setup()
安裝指令:
pip install -e .
提示:若你只想在開發環境使用 editable 安裝,建議在
requirements-dev.txt中寫入-e .,讓 CI/CD 的測試階段自動安裝。
範例 3:PEP 517/518 風格的 pyproject.toml
myproj/
├─ src/
│ └─ mypkg/
│ ├─ __init__.py
│ └─ math.py
├─ tests/
│ └─ test_math.py
└─ pyproject.toml
pyproject.toml(使用 setuptools 作為建構後端):
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "myproj"
version = "0.3.0"
description = "使用 pyproject.toml 的範例專案"
requires-python = ">=3.9"
dependencies = []
[tool.setuptools.packages.find]
where = ["src"]
安裝指令:
pip install -e .
此時 import mypkg.math 會自動從 src/mypkg 讀取,無需在 setup.py 中寫任何程式碼。
範例 4:在 monorepo 中同時 editable 多套件
假設你有兩個子套件 lib_a、lib_b,lib_b 依賴 lib_a。
repo/
├─ lib_a/
│ ├─ src/
│ │ └─ lib_a/
│ │ └─ __init__.py
│ └─ pyproject.toml
├─ lib_b/
│ ├─ src/
│ │ └─ lib_b/
│ │ └─ __init__.py
│ └─ pyproject.toml
└─ requirements-dev.txt
requirements-dev.txt:
-e ./lib_a
-e ./lib_b
執行:
pip install -r requirements-dev.txt
此時 lib_b 內的程式碼若 import lib_a,會直接使用 editable 安裝的 lib_a,即使你同時在兩個套件內部修改,都能即時反映。
範例 5:搭配 pytest 直接測試
在專案根目錄建立 pytest.ini(或 pyproject.toml 中的 [tool.pytest.ini_options]):
# pytest.ini
[pytest]
addopts = -ra -q
testpaths = tests
執行測試:
pytest
因為專案已以 -e 方式安裝,tests 內的 import mypkg.core 會直接載入最新程式碼,無需額外的 PYTHONPATH 設定。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案或最佳實踐 |
|---|---|---|
| 1. 權限問題 | 在全域環境使用 pip install -e . 可能需要 sudo,但 sudo 會把檔案安裝在系統路徑,導致後續無法以使用者身分更新。 |
使用 virtualenv / venv,在隔離環境中安裝 editable 套件,避免權限衝突。 |
2. src 佈局未正確設定 |
若專案使用 src/ 目錄,但 setup.cfg/pyproject.toml 沒告訴 setuptools 在哪裡找套件,editable 安裝會失敗。 |
在 setup.cfg 加入 <options.packages.find.where = src> 或在 pyproject.toml 中的 [tool.setuptools.packages.find] where = ["src"]。 |
| 3. 依賴未同步更新 | editable 只會把套件本身設為指向原始碼,不會自動安裝 install_requires 中的依賴。 |
在第一次安裝時執行 pip install -e .[dev](使用 extras)或手動安裝缺少的依賴。 |
4. 發佈時忘記移除 -e |
某些 CI/CD pipeline 直接把 requirements.txt 推上 PyPI,若裡面仍有 -e .,會導致安裝失敗。 |
分離開發需求(requirements-dev.txt)與正式需求(requirements.txt),確保發佈時不包含 -e。 |
| 5. 多個同名套件的衝突 | 在同一環境中同時安裝兩個不同路徑的 editable 套件,且套件名稱相同,會產生衝突。 | 使用 unique package name,或在不同 virtualenv 中分別安裝。 |
最佳實踐小結
- 永遠在 virtualenv 中使用
-e,保持系統環境乾淨。 - 把
-e .放入requirements-dev.txt,讓開發者只需一條指令即可安裝全部開發相依。 - 使用
src佈局 搭配正確的find_packages(where="src"),可避免意外把測試檔案打包進發佈版。 - 在 CI 中加入
pip install -e .,確保測試跑的是最新程式碼,且與本機行為一致。 - 定期檢查
.pth檔,若不再需要可手動刪除或使用pip uninstall移除套件。
實際應用場景
| 場景 | 為什麼適合使用 pip install -e |
|---|---|
| 1. 套件開發者 | 每次改動都立即可在 REPL、測試或示範腳本中驗證,省去重複打包的時間。 |
| 2. 多服務微服務架構 | 各微服務的共用函式庫(例如 common-utils)可以以 editable 方式安裝,保證所有服務執行的都是同一份最新程式碼。 |
| 3. 教學與工作坊 | 講師只需要一次 pip install -e .,學員即能在自己的筆記本上即時修改範例程式碼,提升互動性。 |
| 4. 研究原型 | 資料科學家常在 Jupyter Notebook 中測試自訂函式庫,editable 安裝讓 Notebook 能即時載入最新的演算法實作。 |
| 5. CI/CD 測試管線 | 在 GitHub Actions、GitLab CI 等環境中,以 pip install -e . 安裝套件,再跑 pytest,可保證測試與開發環境行為一致。 |
總結
pip install -e .是 開發階段的神器,讓 Python 套件在本機環境中保持「即時」與「可編輯」的狀態。- 透過正確的專案結構(
src佈局或pyproject.toml),配合setup.cfg/setup.py,即可輕鬆完成 editable 安裝。 - 注意 權限、依賴、路徑設定 等常見陷阱,並遵循 virtualenv + requirements‑dev.txt 的最佳實踐,能讓專案維護更安全、團隊協作更順暢。
- 無論是單一套件開發、monorepo 多套件管理,或是教學、研究原型,都能從
pip install -e中受益,提升開發效率與除錯體驗。
快把這個指令加入你的開發工作流程吧!在下一次改寫程式碼時,你會發現 不需要再重新安裝,而是直接在 Python 直譯器或測試套件中看到最新的變化。祝開發順利 🚀.