让任意Python脚本可复现运行:Uv2nix development-scripts模式
让任意Python脚本可复现运行:Uv2nix development-scripts模式
【免费下载链接】uv2nixUv2nix - Ingest uv workspaces using Nix [maintainer=@adisbladis]项目地址: https://gitcode.com/gh_mirrors/uv/uv2nix
uv2nix是一个用 Nix 摄取(ingest)uv 工作区的开源工具,它的development-scripts 模式能让你把任意一堆 Python 脚本变成"可复现运行"的 Nix 应用——不依赖本机 Python 版本,不依赖pip install的历史操作,任何人在任何机器上执行nix run .#greet都能得到完全一致的环境与结果。
为什么 Python 脚本难以"可复现运行"?
写过 Python 脚本的人都有体会:
- ❌ 同事机器上能跑,我这边缺依赖
- ❌ 今天能跑,明天某个依赖升级后突然崩了
- ❌
requirements.txt没锁版本,环境漂移了都不知道
development-scripts 模式正是为"不想把开发脚本打包成正式 Python 包,但又想要可复现环境"的场景设计的。它做三件事:
- 把一个目录里的脚本(如
examples/)视为来源 - 用 uv 锁定的依赖生成一个 Nix 构建的虚拟环境(virtualenv)
- 让每个脚本都能直接用
nix run启动 ✅
模式的核心文件在哪里?
这个模式的完整示例位于 doc/src/patterns/development-scripts/ 目录:
doc/src/patterns/development-scripts/ ├── flake.nix # 核心:把目录里的 .py 变成 nix run 应用 ├── pyproject.toml # uv 项目声明,记录依赖 ├── uv.lock # uv 锁文件,环境可复现的关键 ├── examples/ │ └── greet.py # 你的开发脚本,任意数量 └── src/development_scripts/__init__.py # 脚本可 import 的辅助代码示例脚本 examples/greet.py 本身非常简单,只是调用本地包的main()打印一句话——重点在于它的运行方式被 Nix 接管了。
工作原理:脚本是如何被"包"起来的?
核心逻辑全在 flake.nix 中,思路可以拆成四步:
1️⃣ 加载 uv 工作区与依赖
workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; overlay = workspace.mkPyprojectOverlay { sourcePreference = "wheel"; };uv2nix读取uv.lock,动态生成每个 Python 依赖的 Nix 派生。sourcePreference = "wheel"表示优先用预编译的二进制 wheel(更稳定);想从源码构建可改为"sdist"。
2️⃣ 构建虚拟环境
venv = pythonSet.mkVirtualEnv "development-scripts-default-env" workspace.deps.default;所有uv.lock里锁定的包被聚合进一个虚拟环境——这就是脚本运行时的"完整宇宙"。
3️⃣ 扫描目录,为每个 .py 生成一个 Nix 应用
flake.nix 会readDir你的脚本目录,筛选出所有.py文件,然后对每个文件:
- 拷贝脚本并加上执行权限
patchShebangs自动改写 shebang:#!/usr/bin/env python3会被替换成指向 Nix 虚拟环境解释器的绝对路径- 去掉
.py后缀作为应用名
4️⃣ 运行
nix run .#greet输出:
Hello from development-scripts!无需source任何环境、无需pip install、无需担心系统 Python 版本——这就是"可复现运行"。
快速上手:三步接入 development-scripts 模式
第一步:克隆项目或基于模板初始化
git clone https://gitcode.com/gh_mirrors/uv/uv2nix或者参照 doc/src/patterns/development-scripts/ 的结构创建自己的项目:一个pyproject.toml+ 一个uv.lock+ 一个脚本目录。
第二步:用 uv 声明并锁定依赖
uv add requests uv lockuv.lock一经提交,环境即被"冻结"——任何人构建出的虚拟环境都一模一样。
第三步:用 nix run 执行任意脚本
把脚本放进约定目录(示例中是examples/),然后:
nix run .#你的脚本名脚本里写的import requests会自动解析到 Nix 虚拟环境里的包,shebang 的改写由 flake.nix 自动完成,你什么都不用改。
实用技巧
🎯脚本即入口点:文件名就是命令名。deploy.py→nix run .#deploy,天然适合把部署、数据清洗、CI 辅助脚本收编进来。
🔒wheel 优先,sdist 兜底:二进制 wheel 构建更快、失败率更低;个别只有源码包的依赖会自动走 sdist 构建,构建系统由pyproject-build-systemsoverlay 提供,一般无需手工处理。
🚫不要再uv run:在 uv2nix 提供的环境里,不要再用uv run——它会让 uv 自己再造一个虚拟环境,绕开 Nix 管理的可复现链路。直接运行nix run .#脚本名即可。
📦多脚本零成本:脚本目录里加多少个.py,nix run .#下就多多少个命令,flakes 会自动发现,不需要逐个注册。
总结
| 痛点 | development-scripts 模式的解法 |
|---|---|
| 依赖版本漂移 | uv.lock锁定 + Nix 派生 |
| 系统 Python 不一致 | shebang 自动改写指向 Nix 虚拟环境 |
| 脚本无法一键分发 | 每个.py自动成为nix run应用 |
uv2nix 的 development-scripts 模式本质上回答了这样一个问题:"一堆散装 Python 脚本,如何像生产级服务一样可复现地运行?"答案就是——把整个目录交给 Nix,让uv.lock做环境事实源,让nix run做唯一入口。想要深入更多模式(测试、应用打包、交叉编译等),可以翻阅项目文档 doc/src/SUMMARY.md 中的 Patterns 章节。
【免费下载链接】uv2nixUv2nix - Ingest uv workspaces using Nix [maintainer=@adisbladis]项目地址: https://gitcode.com/gh_mirrors/uv/uv2nix
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
