资讯动态

Python 项目迁移到 uv:经验小结与可复用工作流

发布时间:2026/9/11 18:49:49 来源:尧图企业网站定制
文章目录迁移前要确定的事初始化 uv 项目导入 requirements.txt私有源踩过的坑URL 依赖过渡期兜底收尾验证如果要回滚uv.lock 体积过大迁移前要确定的事Python 版本区间:项目实际验证过的版本非默认来源:私有源地址、--extra-index-url、--trusted-host、裸 wheel URL当前版本快照:pip freeze baseline-freeze.txt,迁移后拿它做对照出问题时也能回滚搞清楚两个容易混的概念:.python-version和requires-python.python-version:由uv python pin生成决定uv run、uv sync用哪个本地解释器requires-python:写在pyproject.toml里决定uv lock解析时覆盖的版本区间。区间越窄锁文件越小初始化 uv 项目已有pyproject.toml则跳过uv inituv init --no-readme uv python pin3.12[project] requires-python 3.12,3.13最好把.python-version提交用 git 管理。一个容易踩的坑uv pip install不读requires-python只认当前激活环境或当前目录下的.venv。所以用uv pip系列命令之前先跑一遍uv sync把.venv建好。导入 requirements.txtrequirements.txt 中的关键包最好锁定精确版本:# 不推荐 vlt-xxx-aws # 推荐 vlt-xxx-aws0.1.53为什么要精确一是私有源上的包往往没有严格的 semver 保证版本号跳动的含义跟 PyPI 上的包不是一回事二是如果内部包跟 PyPI 上某个公开包重名不锁精确版本时解析器可能会取到完全没预料到的来源。这类问题排查起来很费时间因为表现出来就是依赖版本对不上但根源是包来源搞错了。导入命令:uvadd-rrequirements.txtuv add -r会重写pyproject.toml并重新解析锁文件。uv sync只按现有锁文件同步环境不能替代迁移导入。私有源踩过的坑举一个真实场景。配好私有源之后uv add突然报这样的错:No solution found when resolving dependencies: - Because there is no version of psutil6.1.0 ... hint: psutil was found on http://xxxx-pip.xxxx.lan/simple, but not at the requested version A compatible version may be available on a subsequent index ...第一反应往往是这个包是不是被删了,但其实包还在只是版本不全——原因出在 uv 默认的index-strategy first-index策略上:uv 按索引顺序查找包名一旦在某个索引里找到就只从这个索引解析这个包不会再去后面的索引找更全的版本。这里的坑是内部镜像代理了psutil,但只同步了部分版本uv 找到包名就停手了根本不会意识到后面还有一个版本更全的索引。索引配置长这样:[[tool.uv.index]] name private url http://xxo-private-pip.xxxx.lan:9090/simple default true [[tool.uv.index]] name internal-wheels url http://172.xx.xxx.229:8899/simple/ [[tool.uv.index]] name xxp url http://xxp-pip.xx.lan/simple [tool.uv] allow-insecure-host [ xxo-private-pip.xxxx.lan, 172.xx.xxx.229, xxp-pip.xx.lan, ]几点要注意:default true会禁用 PyPI,并把这个索引放到已配置索引里优先级最低的位置。requirements.txt 里的--trusted-host对应的是allow-insecure-host,不是索引配置里的trusted字段——这两个名字太像很容易配错地方。能配出有效 TLS 证书的话优先修证书allow-insecure-host只应该是长期方案里的例外不是常态。解决刚才那个 psutil 问题,有两种思路:一种是全局关闭first-index策略:[tool.uv] index-strategy unsafe-best-mxxxh这样会跨所有索引找最高版本但风险也最大——只有当你配置的所有索引都完全可信时才应该这么做否则相当于把供应链安全性拱手让出去。更推荐的做法是单包 pin 来源,只解决出问题的那一个包:[tool.uv.sources] psutil { index private } [[tool.uv.index]] name private url http://xxxx-private-pip.xxxx.lan:9090/simpleURL 依赖uv 不允许 URL 依赖仅作为传递依赖存在必须在dependencies中显式声明并在[tool.uv.sources]里配置来源:[project] dependencies [xxx-engine-alarm] [tool.uv.sources] xxx-engine-alarm { url http://.../xxx_engine_alarm-0.0.3-py3-none-any.whl }如果第三方包内部也用 URL 声明了同一个依赖两边必须完全一致(版本、URL、hash),否则会报类似这样的冲突:error: Requirements contain conflicting URLs for package xxx-engine-alarm: - http://.../xxx_engine_alarm-0.0.3-py3-none-any.whl - http://.../xxx_engine_alarm-0.0.3-py3-none-any.whl (from a transitive dependency)看着像是同一个 URL,但差一个字符(比如内部包里写的是旧版本号或者 hash 对不上)都会触发。遇到这种报错先去翻第三方包自己声明的依赖来源跟你项目里写的逐字比对。过渡期兜底如果时间紧可以先用两条命令临时把依赖跑起来:uvadd-rrequirements.txt--frozen# 跳过锁文件更新临时导入依赖声明uv pipinstall-rrequirements.txt# 完全绕开项目解析这两条本质上相同——都是先让代码跑起来锁文件的事后面再说。--frozen不会生成可信的uv.lock;uv pip install干脆不走项目解析这条路执行前记得确认.venv已经建好。这两条命令用完之后一定要回头补上正式的uv lock。收尾验证重新生成锁文件uv lock干净环境复现# WindowsRemove-Item-Recurse-Force.venv# Unix/macOSrm-rf.venv uvsyncuv run python-cimport sys; print(sys.version)对照baseline-freeze.txt抽查关键包版本在 CI 或另一台干净机器上再跑一次uv sync确认不依赖本机缓存。最终再执行一遍工程的测试或者服务确保没有报错这才是根本的。如果要回滚迁移过程中如果卡住回滚比继续排查更划算的情况不少见。核心是保留住两样东西:baseline-freeze.txt和原来的requirements.txt。rm-rf.venv python-mvenv .venvsource.venv/bin/activate# Windows 用 .venv\Scripts\activatepipinstall-rrequirements.txt回滚不需要动pyproject.toml和uv.lock——留着它们下次再迁移时可以从上次中断的地方继续。uv.lock 体积过大最常见的原因是requires-python区间过宽。修复方式是收窄区间后重新生成锁文件:[project] requires-python 3.12,3.13uv lock另一个不那么明显的原因是索引配置太多:配置的源越多解析器给每个包做候选校验时要跨的源就越多锁文件里记录的候选信息也会跟着膨胀。如果收窄requires-python之后体积还是没降下来可以回头看看[[tool.uv.index]]是不是配多了有没有可以合并或去掉的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价