你可能寫過這種 code
多個子專案要共用一組工具模組,於是在每個進入點加上:
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from common.utils import some_helper
Code language: JavaScript (javascript)runtime 能跑,但問題不少:
- IDE 紅線滿天飛——Pylance 不認
sys.path動態修改,import 全部標紅 - 路徑脆弱——搬個檔案就爆,
.parent.parent數量要人工對齊 - 越寫越亂——有人寫
if __package__:/else:雙分支來同時支援模組和腳本模式,可讀性直接砍半
更好的做法:讓它變成正式套件
核心思路很簡單——在 pyproject.toml 加 build-system,讓 uv sync 以 editable 模式安裝你的共用模組。之後所有 import 都是標準套件路徑,IDE 和 runtime 都認。
1. 把共用模組搬到根目錄
# Before # After
project/ project/
├── agents/ ├── agent_common/ ← 搬出來
│ ├── common/ │ ├── __init__.py
│ │ ├── __init__.py │ ├── tracker.py
│ │ └── tracker.py │ └── helpers.py
│ ├── project_a/ ├── agents/
│ │ └── agent.py │ ├── project_a/
│ └── project_b/ │ │ └── agent.py
│ └── agent.py │ └── project_b/
└── pyproject.toml │ └── agent.py
└── pyproject.toml
Code language: PHP (php)2. pyproject.toml 加兩個區塊
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["agent_common"]
Code language: JavaScript (javascript)
packages要明確指定,因為 hatchling 預設找跟專案名稱對應的目錄。如果你的套件目錄名跟[project] name不一致,就需要這行。
3. uv sync
uv sync
hatchling 會以 editable 模式安裝,agent_common 變成正式可 import 的套件。
4. 把所有 hack 換成乾淨的 import
# Before
import sys
sys.path.insert(0, str(Path(__file__).parent.parent))
from common.tracker import SubagentTracker
# After
from agent_common.tracker import SubagentTracker
Code language: CSS (css)刪掉 import sys、刪掉 sys.path.insert、刪掉 if __package__: 分支。
就這樣
整個過程只動了三個東西:
mv搬目錄pyproject.toml加 6 行設定- 每個檔案把
from common.改成from agent_common.
之後 IDE 不再報錯、import 路徑穩定、新人看 code 不用猜 sys.path 在幹嘛。
如果你的共用模組需要被多個子專案 import,讓它成為正式安裝的套件,而不是靠 sys.path hack。uv + hatchling 的 editable 模式讓這件事幾乎零成本。