
文章目录依赖混乱通常长什么样三套清单、三种解释器入口不统一的代价用 uv 把依赖与运行器钉死一份 pyproject.toml 说清版本与包统一入口uv run 而不是猜解释器入口怎么拆函数优先CLI 另放业务能力做成可 import 的函数cli.py 只做 argparse 与退出码仓库根怎么定位只认 pyproject.toml跨平台落地时的几条硬约定跨平台 Python 脚本库里真正拖垮协作效率的往往不是业务逻辑而是两件事依赖装哪一套、脚本从哪进。有人用系统 Python有人各自venvWin / macOS / Linux 再各装一版包入口则是「这个目录下那个main.py」「记得先cd」「用哪份解释器」——新人跑不起来老人也复现不了。本文只讲一件事用uv pyproject.toml把依赖与运行环境收口再用统一的uv run 入口.py和「函数优先、CLI 另放」把入口理顺。做法来自一套真实的跨平台 CLI 脚本库实践可直接照着改自己的仓库。依赖混乱通常长什么样三套清单、三种解释器常见现场requirements.txt、requirements-dev.txt、某人本机再加pip install xxx没有锁版本文档写「Python 3.10」机器上是 3.9 或 3.12行为不一致有人python script.py有人python3Windows 上还有「装了但 PATH 没指到」结果是同一仓库A 机能跑、B 机缺包、C 机包版本漂移导致诡异报错。排错时间花在环境上而不是代码上。入口不统一的代价脚本多了以后若每个目录各写一套「怎么跑」就会出现相对路径错乱日志、产物写到 cwd 而不是仓库根import core...失败没把仓库根放进sys.path长参数塞进命令行尤其 Windows被截断业务侧却以为是逻辑 bug依赖和入口是一套问题没有统一运行器就很难保证「同一份声明的依赖」真的被用上。用 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业务运行时依赖只维护这一处能写范围就写范围减少「某天静默升到不兼容大版本」dependency-groups.dev测试等开发依赖与运行时拆开CI / 本机按需装装依赖与生成锁文件用 uv在仓库根执行uvsync有人改了依赖声明提交pyproject.toml与锁文件其他人拉代码后再uv sync三台机器拿到同一解析结果。这比「邮件里贴一句 pip 命令」可核对得多。统一入口uv run而不是猜解释器约定所有可执行脚本都在仓库根调用uv run ai/cli.py--prompt短任务uv run cron/cli.py--oncesome-job uv run core/browser/cli.py --user-data demouv run会按项目配置选解释器、带上已同步的依赖再执行脚本。同事不必记「激活哪个 venv」你自己在 Win / macOS / Linux 换机器命令形态也一致。uv run适合人工快捷测试。业务脚本互相调用时优先import 函数不要再套一层子进程去uv run——函数没有命令行长度限制也少一层进程开销。入口怎么拆函数优先CLI 另放业务能力做成可 import 的函数模块内把能力做成函数例如run_agent、run_scheduler其它脚本直接fromai.agentimportrun_agent run_agent(promptlong_text,modeask)长文本、多行任务走函数参数避开命令行截断。跨平台时这一点在 Windows 上尤其明显。cli.py只做 argparse 与退出码给人点的入口单独放cli.py或多步编排用workflow-cli.py解析参数、校验、设退出码调用本模块函数不复制业务逻辑捕获KeyboardInterrupt打印一行[SKIP] 已中断退出码130不要堆 traceback示意结构#!/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定时任务、其它模块走import。入口统一逻辑不分裂。仓库根怎么定位只认pyproject.toml脚本里常要写日志、产物到固定目录。不要用「往上数两级」或认.git——子模块、拷贝目录、IDE 工作区一变就错。实践约定自当前文件路径向上找直到出现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勿手写盘符拼接子进程参数用列表避免shellTrue平台差异极大时拆*_win.py/*_mac.py必要时*_linux.py由主入口按sys.platform选用不要在一个文件里堆巨型if产物与日志落到仓库根下固定目录如build/、logs/相对根路径计算不相对「你碰巧 cd 到哪」依赖变更只改pyproject.toml再uv sync不要私下pip install不入库把「声明在 toml、运行靠 uv、根目录靠 pyproject、给人看的入口只有 cli」这四条钉死跨平台脚本库的环境债会少一大截新人照命令跑老脚本互相 importWin / macOS / Linux 不再各讲各的方言。