一)
文章目錄依賴混亂通常長什么樣三套清單、三種解釋器入口不統(tǒng)一的代價用 uv 把依賴與運行器釘死一份 pyproject.toml 說清版本與包統(tǒng)一入口uv run 而不是猜解釋器入口怎么拆函數(shù)優(yōu)先CLI 另放業(yè)務(wù)能力做成可 import 的函數(shù)cli.py 只做 argparse 與退出碼倉庫根怎么定位只認(rèn) pyproject.toml跨平臺落地時的幾條硬約定跨平臺 Python 腳本庫里真正拖垮協(xié)作效率的往往不是業(yè)務(wù)邏輯而是兩件事依賴裝哪一套、腳本從哪進。有人用系統(tǒng) Python有人各自venvWin / macOS / Linux 再各裝一版包入口則是「這個目錄下那個main.py」「記得先cd」「用哪份解釋器」——新人跑不起來老人也復(fù)現(xiàn)不了。本文只講一件事用uv pyproject.toml把依賴與運行環(huán)境收口再用統(tǒng)一的uv run 入口.py和「函數(shù)優(yōu)先、CLI 另放」把入口理順。做法來自一套真實的跨平臺 CLI 腳本庫實踐可直接照著改自己的倉庫。依賴混亂通常長什么樣三套清單、三種解釋器常見現(xiàn)場requirements.txt、requirements-dev.txt、某人本機再加pip install xxx沒有鎖版本文檔寫「Python 3.10」機器上是 3.9 或 3.12行為不一致有人python script.py有人python3Windows 上還有「裝了但 PATH 沒指到」結(jié)果是同一倉庫A 機能跑、B 機缺包、C 機包版本漂移導(dǎo)致詭異報錯。排錯時間花在環(huán)境上而不是代碼上。入口不統(tǒng)一的代價腳本多了以后若每個目錄各寫一套「怎么跑」就會出現(xiàn)相對路徑錯亂日志、產(chǎn)物寫到 cwd 而不是倉庫根import core...失敗沒把倉庫根放進sys.path長參數(shù)塞進命令行尤其 Windows被截斷業(yè)務(wù)側(cè)卻以為是邏輯 bug依賴和入口是一套問題沒有統(tǒng)一運行器就很難保證「同一份聲明的依賴」真的被用上。用 uv 把依賴與運行器釘死一份pyproject.toml說清版本與包在倉庫根放pyproject.toml至少寫清三塊[project] name mscc-cli version 0.1.0 requires-python 3.13 dependencies [ apscheduler3.10,4, portalocker4.1.0, ] [dependency-groups] dev [ pytest9.1.1, ]要點requires-python跨平臺團隊先對齊解釋器下限比口頭約定可靠dependencies業(yè)務(wù)運行時依賴只維護這一處能寫范圍就寫范圍減少「某天靜默升到不兼容大版本」dependency-groups.dev測試等開發(fā)依賴與運行時拆開CI / 本機按需裝裝依賴與生成鎖文件用 uv在倉庫根執(zhí)行uvsync有人改了依賴聲明提交pyproject.toml與鎖文件其他人拉代碼后再uv sync三臺機器拿到同一解析結(jié)果。這比「郵件里貼一句 pip 命令」可核對得多。統(tǒng)一入口uv run而不是猜解釋器約定所有可執(zhí)行腳本都在倉庫根調(diào)用uv run ai/cli.py--prompt短任務(wù)uv run cron/cli.py--oncesome-job uv run core/browser/cli.py --user-data demouv run會按項目配置選解釋器、帶上已同步的依賴再執(zhí)行腳本。同事不必記「激活哪個 venv」你自己在 Win / macOS / Linux 換機器命令形態(tài)也一致。uv run適合人工快捷測試。業(yè)務(wù)腳本互相調(diào)用時優(yōu)先import 函數(shù)不要再套一層子進程去uv run——函數(shù)沒有命令行長度限制也少一層進程開銷。入口怎么拆函數(shù)優(yōu)先CLI 另放業(yè)務(wù)能力做成可 import 的函數(shù)模塊內(nèi)把能力做成函數(shù)例如run_agent、run_scheduler其它腳本直接fromai.agentimportrun_agent run_agent(promptlong_text,modeask)長文本、多行任務(wù)走函數(shù)參數(shù)避開命令行截斷??缙脚_時這一點在 Windows 上尤其明顯。cli.py只做 argparse 與退出碼給人點的入口單獨放cli.py或多步編排用workflow-cli.py解析參數(shù)、校驗、設(shè)退出碼調(diào)用本模塊函數(shù)不復(fù)制業(yè)務(wù)邏輯捕獲KeyboardInterrupt打印一行[SKIP] 已中斷退出碼130不要堆 traceback示意結(jié)構(gòu)#!/usr/bin/env python3用法uv run 模塊/cli.py [args...]from__future__importannotationsimportargparsefrompathlibimportPathfromcore.loggerimportensure_repo_path ROOTensure_repo_path(Path(__file__))frommypkg.serviceimportrun_job# noqa: E402defmain(argv:list[str]|NoneNone)-int:pargparse.ArgumentParser()p.add_argument(--once,default)argsp.parse_args(argv)try:returnrun_job(onceargs.once)exceptKeyboardInterrupt:print([SKIP] 已中斷,flushTrue)return130if__name____main__:raiseSystemExit(main())人工測試走uv run .../cli.py定時任務(wù)、其它模塊走import。入口統(tǒng)一邏輯不分裂。倉庫根怎么定位只認(rèn)pyproject.toml腳本里常要寫日志、產(chǎn)物到固定目錄。不要用「往上數(shù)兩級」或認(rèn).git——子模塊、拷貝目錄、IDE 工作區(qū)一變就錯。實踐約定自當(dāng)前文件路徑向上找直到出現(xiàn)pyproject.toml該目錄即倉庫根。找到即停。defrepo_root(start:Path|NoneNone)-Path:here(startorPath.cwd()).resolve()ifhere.is_file():herehere.parentforcandidatein(here,*here.parents):if(candidate/pyproject.toml).is_file():returncandidatereturnPath.cwd().resolve()defensure_repo_path(start:Path|NoneNone)-Path:rootrepo_root(start)root_sstr(root)ifroot_snotinsys.path:sys.path.insert(0,root_s)returnrootensure_repo_path(Path(__file__))順帶把根目錄塞進sys.pathfrom core...才能在「直接跑腳本」時成立。這和 uv 的「以含pyproject.toml的目錄為項目根」是同一套心智模型。跨平臺落地時的幾條硬約定項建議路徑一律pathlib.Path勿手寫盤符拼接子進程參數(shù)用列表避免shellTrue平臺差異極大時拆*_win.py/*_mac.py必要時*_linux.py由主入口按sys.platform選用不要在一個文件里堆巨型if產(chǎn)物與日志落到倉庫根下固定目錄如build/、logs/相對根路徑計算不相對「你碰巧 cd 到哪」依賴變更只改pyproject.toml再uv sync不要私下pip install不入庫把「聲明在 toml、運行靠 uv、根目錄靠 pyproject、給人看的入口只有 cli」這四條釘死跨平臺腳本庫的環(huán)境債會少一大截新人照命令跑老腳本互相 importWin / macOS / Linux 不再各講各的方言。